Simplify build by manually resolving external symbols in layoutrom.py.
[seabios.git] / tools / layoutrom.py
index d0ca9a6505f907dcefd79d868d749cca41df541b..5f8c3682e94176d03bd85cd6a7f0d155dae0c038 100755 (executable)
@@ -7,11 +7,6 @@
 
 import sys
 
-# Align 'pos' to 'alignbytes' offset
-def alignpos(pos, alignbytes):
-    mask = alignbytes - 1
-    return (pos + mask) & ~mask
-
 # LD script headers/trailers
 COMMONHEADER = """
 /* DO NOT EDIT!  This is an autogenerated file.  See tools/layoutrom.py. */
@@ -21,15 +16,29 @@ SECTIONS
 {
 """
 COMMONTRAILER = """
+
+        /* Discard regular data sections to force a link error if
+         * code attempts to access data not marked with VAR16 (or other
+         * appropriate macro)
+         */
+        /DISCARD/ : {
+                *(.text*) *(.data*) *(.bss*) *(.rodata*)
+                *(COMMON) *(.discard*) *(.eh_frame)
+                }
 }
 """
 
 
 ######################################################################
-# 16bit fixed address section fitting
+# Determine section locations
 ######################################################################
 
-# Get the maximum start position for a list of sections that end at an
+# Align 'pos' to 'alignbytes' offset
+def alignpos(pos, alignbytes):
+    mask = alignbytes - 1
+    return (pos + mask) & ~mask
+
+# Determine the final addresses for a list of sections that end at an
 # address.
 def getSectionsStart(sections, endaddr, minalign=1):
     totspace = 0
@@ -37,47 +46,46 @@ def getSectionsStart(sections, endaddr, minalign=1):
         if align > minalign:
             minalign = align
         totspace = alignpos(totspace, align) + size
-    return (endaddr - totspace) / minalign * minalign
+    startaddr = (endaddr - totspace) / minalign * minalign
+    curaddr = startaddr
+    # out = [(addr, sectioninfo), ...]
+    out = []
+    for sectioninfo in sections:
+        size, align, name = sectioninfo
+        curaddr = alignpos(curaddr, align)
+        out.append((curaddr, sectioninfo))
+        curaddr += size
+    return out, startaddr
 
-# Write LD script includes for the given sections
-def outSections(file, sections):
+# Return the subset of sections with a given name prefix
+def getSectionsPrefix(sections, prefix):
+    lp = len(prefix)
+    out = []
     for size, align, name in sections:
-        file.write("*(%s)\n" % (name,))
+        if name[:lp] == prefix:
+            out.append((size, align, name))
+    return out
 
 # The 16bit code can't exceed 64K of space.
-MAXPOS = 64*1024
+BUILD_BIOS_ADDR = 0xf0000
+BUILD_BIOS_SIZE = 0x10000
 
 # Layout the 16bit code.  This ensures sections with fixed offset
 # requirements are placed in the correct location.  It also places the
 # 16bit code as high as possible in the f-segment.
-def doLayout16(sections, outname):
-    textsections = []
-    rodatasections = []
-    datasections = []
-    # fixedsections = [(addr, sectioninfo, extasectionslist), ...]
+def fitSections(sections, fillsections):
+    canrelocate = list(fillsections)
+    # fixedsections = [(addr, sectioninfo), ...]
     fixedsections = []
-    # canrelocate = [(sectioninfo, list), ...]
-    canrelocate = []
-
-    # Find desired sections.
-    for section in sections:
-        size, align, name = section
+    for sectioninfo in sections:
+        size, align, name = sectioninfo
         if name[:11] == '.fixedaddr.':
             addr = int(name[11:], 16)
-            fixedsections.append((addr, section, []))
+            fixedsections.append((addr, sectioninfo))
             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()
@@ -85,20 +93,21 @@ def doLayout16(sections, outname):
     fixedAddr = []
     for i in range(len(fixedsections)):
         fixedsectioninfo = fixedsections[i]
-        addr, section, extrasectionslist = fixedsectioninfo
+        addr, section = fixedsectioninfo
         if i == len(fixedsections) - 1:
