summaryrefslogtreecommitdiff
path: root/gpxe/src/util/symcheck.pl
blob: 8925ca623db3e1caa51c141150fcc1a706b98f80 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#!/usr/bin/perl -w

use strict;
use warnings;

use constant WARNING_SIZE => 512;

my $symtab = {};

# Scan output of "objdump -w -t bin/blib.a" and build up symbol table
#
my $object;
while ( <> ) {
  chomp;
  if ( /^In archive/ ) {
    # Do nothing
  } elsif ( /^$/ ) {
    # Do nothing
  } elsif ( /^(\S+\.o):\s+file format/ ) {
    $object = $1;
  } elsif ( /^SYMBOL TABLE:/ ) {
    # Do nothing
  } elsif ( /^([0-9a-fA-F]+)\s(l|g|\s)......\s(\S+)\s+([0-9a-fA-F]+)\s+(\S+)$/ ) {
    my $value = $1;
    my $scope = $2;
    my $section = $3;
    my $size = $4;
    my $symbol = $5;
    $symtab->{$object}->{$symbol} = {
      global	=> ( $scope ne "l" ),
      section	=> ( $section eq "*UND*" ? undef : $section ),
      value	=> ( $value ? hex ( $value ) : 0 ),
      size	=> ( $size ? hex ( $size ) : 0 ),
    };
  } else {
    die "Unrecognized line \"$_\"";
  }
}

# Add symbols that we know will be generated or required by the linker
#
foreach my $object ( keys %$symtab ) {
  my $obj_symbol = "obj_$object";
  $obj_symbol =~ s/\.o$//;
  $obj_symbol =~ s/\W/_/g;
  $symtab->{LINKER}->{$obj_symbol} = {
    global	=> 1,
    section	=> undef,
    value	=> 0,
    size	=> 0,
  };
}
foreach my $link_sym qw ( __prefix _prefix _prefix_load_offset
			  _prefix_size _prefix_progbits_size _prefix_size_pgh
			  __text16 _text16 _text16_load_offset
			  _text16_size _text16_progbits_size _text16_size_pgh
			  __data16 _data16 _data16_load_offset
			  _data16_size _data16_progbits_size _data16_size_pgh
			  __text _text __data _data _textdata_load_offset
			  _textdata_size _textdata_progbits_size
			  __rodata __bss _end
			  _payload_offset _max_align
			  _load_size _load_size_pgh _load_size_sect
			  pci_vendor_id pci_device_id ) {
  $symtab->{LINKER}->{$link_sym} = {
    global	=> 1,
    section	=> '*ABS*',
    value	=> 0,
    size	=> 0,
  };
}

# Add symbols that we know will be used by the debug system
#
foreach my $debug_sym qw ( dbg_autocolourise dbg_decolourise
			   dbg_hex_dump_da ) {
  $symtab->{DEBUG}->{$debug_sym} = {
    global	=> 1,
    section	=> undef,
    value	=> 0,
    size	=> 0,
  };
}

# Build up requires, provides and shares symbol tables for global
# symbols
#
my $globals = {};
while ( ( my $object, my $symbols ) = each %$symtab ) {
  while ( ( my $symbol, my $info ) = each %$symbols ) {
    if ( $info->{global} ) {
      my $category;
      if ( ! defined $info->{section} ) {
	$category = "requires";
      } elsif ( $info->{section} eq "*COM*" ) {
	$category = "shares";
      } else {
	$category = "provides";
      }
      $globals->{$symbol}->{$category}->{$object} = 1;
    }
  }
}

# Check for multiply defined, never-defined and unused global symbols
#
my $problems = {};
while ( ( my $symbol, my $info ) = each %$globals ) {
  my @provides = keys %{$info->{provides}};
  my @requires = keys %{$info->{requires}};
  my @shares = keys %{$info->{shares}};

  if ( ( @provides == 0 ) && ( @shares == 1 ) ) {
    # A symbol "shared" by just a single file is actually being
    # provided by that file; it just doesn't have an initialiser.
    @provides = @shares;
    @shares = ();
  }

  if ( ( @requires > 0 ) && ( @provides == 0 ) && ( @shares == 0 ) ) {
    # No object provides this symbol, but some objects require it.
    $problems->{$_}->{nonexistent}->{$symbol} = 1 foreach @requires;
  }

  if ( ( @requires == 0 ) && ( @provides > 0 ) ) {
    # No object requires this symbol, but some objects provide it.
    foreach my $provide ( @provides ) {
      if ( $provide eq "LINKER" ) {
	# Linker-provided symbols are exempt from this check.
      } elsif ( $symtab->{$provide}->{$symbol}->{section} =~ /^\.tbl\./ ) {
	# Linker tables are exempt from this check.
      } else {
	$problems->{$provide}->{unused}->{$symbol} = 1;
      }
    }
  }

  if ( ( @shares > 0 ) && ( @provides > 0 ) ) {
    # A shared symbol is being initialised by an object
    $problems->{$_}->{shared}->{$symbol} = 1 foreach @provides;
  }

  if ( @provides > 1 ) {
    # A non-shared symbol is defined in multiple objects
    $problems->{$_}->{multiples}->{$symbol} = 1 foreach @provides;
  }
}

# Check for excessively large local symbols.  Text and rodata symbols
# are exempt from this check
#
while ( ( my $object, my $symbols ) = each %$symtab ) {
  while ( ( my $symbol, my $info ) = each %$symbols ) {
    if ( ( ! $info->{global} ) &&
	 ( ( defined $info->{section} ) &&
	   ! ( $info->{section} =~ /^(\.text|\.rodata)/ ) ) &&
	 ( $info->{size} >= WARNING_SIZE ) ) {
      $problems->{$object}->{large}->{$symbol} = 1;
    }
  }
}

# Print out error messages
#
my $errors = 0;
my $warnings = 0;
foreach my $object ( sort keys %$problems ) {
  my @nonexistent = sort keys %{$problems->{$object}->{nonexistent}};
  my @multiples = sort keys %{$problems->{$object}->{multiples}};
  my @unused = sort keys %{$problems->{$object}->{unused}};
  my @shared = sort keys %{$problems->{$object}->{shared}};
  my @large = sort keys %{$problems->{$object}->{large}};

  print "WARN $object provides unused symbol $_\n" foreach @unused;
  $warnings += @unused;
  print "WARN $object has large static symbol $_\n" foreach @large;
  $warnings += @large;
  print "ERR  $object requires non-existent symbol $_\n" foreach @nonexistent;
  $errors += @nonexistent;
  foreach my $symbol ( @multiples ) {
    my @other_objects = sort grep { $_ ne $object }
		        keys %{$globals->{$symbol}->{provides}};
    print "ERR  $object provides symbol $symbol"
	." (also provided by @other_objects)\n";
  }
  $errors += @multiples;
  print "ERR  $object misuses shared symbol $_\n" foreach @shared;
}

print "$errors error(s), $warnings warning(s)\n";
exit ( $errors ? 1 : 0 );