#!/usr/bin/env python # Script to arrange sections to ensure fixed offsets. # # Copyright (C) 2008 Kevin O'Connor # # This file may be distributed under the terms of the GNU GPLv3 license. import sys def alignpos(pos, alignbytes): mask = alignbytes - 1 return (pos + mask) & ~mask ###################################################################### # 16bit fixed address section fitting ###################################################################### MAXPOS = 0x10000 def doLayout16(sections, outname): textsections = [] rodatasections = [] datasections = [] # fixedsections = [(addr, sectioninfo, extasectionslist), ...] fixedsections = [] # canrelocate = [(sectioninfo, list), ...] canrelocate = [] # Find desired sections. for section in sections: size, align, name = section if name[:11] == '.fixedaddr.': addr = int(name[11:], 16) fixedsections.append((addr, section, [])) if align != 1: print "Error: Fixed section %s has non-zero alignment (%d)" % ( name, align) sys.exit(1) if name[:6] == '.text.': textsections.append(section) canrelocate.append((section, textsections)) if name[:17] == '.rodata.__func__.' or name == '.rodata.str1.1': rodatasections.append(section) #canrelocate.append((section, rodatasections)) if name[:8] == '.data16.': datasections.append(section) #canrelocate.append((section, datasections)) # Find freespace in fixed address area fixedsections.sort() # fixedAddr = [(freespace, sectioninfo), ...] fixedAddr = [] for i in range(len(fixedsections)): fixedsectioninfo = fixedsections[i] addr, section, extrasectionslist = fixedsectioninfo if i == len(fixedsections) - 1: nextaddr = MAXPOS else: nextaddr = fixedsections[i+1][0] avail = nextaddr - addr - section[0] fixedAddr.append((avail, fixedsectioninfo)) # Attempt to fit other sections into fixed area fixedAddr.sort() canrelocate.sort() totalused = 0 for freespace, fixedsectioninfo in fixedAddr: fixedaddr, fixedsection, extrasections = fixedsectioninfo addpos = fixedaddr + fixedsection[0] totalused += fixedsection[0] nextfixedaddr = addpos + freespace # print "Filling section %x uses %d, next=%x, available=%d" % ( # fixedaddr, fixedsection[0], nextfixedaddr, freespace) while 1: canfit = None for fixedaddrinfo in canrelocate: fitsection, inlist = fixedaddrinfo fitsize, fitalign, fitname = fitsection if addpos + fitsize > nextfixedaddr: # Can't fit and nothing else will fit. break fitnextaddr = alignpos(addpos, fitalign) + fitsize # print "Test %s - %x vs %x" % ( # fitname, fitnextaddr, nextfixedaddr) if fitnextaddr > nextfixedaddr: # This item can't fit. continue canfit = (fitnextaddr, fixedaddrinfo) if canfit is None: break # Found a section that can fit. fitnextaddr, fixedaddrinfo = canfit canrelocate.remove(fixedaddrinfo) fitsection, inlist = fixedaddrinfo inlist.remove(fitsection) extrasections.append(fitsection) addpos = fitnextaddr totalused += fitsection[0] # print " Adding %s (size %d align %d) pos=%x avail=%d" % ( # fitsection[2], fitsection[0], fitsection[1] # , fitnextaddr, nextfixedaddr - fitnextaddr) firstfixed = fixedsections[0][0] # Find overall start position restalign = 0 restspace = 0 restsections = [] for section in textsections + rodatasections + datasections: size, align, name = section if align > restalign: restalign = align restspace = alignpos(restspace, align) + size restsections.append(section) startrest = (firstfixed - restspace) / restalign * restalign # Report stats total = MAXPOS-firstfixed slack = total - totalused print ("Fixed space: 0x%x-0x%x total: %d slack: %d" " Percent slack: %.1f%%" % ( firstfixed, MAXPOS, total, slack, (float(slack) / total) * 100.0)) # Write header output = open(outname, 'wb') output.write(""" .text16 0x%x : { code16_start = ABSOLUTE(.) ; freespace_end = . ; """ % startrest) # Write regular sections for section in restsections: name = section[2] if name == rodatasections[0][2]: output.write("code16_rodata = . ;\n") output.write("*(%s)\n" % (name,)) # Write fixed sections for addr, section, extrasections in fixedsections: name = section[2] output.write(". = ( 0x%x - code16_start ) ;\n" % (addr,)) output.write("*(%s)\n" % (name,)) for extrasection in extrasections: output.write("*(%s)\n" % (extrasection[2],)) # Write trailer output.write(""" code16_end = ABSOLUTE(.) ; } """) ###################################################################### # 32bit section outputting ###################################################################### def outsections(file, sections, prefix): lp = len(prefix) for size, align, name in sections: if name[:lp] == prefix: file.write("*(%s)\n" % (name,)) def doLayout32(sections, outname): output = open(outname, 'wb') outsections(output, sections, '.text.') output.write("code32_rodata = . ;\n") outsections(output, sections, '.rodata') outsections(output, sections, '.data.') outsections(output, sections, '.bss.') ###################################################################### # Section garbage collection ###################################################################### def keepsection(name, pri, alt): if name in pri[3]: # Already kept - nothing to do. return pri[3].append(name) relocs = pri[2].get(name) if relocs is None: return # Keep all sections that this section points to for symbol in relocs: section = pri[1].get(symbol) if section is not None and section[:9] != '.discard.': keepsection(section, pri, alt) continue # Not in primary sections - it may be a cross 16/32 reference section = alt[1].get(symbol) if section is not None: keepsection(section, alt, pri) def gc(info16, info32): # pri = (sections, symbols, relocs, keep sections) pri = (info16[0], info16[1], info16[2], []) alt = (info32[0], info32[1], info32[2], []) # Start by keeping sections that are globally visible. for size, align, section in info16[0]: if section[:11] == '.fixedaddr.' or '.export.' in section: keepsection(section, pri, alt) # Return sections found. sections16 = [] for info in info16[0]: size, align, section = info if section not in pri[3]: # print "gc16", section continue sections16.append(info) sections32 = [] for info in info32[0]: size, align, section = info if section not in alt[3]: # print "gc32", section continue sections32.append(info) return sections16, sections32 ###################################################################### # Startup and input parsing ###################################################################### # Read in output from objdump def parseObjDump(file): # sections = [(size, align, section), ...] sections = [] # symbols[symbol] = section symbols = {} # relocs[section] = [symbol, ...] relocs = {} state = None for line in file.readlines(): line = line.rstrip() if line == 'Sections:': state = 'section' continue if line == 'SYMBOL TABLE:': state = 'symbol' continue if line[:24] == 'RELOCATION RECORDS FOR [': state = 'reloc' relocsection = line[24:-2] continue if state == 'section': try: idx, name, size, vma, lma, fileoff, align = line.split() if align[:3] != '2**': continue sections.append((int(size, 16), 2**int(align[3:]), name)) except: pass continue if state == 'symbol': try: section, off, symbol = line[17:].split() off = int(off, 16) if '*' not in section: symbols[symbol] = section except: pass continue if state == 'reloc': try: off, type, symbol = line.split() off = int(off, 16) relocs.setdefault(relocsection, []).append(symbol) except: pass return sections, symbols, relocs def main(): # Get output name in16, in32, out16, out32 = sys.argv[1:] infile16 = open(in16, 'rb') infile32 = open(in32, 'rb') info16 = parseObjDump(infile16) info32 = parseObjDump(infile32) sections16, sections32 = gc(info16, info32) doLayout16(sections16, out16) doLayout32(sections32, out32) if __name__ == '__main__': main()