build: rework library bundling
authorJo-Philipp Wich <jo@mein.io>
Tue, 10 Jan 2017 10:37:47 +0000 (11:37 +0100)
committerJo-Philipp Wich <jo@mein.io>
Tue, 10 Jan 2017 11:27:28 +0000 (12:27 +0100)
Rework the bundle-libraries.sh implementation to use a more robust approach
for executing host binaries through the shipped ELF loader and libraries.

The previous approach relied on symlinks pointing to a wrapper script which
caused various issues, especially with multicall binaries as the original
argv[0] name was not preserved through the ld.so invocation. Another down-
side was the fact that the actual binaries got moved into another directory
which caused executables to fail looking up resources with paths relative
to the executable location.

The new library wrapper implements the following improvements:

 - Instead of symlinks pointing to a common wrapper, each ELF executable
   is now replaced by a unqiue shell script which retains the original
   program name getting called

 - Instead of letting ld.so invoke the ELF executable directly, launch
   the final ELF binary through a helper program which fixes up the argv[0]
   argument for the target program

 - Support sharing a common location for the bundled libraries instead of
   having one copy in each directory containing wrapped binaries

Finally modify the SDK build to wrap the staging_dir and toolchain binaries
which allows to use the SDK on systems with a different glibc version.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
scripts/bundle-libraries.sh
target/imagebuilder/Makefile
target/sdk/Makefile

index aba1cd6268e35adafee312a8855935371ee27342..dfc2b85a47a922f6544e0c95206b0b15260a4ba7 100755 (executable)
@@ -2,7 +2,7 @@
 #
 #   Script to install host system binaries along with required libraries.
 #
-#   Copyright (C) 2012-2013 Jo-Philipp Wich <jo@mein.io>
+#   Copyright (C) 2012-2017 Jo-Philipp Wich <jo@mein.io>
 #
 #   This program is free software; you can redistribute it and/or modify
 #   it under the terms of the GNU General Public License as published by
@@ -27,6 +27,13 @@ _cp() {
        }
 }
 
