source: trunk/tools/mapsymw.pl@ 10

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