#!/bin/sh -f
# $TOG: elistgen.1 /main/7 1997/10/24 15:16:44 bill $
#
# ########################################################################
# Construct shared-library scoping mapfile for Solaris based on standardized
# export list description file
#
# Usage: exportlistgen libfoo.so libfoo.elist > mapfile.scope
#
#	libfoo.so    => shared library of interest
#	libfoo.elist => Meta description of necessary export list.
#
#    The output file, "mapfile.scope" may then be passed to the Solaris 
#    linker to reconstruct the shared library, libfoo.so.
#
# ########################################################################
#
# $XFree86: xc/config/util/elistgen.1,v 1.6 2000/02/09 17:10:25 dawes Exp $

# Utility programs
FILTER=			# C++ symbol demangler
AWK=nawk			# Awk

# For nm, cat, pr, expand, awk, c++filt
PATH=/usr/bin:/bin:/usr/ccs/bin:/usr/ucb:$PATH

# Try to detect broken versions of c++filt.
if [ `echo _okay | ${FILTER:-cat}` != "_okay" ]; then
    if [ -x /opt/SUNWspro/bin/c++filt ]; then
	echo "# Your $FILTER is broken -- using /opt/SUNWspro/bin/c++filt."
	FILTER=/opt/SUNWspro/bin/c++filt
    else
	echo "# ERROR: no working $FILTER available."
	exit 1
    fi;
fi;

# Temporary files
EXPORTLIST=/tmp/elistgen1.$$	# export directives from "libfoo.list"
NMLIST=/tmp/elistgen2.$$	# name list from libfoo.sl
FILTLIST=/tmp/elistgen3.$$	# demangled (C++) version of above

# Print useful information at the top of the output
echo "#" `date`
echo "# This scoping mapfile was produced by" $0
echo "# Export list description taken from:" $2
echo "# Target library:" $1
echo "# Target Operating System:" `uname -msrv`
echo "# "

# Extract the globally visible symbols from target library
# The NMLIST generated here is later used to cross-check the symbols in the
# nm stopped working on Solaris 2.5, use dump instead.
nm -p $1 | $AWK '/ [ TDBS] /{print $3}' > $NMLIST
# /usr/ccs/bin/dump -t -v $1 | $AWK '/(FUNC|OBJT).(GLOB|WEAK)/{print $8}' > $NMLIST

# Extract the globally visible symbols from target library
# The NMLIST generated here is later used to cross-check the symbols in the
${FILTER:-cat} < $NMLIST > $FILTLIST

# Clean up the export-list description file.  Sort the directives.
$AWK '
    BEGIN {
	csyms     = 0;	# C   public symbols in libfoo.list
	cplusplus = 0;	# C++ public symbols in libfoo.list
	isyms     = 0;	# C   internal symbols in libfoo.list
	iplusplus = 0;	# C++ internal symbols in libfoo.list
	implicit  = "";	# Handling of implicit symbols
    }
    $1 == "default" {
	# A default clause suppresses warnings about implicit symbols.
	if ($2 != "" && $2 != "force" && $2 != "public" && $2 != "private" && $2 != "internal") {
	    print "# Warning: illegal default clause:", $2 | "cat 1>&2";
	    next;
	}
	if (implicit != "")
	    print "# Warning: multiple default clauses." | "cat 1>&2";
	implicit = $2;
	next;
    }
    $1 == "force" {
	csyms ++;
	print $1 ";;" $2;
	next;
    }
    $1 == "public" {
	csyms ++;
	print $1 ";;" $2;
	next;
    }
    $1 == "publicC++" {
	cplusplus ++;
	string = $2;
	for (n = 3; n <= NF; n++)
	    string = string " " $n;
	print $1 ";;" string;
	next;
    }
    $1 == "private" {
	csyms ++;
	print $1 ";;" $2;
	next;
    }
    $1 == "privateC++" {
	cplusplus ++;
	string = $2;
	for (n = 3; n <= NF; n++)
	    string = string " " $n;
	print $1 ";;" string;
	next;
    }
    $1 == "internal" {
	isyms ++;
	print $1 ";;" $2;
	next;
    }
    $1 == "internalC++" {
	iplusplus ++;
	string = $2;
	for (n = 3; n <= NF; n++)
	    string = string " " $n;
	print $1 ";;" string;
	next;
    }
    $1 == "#line" || $1 == "#" {
	# cpp will have removed comments, but may have added other stuff.
	next;
    }
    NF > 0 {
	print "# Warning: unrecognized directive:", $0 | "cat 1>&2";
	next;
    }
    END {
	printf("# Exporting %d C and %d C++ symbols, hiding %d and %d.\n", csyms, cplusplus, isyms, iplusplus) | "cat 1>&2";
	if (implicit != "") {
	    print "# Unspecified symbols are " implicit "." | "cat 1>&2";
	    print "default;;" implicit;
	}
    }
