Anas Nashif | ed3d7c1 | 2017-04-10 08:53:23 -0400 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
Andy Ross | 4cc228b | 2016-10-03 12:20:39 -0700 | [diff] [blame] | 2 | import fileinput |
| 3 | import re |
| 4 | import sys |
| 5 | |
| 6 | # Linker address generation validity checker. By default, GNU ld is |
| 7 | # broken when faced with sections where the load address (i.e. the |
| 8 | # spot in the XIP program binary where initialized data lives) differs |
| 9 | # from the virtual address (i.e. the location in RAM where that data |
| 10 | # will live at runtime. We need to be sure we're using the |
| 11 | # ALIGN_WITH_INPUT feature correctly everywhere, which is hard -- |
| 12 | # especially so given that many of these bugs are semi-invisible at |
| 13 | # runtime (most initialized data is still a bunch of zeros and often |
| 14 | # "works" even if it's wrong). |
| 15 | # |
| 16 | # This quick test just checks the offsets between sequential segments |
| 17 | # with separate VMA/LMA addresses and verifies that the size deltas |
| 18 | # are identical. |
| 19 | # |
| 20 | # Note that this is assuming that the address generation is always |
| 21 | # in-order and that there is only one "ROM" LMA block. It's possible |
| 22 | # to write a valid linker script that will fail this script, but we |
| 23 | # don't have such a use case and one isn't forseen. |
| 24 | |
| 25 | section_re = re.compile('(?x)' # (allow whitespace) |
| 26 | '^([a-zA-Z0-9_\.]+) \s+' # name |
| 27 | ' (0x[0-9a-f]+) \s+' # addr |
Andrew Boie | d9c4856 | 2017-06-19 12:47:55 -0700 | [diff] [blame] | 28 | ' (0x[0-9a-f]+)\s*') # size |
Andy Ross | 4cc228b | 2016-10-03 12:20:39 -0700 | [diff] [blame] | 29 | |
| 30 | load_addr_re = re.compile('load address (0x[0-9a-f]+)') |
| 31 | |
| 32 | in_prologue = True |
| 33 | lma = 0 |
| 34 | last_sec = None |
| 35 | |
| 36 | for line in fileinput.input(): |
| 37 | # Skip the header junk |
| 38 | if line.find("Linker script and memory map") >= 0: |
| 39 | in_prologue = False |
| 40 | continue |
| 41 | |
| 42 | match = section_re.match(line.rstrip()) |
| 43 | if match: |
| 44 | sec = match.group(1) |
| 45 | vma = int(match.group(2), 16) |
| 46 | size = int(match.group(3), 16) |
| 47 | |
Andrew Boie | d9c4856 | 2017-06-19 12:47:55 -0700 | [diff] [blame] | 48 | if (sec == "bss"): |
| 49 | # Make sure we don't compare the last section of kernel data |
| 50 | # with the first section of application data, the kernel's bss |
| 51 | # and noinit are in between. |
| 52 | last_sec = None |
| 53 | continue |
| 54 | |
Andy Ross | 4cc228b | 2016-10-03 12:20:39 -0700 | [diff] [blame] | 55 | lmatch = load_addr_re.search(line.rstrip()) |
| 56 | if lmatch: |
| 57 | lma = int(lmatch.group(1), 16) |
| 58 | else: |
| 59 | last_sec = None |
| 60 | continue |
| 61 | |
| 62 | if last_sec != None: |
| 63 | dv = vma - last_vma |
| 64 | dl = lma - last_lma |
| 65 | if dv != dl: |
| 66 | sys.stderr.write("ERROR: section %s is %d bytes " |
| 67 | "in the virtual/runtime address space, " |
| 68 | "but only %d in the loaded/XIP section!\n" |
| 69 | % (last_sec, dv, dl)) |
| 70 | sys.exit(1) |
| 71 | |
| 72 | last_sec = sec |
| 73 | last_vma = vma |
| 74 | last_lma = lma |