zephyr/scripts/check_link_map.py

75 lines
2.6 KiB
Python
Executable File

#!/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(r'(?x)' # (allow whitespace)
r'^([a-zA-Z0-9_\.]+) \s+' # name
r' (0x[0-9a-f]+) \s+' # addr
r' (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