' $2 1>$EXPORTLIST


# Read in the above files and write result to stdout.  The contents
# of NMLIST and FILTLIST are used to construct a symbol lookup table.
# The contents of EXPORTLIST are converted with the help of this table.
# Use ";" as a delimiter in the symbol lookup table.
(pr -m -s";" -t -w1024 $NMLIST $FILTLIST | expand -1; cat $EXPORTLIST) | $AWK '
    BEGIN {
	FS = ";";
	implicit = 0;
    }
    NF == 2 {
	# This is "pr" output, i.e., symbol translation table
	r2=$2;
	gsub(/static /,"",r2); # remove keyword "static" as above
	gsub(/ /,"",r2);       # Remove spaces because c++filt is unpredictable
	syms[r2] = $1;
	r1=$1;
	gsub(/ /,"",r1);
	mangled[r1] = 1;       # Save the mangling because the export lists
	                       # sometimes use it instead of a prototype.
	next;
    }
    NF == 3 && $1 == "default" {
	# Treatment of unspecified symbols.
	if ($3 == "internal" || $3 == "internalC++")
	    implicit = 1;
	else if ($3 == "private" || $3 == "privateC++")
	    implicit = 2;
	else if ($3 == "public" || $3 == "publicC++")
	    implicit = 3;
	else # $3 == "force"
	    implicit = 4;
	next;
    }
    NF == 3 {
	# Generate canonical demangled form as an alternate symbol.
	alt=$3;
	gsub(/ \.\.\./,",...",alt);  # change " ..." to ",..." to match c++filt
	gsub(/ /,"",alt);            # remove all spaces
	
	# An export directive.  Parse our instructions for this symbol.
	if ($1 == "internal" || $1 == "internalC++")
	    export = 1;
	else if ($3 == "private" || $3 == "privateC++")
	    export = 2;
	else if ($3 == "public" || $3 == "publicC++")
	    export = 3;
	else # $3 == "force"
	    export = 4;

	# Process it.
	if ((length(syms[alt]) > 0) || mangled[alt]) {
	    # This symbol is present in the library.
	    if (donelist[alt])
	        print "# Warning: Duplicate entry for", $3, 
	            "in export list" | "cat 1>&2";
	    if (donelist[alt] < export) {
	        donelist[alt] = export;
	    }
	} else { 
	    # Print forced-export symbols without complaining.
	    if (export == 4) {
	        donelist[alt] = export;
	    } else {
	        print "# Warning:", $3,
	            "was not in the nm list for this library" | "cat 1>&2";
	    }
	}
	
	next;
    }
    END { 
	# Ignore magic linker symbols.
	if (implicit == 0) {
	    if (!donelist["_DYNAMIC"])
		donelist["_DYNAMIC"] = 1;
	    if (!donelist["_GLOBAL_OFFSET_TABLE_"])
		donelist["_GLOBAL_OFFSET_TABLE_"] = 1;
	    if (!donelist["_PROCEDURE_LINKAGE_TABLE_"])
		donelist["_PROCEDURE_LINKAGE_TABLE_"] = 1;
	    if (!donelist["_edata"])
		donelist["_edata"] = 1;
	    if (!donelist["_end"])
		donelist["_end"] = 1;
	    if (!donelist["_etext"])
		donelist["_etext"] = 1;
        }

	# Process implicit symbols.
	for (i in syms) {
	    if (donelist[i] == 0 && length(syms[i]) > 0) {
		if (implicit == 0) {
		    # Ignore magic symbols introduced by the C++ compiler.
		    if ((syms[i] !~ /^__vtbl__[0-9]*_/) && (syms[i] !~ /^__ptbl_vec__/))
			print "# Warning:", syms[i], "was not in the export list" | "cat 1>&2";
		} else {
		    donelist[i] = implicit;
		}
	    }
	}

	# Generate the linker file.
	print "";
	print "SUNW.1.1 {";
	print "    global:";
	for (i in syms)
	    if (donelist[i] >= 2 && length(syms[i]) > 0)
		print "        " syms[i] ";";
	print "    local:";
	print "        *;";
	print "};";

	print "";
	print "SUNW_private.1.1 {";
	print "    global:";
	for (i in syms)
	    if (donelist[i] == 1 && length(syms[i]) > 0)
		print "        " syms[i] ";";
	print "} SUNW.1.1;"
    }
'

# Clean up temporary files
rm $EXPORTLIST
rm $NMLIST
rm $FILTLIST
