Skip to content

Commit a69b2e7

Browse files
authored
Merge pull request #1 from rversteegen/master
Improvements and fixes
2 parents 3badc17 + 500328c commit a69b2e7

File tree

2 files changed

+135
-50
lines changed

2 files changed

+135
-50
lines changed

README.md

+55-25
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,64 @@
11
# Linker Map Summary
2-
Summarizes the size of objects linked into a binary based on the linker map.
2+
Summarizes the size of objects linked into a binary based on a linker map
3+
generated by GNU ld with --print-map (should work on all platforms), by summing
4+
together portions in sections spread throughout the binary.
35
This is useful to see the size impact of different portions of code after the
46
linker has dropped unneeded sections.
57

8+
Requires python 2.7+ or 3.2+.
9+
610
## Use
7-
`python2 analyze_map.py firmware.elf.map`
11+
`python analyze_map.py [--combine] firmware.elf.map`
812

913
## Example Output
14+
(Older output format)
15+
```
16+
/usr/local/Cellar/arm-none-eabi-gcc/20150921/arm-none-eabi/lib/armv6-m/libm.a(lib_a-kf_rem_pio2.o) 1888
17+
build-arduino_zero/py/mpprint.o 1938
18+
build-arduino_zero/py/objset.o 2018
19+
build-arduino_zero/py/obj.o 2038
20+
build-arduino_zero/py/objarray.o 2122
21+
build-arduino_zero/boards/arduino_zero/pins.o 2144
22+
build-arduino_zero/py/objdict.o 2146
23+
build-arduino_zero/py/objlist.o 2210
24+
build-arduino_zero/py/lexer.o 2405
25+
build-arduino_zero/py/objexcept.o 2466
26+
build-arduino_zero/asf/sam0/drivers/usb/stack_interface/usb_device_udd.o 2692
27+
build-arduino_zero/asf/sam0/drivers/usb/usb_sam_d_r/usb.o 3026
28+
build-arduino_zero/py/emitbc.o 3166
29+
build-arduino_zero/py/modbuiltins.o 3219
30+
build-arduino_zero/py/gc.o 3411
31+
build-arduino_zero/py/objtype.o 3579
32+
build-arduino_zero/py/vm.o 4259
33+
build-arduino_zero/py/runtime.o 4627
34+
build-arduino_zero/py/parse.o 4676
35+
build-arduino_zero/py/qstr.o 6589
36+
build-arduino_zero/py/objstr.o 9070
37+
build-arduino_zero/lib/fatfs/ff.o 10777
38+
build-arduino_zero/py/compile.o 11731
1039
```
11-
/usr/local/Cellar/arm-none-eabi-gcc/20150921/bin/../lib/gcc/arm-none-eabi/4.9.3/../../../../arm-none-eabi/lib/armv6-m//libm.a(lib_a-kf_rem_pio2.o) 1888
12-
build-arduino_zero/py/mpprint.o 1938
13-
build-arduino_zero/py/objset.o 2018
14-
build-arduino_zero/py/obj.o 2038
15-
build-arduino_zero/py/objarray.o 2122
16-
build-arduino_zero/boards/arduino_zero/pins.o 2144
17-
build-arduino_zero/py/objdict.o 2146
18-
build-arduino_zero/py/objlist.o 2210
19-
build-arduino_zero/py/lexer.o 2405
20-
build-arduino_zero/py/objexcept.o 2466
21-
build-arduino_zero/asf/sam0/drivers/usb/stack_interface/usb_device_udd.o 2692
22-
build-arduino_zero/asf/sam0/drivers/usb/usb_sam_d_r/usb.o 3026
23-
build-arduino_zero/py/emitbc.o 3166
24-
build-arduino_zero/py/modbuiltins.o 3219
25-
build-arduino_zero/py/gc.o 3411
26-
build-arduino_zero/py/objtype.o 3579
27-
build-arduino_zero/py/vm.o 4259
28-
build-arduino_zero/py/runtime.o 4627
29-
build-arduino_zero/py/parse.o 4676
30-
build-arduino_zero/py/qstr.o 6589
31-
build-arduino_zero/py/objstr.o 9070
32-
build-arduino_zero/lib/fatfs/ff.o 10777
33-
build-arduino_zero/py/compile.o 11731
40+
41+
An example with --combine:
3442
```
43+
C:\Program Files\FreeBASIC-1.04.0-2-win32-mingworg\lib\win32\*.o 60 (code: 40 data: 20)
44+
c:/mingw/lib/libole32.a 78 (code: 8 data: 70)
45+
c:/mingw/lib/libshell32.a 124 (code: 16 data: 108)
46+
c:/mingw/lib/libpsapi.a 132 (code: 16 data: 116)
47+
c:/mingw/lib/libmoldname.a 132 (code: 24 data: 108)
48+
c:/mingw/lib/libadvapi32.a 168 (code: 24 data: 144)
49+
c:/mingw/lib/libuser32.a 196 (code: 32 data: 164)
50+
*fill* 432 (code: 28 data: 404)
51+
c:/mingw/lib/gcc/mingw32/4.8.1/*.o 552 (code: 260 data: 292)
52+
c:/mingw/lib/*.o 980 (code: 768 data: 212)
53+
c:/mingw/lib/libmsvcrt.a 2790 (code: 680 data: 2110)
54+
c:/mingw/lib/libkernel32.a 3140 (code: 624 data: 2516)
55+
c:/mingw/lib/gcc/mingw32/4.8.1/libgcc.a 3736 (code: 2476 data: 1260)
56+
c:/mingw/lib/libmingw32.a 4232 (code: 2848 data: 1384)
57+
build\lib\*.o 6244 (code: 6224 data: 20)
58+
c:/mingw/lib/gcc/mingw32/4.8.1/libgcc_eh.a 21252 (code: 16704 data: 4548)
59+
c:/mingw/lib/libmingwex.a 29992 (code: 24704 data: 5288)
60+
c:/mingw/lib/gcc/mingw32/4.8.1\libstdc++.a 49144 (code: 31624 data: 17520)
61+
C:\Program Files\FreeBASIC-1.04.0-win32-mingworg\lib\win32/libfbmt.a 51688 (code: 46272 data: 5416)
62+
build\*.o 168248 (code: 149456 data: 18792)
63+
TOTAL 343320 (code: 282828 data: 60492)
64+
```

