#!/usr/bin/env perl12# make_sunver.pl3#4# Copyright (C) 2010, 2011, 2012, 20135# Free Software Foundation, Inc.6#7# This file is free software; you can redistribute it and/or modify it8# under the terms of the GNU General Public License as published by9# the Free Software Foundation; either version 3 of the License, or10# (at your option) any later version.11#12# This program is distributed in the hope that it will be useful, but13# WITHOUT ANY WARRANTY; without even the implied warranty of14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU15# General Public License for more details.16#17# You should have received a copy of the GNU General Public License18# along with this program; see the file COPYING.GPLv3. If not see19# <http://www.gnu.org/licenses/>.2021# This script takes at least two arguments, a GNU style version script and22# a list of object and archive files, and generates a corresponding Sun23# style version script as follows:24#25# Each glob pattern, C++ mangled pattern or literal in the input script is26# matched against all global symbols in the input objects, emitting those27# that matched (or nothing if no match was found).28# A comment with the original pattern and its type is left in the output29# file to make it easy to understand the matches.30#31# It uses elfdump when present (native), GNU readelf otherwise.32# It depends on the GNU version of c++filt, since it must understand the33# GNU mangling style.3435use FileHandle;36use IPC::Open2;3738# Enforce C locale.39$ENV{'LC_ALL'} = "C";40$ENV{'LANG'} = "C";4142# Input version script, GNU style.43my $symvers = shift;4445##########46# Get all the symbols from the library, match them, and add them to a hash.4748my %sym_hash = ();4950# List of objects and archives to process.51my @OBJECTS = ();5253# List of shared objects to omit from processing.54my @SHAREDOBJS = ();5556# Filter out those input archives that have corresponding shared objects to57# avoid adding all symbols matched in the archive to the output map.58foreach $file (@ARGV) {59if (($so = $file) =~ s/\.a$/.so/ && -e $so) {60printf STDERR "omitted $file -> $so\n";61push (@SHAREDOBJS, $so);62} else {63push (@OBJECTS, $file);64}65}6667# We need to detect and ignore hidden symbols. Solaris nm can only detect68# this in the harder to parse default output format, and GNU nm not at all,69# so use elfdump -s in the native case and GNU readelf -s otherwise.70# GNU objdump -t cannot be used since it produces a variable number of71# columns.7273# The path to elfdump.74my $elfdump = "/usr/ccs/bin/elfdump";7576if (-f $elfdump) {77open ELFDUMP,$elfdump.' -s '.(join ' ',@OBJECTS).'|' or die $!;78my $skip_arsym = 0;7980while (<ELFDUMP>) {81chomp;8283# Ignore empty lines.84if (/^$/) {85# End of archive symbol table, stop skipping.86$skip_arsym = 0 if $skip_arsym;87next;88}8990# Keep skipping until end of archive symbol table.91next if ($skip_arsym);9293# Ignore object name header for individual objects and archives.94next if (/:$/);9596# Ignore table header lines.97next if (/^Symbol Table Section:/);98next if (/index.*value.*size/);99100# Start of archive symbol table: start skipping.101if (/^Symbol Table: \(archive/) {102$skip_arsym = 1;103next;104}105106# Split table.107(undef, undef, undef, undef, $bind, $oth, undef, $shndx, $name) = split;108109# Error out for unknown input.110die "unknown input line:\n$_" unless defined($bind);111112# Ignore local symbols.113next if ($bind eq "LOCL");114# Ignore hidden symbols.115next if ($oth eq "H");116# Ignore undefined symbols.117next if ($shndx eq "UNDEF");118# Error out for unhandled cases.119if ($bind !~ /^(GLOB|WEAK)/ or $oth ne "D") {120die "unhandled symbol:\n$_";121}122123# Remember symbol.124$sym_hash{$name}++;125}126close ELFDUMP or die "$elfdump error";127} else {128open READELF, 'readelf -s -W '.(join ' ',@OBJECTS).'|' or die $!;129# Process each symbol.130while (<READELF>) {131chomp;132133# Ignore empty lines.134next if (/^$/);135136# Ignore object name header.137next if (/^File: .*$/);138139# Ignore table header lines.140next if (/^Symbol table.*contains.*:/);141next if (/Num:.*Value.*Size/);142143# Split table.144(undef, undef, undef, undef, $bind, $vis, $ndx, $name) = split;145146# Error out for unknown input.147die "unknown input line:\n$_" unless defined($bind);148149# Ignore local symbols.150next if ($bind eq "LOCAL");151# Ignore hidden symbols.152next if ($vis eq "HIDDEN");153# Ignore undefined symbols.154next if ($ndx eq "UND");155# Error out for unhandled cases.156if ($bind !~ /^(GLOBAL|WEAK)/ or $vis ne "DEFAULT") {157die "unhandled symbol:\n$_";158}159160# Remember symbol.161$sym_hash{$name}++;162}163close READELF or die "readelf error";164}165166##########167# The various types of glob patterns.168#169# A glob pattern that is to be applied to the demangled name: 'cxx'.170# A glob patterns that applies directly to the name in the .o files: 'glob'.171# This pattern is ignored; used for local variables (usually just '*'): 'ign'.172173# The type of the current pattern.174my $glob = 'glob';175176# We're currently inside `extern "C++"', which Sun ld doesn't understand.177my $in_extern = 0;178179# The c++filt command to use. This *must* be GNU c++filt; the Sun Studio180# c++filt doesn't handle the GNU mangling style.181my $cxxfilt = $ENV{'CXXFILT'} || "c++filt";182183# The current version name.184my $current_version = "";185186# Was there any attempt to match a symbol to this version?187my $matches_attempted;188189# The number of versions which matched this symbol.190my $matched_symbols;191192open F,$symvers or die $!;193194# Print information about generating this file195print "# This file was generated by make_sunver.pl. DO NOT EDIT!\n";196print "# It was generated by:\n";197printf "# %s %s %s\n", $0, $symvers, (join ' ',@ARGV);198printf "# Omitted archives with corresponding shared libraries: %s\n",199(join ' ', @SHAREDOBJS) if $#SHAREDOBJS >= 0;200print "#\n\n";201202print "\$mapfile_version 2\n";203204while (<F>) {205# Lines of the form '};'206if (/^([ \t]*)(\}[ \t]*;[ \t]*)$/) {207$glob = 'glob';208if ($in_extern) {209$in_extern--;210print "$1##$2\n";211} else {212print;213}214next;215}216217# Lines of the form '} SOME_VERSION_NAME_1.0;'218if (/^[ \t]*\}[ \tA-Z0-9_.a-z]+;[ \t]*$/) {219$glob = 'glob';220# We tried to match symbols agains this version, but none matched.221# Emit dummy hidden symbol to avoid marking this version WEAK.222if ($matches_attempted && $matched_symbols == 0) {223print " hidden:\n";224print " .force_WEAK_off_$current_version = DATA S0x0 V0x0;\n";225}226print; next;227}228229# Comment and blank lines230if (/^[ \t]*\#/) { print; next; }231if (/^[ \t]*$/) { print; next; }232233# Lines of the form '{'234if (/^([ \t]*){$/) {235if ($in_extern) {236print "$1##{\n";237} else {238print;239}240next;241}242243# Lines of the form 'SOME_VERSION_NAME_1.1 {'244if (/^([A-Z0-9_.]+)[ \t]+{$/) {245# Record version name.246$current_version = $1;247# Reset match attempts, #matched symbols for this version.248$matches_attempted = 0;249$matched_symbols = 0;250print "SYMBOL_VERSION $1 {\n";251next;252}253254# Ignore 'global:'255if (/^[ \t]*global:$/) { print; next; }256257# After 'local:', globs should be ignored, they won't be exported.258if (/^[ \t]*local:$/) {259$glob = 'ign';260print;261next;262}263264# After 'extern "C++"', globs are C++ patterns265if (/^([ \t]*)(extern \"C\+\+\"[ \t]*)$/) {266$in_extern++;267$glob = 'cxx';268# Need to comment, Sun ld cannot handle this.269print "$1##$2\n"; next;270}271272# Chomp newline now we're done with passing through the input file.273chomp;274275# Catch globs. Note that '{}' is not allowed in globs by this script,276# so only '*' and '[]' are available.277if (/^([ \t]*)([^ \t;{}#]+);?[ \t]*$/) {278my $ws = $1;279my $ptn = $2;280# Turn the glob into a regex by replacing '*' with '.*', '?' with '.'.281# Keep $ptn so we can still print the original form.282($pattern = $ptn) =~ s/\*/\.\*/g;283$pattern =~ s/\?/\./g;284285if ($glob eq 'ign') {286# We're in a local: * section; just continue.287print "$_\n";288next;289}290291# Print the glob commented for human readers.292print "$ws##$ptn ($glob)\n";293# We tried to match a symbol to this version.294$matches_attempted++;295296if ($glob eq 'glob') {297my %ptn_syms = ();298299# Match ptn against symbols in %sym_hash.300foreach my $sym (keys %sym_hash) {301# Maybe it matches one of the patterns based on the symbol in302# the .o file.303$ptn_syms{$sym}++ if ($sym =~ /^$pattern$/);304}305306foreach my $sym (sort keys(%ptn_syms)) {307$matched_symbols++;308print "$ws$sym;\n";309}310} elsif ($glob eq 'cxx') {311my %dem_syms = ();312313# Verify that we're actually using GNU c++filt. Other versions314# most likely cannot handle GNU style symbol mangling.315my $cxxout = `$cxxfilt --version 2>&1`;316$cxxout =~ m/GNU/ or die "$0 requires GNU c++filt to function";317318# Talk to c++filt through a pair of file descriptors.319# Need to start a fresh instance per pattern, otherwise the320# process grows to 500+ MB.321my $pid = open2(*FILTIN, *FILTOUT, $cxxfilt) or die $!;322323# Match ptn against symbols in %sym_hash.324foreach my $sym (keys %sym_hash) {325# No? Well, maybe its demangled form matches one of those326# patterns.327printf FILTOUT "%s\n",$sym;328my $dem = <FILTIN>;329chomp $dem;330$dem_syms{$sym}++ if ($dem =~ /^$pattern$/);331}332333close FILTOUT or die "c++filt error";334close FILTIN or die "c++filt error";335# Need to wait for the c++filt process to avoid lots of zombies.336waitpid $pid, 0;337338foreach my $sym (sort keys(%dem_syms)) {339$matched_symbols++;340print "$ws$sym;\n";341}342} else {343# No? Well, then ignore it.344}345next;346}347# Important sanity check. This script can't handle lots of formats348# that GNU ld can, so be sure to error out if one is seen!349die "strange line `$_'";350}351close F;352353354