summaryrefslogtreecommitdiff
path: root/tools/apbuild/relaytool
diff options
context:
space:
mode:
Diffstat (limited to 'tools/apbuild/relaytool')
-rwxr-xr-xtools/apbuild/relaytool584
1 files changed, 584 insertions, 0 deletions
diff --git a/tools/apbuild/relaytool b/tools/apbuild/relaytool
new file mode 100755
index 00000000..40b6de03
--- /dev/null
+++ b/tools/apbuild/relaytool
@@ -0,0 +1,584 @@
+#!/bin/bash
+
+# relaytool 1.2
+# Copyright 2004-2005 Mike Hearn <mike@plan99.net>
+# Copyright 2005 Vincent Béron <vberon@mecano.gme.usherb.ca>
+# Copyright 2006 Psyche <psyche@ruidoabsurdo.com>
+# Copyright 2007 Taj Morton <tajmorton@gmail.com>
+#
+#############################################################################
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+#############################################################################
+#
+# TODO:
+# - Figure out how to grab the GOT addr on PowerPC
+# - Port to more archs
+# - Figure out a way to check if we're on an ELF platform or not,
+# maybe check for _GLOBAL_OFFSET_TABLE_ ?
+
+using_partial_map=false
+using_minimal_list=false
+using_multilink=false
+outdir="."
+
+if [[ "$1" == "--version" ]]; then
+ echo "Relaytool 1.11"
+ echo "Copyright 2004 Mike Hearn"
+ echo "Copyright 2005 Vincent Béron"
+ echo
+ echo "See $0 for license details."
+ exit 1
+fi
+
+if [[ "$@" == "" ]] || [[ "$1" == "--help" ]] || [[ "$1" == "-h" ]]; then
+ echo "Relaytool will generate a file that can be used instead of linking"
+ echo "directly against a library, which will dlopen the DSO at init time"
+ echo
+ echo "Usage: relaytool [OPTION]... [LINKER COMMAND]..."
+ echo
+ echo "Options:"
+ echo " --relay LIB If a matching -lLIB is found, generate a file"
+ echo " that can be used instead of linking directly to"
+ echo " LIB. The name of the file is echoed on stdout."
+ echo " Multiple --relay can be used together, a file will"
+ echo " be generated for each matching ones."
+ echo " --replace-all-libs Generate a file for every -lLIB parameter."
+ echo " --minimal-list OBJ_LIST Will look in OBJ_LIST for undefined symbols, and"
+ echo " generate a file creating only the needed symbols"
+ echo " for each LIB."
+ echo " --partial-map MAP_FILE Generate a file creating only the symbols contained"
+ echo " in MAP_FILE. Will apply to all further -lLIB"
+ echo " parameters, so in general is not suitable to"
+ echo " multiple libs in the same invocation of relaytool."
+ echo " --no-replace Echo -lLIB on stdout even if a --relay LIB is"
+ echo " found, so it'll be linked in normally."
+ echo " --multilink [SONAMES...] If a library has different SONAMES on different"
+ echo " Linux distributions you can specify the various"
+ echo " SONAMES that it's known by here. Relaytool will"
+ echo " attempt to load them (in the order provided) until"
+ echo " one if found. This cannot be used with multiple"
+ echo " --relay options. The first SONAME in the list will"
+ echo " be used as the name in the _is_present variable and"
+ echo " _symbol_is_present function."
+ echo " --out-dir DIRECTORY Write stub file to DIRECTORY instead of CWD."
+ echo "Linker commands:"
+ echo " -LPATH Add PATH to the list of paths to search for LIBs."
+ echo " -lLIB If a matching --relay LIB is found (or if"
+ echo " --replace-all-libs is specified), generate a file"
+ echo " that can be used instead of linking directly to"
+ echo " LIB. If there's no --relay LIB, echo -lLIB to"
+ echo " stdout."
+ echo " All other linker commands are passed as is to stdout."
+ echo "Other commands:"
+ echo " --help|-h Print this help."
+ echo " --version Print version information."
+ exit 1
+fi
+
+function error() {
+ echo $@ >/dev/stderr
+ exit 1
+}
+
+function readfinallink() {
+ link_name="$1"
+ while [ -L "$link_name" ]; do
+ new_name=$( readlink "$link_name" )
+ if [ "${new_name:0:1}" == "/" ]; then
+ link_name="$new_name"
+ else
+ link_name="`dirname "$link_name"`/$new_name"
+ fi
+ done
+ if [ -f "$link_name" ]; then
+ echo -n "$link_name"
+ exit 0
+ else
+ exit 1
+ fi
+}
+
+function relay() {
+ lib="$1"
+ if $using_multilink; then
+ libname=$( echo $( basename ${multilinklist[0]} ) | sed 's/\.so.*//' | tr '-' '_' | tr '.' '_' )
+ else
+ libname=$( echo $( basename "$lib" ) | sed 's/\.so.*//' | tr '-' '_' | tr '.' '_' )
+ fi
+ soname=$( objdump -x "$lib" |grep SONAME | awk '{print $2}' )
+ outfile="$outdir/`basename "$soname"`.stub.c"
+
+ echo -n "$outfile"
+
+ if $using_partial_map; then
+ functions=$( grep "^F " "$partial_map" | cut -d' ' -f2 )
+ variables=$( grep "^V " "$partial_map" | cut -d' ' -f2 )
+ else
+ functions=$( nm --extern-only -D "$lib" | awk '{ if (($2 == "T") || ($2 == "W")) print $3; }' | LC_ALL=C grep -v '\(\<_init\>\|\<_fini\>\)' | LC_ALL=C sort -u )
+ variables=$( nm --extern-only -D "$lib" | awk '{ if (($2 == "D") || ($2 == "G") || ($2 == "B") || ($2 == "V")) print $3; }' | LC_ALL=C sort -u )
+ fi
+ if $using_minimal_list; then
+ functions="$functions
+$( nm `echo "$object_list"` | awk '{ if ($1 == "U") print $2; }' | LC_ALL=C sort -u )"
+ functions=$( echo "$functions" | LC_ALL=C sort | LC_ALL=C uniq -d )
+ variables="$variables
+$( nm `echo "$object_list"` | awk '{ if ($1 == "U") print $2; }' | LC_ALL=C sort -u )"
+ variables=$( echo "$variables" | LC_ALL=C sort | LC_ALL=C uniq -d )
+ fi
+
+ if [ "$functions" == "" ] && [ "$variables" == "" ]; then
+ # Nothing will be used, so do nothing for that lib
+ exit 1
+ fi
+
+ cat <<EOF >"$outfile"
+/* automatically generated: `date` by `id -un`@`uname -n`, do not edit
+ *
+ * Built by relaytool, a program for building delay-load jumptables
+ * relaytool is (C) 2004 Mike Hearn <mike@navi.cx>
+ * See http://autopackage.org/ for details.
+ */
+#include <dlfcn.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/mman.h>
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+static void **ptrs;
+static char *functions[] = {
+EOF
+
+ for s in $functions; do
+ echo " \"$s\"," >>"$outfile"
+ done
+ echo " 0" >>"$outfile"
+
+ cat <<EOF >>"$outfile"
+};
+
+static char *variables[] = {
+EOF
+
+ for s in $variables; do
+ echo " \"$s\"," >>"$outfile"
+ done
+ echo " 0" >>"$outfile"
+
+ cat <<EOF >>"$outfile"
+};
+
+/* 1 if present, 0 if not */
+int ${libname}_is_present = 0;
+
+static void *handle = 0;
+
+/* 1 if present, 0 if not, 0 with warning to stderr if lib not present or symbol not found */
+int ${libname}_symbol_is_present(char *s)
+{
+ int i;
+
+ if( !${libname}_is_present ) {
+ fprintf(stderr, "%s: relaytool: `basename "$lib"` not present so cannot check for symbol %s.\n", getenv("_"), s);
+ fprintf(stderr, "%s: relaytool: This probably indicates a bug in the application, please report.\n", getenv("_"));
+ return 0;
+ }
+
+ i = 0;
+ while (functions[i++]) if (!strcmp( functions[i - 1], s )) return ptrs[i - 1] > 0 ? 1 : 0;
+ i = 0;
+ while (variables[i++]) if (!strcmp( variables[i - 1], s )) return dlsym( handle, s ) > 0 ? 1 : 0;
+
+ fprintf( stderr, "%s: relaytool: %s is an unknown symbol in `basename "$lib"`.\n", getenv("_"), s );
+ fprintf( stderr, "%s: relaytool: If you are the developer of this program, please correct the symbol name or rerun relaytool.\n", getenv("_") );
+ return 0;
+}
+
+__attribute__((noreturn)) void _relaytool_stubcall_${libname}(int offset)
+{
+ fprintf( stderr, "%s: relaytool: stub call to `basename "${lib}"`:%s, aborting.\n", getenv("_"),
+ functions[offset / sizeof(void*)] );
+ exit( 1 );
+}
+
+#if defined( __i386__ )
+ #define FIXUP_GOT_RELOC(sym, addr) \\
+ asm("\tmovl %0, %%eax\n" \\
+ "\tmovl %%eax, " sym "@GOT(%%ebx)\n" : : "r" (addr));
+#elif defined( __powerpc__ )
+
+ /* The PowerPC ELF ABI is a twisted nightmare. Until I figure it out,
+ for now we don't support GOT fixup on this architecture */
+
+ #error Variables are not currently supported on PowerPC
+
+#elif defined( __x86_64__ )
+ #define FIXUP_GOT_RELOC(sym, addr) \\
+ asm("\tmovq %0, %%rax\n" \\
+ "\tmovq %%rax, " sym "@GOT(%%rbx)\n" : : "r" (addr));
+#else
+ #error Please define FIXUP_GOT_RELOC for your architecture
+#endif
+
+void __attribute__((constructor)) _relaytool_init_${libname}()
+{
+ int i = 0;
+
+ ptrs = malloc( sizeof(functions) );
+ memset( ptrs, 0, sizeof(functions) );
+EOF
+ if $using_multilink; then
+ echo -n "char *multilink_libs[${#multilinklist[@]}] = {" | cat >> "$outfile"
+ for l in ${multilinklist[@]}; do
+ echo -n "\"$l\"" | cat >> "$outfile";
+ if [[ "$l" != "${multilinklist[${#multilinklist[@]}-1]}" ]]; then
+ echo -n ", " | cat >> "$outfile";
+ else
+ echo "};" | cat >> "$outfile"
+ fi
+ done
+ echo 'int multilink_count=0;' | cat >> "$outfile"
+
+ echo 'while (!handle) {
+ handle = dlopen(multilink_libs[multilink_count++], RTLD_LAZY );' | cat >> "$outfile"
+ echo "if (multilink_count==${#multilinklist[@]}) break;}"| cat >> "$outfile"
+ else
+ echo "handle = dlopen( \"$soname\", RTLD_LAZY );" | cat >> "$outfile"
+ fi
+cat <<EOF >>"$outfile"
+ if (!handle) return;
+
+ ${libname}_is_present = 1;
+
+ /* build function jumptable */
+ while (functions[i++]) ptrs[i - 1] = dlsym( handle, functions[i - 1] );
+
+EOF
+
+ if [ "$variables" != "" ]; then echo " /* now fixup the global offset table for variable imports */" >>"$outfile"; fi
+ for s in $variables; do
+ echo " FIXUP_GOT_RELOC( \"$s\", dlsym(handle, \"$s\") );" >>"$outfile"
+ done
+
+ cat <<EOF >>"$outfile"
+}
+
+#if defined( __i386__ )
+
+#define JUMP_SLOT(name, index) \\
+ asm(".section .text." name ", \"ax\", @progbits\n" \\
+ ".globl " name "\n" \\
+ ".hidden " name "\n" \\
+ " .type " name ", @function\n" \\
+ name ":\n" \\
+ " movl ptrs, %eax\n" \\
+ " movl " #index "(%eax), %eax\n" \\
+ " test %eax, %eax\n" \\
+ " jnz JS" #index "\n" \\
+ " push \$" #index "\n" \\
+ " call _relaytool_stubcall_${libname}\n" \\
+ "JS" #index ": jmp *%eax\n");
+
+
+#elif defined( __x86_64__ )
+
+#define JUMP_SLOT(name, index) \\
+ asm(".section .text." name ", \"ax\", @progbits\n" \\
+ ".globl " name "\n" \\
+ ".hidden " name "\n" \\
+ " .type " name ", @function\n" \\
+ name ":\n" \\
+ " movq ptrs, %r11\n" \\
+ " movq " #index "(%r11), %r11\n" \\
+ " test %r11, %r11\n" \\
+ " jnz JS" #index "\n" \\
+ " push $" #index "\n" \\
+ " call _relaytool_stubcall_${libname}\n" \\
+ "JS" #index ": jmp *%r11\n");
+#elif defined( __powerpc__ )
+
+#define JUMP_SLOT(name, index) \ \
+ asm(".section .text." name ", \"ax\", @progbits\n" \\
+ ".globl " name "\n" \\
+ ".hidden " name "\n" \\
+ " .type " name ", @function\n" \\
+ name ":\n" \\
+ " lis r11, ptrs@ha\n" \\
+ " lwz r11, " #index "(r11)\n" \\
+ " cmpi cr0,r11,0\n" \\
+ " beq- 1f\n" \\
+ " mtctr r11\n" \\
+ " bctr\n" \\
+ "1: li r3, " #index "\n" \\
+ " b _relaytool_stubcall_${libname}\n" \\
+ );
+
+#else
+ #error Please define JUMP_SLOT for your architecture
+#endif
+
+/* define placeholders for the variable imports: their type doesn't matter,
+ however we must restrict ELF symbol scope to prevent the definition in the imported
+ shared library being bound to this dummy symbol (not all libs are compiled -Bsymbolic)
+ */
+EOF
+
+ for s in $variables; do
+ echo "int $s __attribute__(( visibility(\"hidden\") )) = -1;" >>"$outfile"
+ done
+
+ cat <<EOF >>"$outfile"
+
+/* define each jump slot in its own section. this increases generated code
+ size, but it means unused slots can be deleted by the linker when
+ --gc-sections is used.
+ */
+EOF
+
+# now generate the stubs
+ c=0
+ for s in $functions; do
+ echo "JUMP_SLOT(\"$s\", $[c * $arch_ptr_size]);" >>"$outfile"
+ (( c++ ))
+ done
+
+ echo >>"$outfile"
+
+ cat <<EOF >>"$outfile"
+
+#ifdef __cplusplus
+ }
+#endif
+EOF
+
+}
+
+function fakerelay() {
+ lib="$1"
+ libname=$( echo $( basename "$lib" ) | sed 's/\.so.*//' | tr '-' '_' | tr '.' '_' )
+ soname=$( objdump -x "$lib" |grep SONAME | awk '{print $2}' )
+ outfile="$outdir/`basename "$soname"`.stub.c"
+
+ echo -n "$outfile"
+
+ cat <<EOF >"$outfile"
+/* automatically generated: `date` by `id -un`@`uname -n`, do not edit
+ *
+ * Built by relaytool, a program for building delay-load jumptables
+ * relaytool is (C) 2004 Mike Hearn <mike@navi.cx>
+ * See http://autopackage.org/ for details.
+ */
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* 1 if present, 0 if not */
+int ${libname}_is_present = 1;
+
+/* 1 if present, 0 if not, 0 with warning to stderr if lib not present or symbol not found */
+int ${libname}_symbol_is_present(char * s)
+{
+ return 1;
+}
+
+#ifdef __cplusplus
+ }
+#endif
+EOF
+
+}
+
+no_replace=false
+replace_all=false
+arch_ok=false
+arch_ptr_size=0
+case `uname -m` in
+ i386 | i486 | i586 | i686 )
+ arch_ok=true
+ arch_ptr_size=4
+ ;;
+ x86_64)
+ arch_ok=true
+ arch_ptr_size=8
+ ;;
+esac
+
+searchpath=( "/usr/lib" "/usr/local/lib" "/lib" `pwd` )
+multilinklist=( )
+relaylist=( )
+
+# process arguments
+i=1
+while (( i <= $# )); do
+ a="${!i}"
+
+ if [ "${a:0:2}" == "-L" ]; then
+ searchpath[${#searchpath}]="${a:2}"
+ echo -n "$a " # copy to stdout
+
+ elif [ "$a" == "--replace-all-libs" ]; then
+ replace_all=true
+
+ elif [ "$a" == "--partial-map" ]; then
+ using_partial_map=true
+ (( i++ ))
+ partial_map="${!i}"
+
+ elif [ "$a" == "--minimal-list" ]; then
+ using_minimal_list=true
+ (( i++ ))
+ object_list="${!i}"
+
+ elif [ "$a" == "--no-replace" ]; then
+ no_replace=true
+
+ elif [ "$a" == "--multilink" ]; then
+ using_multilink=true
+ (( i++ ))
+ while [[ $i -lt $# && ${!i:0:2} != "--" ]]; do
+ multilinklist[${#multilinklist[@]}]="${!i}"
+ (( i++ ))
+ done
+ continue # $i has already been incremented, just continue with the loop
+
+ elif [ "$a" == "--relay" ]; then
+ (( i++ ))
+ relaylist[${#relaylist[@]}]="${!i}"
+
+ elif [ "$a" == "--out-dir" ]; then
+ (( i++ ))
+ outdir="${!i}"
+
+ elif [ "$a" == "-ldl" ]; then
+ # libdl won't ever be supported by relaytool, so just pass it to stdout
+ echo -n "$a "
+
+ elif [ "${a:0:2}" == "-l" ]; then
+ lib="${a:2}"
+
+ # is this lib meant to be relayed?
+ if $replace_all; then
+ found=true
+ else
+ found=false
+ for b in ${relaylist[@]}; do
+ if [ "$b" == "$lib" ]; then
+ found=true
+ fi
+ done
+ fi
+
+ if $found && $arch_ok; then
+
+ # yes, so let's find its absolute filename by checking in each search path directory
+ spfound=false
+ for d in ${searchpath[@]}; do
+ if [ -e "$d/lib$lib.so" ]; then
+
+ absname=$( readfinallink "$d/lib$lib.so" )
+ if [ $? != 0 ] || [ ! -f "$absname" ]; then
+ error broken symlink "$absname"
+ fi
+
+ stubfile=$( relay "$absname" )
+
+ # now we have to compile the stub
+ if [ $? == 0 ]; then
+ stubobj=$( echo "$stubfile" | sed 's/\.c$/\.o/' )
+ # remove -include flags from CFLAGS, if any
+ CFLAGS=$( echo $CFLAGS | sed 's/-include .*\.h//g' )
+ if [ -e /dev/tty ]; then
+ ${CC:-gcc} ${CFLAGS} -fPIC -c -o "$stubobj" "$stubfile" 2>/dev/tty
+ else
+ ${CC:-gcc} ${CFLAGS} -fPIC -c -o "$stubobj" "$stubfile"
+ fi
+ echo -n "$stubobj "
+ fi
+
+ if $no_replace; then
+ echo -n "-l$lib "
+ fi
+
+ spfound=true
+ break;
+ fi
+ done
+
+ if ! $spfound; then
+ error could not find "$lib" in search path
+ fi
+ elif $found && ! $arch_ok; then
+ # yes, so let's find its absolute filename by checking in each search path directory
+ spfound=false
+ for d in ${searchpath[@]}; do
+ if [ -e "$d/lib$lib.so" ]; then
+
+ absname=$( readfinallink "$d/lib$lib.so" )
+ if [ $? != 0 ] || [ ! -f "$absname" ]; then
+ error broken symlink "$absname"
+ fi
+
+ # Create a stub C source that just contains dummy
+ # libwhatever_... support functions
+ stubfile=$( fakerelay "$absname" )
+
+ # now we have to compile the stub
+ if [ $? == 0 ]; then
+ stubobj=$( echo "$stubfile" | sed 's/\.c$/\.o/' )
+ # remove -include flags from CFLAGS, if any
+ CFLAGS=$( echo $CFLAGS | sed 's/-include .*\.h//g' )
+ if [ -e /dev/tty ]; then
+ ${CC:-gcc} ${CFLAGS} -fPIC -c -o "$stubobj" "$stubfile" 2>/dev/tty
+ else
+ ${CC:-gcc} ${CFLAGS} -fPIC -c -o "$stubobj" "$stubfile"
+ fi
+ echo -n "$stubobj "
+ fi
+
+ if $no_replace; then
+ echo -n "-l$lib "
+ fi
+
+ spfound=true
+ break;
+ fi
+ done
+
+ if ! $spfound; then
+ error could not find "$lib" in search path
+ fi
+ else
+ echo -n "$a "
+ fi
+
+ else
+ # just copy whatever we don't recognise
+ echo -n "$a "
+ fi
+
+ (( i++ ))
+done
+echo