|
|
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:
1.1.1.2 ! root 145: # Find start positions
! 146: text16_start = getSectionsStart(textsections, firstfixed)
! 147: data16_start = getSectionsStart(rodatasections + datasections, text16_start)
1.1 root 148:
1.1.1.2 ! root 149: # Write header and regular sections
1.1 root 150: output = open(outname, 'wb')
151: output.write(COMMONHEADER + """
1.1.1.2 ! root 152: data16_start = 0x%x ;
! 153: .data16 data16_start : {
! 154: """ % data16_start)
! 155: outSections(output, datasections)
1.1 root 156: output.write("code16_rodata = . ;\n")
157: outSections(output, rodatasections)
1.1.1.2 ! root 158: output.write("""
! 159: }
! 160:
! 161: text16_start = 0x%x ;
! 162: .text16 text16_start : {
! 163: """ % text16_start)
! 164: outSections(output, textsections)
1.1 root 165:
166: # Write fixed sections
167: for addr, section, extrasections in fixedsections:
168: name = section[2]
1.1.1.2 ! root 169: output.write(". = ( 0x%x - text16_start ) ;\n" % (addr,))
1.1 root 170: output.write("*(%s)\n" % (name,))
171: for extrasection in extrasections:
172: output.write("*(%s)\n" % (extrasection[2],))
173:
174: # Write trailer
175: output.write("""
1.1.1.2 ! root 176: text16_end = ABSOLUTE(.) ;
1.1 root 177: }
178:
179: /* Discard regular data sections to force a link error if
180: * 16bit code attempts to access data not marked with VAR16
181: */
182: /DISCARD/ : { *(.text*) *(.rodata*) *(.data*) *(.bss*) *(COMMON) }
183: """ + COMMONTRAILER)
184:
1.1.1.2 ! root 185: return data16_start
1.1 root 186:
187:
188: ######################################################################
189: # 32bit section outputting
190: ######################################################################
191:
192: # Return the subset of sections with a given name prefix
193: def getSectionsPrefix(sections, prefix):
194: lp = len(prefix)
195: out = []
196: for size, align, name in sections:
197: if name[:lp] == prefix:
198: out.append((size, align, name))
199: return out
200:
1.1.1.2 ! root 201: # Layout the 32bit segmented code. This places the code as high as possible.
! 202: def doLayout32seg(sections, outname, endat):
! 203: # Find sections to output
! 204: textsections = getSectionsPrefix(sections, '.text.')
! 205: rodatasections = (getSectionsPrefix(sections, '.rodata.str1.1')
! 206: + getSectionsPrefix(sections, '.rodata.__func__.'))
! 207: datasections = getSectionsPrefix(sections, '.data32seg.')
! 208: startat = getSectionsStart(
! 209: textsections + rodatasections + datasections, endat)
! 210:
! 211: # Write sections
! 212: output = open(outname, 'wb')
! 213: output.write(COMMONHEADER + """
! 214: code32seg_start = 0x%x ;
! 215: .text32seg code32seg_start : {
! 216: freespace_end = . ;
! 217: """ % startat)
! 218:
! 219: outSections(output, textsections)
! 220: output.write("code32seg_rodata = . ;\n")
! 221: outSections(output, rodatasections)
! 222: outSections(output, datasections)
! 223:
! 224: output.write("""
! 225: code32seg_end = ABSOLUTE(.) ;
! 226: }
! 227: /DISCARD/ : { *(.text*) *(.rodata*) *(.data*) *(.bss*) *(COMMON) }
! 228: """ + COMMONTRAILER)
! 229: return startat
! 230:
! 231: # Layout the 32bit flat code. This places the code as high as possible.
! 232: def doLayout32flat(sections, outname, endat):
! 233: endat += 0xf0000
1.1 root 234: # Find sections to output
235: textsections = getSectionsPrefix(sections, '.text.')
236: rodatasections = getSectionsPrefix(sections, '.rodata')
237: datasections = getSectionsPrefix(sections, '.data.')
238: bsssections = getSectionsPrefix(sections, '.bss.')
1.1.1.2 ! root 239: startat = getSectionsStart(
! 240: textsections + rodatasections + datasections + bsssections, endat, 512)
1.1 root 241:
242: # Write sections
243: output = open(outname, 'wb')
244: output.write(COMMONHEADER + """
1.1.1.2 ! root 245: code32flat_start = 0x%x ;
! 246: .text32flat code32flat_start : {
! 247: """ % startat)
1.1 root 248:
249: outSections(output, textsections)
250: output.write("code32_rodata = . ;\n")
251: outSections(output, rodatasections)
252: outSections(output, datasections)
253: outSections(output, bsssections)
254:
255: output.write("""
256: freespace_start = . ;
1.1.1.2 ! root 257: code32flat_end = ABSOLUTE(.) ;
1.1 root 258: }
259: """ + COMMONTRAILER)
1.1.1.2 ! root 260: return startat
1.1 root 261:
262:
263: ######################################################################
264: # Section garbage collection
265: ######################################################################
266:
1.1.1.2 ! root 267: def getSectionsList(info, names):
! 268: out = []
! 269: for i in info[0]:
! 270: size, align, section = i
! 271: if section not in names:
! 272: # print "gc", section
! 273: continue
! 274: out.append(i)
! 275: return out
! 276:
! 277: # Find and keep the section associated with a symbol (if available).
! 278: def keepsymbol(symbol, infos, pos):
! 279: addr, section = infos[pos][1].get(symbol, (None, None))
! 280: if section is None or '*' in section or section[:9] == '.discard.':
! 281: return -1
! 282: keepsection(section, infos, pos)
! 283: return 0
! 284:
1.1 root 285: # Note required section, and recursively set all referenced sections
286: # as required.
1.1.1.2 ! root 287: def keepsection(name, infos, pos=0):
! 288: if name in infos[pos][3]:
1.1 root 289: # Already kept - nothing to do.
290: return
1.1.1.2 ! root 291: infos[pos][3].append(name)
! 292: relocs = infos[pos][2].get(name)
1.1 root 293: if relocs is None:
294: return
295: # Keep all sections that this section points to
296: for symbol in relocs:
1.1.1.2 ! root 297: ret = keepsymbol(symbol, infos, pos)
! 298: if not ret:
1.1 root 299: continue
300: # Not in primary sections - it may be a cross 16/32 reference
1.1.1.2 ! root 301: ret = keepsymbol(symbol, infos, (pos+1)%3)
! 302: if not ret:
! 303: continue
! 304: ret = keepsymbol(symbol, infos, (pos+2)%3)
! 305: if not ret:
! 306: continue
1.1 root 307:
308: # Determine which sections are actually referenced and need to be
309: # placed into the output file.
1.1.1.2 ! root 310: def gc(info16, info32seg, info32flat):
! 311: # infos = ((sections, symbols, relocs, keep sections), ...)
! 312: infos = ((info16[0], info16[1], info16[2], []),
! 313: (info32seg[0], info32seg[1], info32seg[2], []),
! 314: (info32flat[0], info32flat[1], info32flat[2], []))
1.1 root 315: # Start by keeping sections that are globally visible.
316: for size, align, section in info16[0]:
317: if section[:11] == '.fixedaddr.' or '.export.' in section:
1.1.1.2 ! root 318: keepsection(section, infos)
1.1 root 319: # Return sections found.
1.1.1.2 ! root 320: sections16 = getSectionsList(info16, infos[0][3])
! 321: sections32seg = getSectionsList(info32seg, infos[1][3])
! 322: sections32flat = getSectionsList(info32flat, infos[2][3])
! 323: return sections16, sections32seg, sections32flat
1.1 root 324:
325:
326: ######################################################################
327: # Startup and input parsing
328: ######################################################################
329:
330: # Read in output from objdump
331: def parseObjDump(file):
332: # sections = [(size, align, section), ...]
333: sections = []
334: # symbols[symbol] = section
335: symbols = {}
336: # relocs[section] = [symbol, ...]
337: relocs = {}
338:
339: state = None
340: for line in file.readlines():
341: line = line.rstrip()
342: if line == 'Sections:':
343: state = 'section'
344: continue
345: if line == 'SYMBOL TABLE:':
346: state = 'symbol'
347: continue
348: if line[:24] == 'RELOCATION RECORDS FOR [':
349: state = 'reloc'
350: relocsection = line[24:-2]
351: continue
352:
353: if state == 'section':
354: try:
355: idx, name, size, vma, lma, fileoff, align = line.split()
356: if align[:3] != '2**':
357: continue
358: sections.append((int(size, 16), 2**int(align[3:]), name))
359: except:
360: pass
361: continue
362: if state == 'symbol':
363: try:
364: section, off, symbol = line[17:].split()
365: off = int(off, 16)
366: addr = int(line[:8], 16)
367: symbols[symbol] = addr, section
368: except:
369: pass
370: continue
371: if state == 'reloc':
372: try:
373: off, type, symbol = line.split()
374: off = int(off, 16)
375: relocs.setdefault(relocsection, []).append(symbol)
376: except:
377: pass
378: return sections, symbols, relocs
379:
380: def main():
381: # Get output name
1.1.1.2 ! root 382: in16, in32seg, in32flat, out16, out32seg, out32flat = sys.argv[1:]
1.1 root 383:
384: infile16 = open(in16, 'rb')
1.1.1.2 ! root 385: infile32seg = open(in32seg, 'rb')
! 386: infile32flat = open(in32flat, 'rb')
1.1 root 387:
388: info16 = parseObjDump(infile16)
1.1.1.2 ! root 389: info32seg = parseObjDump(infile32seg)
! 390: info32flat = parseObjDump(infile32flat)
1.1 root 391:
1.1.1.2 ! root 392: sections16, sections32seg, sections32flat = gc(info16, info32seg, info32flat)
1.1 root 393:
394: start16 = doLayout16(sections16, out16)
1.1.1.2 ! root 395: start32seg = doLayout32seg(sections32seg, out32seg, start16)
! 396: doLayout32flat(sections32flat, out32flat, start32seg)
1.1 root 397:
398: if __name__ == '__main__':
399: main()
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.