Allow rom to grow beyond 64K.
[seabios.git] / tools / layoutrom.py
index 8d557bc1c74e9420adc6aa07dd5a003561122c72..da2940f9241e6df790b4de6d86ac7c24a30e50b5 100755 (executable)
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-# Script to arrange sections to ensure fixed offsets.
+# Script to analyze code and arrange ld sections.
 #
 # Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
 #
@@ -7,18 +7,49 @@
 
 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. */
+OUTPUT_FORMAT("elf32-i386")
+OUTPUT_ARCH("i386")
+SECTIONS
+{
+"""
+COMMONTRAILER = """
+}
+"""
+
 
 ######################################################################
 # 16bit fixed address section fitting
 ######################################################################
 
-MAXPOS = 0x10000
+# Get the maximum start position for a list of sections that end at an
+# address.
+def getSectionsStart(sections, endaddr, minalign=1):
+    totspace = 0
+    for size, align, name in sections:
+        if align > minalign:
+            minalign = align
+        totspace = alignpos(totspace, align) + size
+    return (endaddr - totspace) / minalign * minalign
+
+# Write LD script includes for the given sections
+def outSections(file, sections):
+    for size, align, name in sections:
+        file.write("*(%s)\n" % (name,))
+
+# The 16bit code can't exceed 64K of space.
+MAXPOS = 64*1024
 
+# 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 = []
@@ -103,18 +134,6 @@ def doLayout16(sections, outname):
 #                , 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
@@ -123,20 +142,23 @@ def doLayout16(sections, outname):
             firstfixed, MAXPOS, total, slack,
             (float(slack) / total) * 100.0))
 
+    # Find overall start position
+    start16 = getSectionsStart(
+        textsections + rodatasections + datasections, firstfixed)
+
     # Write header
     output = open(outname, 'wb')
-    output.write("""
+    output.write(COMMONHEADER + """
         .text16 0x%x : {
                 code16_start = ABSOLUTE(.) ;
                 freespace_end = . ;
-""" % startrest)
+""" % start16)
 
     # Write regular sections
-    for section in restsections:
-        name = section[2]
-        if rodatasections and name == rodatasections[0][2]:
-            output.write("code16_rodata = . ;\n")
-        output.write("*(%s)\n" % (name,))
+    outSections(output, textsections)
+    output.write("code16_rodata = . ;\n")
+    outSections(output, rodatasections)
+    outSections(output, datasections)
 
     # Write fixed sections
     for addr, section, extrasections in fixedsections:
@@ -150,32 +172,66 @@ def doLayout16(sections, outname):
     output.write("""
                 code16_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 start16
 
 
 ######################################################################
 # 32bit section outputting
 ######################################################################
 
-def outsections(file, sections, prefix):
+# 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:
-            file.write("*(%s)\n" % (name,))
-
-def doLayout32(sections, outname):
+            out.append((size, align, name))
+    return out
+
+# Layout the 32bit code.  This places the code as high as possible.
+def doLayout32(sections, outname, start16):
+    start16 += 0xf0000
+    # Find sections to output
+    textsections = getSectionsPrefix(sections, '.text.')
+    rodatasections = getSectionsPrefix(sections, '.rodata')
+    datasections = getSectionsPrefix(sections, '.data.')
+    bsssections = getSectionsPrefix(sections, '.bss.')
+    start32 = getSectionsStart(
+        textsections + rodatasections + datasections + bsssections, start16, 512)
+
+    # Write sections
     output = open(outname, 'wb')
-    outsections(output, sections, '.text.')
+    output.write(COMMONHEADER + """
+        .text32 0x%x : {
+                code32_start = ABSOLUTE(.) ;
+""" % start32)
+
+    outSections(output, textsections)
     output.write("code32_rodata = . ;\n")
-    outsections(output, sections, '.rodata')
-    outsections(output, sections, '.data.')
-    outsections(output, sections, '.bss.')
+    outSections(output, rodatasections)
+    outSections(output, datasections)
+    outSections(output, bsssections)
+
+    output.write("""
+                freespace_start = . ;
+                code32_end = ABSOLUTE(.) ;
+        }
+""" + COMMONTRAILER)
 
 
 ######################################################################
 # Section garbage collection
 ######################################################################
 
+# Note required section, and recursively set all referenced sections
+# as required.
 def keepsection(name, pri, alt):
     if name in pri[3]:
         # Already kept - nothing to do.
@@ -186,15 +242,18 @@ def keepsection(name, pri, alt):
         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.':
+        addr, section = pri[1].get(symbol, (None, None))
+        if (section is not None and '*' not in section
+            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:
+        addr, section = alt[1].get(symbol, (None, None))
+        if section is not None and '*' not in section:
             keepsection(section, alt, pri)
 
+# Determine which sections are actually referenced and need to be
+# placed into the output file.
 def gc(info16, info32):
     # pri = (sections, symbols, relocs, keep sections)
     pri = (info16[0], info16[1], info16[2], [])
@@ -261,8 +320,8 @@ def parseObjDump(file):
             try:
                 section, off, symbol = line[17:].split()
                 off = int(off, 16)
-                if '*' not in section:
-                    symbols[symbol] = section
+                addr = int(line[:8], 16)
+                symbols[symbol] = addr, section
             except:
                 pass
             continue
@@ -287,8 +346,8 @@ def main():
 
     sections16, sections32 = gc(info16, info32)
 
-    doLayout16(sections16, out16)
-    doLayout32(sections32, out32)
+    start16 = doLayout16(sections16, out16)
+    doLayout32(sections32, out32, start16)
 
 if __name__ == '__main__':
     main()