-            nextaddr = MAXPOS
+            nextaddr = BUILD_BIOS_SIZE
         else:
             nextaddr = fixedsections[i+1][0]
         avail = nextaddr - addr - section[0]
         fixedAddr.append((avail, fixedsectioninfo))
 
     # Attempt to fit other sections into fixed area
+    extrasections = []
     fixedAddr.sort()
     canrelocate.sort()
     totalused = 0
     for freespace, fixedsectioninfo in fixedAddr:
-        fixedaddr, fixedsection, extrasections = fixedsectioninfo
+        fixedaddr, fixedsection = fixedsectioninfo
         addpos = fixedaddr + fixedsection[0]
         totalused += fixedsection[0]
         nextfixedaddr = addpos + freespace
@@ -106,8 +115,7 @@ def doLayout16(sections, outname):
 #            fixedaddr, fixedsection[0], nextfixedaddr, freespace)
         while 1:
             canfit = None
-            for fixedaddrinfo in canrelocate:
-                fitsection, inlist = fixedaddrinfo
+            for fitsection in canrelocate:
                 fitsize, fitalign, fitname = fitsection
                 if addpos + fitsize > nextfixedaddr:
                     # Can't fit and nothing else will fit.
@@ -118,15 +126,13 @@ def doLayout16(sections, outname):
                 if fitnextaddr > nextfixedaddr:
                     # This item can't fit.
                     continue
-                canfit = (fitnextaddr, fixedaddrinfo)
+                canfit = (fitnextaddr, fitsection)
             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)
+            fitnextaddr, fitsection = canfit
+            canrelocate.remove(fitsection)
+            extrasections.append((addpos, fitsection))
             addpos = fitnextaddr
             totalused += fitsection[0]
 #            print "    Adding %s (size %d align %d) pos=%x avail=%d" % (
@@ -135,150 +141,166 @@ def doLayout16(sections, outname):
     firstfixed = fixedsections[0][0]
 
     # Report stats
-    total = MAXPOS-firstfixed
+    total = BUILD_BIOS_SIZE-firstfixed
     slack = total - totalused
     print ("Fixed space: 0x%x-0x%x  total: %d  slack: %d"
            "  Percent slack: %.1f%%" % (
-            firstfixed, MAXPOS, total, slack,
+            firstfixed, BUILD_BIOS_SIZE, total, slack,
             (float(slack) / total) * 100.0))
 
-    # Find start positions
-    text16_start = getSectionsStart(textsections, firstfixed)
-    data16_start = getSectionsStart(rodatasections + datasections, text16_start)
-
-    # Write header and regular sections
-    output = open(outname, 'wb')
-    output.write(COMMONHEADER + """
-        data16_start = 0x%x ;
-        .data16 data16_start : {
-""" % data16_start)
-    outSections(output, datasections)
-    output.write("code16_rodata = . ;\n")
-    outSections(output, rodatasections)
-    output.write("""
-        }
-
-        text16_start = 0x%x ;
-        .text16 text16_start : {
-""" % text16_start)
-    outSections(output, textsections)
-
-    # Write fixed sections
-    for addr, section, extrasections in fixedsections:
-        name = section[2]
-        output.write(". = ( 0x%x - text16_start ) ;\n" % (addr,))
-        output.write("*(%s)\n" % (name,))
-        for extrasection in extrasections:
-            output.write("*(%s)\n" % (extrasection[2],))
-
-    # Write trailer
-    output.write("""
-                text16_end = ABSOLUTE(.) ;
-        }
-
-        /* Discard regular data sections to force a link error if
-         * 16bit code attempts to access data not marked with VAR16
-         */
-        /DISCARD/ : { *(.text*) *(.rodata*) *(.data*) *(.bss*) *(COMMON) }
-""" + COMMONTRAILER)
-
-    return data16_start
+    return fixedsections + extrasections, firstfixed
+
+def doLayout(sections16, sections32seg, sections32flat):
+    # Determine 16bit positions
+    textsections = getSectionsPrefix(sections16, '.text.')
+    rodatasections = (getSectionsPrefix(sections16, '.rodata.str1.1')
+                      + getSectionsPrefix(sections16, '.rodata.__func__.'))
+    datasections = getSectionsPrefix(sections16, '.data16.')
+    fixedsections = getSectionsPrefix(sections16, '.fixedaddr.')
+
+    locs16fixed, firstfixed = fitSections(fixedsections, textsections)
+    prunesections = [i[1] for i in locs16fixed]
+    remsections = [i for i in textsections+rodatasections+datasections
+                   if i not in prunesections]
+    locs16, code16_start = getSectionsStart(remsections, firstfixed)
+    locs16 = locs16 + locs16fixed
+    locs16.sort()
+
+    # Determine 32seg positions
+    textsections = getSectionsPrefix(sections32seg, '.text.')
+    rodatasections = (getSectionsPrefix(sections32seg, '.rodata.str1.1')
+                      + getSectionsPrefix(sections32seg, '.rodata.__func__.'))
+    datasections = getSectionsPrefix(sections32seg, '.data32seg.')
+
+    locs32seg, code32seg_start = getSectionsStart(
+        textsections + rodatasections + datasections, code16_start)
+
+    # Determine 32flat positions
+    textsections = getSectionsPrefix(sections32flat, '.text.')
+    rodatasections = getSectionsPrefix(sections32flat, '.rodata')
+    datasections = getSectionsPrefix(sections32flat, '.data.')
+    bsssections = getSectionsPrefix(sections32flat, '.bss.')
+
+    locs32flat, code32flat_start = getSectionsStart(
+        textsections + rodatasections + datasections + bsssections
+        , code32seg_start + BUILD_BIOS_ADDR, 16)
+
+    # Print statistics
+    size16 = BUILD_BIOS_SIZE - code16_start
+    size32seg = code16_start - code32seg_start
+    size32flat = code32seg_start + BUILD_BIOS_ADDR - code32flat_start
+    print "16bit size:           %d" % size16
+    print "32bit segmented size: %d" % size32seg
+    print "32bit flat size:      %d" % size32flat
+
+    return locs16, locs32seg, locs32flat
 
 
 ######################################################################
