source: trunk/debugtools/mapsymw.pl@ 1342

Last change on this file since 1342 was 1342, checked in by Steven Levine, 17 years ago

Ensure symbols sorted by value

File size: 10.0 KB
Line 
1#!perl -w
2# mapsymw - mapsym Watcom map files
3
4# Copyright (c) 2007, 2008 Steven Levine and Associates, Inc.
5# All rights reserved.
6
7# $TLIB$: $ &(#) %n - Ver %v, %f $
8# TLIB: $ $
9
10# This program is free software licensed under the terms of the GNU
11# General Public License. The GPL Software License can be found in
12# gnugpl2.txt or at http://www.gnu.org/licenses/licenses.html#GPL
13
14# 02 Jul 07 SHL Baseline
15# 02 Jul 07 SHL Adapt from mapsymb.pl
16# 28 Jul 07 SHL Relax module name detect
17# 30 Jul 07 SHL Auto-trim libstdc++ symbols from libc06x maps
18# 09 Aug 07 SHL Generate dummy symbol for interior segments with no symbols
19# 08 Nov 07 SHL Drop leading keywords from function definitions
20# 14 Dec 08 SHL Ensure symbols sorted by value - some apps care
21
22# mapsym requires each segment to have at least 1 symbol
23# mapsym requires 32 bit segments to have at least 1 symbol with offset > 65K
24# we generate dummy symbols to enforce this
25# mapsym does not understand segment 0
26# we generate Imp flags to support this
27
28use strict;
29use warnings;
30
31# use Package::Subpackage Options;
32use POSIX qw(strftime);
33use Getopt::Std;
34use File::Spec;
35use File::Basename;
36
37our $g_version = '0.3';
38
39our $g_cmdname;
40our $g_tmpdir;
41our @g_mapfiles; # All map files
42our $g_mapfile; # Current .map file name
43
44&initialize;
45
46our %g_opts;
47
48&scan_args;
49
50print "\n";
51
52foreach $g_mapfile (@g_mapfiles) {
53 &mapsym;
54}
55
56exit;
57
58# end main
59
60#=== initialize() Intialize globals ===
61
62sub initialize {
63
64 &set_cmd_name;
65 &get_tmp_dir;
66
67} # initialize
68
69#=== mapsym() Generate work file, run mapsym on work file ===
70
71sub mapsym {
72
73 # Isolate map file basename
74 my $mapid = basename($g_mapfile);
75 $mapid =~ s/\.[^.]*$//; # Strip ext
76 verbose_msg("\nProcessing $mapid");
77
78 fatal("$g_mapfile does not exist.") if ! -f $g_mapfile;
79
80 open MAPFILE, $g_mapfile or die "open $g_mapfile $!";
81
82 my $g_wrkfile = File::Spec->catfile($g_tmpdir, "$mapid.map");
83 unlink $g_wrkfile || die "unlink $g_wrkfile $!" if -f $g_wrkfile;
84 open WRKFILE, ">$g_wrkfile" or die "open $g_wrkfile $!";
85
86 my $modname;
87 my $state = '';
88 my $segcnt = 0;
89 my $symcnt = 0;
90 my $is32bit;
91 my %segsinfo;
92 my %syms;
93 my $segnum;
94 my $offset;
95 my $segaddr;
96
97 my $segfmt;
98 my $symfmt;
99
100 while (<MAPFILE>) {
101
102 chomp; # EOL
103
104 if (/Executable Image: (\S+)\.\w+$/) {
105 $modname = $1;
106 print WRKFILE "Generated by $g_cmdname from $g_mapfile on ",
107 strftime('%A, %B %d, %Y at %I:%M %p', localtime), "\n\n";
108 print WRKFILE " $modname\n";
109 }
110
111 $state = 'segments'
112 if /Segment Class Group Address Size/;
113
114 $state = 'addresses' if /Address Symbol/;
115
116 # Skip don't cares
117 next if /^=/;
118 next if /^ /;
119 next if /^$/;
120
121 if ($state eq 'segments') {
122 # In
123 # Segment Class Group Address Size
124 # _TEXT16 CODE AUTO 0001:00000000 00000068
125 # Out
126 # 0 1 2 3 4 5 6
127 # 123456789012345678901234567890123456789012345678901234567890
128 # Start Length Name Class
129 # 0001:00000000 000000030H _MSGSEG32 CODE 32-bit
130
131 if (/^(\w+)\s+(\w+)\s+\w+\s+([[:xdigit:]]+):([[:xdigit:]]+)\s+([[:xdigit:]]+)$/) {
132 my $segname = $1;
133 my $class = $2;
134 $segnum = $3; # Has leading 0's
135 $offset = $4;
136 my $seglen = $5;
137
138 $segaddr = "$segnum:$offset";
139
140 if (!$segcnt) {
141 # First segment - determine address size (16/32 bit)
142 $is32bit = length($offset) == 8;
143 # Output title
144 print WRKFILE "\n";
145 if ($is32bit) {
146 print WRKFILE " Start Length Name Class\n";
147 $segfmt = " %13s 0%8sH %-22s %s\n";
148 $symfmt = " %13s %3s %s\n";
149 } else {
150 print WRKFILE " Start Length Name Class\n";
151 $segfmt = " %9s 0%4sH %-22s %s\n";
152 $symfmt = " %9s %3s %s\n";
153 }
154 }
155
156 $seglen = substr($5, -4) if !$is32bit;
157
158 printf WRKFILE $segfmt, $segaddr, $seglen, $segname, $class;
159 $segcnt++;
160 }
161 } # if segments
162
163 if ($state eq 'addresses') {
164 # In
165 # Address Symbol
166 # 0002:0004ae46+ ArcTextProc
167 # Out
168 # 0 1 2 3 4 5 6
169 # 123456789012345678901234567890123456789012345678901234567890
170 # Address Publics by Value
171 # 0000:00000000 Imp WinEmptyClipbrd (PMWIN.733)
172 # 0002:0001ED40 __towlower_dummy
173 if (/^([[:xdigit:]]+):([[:xdigit:]]+)[+*]?\s+(\w+)$/) {
174 $segnum = $1;
175 $offset = $2;
176 my $sym = $3;
177
178 my $seginfo;
179 if (defined($segsinfo{$1})) {
180 $seginfo = $segsinfo{$1};
181 }
182 else {
183 $seginfo = {max_offset => 0,
184 symcnt => 0};
185 }
186
187 my $n = hex $offset;
188 # Remember max symbol offset
189 $seginfo->{max_offset} = $n if $n > $seginfo->{max_offset};
190 $seginfo->{symcnt}++;
191
192 $segsinfo{$1} = $seginfo;
193
194 $segaddr = "$segnum:$offset";
195
196 # Convert C++ symbols to something mapsym will accept
197
198 $_ = $sym;
199
200 # s/\bIdle\b/ /; # Drop Idle keyword
201 s/\(.*\).*$//; # Drop (... tails
202
203 s/::~/_x/; # Replace ::~ with _x
204 s/::/_/; # Replace :: with _
205
206 s/[<,]/_/g; # Replace < and , with _
207 s/[>]//g; # Replace > with nothing
208 s/[\[\]]//g; # Replace [] with nothing
209 s/_*$//; # Drop trailing _
210 s/\W+\w//; # Drop leading keywords (including Idle)
211
212 # Drop leading and trailing _ to match source code
213
214 s/^_//; # Drop leading _ (cdecl)
215 s/_$//; # Drop trailing _ (watcall)
216
217 # Prune some libc symbols to avoid mapsym overflows
218 if ($mapid =~ /libc06/) {
219 # 0001:000b73e0 __ZNSt7codecvtIcc11__mbstate_tEC2Ej
220 # next if / [0-9A-F]{4}:[0-9A-F]{8} {7}S/;
221 next if /\b__Z/; # Prune libstdc++
222 }
223
224 if (!$symcnt) {
225 # First symbol - output title
226 print WRKFILE "\n";
227 if ($is32bit) {
228 print WRKFILE " Address Publics by Value\n";
229 } else {
230 print WRKFILE " Address Publics by Value\n";
231 }
232 }
233
234 $syms{$segaddr} = $_;
235
236 $symcnt++;
237 }
238 } # if addresses
239
240 } # while lines
241
242 close MAPFILE;
243
244 # Generate dummy symbols as needed
245
246 my @keys = sort keys %segsinfo;
247 if (@keys) {
248 my $maxseg = pop @keys;
249 @keys = '0000'..$maxseg;
250 }
251
252 foreach $segnum (@keys) {
253 if ($segnum != 0) {
254 my $seginfo;
255 if (defined($segsinfo{$segnum})) {
256 $seginfo = $segsinfo{$segnum};
257 }
258 else {
259 $seginfo = {max_offset => 0,
260 symcnt => 0};
261 }
262 if ($seginfo->{symcnt} == 0) {
263 warn "Segment $segnum has no symbols - generating dummy symbol\n";
264 $_ = "Seg${segnum}_dummy";
265 if ($is32bit) {
266 $segaddr = "$segnum:00010000";
267 } else {
268 $segaddr = "$segnum:0000";
269 }
270 $syms{$segaddr} = $_;
271 $symcnt++;
272 } elsif ($is32bit && $seginfo->{max_offset} < 0x10000) {
273 warn "32 bit segment $segnum is smaller than 64K - generating dummy symbol\n";
274 $_ = "Seg${segnum}_dummy";
275 $segaddr = "$segnum:00010000";
276 $syms{$segaddr} = $_;
277 $symcnt++;
278 }
279 }
280 } # foreach
281
282 @keys = sort keys %syms;
283 foreach $segaddr (@keys) {
284 my $sym = $syms{$segaddr};
285 my $imp = substr($segaddr, 0, 4) eq '0000' ? 'Imp' : '';
286 printf WRKFILE $symfmt, $segaddr, $imp, $sym;
287 }
288
289 close WRKFILE;
290
291 die "Can not locate module name. $g_mapfile is probably not a Watcom map file\n" if !defined($modname);
292
293 my $symfile = "$mapid.sym";
294 unlink $symfile || die "unlink $symfile $!" if -f $symfile;
295
296 warn "Processed $segcnt segments and $symcnt symbols for $modname\n";
297
298 system("mapsym $g_wrkfile");
299
300} # mapsym
301
302#=== scan_args(cmdLine) Scan command line ===
303
304sub scan_args {
305
306 getopts('dhtvV', \%g_opts) || &usage;
307
308 &help if $g_opts{h};
309
310 if ($g_opts{V}) {
311 print "$g_cmdname v$g_version";
312 exit;
313 }
314
315 my $arg;
316
317 for $arg (@ARGV) {
318 my @maps = glob($arg);
319 usage("File $arg not found") if @maps == 0;
320 push @g_mapfiles, @maps;
321 } # for arg
322
323} # scan_args
324
325#=== help() Display scan_args usage help exit routine ===
326
327sub help {
328
329 print <<EOD;
330Generate .sym file for Watcom map files.
331Generates temporary map file reformatted for mapsym and
332invokes mapsym to process this map file.
333
334Usage: $g_cmdname [-d] [-h] [-v] [-V] mapfile...
335 -d Display debug messages
336 -h Display this message
337 -v Display progress messages
338 -V Display version
339
340 mapfile List of map files to process
341EOD
342
343 exit 255;
344
345} # help
346
347#=== usage(message) Report Scanargs usage error exit routine ===
348
349sub usage {
350
351 my $msg = shift;
352 print "\n$msg\n" if $msg;
353print <<EOD;
354
355Usage: $g_cmdname [-d] [-h] [-v] [-V] mapfile...
356EOD
357 exit 255;
358
359} # usage
360
361#==========================================================================
362#=== SkelFunc standards - Delete unused - Move modified above this mark ===
363#==========================================================================
364
365#=== verbose_msg(message) Display message if verbose ===
366
367sub verbose_msg {
368 if ($g_opts{v}) {
369 my $msg = shift;
370 if (defined $msg) {
371 print STDOUT "$msg\n";
372 } else {
373 print STDOUT "\n";
374 }
375 }
376} # verbose_msg
377
378#==========================================================================
379#=== SkelPerl standards - Delete unused - Move modified above this mark ===
380#==========================================================================
381
382#=== fatal(message) Report fatal error and exit ===
383
384sub fatal {
385 my $msg = shift;
386 print "\n";
387 print STDERR "$g_cmdname: $msg\a\n";
388 exit 254;
389
390} # fatal
391
392#=== set_cmd_name() Set $g_cmdname to script name less path and extension ===
393
394sub set_cmd_name {
395 $g_cmdname = $0;
396 $g_cmdname = basename($g_cmdname);
397 $g_cmdname =~ s/\.[^.]*$//; # Chop ext
398
399} # set_cmd_name
400
401#=== get_tmp_dir() Get TMP dir name with trailing backslash, set Gbl. ===
402
403sub get_tmp_dir {
404
405 $g_tmpdir = File::Spec->tmpdir();
406 die "Need to have TMP or TMPDIR or TEMP defined" unless $g_tmpdir;
407
408} # get_tmp_dir
409
410# The end
Note: See TracBrowser for help on using the repository browser.