#!/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>
#
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 = []
# , 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
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:
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.
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], [])
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
sections16, sections32 = gc(info16, info32)
- doLayout16(sections16, out16)
- doLayout32(sections32, out32)
+ start16 = doLayout16(sections16, out16)
+ doLayout32(sections32, out32, start16)
if __name__ == '__main__':
main()