|
#!/bin/bash
|
|
#
|
|
# --- NO-ROCK-COPYRIGHT-NOTE ---
|
|
#
|
|
# Found in the debian boot-floppies source package from:
|
|
# http://www.kernel.org/debian/dists/potato/main/source/admin/
|
|
#
|
|
# mklibs.sh: An automated way to create a minimal /lib/ directory.
|
|
#
|
|
# Copyright 1999 by Marcus Brinkmann <Marcus.Brinkmann@ruhr-uni-bochum.de>
|
|
#
|
|
# 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
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
# Introduction:
|
|
# When creating boot floppies, there is never enough room on the disks.
|
|
# So it is important not to waste bytes on unnecessary code.
|
|
# Shared Libraries contain many functions that are probably not used in the
|
|
# binaries included in the boot disks, and copying the whole library is a
|
|
# waste of space.
|
|
# This utilitiy helps to reduce the necessary libraries to only include the
|
|
# symbols needed to run a given set of executables.
|
|
#
|
|
# Features:
|
|
# * Automatic detection of all necessary libraries, even for inter-library
|
|
# dependencies, for a given set of executables.
|
|
# * Automatic installation of all needed libraries and soname links.
|
|
# * Automatic reduction of all libraries to minimal size for which PIC
|
|
# libraries are provided.
|
|
#
|
|
# Requirements:
|
|
# * Beside the shared libraries, you need libfoo_pic.a files for all
|
|
# libraries you want to reduce.
|
|
# * You need binutils (notably objdump and objcopy) installed.
|
|
|
|
# A GENERAL NOTE ABOUT LANGUAGE ABUSE
|
|
#
|
|
# If you believe this program had better not been written in shell script
|
|
# language, I invite you to reimplement it in the language of your
|
|
# preference.
|
|
# The reasons I chose shell are:
|
|
# * Shell scripts are very portable and available even on minimal systems as
|
|
# well as boot disks.
|
|
# * Shell scripts can be run without compilation.
|
|
# * Shell scripts provide a very easy interface to the various system
|
|
# commands I need to get the library dependencies and sort through them.
|
|
# Perl is lacking good data types, so implementing this would be equally
|
|
# cumbersome in perl (I need trees and queues, for example).
|
|
# C and C++ are lacking easy access to the system commands.
|
|
#
|
|
# Of course, shell scripting has many problems:
|
|
# * Use of temporary files for string arrays.
|
|
# * Slow.
|
|
# * Hard to debug.
|
|
#
|
|
# I think for hand written code, I hit a limit with the size and execution
|
|
# time of this program of what is still acceptable as a shell script. I also
|
|
# tried to improve the situation with many comments.
|
|
|
|
# TODO:
|
|
# * Make sure versioned symbols get correct version number.
|
|
# This seems to work now, however, we always include
|
|
# all versions of a symbol. This is not a problem. To do
|
|
# it properly, we had to parse the version information in
|
|
# objdump, which is hard.
|
|
# * Use --dynamic-syms on so lib instead --syms on pic file.
|
|
# * Autodetect that libc needs ld (should be possible from
|
|
# output of objdump --privat-headers| grep NEEDD).
|
|
# * Code to create libs in cycles !!!
|
|
|
|
# HISTORY:
|
|
#
|
|
# 1999-09-13 Marcus Brinkmann <brinkmd@debian.org>
|
|
#
|
|
# * Initial release (v0.1).
|
|
#
|
|
|
|
# STATIC DATA SECTION
|
|
#
|
|
|
|
usage="Usage: $0 [OPTION]... -d DEST FILE ..."
|
|
try="Try "\`"$0 --help' for more information"
|
|
version="$0 0.1, Copyright 1999 Marcus Brinkmann"
|
|
|
|
PATH=/bin:/usr/bin
|
|
|
|
default_src_path=/lib:/usr/lib
|
|
dest=
|
|
exec=
|
|
action=
|
|
verbose="false"
|
|
|
|
gcc=${GCC-gcc}
|
|
objdump=${OBJDUMP-objdump}
|
|
objcopy=${OBJCOPY-objcopy}
|
|
|
|
# =================
|
|
# GRAPH ABSTRACTION
|
|
# =================
|
|
#
|
|
# Because we do some hairy graph operations, we provide some
|
|
# abstractions of them. Some functions here are very simple, but
|
|
# the source is much more readable this way.
|
|
|
|
# check-node NODE ...
|
|
# checks if all NODEs are valid node names.
|
|
# Used internally for verificaton only.
|
|
# Return 0 if all NODEs are valid.
|
|
# Currently, a node is valid if it does not contain a space.
|
|
|
|
check-node () {
|
|
local node
|
|
for node in "$@" ; do
|
|
if [ "x`echo $node | sed -e '/ /d'`" = x ] ; then
|
|
echo 1>&2 $0: check-node: invalid node \"$node\"
|
|
exit 1
|
|
fi
|
|
done
|
|
return 0
|
|
}
|
|
|
|
# is-graph FILE ...
|
|
# provides a very simple type assertion
|
|
# Turns FILE into a graph if it isn't already and returns 0.
|
|
|
|
is-graph () {
|
|
local file
|
|
for file in "$@" ; do
|
|
if [ ! -e "$file" ] ; then
|
|
touch "$qfile"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# add-node FILE NODE
|
|
# add a node NODE to graph FILE.
|
|
# This is useful if you need to make sure that a node appears
|
|
# in the graph without actually connecting it to an arrow.
|
|
# You don't need to add nodes that are part of an arrow.
|
|
|
|
add-node () {
|
|
if [ $# != 2 ] ; then
|
|
echo 1>&2 $0: add-node: internal error: called with invalid number of arguments
|
|
exit 1
|
|
fi
|
|
check-node "$2"
|
|
echo "$2 $2" >> "$1"
|
|
return 0
|
|
}
|
|
|
|
# add-arrow FILE NODE1 NODE2
|
|
# add an arrow from NODE1 to NODE2 to graph FILE.
|
|
|
|
add-arrow () {
|
|
if [ $# != 3 ] ; then
|
|
echo 1>&2 $0: add-arrow: internal error: called with invalid number of arguments
|
|
exit 1
|
|
fi
|
|
check-node "$2" "$3"
|
|
echo "$2 $3" >> "$1"
|
|
return 0
|
|
}
|
|
|
|
# find-cycle FILE
|
|
# finds a cycle in a graph FILE.
|
|
# If a cycle is found, it is printed out at stdin, one node each line,
|
|
# and 0 is returned. Otherwise, nothing is printed on stdout and exit
|
|
# status is 1.
|
|
|
|
find-cycle () {
|
|
if [ $# != 1 ] ; then
|
|
echo 1>&2 $0: find-cycle: internal error: called with invalid number of arguments
|
|
exit 1
|
|
fi
|
|
tsort "$1" 2> "$fl_dir/find-cycle" > /dev/null
|
|
if [ "x`cat $fl_dir/find-cycle`" = x ] ; then
|
|
return 1
|
|
else
|
|
if [ "x`head -n 1 $fl_dir/find-cycle`" != "xtsort: cycle in data" ] ; then
|
|
echo 1>&2 $0: find-cycle: internal error: tsort has invalid output format
|
|
exit 1
|
|
fi
|
|
cat "$fl_dir/find-cycle" | sed -e '1d' -e '/tsort: cycle in data/,$d' -e 's/^tsort: //'
|
|
fi
|
|
}
|
|
|
|
# shrink-nodes FILE NODE1 ...
|
|
# shrinks several nodes NODE1 ... to a single node in graph FILE.
|
|
# To hide cycles, we treat a cycle as a single node and replace
|
|
# each occurence of a node in the cycle with a new node
|
|
# [NODE1,...] . This change is destructive and can not be undone!
|
|
# (You would need to store the entry point to the cycle for each arrow
|
|
# pointing to/from it).
|
|
# This function does not check if the the nodes NODE1 ... exist.
|
|
# However, if none of these nodes exists already, the new node will
|
|
# not appear either. This makes this function sort of idem potent.
|
|
# It does not check if NODE1 ... are a cycle. We will assume this
|
|
# later in the library dependency analysis, but nothing in the code
|
|
# relies on it.
|
|
# Always shrink all cycles, or you may get unresolved symbols.
|
|
#
|
|
# Example:
|
|
# N1 ---> N2 N1 -------> /------------\
|
|
# | "shrink-nodes N2 N4" | _ | [N2,N4] |
|
|
# v -------------------> v _____/| \------------/
|
|
# N3 ---> N4 N3 /
|
|
|
|
# A small helper function will aid us...
|
|
# equal-match STRING STRING1 ...
|
|
# return 0 if STRING is among STRING1 ..., 1 otherwise.
|
|
equal-match () {
|
|
local string
|
|
local stringk
|
|
string="$1"
|
|
shift
|
|
for stringk in "$@" ; do
|
|
if [ "x$string" = "x$stringk" ] ; then
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
shrink-nodes () {
|
|
local head
|
|
local lnode
|
|
local rnode
|
|
local graph="$1"
|
|
shift
|
|
is-graph "$graph"
|
|
check-node "$@"
|
|
local cnode="[`echo "$@" | sed 's/ /,/g'`]"
|
|
# Okay, it's a hack. We treat the graph as a queue. I am just too
|
|
# lazy to copy the relevant code here. Of course, we exploit several
|
|
# properties of the graph and queue file format here (for example,
|
|
# that graphs never can contain a QUEUE_SEPERATOR, and that a graph is
|
|
# really a simple file with "a b" entries).
|
|
cat /dev/null > "$fl_dir/shrink-cycle"
|
|
while head=`get-top-of-queue "$graph"` ; do
|
|
lnode=`echo $head|sed 's/ [^ ]*$//'`
|
|
if equal-match "$lnode" "$@" ; then
|
|
lnode="$cnode"
|
|
fi
|
|
rnode=`echo $head|sed 's/^[^ ]* //'`
|
|
if equal-match "$rnode" "$@" ; then
|
|
rnode="$cnode"
|
|
fi
|
|
echo "$lnode $rnode" >> "$fl_dir/shrink-cycle"
|
|
done
|
|
cat "$fl_dir/shrink-cycle" | sort -u > "$graph"
|
|
}
|
|
|
|
# =================
|
|
# QUEUE ABSTRACTION
|
|
# =================
|
|
#
|
|
# I added an abstract interface for queues to make the code more readable.
|
|
# Queue operations usually consist of several atomic file operations, which
|
|
# can get quite messy.
|
|
#
|
|
# You can use queues to simply loop through all lines of a file, but you
|
|
# also can add stuff to the queue while processing it.
|
|
#
|
|
# Implementation: All queues consist of a QUEUE_FILE which has two parts:
|
|
# the remaining entries in the queue (QUEUE) and the already processed
|
|
# entries (BUCKET).
|
|
# The two parts are seperated by a line containing only QUEUE_SEPERATOR.
|
|
|
|
QUEUE_SEPERATOR=SEPERATOR___ABOVE_IS_QUEUE__BELOW_IS_BUCKET___SEPERATOR
|
|
|
|
# check-queue-entry QENTRY ...
|
|
# checks if all queue entries QENTRY are valid.
|
|
# Used internally for verificaton only.
|
|
# Return 0 if all QENTRYs are valid.
|
|
# Currently, a node is valid if it does not match the QUEUE_SEPERATOR.
|
|
|
|
check-queue-entry () {
|
|
local qentry
|
|
for qentry in "$@" ; do
|
|
if [ "x`echo $qentry | sed "/^$QUEUE_SEPERATOR$/d"`" = x ] ; then
|
|
echo 1>&2 $0: check-queue-entry: invalid qentry name \"$qentry\"
|
|
exit 1
|
|
fi
|
|
done
|
|
return 0
|
|
}
|
|
|
|
# is-queue QUEUE_FILE ...
|
|
# provides a very simple type assertion
|
|
# Turns QUEUE_FILE into a queue if it isn't already and returns 0.
|
|
|
|
is-queue () {
|
|
local qfile
|
|
for qfile in "$@" ; do
|
|
if [ ! -e "$qfile" ] ; then
|
|
echo "$QUEUE_SEPERATOR" > "$qfile"
|
|
else
|
|
if ! grep -q "^$QUEUE_SEPERATOR$" "$qfile" ; then
|
|
echo "$QUEUE_SEPERATOR" >> "$qfile";
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
# get-top-of-queue QUEUE_FILE
|
|
# processes a queue one more time.
|
|
# If QUEUE of QUEUE_FILE is empty, exit status is 1 and no output is given.
|
|
# Otherwise, top of QUEUE is removed, returned on stdout and
|
|
# appended to the end of the BUCKET part of QUEUE_FILE.
|
|
|
|
get-top-of-queue () {
|
|
if [ $# != 1 ] ; then
|
|
echo 1>&2 $0: get-top-of-queue: internal error: called with invalid number of arguments
|
|
exit 1
|
|
fi
|
|
is-queue "$1"
|
|
local head=`head -n 1 "$1"`
|
|
if [ "x$head" = "x$QUEUE_SEPERATOR" ] ; then
|
|
return 1
|
|
else
|
|
sed -e 1d "$1" > "$fl_dir/get-top-of-queue"
|
|
echo "$head" | tee --append "$fl_dir/get-top-of-queue"
|
|
cat "$fl_dir/get-top-of-queue" > "$1"
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
# add-to-queue-if-not-there QUEUE_FILE QENTRY ...
|
|
# add queue entries QENTRY ... to the beginning of the
|
|
# QUEUE of QUEUE_FILE if it is neither in QUEUE nor in BUCKET
|
|
# of QUEUE_FILE.
|
|
# Return with exit status 0.
|
|
# Note: If you want to add QENTRY to the *end* of QUEUE, you would do
|
|
# something like the following:
|
|
# sed -e s/^$QUEUE_SEPERATOR$/$head"'\
|
|
# '"$QUEUE_SEPERATOR/"
|
|
# which is necessary to pass the newline to sed. I think we can take the
|
|
# easy way out.
|
|
|
|
add-to-queue-if-not-there () {
|
|
local qentry
|
|
local qfile="$1"
|
|
shift
|
|
check-queue-entry "$@"
|
|
is-queue "$qfile"
|
|
for qentry in "$@" ; do
|
|
if ! grep -q "^$qentry\$" "$qfile" ; then
|
|
echo "$qentry" > "$fl_dir/add-to-queue-if-not-there"
|
|
cat "$qfile" >> "$fl_dir/add-to-queue-if-not-there"
|
|
cat "$fl_dir/add-to-queue-if-not-there" > "$qfile"
|
|
fi
|
|
done
|
|
return 0
|
|
}
|
|
|
|
# ==================
|
|
# LIBRARY PROCESSING
|
|
# ==================
|
|
#
|
|
# The following helper functions mess around with the actual
|
|
# processing and installation of libraries.
|
|
#
|
|
|
|
# get-library-depends OBJ1 ...
|
|
# get all libraries the objects OBJ1 ... depend on.
|
|
# OBJs can be binaries or shared libraries.
|
|
# The list is neither sort'ed nor uniq'ed.
|
|
|
|
get-library-depends () {
|
|
if [ $# = 0 ] ; then
|
|
echo 1>&2 $0: get-library-depends: internal error: no arguments
|
|
exit 1
|
|
fi
|
|
$objdump --private-headers "$@" 2> /dev/null \
|
|
| sed -n 's/^ *NEEDED *\([^ ]*\)$/\1/p'
|
|
}
|
|
|
|
# get-undefined-symbols OBJ1 ...
|
|
# get all unresolved symbols in OBJ1 ...
|
|
# The list is neither sort'ed nor uniq'ed.
|
|
|
|
get-undefined-symbols () {
|
|
if [ $# = 0 ] ; then
|
|
echo 1>&2 $0: get-undefined-symbols: internal error: no arguments
|
|
exit 1
|
|
fi
|
|
# ash has undefined reference to sys_siglist if .bss is not mentioned
|
|
# here. Reported by Joel Klecker.
|
|
# All symbols are epxosed, so we just catch all. Suggested by Roland
|
|
# McGrath. Another thing to try is to investigate --dynamic-reloc.
|
|
$objdump --dynamic-syms "$@" 2> /dev/null \
|
|
| sed -n 's/^.* \([^ ]*\)$/\1/p'
|
|
# | sed -n 's/^.*[\*UND\*|.bss].* \([^ ]*\)$/\1/p'
|
|
}
|
|
|
|
# get-provided-symbols LIB1 LIB2 ...
|
|
# get all symbols available from libraries LIB1 ... .
|
|
# Does only work for pic libraries.
|
|
#
|
|
# v Watch the tab stop here.
|
|
# 00000000 w F .text 00000000 syscall_device_write_request
|
|
# 00000000 g F .text 0000056c __strtoq_internal
|
|
|
|
get-provided-symbols () {
|
|
if [ $# = 0 ] ; then
|
|
echo 1>&2 $0: get-provided-symbols: internal error: no arguments
|
|
exit 1
|
|
fi
|
|
$objdump --syms "$@" 2>/dev/null | grep -v '\*UND\*' \
|
|
| sed -n 's/^[0-9a-f]\+ \(g \| w\) .. .* [0-9a-f]\+ \(0x8[08]\)\? *\([^ ]*\)$/\3/p'
|
|
}
|
|
|
|
# Crude hack (?) only used for diagnostic.
|
|
|
|
get-provided-symbols-of-so-lib () {
|
|
if [ $# = 0 ] ; then
|
|
echo 1>&2 $0: get-provided-symbols: internal error: no arguments
|
|
exit 1
|
|
fi
|
|
$objdump --dynamic-syms "$@" 2>/dev/null \
|
|
| sed -e '/\*UND\*/d' | sed -n 's/^.* \([^ ]*\)$/\1/p'
|
|
}
|
|
|
|
# get-common-symbols FILE1 FILE2
|
|
# returns a list of all symbols in FILE1 that appear also in FILE2
|
|
# Note: When get-common-symbols returns, FILE1 and FILE2 are "sort -u"'ed.
|
|
# Note: Version Information in FILE1 is ignored when comparing.
|
|
|
|
get-common-symbols () {
|
|
if [ $# != 2 ] ; then
|
|
echo 1>&2 $0: get-common-symbols: internal error: called with invalid number of arguments
|
|
exit 1
|
|
fi
|
|
# Not needed anymore, but we go for compatibility.
|
|
# (Somewhere we HAVE to clean FILE2 up).
|
|
sort -u "$1" > $fl_dir/get-common-symbols
|
|
cat $fl_dir/get-common-symbols > "$1"
|
|
sort -u "$2" > $fl_dir/get-common-symbols
|
|
cat $fl_dir/get-common-symbols > "$2"
|
|
|
|
local symbol=
|
|
while symbol=`get-top-of-queue $fl_dir/get-common-symbols` ; do
|
|
grep ^$symbol\$\\\|^$symbol@ "$1"
|
|
done
|
|
}
|
|
|
|
# create-link TARGET LINK_NAME
|
|
# creates a soft link if there isn't one already.
|
|
|
|
create-link () {
|
|
if [ $# != 2 ] ; then
|
|
echo 1>&2 $0: create-link: internal error: called with invalid number of arguments
|
|
exit 1
|
|
fi
|
|
if [ ! -e "$2" ] ; then
|
|
$action ln -s "$1" "$2"
|
|
fi
|
|
}
|
|
|
|
# find-file PATH FILE
|
|
# search all directories in PATH for file FILE, return absolute path
|
|
# FILE can be a relative path and a filename.
|
|
# PATH is a list, seperator is ':'.
|
|
|
|
find-file () {
|
|
if [ $# != 2 ] ; then
|
|
echo 1>&2 $0: find-file: internal error: exactly two arguments required
|
|
exit 1
|
|
fi
|
|
local path=$1
|
|
local dir=`echo $path | sed -e 's/:.*$//'`
|
|
until [ "x$path" = x ] ; do
|
|
if [ "x$dir" != x ] ; then
|
|
if [ -e "$dir/$2" ] ; then
|
|
echo "$dir/$2"
|
|
return 0
|
|
fi
|
|
fi
|
|
path=`echo $path | sed -e 's/^[^:]*:*//'`
|
|
dir=`echo $path | sed -e 's/:.*$//'`
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# find-files PATH FILE1 FILE2 ...
|
|
# search all directories in PATH for file FILE1, FILE2...
|
|
# FILE can be a relative path and a filename.
|
|
# PATH is a list, seperator is ':'.
|
|
# Return value is a white space seperated list of absolute filenames.
|
|
|
|
find-files () {
|
|
if [ $# -lt 2 ] ; then
|
|
echo 1>&2 $0: find-files: internal error: too few arguments
|
|
exit 1
|
|
fi
|
|
local path="$1" ; shift
|
|
while [ $# != 0 ] ; do
|
|
find-file $path $1
|
|
shift
|
|
done
|
|
}
|
|
|
|
# get-pic-file LIB
|
|
# returns the filename of the pic archive for LIB.
|
|
# Note: There doesn't seem to be any convention, *ick*.
|
|
|
|
get-pic-file () {
|
|
if [ $# != 1 ] ; then
|
|
echo 1>&2 $0: get-pic-file: internal error: called with invalid number of arguments
|
|
exit 1
|
|
fi
|
|
if [ "x$1" = "xlibc-2.0.7.so" ] ; then
|
|
# Order does matter! First init, then lib, then fini!
|
|
echo `find-files $src_path libc_pic/soinit.so libc_pic.a libc_pic/sofini.so`
|
|
return 0
|
|
fi
|
|
if [ "x$1" = "xlibc-2.1.2.so" -o "x$1" = "xlibc-2.1.3.so" \
|
|
-o "x$1" = "xlibc-2.2.so" -o "x$1" = "xlibc-2.2.1.so" \
|
|
-o "x$1" = "xlibc-2.2.2.so" ] ; then
|
|
# Order does matter! First init, then lib, then fini!
|
|
echo `find-files $src_path libc_pic/soinit.o libc_pic.a libc_pic/sofini.o libc_pic/interp.o`
|
|
return 0
|
|
fi
|
|
if [ "x$1" = "xlibm-2.1.2.so" -o "x$1" = "xlibm-2.1.3.so" \
|
|
-o "x$1" = "xlibm-2.2.so" -o "x$1" = "xlibm-2.2.1.so" \
|
|
-o "x$1" = "xlibm-2.2.2.so" ] ; then
|
|
echo `find-file "$src_path" libm_pic.a`
|
|
return 0
|
|
fi
|
|
if [ "x$1" = "xlibslang.so.1.3.9" ] ; then
|
|
echo `find-file $src_path libslang1.3.9_pic.a`
|
|
return 0
|
|
fi
|
|
if [ "x$1" = "xlibslang.so.1.4.1" ] ; then
|
|
echo `find-file $src_path libslang1.4.1_pic.a`
|
|
return 0
|
|
fi
|
|
local libname=`echo $1 | sed -e 's/^lib\(.*\).so.*/\1/'`
|
|
echo `find-file "$src_path" lib${libname}_pic.a`
|
|
return 0
|
|
}
|
|
|
|
get-extra-flags () {
|
|
if [ $# != 1 ] ; then
|
|
echo 1>&2 $0: get-extra-flags: internal error: called with invalid number of arguments
|
|
exit 1
|
|
fi
|
|
if [ "x$1" = "xlibc-2.0.7.so" ] ; then
|
|
echo `find-file $src_path ld-2.0.7.so` -lgcc
|
|
return 0
|
|
fi
|
|
if [ "x$1" = "xlibc-2.1.2.so" ] ; then
|
|
echo "`find-file $src_path ld-2.1.2.so` -lgcc -Wl,--version-script=`find-file $src_path libc_pic.map`"
|
|
return 0
|
|
fi
|
|
if [ "x$1" = "xlibm-2.1.2.so" -o "x$1" = "xlibm-2.1.3.so" ] ; then
|
|
echo "-Wl,--version-script=`find-file $src_path libm_pic.map`"
|
|
return 0
|
|
fi
|
|
if [ "x$1" = "xlibc-2.1.3.so" ] ; then
|
|
echo "`find-file $src_path ld-2.1.3.so` -lgcc -Wl,--version-script=`find-file $src_path libc_pic.map`"
|
|
return 0
|
|
fi
|
|
if [ "x$1" = "xlibc-2.2.so" ] ; then
|
|
echo "`find-file $src_path ld-2.2.so` -lgcc -Wl,--version-script=`find-file $src_path libc_pic.map`"
|
|
return 0
|
|
fi
|
|
if [ "x$1" = "xlibc-2.2.1.so" ] ; then
|
|
echo "`find-file $src_path ld-2.2.1.so` -lgcc -Wl,--version-script=`find-file $src_path libc_pic.map`"
|
|
return 0
|
|
fi
|
|
if [ "x$1" = "xlibc-2.2.2.so" ] ; then
|
|
echo "`find-file $src_path ld-2.2.2.so` -lgcc -Wl,--version-script=`find-file $src_path libc_pic.map`"
|
|
return 0
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# install-small-lib LIB_SONAME
|
|
# makes a small version of library LIB_SONAME
|
|
#
|
|
# This happens the following way:
|
|
# 0. Make exception for the linker ld.
|
|
# 1. Try to figure out complete path of pic library.
|
|
# 2. If no found, copy the shared library, else:
|
|
# a. Get shared libraries this lib depends on, transform into a
|
|
# list of "-lfoo" options.
|
|
# b. Get a list of symbols both provided by the lib and in the undefined
|
|
# symbols list.
|
|
# c. Make the library, strip it.
|
|
# d. Add symbols that are still undefined to the undefined symbols list.
|
|
# e. Put library into place.
|
|
|
|
install-small-lib () {
|
|
if [ $# != 1 ] ; then
|
|
echo 1>&2 $0: install-small-lib: internal error: called with invalid number of arguments
|
|
exit 1
|
|
fi
|
|
local src_file=`find-file $src_path $1`
|
|
if `echo "$1" | grep -q ^ld` ; then
|
|
get-provided-symbols "$src_file" >> $fl_dir/provided-symbols
|
|
$action $objcopy --strip-unneeded -R .note -R .comment "$src_file" "$dest/$1"
|
|
return 0
|
|
fi
|
|
local pic_objects=`get-pic-file "$1"`
|
|
local extra_flags=`get-extra-flags "$1"`
|
|
local architecture=`dpkg --print-architecture`
|
|
# some arm bins or libs are improperly linked, force -lgcc
|
|
if [ "$architecture" = arm ]; then
|
|
extra_flags="$extra_flags -lgcc"
|
|
fi
|
|
if [ "x$pic_objects" = x ] ; then
|
|
$verbose 2>&1 No pic archive for library "$1" found, falling back to simple copy.
|
|
get-provided-symbols-of-so-lib "$src_file" >> $fl_dir/provided-symbols
|
|
get-undefined-symbols "$src_file" >> $fl_dir/undefined-symbols
|
|
$action $objcopy --strip-unneeded -R .note -R .comment "$src_file" "$dest/$1"
|
|
else
|
|
$verbose 2>&1 Make small lib from "$pic_objects" in "$dest/$1".
|
|
|
|
# XXX: If ld is NEEDED, we need to include it on the gcc command line
|
|
get-library-depends "$src_file" \
|
|
| sed -n -e 's/^lib\(.*\)\.so.*$/\1/p' > $fl_dir/lib-dependencies
|
|
get-provided-symbols $pic_objects > $fl_dir/lib-provided-symbols
|
|
# Argument order does matter:
|
|
get-common-symbols $fl_dir/lib-provided-symbols \
|
|
$fl_dir/undefined-symbols > $fl_dir/lib-symbols-to-include
|
|
|
|
${gcc} \
|
|
-nostdlib -nostartfiles -shared \
|
|
"-Wl,-soname=$1" \
|
|
`cat $fl_dir/lib-symbols-to-include | sed 's/^/-u/'` \
|
|
-o $fl_dir/lib-so \
|
|
$pic_objects $extra_flags \
|
|
"-L$dest" \
|
|
-L`echo $src_path | sed -e 's/::*/:/g' -e 's/^://' -e 's/:$//' \
|
|
-e 's/:/ -L/g'` \
|
|
`cat $fl_dir/lib-dependencies | sed 's/^/-l/'` \
|
|
&& $objcopy --strip-unneeded -R .note -R .comment $fl_dir/lib-so $fl_dir/lib-so-stripped \
|
|
|| {
|
|
echo 1>&2 $0: install-small-lib: $gcc or $objcopy failed.
|
|
exit 1
|
|
}
|
|
get-undefined-symbols $fl_dir/lib-so-stripped \
|
|
>> $fl_dir/undefined-symbols
|
|
get-provided-symbols-of-so-lib $fl_dir/lib-so-stripped >> $fl_dir/provided-symbols
|
|
$action cp $fl_dir/lib-so-stripped "$dest/$1"
|
|
fi
|
|
}
|
|
|
|
# install-libs-in-sphere [LIB1,...]
|
|
# extracts the libs in a shrinked node and cycles through them until all
|
|
# possible symbols are resolved.
|
|
# Always make sure this can be called recursively (from install-libs)!
|
|
|
|
install-libs-in-sphere () {
|
|
if [ $# != 1 ] ; then
|
|
echo 1>&2 $0: install-libs-in-sphere: internal error: called with invalid number of arguments
|
|
exit 1
|
|
fi
|
|
# Unfortunately, we need a small parser here to do the right thing when
|
|
# spheres are within spheres etc. RegEx simply can't count brackets. :(
|
|
local string=`echo "$1" | sed -e 's/^\[//' -e 's/\]$//'`
|
|
local char
|
|
local result=
|
|
local depth=0
|
|
while [ "x$string" != x ] ; do
|
|
# Jump to next special char for faster operation.
|
|
# Don't be confused by the regex, it matches everything but ],[
|
|
char=`echo $string | sed -e 's/^\([^],[]*\).*$/\1/'`
|
|
string=`echo $string | sed -e 's/^[^],[]*//'`
|
|
result="$result$char";
|
|
# Read special char
|
|
char=`echo $string | sed -e 's/^\(.\).*$/\1/'`
|
|
string=`echo $string | sed -e 's/^.//'`
|
|
case "$char" in
|
|
[) depth=$(($depth+1));;
|
|
]) depth=$(($depth-1));;
|
|
,) if [ $depth = 0 ] ; then
|
|
char=' ';
|
|
fi;;
|
|
esac
|
|
result="$result$char";
|
|
done
|
|
$verbose 2>&1 "RESOLVING LOOP...`echo $result | md5sum`"
|
|
echo XXX: CODE NOT FINISHED
|
|
install-libs $result
|
|
$verbose 2>&1 "END OF LOOP... `echo $result | md5sum`"
|
|
}
|
|
|
|
# install-libs LIB1 ...
|
|
# goes through an ordered list of libraries and installs them.
|
|
# Make sure this can be called recursively, or hell breaks loose.
|
|
# Note that the code is (almost) tail-recursive. I wish I could
|
|
# write this in Scheme ;)
|
|
|
|
install-libs () {
|
|
local cur_lib
|
|
local lib
|
|
for cur_lib in "$@" ; do
|
|
if echo "$cur_lib" | grep -q '^\[' ; then
|
|
install-libs-in-sphere "$cur_lib"
|
|
else
|
|
lib=`find-file $src_path $cur_lib`
|
|
if [ -L "$lib" ] ; then
|
|
lib=`basename \`readlink $lib\``
|
|
create-link $lib $dest/$cur_lib
|
|
else
|
|
install-small-lib $cur_lib
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
#
|
|
# MAIN PROGRAM
|
|
#
|
|
# 1. Option Processing
|
|
# 2. Data Initialization
|
|
# 3. Graph Construction and Reduction
|
|
# 4. Library Installation
|
|
|
|
# Global Files:
|
|
# $fl_dir/undefined-symbols
|
|
# Holds all undefined symbols we consider for inclusion.
|
|
# Only grows. Does not to be sort'ed and uniq'ed, but will
|
|
# get occasionally.
|
|
# $fl_dir/provided-symbols
|
|
# Holds all defined symbols we included.
|
|
# Only grows. Should later be a superset of undefined-symbols.
|
|
# But some weak symbols may be missing!
|
|
# $fl_dir/library-depends
|
|
# Queue of all libraries to consider.
|
|
|
|
#
|
|
# 1. Option Processing
|
|
#
|
|
|
|
while :; do
|
|
case "$1" in
|
|
-L) src_path="$src_path:$2"; shift 2;;
|
|
-d|--dest-dir) dest=$2; shift 2;;
|
|
-n|--dry-run) action="echo"; shift;;
|
|
-v|--verbose) verbose="echo"; shift;;
|
|
-V|--version) echo "$version"; exit 1;;
|
|
-h|--help)
|
|
echo "$usage"
|
|
echo "Make a set of minimal libraries for FILE ... in directory DEST."
|
|
echo ''
|
|
echo "\
|
|
Options:
|
|
-L DIRECTORY Add DIRECTORY to library search path.
|
|
-n, --dry-run Don't actually run any commands; just print them.
|
|
-v, --verbose Print additional progress information.
|
|
-V, --version Print the version number and exit.
|
|
-h, --help Print this help and exit.
|
|
|
|
-d, --dest-dir DIRECTORY Create libraries in DIRECTORY.
|
|
|
|
Required arguments for long options are also mandatory for the short options."
|
|
exit 0;;
|
|
-*) echo 1>&2 $0: $1: unknown flag; echo 1>&2 "$usage"; echo 1>&2 "$try"; exit 1;;
|
|
?*) exec="$exec $1"; shift;;
|
|
*) break;;
|
|
esac
|
|
done
|
|
|
|
src_path=${src_path-$default_src_path}
|
|
|
|
if [ "x$exec" = x ] ; then
|
|
exit 0
|
|
fi
|
|
if [ "x$dest" = x ] ; then
|
|
echo 1>&2 $0: no destination directory given; echo 1>&2 "$usage"; exit 1
|
|
fi
|
|
|
|
|
|
#
|
|
# 2. Data Initialization
|
|
#
|
|
|
|
$verbose -n 2>&1 "Initializing data objects... "
|
|
|
|
# Temporary directory.
|
|
fl_dir="/tmp/,mklibs.$$"
|
|
set -e
|
|
mkdir $fl_dir
|
|
set +e
|
|
|
|
trap "rm -fr $fl_dir" EXIT
|
|
|
|
# Intialize our symbol array and library queue with the information
|
|
# from the executables.
|
|
|
|
get-undefined-symbols $exec > $fl_dir/undefined-symbols
|
|
add-to-queue-if-not-there $fl_dir/library-depends `get-library-depends $exec`
|
|
|
|
$verbose 2>&1 "done."
|
|
|
|
#
|
|
# 3.a Graph Construction
|
|
#
|
|
# Build the dependency graph, add new library dependencies to the queue on
|
|
# the way.
|
|
# If the soname is a link, add the target to the end of the queue and
|
|
# add a simple arrow to the graph.
|
|
# If the soname is a real lib, get its dependencies and add them to
|
|
# the queue. Furthermore, add arrows to the graph. If the lib is not
|
|
# dependant on any other lib, add the node to make sure it is mentioned
|
|
# at least once in the graph.
|
|
|
|
$verbose -n 2>&1 "Constructing dependency graph... ("
|
|
|
|
while cur_lib=`get-top-of-queue $fl_dir/library-depends`
|
|
do
|
|
lib=`find-file $src_path $cur_lib`
|
|
if [ -L "$lib" ] ; then
|
|
$verbose -n 2>&1 L
|
|
lib=`basename \`readlink $lib\``
|
|
add-to-queue-if-not-there $fl_dir/library-depends "$lib"
|
|
add-arrow $fl_dir/dependency-graph "$cur_lib" "$lib"
|
|
else
|
|
get-library-depends "$lib" > $fl_dir/backup
|
|
if [ "x`head -n 1 $fl_dir/backup`" = x ] ; then
|
|
$verbose -n 2>&1 N
|
|
add-node $fl_dir/dependency-graph "$cur_lib"
|
|
else
|
|
$verbose -n 2>&1 A
|
|
for lib in `cat $fl_dir/backup` ; do
|
|
add-to-queue-if-not-there $fl_dir/library-depends "$lib"
|
|
add-arrow $fl_dir/dependency-graph "$cur_lib" "$lib"
|
|
done
|
|
fi
|
|
fi
|
|
done
|
|
|
|
$verbose 2>&1 ") done."
|
|
|
|
#
|
|
# 3.b Graph Reduction
|
|
#
|
|
# Find and shrink cycles in the graph.
|
|
|
|
$verbose -n 2>&1 "Eliminating cycles... ("
|
|
|
|
while cycle=`find-cycle "$fl_dir/dependency-graph"` ; do
|
|
$verbose -n 2>&1 C
|
|
shrink-nodes "$fl_dir/dependency-graph" $cycle
|
|
done
|
|
|
|
$verbose 2>&1 ") done."
|
|
|
|
#
|
|
# 4. Library Installation
|
|
#
|
|
# Let tsort(1) do the actual work on the cycle-free graph.
|
|
|
|
tsort $fl_dir/dependency-graph > $fl_dir/backup
|
|
|
|
# Now the ordered list of libraries (or cycles of them)
|
|
# can be processed by install-libs. This is indeed the last step.
|
|
|
|
install-libs `cat $fl_dir/backup`
|
|
|
|
#sort -u $fl_dir/provided-symbols > $fl_dir/diag1
|
|
#sort -u $fl_dir/undefined-symbols > $fl_dir/diag2
|
|
#cat $fl_dir/diag1 $fl_dir/diag2 | sort | uniq -u > $fl_dir/diag3
|
|
## diag3 has now the symmetric difference.
|
|
#cat $fl_dir/diag3 $fl_dir/diag2 | sort | uniq -d > $fl_dir/diag1
|
|
## diag1 has now all undefined symbols that are not provided.
|
|
##cat $fl_dir/diag1 | wc
|
|
## Note that some of these symbols are weak and not having them is probably
|
|
## not an error.
|
|
|
|
exit 0
|
|
|