#!/usr/bin/env python3 """Position-independence lint for the pureboot image. The self-staging design lets the identical binary run from any 512-byte slot, which holds only if nothing in the image addresses itself absolutely. Two link-time facts guarantee it, both asserted here from the built ELF: 1. No absolute jmp/call opcodes — all control flow is PC-relative (rjmp/rcall/ijmp/icall). -mrelax normally guarantees this; a code change that grows a branch out of relaxation range would break it silently. 2. The info block sits within the image's first 256 bytes: the 'b' command rebuilds its address as (running slot high byte : low byte of the link address), which needs the offset to fit that low byte. Usage: check_pi.py """ import re import subprocess import sys def main(): objdump, nm, elf, text_start = sys.argv[1:] text_start = int(text_start, 0) listing = subprocess.run([objdump, "-d", elf], capture_output=True, text=True, check=True).stdout absolute = [ line for line in listing.splitlines() if re.search(r"\t(jmp|call)\t", line) ] if absolute: print("FAIL: absolute control flow in the image:") print("\n".join(absolute)) sys.exit(1) symbols = subprocess.run([nm, "-C", elf], capture_output=True, text=True, check=True).stdout info = [line for line in symbols.splitlines() if "flash_table" in line and "::storage" in line] if len(info) != 1: print(f"FAIL: expected one info-block storage symbol, found {len(info)}") sys.exit(1) address = int(info[0].split()[0], 16) offset = address - text_start if not 0 <= offset < 256: print(f"FAIL: info block at image offset {offset:#x}, must sit in the first 256 bytes") sys.exit(1) print(f"PI lint: control flow PC-relative, info block at offset {offset:#x}") if __name__ == "__main__": main()