Annotation of qemu/roms/seabios/tools/layoutrom.py, revision 1.1.1.3

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: # LD script headers/trailers
                     11: COMMONHEADER = """
                     12: /* DO NOT EDIT!  This is an autogenerated file.  See tools/layoutrom.py. */
                     13: OUTPUT_FORMAT("elf32-i386")
                     14: OUTPUT_ARCH("i386")
                     15: SECTIONS
                     16: {
                     17: """
                     18: COMMONTRAILER = """
1.1.1.3 ! root       19: 
        !            20:         /* Discard regular data sections to force a link error if
        !            21:          * code attempts to access data not marked with VAR16 (or other
        !            22:          * appropriate macro)
        !            23:          */
        !            24:         /DISCARD/ : {
        !            25:                 *(.text*) *(.data*) *(.bss*) *(.rodata*)
        !            26:                 *(COMMON) *(.discard*) *(.eh_frame)
        !            27:                 }
1.1       root       28: }
                     29: """
                     30: 
                     31: 
                     32: ######################################################################
1.1.1.3 ! root       33: # Determine section locations
1.1       root       34: ######################################################################
                     35: 
1.1.1.3 ! root       36: # Align 'pos' to 'alignbytes' offset
        !            37: def alignpos(pos, alignbytes):
        !            38:     mask = alignbytes - 1
        !            39:     return (pos + mask) & ~mask
        !            40: 
        !            41: # Determine the final addresses for a list of sections that end at an
1.1       root       42: # address.
                     43: def getSectionsStart(sections, endaddr, minalign=1):
                     44:     totspace = 0
                     45:     for size, align, name in sections:
                     46:         if align > minalign:
                     47:             minalign = align
                     48:         totspace = alignpos(totspace, align) + size
1.1.1.3 ! root       49:     startaddr = (endaddr - totspace) / minalign * minalign
        !            50:     curaddr = startaddr
        !            51:     # out = [(addr, sectioninfo), ...]
        !            52:     out = []
        !            53:     for sectioninfo in sections:
        !            54:         size, align, name = sectioninfo
        !            55:         curaddr = alignpos(curaddr, align)
        !            56:         out.append((curaddr, sectioninfo))
        !            57:         curaddr += size
        !            58:     return out, startaddr
1.1       root       59: 
1.1.1.3 ! root       60: # Return the subset of sections with a given name prefix
        !            61: def getSectionsPrefix(sections, prefix):
        !            62:     lp = len(prefix)
        !            63:     out = []
1.1       root       64:     for size, align, name in sections:
1.1.1.3 ! root       65:         if name[:lp] == prefix:
        !            66:             out.append((size, align, name))
        !            67:     return out
1.1       root       68: 
                     69: # The 16bit code can't exceed 64K of space.
1.1.1.3 ! root       70: BUILD_BIOS_ADDR = 0xf0000
        !            71: BUILD_BIOS_SIZE = 0x10000
1.1       root       72: 
                     73: # Layout the 16bit code.  This ensures sections with fixed offset
                     74: # requirements are placed in the correct location.  It also places the
                     75: # 16bit code as high as possible in the f-segment.
1.1.1.3 ! root       76: def fitSections(sections, fillsections):
        !            77:     canrelocate = list(fillsections)
        !            78:     # fixedsections = [(addr, sectioninfo), ...]
1.1       root       79:     fixedsections = []
1.1.1.3 ! root       80:     for sectioninfo in sections:
        !            81:         size, align, name = sectioninfo
1.1       root       82:         if name[:11] == '.fixedaddr.':
                     83:             addr = int(name[11:], 16)
1.1.1.3 ! root       84:             fixedsections.append((addr, sectioninfo))
1.1       root       85:             if align != 1:
                     86:                 print "Error: Fixed section %s has non-zero alignment (%d)" % (
                     87:                     name, align)
                     88:                 sys.exit(1)
                     89: 
                     90:     # Find freespace in fixed address area
                     91:     fixedsections.sort()
                     92:     # fixedAddr = [(freespace, sectioninfo), ...]
                     93:     fixedAddr = []
                     94:     for i in range(len(fixedsections)):
                     95:         fixedsectioninfo = fixedsections[i]