-# 32bit section outputting
+# Linker script output
 ######################################################################
 
-# Return the subset of sections with a given name prefix
-def getSectionsPrefix(sections, prefix):
-    lp = len(prefix)
-    out = []
-    for size, align, name in sections:
-        if name[:lp] == prefix:
-            out.append((size, align, name))
+# Write LD script includes for the given cross references
+def outXRefs(xrefs, finallocs, delta=0):
+    out = ""
+    for symbol, (fileid, section, addr) in xrefs.items():
+        if fileid < 2:
+            addr += delta
+        out += "%s = 0x%x ;\n" % (symbol, finallocs[(fileid, section)] + addr)
+    return out
+
+# Write LD script includes for the given sections using relative offsets
+def outRelSections(locs, startsym):
+    out = ""
+    for addr, sectioninfo in locs:
+        size, align, name = sectioninfo
+        out += ". = ( 0x%x - %s ) ;\n" % (addr, startsym)
+        if name == '.rodata.str1.1':
+            out += "_rodata = . ;\n"
+        out += "*(%s)\n" % (name,)
     return out
 
 # Layout the 32bit segmented code.  This places the code as high as possible.
-def doLayout32seg(sections, outname, endat):
-    # Find sections to output
-    textsections = getSectionsPrefix(sections, '.text.')
-    rodatasections = (getSectionsPrefix(sections, '.rodata.str1.1')
-                      + getSectionsPrefix(sections, '.rodata.__func__.'))
-    datasections = getSectionsPrefix(sections, '.data32seg.')
-    startat = getSectionsStart(
-        textsections + rodatasections + datasections, endat)
-
-    # Write sections
-    output = open(outname, 'wb')
-    output.write(COMMONHEADER + """
-        code32seg_start = 0x%x ;
-        .text32seg code32seg_start : {
-                freespace_end = . ;
-""" % startat)
-
-    outSections(output, textsections)
-    output.write("code32seg_rodata = . ;\n")
-    outSections(output, rodatasections)
-    outSections(output, datasections)
-
-    output.write("""
-                code32seg_end = ABSOLUTE(.) ;
-        }
-        /DISCARD/ : { *(.text*) *(.rodata*) *(.data*) *(.bss*) *(COMMON) }
-""" + COMMONTRAILER)
-    return startat
-
-# Layout the 32bit flat code.  This places the code as high as possible.
-def doLayout32flat(sections, outname, endat):
-    endat += 0xf0000
-    # Find sections to output
-    textsections = getSectionsPrefix(sections, '.text.')
-    rodatasections = getSectionsPrefix(sections, '.rodata')
-    datasections = getSectionsPrefix(sections, '.data.')
-    bsssections = getSectionsPrefix(sections, '.bss.')
-    startat = getSectionsStart(
-        textsections + rodatasections + datasections + bsssections, endat, 512)
-
-    # Write sections
-    output = open(outname, 'wb')
-    output.write(COMMONHEADER + """
-        code32flat_start = 0x%x ;
-        .text32flat code32flat_start : {
-""" % startat)
-
-    outSections(output, textsections)
-    output.write("code32_rodata = . ;\n")
-    outSections(output, rodatasections)
-    outSections(output, datasections)
-    outSections(output, bsssections)
-
-    output.write("""
-                freespace_start = . ;
-                code32flat_end = ABSOLUTE(.) ;
-        }
-""" + COMMONTRAILER)
-    return startat
+def writeLinkerScripts(locs16, locs32seg, locs32flat
+                       , xref16, xref32seg, xref32flat
+                       , out16, out32seg, out32flat):
+    # Index to final location for each section
+    # finallocs[(fileid, section)] = addr
+    finallocs = {}
+    for fileid, locs in ((0, locs16), (1, locs32seg), (2, locs32flat)):
+        for addr, sectioninfo in locs:
+            finallocs[(fileid, sectioninfo[2])] = addr
+
+    # Write 16bit linker script
+    code16_start = locs16[0][0]
+    output = open(out16, 'wb')
+    output.write(COMMONHEADER + outXRefs(xref16, finallocs) + """
+    code16_start = 0x%x ;
+    .text16 code16_start : {
+""" % (code16_start)
+                 + outRelSections(locs16, 'code16_start')
+                 + """
+    }
+"""
+                 + COMMONTRAILER)
+    output.close()
+
+    # Write 32seg linker script
+    code32seg_start = code16_start
+    if locs32seg:
+        code32seg_start = locs32seg[0][0]
+    output = open(out32seg, 'wb')
+    output.write(COMMONHEADER + outXRefs(xref32seg, finallocs) + """
+    code32seg_start = 0x%x ;
+    .text32seg code32seg_start : {
+""" % (code32seg_start)
+                 + outRelSections(locs32seg, 'code32seg_start')
+                 + """
+    }
+"""
+                 + COMMONTRAILER)
+    output.close()
+
+    # Write 32flat linker script
+    output = open(out32flat, 'wb')
+    output.write(COMMONHEADER
+                 + outXRefs(xref32flat, finallocs, BUILD_BIOS_ADDR) + """
+    code32flat_start = 0x%x ;
+    .text code32flat_start : {
+""" % (locs32flat[0][0])
+                 + outRelSections(locs32flat, 'code32flat_start')
+                 + """
+        . = ( 0x%x - code32flat_start ) ;
+        *(.text32seg)
+        . = ( 0x%x - code32flat_start ) ;
+        *(.text16)
+        code32flat_end = ABSOLUTE(.) ;
+    } :text
+""" % (code32seg_start + BUILD_BIOS_ADDR, code16_start + BUILD_BIOS_ADDR)
+                 + COMMONTRAILER
+                 + """
+ENTRY(post32)
+PHDRS
+{
+        text PT_LOAD AT ( code32flat_start ) ;
+}
+""")
+    output.close()
 
 
 ######################################################################
 # Section garbage collection
 ######################################################################
 
