|
|
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.