1.1.1.3 ! root       96:         addr, section = fixedsectioninfo
1.1       root       97:         if i == len(fixedsections) - 1:
1.1.1.3 ! root       98:             nextaddr = BUILD_BIOS_SIZE
1.1       root       99:         else:
                    100:             nextaddr = fixedsections[i+1][0]
                    101:         avail = nextaddr - addr - section[0]
                    102:         fixedAddr.append((avail, fixedsectioninfo))
                    103: 
                    104:     # Attempt to fit other sections into fixed area
1.1.1.3 ! root      105:     extrasections = []
1.1       root      106:     fixedAddr.sort()
                    107:     canrelocate.sort()
                    108:     totalused = 0
                    109:     for freespace, fixedsectioninfo in fixedAddr:
1.1.1.3 ! root      110:         fixedaddr, fixedsection = fixedsectioninfo
1.1       root      111:         addpos = fixedaddr + fixedsection[0]
                    112:         totalused += fixedsection[0]
                    113:         nextfixedaddr = addpos + freespace
                    114: #        print "Filling section %x uses %d, next=%x, available=%d" % (
                    115: #            fixedaddr, fixedsection[0], nextfixedaddr, freespace)
                    116:         while 1:
                    117:             canfit = None
1.1.1.3 ! root      118:             for fitsection in canrelocate:
1.1       root      119:                 fitsize, fitalign, fitname = fitsection
                    120:                 if addpos + fitsize > nextfixedaddr:
                    121:                     # Can't fit and nothing else will fit.
                    122:                     break
                    123:                 fitnextaddr = alignpos(addpos, fitalign) + fitsize
                    124: #                print "Test %s - %x vs %x" % (
                    125: #                    fitname, fitnextaddr, nextfixedaddr)
                    126:                 if fitnextaddr > nextfixedaddr:
                    127:                     # This item can't fit.
                    128:                     continue
1.1.1.3 ! root      129:                 canfit = (fitnextaddr, fitsection)
1.1       root      130:             if canfit is None:
                    131:                 break
                    132:             # Found a section that can fit.
1.1.1.3 ! root      133:             fitnextaddr, fitsection = canfit
        !           134:             canrelocate.remove(fitsection)
        !           135:             extrasections.append((addpos, fitsection))
1.1       root      136:             addpos = fitnextaddr
                    137:             totalused += fitsection[0]
                    138: #            print "    Adding %s (size %d align %d) pos=%x avail=%d" % (
                    139: #                fitsection[2], fitsection[0], fitsection[1]
                    140: #                , fitnextaddr, nextfixedaddr - fitnextaddr)
                    141:     firstfixed = fixedsections[0][0]
                    142: 
                    143:     # Report stats
1.1.1.3 ! root      144:     total = BUILD_BIOS_SIZE-firstfixed
1.1       root      145:     slack = total - totalused
                    146:     print ("Fixed space: 0x%x-0x%x  total: %d  slack: %d"
                    147:            "  Percent slack: %.1f%%" % (
1.1.1.3 ! root      148:             firstfixed, BUILD_BIOS_SIZE, total, slack,
1.1       root      149:             (float(slack) / total) * 100.0))
                    150: 
1.1.1.3 ! root      151:     return fixedsections + extrasections, firstfixed
1.1       root      152: 
1.1.1.3 ! root      153: def doLayout(sections16, sections32seg, sections32flat):
        !           154:     # Determine 16bit positions
        !           155:     textsections = getSectionsPrefix(sections16, '.text.')
        !           156:     rodatasections = (getSectionsPrefix(sections16, '.rodata.str1.1')
        !           157:                       + getSectionsPrefix(sections16, '.rodata.__func__.'))
        !           158:     datasections = getSectionsPrefix(sections16, '.data16.')
        !           159:     fixedsections = getSectionsPrefix(sections16, '.fixedaddr.')
        !           160: 
        !           161:     locs16fixed, firstfixed = fitSections(fixedsections, textsections)
        !           162:     prunesections = [i[1] for i in locs16fixed]
        !           163:     remsections = [i for i in textsections+rodatasections+datasections
        !           164:                    if i not in prunesections]
        !           165:     locs16, code16_start = getSectionsStart(remsections, firstfixed)
        !           166:     locs16 = locs16 + locs16fixed
        !           167:     locs16.sort()
        !           168: 
        !           169:     # Determine 32seg positions
        !           170:     textsections = getSectionsPrefix(sections32seg, '.text.')
        !           171:     rodatasections = (getSectionsPrefix(sections32seg, '.rodata.str1.1')
        !           172:                       + getSectionsPrefix(sections32seg, '.rodata.__func__.'))
        !           173:     datasections = getSectionsPrefix(sections32seg, '.data32seg.')
        !           174: 
        !           175:     locs32seg, code32seg_start = getSectionsStart(
        !           176:         textsections + rodatasections + datasections, code16_start)
        !           177: 
        !           178:     # Determine 32flat positions
        !           179:     textsections = getSectionsPrefix(sections32flat, '.text.')
        !           180:     rodatasections = getSectionsPrefix(sections32flat, '.rodata')
        !           181:     datasections = getSectionsPrefix(sections32flat, '.data.')
        !           182:     bsssections = getSectionsPrefix(sections32flat, '.bss.')
        !           183: 
        !           184:     locs32flat, code32flat_start = getSectionsStart(
        !           185:         textsections + rodatasections + datasections + bsssections
        !           186:         , code32seg_start + BUILD_BIOS_ADDR, 16)
        !           187: 
        !           188:     # Print statistics
        !           189:     size16 = BUILD_BIOS_SIZE - code16_start
        !           190:     size32seg = code16_start - code32seg_start
        !           191:     size32flat = code32seg_start + BUILD_BIOS_ADDR - code32flat_start
        !           192:     print "16bit size:           %d" % size16
        !           193:     print "32bit segmented size: %d" % size32seg
        !           194:     print "32bit flat size:      %d" % size32flat
