|
|
1.1 root 1: #!/usr/bin/env python
2: # Script to analyze code and arrange ld sections.
3: #
4: # Copyright (C) 2008 Kevin O'Connor <[email protected]>
5: #
6: # This file may be distributed under the terms of the GNU GPLv3 license.
7:
8: import sys
9:
10: # Align 'pos' to 'alignbytes' offset
11: def alignpos(pos, alignbytes):
12: mask = alignbytes - 1
13: return (pos + mask) & ~mask
14:
15: # LD script headers/trailers
16: COMMONHEADER = """
17: /* DO NOT EDIT! This is an autogenerated file. See tools/layoutrom.py. */
18: OUTPUT_FORMAT("elf32-i386")
19: OUTPUT_ARCH("i386")
20: SECTIONS
21: {
22: """
23: COMMONTRAILER = """
24: }
25: """
26:
27:
28: ######################################################################
29: # 16bit fixed address section fitting
30: ######################################################################
31:
32: # Get the maximum start position for a list of sections that end at an
33: # address.
34: def getSectionsStart(sections, endaddr, minalign=1):
35: totspace = 0
36: for size, align, name in sections:
37: if align > minalign:
38: minalign = align
39: totspace = alignpos(totspace, align) + size
40: return (endaddr - totspace) / minalign * minalign
41:
42: # Write LD script includes for the given sections
43: def outSections(file, sections):
44: for size, align, name in sections:
45: file.write("*(%s)\n" % (name,))
46:
47: # The 16bit code can't exceed 64K of space.
48: MAXPOS = 64*1024
49:
50: # Layout the 16bit code. This ensures sections with fixed offset
51: # requirements are placed in the correct location. It also places the
52: # 16bit code as high as possible in the f-segment.
53: def doLayout16(sections, outname):
54: textsections = []
55: rodatasections = []
56: datasections = []
57: # fixedsections = [(addr, sectioninfo, extasectionslist), ...]
58: fixedsections = []
59: # canrelocate = [(sectioninfo, list), ...]
60: canrelocate = []
61:
62: # Find desired sections.
63: for section in sections:
64: size, align, name = section
65: if name[:11] == '.fixedaddr.':
66: addr = int(name[11:], 16)
67: fixedsections.append((addr, section, []))
68: if align != 1:
69: print "Error: Fixed section %s has non-zero alignment (%d)" % (
70: name, align)
71: sys.exit(1)
72: if name[:6] == '.text.':
73: textsections.append(section)
74: canrelocate.append((section, textsections))
75: if name[:17] == '.rodata.__func__.' or name == '.rodata.str1.1':
76: rodatasections.append(section)
77: #canrelocate.append((section, rodatasections))
78: if name[:8] == '.data16.':
79: datasections.append(section)
80: #canrelocate.append((section, datasections))
81:
82: # Find freespace in fixed address area
83: fixedsections.sort()
84: # fixedAddr = [(freespace, sectioninfo), ...]
85: fixedAddr = []
86: for i in range(len(fixedsections)):
87: fixedsectioninfo = fixedsections[i]
88: addr, section, extrasectionslist = fixedsectioninfo
89: if i == len(fixedsections) - 1:
90: nextaddr = MAXPOS
91: else:
92: nextaddr = fixedsections[i+1][0]
93: avail = nextaddr - addr - section[0]
94: fixedAddr.append((avail, fixedsectioninfo))
95:
96: # Attempt to fit other sections into fixed area
97: fixedAddr.sort()
98: canrelocate.sort()
99: totalused = 0
100: for freespace, fixedsectioninfo in fixedAddr:
101: fixedaddr, fixedsection, extrasections = fixedsectioninfo
102: addpos = fixedaddr + fixedsection[0]
103: totalused += fixedsection[0]
104: nextfixedaddr = addpos + freespace
105: # print "Filling section %x uses %d, next=%x, available=%d" % (
106: # fixedaddr, fixedsection[0], nextfixedaddr, freespace)
107: while 1:
108: canfit = None
109: for fixedaddrinfo in canrelocate:
110: fitsection, inlist = fixedaddrinfo
111: fitsize, fitalign, fitname = fitsection
112: if addpos + fitsize > nextfixedaddr:
113: # Can't fit and nothing else will fit.
114: break
115: fitnextaddr = alignpos(addpos, fitalign) + fitsize
116: # print "Test %s - %x vs %x" % (
117: # fitname, fitnextaddr, nextfixedaddr)
118: if fitnextaddr > nextfixedaddr:
119: # This item can't fit.
120: continue
121: canfit = (fitnextaddr, fixedaddrinfo)
122: if canfit is None:
123: break
124: # Found a section that can fit.
125: fitnextaddr, fixedaddrinfo = canfit
126: canrelocate.remove(fixedaddrinfo)
127: fitsection, inlist = fixedaddrinfo
128: inlist.remove(fitsection)
129: extrasections.append(fitsection)
130: addpos = fitnextaddr
131: totalused += fitsection[0]
132: # print " Adding %s (size %d align %d) pos=%x avail=%d" % (
133: # fitsection[2], fitsection[0], fitsection[1]
134: # , fitnextaddr, nextfixedaddr - fitnextaddr)
135: firstfixed = fixedsections[0][0]
136:
137: # Report stats
138: total = MAXPOS-firstfixed
139: slack = total - totalused
140: print ("Fixed space: 0x%x-0x%x total: %d slack: %d"
141: " Percent slack: %.1f%%" % (
142: firstfixed, MAXPOS, total, slack,
143: (float(slack) / total) * 100.0))
144:
145: # Find overall start position
146: start16 = getSectionsStart(
147: textsections + rodatasections + datasections, firstfixed)
148:
149: # Write header
150: output = open(outname, 'wb')
151: output.write(COMMONHEADER + """
152: .text16 0x%x : {
153: code16_start = ABSOLUTE(.) ;
154: freespace_end = . ;
155: """ % start16)
156:
157: # Write regular sections
158: outSections(output, textsections)
159: output.write("code16_rodata = . ;\n")
160: outSections(output, rodatasections)
161: outSections(output, datasections)
162:
163: # Write fixed sections
164: for addr, section, extrasections in fixedsections:
165: name = section[2]
166: output.write(". = ( 0x%x - code16_start ) ;\n" % (addr,))
167: output.write("*(%s)\n" % (name,))
168: for extrasection in extrasections:
169: output.write("*(%s)\n" % (extrasection[2],))
170:
171: # Write trailer
172: output.write("""
173: code16_end = ABSOLUTE(.) ;
174: }
175:
176: /* Discard regular data sections to force a link error if
177: * 16bit code attempts to access data not marked with VAR16
178: */
179: /DISCARD/ : { *(.text*) *(.rodata*) *(.data*) *(.bss*) *(COMMON) }
180: """ + COMMONTRAILER)
181:
182: return start16
183:
184:
185: ######################################################################
186: # 32bit section outputting
187: ######################################################################
188:
189: # Return the subset of sections with a given name prefix
190: def getSectionsPrefix(sections, prefix):
191: lp = len(prefix)
192: out = []
193: for size, align, name in sections:
194: if name[:lp] == prefix:
195: out.append((size, align, name))
196: return out
197:
198: # Layout the 32bit code. This places the code as high as possible.
199: def doLayout32(sections, outname, start16):
200: start16 += 0xf0000
201: # Find sections to output
202: textsections = getSectionsPrefix(sections, '.text.')
203: rodatasections = getSectionsPrefix(sections, '.rodata')
204: datasections = getSectionsPrefix(sections, '.data.')
205: bsssections = getSectionsPrefix(sections, '.bss.')
206: start32 = getSectionsStart(
207: textsections + rodatasections + datasections + bsssections, start16, 512)
208:
209: # Write sections
210: output = open(outname, 'wb')
211: output.write(COMMONHEADER + """
212: .text32 0x%x : {
213: code32_start = ABSOLUTE(.) ;
214: """ % start32)
215:
216: outSections(output, textsections)
217: output.write("code32_rodata = . ;\n")
218: outSections(output, rodatasections)
219: outSections(output, datasections)
220: outSections(output, bsssections)
221:
222: output.write("""
223: freespace_start = . ;
224: code32_end = ABSOLUTE(.) ;
225: }
226: """ + COMMONTRAILER)
227:
228:
229: ######################################################################
230: # Section garbage collection
231: ######################################################################
232:
233: # Note required section, and recursively set all referenced sections
234: # as required.
235: def keepsection(name, pri, alt):
236: if name in pri[3]:
237: # Already kept - nothing to do.
238: return
239: pri[3].append(name)
240: relocs = pri[2].get(name)
241: if relocs is None:
242: return
243: # Keep all sections that this section points to
244: for symbol in relocs:
245: addr, section = pri[1].get(symbol, (None, None))
246: if (section is not None and '*' not in section
247: and section[:9] != '.discard.'):
248: keepsection(section, pri, alt)
249: continue
250: # Not in primary sections - it may be a cross 16/32 reference
251: addr, section = alt[1].get(symbol, (None, None))
252: if section is not None and '*' not in section:
253: keepsection(section, alt, pri)
254:
255: # Determine which sections are actually referenced and need to be
256: # placed into the output file.
257: def gc(info16, info32):
258: # pri = (sections, symbols, relocs, keep sections)
259: pri = (info16[0], info16[1], info16[2], [])
260: alt = (info32[0], info32[1], info32[2], [])
261: # Start by keeping sections that are globally visible.
262: for size, align, section in info16[0]:
263: if section[:11] == '.fixedaddr.' or '.export.' in section:
264: keepsection(section, pri, alt)
265: # Return sections found.
266: sections16 = []
267: for info in info16[0]:
268: size, align, section = info
269: if section not in pri[3]:
270: # print "gc16", section
271: continue
272: sections16.append(info)
273: sections32 = []
274: for info in info32[0]:
275: size, align, section = info
276: if section not in alt[3]:
277: # print "gc32", section
278: continue
279: sections32.append(info)
280: return sections16, sections32
281:
282:
283: ######################################################################
284: # Startup and input parsing
285: ######################################################################
286:
287: # Read in output from objdump
288: def parseObjDump(file):
289: # sections = [(size, align, section), ...]
290: sections = []
291: # symbols[symbol] = section
292: symbols = {}
293: # relocs[section] = [symbol, ...]
294: relocs = {}
295:
296: state = None
297: for line in file.readlines():
298: line = line.rstrip()
299: if line == 'Sections:':
300: state = 'section'
301: continue
302: if line == 'SYMBOL TABLE:':
303: state = 'symbol'
304: continue
305: if line[:24] == 'RELOCATION RECORDS FOR [':
306: state = 'reloc'
307: relocsection = line[24:-2]
308: continue
309:
310: if state == 'section':
311: try:
312: idx, name, size, vma, lma, fileoff, align = line.split()
313: if align[:3] != '2**':
314: continue
315: sections.append((int(size, 16), 2**int(align[3:]), name))
316: except:
317: pass
318: continue
319: if state == 'symbol':
320: try:
321: section, off, symbol = line[17:].split()
322: off = int(off, 16)
323: addr = int(line[:8], 16)
324: symbols[symbol] = addr, section
325: except:
326: pass
327: continue
328: if state == 'reloc':
329: try:
330: off, type, symbol = line.split()
331: off = int(off, 16)
332: relocs.setdefault(relocsection, []).append(symbol)
333: except:
334: pass
335: return sections, symbols, relocs
336:
337: def main():
338: # Get output name
339: in16, in32, out16, out32 = sys.argv[1:]
340:
341: infile16 = open(in16, 'rb')
342: infile32 = open(in32, 'rb')
343:
344: info16 = parseObjDump(infile16)
345: info32 = parseObjDump(infile32)
346:
347: sections16, sections32 = gc(info16, info32)
348:
349: start16 = doLayout16(sections16, out16)
350: doLayout32(sections32, out32, start16)
351:
352: if __name__ == '__main__':
353: main()
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.