Home | History | Annotate | Line # | Download | only in contrib
      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  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