Home | History | Annotate | Line # | Download | only in contrib
make_sunver.pl revision 1.1.1.2
      1      1.1  mrg #!/usr/bin/perl -w
      2      1.1  mrg 
      3      1.1  mrg # make_sunver.pl
      4      1.1  mrg #
      5      1.1  mrg # This script takes at least two arguments, a GNU style version script and
      6      1.1  mrg # a list of object and archive files, and generates a corresponding Sun
      7      1.1  mrg # style version script as follows:
      8      1.1  mrg #
      9      1.1  mrg # Each glob pattern, C++ mangled pattern or literal in the input script is
     10      1.1  mrg # matched against all global symbols in the input objects, emitting those
     11      1.1  mrg # that matched (or nothing if no match was found).
     12      1.1  mrg # A comment with the original pattern and its type is left in the output
     13      1.1  mrg # file to make it easy to understand the matches.
     14      1.1  mrg #
     15      1.1  mrg # It uses elfdump when present (native), GNU readelf otherwise.
     16      1.1  mrg # It depends on the GNU version of c++filt, since it must understand the
     17      1.1  mrg # GNU mangling style.
     18      1.1  mrg 
     19      1.1  mrg use FileHandle;
     20      1.1  mrg use IPC::Open2;
     21      1.1  mrg 
     22      1.1  mrg # Enforce C locale.
     23      1.1  mrg $ENV{'LC_ALL'} = "C";
     24      1.1  mrg $ENV{'LANG'} = "C";
     25      1.1  mrg 
     26      1.1  mrg # Input version script, GNU style.
     27      1.1  mrg my $symvers = shift;
     28      1.1  mrg 
     29      1.1  mrg ##########
     30      1.1  mrg # Get all the symbols from the library, match them, and add them to a hash.
     31      1.1  mrg 
     32      1.1  mrg my %sym_hash = ();
     33      1.1  mrg 
     34      1.1  mrg # List of objects and archives to process.
     35      1.1  mrg my @OBJECTS = ();
     36      1.1  mrg 
     37      1.1  mrg # List of shared objects to omit from processing.
     38      1.1  mrg my @SHAREDOBJS = ();
     39      1.1  mrg 
     40      1.1  mrg # Filter out those input archives that have corresponding shared objects to
     41      1.1  mrg # avoid adding all symbols matched in the archive to the output map.
     42      1.1  mrg foreach $file (@ARGV) {
     43      1.1  mrg     if (($so = $file) =~ s/\.a$/.so/ && -e $so) {
     44      1.1  mrg 	printf STDERR "omitted $file -> $so\n";
     45      1.1  mrg 	push (@SHAREDOBJS, $so);
     46      1.1  mrg     } else {
     47      1.1  mrg 	push (@OBJECTS, $file);
     48      1.1  mrg     }
     49      1.1  mrg }
     50      1.1  mrg 
     51      1.1  mrg # We need to detect and ignore hidden symbols.  Solaris nm can only detect
     52      1.1  mrg # this in the harder to parse default output format, and GNU nm not at all,
     53      1.1  mrg # so use elfdump -s in the native case and GNU readelf -s otherwise.
     54      1.1  mrg # GNU objdump -t cannot be used since it produces a variable number of
     55      1.1  mrg # columns.
     56      1.1  mrg 
     57      1.1  mrg # The path to elfdump.
     58      1.1  mrg my $elfdump = "/usr/ccs/bin/elfdump";
     59      1.1  mrg 
     60      1.1  mrg if (-f $elfdump) {
     61      1.1  mrg     open ELFDUMP,$elfdump.' -s '.(join ' ',@OBJECTS).'|' or die $!;
     62      1.1  mrg     my $skip_arsym = 0;
     63      1.1  mrg 
     64      1.1  mrg     while (<ELFDUMP>) {
     65      1.1  mrg 	chomp;
     66      1.1  mrg 
     67      1.1  mrg 	# Ignore empty lines.
     68      1.1  mrg 	if (/^$/) {
     69      1.1  mrg 	    # End of archive symbol table, stop skipping.
     70      1.1  mrg 	    $skip_arsym = 0 if $skip_arsym;
     71      1.1  mrg 	    next;
     72      1.1  mrg 	}
     73      1.1  mrg 
     74      1.1  mrg 	# Keep skipping until end of archive symbol table.
     75      1.1  mrg 	next if ($skip_arsym);
     76      1.1  mrg 
     77      1.1  mrg 	# Ignore object name header for individual objects and archives.
     78      1.1  mrg 	next if (/:$/);
     79      1.1  mrg 
     80      1.1  mrg 	# Ignore table header lines.
     81      1.1  mrg 	next if (/^Symbol Table Section:/);
     82      1.1  mrg 	next if (/index.*value.*size/);
     83      1.1  mrg 
     84      1.1  mrg 	# Start of archive symbol table: start skipping.
     85      1.1  mrg 	if (/^Symbol Table: \(archive/) {
     86      1.1  mrg 	    $skip_arsym = 1;
     87      1.1  mrg 	    next;
     88      1.1  mrg 	}
     89      1.1  mrg 
     90      1.1  mrg 	# Split table.
     91      1.1  mrg 	(undef, undef, undef, undef, $bind, $oth, undef, $shndx, $name) = split;
     92      1.1  mrg 
     93      1.1  mrg 	# Error out for unknown input.
     94      1.1  mrg 	die "unknown input line:\n$_" unless defined($bind);
     95      1.1  mrg 
     96      1.1  mrg 	# Ignore local symbols.
     97      1.1  mrg 	next if ($bind eq "LOCL");
     98      1.1  mrg 	# Ignore hidden symbols.
     99      1.1  mrg 	next if ($oth eq "H");
    100      1.1  mrg 	# Ignore undefined symbols.
    101      1.1  mrg 	next if ($shndx eq "UNDEF");
    102      1.1  mrg 	# Error out for unhandled cases.
    103      1.1  mrg 	if ($bind !~ /^(GLOB|WEAK)/ or $oth ne "D") {
    104      1.1  mrg 	    die "unhandled symbol:\n$_";
    105      1.1  mrg 	}
    106      1.1  mrg 
    107      1.1  mrg 	# Remember symbol.
    108      1.1  mrg 	$sym_hash{$name}++;
    109      1.1  mrg     }
    110      1.1  mrg     close ELFDUMP or die "$elfdump error";
    111      1.1  mrg } else {
    112      1.1  mrg     open READELF, 'readelf -s -W '.(join ' ',@OBJECTS).'|' or die $!;
    113      1.1  mrg     # Process each symbol.
    114      1.1  mrg     while (<READELF>) {
    115      1.1  mrg 	chomp;
    116      1.1  mrg 
    117      1.1  mrg 	# Ignore empty lines.
    118      1.1  mrg 	next if (/^$/);
    119      1.1  mrg 
    120      1.1  mrg 	# Ignore object name header.
    121      1.1  mrg 	next if (/^File: .*$/);
    122      1.1  mrg 
    123      1.1  mrg 	# Ignore table header lines.
    124      1.1  mrg 	next if (/^Symbol table.*contains.*:/);
    125      1.1  mrg 	next if (/Num:.*Value.*Size/);
    126      1.1  mrg 
    127      1.1  mrg 	# Split table.
    128      1.1  mrg 	(undef, undef, undef, undef, $bind, $vis, $ndx, $name) = split;
    129      1.1  mrg 
    130      1.1  mrg 	# Error out for unknown input.
    131      1.1  mrg 	die "unknown input line:\n$_" unless defined($bind);
    132      1.1  mrg 
    133      1.1  mrg 	# Ignore local symbols.
    134      1.1  mrg 	next if ($bind eq "LOCAL");
    135      1.1  mrg 	# Ignore hidden symbols.
    136      1.1  mrg 	next if ($vis eq "HIDDEN");
    137      1.1  mrg 	# Ignore undefined symbols.
    138      1.1  mrg 	next if ($ndx eq "UND");
    139      1.1  mrg 	# Error out for unhandled cases.
    140      1.1  mrg 	if ($bind !~ /^(GLOBAL|WEAK)/ or $vis ne "DEFAULT") {
    141      1.1  mrg 	    die "unhandled symbol:\n$_";
    142      1.1  mrg 	}
    143      1.1  mrg 
    144      1.1  mrg 	# Remember symbol.
    145      1.1  mrg 	$sym_hash{$name}++;
    146      1.1  mrg     }
    147      1.1  mrg     close READELF or die "readelf error";
    148      1.1  mrg }
    149      1.1  mrg 
    150      1.1  mrg ##########
    151      1.1  mrg # The various types of glob patterns.
    152      1.1  mrg #
    153      1.1  mrg # A glob pattern that is to be applied to the demangled name: 'cxx'.
    154      1.1  mrg # A glob patterns that applies directly to the name in the .o files: 'glob'.
    155      1.1  mrg # This pattern is ignored; used for local variables (usually just '*'): 'ign'.
    156      1.1  mrg 
    157      1.1  mrg # The type of the current pattern.
    158      1.1  mrg my $glob = 'glob';
    159      1.1  mrg 
    160      1.1  mrg # We're currently inside `extern "C++"', which Sun ld doesn't understand.
    161      1.1  mrg my $in_extern = 0;
    162      1.1  mrg 
    163      1.1  mrg # The c++filt command to use.  This *must* be GNU c++filt; the Sun Studio
    164      1.1  mrg # c++filt doesn't handle the GNU mangling style.
    165      1.1  mrg my $cxxfilt = $ENV{'CXXFILT'} || "c++filt";
    166      1.1  mrg 
    167      1.1  mrg # The current version name.
    168      1.1  mrg my $current_version = "";
    169      1.1  mrg 
    170      1.1  mrg # Was there any attempt to match a symbol to this version?
    171      1.1  mrg my $matches_attempted;
    172      1.1  mrg 
    173      1.1  mrg # The number of versions which matched this symbol.
    174      1.1  mrg my $matched_symbols;
    175      1.1  mrg 
    176      1.1  mrg open F,$symvers or die $!;
    177      1.1  mrg 
    178      1.1  mrg # Print information about generating this file
    179      1.1  mrg print "# This file was generated by make_sunver.pl.  DO NOT EDIT!\n";
    180      1.1  mrg print "# It was generated by:\n";
    181      1.1  mrg printf "# %s %s %s\n", $0, $symvers, (join ' ',@ARGV);
    182      1.1  mrg printf "# Omitted archives with corresponding shared libraries: %s\n",
    183      1.1  mrg     (join ' ', @SHAREDOBJS) if $#SHAREDOBJS >= 0;
    184      1.1  mrg print "#\n\n";
    185      1.1  mrg 
    186      1.1  mrg while (<F>) {
    187      1.1  mrg     # Lines of the form '};'
    188      1.1  mrg     if (/^([ \t]*)(\}[ \t]*;[ \t]*)$/) {
    189      1.1  mrg 	$glob = 'glob';
    190      1.1  mrg 	if ($in_extern) {
    191      1.1  mrg 	    $in_extern--;
    192      1.1  mrg 	    print "$1##$2\n";
    193      1.1  mrg 	} else {
    194      1.1  mrg 	    print;
    195      1.1  mrg 	}
    196      1.1  mrg 	next;
    197      1.1  mrg     }
    198      1.1  mrg 
    199      1.1  mrg     # Lines of the form '} SOME_VERSION_NAME_1.0;'
    200      1.1  mrg     if (/^[ \t]*\}[ \tA-Z0-9_.a-z]+;[ \t]*$/) {
    201      1.1  mrg 	$glob = 'glob';
    202      1.1  mrg 	# We tried to match symbols agains this version, but none matched.
    203      1.1  mrg 	# Emit dummy hidden symbol to avoid marking this version WEAK.
    204      1.1  mrg 	if ($matches_attempted && $matched_symbols == 0) {
    205      1.1  mrg 	    print "  hidden:\n";
    206      1.1  mrg 	    print "    .force_WEAK_off_$current_version = DATA S0x0 V0x0;\n";
    207      1.1  mrg 	}
    208      1.1  mrg 	print; next;
    209      1.1  mrg     }
    210      1.1  mrg 
    211      1.1  mrg     # Comment and blank lines
    212      1.1  mrg     if (/^[ \t]*\#/) { print; next; }
    213      1.1  mrg     if (/^[ \t]*$/) { print; next; }
    214      1.1  mrg 
    215      1.1  mrg     # Lines of the form '{'
    216  1.1.1.2  mrg     if (/^([ \t]*)\{$/) {
    217      1.1  mrg 	if ($in_extern) {
    218      1.1  mrg 	    print "$1##{\n";
    219      1.1  mrg 	} else {
    220      1.1  mrg 	    print;
    221      1.1  mrg 	}
    222      1.1  mrg 	next;
    223      1.1  mrg     }
    224      1.1  mrg 
    225      1.1  mrg     # Lines of the form 'SOME_VERSION_NAME_1.1 {'
    226      1.1  mrg     if (/^([A-Z0-9_.]+)[ \t]+{$/) {
    227      1.1  mrg 	# Record version name.
    228      1.1  mrg 	$current_version = $1;
    229      1.1  mrg 	# Reset match attempts, #matched symbols for this version.
    230      1.1  mrg 	$matches_attempted = 0;
    231      1.1  mrg 	$matched_symbols = 0;
    232      1.1  mrg 	print;
    233      1.1  mrg 	next;
    234      1.1  mrg     }
    235      1.1  mrg 
    236      1.1  mrg     # Ignore 'global:'
    237      1.1  mrg     if (/^[ \t]*global:$/) { print; next; }
    238      1.1  mrg 
    239      1.1  mrg     # After 'local:', globs should be ignored, they won't be exported.
    240      1.1  mrg     if (/^[ \t]*local:$/) {
    241      1.1  mrg 	$glob = 'ign';
    242      1.1  mrg 	print;
    243      1.1  mrg 	next;
    244      1.1  mrg     }
    245      1.1  mrg 
    246      1.1  mrg     # After 'extern "C++"', globs are C++ patterns
    247      1.1  mrg     if (/^([ \t]*)(extern \"C\+\+\"[ \t]*)$/) {
    248      1.1  mrg 	$in_extern++;
    249      1.1  mrg 	$glob = 'cxx';
    250      1.1  mrg 	# Need to comment, Sun ld cannot handle this.
    251      1.1  mrg 	print "$1##$2\n"; next;
    252      1.1  mrg     }
    253      1.1  mrg 
    254      1.1  mrg     # Chomp newline now we're done with passing through the input file.
    255      1.1  mrg     chomp;
    256      1.1  mrg 
    257      1.1  mrg     # Catch globs.  Note that '{}' is not allowed in globs by this script,
    258      1.1  mrg     # so only '*' and '[]' are available.
    259      1.1  mrg     if (/^([ \t]*)([^ \t;{}#]+);?[ \t]*$/) {
    260      1.1  mrg 	my $ws = $1;
    261      1.1  mrg 	my $ptn = $2;
    262      1.1  mrg 	# Turn the glob into a regex by replacing '*' with '.*', '?' with '.'.
    263      1.1  mrg 	# Keep $ptn so we can still print the original form.
    264      1.1  mrg 	($pattern = $ptn) =~ s/\*/\.\*/g;
    265      1.1  mrg 	$pattern =~ s/\?/\./g;
    266      1.1  mrg 
    267      1.1  mrg 	if ($glob eq 'ign') {
    268      1.1  mrg 	    # We're in a local: * section; just continue.
    269      1.1  mrg 	    print "$_\n";
    270      1.1  mrg 	    next;
    271      1.1  mrg 	}
    272      1.1  mrg 
    273      1.1  mrg 	# Print the glob commented for human readers.
    274      1.1  mrg 	print "$ws##$ptn ($glob)\n";
    275      1.1  mrg 	# We tried to match a symbol to this version.
    276      1.1  mrg 	$matches_attempted++;
    277      1.1  mrg 
    278      1.1  mrg 	if ($glob eq 'glob') {
    279      1.1  mrg 	    my %ptn_syms = ();
    280      1.1  mrg 
    281      1.1  mrg 	    # Match ptn against symbols in %sym_hash.
    282      1.1  mrg 	    foreach my $sym (keys %sym_hash) {
    283      1.1  mrg 		# Maybe it matches one of the patterns based on the symbol in
    284      1.1  mrg 		# the .o file.
    285      1.1  mrg 		$ptn_syms{$sym}++ if ($sym =~ /^$pattern$/);
    286      1.1  mrg 	    }
    287      1.1  mrg 
    288      1.1  mrg 	    foreach my $sym (sort keys(%ptn_syms)) {
    289      1.1  mrg 		$matched_symbols++;
    290      1.1  mrg 		print "$ws$sym;\n";
    291      1.1  mrg 	    }
    292      1.1  mrg 	} elsif ($glob eq 'cxx') {
    293      1.1  mrg 	    my %dem_syms = ();
    294      1.1  mrg 
    295      1.1  mrg 	    # Verify that we're actually using GNU c++filt.  Other versions
    296      1.1  mrg 	    # most likely cannot handle GNU style symbol mangling.
    297      1.1  mrg 	    my $cxxout = `$cxxfilt --version 2>&1`;
    298      1.1  mrg 	    $cxxout =~ m/GNU/ or die "$0 requires GNU c++filt to function";
    299      1.1  mrg 
    300      1.1  mrg 	    # Talk to c++filt through a pair of file descriptors.
    301      1.1  mrg 	    # Need to start a fresh instance per pattern, otherwise the
    302      1.1  mrg 	    # process grows to 500+ MB.
    303      1.1  mrg 	    my $pid = open2(*FILTIN, *FILTOUT, $cxxfilt) or die $!;
    304      1.1  mrg 
    305      1.1  mrg 	    # Match ptn against symbols in %sym_hash.
    306      1.1  mrg 	    foreach my $sym (keys %sym_hash) {
    307      1.1  mrg 		# No?  Well, maybe its demangled form matches one of those
    308      1.1  mrg 		# patterns.
    309      1.1  mrg 		printf FILTOUT "%s\n",$sym;
    310      1.1  mrg 		my $dem = <FILTIN>;
    311      1.1  mrg 		chomp $dem;
    312      1.1  mrg 		$dem_syms{$sym}++ if ($dem =~ /^$pattern$/);
    313      1.1  mrg 	    }
    314      1.1  mrg 
    315      1.1  mrg 	    close FILTOUT or die "c++filt error";
    316      1.1  mrg 	    close FILTIN or die "c++filt error";
    317      1.1  mrg 	    # Need to wait for the c++filt process to avoid lots of zombies.
    318      1.1  mrg 	    waitpid $pid, 0;
    319      1.1  mrg 
    320      1.1  mrg 	    foreach my $sym (sort keys(%dem_syms)) {
    321      1.1  mrg 		$matched_symbols++;
    322      1.1  mrg 		print "$ws$sym;\n";
    323      1.1  mrg 	    }
    324      1.1  mrg 	} else {
    325      1.1  mrg 	    # No?  Well, then ignore it.
    326      1.1  mrg 	}
    327      1.1  mrg 	next;
    328      1.1  mrg     }
    329      1.1  mrg     # Important sanity check.  This script can't handle lots of formats
    330      1.1  mrg     # that GNU ld can, so be sure to error out if one is seen!
    331      1.1  mrg     die "strange line `$_'";
    332      1.1  mrg }
    333      1.1  mrg close F;
    334