-def getSectionsList(info, names):
-    out = []
-    for i in info[0]:
-        size, align, section = i
-        if section not in names:
-#            print "gc", section
-            continue
-        out.append(i)
-    return out
-
 # Find and keep the section associated with a symbol (if available).
-def keepsymbol(symbol, infos, pos):
+def keepsymbol(symbol, infos, pos, callerpos=None):
     addr, section = infos[pos][1].get(symbol, (None, None))
     if section is None or '*' in section or section[:9] == '.discard.':
         return -1
+    if callerpos is not None and symbol not in infos[callerpos][4]:
+        # This symbol reference is a cross section reference (an xref).
+        # xref[symbol] = (fileid, section, addr)
+        infos[callerpos][4][symbol] = (pos, section, addr)
     keepsection(section, infos, pos)
     return 0
 
@@ -298,29 +320,34 @@ def keepsection(name, infos, pos=0):
         if not ret:
             continue
         # Not in primary sections - it may be a cross 16/32 reference
-        ret = keepsymbol(symbol, infos, (pos+1)%3)
+        ret = keepsymbol(symbol, infos, (pos+1)%3, pos)
         if not ret:
             continue
-        ret = keepsymbol(symbol, infos, (pos+2)%3)
+        ret = keepsymbol(symbol, infos, (pos+2)%3, pos)
         if not ret:
             continue
 
