#!/usr/bin/env python3
"""Compare board/driver DWARF class layouts in the actual linked firmware.

M5PM1/M5IOE1 compat headers vary fields with __has_include(M5GFX.h).
A build that links successfully can still overflow a board-allocated object.
"""
import sys
from elftools.elf.elffile import ELFFile

layouts = {}
objects = {}
with open(sys.argv[1], "rb") as handle:
    elf = ELFFile(handle)
    for symbol in elf.get_section_by_name(".symtab").iter_symbols():
        for name in ("s_pmic", "s_ioe"):
            if symbol.name.endswith(f"{len(name)}{name}E"):
                objects[name] = (symbol["st_value"], symbol["st_size"])
    for cu in elf.get_dwarf_info().iter_CUs():
        path = cu.get_top_DIE().attributes["DW_AT_name"].value.decode()
        unit = path.rsplit("/", 1)[-1]
        if unit not in ("board.cpp", "M5PM1.cpp", "M5IOE1.cpp"):
            continue
        for die in cu.iter_DIEs():
            if die.tag != "DW_TAG_class_type" or "DW_AT_byte_size" not in die.attributes:
                continue
            name = die.attributes.get("DW_AT_name")
            if not name or name.value not in (b"M5PM1", b"M5IOE1"):
                continue
            members = {
                child.attributes["DW_AT_name"].value.decode(): child.attributes["DW_AT_data_member_location"].value
                for child in die.iter_children() if child.tag == "DW_TAG_member"
            }
            layouts[(unit, name.value.decode())] = (die.attributes["DW_AT_byte_size"].value, members)
for name, obj in (("M5PM1", "s_pmic"), ("M5IOE1", "s_ioe")):
    board = layouts[("board.cpp", name)]
    driver = layouts[(name + ".cpp", name)]
    assert board == driver, f"{name} board/driver ABI mismatch: {board} != {driver}"
    assert objects[obj][1] == board[0], f"{obj} allocation mismatch"
    print(f"{name} board/driver ABI PASS: {board[0]} bytes, all {len(board[1])} member offsets match")
a, b = sorted(objects.values())
assert a[0] + a[1] <= b[0], "PMIC/IOE allocations overlap"
print("PMIC/IOE linked allocations do not overlap PASS")
