From cbac39e8b85734c16c0cc3871ea917cebf0eebc8 Mon Sep 17 00:00:00 2001 From: Alex Birch Date: Sun, 17 Jun 2018 15:52:20 +0100 Subject: [PATCH] backup of useful relinking script, in case GitHub disappears --- Builds/MacOSX/make_portable.sh | 93 ++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100755 Builds/MacOSX/make_portable.sh diff --git a/Builds/MacOSX/make_portable.sh b/Builds/MacOSX/make_portable.sh new file mode 100755 index 0000000..605d342 --- /dev/null +++ b/Builds/MacOSX/make_portable.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash + +# Example input: +# ./make_portable.sh mycoolbinary +# where mycoolbinary is a mach-o object file +# (for example an executable binary or a .dylib) +# +# this script rewrites your file's every environment-specific +# dynamic link (recursively!) +# such that they point to local .dylibs. +# these .dylibs are then copied to a folder lib, next to your binary +# +# by "environment-specific" I mean any link to a .dylib under /usr/local + +set -o pipefail + +error() { + local parent_lineno="$1" + local message="$2" + local code="${3:-1}" + if [[ -n "$message" ]] ; then + echo "Error on or near line ${parent_lineno}: ${message}; exiting with status ${code}" + else + echo "Error on or near line ${parent_lineno}; exiting with status ${code}" + fi + exit "${code}" +} +trap 'error ${LINENO}' ERR + +BINARY="$1" +BINARYDIR=$(dirname "$BINARY") +LIBREL="lib" +LIB="$BINARYDIR/$LIBREL" + +# make a lib folder +mkdir -p "$LIB" + +# find every LC_LOAD_DYLIB command in the obj file +# filter to just loads under /usr/local +# print the absolute path of each such dylib +get_env_specific_direct_dependencies () { + # otool -L shows us every LC_LOAD_DYLIB plus LC_ID_DYLIB + # otool -D shows us just LC_ID_DYLIB + ALL_DYLIBS=$(otool -L "$1" | awk 'NR>1') + DYLIB_ID=$(otool -D "$1" | awk 'NR>1') + if [ -z "$DYLIB_ID" ]; then + DIRECT_DEPS="$ALL_DYLIBS" + else + DIRECT_DEPS=$(echo "$ALL_DYLIBS" | grep -v "$DYLIB_ID") + fi + echo "$DIRECT_DEPS" \ + | awk '/\/usr\/local\//,/.dylib/ {print $1}' +} + +# lookup LC_LOAD_DYLIB commands in an obj file, +# then follow those loads and ask the same of each +# of its dylibs, recursively +get_env_specific_dependencies_recursive () { + while read -r obj; do + [ -z "$obj" ] && continue + echo "$obj" + get_env_specific_dependencies_recursive "$obj" + done < <(get_env_specific_direct_dependencies "$1") +} + +DEP_PATHS=$(get_env_specific_dependencies_recursive "$BINARY") + +mkdir -p "$LIB" +echo "$DEP_PATHS" \ +| xargs -n 1 -I'{}' cp {} "$LIB/" + +chmod +w "$LIB"/*.dylib + +while read -r obj; do + [ -z "$obj" ] && continue + OBJ_LEAF_NAME=$(echo "$obj" | awk -F'/' '{print $NF}') + # rewrite the install name of this obj file. completely optional. + # provides good default for future people who link to it. + install_name_tool -id "@rpath/$OBJ_LEAF_NAME" "$obj" + + # iterate over every LC_LOAD_DYLIB command in the objfile + while read -r load; do + [ -z "$load" ] && continue + LOAD_LEAF_NAME=$(echo "$load" | awk -F'/' '{print $NF}') + # rewrite a LC_LOAD_DYLIB command in this obj file + # to point relative to @rpath + install_name_tool -change "$load" "@rpath/$LOAD_LEAF_NAME" "$obj" + done < <(get_env_specific_direct_dependencies "$obj") +done < <(cat <(echo "$BINARY") <(echo "$DEP_PATHS" | awk -F'/' -v l="$LIB" -v OFS='/' '{print l,$NF}')) + +# define in our binary what it should expand the +# runtime search path @rpath to +install_name_tool -add_rpath "@loader_path/$LIBREL" "$BINARY"