+# Return a list of kept sections.
+def getSectionsList(sections, names):
+    return [i for i in sections if i[2] in names]
+
 # Determine which sections are actually referenced and need to be
 # placed into the output file.
 def gc(info16, info32seg, info32flat):
-    # infos = ((sections, symbols, relocs, keep sections), ...)
-    infos = ((info16[0], info16[1], info16[2], []),
-             (info32seg[0], info32seg[1], info32seg[2], []),
-             (info32flat[0], info32flat[1], info32flat[2], []))
+    # infos = ((sections, symbols, relocs, keep sections, xrefs), ...)
+    infos = ((info16[0], info16[1], info16[2], [], {}),
+             (info32seg[0], info32seg[1], info32seg[2], [], {}),
+             (info32flat[0], info32flat[1], info32flat[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, infos)
+    keepsymbol('post32', infos, 0, 2)
     # Return sections found.
-    sections16 = getSectionsList(info16, infos[0][3])
-    sections32seg = getSectionsList(info32seg, infos[1][3])
-    sections32flat = getSectionsList(info32flat, infos[2][3])
-    return sections16, sections32seg, sections32flat
+    keep16 = getSectionsList(info16[0], infos[0][3]), infos[0][4]
+    keep32seg = getSectionsList(info32seg[0], infos[1][3]), infos[1][4]
+    keep32flat = getSectionsList(info32flat[0], infos[2][3]), infos[2][4]
+    return keep16, keep32seg, keep32flat
 
 
 ######################################################################
@@ -331,7 +358,7 @@ def gc(info16, info32seg, info32flat):
 def parseObjDump(file):
     # sections = [(size, align, section), ...]
     sections = []
-    # symbols[symbol] = section
+    # symbols[symbol] = (addr, section)
     symbols = {}
     # relocs[section] = [symbol, ...]
     relocs = {}
@@ -361,8 +388,8 @@ def parseObjDump(file):
             continue
         if state == 'symbol':
             try:
-                section, off, symbol = line[17:].split()
-                off = int(off, 16)
+                section, size, symbol = line[17:].split()
+                size = int(size, 16)
                 addr = int(line[:8], 16)
                 symbols[symbol] = addr, section
             except:
@@ -381,19 +408,29 @@ def main():
     # Get output name
     in16, in32seg, in32flat, out16, out32seg, out32flat = sys.argv[1:]
 
+    # Read in the objdump information
     infile16 = open(in16, 'rb')
     infile32seg = open(in32seg, 'rb')
     infile32flat = open(in32flat, 'rb')
 
+    # infoX = (sections, symbols, relocs)
     info16 = parseObjDump(infile16)
     info32seg = parseObjDump(infile32seg)
     info32flat = parseObjDump(infile32flat)
 
-    sections16, sections32seg, sections32flat = gc(info16, info32seg, info32flat)
+    # Figure out which sections to keep.
+    # keepX = (sections, xrefs)
+    keep16, keep32seg, keep32flat = gc(info16, info32seg, info32flat)
+
+    # Determine the final memory locations of each kept section.
+    # locsX = [(addr, sectioninfo), ...]
+    locs16, locs32seg, locs32flat = doLayout(
+        keep16[0], keep32seg[0], keep32flat[0])
 
-    start16 = doLayout16(sections16, out16)
-    start32seg = doLayout32seg(sections32seg, out32seg, start16)
-    doLayout32flat(sections32flat, out32flat, start32seg)
+    # Write out linker script files.
+    writeLinkerScripts(locs16, locs32seg, locs32flat
+                       , keep16[1], keep32seg[1], keep32flat[1]
+                       , out16, out32seg, out32flat)
 
 if __name__ == '__main__':
     main()