[Buildroot] [RFC] Relocatable host tools

Thomas De Schampheleire patrickdepinguin at gmail.com
Fri Dec 11 14:11:27 UTC 2015


Hi,

Recently, a few patches by Yann E. Morin have entered Buildroot
master, which make sure that all host tools (output/host/usr/bin/...)
have a correct RPATH encoded, namely
/absolute/buildroot/output/host/usr/lib.

I believe this is one step closer to having a relocatable host SDK for
Buildroot. While I'm not claiming (nor have I tested) a completely
relocatable host SDK, by changing the RPATH from an absolute path to
'$ORIGIN/../lib'.

'$ORIGIN' here is a literal string encoded in the ELF binary and
replaced at runtime by the location of the binary. So, a binary
originally created in /home/foo/buildroot/output/host/usr/bin and run
from there will expect its libraries in
/home/foo/buildroot/output/host/usr/lib, but the same binary copied
and run from /tmp/hostsdk/bin will expect its libraries in
/tmp/hostsdk/lib.

One could even set a combined RPATH:
    $ORIGIN/../lib:/absolute/buildroot/output/host/usr/lib
Then, if one copies the binary but not the entire host directory,
things still work as 'expected'.

Reliably passing the string $ORIGIN from buildroot make to each
package such that the resulting binary still has the untouched $ORIGIN
string proves to be difficult. Depending on how the package Makefile
is treating the LDFLAGS, the dollar will be expanded or not.

My original attempt was to change package/Makefile.in directly and
pass -Wl,-rpath='\$$\$$ORIGIN/../lib' which worked for most cases, but
e.g. not for host-kmod (where the combination \$$\$$ became a shell $$
which expanded to a PID). The web is full of other developers that
face the same type of problems.

The alternative solution is to change the RPATH /after/ the package is
built, e.g. using patchelf. I have successfully used
    patchelf --set-rpath '$ORIGIN/../lib' <host-binary>
(this in fact sets RUNPATH rather than RPATH, which is fine too).

Exactly how/where this fits into the build process is up for
discussion. For my proof-of-concept I have abused the check-host-rpath
script. Here, host-patchelf was first built manually, but this should
be added as an automatic dependency.
This location is unfortunate, as it is run as an instrumentation hook.
If it fails, the package seems to be installed correctly (but with an
incorrect RPATH). Instead, HOST_FOO_POST_INSTALL_HOOKS is probably the
better place.

Perhaps, a configure option should be added that enables/disables this
feature, to avoid an unnecessary host-patchelf dependency for people
that don't need it.

I'm pasting my proof-of-concept below, just for reference. In no way
is this supposed to be code ready for inclusion.

diff --git a/package/pkg-generic.mk b/package/pkg-generic.mk
--- a/package/pkg-generic.mk
+++ b/package/pkg-generic.mk
@@ -58,8 +58,9 @@ GLOBAL_INSTRUMENTATION_HOOKS += step_tim
 # This hook checks that host packages that need libraries that we build
 # have a proper DT_RPATH or DT_RUNPATH tag
 define check_host_rpath
+    $(if $(filter-out host-patchelf,$(3)),\
     $(if $(filter install-host,$(2)),\
-        $(if $(filter end,$(1)),support/scripts/check-host-rpath $(3)
$(HOST_DIR)))
+        $(if $(filter end,$(1)),support/scripts/check-host-rpath $(3)
$(HOST_DIR))))
 endef
 GLOBAL_INSTRUMENTATION_HOOKS += check_host_rpath

diff --git a/support/scripts/check-host-rpath b/support/scripts/check-host-rpath
--- a/support/scripts/check-host-rpath
+++ b/support/scripts/check-host-rpath
@@ -19,6 +19,7 @@ main() {
     ret=0
     while read file; do
         elf_needs_rpath "${file}" "${hostdir}" || continue
+        set_elf_rpath "${file}" "${hostdir}"
         check_elf_has_rpath "${file}" "${hostdir}" && continue
         if [ ${ret} -eq 0 ]; then
             ret=1
@@ -49,6 +50,12 @@ elf_needs_rpath() {
     return 1
 }

+set_elf_rpath() {
+    local file="${1}"
+    local hostdir="${2}"
+    ${hostdir}/usr/bin/patchelf --set-rpath '$ORIGIN/../lib' $file
+}
+
 check_elf_has_rpath() {
     local file="${1}"
     local hostdir="${2}"
@@ -58,7 +65,7 @@ check_elf_has_rpath() {
         for dir in ${rpath//:/ }; do
             # Remove duplicate and trailing '/' for proper match
             dir="$( sed -r -e 's:/+:/:g; s:/$::;' <<<"${dir}" )"
-            [ "${dir}" = "${hostdir}/usr/lib" ] && return 0
+            [ "${dir}" = '$ORIGIN/../lib' ] && return 0
         done
     done < <( readelf -d "${file}"
          \
               |sed -r -e '/.* \(R(UN)?PATH\) +Library r(un)?path:
\[(.+)\]$/!d' \


Looking forward to your feedback.

/Thomas


More information about the buildroot mailing list