# A comment with the original pattern and its type is left in the output
# file to make it easy to understand the matches.
#
-# It expects a 'nm' with the POSIX '-P' option, but everyone has one of
-# those, right?
+# It uses elfdump when present (native), GNU readelf otherwise.
# It depends on the GNU version of c++filt, since it must understand the
# GNU mangling style.
-use File::Glob ':glob';
use FileHandle;
use IPC::Open2;
+# Enforce C locale.
+$ENV{'LC_ALL'} = "C";
+$ENV{'LANG'} = "C";
+
# Input version script, GNU style.
my $symvers = shift;
}
}
-# The nm command to use.
-my $nm = $ENV{'NM_FOR_TARGET'} || "nm";
+# We need to detect and ignore hidden symbols. Solaris nm can only detect
+# this in the harder to parse default output format, and GNU nm not at all,
+# so use elfdump -s in the native case and GNU readelf -s otherwise.
+# GNU objdump -t cannot be used since it produces a variable number of
+# columns.
-# Process each symbol.
-open NM,$nm.' -P '.(join ' ',@OBJECTS).'|' or die $!;
-while (<NM>) {
- my $i;
- chomp;
+# The path to elfdump.
+my $elfdump = "/usr/ccs/bin/elfdump";
+
+if (-f $elfdump) {
+ open ELFDUMP,$elfdump.' -s '.(join ' ',@OBJECTS).'|' or die $!;
+ my $skip_arsym = 0;
+
+ while (<ELFDUMP>) {
+ chomp;
+
+ # Ignore empty lines.
+ if (/^$/) {
+ # End of archive symbol table, stop skipping.
+ $skip_arsym = 0 if $skip_arsym;
+ next;
+ }
- # nm prints out stuff at the start, ignore it.
- next if (/^$/);
- next if (/:$/);
- # Ignore register (SPARC only), undefined and local symbols. The
- # symbol name is optional; Sun nm emits none for local or .bss symbols.
- next if (/^([^ ]+)?[ \t]+[RUa-z][ \t]+/);
- # Ignore objects without symbol table. Message goes to stdout with Sun
- # nm, while GNU nm emits the corresponding message to stderr.
- next if (/.* - No symbol table data/);
-
- # $sym is the name of the symbol.
- die "unknown nm output $_" if (! /^([^ ]+)[ \t]+[A-Z][ \t]+/);
- my $sym = $1;
-
- # Remember symbol.
- $sym_hash{$sym}++;
+ # Keep skipping until end of archive symbol table.
+ next if ($skip_arsym);
+
+ # Ignore object name header for individual objects and archives.
+ next if (/:$/);
+
+ # Ignore table header lines.
+ next if (/^Symbol Table Section:/);
+ next if (/index.*value.*size/);
+
+ # Start of archive symbol table: start skipping.
+ if (/^Symbol Table: \(archive/) {
+ $skip_arsym = 1;
+ next;
+ }
+
+ # Split table.
+ (undef, undef, undef, undef, $bind, $oth, undef, $shndx, $name) = split;
+
+ # Error out for unknown input.
+ die "unknown input line:\n$_" unless defined($bind);
+
+ # Ignore local symbols.
+ next if ($bind eq "LOCL");
+ # Ignore hidden symbols.
+ next if ($oth eq "H");
+ # Ignore undefined symbols.
+ next if ($shndx eq "UNDEF");
+ # Error out for unhandled cases.
+ if ($bind !~ /^(GLOB|WEAK)/ or $oth ne "D") {
+ die "unhandled symbol:\n$_";
+ }
+
+ # Remember symbol.
+ $sym_hash{$name}++;
+ }
+ close ELFDUMP or die "$elfdump error";
+} else {
+ open READELF, 'readelf -s -W '.(join ' ',@OBJECTS).'|' or die $!;
+ # Process each symbol.
+ while (<READELF>) {
+ chomp;
+
+ # Ignore empty lines.
+ next if (/^$/);
+
+ # Ignore object name header.
+ next if (/^File: .*$/);
+
+ # Ignore table header lines.
+ next if (/^Symbol table.*contains.*:/);
+ next if (/Num:.*Value.*Size/);
+
+ # Split table.
+ (undef, undef, undef, undef, $bind, $vis, $ndx, $name) = split;
+
+ # Error out for unknown input.
+ die "unknown input line:\n$_" unless defined($bind);
+
+ # Ignore local symbols.
+ next if ($bind eq "LOCAL");
+ # Ignore hidden symbols.
+ next if ($vis eq "HIDDEN");
+ # Ignore undefined symbols.
+ next if ($ndx eq "UND");
+ # Error out for unhandled cases.
+ if ($bind !~ /^(GLOBAL|WEAK)/ or $vis ne "DEFAULT") {
+ die "unhandled symbol:\n$_";
+ }
+
+ # Remember symbol.
+ $sym_hash{$name}++;
+ }
+ close READELF or die "readelf error";
}
-close NM or die "nm error";
##########
# The various types of glob patterns.
# We're currently inside `extern "C++"', which Sun ld doesn't understand.
my $in_extern = 0;
-# We're currently inside a conditional section: just skip it.
-my $in_ifdef = 0;
-
# The c++filt command to use. This *must* be GNU c++filt; the Sun Studio
# c++filt doesn't handle the GNU mangling style.
my $cxxfilt = $ENV{'CXXFILT'} || "c++filt";
print "#\n\n";
while (<F>) {
- # End of skipped section.
- if (/^[ \t]*\#endif/) {
- $in_ifdef = 0;
- next;
- }
-
- # Just skip a conditional section.
- if ($in_ifdef) { next; }
-
# Lines of the form '};'
if (/^([ \t]*)(\}[ \t]*;[ \t]*)$/) {
$glob = 'glob';
if ($in_extern) {
$in_extern--;
- print "$1##$2";
+ print "$1##$2\n";
} else {
print;
}
print; next;
}
- # Special comments that look like C preprocessor conditionals.
- # Just skip the contents for now.
- # FIXME: Allow passing in conditionals from the command line to really
- # control the skipping.
- if (/^[ \t]*\#ifdef/) {
- $in_ifdef = 1;
- next;
- }
-
# Comment and blank lines
if (/^[ \t]*\#/) { print; next; }
if (/^[ \t]*$/) { print; next; }
if (/^([ \t]*)([^ \t;{}#]+);?[ \t]*$/) {
my $ws = $1;
my $ptn = $2;
- # Turn the glob into a regex by replacing '*' with '.*'.
+ # Turn the glob into a regex by replacing '*' with '.*', '?' with '.'.
# Keep $ptn so we can still print the original form.
($pattern = $ptn) =~ s/\*/\.\*/g;
+ $pattern =~ s/\?/\./g;
if ($glob eq 'ign') {
# We're in a local: * section; just continue.