1.1       root      195: 
1.1.1.3 ! root      196:     return locs16, locs32seg, locs32flat
1.1       root      197: 
                    198: 
                    199: ######################################################################
1.1.1.3 ! root      200: # Linker script output
1.1       root      201: ######################################################################
                    202: 
1.1.1.3 ! root      203: # Write LD script includes for the given cross references
        !           204: def outXRefs(xrefs, finallocs, delta=0):
        !           205:     out = ""
        !           206:     for symbol, (fileid, section, addr) in xrefs.items():
        !           207:         if fileid < 2:
        !           208:             addr += delta
        !           209:         out += "%s = 0x%x ;\n" % (symbol, finallocs[(fileid, section)] + addr)
        !           210:     return out
        !           211: 
        !           212: # Write LD script includes for the given sections using relative offsets
        !           213: def outRelSections(locs, startsym):
        !           214:     out = ""
        !           215:     for addr, sectioninfo in locs:
        !           216:         size, align, name = sectioninfo
        !           217:         out += ". = ( 0x%x - %s ) ;\n" % (addr, startsym)
        !           218:         if name == '.rodata.str1.1':
        !           219:             out += "_rodata = . ;\n"
        !           220:         out += "*(%s)\n" % (name,)
1.1       root      221:     return out
                    222: 
1.1.1.2   root      223: # Layout the 32bit segmented code.  This places the code as high as possible.
1.1.1.3 ! root      224: def writeLinkerScripts(locs16, locs32seg, locs32flat
        !           225:                        , xref16, xref32seg, xref32flat
        !           226:                        , out16, out32seg, out32flat):
        !           227:     # Index to final location for each section
        !           228:     # finallocs[(fileid, section)] = addr
        !           229:     finallocs = {}
        !           230:     for fileid, locs in ((0, locs16), (1, locs32seg), (2, locs32flat)):
        !           231:         for addr, sectioninfo in locs:
        !           232:             finallocs[(fileid, sectioninfo[2])] = addr
        !           233: 
        !           234:     # Write 16bit linker script
        !           235:     code16_start = locs16[0][0]
        !           236:     output = open(out16, 'wb')
        !           237:     output.write(COMMONHEADER + outXRefs(xref16, finallocs) + """
        !           238:     code16_start = 0x%x ;
        !           239:     .text16 code16_start : {
        !           240: """ % (code16_start)
        !           241:                  + outRelSections(locs16, 'code16_start')
        !           242:                  + """
        !           243:     }
        !           244: """
        !           245:                  + COMMONTRAILER)
        !           246:     output.close()
        !           247: 
        !           248:     # Write 32seg linker script
        !           249:     code32seg_start = code16_start
        !           250:     if locs32seg:
        !           251:         code32seg_start = locs32seg[0][0]
        !           252:     output = open(out32seg, 'wb')
        !           253:     output.write(COMMONHEADER + outXRefs(xref32seg, finallocs) + """
        !           254:     code32seg_start = 0x%x ;
        !           255:     .text32seg code32seg_start : {
        !           256: """ % (code32seg_start)
        !           257:                  + outRelSections(locs32seg, 'code32seg_start')
        !           258:                  + """
        !           259:     }
        !           260: """
        !           261:                  + COMMONTRAILER)
        !           262:     output.close()
        !           263: 
        !           264:     # Write 32flat linker script
        !           265:     output = open(out32flat, 'wb')
        !           266:     output.write(COMMONHEADER
        !           267:                  + outXRefs(xref32flat, finallocs, BUILD_BIOS_ADDR) + """
        !           268:     code32flat_start = 0x%x ;
        !           269:     .text code32flat_start : {
        !           270: """ % (locs32flat[0][0])
        !           271:                  + outRelSections(locs32flat, 'code32flat_start')
        !           272:                  + """
        !           273:         . = ( 0x%x - code32flat_start ) ;
        !           274:         *(.text32seg)
        !           275:         . = ( 0x%x - code32flat_start ) ;
        !           276:         *(.text16)
        !           277:         code32flat_end = ABSOLUTE(.) ;
        !           278:     } :text
        !           279: """ % (code32seg_start + BUILD_BIOS_ADDR, code16_start + BUILD_BIOS_ADDR)
        !           280:                  + COMMONTRAILER
        !           281:                  + """
        !           282: ENTRY(post32)
        !           283: PHDRS
        !           284: {
        !           285:         text PT_LOAD AT ( code32flat_start ) ;
        !           286: }
        !           287: """)
        !           288:     output.close()
