fix(f722): bound flash operations from sram
This commit is contained in:
1 parent
9fec2fa095
commit
1c90870634
4 files changed
+302
-63
No files matched your search
@@ -19,7 +19,7 @@ REGION = re.compile(
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
Section = namedtuple("Section", "name size address allocated")
|
||||
Section = namedtuple("Section", "name size address allocated executable")
|
||||
LoadSegment = namedtuple(
|
||||
"LoadSegment", "virtual_address physical_address file_size memory_size"
|
||||
)
|
||||
@@ -78,6 +78,7 @@ def elf_layout(output):
|
||||
section["Size"],
|
||||
section["Address"],
|
||||
"SHF_ALLOC" in flags,
|
||||
"SHF_EXECINSTR" in flags,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -186,6 +187,76 @@ def validation_errors(layout, section_list, load_segments):
|
||||
return errors
|
||||
|
||||
|
||||
def flash_operation_section_errors(layout, section_list):
|
||||
"""Validate the output section that contains the F722 Flash operation."""
|
||||
matches = [section for section in section_list if section.name == ".data"]
|
||||
if len(matches) != 1:
|
||||
return ["firmware must contain exactly one .data section"]
|
||||
section = matches[0]
|
||||
if not section.allocated or not section.executable or section.size == 0:
|
||||
return [".data must contain executable SRAM Flash-operation code"]
|
||||
if region_for_span(section.address, section.size, layout) != "RAM":
|
||||
return ["executable .data must reside completely in RAM"]
|
||||
return []
|
||||
|
||||
|
||||
INSTRUCTION = re.compile(
|
||||
r"^\s*([0-9A-Fa-f]+):\s+([A-Za-z][A-Za-z0-9.]*)\s*(.*?)\s*$"
|
||||
)
|
||||
DIRECT_BRANCH = re.compile(
|
||||
r"^(?:b(?:eq|ne|cs|hs|cc|lo|mi|pl|vs|vc|hi|ls|ge|lt|gt|le)?"
|
||||
r"(?:\.[nw])?|blx?|bx|cbz|cbnz)$"
|
||||
)
|
||||
|
||||
|
||||
def sram_control_flow_errors(section, disassembly):
|
||||
"""Reject external control flow and PC-relative loads from SRAM code."""
|
||||
errors = []
|
||||
instructions = 0
|
||||
end = section.address + section.size
|
||||
for line in disassembly.splitlines():
|
||||
match = INSTRUCTION.match(line)
|
||||
if match is None:
|
||||
continue
|
||||
instructions += 1
|
||||
address = int(match.group(1), 16)
|
||||
mnemonic = match.group(2).lower()
|
||||
operands = match.group(3).split("@", 1)[0].strip().lower()
|
||||
if not section.address <= address < end:
|
||||
errors.append(f"instruction 0x{address:08X} is outside .data")
|
||||
continue
|
||||
|
||||
if DIRECT_BRANCH.fullmatch(mnemonic):
|
||||
target = re.search(r"\b0x([0-9a-f]+)\b", operands)
|
||||
if target is None:
|
||||
if mnemonic == "bx" and operands == "lr":
|
||||
continue
|
||||
errors.append(
|
||||
f"0x{address:08X} uses indirect control transfer {mnemonic}"
|
||||
)
|
||||
continue
|
||||
destination = int(target.group(1), 16)
|
||||
if not section.address <= destination < end:
|
||||
errors.append(
|
||||
f"0x{address:08X} branches outside SRAM .data to "
|
||||
f"0x{destination:08X}"
|
||||
)
|
||||
continue
|
||||
|
||||
if mnemonic in ("tbb", "tbh"):
|
||||
errors.append(f"0x{address:08X} uses indirect control transfer {mnemonic}")
|
||||
elif mnemonic == "pop" and re.search(r"\bpc\b", operands):
|
||||
continue
|
||||
elif re.match(r"^pc(?:\s|,|$)", operands):
|
||||
errors.append(f"0x{address:08X} writes the program counter indirectly")
|
||||
elif mnemonic.startswith("ldr") and re.search(r"\[\s*pc\b", operands):
|
||||
errors.append(f"0x{address:08X} performs a PC-relative load")
|
||||
|
||||
if instructions == 0:
|
||||
errors.append("llvm-objdump found no instructions in executable .data")
|
||||
return errors
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
raise ValueError(f"usage: {sys.argv[0]} <firmware.elf> <memory.x>")
|
||||
@@ -248,6 +319,26 @@ def main():
|
||||
if errors:
|
||||
raise ValueError("firmware memory validation failed:\n " + "\n ".join(errors))
|
||||
|
||||
errors = flash_operation_section_errors(layout, section_list)
|
||||
if errors:
|
||||
raise ValueError("Flash-operation placement validation failed:\n " + "\n ".join(errors))
|
||||
data = next(section for section in section_list if section.name == ".data")
|
||||
objdump = llvm_tool("llvm-objdump")
|
||||
errors = sram_control_flow_errors(
|
||||
data,
|
||||
run(
|
||||
[
|
||||
str(objdump),
|
||||
"-d",
|
||||
"--no-show-raw-insn",
|
||||
"--section=.data",
|
||||
str(elf),
|
||||
]
|
||||
),
|
||||
)
|
||||
if errors:
|
||||
raise ValueError("Flash-operation control-flow validation failed:\n " + "\n ".join(errors))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
|
||||
Reference in new issue
Block a user