#! /bin/sh
#
# Copyright (c) 1994,1995 Berkeley Software Design, Inc. All rights reserved.
# The Berkeley Software Design Inc. software License Agreement specifies
# the terms and conditions for redistribution.
#
#	BSDI shlib,v 2.4 1995/07/26 17:51:49 donn Exp
#

#
# Convert a normal archive library into a static shared library.
#

#
# Layout of a sharable image:
#
# library loader:
#	first instruction jumps to code
#	padding to page boundary (includes QMAGIC header)
# jump table:
#	jump to initialization routine
#	jump table elements (8 bytes each)
#	padding to page boundary
# const data:
#	const data in specified order
# ordinary text:
#	code for library functions
#	[big break]
# exported data:
#	data in specified order
# private data:
#	data in any order
#

#
# Scan the library archive and generate a symbol map file
# that can be used to produce a jump table and other
# data structures that are useful to a shared library.
#
scan() {
	nm -op "$1" 2> /dev/null |
	awk -v CONST=$CONST -v EXCEPT=$EXCEPT '

	BEGIN {
		while (getline < CONST > 0) \
			const[$0] = 1
		while (getline < EXCEPT > 0) \
			except[$0] = 1
	}

	/ [Ua-z] / {
		next
	}

	{
		sub(/^[^:]*:/, "")
		sub(/:/, " ")
	}

	const[$4] {
		$3 = "Z"
	}

	$4 !~ /^_[^_]/ && !except[$4] {
		next
	}

	$4 ~ /^_[^_]/ && except[$4] {
		next
	}

	$3 == "C" {
		print $1, $3, $4, $2
		next
	}

	{
		print $1, $3, $4
	}'

	return 0
}

#
# Build the .o file for the jump table.
# The table is an array of code fragments
# whose offsets are exported to user programs
# through the stub library.
# Each code fragment is a jump to the appropriate
# library routine inside the shared library.
# The jump table lets us change the offsets
# (that is, the contents) of library routines
# in the shared library without disturbing
# the interface to user programs.
#
jump() {
	if [ -f "$IMAGE" -a -r "$IMAGE" ]
	then
		nm -n "$IMAGE" |
			sed -n -e '/ T JUMP/s/^.* T JUMP//p' > jump
	else
		if [ -n "$IMAGE" ]
		then
			echo "shlib: warning: can't open $IMAGE, continuing"
		fi
		touch jump
	fi
	sort jump > jump.sorted
	sed -n -e 's/^.* T //p' $1 | sort > text
	if [ -s jump.sorted ]
	then
		egrep -v DELETED jump.sorted | comm -23 - text > deleted
		if [ -s deleted ]
		then
			echo ''
			echo Deleted entry points:
			column deleted
		fi
		comm -13 jump.sorted text > added
		if [ -s added ]
		then
			echo ''
			echo Added entry points:
			column added
		fi
	else
		touch deleted
		mv text added
	fi
	awk -v DELETED=deleted -v ADDED=added '

	BEGIN {
		print "#include <machine/shlib.h>"
		while (getline < DELETED > 0) \
			deleted[$0] = 1
		print "\t.text"
		print "\tJUMPTO(_loader)"
	}

	deleted[$0] {
		$0 = "_DELETED_" NR
	}

	{
		jump()
	}

	END {
		while (getline < ADDED > 0) \
			jump()
		print "PAGE_ALIGN"
	}

	function jump() {
		j = "JUMP" $0
		if ($0 ~ /^_DELETED_/) \
			print "\tJUMPENTRY(" j ",_abort)"
		else \
			print "\tJUMPENTRY(" j "," $0 ")"
	}' jump | cpp -DLOCORE | as -o jump_table.o

	return 0
}

#
# Given an old shared library image and a map file,
# generate an ordered list of object files
# to be linked into the new image that
# matches the old image's data offsets,
# insofar as this is possible.
#
imagedata() {
	nm -n "$IMAGE" |
	awk -v MAP="$1" '
	BEGIN {
		while (getline < MAP > 0) {
			if ($2 ~ /[ZD]/) {
				dataobj[$1] = 1
				common[$3] = -1
			}
			else if ($2 == "C" && common[$3] == 0) \
				common[$3] = 1
		}
		for (c in common) {
			if (common[c] == 1) \
				dataobj["COMMON" c ".o"] = 1
		}
	}

	$2 == "F" && dataobj[$3] {
		print $3
		dataobj[$3] = 0
	}

	END {
		for (f in dataobj) \
			if (dataobj[f] == 1) \
				print f
	}'

	return 0
}

#
# Generate a list of 'common' definition files from the map,
# for use in forcing a specific link order for data.
#
commonfiles() {
	awk '
	$2 == "D" {
		common[$3] = -1
		next
	}

	$2 == "C" && common[$3] == 0 {
		common[$3] = 1
		next
	}

	END {
		for (c in common) {
			if (common[c] == 1) \
				print "COMMON" c ".o"
		}
	}' "$1"
}