analyze_map.py

100644100755
+80-25
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
#!/usr/bin/env python3
12
# MIT License
23
#
34
# Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
5+
# Copyright (c) 2018 Ralph Versteegen
46
#
57
# Permission is hereby granted, free of charge, to any person obtaining a copy
68
# of this software and associated documentation files (the "Software"), to deal
@@ -23,31 +25,84 @@
2325
from __future__ import print_function
2426

2527
import sys
28+
import os
29+
import argparse
30+
31+
parser = argparse.ArgumentParser(description='Summarises the size of each object file in an ld linker map.')
32+
parser.add_argument('map_file', help="A map file generated by passing -M/--print-map to ld during linking.")
33+
parser.add_argument('--combine', action='store_true',
34+
help="All object files in an .a archive or in a directory are combined")
35+
args = parser.parse_args()
36+
37+
class SectionSize():
38+
code = 0
39+
data = 0 # Including metadata like import tables
40+
def total(self):
41+
return self.code + self.data
42+
def add_section(self, section, size):
43+
if section.startswith('.text'):
44+
self.code += size
45+
elif section != '.bss':
46+
self.data += size
2647

2748
size_by_source = {}
28-
with open(sys.argv[1]) as f:
29-
memory_map_started = False
30-
current_section = None
31-
for line in f:
32-
if memory_map_started:
33-
if True or line.startswith((".", " .")):
34-
pieces = line.split()
35-
if line.startswith("."):
36-
current_section = pieces[0]
37-
elif len(pieces) >= 3 and current_section in [".rodata", ".text"] and "=" not in pieces and "before" not in pieces:
38-
if pieces[0] == "*fill*":
39-
source = pieces[0]
40-
size = int(pieces[-1], 16)
41-
else:
42-
source = pieces[-1]
43-
size = int(pieces[-2], 16)
44-
if source not in size_by_source:
45-
size_by_source[source] = 0
46-
size_by_source[source] += size
47-
elif line.strip() == "Linker script and memory map":
48-
memory_map_started = True
49-
50-
sources = size_by_source.keys()
51-
sources.sort(key=lambda x: size_by_source[x])
49+
with open(args.map_file) as f:
50+
lines = iter(f)
51+
for line in lines:
52+
if line.strip() == "Linker script and memory map":
53+
break
54+
55+
current_section = None
56+
split_line = None
57+
for line in lines:
58+
line = line.strip('\n')
59+
if split_line:
60+
# Glue a line that was split in two back together
61+
if line.startswith(' ' * 16):
62+
line = split_line + line
63+
else: # Shouldn't happen
64+
print("Warning: discarding line ", split_line)
65+
split_line = None
66+
67+
if line.startswith((".", " .", " *fill*")):
68+
pieces = line.split(None, 3) # Don't split paths containing spaces
69+
70+
if line.startswith("."):
71+
# Note: this line might be wrapped, with the size of the section
72+
# on the next line, but we ignore the size anyway and will ignore that line
73+
current_section = pieces[0]
74+
elif len(pieces) == 1 and len(line) > 14:
75+
# ld splits the rest of this line onto the next if the section name is too long
76+
split_line = line
77+
elif len(pieces) >= 3 and "=" not in pieces and "before" not in pieces:
78+
if pieces[0] == "*fill*":
79+
source = pieces[0]
80+
size = int(pieces[-1], 16)
81+
else:
82+
source = pieces[-1]
83+
size = int(pieces[-2], 16)
84+
85+
if args.combine:
86+
if '.a(' in source:
87+
# path/to/archive.a(object.o)
88+
source = source[:source.index('.a(') + 2]
89+
elif source.endswith('.o'):
90+
where = max(source.rfind('\\'), source.rfind('/'))
91+
if where:
92+
source = source[:where + 1] + '*.o'
93+
94+
if source not in size_by_source:
95+
size_by_source[source] = SectionSize()
96+
size_by_source[source].add_section(current_section, size)
97+
98+
# Print out summary
99+
sources = list(size_by_source.keys())
100+
sources.sort(key = lambda x: size_by_source[x].total())
101+
sumtotal = sumcode = sumdata = 0
52102
for source in sources:
53-
print(source, size_by_source[source])
103+
size = size_by_source[source]
104+
sumcode += size.code
105+
sumdata += size.data
106+
sumtotal += size.total()
107+
print("%-40s \t%7s (code: %d data: %d)" % (os.path.normpath(source), size.total(), size.code, size.data))
108+
print("TOTAL %d (code: %d data: %d)" % (sumtotal, sumcode, sumdata))

0 commit comments

Comments
 (0)