#!/bin/sh
#
#	Given a list of objects, strip all static symbols except those
#	required by insmod.
#
#	Copyright Keith Owens <kaos@ocs.com.au>.  GPL.
#	Sat Feb  1 12:52:17 EST 1997
#	
#	Mainly intended for reducing the size of modules to save space
#	on emergency and install disks.  Be aware that removing the
#	static symbols reduces the amount of diagnostic information
#	available for oops.  Not recommended for normal module usage.
#
#	This code requires the modules use MODULE_PARM and EXPORT_.
#	Do not strip modules that have not been converted to use
#	MODULE_PARM or are using the old method of exporting symbols.
#	In particular do not use on modules prior to 2.1.20 (approx).
#
#	The objects are stripped in /tmp, only if the strip works is
#	the original overwritten.  If the command line to strip the
#	symbols becomes too long, the strip is done in multiple passes.
#	Running strip_module twice on the same object is safe (and a
#	waste of time).
#

sizeofptr="/tmp/$$.sizeofptr"
echo 'int main() { return sizeof(void *); }' | gcc -xc - -o $sizeofptr
$sizeofptr
export SIZEOF_POINTER=$?
rm -f $sizeofptr

cat > /tmp/$$.awk <<\EOF
BEGIN	{
	strip = "/usr/bin/objcopy";
	nm = "/usr/bin/nm";
	cp = "/bin/cp";
	mv = "/bin/mv";
	rm = "/bin/rm";
	tmp = "/tmp";
	command_size = 400;	# arbitrary but safe

	getline < "/proc/self/stat";
	pid = $1;
	tmpcopy = tmp "/" pid ".object";
	nmout = tmp "/" pid ".nmout";

	for (i = 1; i < ARGC; ++i)
		strip_module(ARGV[i]);

	do_command(rm " -f " tmpcopy " " nmout);

	exit(0);
}

function strip_module(object,
	keep_symbol, to_strip, symbol, command, changed) {
	do_command(cp " -a " object " " tmpcopy);
	do_command(nm " " tmpcopy " > " nmout);
	# delete array_name sometimes breaks, internal error, play safe
	for (symbol in keep_symbol)
		delete keep_symbol[symbol];
	for (symbol in to_strip)
		delete to_strip[symbol];
	new_module_format = 0;
	ptrskip = 2 + 2 * ENVIRON["SIZEOF_POINTER"];
	while ((getline < nmout) > 0) {
		$0 = substr($0, ptrskip);
		# b static variable, uninitialised
		# d static variable, initialised
		# r static array, initialised
		# t static label/procedures
		print $0 ":" $3 ":" $5 > "/dev/stderr";
		if ($1 ~ /[bdrt]/)
			to_strip[$2] = "";
		else if ($2 ~ /R __ksymtab_/)
			keep_symbol[substr($2, 11)] = "";
		else if ($0 ~ /R __module_parm_/)
			keep_symbol[substr($2, 15)] = "";
		else if ($0 ~ /D __parm_/)
			keep_symbol[substr($2, 8)] = "";
		else if ($3 ~ /__ksymtab/) {
			print "keep " $5 > "/dev/stderr";
			keep_symbol[$5] = "";
		}
		else if ($1 != "?")
			keep_symbol[$2] = "";
		if ($2 ~ /__module/)
			new_module_format = 1;
	}
	close(nmout);
	command = "";
	changed = 0;
	failure = 0;
	if (new_module_format) {
		for (symbol in to_strip) {
			if (!(symbol in keep_symbol)) {
				changed = 1;
				if (length(command) > command_size) {
					failure = failure || do_command(strip command " " tmpcopy);
					command = "";
				}
				command = command " --strip-symbol=" symbol;
			}
		}
	}
	if (command != "") {
		changed = 1;
		failure = failure || do_command(strip command " " tmpcopy);
	}
	if (changed && !failure)
		do_command(mv " " tmpcopy " " object);
}

function do_command(command) {
	if ((ret = system(command)) != 0) {
		giveup("command \"" command "\" failed " ret, ret);
		return 1;
	}
	return 0;
}

function giveup(message, ret) {
	print "strip_module: " message > "/dev/stderr";
#	exit(ret);
}
EOF

awk -f /tmp/$$.awk "$@"
ret=$?
rm -f /tmp/$$.awk
exit $ret