+_mv() {
+       mv ${VERBOSE:+-v} "$1" "$2" || {
+               echo "mv($1 $2) failed" >&2
+               exit 1
+       }
+}
+
 _md() {
        mkdir ${VERBOSE:+-v} -p "$1" || {
                echo "mkdir($1) failed" >&2
@@ -41,6 +48,52 @@ _ln() {
        }
 }
 
+_relpath() {
+       local base="$(readlink -f "$1")"
+       local dest="$(readlink -f "$2")"
+       local up
+
+       [ -d "$base" ] || base="${base%/*}"
+       [ -d "$dest" ] || dest="${dest%/*}"
+
+       while true; do
+               case "$base"
+                       in "$dest"/*)
+                               echo "$up/${base#$dest/}"
+                               break
+                       ;;
+                       *)
+                               dest="${dest%/*}"
+                               up="${up:+$up/}.."
+                       ;;
+               esac
+       done
+}
+
+_wrapper() {
+       cat <<-EOT | ${CC:-gcc} -x c -o "$1" -
+               #include <unistd.h>
+               #include <stdio.h>
+
+               int main(int argc, char **argv) {
+                       const char *self   = argv[0];
+                       const char *target = argv[1];
+
+                       if (argc < 3) {
+                               fprintf(stderr, "Usage: %s executable arg0 [args...]\n", self);
+                               return 1;
+                       }
+
+                       return execv(target, argv + 2);
+               }
+       EOT
+
+       [ -x "$1" ] || {
+               echo "compiling wrapper failed" >&2
+               exit 5
+       }
+}
+
 for LDD in ${PATH//://ldd }/ldd; do
        "$LDD" --version >/dev/null 2>/dev/null && break
        LDD=""
@@ -49,29 +102,31 @@ done
 [ -n "$LDD" -a -x "$LDD" ] || LDD=
 
 for BIN in "$@"; do
-       [ -n "$BIN" -a -x "$BIN" -a -n "$DIR" ] || {
+       [ -n "$BIN" -a -n "$DIR" ] || {
                echo "Usage: $0 <destdir> <executable> ..." >&2
                exit 1
        }
 
-       [ ! -d "$DIR/bundled/lib" ] && {
-               _md "$DIR/bundled/lib"
-               _md "$DIR/bundled/usr"
-               _ln "../lib" "$DIR/bundled/usr/lib"
+       [ ! -d "$DIR/lib" ] && {
+               _md "$DIR/lib"
+               _md "$DIR/usr"
+               _ln "../lib" "$DIR/usr/lib"
+       }
+
+       [ ! -x "$DIR/lib/runas" ] && {
+               _wrapper "$DIR/lib/runas"
        }
 
        LDSO=""
 
-       echo "Bundling ${BIN##*/}"
-       [ -n "$LDD" ] && {
+       [ -n "$LDD" ] && [ -x "$BIN" ] && file "$BIN" | grep -sqE "ELF.*executable" && {
                for token in $("$LDD" "$BIN" 2>/dev/null); do
                        case "$token" in */*.so*)
                                case "$token" in
                                        *ld-*.so*) LDSO="${token##*/}" ;;
-                                       *) echo " * lib: ${token##*/}" ;;
                                esac
 
-                               dest="$DIR/bundled/lib/${token##*/}"
+                               dest="$DIR/lib/${token##*/}"
                                ddir="${dest%/*}"
 
                                [ -f "$token" -a ! -f "$dest" ] && {
@@ -82,29 +137,22 @@ for BIN in "$@"; do
                done
        }
 
-       _md "$DIR"
-
        # is a dynamically linked executable
        if [ -n "$LDSO" ]; then
-               _cp "$BIN" "$DIR/bundled/${BIN##*/}"
+               echo "Bundling ${BIN##*/}"
 
+               RUNDIR="$(readlink -f "$BIN")"; RUNDIR="${RUNDIR%/*}"
                RUN="${LDSO#ld-}"; RUN="run-${RUN%%.so*}.sh"
+               REL="$(_relpath "$DIR/lib" "$BIN")"
 
-               [ -x "$DIR/bundled/$RUN" ] || {
-                       cat <<-EOF > "$DIR/bundled/$RUN"
-                               #!/usr/bin/env bash
-                               dir="\$(dirname "\$0")"
-                               bin="\$(basename "\$0")"
-                               exec -a "\$0" "\$dir/bundled/lib/$LDSO" --library-path "\$dir/bundled/lib" "\$dir/bundled/\$bin" "\$@"
-                       EOF
-                       chmod ${VERBOSE:+-v} 0755 "$DIR/bundled/$RUN"
-               }
+               _mv "$BIN" "$RUNDIR/.${BIN##*/}.bin"
 
-               _ln "./bundled/$RUN" "$DIR/${BIN##*/}"
+               cat <<-EOF > "$BIN"
+                       #!/usr/bin/env bash
+                       dir="\$(dirname "\$0")"
+                       exec "\$dir/${REL:+$REL/}$LDSO" --library-path "\$dir/${REL:+$REL/}" "\$dir/${REL:+$REL/}runas" "\$dir/.${BIN##*/}.bin" "\$0" "\$@"
+               EOF
 
-       # is a static executable or non-elf binary
-       else
-               [ -n "$LDD" ] && echo " * not dynamically linked"
-               _cp "$BIN" "$DIR/${BIN##*/}"
+               chmod ${VERBOSE:+-v} 0755 "$BIN"
        fi
 done
index b43662b6ac8a8a3f75dea59be5d96abbad12e448..8a7c2093b987e5e339138305f05bfd400f3b2174 100644 (file)
@@ -66,16 +66,20 @@ endif
                $(PKG_BUILD_DIR)/target/linux/*/patches{,-*}
        -cp $(KERNEL_BUILD_DIR)/* $(IB_KDIR)/ # don't copy subdirectories here
        -cp $(LINUX_DIR)/.config $(IB_LDIR)/
-       -$(SCRIPT_DIR)/bundle-libraries.sh $(IB_LDIR)/scripts/dtc \
-         $(LINUX_DIR)/scripts/dtc/dtc
+       if [ -x $(LINUX_DIR)/scripts/dtc/dtc ]; then \
+               $(INSTALL_DIR) $(IB_LDIR)/scripts/dtc; \
+               $(INSTALL_BIN) $(LINUX_DIR)/scripts/dtc/dtc $(IB_LDIR)/scripts/dtc/dtc; \
+       fi
        if [ -d $(LINUX_DIR)/arch/$(ARCH)/boot/dts ]; then \
                $(CP) $(LINUX_DIR)/arch/$(ARCH)/boot/dts/* $(IB_DTSDIR); \
        fi
        $(SED) 's,^# REVISION:=.*,REVISION:=$(REVISION),g' $(PKG_BUILD_DIR)/include/version.mk
        find $(PKG_BUILD_DIR) -name CVS -o -name .git -o -name .svn \
          | $(XARGS) rm -rf
-       find $(STAGING_DIR_HOST)/bin -maxdepth 1 -type f -perm -u=x \
-         | $(XARGS) $(SCRIPT_DIR)/bundle-libraries.sh $(PKG_BUILD_DIR)/staging_dir/host/bin/
+       $(INSTALL_DIR) $(PKG_BUILD_DIR)/staging_dir/host/bin
+       $(CP) $(STAGING_DIR_HOST)/bin/* $(PKG_BUILD_DIR)/staging_dir/host/bin/
+       (cd $(PKG_BUILD_DIR); find staging_dir/host/bin/ $(IB_LDIR)/scripts/dtc/ -type f | \
+               $(XARGS) $(SCRIPT_DIR)/bundle-libraries.sh $(PKG_BUILD_DIR)/staging_dir/host)
        STRIP=sstrip $(SCRIPT_DIR)/rstrip.sh $(PKG_BUILD_DIR)/staging_dir/host/bin/
        $(TAR) -cf - -C $(BUILD_DIR) $(IB_NAME) | xz -zc -7e > $@
 
index de77d1a31aa45e6c6dd2626595c5dcd9ca836854..c1eca736b0f3bfa48dd23534375900bb700cef05 100644 (file)
@@ -76,6 +76,10 @@ $(BIN_DIR)/$(SDK_NAME).tar.xz: clean
                $(SDK_DIRS) $(KERNEL_FILES) | \
                $(TAR) -xf - -C $(SDK_BUILD_DIR)
 
+       (cd $(SDK_BUILD_DIR); find $(STAGING_SUBDIR_HOST)/bin $(STAGING_SUBDIR_HOST)/usr/bin \
+               $(STAGING_SUBDIR_TOOLCHAIN)/bin $(STAGING_SUBDIR_TOOLCHAIN)/*/bin $(STAGING_SUBDIR_TOOLCHAIN)/libexec \
+               -type f | $(XARGS) $(SCRIPT_DIR)/bundle-libraries.sh $(SDK_BUILD_DIR)/$(STAGING_SUBDIR_HOST))
+
        @-( \
                find \
                        $(SDK_BUILD_DIR)/$(STAGING_SUBDIR_HOST)/bin \