#
# Generate a list of .o files from the archive
# that contain exported data.
# We try to duplicate the order of files
# in an old image if one is available,
# appending new data in no fixed order;
# otherwise, we list const data first,
# blank data second and regular data third,
# because we hypothesize that this will lead
# to fewer possible inconsistencies in future updates.
#
data() {
	if [ -f "$IMAGE" -a -r "$IMAGE" ]
	then
		imagedata "$1"
	else
		# const data first, blank data second, regular data third
		awk '$2 == "Z" { print $1 }' map | sort -u > dtmp1
		commonfiles map | sort -u >> dtmp1
		awk '$2 == "D" { print $1 }' map | sort -u |
			comm -23 - dtmp1 > dtmp2
		cat dtmp1 dtmp2
	fi
}

#
# Create stub files to define blank common data.
#
commonobjs() {
	awk -v MAP="$1" '
	BEGIN {
		while (getline < MAP > 0) \
			if ($2 == "C") \
				common[$3] = $4
	}

	/^COMMON.*\.o$/ {
		c = substr($0, 7)
		c = substr(c, 1, length(c) - 2)
		print "("
		print "echo '"'"'\t.data'"'"'"
		print "echo '"'"'\t.globl", c "'"'"'"
		print "echo '"'"'" c ":'"'"'"
		print "echo '"'"'\t.space 0x" common[c] "'"'"'"
		print ") | as -o", $0
	}
	' "$2" |
	sh
}

#
# Generate the object files for the stub archive
# corresponding to a new shared library image.
# The stub objects are empty except for symbol definitions
# that provide offsets to jump table entries and global data
# in the new shared library image.
# Users link against a stub library rather than
# the actual sharable image so that they may
# redefine symbols that occur in the library without
# generating a linking conflict with a library symbol.
# Note that the library will use the library's internal
# version of the function or data regardless of any redefinition,
# however.
#
stubobjs() {
	nm -p "$1" |
	awk -v MAP="$2" -v LIBC="$3" '
	BEGIN {
		while ("cat " MAP | getline > 0) \
			if ($2 != "C" || map[$3] != "D") \
				map[$3] = $2
	}

	substr($3, 1, 4) == "JUMP" && map[substr($3, 5)] == "T" {
		map[substr($3, 5)] = "0x" $1
		next
	}

	map[$3] ~ /^[CDZ]$/ {
		map[$3] = "0x" $1
	}

	LIBC != "" && $3 == "_environ" {
		print "echo '"'"'.globl _environ; .set _environ,0x" $1 "'"'"' |"
		print "\tas -o environ.o"
	}

	LIBC != "" && $3 == "___progname" {
		print "echo '"'"'.globl ___progname; .set ___progname,0x" $1 "'"'"' |"
		print "\tas -o __progname.o"
	}

	LIBC != "" && $3 == "___ps_strings" {
		print "echo '"'"'.globl ___ps_strings; .set ___ps_strings,0x" $1 "'"'"' |"
		print "\tas -o ps_strings.o"
	}

	END {
		first = 1
		while (getline < MAP > 0) {
			if ($1 !~ /\.o$/) \
				$1 = $1 ".o"
			if (first) {
				first = 0
				print "("
			} else if (obj != $1) {
				print ") | as -o", obj
				print "("
			}
			obj = $1
			if (substr(map[$3], 1, 1) != "0") \
				print "shlib: warning: no address for", $3 \
					> "/dev/stderr"
			else {
				print "\techo '"'"'.globl", $3 "'"'"'"
				print "\techo '"'"'.set", $3 "," map[$3] "'"'"'"
			}
		}
		print ") | as -o", obj
	}
	' |
	sh
}

