Fix bug causing layoutrom.py to break if no rodata sections.
[seabios.git] / tools / layoutrom.py
1 #!/usr/bin/env python
2 # Script to arrange sections to ensure fixed offsets.
3 #
4 # Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
5 #
6 # This file may be distributed under the terms of the GNU GPLv3 license.
7
8 import sys
9
10
11 def alignpos(pos, alignbytes):
12     mask = alignbytes - 1
13     return (pos + mask) & ~mask
14
15
16 ######################################################################
17 # 16bit fixed address section fitting
18 ######################################################################
19
20 MAXPOS = 0x10000
21
22 def doLayout16(sections, outname):
23     textsections = []
24     rodatasections = []
25     datasections = []
26     # fixedsections = [(addr, sectioninfo, extasectionslist), ...]
27     fixedsections = []
28     # canrelocate = [(sectioninfo, list), ...]
29     canrelocate = []
30
31     # Find desired sections.
32     for section in sections:
33         size, align, name = section
34         if name[:11] == '.fixedaddr.':
35             addr = int(name[11:], 16)
36             fixedsections.append((addr, section, []))
37             if align != 1:
38                 print "Error: Fixed section %s has non-zero alignment (%d)" % (
39                     name, align)
40                 sys.exit(1)
41         if name[:6] == '.text.':
42             textsections.append(section)
43             canrelocate.append((section, textsections))
44         if name[:17] == '.rodata.__func__.' or name == '.rodata.str1.1':
45             rodatasections.append(section)
46             #canrelocate.append((section, rodatasections))
47         if name[:8] == '.data16.':
48             datasections.append(section)
49             #canrelocate.append((section, datasections))
50
51     # Find freespace in fixed address area
52     fixedsections.sort()
53     # fixedAddr = [(freespace, sectioninfo), ...]
54     fixedAddr = []
55     for i in range(len(fixedsections)):
56         fixedsectioninfo = fixedsections[i]
57         addr, section, extrasectionslist = fixedsectioninfo
58         if i == len(fixedsections) - 1:
59             nextaddr = MAXPOS
60         else:
61             nextaddr = fixedsections[i+1][0]
62         avail = nextaddr - addr - section[0]
63         fixedAddr.append((avail, fixedsectioninfo))
64
65     # Attempt to fit other sections into fixed area
66     fixedAddr.sort()
67     canrelocate.sort()
68     totalused = 0
69     for freespace, fixedsectioninfo in fixedAddr:
70         fixedaddr, fixedsection, extrasections = fixedsectioninfo
71         addpos = fixedaddr + fixedsection[0]
72         totalused += fixedsection[0]
73         nextfixedaddr = addpos + freespace
74 #        print "Filling section %x uses %d, next=%x, available=%d" % (
75 #            fixedaddr, fixedsection[0], nextfixedaddr, freespace)
76         while 1:
77             canfit = None
78             for fixedaddrinfo in canrelocate:
79                 fitsection, inlist = fixedaddrinfo
80                 fitsize, fitalign, fitname = fitsection
81                 if addpos + fitsize > nextfixedaddr:
82                     # Can't fit and nothing else will fit.
83                     break
84                 fitnextaddr = alignpos(addpos, fitalign) + fitsize
85 #                print "Test %s - %x vs %x" % (
86 #                    fitname, fitnextaddr, nextfixedaddr)
87                 if fitnextaddr > nextfixedaddr:
88                     # This item can't fit.
89                     continue
90                 canfit = (fitnextaddr, fixedaddrinfo)
91             if canfit is None:
92                 break
93             # Found a section that can fit.
94             fitnextaddr, fixedaddrinfo = canfit
95             canrelocate.remove(fixedaddrinfo)
96             fitsection, inlist = fixedaddrinfo
97             inlist.remove(fitsection)
98             extrasections.append(fitsection)
99             addpos = fitnextaddr
100             totalused += fitsection[0]
101 #            print "    Adding %s (size %d align %d) pos=%x avail=%d" % (
102 #                fitsection[2], fitsection[0], fitsection[1]
103 #                , fitnextaddr, nextfixedaddr - fitnextaddr)
104     firstfixed = fixedsections[0][0]
105
106     # Find overall start position
107     restalign = 0
108     restspace = 0
109     restsections = []
110     for section in textsections + rodatasections + datasections:
111         size, align, name = section
112         if align > restalign:
113             restalign = align
114         restspace = alignpos(restspace, align) + size
115         restsections.append(section)
116     startrest = (firstfixed - restspace) / restalign * restalign
117
118     # Report stats
119     total = MAXPOS-firstfixed
120     slack = total - totalused
121     print ("Fixed space: 0x%x-0x%x  total: %d  slack: %d"
122            "  Percent slack: %.1f%%" % (
123             firstfixed, MAXPOS, total, slack,
124             (float(slack) / total) * 100.0))
125
126     # Write header
127     output = open(outname, 'wb')
128     output.write("""
129         .text16 0x%x : {
130                 code16_start = ABSOLUTE(.) ;
131                 freespace_end = . ;
132 """ % startrest)
133
134     # Write regular sections
135     for section in restsections:
136         name = section[2]
137         if rodatasections and name == rodatasections[0][2]:
138             output.write("code16_rodata = . ;\n")
139         output.write("*(%s)\n" % (name,))
140
141     # Write fixed sections
142     for addr, section, extrasections in fixedsections:
143         name = section[2]
144         output.write(". = ( 0x%x - code16_start ) ;\n" % (addr,))
145         output.write("*(%s)\n" % (name,))
146         for extrasection in extrasections:
147             output.write("*(%s)\n" % (extrasection[2],))
148
149     # Write trailer
150     output.write("""
151                 code16_end = ABSOLUTE(.) ;
152         }
153 """)
154
155
156 ######################################################################
157 # 32bit section outputting
158 ######################################################################
159
160 def outsections(file, sections, prefix):
161     lp = len(prefix)
162     for size, align, name in sections:
163         if name[:lp] == prefix:
164             file.write("*(%s)\n" % (name,))
165
166 def doLayout32(sections, outname):
167     output = open(outname, 'wb')
168     outsections(output, sections, '.text.')
169     output.write("code32_rodata = . ;\n")
170     outsections(output, sections, '.rodata')
171     outsections(output, sections, '.data.')
172     outsections(output, sections, '.bss.')
173
174
175 ######################################################################
176 # Section garbage collection
177 ######################################################################
178
179 def keepsection(name, pri, alt):
180     if name in pri[3]:
181         # Already kept - nothing to do.
182         return
183     pri[3].append(name)
184     relocs = pri[2].get(name)
185     if relocs is None:
186         return
187     # Keep all sections that this section points to
188     for symbol in relocs:
189         section = pri[1].get(symbol)
190         if section is not None and section[:9] != '.discard.':
191             keepsection(section, pri, alt)
192             continue
193         # Not in primary sections - it may be a cross 16/32 reference
194         section = alt[1].get(symbol)
195         if section is not None:
196             keepsection(section, alt, pri)
197
198 def gc(info16, info32):
199     # pri = (sections, symbols, relocs, keep sections)
200     pri = (info16[0], info16[1], info16[2], [])
201     alt = (info32[0], info32[1], info32[2], [])
202     # Start by keeping sections that are globally visible.
203     for size, align, section in info16[0]:
204         if section[:11] == '.fixedaddr.' or '.export.' in section:
205             keepsection(section, pri, alt)
206     # Return sections found.
207     sections16 = []
208     for info in info16[0]:
209         size, align, section = info
210         if section not in pri[3]:
211 #            print "gc16", section
212             continue
213         sections16.append(info)
214     sections32 = []
215     for info in info32[0]:
216         size, align, section = info
217         if section not in alt[3]:
218 #            print "gc32", section
219             continue
220         sections32.append(info)
221     return sections16, sections32
222
223
224 ######################################################################
225 # Startup and input parsing
226 ######################################################################
227
228 # Read in output from objdump
229 def parseObjDump(file):
230     # sections = [(size, align, section), ...]
231     sections = []
232     # symbols[symbol] = section
233     symbols = {}
234     # relocs[section] = [symbol, ...]
235     relocs = {}
236
237     state = None
238     for line in file.readlines():
239         line = line.rstrip()
240         if line == 'Sections:':
241             state = 'section'
242             continue
243         if line == 'SYMBOL TABLE:':
244             state = 'symbol'
245             continue
246         if line[:24] == 'RELOCATION RECORDS FOR [':
247             state = 'reloc'
248             relocsection = line[24:-2]
249             continue
250
251         if state == 'section':
252             try:
253                 idx, name, size, vma, lma, fileoff, align = line.split()
254                 if align[:3] != '2**':
255                     continue
256                 sections.append((int(size, 16), 2**int(align[3:]), name))
257             except:
258                 pass
259             continue
260         if state == 'symbol':
261             try:
262                 section, off, symbol = line[17:].split()
263                 off = int(off, 16)
264                 if '*' not in section:
265                     symbols[symbol] = section
266             except:
267                 pass
268             continue
269         if state == 'reloc':
270             try:
271                 off, type, symbol = line.split()
272                 off = int(off, 16)
273                 relocs.setdefault(relocsection, []).append(symbol)
274             except:
275                 pass
276     return sections, symbols, relocs
277
278 def main():
279     # Get output name
280     in16, in32, out16, out32 = sys.argv[1:]
281
282     infile16 = open(in16, 'rb')
283     infile32 = open(in32, 'rb')
284
285     info16 = parseObjDump(infile16)
286     info32 = parseObjDump(infile32)
287
288     sections16, sections32 = gc(info16, info32)
289
290     doLayout16(sections16, out16)
291     doLayout32(sections32, out32)
292
293 if __name__ == '__main__':
294     main()