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