2 # Script to analyze code and arrange ld sections.
4 # Copyright (C) 2008-2014 Kevin O'Connor <kevin@koconnor.net>
6 # This file may be distributed under the terms of the GNU GPLv3 license.
11 # LD script headers/trailers
13 /* DO NOT EDIT! This is an autogenerated file. See scripts/layoutrom.py. */
14 OUTPUT_FORMAT("elf32-i386")
21 /* Discard regular data sections to force a link error if
22 * code attempts to access data not marked with VAR16 (or other
26 *(.text*) *(.data*) *(.bss*) *(.rodata*)
27 *(COMMON) *(.discard*) *(.eh_frame) *(.note*)
33 ######################################################################
34 # Determine section locations
35 ######################################################################
37 # Align 'pos' to 'alignbytes' offset
38 def alignpos(pos, alignbytes):
40 return (pos + mask) & ~mask
42 # Determine the final addresses for a list of sections that end at an
44 def setSectionsStart(sections, endaddr, minalign=1, segoffset=0):
46 for section in sections:
47 if section.align > minalign:
48 minalign = section.align
49 totspace = alignpos(totspace, section.align) + section.size
50 startaddr = int((endaddr - totspace) / minalign) * minalign
52 for section in sections:
53 curaddr = alignpos(curaddr, section.align)
54 section.finalloc = curaddr
55 section.finalsegloc = curaddr - segoffset
56 curaddr += section.size
57 return startaddr, minalign
59 # The 16bit code can't exceed 64K of space.
60 BUILD_BIOS_ADDR = 0xf0000
61 BUILD_BIOS_SIZE = 0x10000
62 BUILD_ROM_START = 0xc0000
63 BUILD_LOWRAM_END = 0xa0000
64 # Space to reserve in f-segment for dynamic allocations
65 BUILD_MIN_BIOSTABLE = 2048
67 # Layout the 16bit code. This ensures sections with fixed offset
68 # requirements are placed in the correct location. It also places the
69 # 16bit code as high as possible in the f-segment.
70 def fitSections(sections, fillsections):
71 # fixedsections = [(addr, section), ...]
73 for section in sections:
74 if section.name.startswith('.fixedaddr.'):
75 addr = int(section.name[11:], 16)
76 section.finalloc = addr + BUILD_BIOS_ADDR
77 section.finalsegloc = addr
78 fixedsections.append((addr, section))
79 if section.align != 1:
80 print("Error: Fixed section %s has non-zero alignment (%d)" % (
81 section.name, section.align))
83 fixedsections.sort(key=operator.itemgetter(0))
84 firstfixed = fixedsections[0][0]
86 # Find freespace in fixed address area
87 # fixedAddr = [(freespace, section), ...]
89 for i in range(len(fixedsections)):
90 fixedsectioninfo = fixedsections[i]
91 addr, section = fixedsectioninfo
92 if i == len(fixedsections) - 1:
93 nextaddr = BUILD_BIOS_SIZE
95 nextaddr = fixedsections[i+1][0]
96 avail = nextaddr - addr - section.size
97 fixedAddr.append((avail, section))
98 fixedAddr.sort(key=operator.itemgetter(0))
100 # Attempt to fit other sections into fixed area
101 canrelocate = [(section.size, section.align, section.name, section)
102 for section in fillsections]
104 canrelocate = [section for size, align, name, section in canrelocate]
106 for freespace, fixedsection in fixedAddr:
107 addpos = fixedsection.finalsegloc + fixedsection.size
108 totalused += fixedsection.size
109 nextfixedaddr = addpos + freespace
110 # print("Filling section %x uses %d, next=%x, available=%d" % (
111 # fixedsection.finalloc, fixedsection.size, nextfixedaddr, freespace))
114 for fitsection in canrelocate:
115 if addpos + fitsection.size > nextfixedaddr:
116 # Can't fit and nothing else will fit.
118 fitnextaddr = alignpos(addpos, fitsection.align) + fitsection.size
119 # print("Test %s - %x vs %x" % (
120 # fitsection.name, fitnextaddr, nextfixedaddr))
121 if fitnextaddr > nextfixedaddr:
122 # This item can't fit.
124 canfit = (fitnextaddr, fitsection)
127 # Found a section that can fit.
128 fitnextaddr, fitsection = canfit
129 canrelocate.remove(fitsection)
130 fitsection.finalloc = addpos + BUILD_BIOS_ADDR
131 fitsection.finalsegloc = addpos
133 totalused += fitsection.size
134 # print(" Adding %s (size %d align %d) pos=%x avail=%d" % (
135 # fitsection[2], fitsection[0], fitsection[1]
136 # , fitnextaddr, nextfixedaddr - fitnextaddr))
139 total = BUILD_BIOS_SIZE-firstfixed
140 slack = total - totalused
141 print ("Fixed space: 0x%x-0x%x total: %d slack: %d"
142 " Percent slack: %.1f%%" % (
143 firstfixed, BUILD_BIOS_SIZE, total, slack,
144 (float(slack) / total) * 100.0))
146 return firstfixed + BUILD_BIOS_ADDR
148 # Return the subset of sections with a given category
149 def getSectionsCategory(sections, category):
150 return [section for section in sections if section.category == category]
152 # Return the subset of sections with a given fileid
153 def getSectionsFileid(sections, fileid):
154 return [section for section in sections if section.fileid == fileid]
156 # Return the subset of sections with a given name prefix
157 def getSectionsPrefix(sections, prefix):
158 return [section for section in sections
159 if section.name.startswith(prefix)]
161 # The sections (and associated information) to be placed in output rom
165 sec32init_start = sec32init_end = sec32init_align = None
166 sec32low_start = sec32low_end = None
167 zonelow_base = final_sec32low_start = None
168 zonefseg_start = zonefseg_end = None
169 final_readonly_start = None
170 varlowsyms = entrysym = None
172 # Determine final memory addresses for sections
173 def doLayout(sections, config, genreloc):
175 li.sections = sections
176 li.genreloc = genreloc
177 # Determine 16bit positions
178 sections16 = getSectionsCategory(sections, '16')
179 textsections = getSectionsPrefix(sections16, '.text.')
180 rodatasections = getSectionsPrefix(sections16, '.rodata')
181 datasections = getSectionsPrefix(sections16, '.data16.')
182 fixedsections = getSectionsCategory(sections, 'fixed')
184 firstfixed = fitSections(fixedsections, textsections)
185 remsections = [s for s in textsections+rodatasections+datasections
186 if s.finalloc is None]
187 sec16_start, sec16_align = setSectionsStart(
188 remsections, firstfixed, segoffset=BUILD_BIOS_ADDR)
190 # Determine 32seg positions
191 sections32seg = getSectionsCategory(sections, '32seg')
192 textsections = getSectionsPrefix(sections32seg, '.text.')
193 rodatasections = getSectionsPrefix(sections32seg, '.rodata')
194 datasections = getSectionsPrefix(sections32seg, '.data32seg.')
196 sec32seg_start, sec32seg_align = setSectionsStart(
197 textsections + rodatasections + datasections, sec16_start
198 , segoffset=BUILD_BIOS_ADDR)
200 # Determine 32bit "fseg memory" data positions
201 sections32textfseg = getSectionsCategory(sections, '32textfseg')
202 sec32textfseg_start, sec32textfseg_align = setSectionsStart(
203 sections32textfseg, sec32seg_start, 16)
205 sections32fseg = getSectionsCategory(sections, '32fseg')
206 sec32fseg_start, sec32fseg_align = setSectionsStart(
207 sections32fseg, sec32textfseg_start, 16
208 , segoffset=BUILD_BIOS_ADDR)
210 # Determine 32flat runtime positions
211 sections32flat = getSectionsCategory(sections, '32flat')
212 textsections = getSectionsPrefix(sections32flat, '.text.')
213 rodatasections = getSectionsPrefix(sections32flat, '.rodata')
214 datasections = getSectionsPrefix(sections32flat, '.data.')
215 bsssections = getSectionsPrefix(sections32flat, '.bss.')
217 sec32flat_start, sec32flat_align = setSectionsStart(
218 textsections + rodatasections + datasections + bsssections
219 , sec32fseg_start, 16)
221 # Determine 32flat init positions
222 sections32init = getSectionsCategory(sections, '32init')
223 init32_textsections = getSectionsPrefix(sections32init, '.text.')
224 init32_rodatasections = getSectionsPrefix(sections32init, '.rodata')
225 init32_datasections = getSectionsPrefix(sections32init, '.data.')
226 init32_bsssections = getSectionsPrefix(sections32init, '.bss.')
228 sec32init_start, sec32init_align = setSectionsStart(
229 init32_textsections + init32_rodatasections
230 + init32_datasections + init32_bsssections
231 , sec32flat_start, 16)
233 # Determine location of ZoneFSeg memory.
234 zonefseg_end = sec32flat_start
236 zonefseg_end = sec32init_start
237 zonefseg_start = BUILD_BIOS_ADDR
238 if zonefseg_start + BUILD_MIN_BIOSTABLE > zonefseg_end:
239 # Not enough ZoneFSeg space - force a minimum space.
240 zonefseg_end = sec32fseg_start
241 zonefseg_start = zonefseg_end - BUILD_MIN_BIOSTABLE
242 sec32flat_start, sec32flat_align = setSectionsStart(
243 textsections + rodatasections + datasections + bsssections
244 , zonefseg_start, 16)
245 sec32init_start, sec32init_align = setSectionsStart(
246 init32_textsections + init32_rodatasections
247 + init32_datasections + init32_bsssections
248 , sec32flat_start, 16)
249 li.sec32init_start = sec32init_start
250 li.sec32init_end = sec32flat_start
251 li.sec32init_align = sec32init_align
252 final_readonly_start = min(BUILD_BIOS_ADDR, sec32flat_start)
254 final_readonly_start = min(BUILD_BIOS_ADDR, sec32init_start)
255 li.zonefseg_start = zonefseg_start
256 li.zonefseg_end = zonefseg_end
257 li.final_readonly_start = final_readonly_start
259 # Determine "low memory" data positions
260 sections32low = getSectionsCategory(sections, '32low')
261 sec32low_end = sec32init_start
262 if config.get('CONFIG_MALLOC_UPPERMEMORY'):
263 final_sec32low_end = final_readonly_start
264 zonelow_base = final_sec32low_end - 64*1024
265 zonelow_base = max(BUILD_ROM_START, alignpos(zonelow_base, 2*1024))
267 final_sec32low_end = BUILD_LOWRAM_END
268 zonelow_base = final_sec32low_end - 64*1024
269 relocdelta = final_sec32low_end - sec32low_end
270 li.sec32low_start, li.sec32low_align = setSectionsStart(
271 sections32low, sec32low_end, 16
272 , segoffset=zonelow_base - relocdelta)
273 li.sec32low_end = sec32low_end
274 li.zonelow_base = zonelow_base
275 li.final_sec32low_start = li.sec32low_start + relocdelta
278 size16 = BUILD_BIOS_ADDR + BUILD_BIOS_SIZE - sec16_start
279 size32seg = sec16_start - sec32seg_start
280 size32textfseg = sec32seg_start - sec32textfseg_start
281 size32fseg = sec32textfseg_start - sec32fseg_start
282 size32flat = sec32fseg_start - sec32flat_start
283 size32init = sec32flat_start - sec32init_start
284 sizelow = li.sec32low_end - li.sec32low_start
285 print("16bit size: %d" % size16)
286 print("32bit segmented size: %d" % size32seg)
287 print("32bit flat size: %d" % (size32flat + size32textfseg))
288 print("32bit flat init size: %d" % size32init)
289 print("Lowmem size: %d" % sizelow)
290 print("f-segment var size: %d" % size32fseg)
294 ######################################################################
295 # Linker script output
296 ######################################################################
298 # Write LD script includes for the given cross references
299 def outXRefs(sections, useseg=0, exportsyms=[], forcedelta=0):
300 xrefs = dict([(symbol.name, symbol) for symbol in exportsyms])
302 for section in sections:
303 for reloc in section.relocs:
304 symbol = reloc.symbol
305 if (symbol.section is not None
306 and (symbol.section.fileid != section.fileid
307 or symbol.name != reloc.symbolname)):
308 xrefs[reloc.symbolname] = symbol
309 for symbolname, symbol in xrefs.items():
310 loc = symbol.section.finalloc
312 loc = symbol.section.finalsegloc
313 out += "%s = 0x%x ;\n" % (symbolname, loc + forcedelta + symbol.offset)
316 # Write LD script includes for the given sections
317 def outSections(sections, useseg=0):
319 for section in sections:
320 loc = section.finalloc
322 loc = section.finalsegloc
323 out += "%s 0x%x : { *(%s) }\n" % (section.name, loc, section.name)
326 # Write LD script includes for the given sections using relative offsets
327 def outRelSections(sections, startsym, useseg=0):
328 sections = [(section.finalloc, section) for section in sections
329 if section.finalloc is not None]
330 sections.sort(key=operator.itemgetter(0))
332 for addr, section in sections:
333 loc = section.finalloc
335 loc = section.finalsegloc
336 out += ". = ( 0x%x - %s ) ;\n" % (loc, startsym)
337 if section.name in ('.rodata.str1.1', '.rodata'):
338 out += "_rodata%s = . ;\n" % (section.fileid,)
339 out += "*%s.*(%s)\n" % (section.fileid, section.name)
342 # Build linker script output for a list of relocations.
343 def strRelocs(outname, outrel, relocs):
345 return (" %s_start = ABSOLUTE(.) ;\n" % (outname,)
346 + "".join(["LONG(0x%x - %s)\n" % (pos, outrel)
348 + " %s_end = ABSOLUTE(.) ;\n" % (outname,))
350 # Find relocations to the given sections
351 def getRelocs(sections, tosection, type=None):
352 return [section.finalloc + reloc.offset
353 for section in sections
354 for reloc in section.relocs
355 if (reloc.symbol.section in tosection
356 and (type is None or reloc.type == type))]
358 # Output the linker scripts for all required sections.
359 def writeLinkerScripts(li, out16, out32seg, out32flat):
360 # Write 16bit linker script
361 filesections16 = getSectionsFileid(li.sections, '16')
362 out = outXRefs(filesections16, useseg=1) + """
363 zonelow_base = 0x%x ;
364 _zonelow_seg = 0x%x ;
367 """ % (li.zonelow_base,
368 int(li.zonelow_base / 16),
369 outSections(filesections16, useseg=1))
370 outfile = open(out16, 'w')
371 outfile.write(COMMONHEADER + out + COMMONTRAILER)
374 # Write 32seg linker script
375 filesections32seg = getSectionsFileid(li.sections, '32seg')
376 out = (outXRefs(filesections32seg, useseg=1)
377 + outSections(filesections32seg, useseg=1))
378 outfile = open(out32seg, 'w')
379 outfile.write(COMMONHEADER + out + COMMONTRAILER)
382 # Write 32flat linker script
383 sec32all_start = li.sec32low_start
386 # Generate relocations
387 initsections = dict([
388 (s, 1) for s in getSectionsCategory(li.sections, '32init')])
389 noninitsections = dict([(s, 1) for s in li.sections
390 if s not in initsections])
391 absrelocs = getRelocs(initsections, initsections, type='R_386_32')
392 relrelocs = getRelocs(initsections, noninitsections, type='R_386_PC32')
393 initrelocs = getRelocs(noninitsections, initsections)
394 relocstr = (strRelocs("_reloc_abs", "code32init_start", absrelocs)
395 + strRelocs("_reloc_rel", "code32init_start", relrelocs)
396 + strRelocs("_reloc_init", "code32flat_start", initrelocs))
397 numrelocs = len(absrelocs + relrelocs + initrelocs)
398 sec32all_start -= numrelocs * 4
399 filesections32flat = getSectionsFileid(li.sections, '32flat')
400 out = outXRefs([], exportsyms=li.varlowsyms
401 , forcedelta=li.final_sec32low_start-li.sec32low_start)
402 out += outXRefs(filesections32flat, exportsyms=[li.entrysym]) + """
403 _reloc_min_align = 0x%x ;
404 zonefseg_start = 0x%x ;
405 zonefseg_end = 0x%x ;
406 zonelow_base = 0x%x ;
407 final_varlow_start = 0x%x ;
408 final_readonly_start = 0x%x ;
409 varlow_start = 0x%x ;
411 code32init_start = 0x%x ;
412 code32init_end = 0x%x ;
414 code32flat_start = 0x%x ;
415 .text code32flat_start : {
418 code32flat_end = ABSOLUTE(.) ;
420 """ % (li.sec32init_align,
424 li.final_sec32low_start,
425 li.final_readonly_start,
432 outRelSections(li.sections, 'code32flat_start'))
433 out = COMMONHEADER + out + COMMONTRAILER + """
437 text PT_LOAD AT ( code32flat_start ) ;
439 """ % (li.entrysym.name,)
440 outfile = open(out32flat, 'w')
445 ######################################################################
446 # Detection of unused sections and init sections
447 ######################################################################
449 # Visit all sections reachable from a given set of start sections
450 def findReachable(anchorsections, checkreloc, data):
451 anchorsections = dict([(section, []) for section in anchorsections])
452 pending = list(anchorsections)
454 section = pending.pop()
455 for reloc in section.relocs:
456 chain = anchorsections[section] + [section.name]
457 if not checkreloc(reloc, section, data, chain):
459 nextsection = reloc.symbol.section
460 if nextsection not in anchorsections:
461 anchorsections[nextsection] = chain
462 pending.append(nextsection)
463 return anchorsections
465 # Find "runtime" sections (ie, not init only sections).
466 def checkRuntime(reloc, rsection, data, chain):
467 section = reloc.symbol.section
468 if section is None or '.init.' in section.name:
470 if '.data.varinit.' in section.name:
471 print("ERROR: %s is VARVERIFY32INIT but used from %s" % (
472 section.name, chain))
476 # Find and keep the section associated with a symbol (if available).
477 def checkKeepSym(reloc, syms, fileid, isxref):
478 symbolname = reloc.symbolname
479 mustbecfunc = symbolname.startswith('_cfunc')
481 symprefix = '_cfunc' + fileid + '_'
482 if not symbolname.startswith(symprefix):
484 symbolname = symbolname[len(symprefix):]
485 symbol = syms.get(symbolname)
486 if (symbol is None or symbol.section is None
487 or symbol.section.name.startswith('.discard.')):
489 isdestcfunc = (symbol.section.name.startswith('.text.')
490 and not symbol.section.name.startswith('.text.asm.'))
491 if ((mustbecfunc and not isdestcfunc)
492 or (not mustbecfunc and isdestcfunc and isxref)):
495 reloc.symbol = symbol
498 # Resolve a relocation and check if it should be kept in the final binary.
499 def checkKeep(reloc, section, symbols, chain):
500 ret = checkKeepSym(reloc, symbols[section.fileid], section.fileid, 0)
503 # Not in primary sections - it may be a cross 16/32 reference
504 for fileid in ('16', '32seg', '32flat'):
505 if fileid != section.fileid:
506 ret = checkKeepSym(reloc, symbols[fileid], fileid, 1)
512 ######################################################################
513 # Startup and input parsing
514 ######################################################################
517 name = size = alignment = fileid = relocs = None
518 finalloc = finalsegloc = category = None
520 offset = type = symbolname = symbol = None
522 name = offset = section = None
524 # Read in output from objdump
525 def parseObjDump(file, fileid):
526 # sections = [section, ...]
529 # symbols[symbolname] = symbol
533 for line in file.readlines():
535 if line == 'Sections:':
538 if line == 'SYMBOL TABLE:':
541 if line.startswith('RELOCATION RECORDS FOR ['):
542 sectionname = line[24:-2]
543 if sectionname.startswith('.debug_'):
544 # Skip debugging sections (to reduce parsing time)
548 relocsection = sectionmap[sectionname]
551 if state == 'section':
553 idx, name, size, vma, lma, fileoff, align = line.split()
554 if align[:3] != '2**':
558 section.size = int(size, 16)
559 section.align = 2**int(align[3:])
560 section.fileid = fileid
562 sections.append(section)
563 sectionmap[name] = section
567 if state == 'symbol':
569 parts = line[17:].split()
571 sectionname, size, name = parts
572 elif len(parts) == 4 and parts[2] == '.hidden':
573 sectionname, size, hidden, name = parts
577 symbol.size = int(size, 16)
578 symbol.offset = int(line[:8], 16)
580 symbol.section = sectionmap.get(sectionname)
581 symbols[name] = symbol
587 off, type, symbolname = line.split()
589 reloc.offset = int(off, 16)
591 reloc.symbolname = symbolname
592 reloc.symbol = symbols.get(symbolname)
593 if reloc.symbol is None:
594 # Some binutils (2.20.1) give section name instead
595 # of a symbol - create a dummy symbol.
596 reloc.symbol = symbol = Symbol()
599 symbol.name = symbolname
600 symbol.section = sectionmap.get(symbolname)
601 symbols[symbolname] = symbol
602 relocsection.relocs.append(reloc)
605 return sections, symbols
607 # Parser for constants in simple C header files.
608 def scanconfig(file):
611 for l in f.readlines():
615 if parts[0] != '#define':
618 if value.isdigit() or (value.startswith('0x') and value[2:].isdigit()):
619 value = int(value, 0)
620 opts[parts[1]] = value
625 in16, in32seg, in32flat, cfgfile, out16, out32seg, out32flat = sys.argv[1:]
627 # Read in the objdump information
628 infile16 = open(in16, 'r')
629 infile32seg = open(in32seg, 'r')
630 infile32flat = open(in32flat, 'r')
632 # infoX = (sections, symbols)
633 info16 = parseObjDump(infile16, '16')
634 info32seg = parseObjDump(infile32seg, '32seg')
635 info32flat = parseObjDump(infile32flat, '32flat')
637 # Read kconfig config file
638 config = scanconfig(cfgfile)
640 # Figure out which sections to keep.
641 allsections = info16[0] + info32seg[0] + info32flat[0]
642 symbols = {'16': info16[1], '32seg': info32seg[1], '32flat': info32flat[1]}
643 if config.get('CONFIG_COREBOOT'):
644 entrysym = symbols['16'].get('entry_elf')
645 elif config.get('CONFIG_CSM'):
646 entrysym = symbols['16'].get('entry_csm')
648 entrysym = symbols['16'].get('reset_vector')
649 anchorsections = [entrysym.section] + [
650 section for section in allsections
651 if section.name.startswith('.fixedaddr.')]
652 keepsections = findReachable(anchorsections, checkKeep, symbols)
653 sections = [section for section in allsections if section in keepsections]
655 # Separate 32bit flat into runtime, init, and special variable parts
657 section for section in sections
658 if ('.data.varlow.' in section.name or '.data.varfseg.' in section.name
659 or '.fixedaddr.' in section.name or '.runtime.' in section.name)]
660 runtimesections = findReachable(anchorsections, checkRuntime, None)
661 for section in sections:
662 if section.name.startswith('.data.varlow.'):
663 section.category = '32low'
664 elif section.name.startswith('.data.varfseg.'):
665 section.category = '32fseg'
666 elif section.name.startswith('.text.32fseg.'):
667 section.category = '32textfseg'
668 elif section.name.startswith('.fixedaddr.'):
669 section.category = 'fixed'
670 elif section.fileid == '32flat' and section not in runtimesections:
671 section.category = '32init'
673 section.category = section.fileid
675 # Determine the final memory locations of each kept section.
676 genreloc = '_reloc_abs_start' in symbols['32flat']
677 li = doLayout(sections, config, genreloc)
680 li.varlowsyms = [symbol for symbol in symbols['32flat'].values()
681 if (symbol.section is not None
682 and symbol.section.finalloc is not None
683 and '.data.varlow.' in symbol.section.name
684 and symbol.name != symbol.section.name)]
685 li.entrysym = entrysym
687 # Write out linker script files.
688 writeLinkerScripts(li, out16, out32seg, out32flat)
690 if __name__ == '__main__':