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
166 sec32init_start = sec32init_end = sec32init_align = None
167 sec32low_start = sec32low_end = None
168 zonelow_base = final_sec32low_start = None
169 zonefseg_start = zonefseg_end = None
170 final_readonly_start = None
171 varlowsyms = entrysym = None
173 # Determine final memory addresses for sections
174 def doLayout(sections, config, genreloc):
177 li.sections = sections
178 li.genreloc = genreloc
179 # Determine 16bit positions
180 sections16 = getSectionsCategory(sections, '16')
181 textsections = getSectionsPrefix(sections16, '.text.')
182 rodatasections = getSectionsPrefix(sections16, '.rodata')
183 datasections = getSectionsPrefix(sections16, '.data16.')
184 fixedsections = getSectionsCategory(sections, 'fixed')
186 firstfixed = fitSections(fixedsections, textsections)
187 remsections = [s for s in textsections+rodatasections+datasections
188 if s.finalloc is None]
189 sec16_start, sec16_align = setSectionsStart(
190 remsections, firstfixed, segoffset=BUILD_BIOS_ADDR)
192 # Determine 32seg positions
193 sections32seg = getSectionsCategory(sections, '32seg')
194 textsections = getSectionsPrefix(sections32seg, '.text.')
195 rodatasections = getSectionsPrefix(sections32seg, '.rodata')
196 datasections = getSectionsPrefix(sections32seg, '.data32seg.')
198 sec32seg_start, sec32seg_align = setSectionsStart(
199 textsections + rodatasections + datasections, sec16_start
200 , segoffset=BUILD_BIOS_ADDR)
202 # Determine 32bit "fseg memory" data positions
203 sections32textfseg = getSectionsCategory(sections, '32textfseg')
204 sec32textfseg_start, sec32textfseg_align = setSectionsStart(
205 sections32textfseg, sec32seg_start, 16)
207 sections32fseg = getSectionsCategory(sections, '32fseg')
208 sec32fseg_start, sec32fseg_align = setSectionsStart(
209 sections32fseg, sec32textfseg_start, 16
210 , segoffset=BUILD_BIOS_ADDR)
212 # Determine 32flat runtime positions
213 sections32flat = getSectionsCategory(sections, '32flat')
214 textsections = getSectionsPrefix(sections32flat, '.text.')
215 rodatasections = getSectionsPrefix(sections32flat, '.rodata')
216 datasections = getSectionsPrefix(sections32flat, '.data.')
217 bsssections = getSectionsPrefix(sections32flat, '.bss.')
219 sec32flat_start, sec32flat_align = setSectionsStart(
220 textsections + rodatasections + datasections + bsssections
221 , sec32fseg_start, 16)
223 # Determine 32flat init positions
224 sections32init = getSectionsCategory(sections, '32init')
225 init32_textsections = getSectionsPrefix(sections32init, '.text.')
226 init32_rodatasections = getSectionsPrefix(sections32init, '.rodata')
227 init32_datasections = getSectionsPrefix(sections32init, '.data.')
228 init32_bsssections = getSectionsPrefix(sections32init, '.bss.')
230 sec32init_start, sec32init_align = setSectionsStart(
231 init32_textsections + init32_rodatasections
232 + init32_datasections + init32_bsssections
233 , sec32flat_start, 16)
235 # Determine location of ZoneFSeg memory.
236 zonefseg_end = sec32flat_start
238 zonefseg_end = sec32init_start
239 zonefseg_start = BUILD_BIOS_ADDR
240 if zonefseg_start + BUILD_MIN_BIOSTABLE > zonefseg_end:
241 # Not enough ZoneFSeg space - force a minimum space.
242 zonefseg_end = sec32fseg_start
243 zonefseg_start = zonefseg_end - BUILD_MIN_BIOSTABLE
244 sec32flat_start, sec32flat_align = setSectionsStart(
245 textsections + rodatasections + datasections + bsssections
246 , zonefseg_start, 16)
247 sec32init_start, sec32init_align = setSectionsStart(
248 init32_textsections + init32_rodatasections
249 + init32_datasections + init32_bsssections
250 , sec32flat_start, 16)
251 li.sec32init_start = sec32init_start
252 li.sec32init_end = sec32flat_start
253 li.sec32init_align = sec32init_align
254 final_readonly_start = min(BUILD_BIOS_ADDR, sec32flat_start)
256 final_readonly_start = min(BUILD_BIOS_ADDR, sec32init_start)
257 li.zonefseg_start = zonefseg_start
258 li.zonefseg_end = zonefseg_end
259 li.final_readonly_start = final_readonly_start
261 # Determine "low memory" data positions
262 sections32low = getSectionsCategory(sections, '32low')
263 sec32low_end = sec32init_start
264 if config.get('CONFIG_MALLOC_UPPERMEMORY'):
265 final_sec32low_end = final_readonly_start
266 zonelow_base = final_sec32low_end - 64*1024
267 zonelow_base = max(BUILD_ROM_START, alignpos(zonelow_base, 2*1024))
269 final_sec32low_end = BUILD_LOWRAM_END
270 zonelow_base = final_sec32low_end - 64*1024
271 relocdelta = final_sec32low_end - sec32low_end
272 li.sec32low_start, li.sec32low_align = setSectionsStart(
273 sections32low, sec32low_end, 16
274 , segoffset=zonelow_base - relocdelta)
275 li.sec32low_end = sec32low_end
276 li.zonelow_base = zonelow_base
277 li.final_sec32low_start = li.sec32low_start + relocdelta
280 size16 = BUILD_BIOS_ADDR + BUILD_BIOS_SIZE - sec16_start
281 size32seg = sec16_start - sec32seg_start
282 size32textfseg = sec32seg_start - sec32textfseg_start
283 size32fseg = sec32textfseg_start - sec32fseg_start
284 size32flat = sec32fseg_start - sec32flat_start
285 size32init = sec32flat_start - sec32init_start
286 sizelow = li.sec32low_end - li.sec32low_start
287 print("16bit size: %d" % size16)
288 print("32bit segmented size: %d" % size32seg)
289 print("32bit flat size: %d" % (size32flat + size32textfseg))
290 print("32bit flat init size: %d" % size32init)
291 print("Lowmem size: %d" % sizelow)
292 print("f-segment var size: %d" % size32fseg)
296 ######################################################################
297 # Linker script output
298 ######################################################################
300 # Write LD script includes for the given cross references
301 def outXRefs(sections, useseg=0, exportsyms=[], forcedelta=0):
302 xrefs = dict([(symbol.name, symbol) for symbol in exportsyms])
304 for section in sections:
305 for reloc in section.relocs:
306 symbol = reloc.symbol
307 if (symbol.section is not None
308 and (symbol.section.fileid != section.fileid
309 or symbol.name != reloc.symbolname)):
310 xrefs[reloc.symbolname] = symbol
311 for symbolname, symbol in xrefs.items():
312 loc = symbol.section.finalloc
314 loc = symbol.section.finalsegloc
315 out += "%s = 0x%x ;\n" % (symbolname, loc + forcedelta + symbol.offset)
318 # Write LD script includes for the given sections
319 def outSections(sections, useseg=0):
321 for section in sections:
322 loc = section.finalloc
324 loc = section.finalsegloc
325 out += "%s 0x%x : { *(%s) }\n" % (section.name, loc, section.name)
328 # Write LD script includes for the given sections using relative offsets
329 def outRelSections(sections, startsym, useseg=0):
330 sections = [(section.finalloc, section) for section in sections
331 if section.finalloc is not None]
332 sections.sort(key=operator.itemgetter(0))
334 for addr, section in sections:
335 loc = section.finalloc
337 loc = section.finalsegloc
338 out += ". = ( 0x%x - %s ) ;\n" % (loc, startsym)
339 if section.name in ('.rodata.str1.1', '.rodata'):
340 out += "_rodata%s = . ;\n" % (section.fileid,)
341 out += "*%s.*(%s)\n" % (section.fileid, section.name)
344 # Build linker script output for a list of relocations.
345 def strRelocs(outname, outrel, relocs):
347 return (" %s_start = ABSOLUTE(.) ;\n" % (outname,)
348 + "".join(["LONG(0x%x - %s)\n" % (pos, outrel)
350 + " %s_end = ABSOLUTE(.) ;\n" % (outname,))
352 # Find relocations to the given sections
353 def getRelocs(sections, tosection, type=None):
354 return [section.finalloc + reloc.offset
355 for section in sections
356 for reloc in section.relocs
357 if (reloc.symbol.section in tosection
358 and (type is None or reloc.type == type))]
360 # Output the linker scripts for all required sections.
361 def writeLinkerScripts(li, out16, out32seg, out32flat):
362 # Write 16bit linker script
363 filesections16 = getSectionsFileid(li.sections, '16')
364 out = outXRefs(filesections16, useseg=1) + """
365 zonelow_base = 0x%x ;
366 _zonelow_seg = 0x%x ;
369 """ % (li.zonelow_base,
370 int(li.zonelow_base / 16),
371 outSections(filesections16, useseg=1))
372 outfile = open(out16, 'w')
373 outfile.write(COMMONHEADER + out + COMMONTRAILER)
376 # Write 32seg linker script
377 filesections32seg = getSectionsFileid(li.sections, '32seg')
378 out = (outXRefs(filesections32seg, useseg=1)
379 + outSections(filesections32seg, useseg=1))
380 outfile = open(out32seg, 'w')
381 outfile.write(COMMONHEADER + out + COMMONTRAILER)
384 # Write 32flat linker script
385 sec32all_start = li.sec32low_start
388 # Generate relocations
389 initsections = dict([
390 (s, 1) for s in getSectionsCategory(li.sections, '32init')])
391 noninitsections = dict([(s, 1) for s in li.sections
392 if s not in initsections])
393 absrelocs = getRelocs(initsections, initsections, type='R_386_32')
394 relrelocs = getRelocs(initsections, noninitsections, type='R_386_PC32')
395 initrelocs = getRelocs(noninitsections, initsections)
396 relocstr = (strRelocs("_reloc_abs", "code32init_start", absrelocs)
397 + strRelocs("_reloc_rel", "code32init_start", relrelocs)
398 + strRelocs("_reloc_init", "code32flat_start", initrelocs))
399 numrelocs = len(absrelocs + relrelocs + initrelocs)
400 sec32all_start -= numrelocs * 4
401 filesections32flat = getSectionsFileid(li.sections, '32flat')
402 out = outXRefs([], exportsyms=li.varlowsyms
403 , forcedelta=li.final_sec32low_start-li.sec32low_start)
404 multiboot_header = ""
405 if li.config.get('CONFIG_MULTIBOOT'):
406 multiboot_header = "LONG(0x1BADB002) LONG(0) LONG(-0x1BADB002)"
407 sec32all_start -= 3 * 4
408 out += outXRefs(filesections32flat, exportsyms=[li.entrysym]) + """
409 _reloc_min_align = 0x%x ;
410 zonefseg_start = 0x%x ;
411 zonefseg_end = 0x%x ;
412 zonelow_base = 0x%x ;
413 final_varlow_start = 0x%x ;
414 final_readonly_start = 0x%x ;
415 varlow_start = 0x%x ;
417 code32init_start = 0x%x ;
418 code32init_end = 0x%x ;
420 code32flat_start = 0x%x ;
421 .text code32flat_start : {
425 code32flat_end = ABSOLUTE(.) ;
427 """ % (li.sec32init_align,
431 li.final_sec32low_start,
432 li.final_readonly_start,
440 outRelSections(li.sections, 'code32flat_start'))
441 out = COMMONHEADER + out + COMMONTRAILER + """
445 text PT_LOAD AT ( code32flat_start ) ;
447 """ % (li.entrysym.name,)
448 outfile = open(out32flat, 'w')
453 ######################################################################
454 # Detection of unused sections and init sections
455 ######################################################################
457 # Visit all sections reachable from a given set of start sections
458 def findReachable(anchorsections, checkreloc, data):
459 anchorsections = dict([(section, []) for section in anchorsections])
460 pending = list(anchorsections)
462 section = pending.pop()
463 for reloc in section.relocs:
464 chain = anchorsections[section] + [section.name]
465 if not checkreloc(reloc, section, data, chain):
467 nextsection = reloc.symbol.section
468 if nextsection not in anchorsections:
469 anchorsections[nextsection] = chain
470 pending.append(nextsection)
471 return anchorsections
473 # Find "runtime" sections (ie, not init only sections).
474 def checkRuntime(reloc, rsection, data, chain):
475 section = reloc.symbol.section
476 if section is None or '.init.' in section.name:
478 if '.data.varinit.' in section.name:
479 print("ERROR: %s is VARVERIFY32INIT but used from %s" % (
480 section.name, chain))
484 # Find and keep the section associated with a symbol (if available).
485 def checkKeepSym(reloc, syms, fileid, isxref):
486 symbolname = reloc.symbolname
487 mustbecfunc = symbolname.startswith('_cfunc')
489 symprefix = '_cfunc' + fileid + '_'
490 if not symbolname.startswith(symprefix):
492 symbolname = symbolname[len(symprefix):]
493 symbol = syms.get(symbolname)
494 if (symbol is None or symbol.section is None
495 or symbol.section.name.startswith('.discard.')):
497 isdestcfunc = (symbol.section.name.startswith('.text.')
498 and not symbol.section.name.startswith('.text.asm.'))
499 if ((mustbecfunc and not isdestcfunc)
500 or (not mustbecfunc and isdestcfunc and isxref)):
503 reloc.symbol = symbol
506 # Resolve a relocation and check if it should be kept in the final binary.
507 def checkKeep(reloc, section, symbols, chain):
508 ret = checkKeepSym(reloc, symbols[section.fileid], section.fileid, 0)
511 # Not in primary sections - it may be a cross 16/32 reference
512 for fileid in ('16', '32seg', '32flat'):
513 if fileid != section.fileid:
514 ret = checkKeepSym(reloc, symbols[fileid], fileid, 1)
520 ######################################################################
521 # Startup and input parsing
522 ######################################################################
525 name = size = alignment = fileid = relocs = None
526 finalloc = finalsegloc = category = None
528 offset = type = symbolname = symbol = None
530 name = offset = section = None
532 # Read in output from objdump
533 def parseObjDump(file, fileid):
534 # sections = [section, ...]
537 # symbols[symbolname] = symbol
541 for line in file.readlines():
543 if line == 'Sections:':
546 if line == 'SYMBOL TABLE:':
549 if line.startswith('RELOCATION RECORDS FOR ['):
550 sectionname = line[24:-2]
551 if sectionname.startswith('.debug_'):
552 # Skip debugging sections (to reduce parsing time)
556 relocsection = sectionmap[sectionname]
559 if state == 'section':
561 idx, name, size, vma, lma, fileoff, align = line.split()
562 if align[:3] != '2**':
566 section.size = int(size, 16)
567 section.align = 2**int(align[3:])
568 section.fileid = fileid
570 sections.append(section)
571 sectionmap[name] = section
575 if state == 'symbol':
577 parts = line[17:].split()
579 sectionname, size, name = parts
580 elif len(parts) == 4 and parts[2] == '.hidden':
581 sectionname, size, hidden, name = parts
585 symbol.size = int(size, 16)
586 symbol.offset = int(line[:8], 16)
588 symbol.section = sectionmap.get(sectionname)
589 symbols[name] = symbol
595 off, type, symbolname = line.split()
597 reloc.offset = int(off, 16)
599 reloc.symbolname = symbolname
600 reloc.symbol = symbols.get(symbolname)
601 if reloc.symbol is None:
602 # Some binutils (2.20.1) give section name instead
603 # of a symbol - create a dummy symbol.
604 reloc.symbol = symbol = Symbol()
607 symbol.name = symbolname
608 symbol.section = sectionmap.get(symbolname)
609 symbols[symbolname] = symbol
610 relocsection.relocs.append(reloc)
613 return sections, symbols
615 # Parser for constants in simple C header files.
616 def scanconfig(file):
619 for l in f.readlines():
623 if parts[0] != '#define':
626 if value.isdigit() or (value.startswith('0x') and value[2:].isdigit()):
627 value = int(value, 0)
628 opts[parts[1]] = value
633 in16, in32seg, in32flat, cfgfile, out16, out32seg, out32flat = sys.argv[1:]
635 # Read in the objdump information
636 infile16 = open(in16, 'r')
637 infile32seg = open(in32seg, 'r')
638 infile32flat = open(in32flat, 'r')
640 # infoX = (sections, symbols)
641 info16 = parseObjDump(infile16, '16')
642 info32seg = parseObjDump(infile32seg, '32seg')
643 info32flat = parseObjDump(infile32flat, '32flat')
645 # Read kconfig config file
646 config = scanconfig(cfgfile)
648 # Figure out which sections to keep.
649 allsections = info16[0] + info32seg[0] + info32flat[0]
650 symbols = {'16': info16[1], '32seg': info32seg[1], '32flat': info32flat[1]}
651 if config.get('CONFIG_COREBOOT'):
652 entrysym = symbols['16'].get('entry_elf')
653 elif config.get('CONFIG_CSM'):
654 entrysym = symbols['16'].get('entry_csm')
656 entrysym = symbols['16'].get('reset_vector')
657 anchorsections = [entrysym.section] + [
658 section for section in allsections
659 if section.name.startswith('.fixedaddr.')]
660 keepsections = findReachable(anchorsections, checkKeep, symbols)
661 sections = [section for section in allsections if section in keepsections]
663 # Separate 32bit flat into runtime, init, and special variable parts
665 section for section in sections
666 if ('.data.varlow.' in section.name or '.data.varfseg.' in section.name
667 or '.fixedaddr.' in section.name or '.runtime.' in section.name)]
668 runtimesections = findReachable(anchorsections, checkRuntime, None)
669 for section in sections:
670 if section.name.startswith('.data.varlow.'):
671 section.category = '32low'
672 elif section.name.startswith('.data.varfseg.'):
673 section.category = '32fseg'
674 elif section.name.startswith('.text.32fseg.'):
675 section.category = '32textfseg'
676 elif section.name.startswith('.fixedaddr.'):
677 section.category = 'fixed'
678 elif section.fileid == '32flat' and section not in runtimesections:
679 section.category = '32init'
681 section.category = section.fileid
683 # Determine the final memory locations of each kept section.
684 genreloc = '_reloc_abs_start' in symbols['32flat']
685 li = doLayout(sections, config, genreloc)
688 li.varlowsyms = [symbol for symbol in symbols['32flat'].values()
689 if (symbol.section is not None
690 and symbol.section.finalloc is not None
691 and '.data.varlow.' in symbol.section.name
692 and symbol.name != symbol.section.name)]
693 li.entrysym = entrysym
695 # Write out linker script files.
696 writeLinkerScripts(li, out16, out32seg, out32flat)
698 if __name__ == '__main__':