| #!/usr/bin/env python3 |
| import fileinput |
| import re |
| import sys |
| |
| # Linker address generation validity checker. By default, GNU ld is |
| # broken when faced with sections where the load address (i.e. the |
| # spot in the XIP program binary where initialized data lives) differs |
| # from the virtual address (i.e. the location in RAM where that data |
| # will live at runtime. We need to be sure we're using the |
| # ALIGN_WITH_INPUT feature correctly everywhere, which is hard -- |
| # especially so given that many of these bugs are semi-invisible at |
| # runtime (most initialized data is still a bunch of zeros and often |
| # "works" even if it's wrong). |
| # |
| # This quick test just checks the offsets between sequential segments |
| # with separate VMA/LMA addresses and verifies that the size deltas |
| # are identical. |
| # |
| # Note that this is assuming that the address generation is always |
| # in-order and that there is only one "ROM" LMA block. It's possible |
| # to write a valid linker script that will fail this script, but we |
| # don't have such a use case and one isn't forseen. |
| |
| section_re = re.compile('(?x)' # (allow whitespace) |
| '^([a-zA-Z0-9_\.]+) \s+' # name |
| ' (0x[0-9a-f]+) \s+' # addr |
| ' (0x[0-9a-f]+)\s*') # size |
| |
| load_addr_re = re.compile('load address (0x[0-9a-f]+)') |
| |
| in_prologue = True |
| lma = 0 |
| last_sec = None |
| |
| for line in fileinput.input(): |
| # Skip the header junk |
| if line.find("Linker script and memory map") >= 0: |
| in_prologue = False |
| continue |
| |
| match = section_re.match(line.rstrip()) |
| if match: |
| sec = match.group(1) |
| vma = int(match.group(2), 16) |
| size = int(match.group(3), 16) |
| |
| if (sec == "bss"): |
| # Make sure we don't compare the last section of kernel data |
| # with the first section of application data, the kernel's bss |
| # and noinit are in between. |
| last_sec = None |
| continue |
| |
| lmatch = load_addr_re.search(line.rstrip()) |
| if lmatch: |
| lma = int(lmatch.group(1), 16) |
| else: |
| last_sec = None |
| continue |
| |
| if last_sec is not None: |
| dv = vma - last_vma |
| dl = lma - last_lma |
| if dv != dl: |
| sys.stderr.write("ERROR: section %s is %d bytes " |
| "in the virtual/runtime address space, " |
| "but only %d in the loaded/XIP section!\n" |
| % (last_sec, dv, dl)) |
| sys.exit(1) |
| |
| last_sec = sec |
| last_vma = vma |
| last_lma = lma |