1.1       root      289: 
                    290: 
                    291: ######################################################################
                    292: # Section garbage collection
                    293: ######################################################################
                    294: 
1.1.1.2   root      295: # Find and keep the section associated with a symbol (if available).
1.1.1.3 ! root      296: def keepsymbol(symbol, infos, pos, callerpos=None):
1.1.1.2   root      297:     addr, section = infos[pos][1].get(symbol, (None, None))
                    298:     if section is None or '*' in section or section[:9] == '.discard.':
                    299:         return -1
1.1.1.3 ! root      300:     if callerpos is not None and symbol not in infos[callerpos][4]:
        !           301:         # This symbol reference is a cross section reference (an xref).
        !           302:         # xref[symbol] = (fileid, section, addr)
        !           303:         infos[callerpos][4][symbol] = (pos, section, addr)
1.1.1.2   root      304:     keepsection(section, infos, pos)
                    305:     return 0
                    306: 
1.1       root      307: # Note required section, and recursively set all referenced sections
                    308: # as required.
1.1.1.2   root      309: def keepsection(name, infos, pos=0):
                    310:     if name in infos[pos][3]:
1.1       root      311:         # Already kept - nothing to do.
                    312:         return
1.1.1.2   root      313:     infos[pos][3].append(name)
                    314:     relocs = infos[pos][2].get(name)
1.1       root      315:     if relocs is None:
                    316:         return
                    317:     # Keep all sections that this section points to
                    318:     for symbol in relocs:
1.1.1.2   root      319:         ret = keepsymbol(symbol, infos, pos)
                    320:         if not ret:
1.1       root      321:             continue
                    322:         # Not in primary sections - it may be a cross 16/32 reference
1.1.1.3 ! root      323:         ret = keepsymbol(symbol, infos, (pos+1)%3, pos)
1.1.1.2   root      324:         if not ret:
                    325:             continue
1.1.1.3 ! root      326:         ret = keepsymbol(symbol, infos, (pos+2)%3, pos)
1.1.1.2   root      327:         if not ret:
                    328:             continue
1.1       root      329: 
1.1.1.3 ! root      330: # Return a list of kept sections.
        !           331: def getSectionsList(sections, names):
        !           332:     return [i for i in sections if i[2] in names]
        !           333: 
1.1       root      334: # Determine which sections are actually referenced and need to be
                    335: # placed into the output file.
1.1.1.2   root      336: def gc(info16, info32seg, info32flat):
1.1.1.3 ! root      337:     # infos = ((sections, symbols, relocs, keep sections, xrefs), ...)
        !           338:     infos = ((info16[0], info16[1], info16[2], [], {}),
        !           339:              (info32seg[0], info32seg[1], info32seg[2], [], {}),
        !           340:              (info32flat[0], info32flat[1], info32flat[2], [], {}))