stubarchive() {
	awk -v LIB="$1" '
	/^[# 	]/ { next }
	$1 == LIB {
		sub(/^.*\/lib/, "", $5)
		print "-l" $5
		LIB = ""
		exit 0
	}
	END {
		if (LIB)
			print LIB
	}' $SHLIBMAP
}

umask 022

CONST=/dev/null
DSTART=
EXCEPT=/dev/null
NEWIMAGE=
IMAGE=
STUB=
TSTART=
ARCHIVE=
SHLIBMAP=/usr/lib/shlib.map
BOOT=/usr/lib/loader.c
CFLAGS=-O
LDADD=
LIBC=

#
# parse arguments
#
while [ $# -gt 0 ]
do
	case "$1" in
	-[DIOU]*) # C compilation flags for loader.c
		CFLAGS="$CFLAGS $1"
		;;
	-L*) # link path flag
		LDADD="$LDADD $1"
		;;
	-b) # bootstrap file (default: loader.c)
		shift
		BOOT="$1"
		;;
	-c) # global const data file (1 symbol per line, with '_')
		shift
		CONST="$1"
		;;
	-d) # data start address (defaults to text start + 0x4000000)
		shift
		DSTART="$1"
		;;
	-e) # exceptions file (1 symbol per line, with '_')
		shift
		EXCEPT="$1"
		;;
	-i) # old shared image
		shift
		IMAGE="$1"
		;;
	-l) # additional shared library to link against
		shift
		L="$1"
		if [ ! -f "$L" ]
		then
			echo "shlib: can't access library $L"
			exit 1
		fi
		if [ `expr "$L" : /.` -lt 2 ]
		then
			L=$PWD/"$L"
		fi
		LDADD="$LDADD $L"
		;;
	-l*) # additional shared library to link against
		S=`stubarchive "$1"`
		LDADD="$LDADD $S"
		;;
	-m) # alternate shlib.map file
		shift
		SHLIBMAP="$1"
		;;
	-n) # new shared image
		shift
		NEWIMAGE="$1"
		;;
	-s) # new stub archive
		shift
		STUB="$1"
		;;
	-t) # text start address (hex, no 0x prefix)
		shift
		TSTART="$1"
		;;
	*)  # archive file
		ARCHIVE="$1"
		;;
	esac
	shift
done

if [ "X$ARCHIVE" = "X" ]
then
	echo "shlib: archive name required"
	exit 1
fi
if [ `basename "$ARCHIVE" .a` = libc ]
then
	LIBC=yes
fi
if [ "X$TSTART" = "X" ]
then
	if [ -f "$IMAGE" ]
	then
		TSTART=`nm -n $IMAGE |
		   sed -n -e '/ T /{s/^\(.....\)....*$/\1000/p;q;}'`
	else
		echo "shlib: text address required"
		exit 1
	fi
fi
if [ "X$DSTART" = "X" ]
then
	if [ -f "$IMAGE" ]
	then
		DSTART=`nm -n $IMAGE |
		   sed -n -e '/ D /{s/^\(.....\)....*$/\1000/p;q;}'`
	else
		echo "shlib: data address required"
		exit 1
	fi
fi

TSTART=`echo $TSTART | tr a-z A-Z`
DSTART=`echo $DSTART | tr a-z A-Z`

NEWIMAGE=${NEWIMAGE:-$PWD/`basename $ARCHIVE .a`_s}
STUB=${STUB:-$NEWIMAGE.a}

#
# convert to absolute pathnames
#
if [ `expr "$ARCHIVE" : /.` -lt 2 ]
then
	ARCHIVE=$PWD/$ARCHIVE
fi
if [ -f "$IMAGE" ]
then
	if [ `expr "$IMAGE" : /.` -lt 2 ]
	then
		IMAGE=$PWD/$IMAGE
	fi
fi
if [ `expr "$NEWIMAGE" : /.` -lt 2 ]
then
	NEWIMAGE=$PWD/$NEWIMAGE
fi
if [ `expr "$STUB" : /.` -lt 2 ]
then
	STUB=$PWD/$STUB
fi
if [ `expr "$CONST" : /.` -lt 2 ]
then
	CONST=$PWD/$CONST
fi
if [ `expr "$EXCEPT" : /.` -lt 2 ]
then
	EXCEPT=$PWD/$EXCEPT
fi
if [ `expr "$BOOT" : /.` -lt 2 ]
then
	BOOT=$PWD/$BOOT
fi

#
# preliminaries
#

trap "cd /var/tmp; rm -fr /var/tmp/shlib.build.$$; exit 1" 1 2 15 25

SAVEPWD=$PWD
mkdir /var/tmp/shlib.build.$$
cd /var/tmp/shlib.build.$$

#
# the real work
#

scan "$ARCHIVE" > map

jump map

data map > dataobjs

cc $CFLAGS -c -o loader.o -DDATA_ADDRESS=0x${DSTART} $BOOT

commonobjs map dataobjs

DATAOBJS=`cat dataobjs`
ar -x $ARCHIVE $DATAOBJS 2> /dev/null

# XXX no -q on sparc, but it is the default on the i386 anyway...???
ld -o $NEWIMAGE.tmp -Ttext $TSTART -Tdata $DSTART -L `dirname $ARCHIVE` \
	loader.o jump_table.o $DATAOBJS $ARCHIVE $LDADD

if [ ! -x $NEWIMAGE.tmp ]
then
	sleep 5
	cd $SAVEPWD
	rm -f $NEWIMAGE.tmp
	rm -fr /var/tmp/shlib.build.$$
	exit 1
fi
mv $NEWIMAGE.tmp $NEWIMAGE
rm -f loader.o jump_table.o $DATAOBJS

#
# build the stub archive library
#

stubobjs $NEWIMAGE map $LIBC

rm -f $STUB
ar -crT $STUB *.o 2> /dev/null
ranlib $STUB

#
# clean up
#
cd $SAVEPWD
rm -fr /var/tmp/shlib.build.$$

exit 0
