mirror of
https://github.com/Dasharo/SeaBIOS.git
synced 2026-03-06 14:47:42 -08:00
Some linkers verify that sections have a start address that is aligned with the minimum alignment of that section. Add extra padding to the ".text" section to ensure it is always aligned with the maximum alignment of any section placed in ".text". Signed-off-by: Kevin O'Connor <kevin@koconnor.net> Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> Reported by: Ed Maste <emaste@FreeBSD.org>
706 lines
26 KiB
Python
Executable File
706 lines
26 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# Script to analyze code and arrange ld sections.
|
|
#
|
|
# Copyright (C) 2008-2014 Kevin O'Connor <kevin@koconnor.net>
|
|
#
|
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
|
|
|
import operator
|
|
import sys
|
|
|
|
# LD script headers/trailers
|
|
COMMONHEADER = """
|
|
/* DO NOT EDIT! This is an autogenerated file. See scripts/layoutrom.py. */
|
|
OUTPUT_FORMAT("elf32-i386")
|
|
OUTPUT_ARCH("i386")
|
|
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) *(.note*)
|
|
}
|
|
}
|
|
"""
|
|
|
|
|
|
######################################################################
|
|
# Determine section locations
|
|
######################################################################
|
|
|
|
# Align 'pos' up to 'alignbytes' offset
|
|
def alignpos(pos, alignbytes):
|
|
mask = alignbytes - 1
|
|
return (pos + mask) & ~mask
|
|
|
|
# Align 'pos' down to 'alignbytes' offset
|
|
def aligndown(pos, alignbytes):
|
|
mask = alignbytes - 1
|
|
return pos & ~mask
|
|
|
|
# Determine the final addresses for a list of sections that end at an
|
|
# address.
|
|
def setSectionsStart(sections, endaddr, minalign=1, segoffset=0):
|
|
totspace = 0
|
|
for section in sections:
|
|
minalign = max(minalign, section.align)
|
|
totspace = alignpos(totspace, section.align) + section.size
|
|
startaddr = int((endaddr - totspace) / minalign) * minalign
|
|
curaddr = startaddr
|
|
for section in sections:
|
|
curaddr = alignpos(curaddr, section.align)
|
|
section.finalloc = curaddr
|
|
section.finalsegloc = curaddr - segoffset
|
|
curaddr += section.size
|
|
return startaddr, minalign
|
|
|
|
# The 16bit code can't exceed 64K of space.
|
|
BUILD_BIOS_ADDR = 0xf0000
|
|
BUILD_BIOS_SIZE = 0x10000
|
|
BUILD_ROM_START = 0xc0000
|
|
BUILD_LOWRAM_END = 0xa0000
|
|
# Space to reserve in f-segment for dynamic allocations
|
|
BUILD_MIN_BIOSTABLE = 2048
|
|
|
|
# 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 fitSections(sections, fillsections):
|
|
# fixedsections = [(addr, section), ...]
|
|
fixedsections = []
|
|
for section in sections:
|
|
if section.name.startswith('.fixedaddr.'):
|
|
addr = int(section.name[11:], 16)
|
|
section.finalloc = addr + BUILD_BIOS_ADDR
|
|
section.finalsegloc = addr
|
|
fixedsections.append((addr, section))
|
|
if section.align != 1:
|
|
print("Error: Fixed section %s has non-zero alignment (%d)" % (
|
|
section.name, section.align))
|
|
sys.exit(1)
|
|
fixedsections.sort(key=operator.itemgetter(0))
|
|
firstfixed = fixedsections[0][0]
|
|
|
|
# Find freespace in fixed address area
|
|
# fixedAddr = [(freespace, section), ...]
|
|
fixedAddr = []
|
|
for i in range(len(fixedsections)):
|
|
fixedsectioninfo = fixedsections[i]
|
|
addr, section = fixedsectioninfo
|
|
if i == len(fixedsections) - 1:
|
|
nextaddr = BUILD_BIOS_SIZE
|
|
else:
|
|
nextaddr = fixedsections[i+1][0]
|
|
avail = nextaddr - addr - section.size
|
|
fixedAddr.append((avail, section))
|
|
fixedAddr.sort(key=operator.itemgetter(0))
|
|
|
|
# Attempt to fit other sections into fixed area
|
|
canrelocate = [(section.size, section.align, section.name, section)
|
|
for section in fillsections]
|
|
canrelocate.sort()
|
|
canrelocate = [section for size, align, name, section in canrelocate]
|
|
totalused = 0
|
|
for freespace, fixedsection in fixedAddr:
|
|
addpos = fixedsection.finalsegloc + fixedsection.size
|
|
totalused += fixedsection.size
|
|
nextfixedaddr = addpos + freespace
|
|
# print("Filling section %x uses %d, next=%x, available=%d" % (
|
|
# fixedsection.finalloc, fixedsection.size, nextfixedaddr, freespace))
|
|
while 1:
|
|
canfit = None
|
|
for fitsection in canrelocate:
|
|
if addpos + fitsection.size > nextfixedaddr:
|
|
# Can't fit and nothing else will fit.
|
|
break
|
|
fitnextaddr = alignpos(addpos, fitsection.align) + fitsection.size
|
|
# print("Test %s - %x vs %x" % (
|
|
# fitsection.name, fitnextaddr, nextfixedaddr))
|
|
if fitnextaddr > nextfixedaddr:
|
|
# This item can't fit.
|
|
continue
|
|
canfit = (fitnextaddr, fitsection)
|
|
if canfit is None:
|
|
break
|
|
# Found a section that can fit.
|
|
fitnextaddr, fitsection = canfit
|
|
canrelocate.remove(fitsection)
|
|
fitsection.finalloc = addpos + BUILD_BIOS_ADDR
|
|
fitsection.finalsegloc = addpos
|
|
addpos = fitnextaddr
|
|
totalused += fitsection.size
|
|
# print(" Adding %s (size %d align %d) pos=%x avail=%d" % (
|
|
# fitsection[2], fitsection[0], fitsection[1]
|
|
# , fitnextaddr, nextfixedaddr - fitnextaddr))
|
|
|
|
# Report stats
|
|
total = BUILD_BIOS_SIZE-firstfixed
|
|
slack = total - totalused
|
|
print ("Fixed space: 0x%x-0x%x total: %d slack: %d"
|
|
" Percent slack: %.1f%%" % (
|
|
firstfixed, BUILD_BIOS_SIZE, total, slack,
|
|
(float(slack) / total) * 100.0))
|
|
|
|
return firstfixed + BUILD_BIOS_ADDR
|
|
|
|
# Return the subset of sections with a given category
|
|
def getSectionsCategory(sections, category):
|
|
return [section for section in sections if section.category == category]
|
|
|
|
# Return the subset of sections with a given fileid
|
|
def getSectionsFileid(sections, fileid):
|
|
return [section for section in sections if section.fileid == fileid]
|
|
|
|
# Return the subset of sections with a given name prefix
|
|
def getSectionsPrefix(sections, prefix):
|
|
return [section for section in sections
|
|
if section.name.startswith(prefix)]
|
|
|
|
# The sections (and associated information) to be placed in output rom
|
|
class LayoutInfo:
|
|
sections = None
|
|
config = None
|
|
genreloc = None
|
|
sec32init_start = sec32init_end = sec32init_align = None
|
|
sec32low_start = sec32low_end = None
|
|
zonelow_base = final_sec32low_start = None
|
|
zonefseg_start = zonefseg_end = None
|
|
final_readonly_start = None
|
|
varlowsyms = entrysym = None
|
|
|
|
# Determine final memory addresses for sections
|
|
def doLayout(sections, config, genreloc):
|
|
li = LayoutInfo()
|
|
li.config = config
|
|
li.sections = sections
|
|
li.genreloc = genreloc
|
|
# Determine 16bit positions
|
|
sections16 = getSectionsCategory(sections, '16')
|
|
textsections = getSectionsPrefix(sections16, '.text.')
|
|
rodatasections = getSectionsPrefix(sections16, '.rodata')
|
|
datasections = getSectionsPrefix(sections16, '.data16.')
|
|
fixedsections = getSectionsCategory(sections, 'fixed')
|
|
|
|
firstfixed = fitSections(fixedsections, textsections)
|
|
remsections = [s for s in textsections+rodatasections+datasections
|
|
if s.finalloc is None]
|
|
sec16_start, sec16_align = setSectionsStart(
|
|
remsections, firstfixed, segoffset=BUILD_BIOS_ADDR)
|
|
|
|
# Determine 32seg positions
|
|
sections32seg = getSectionsCategory(sections, '32seg')
|
|
textsections = getSectionsPrefix(sections32seg, '.text.')
|
|
rodatasections = getSectionsPrefix(sections32seg, '.rodata')
|
|
datasections = getSectionsPrefix(sections32seg, '.data32seg.')
|
|
|
|
sec32seg_start, sec32seg_align = setSectionsStart(
|
|
textsections + rodatasections + datasections, sec16_start
|
|
, segoffset=BUILD_BIOS_ADDR)
|
|
|
|
# Determine 32bit "fseg memory" data positions
|
|
sections32textfseg = getSectionsCategory(sections, '32textfseg')
|
|
sec32textfseg_start, sec32textfseg_align = setSectionsStart(
|
|
sections32textfseg, sec32seg_start, 16)
|
|
|
|
sections32fseg = getSectionsCategory(sections, '32fseg')
|
|
sec32fseg_start, sec32fseg_align = setSectionsStart(
|
|
sections32fseg, sec32textfseg_start, 16
|
|
, segoffset=BUILD_BIOS_ADDR)
|
|
|
|
# Determine 32flat runtime positions
|
|
sections32flat = getSectionsCategory(sections, '32flat')
|
|
textsections = getSectionsPrefix(sections32flat, '.text.')
|
|
rodatasections = getSectionsPrefix(sections32flat, '.rodata')
|
|
datasections = getSectionsPrefix(sections32flat, '.data.')
|
|
bsssections = getSectionsPrefix(sections32flat, '.bss.')
|
|
|
|
sec32flat_start, sec32flat_align = setSectionsStart(
|
|
textsections + rodatasections + datasections + bsssections
|
|
, sec32fseg_start, 16)
|
|
|
|
# Determine 32flat init positions
|
|
sections32init = getSectionsCategory(sections, '32init')
|
|
init32_textsections = getSectionsPrefix(sections32init, '.text.')
|
|
init32_rodatasections = getSectionsPrefix(sections32init, '.rodata')
|
|
init32_datasections = getSectionsPrefix(sections32init, '.data.')
|
|
init32_bsssections = getSectionsPrefix(sections32init, '.bss.')
|
|
|
|
sec32init_start, sec32init_align = setSectionsStart(
|
|
init32_textsections + init32_rodatasections
|
|
+ init32_datasections + init32_bsssections
|
|
, sec32flat_start, 16)
|
|
|
|
# Determine location of ZoneFSeg memory.
|
|
zonefseg_end = sec32flat_start
|
|
if not genreloc:
|
|
zonefseg_end = sec32init_start
|
|
zonefseg_start = BUILD_BIOS_ADDR
|
|
if zonefseg_start + BUILD_MIN_BIOSTABLE > zonefseg_end:
|
|
# Not enough ZoneFSeg space - force a minimum space.
|
|
zonefseg_end = sec32fseg_start
|
|
zonefseg_start = zonefseg_end - BUILD_MIN_BIOSTABLE
|
|
sec32flat_start, sec32flat_align = setSectionsStart(
|
|
textsections + rodatasections + datasections + bsssections
|
|
, zonefseg_start, 16)
|
|
sec32init_start, sec32init_align = setSectionsStart(
|
|
init32_textsections + init32_rodatasections
|
|
+ init32_datasections + init32_bsssections
|
|
, sec32flat_start, 16)
|
|
li.sec32init_start = sec32init_start
|
|
li.sec32init_end = sec32flat_start
|
|
li.sec32init_align = sec32init_align
|
|
final_readonly_start = min(BUILD_BIOS_ADDR, sec32flat_start)
|
|
if not genreloc:
|
|
final_readonly_start = min(BUILD_BIOS_ADDR, sec32init_start)
|
|
li.zonefseg_start = zonefseg_start
|
|
li.zonefseg_end = zonefseg_end
|
|
li.final_readonly_start = final_readonly_start
|
|
|
|
# Determine "low memory" data positions
|
|
sections32low = getSectionsCategory(sections, '32low')
|
|
sec32low_end = sec32init_start
|
|
if config.get('CONFIG_MALLOC_UPPERMEMORY'):
|
|
final_sec32low_end = final_readonly_start
|
|
zonelow_base = final_sec32low_end - 64*1024
|
|
zonelow_base = max(BUILD_ROM_START, alignpos(zonelow_base, 2*1024))
|
|
else:
|
|
final_sec32low_end = BUILD_LOWRAM_END
|
|
zonelow_base = final_sec32low_end - 64*1024
|
|
relocdelta = final_sec32low_end - sec32low_end
|
|
li.sec32low_start, sec32low_align = setSectionsStart(
|
|
sections32low, sec32low_end, 16
|
|
, segoffset=zonelow_base - relocdelta)
|
|
li.sec32low_end = sec32low_end
|
|
li.zonelow_base = zonelow_base
|
|
li.final_sec32low_start = li.sec32low_start + relocdelta
|
|
|
|
# Print statistics
|
|
size16 = BUILD_BIOS_ADDR + BUILD_BIOS_SIZE - sec16_start
|
|
size32seg = sec16_start - sec32seg_start
|
|
size32textfseg = sec32seg_start - sec32textfseg_start
|
|
size32fseg = sec32textfseg_start - sec32fseg_start
|
|
size32flat = sec32fseg_start - sec32flat_start
|
|
size32init = sec32flat_start - sec32init_start
|
|
sizelow = li.sec32low_end - li.sec32low_start
|
|
print("16bit size: %d" % size16)
|
|
print("32bit segmented size: %d" % size32seg)
|
|
print("32bit flat size: %d" % (size32flat + size32textfseg))
|
|
print("32bit flat init size: %d" % size32init)
|
|
print("Lowmem size: %d" % sizelow)
|
|
print("f-segment var size: %d" % size32fseg)
|
|
return li
|
|
|
|
|
|
######################################################################
|
|
# Linker script output
|
|
######################################################################
|
|
|
|
# Write LD script includes for the given cross references
|
|
def outXRefs(sections, useseg=0, exportsyms=[], forcedelta=0):
|
|
xrefs = dict([(symbol.name, symbol) for symbol in exportsyms])
|
|
out = ""
|
|
for section in sections:
|
|
for reloc in section.relocs:
|
|
symbol = reloc.symbol
|
|
if (symbol.section is not None
|
|
and (symbol.section.fileid != section.fileid
|
|
or symbol.name != reloc.symbolname)):
|
|
xrefs[reloc.symbolname] = symbol
|
|
for symbolname, symbol in xrefs.items():
|
|
loc = symbol.section.finalloc
|
|
if useseg:
|
|
loc = symbol.section.finalsegloc
|
|
out += "%s = 0x%x ;\n" % (symbolname, loc + forcedelta + symbol.offset)
|
|
return out
|
|
|
|
# Write LD script includes for the given sections
|
|
def outSections(sections, useseg=0):
|
|
out = ""
|
|
for section in sections:
|
|
loc = section.finalloc
|
|
if useseg:
|
|
loc = section.finalsegloc
|
|
out += "%s 0x%x : { *(%s) }\n" % (section.name, loc, section.name)
|
|
return out
|
|
|
|
# Write LD script includes for the given sections using relative offsets
|
|
def outRelSections(sections, startsym, useseg=0):
|
|
sections = [(section.finalloc, section) for section in sections
|
|
if section.finalloc is not None]
|
|
sections.sort(key=operator.itemgetter(0))
|
|
out = ""
|
|
for addr, section in sections:
|
|
loc = section.finalloc
|
|
if useseg:
|
|
loc = section.finalsegloc
|
|
out += ". = ( 0x%x - %s ) ;\n" % (loc, startsym)
|
|
if section.name in ('.rodata.str1.1', '.rodata'):
|
|
out += "_rodata%s = . ;\n" % (section.fileid,)
|
|
out += "*%s.*(%s)\n" % (section.fileid, section.name)
|
|
return out
|
|
|
|
# Build linker script output for a list of relocations.
|
|
def strRelocs(outname, outrel, relocs):
|
|
relocs.sort()
|
|
return (" %s_start = ABSOLUTE(.) ;\n" % (outname,)
|
|
+ "".join(["LONG(0x%x - %s)\n" % (pos, outrel)
|
|
for pos in relocs])
|
|
+ " %s_end = ABSOLUTE(.) ;\n" % (outname,))
|
|
|
|
# Find relocations to the given sections
|
|
def getRelocs(sections, tosection, type=None):
|
|
return [section.finalloc + reloc.offset
|
|
for section in sections
|
|
for reloc in section.relocs
|
|
if (reloc.symbol.section in tosection
|
|
and (type is None or reloc.type == type))]
|
|
|
|
# Output the linker scripts for all required sections.
|
|
def writeLinkerScripts(li, out16, out32seg, out32flat):
|
|
# Write 16bit linker script
|
|
filesections16 = getSectionsFileid(li.sections, '16')
|
|
out = outXRefs(filesections16, useseg=1) + """
|
|
zonelow_base = 0x%x ;
|
|
_zonelow_seg = 0x%x ;
|
|
|
|
%s
|
|
""" % (li.zonelow_base,
|
|
int(li.zonelow_base / 16),
|
|
outSections(filesections16, useseg=1))
|
|
outfile = open(out16, 'w')
|
|
outfile.write(COMMONHEADER + out + COMMONTRAILER)
|
|
outfile.close()
|
|
|
|
# Write 32seg linker script
|
|
filesections32seg = getSectionsFileid(li.sections, '32seg')
|
|
out = (outXRefs(filesections32seg, useseg=1)
|
|
+ outSections(filesections32seg, useseg=1))
|
|
outfile = open(out32seg, 'w')
|
|
outfile.write(COMMONHEADER + out + COMMONTRAILER)
|
|
outfile.close()
|
|
|
|
# Write 32flat linker script
|
|
sec32all_start = li.sec32low_start
|
|
relocstr = ""
|
|
if li.genreloc:
|
|
# Generate relocations
|
|
initsections = dict([
|
|
(s, 1) for s in getSectionsCategory(li.sections, '32init')])
|
|
noninitsections = dict([(s, 1) for s in li.sections
|
|
if s not in initsections])
|
|
absrelocs = getRelocs(initsections, initsections, type='R_386_32')
|
|
relrelocs = getRelocs(initsections, noninitsections, type='R_386_PC32')
|
|
initrelocs = getRelocs(noninitsections, initsections)
|
|
relocstr = (strRelocs("_reloc_abs", "code32init_start", absrelocs)
|
|
+ strRelocs("_reloc_rel", "code32init_start", relrelocs)
|
|
+ strRelocs("_reloc_init", "code32flat_start", initrelocs))
|
|
numrelocs = len(absrelocs + relrelocs + initrelocs)
|
|
sec32all_start -= numrelocs * 4
|
|
filesections32flat = getSectionsFileid(li.sections, '32flat')
|
|
out = outXRefs([], exportsyms=li.varlowsyms
|
|
, forcedelta=li.final_sec32low_start-li.sec32low_start)
|
|
multiboot_header = ""
|
|
if li.config.get('CONFIG_MULTIBOOT'):
|
|
multiboot_header = "LONG(0x1BADB002) LONG(0) LONG(-0x1BADB002)"
|
|
sec32all_start -= 3 * 4
|
|
sec32all_align = max([section.align for section in li.sections])
|
|
sec32all_start = aligndown(sec32all_start, sec32all_align)
|
|
out += outXRefs(filesections32flat, exportsyms=[li.entrysym]) + """
|
|
_reloc_min_align = 0x%x ;
|
|
zonefseg_start = 0x%x ;
|
|
zonefseg_end = 0x%x ;
|
|
zonelow_base = 0x%x ;
|
|
final_varlow_start = 0x%x ;
|
|
final_readonly_start = 0x%x ;
|
|
varlow_start = 0x%x ;
|
|
varlow_end = 0x%x ;
|
|
code32init_start = 0x%x ;
|
|
code32init_end = 0x%x ;
|
|
|
|
code32flat_start = 0x%x ;
|
|
.text code32flat_start : {
|
|
%s
|
|
%s
|
|
%s
|
|
code32flat_end = ABSOLUTE(.) ;
|
|
} :text
|
|
""" % (li.sec32init_align,
|
|
li.zonefseg_start,
|
|
li.zonefseg_end,
|
|
li.zonelow_base,
|
|
li.final_sec32low_start,
|
|
li.final_readonly_start,
|
|
li.sec32low_start,
|
|
li.sec32low_end,
|
|
li.sec32init_start,
|
|
li.sec32init_end,
|
|
sec32all_start,
|
|
multiboot_header,
|
|
relocstr,
|
|
outRelSections(li.sections, 'code32flat_start'))
|
|
out = COMMONHEADER + out + COMMONTRAILER + """
|
|
ENTRY(%s)
|
|
PHDRS
|
|
{
|
|
text PT_LOAD AT ( code32flat_start ) ;
|
|
}
|
|
""" % (li.entrysym.name,)
|
|
outfile = open(out32flat, 'w')
|
|
outfile.write(out)
|
|
outfile.close()
|
|
|
|
|
|
######################################################################
|
|
# Detection of unused sections and init sections
|
|
######################################################################
|
|
|
|
# Visit all sections reachable from a given set of start sections
|
|
def findReachable(anchorsections, checkreloc, data):
|
|
anchorsections = dict([(section, []) for section in anchorsections])
|
|
pending = list(anchorsections)
|
|
while pending:
|
|
section = pending.pop()
|
|
for reloc in section.relocs:
|
|
chain = anchorsections[section] + [section.name]
|
|
if not checkreloc(reloc, section, data, chain):
|
|
continue
|
|
nextsection = reloc.symbol.section
|
|
if nextsection not in anchorsections:
|
|
anchorsections[nextsection] = chain
|
|
pending.append(nextsection)
|
|
return anchorsections
|
|
|
|
# Find "runtime" sections (ie, not init only sections).
|
|
def checkRuntime(reloc, rsection, data, chain):
|
|
section = reloc.symbol.section
|
|
if section is None or '.init.' in section.name:
|
|
return 0
|
|
if '.data.varinit.' in section.name:
|
|
print("ERROR: %s is VARVERIFY32INIT but used from %s" % (
|
|
section.name, chain))
|
|
sys.exit(1)
|
|
return 1
|
|
|
|
# Find and keep the section associated with a symbol (if available).
|
|
def checkKeepSym(reloc, syms, fileid, isxref):
|
|
symbolname = reloc.symbolname
|
|
mustbecfunc = symbolname.startswith('_cfunc')
|
|
if mustbecfunc:
|
|
symprefix = '_cfunc' + fileid + '_'
|
|
if not symbolname.startswith(symprefix):
|
|
return 0
|
|
symbolname = symbolname[len(symprefix):]
|
|
symbol = syms.get(symbolname)
|
|
if (symbol is None or symbol.section is None
|
|
or symbol.section.name.startswith('.discard.')):
|
|
return 0
|
|
isdestcfunc = (symbol.section.name.startswith('.text.')
|
|
and not symbol.section.name.startswith('.text.asm.'))
|
|
if ((mustbecfunc and not isdestcfunc)
|
|
or (not mustbecfunc and isdestcfunc and isxref)):
|
|
return 0
|
|
|
|
reloc.symbol = symbol
|
|
return 1
|
|
|
|
# Resolve a relocation and check if it should be kept in the final binary.
|
|
def checkKeep(reloc, section, symbols, chain):
|
|
ret = checkKeepSym(reloc, symbols[section.fileid], section.fileid, 0)
|
|
if ret:
|
|
return ret
|
|
# Not in primary sections - it may be a cross 16/32 reference
|
|
for fileid in ('16', '32seg', '32flat'):
|
|
if fileid != section.fileid:
|
|
ret = checkKeepSym(reloc, symbols[fileid], fileid, 1)
|
|
if ret:
|
|
return ret
|
|
return 0
|
|
|
|
|
|
######################################################################
|
|
# Startup and input parsing
|
|
######################################################################
|
|
|
|
class Section:
|
|
name = size = alignment = fileid = relocs = None
|
|
finalloc = finalsegloc = category = None
|
|
class Reloc:
|
|
offset = type = symbolname = symbol = None
|
|
class Symbol:
|
|
name = offset = section = None
|
|
|
|
# Read in output from objdump
|
|
def parseObjDump(file, fileid):
|
|
# sections = [section, ...]
|
|
sections = []
|
|
sectionmap = {}
|
|
# symbols[symbolname] = symbol
|
|
symbols = {}
|
|
|
|
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.startswith('RELOCATION RECORDS FOR ['):
|
|
sectionname = line[24:-2]
|
|
if sectionname.startswith('.debug_'):
|
|
# Skip debugging sections (to reduce parsing time)
|
|
state = None
|
|
continue
|
|
state = 'reloc'
|
|
relocsection = sectionmap[sectionname]
|
|
continue
|
|
|
|
if state == 'section':
|
|
try:
|
|
idx, name, size, vma, lma, fileoff, align = line.split()
|
|
if align[:3] != '2**':
|
|
continue
|
|
section = Section()
|
|
section.name = name
|
|
section.size = int(size, 16)
|
|
section.align = 2**int(align[3:])
|
|
section.fileid = fileid
|
|
section.relocs = []
|
|
sections.append(section)
|
|
sectionmap[name] = section
|
|
except ValueError:
|
|
pass
|
|
continue
|
|
if state == 'symbol':
|
|
try:
|
|
parts = line[17:].split()
|
|
if len(parts) == 3:
|
|
sectionname, size, name = parts
|
|
elif len(parts) == 4 and parts[2] == '.hidden':
|
|
sectionname, size, hidden, name = parts
|
|
else:
|
|
continue
|
|
symbol = Symbol()
|
|
symbol.size = int(size, 16)
|
|
symbol.offset = int(line[:8], 16)
|
|
symbol.name = name
|
|
symbol.section = sectionmap.get(sectionname)
|
|
symbols[name] = symbol
|
|
except ValueError:
|
|
pass
|
|
continue
|
|
if state == 'reloc':
|
|
try:
|
|
off, type, symbolname = line.split()
|
|
reloc = Reloc()
|
|
reloc.offset = int(off, 16)
|
|
reloc.type = type
|
|
reloc.symbolname = symbolname
|
|
reloc.symbol = symbols.get(symbolname)
|
|
if reloc.symbol is None:
|
|
# Some binutils (2.20.1) give section name instead
|
|
# of a symbol - create a dummy symbol.
|
|
reloc.symbol = symbol = Symbol()
|
|
symbol.size = 0
|
|
symbol.offset = 0
|
|
symbol.name = symbolname
|
|
symbol.section = sectionmap.get(symbolname)
|
|
symbols[symbolname] = symbol
|
|
relocsection.relocs.append(reloc)
|
|
except ValueError:
|
|
pass
|
|
return sections, symbols
|
|
|
|
# Parser for constants in simple C header files.
|
|
def scanconfig(file):
|
|
f = open(file, 'r')
|
|
opts = {}
|
|
for l in f.readlines():
|
|
parts = l.split()
|
|
if len(parts) != 3:
|
|
continue
|
|
if parts[0] != '#define':
|
|
continue
|
|
value = parts[2]
|
|
if value.isdigit() or (value.startswith('0x') and value[2:].isdigit()):
|
|
value = int(value, 0)
|
|
opts[parts[1]] = value
|
|
return opts
|
|
|
|
def main():
|
|
# Get output name
|
|
in16, in32seg, in32flat, cfgfile, out16, out32seg, out32flat = sys.argv[1:]
|
|
|
|
# Read in the objdump information
|
|
infile16 = open(in16, 'r')
|
|
infile32seg = open(in32seg, 'r')
|
|
infile32flat = open(in32flat, 'r')
|
|
|
|
# infoX = (sections, symbols)
|
|
info16 = parseObjDump(infile16, '16')
|
|
info32seg = parseObjDump(infile32seg, '32seg')
|
|
info32flat = parseObjDump(infile32flat, '32flat')
|
|
|
|
# Read kconfig config file
|
|
config = scanconfig(cfgfile)
|
|
|
|
# Figure out which sections to keep.
|
|
allsections = info16[0] + info32seg[0] + info32flat[0]
|
|
symbols = {'16': info16[1], '32seg': info32seg[1], '32flat': info32flat[1]}
|
|
if config.get('CONFIG_COREBOOT'):
|
|
entrysym = symbols['16'].get('entry_elf')
|
|
elif config.get('CONFIG_CSM'):
|
|
entrysym = symbols['16'].get('entry_csm')
|
|
else:
|
|
entrysym = symbols['16'].get('reset_vector')
|
|
anchorsections = [entrysym.section] + [
|
|
section for section in allsections
|
|
if section.name.startswith('.fixedaddr.')]
|
|
keepsections = findReachable(anchorsections, checkKeep, symbols)
|
|
sections = [section for section in allsections if section in keepsections]
|
|
|
|
# Separate 32bit flat into runtime, init, and special variable parts
|
|
anchorsections = [
|
|
section for section in sections
|
|
if ('.data.varlow.' in section.name or '.data.varfseg.' in section.name
|
|
or '.fixedaddr.' in section.name or '.runtime.' in section.name)]
|
|
runtimesections = findReachable(anchorsections, checkRuntime, None)
|
|
for section in sections:
|
|
if section.name.startswith('.data.varlow.'):
|
|
section.category = '32low'
|
|
elif section.name.startswith('.data.varfseg.'):
|
|
section.category = '32fseg'
|
|
elif section.name.startswith('.text.32fseg.'):
|
|
section.category = '32textfseg'
|
|
elif section.name.startswith('.fixedaddr.'):
|
|
section.category = 'fixed'
|
|
elif section.fileid == '32flat' and section not in runtimesections:
|
|
section.category = '32init'
|
|
else:
|
|
section.category = section.fileid
|
|
|
|
# Determine the final memory locations of each kept section.
|
|
genreloc = '_reloc_abs_start' in symbols['32flat']
|
|
li = doLayout(sections, config, genreloc)
|
|
|
|
# Exported symbols
|
|
li.varlowsyms = [symbol for symbol in symbols['32flat'].values()
|
|
if (symbol.section is not None
|
|
and symbol.section.finalloc is not None
|
|
and '.data.varlow.' in symbol.section.name
|
|
and symbol.name != symbol.section.name)]
|
|
li.entrysym = entrysym
|
|
|
|
# Write out linker script files.
|
|
writeLinkerScripts(li, out16, out32seg, out32flat)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|