1.1       root      341:     # Start by keeping sections that are globally visible.
                    342:     for size, align, section in info16[0]:
                    343:         if section[:11] == '.fixedaddr.' or '.export.' in section:
1.1.1.2   root      344:             keepsection(section, infos)
1.1.1.3 ! root      345:     keepsymbol('post32', infos, 0, 2)
1.1       root      346:     # Return sections found.
1.1.1.3 ! root      347:     keep16 = getSectionsList(info16[0], infos[0][3]), infos[0][4]
        !           348:     keep32seg = getSectionsList(info32seg[0], infos[1][3]), infos[1][4]
        !           349:     keep32flat = getSectionsList(info32flat[0], infos[2][3]), infos[2][4]
        !           350:     return keep16, keep32seg, keep32flat
1.1       root      351: 
                    352: 
                    353: ######################################################################
                    354: # Startup and input parsing
                    355: ######################################################################
                    356: 
                    357: # Read in output from objdump
                    358: def parseObjDump(file):
                    359:     # sections = [(size, align, section), ...]
                    360:     sections = []
1.1.1.3 ! root      361:     # symbols[symbol] = (addr, section)
1.1       root      362:     symbols = {}
                    363:     # relocs[section] = [symbol, ...]
                    364:     relocs = {}
                    365: 
                    366:     state = None
                    367:     for line in file.readlines():
                    368:         line = line.rstrip()
                    369:         if line == 'Sections:':
                    370:             state = 'section'
                    371:             continue
                    372:         if line == 'SYMBOL TABLE:':
                    373:             state = 'symbol'
                    374:             continue
                    375:         if line[:24] == 'RELOCATION RECORDS FOR [':
                    376:             state = 'reloc'
                    377:             relocsection = line[24:-2]
                    378:             continue
                    379: 
                    380:         if state == 'section':
                    381:             try:
                    382:                 idx, name, size, vma, lma, fileoff, align = line.split()
                    383:                 if align[:3] != '2**':
                    384:                     continue
                    385:                 sections.append((int(size, 16), 2**int(align[3:]), name))
                    386:             except:
                    387:                 pass
                    388:             continue
                    389:         if state == 'symbol':
                    390:             try:
1.1.1.3 ! root      391:                 section, size, symbol = line[17:].split()
        !           392:                 size = int(size, 16)
1.1       root      393:                 addr = int(line[:8], 16)
                    394:                 symbols[symbol] = addr, section
                    395:             except:
                    396:                 pass
                    397:             continue
                    398:         if state == 'reloc':
                    399:             try:
                    400:                 off, type, symbol = line.split()
                    401:                 off = int(off, 16)
                    402:                 relocs.setdefault(relocsection, []).append(symbol)
                    403:             except:
                    404:                 pass
                    405:     return sections, symbols, relocs
                    406: 
                    407: def main():
                    408:     # Get output name
1.1.1.2   root      409:     in16, in32seg, in32flat, out16, out32seg, out32flat = sys.argv[1:]
1.1       root      410: 
1.1.1.3 ! root      411:     # Read in the objdump information
1.1       root      412:     infile16 = open(in16, 'rb')
1.1.1.2   root      413:     infile32seg = open(in32seg, 'rb')
                    414:     infile32flat = open(in32flat, 'rb')
1.1       root      415: 
1.1.1.3 ! root      416:     # infoX = (sections, symbols, relocs)
1.1       root      417:     info16 = parseObjDump(infile16)
1.1.1.2   root      418:     info32seg = parseObjDump(infile32seg)
                    419:     info32flat = parseObjDump(infile32flat)
1.1       root      420: 
1.1.1.3 ! root      421:     # Figure out which sections to keep.
        !           422:     # keepX = (sections, xrefs)
        !           423:     keep16, keep32seg, keep32flat = gc(info16, info32seg, info32flat)
        !           424: 
        !           425:     # Determine the final memory locations of each kept section.
        !           426:     # locsX = [(addr, sectioninfo), ...]
        !           427:     locs16, locs32seg, locs32flat = doLayout(
        !           428:         keep16[0], keep32seg[0], keep32flat[0])
        !           429: 
        !           430:     # Write out linker script files.
        !           431:     writeLinkerScripts(locs16, locs32seg, locs32flat
        !           432:                        , keep16[1], keep32seg[1], keep32flat[1]
        !           433:                        , out16, out32seg, out32flat)
1.1       root      434: 
                    435: if __name__ == '__main__':
                    436:     main()

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.