source: trunk/debugtools/mapsymw.pl@ 1567

Last change on this file since 1567 was 1551, checked in by Gregg Young, 15 years ago

This code adds the semaphores to prevent a rescan from starting before the current one is finished; it fixes the double directory listing in the tree container and streamlines scanning. It update mapsym.pl to the latest version. Some code cleanup is included

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