1
0
This commit is contained in:
Aiden
2026-05-25 13:47:13 +10:00
parent 46ccaf3e39
commit 5ad90ade49
12 changed files with 17974 additions and 883 deletions

View File

@@ -59,3 +59,103 @@ def collect_labels(instructions: Iterable[Instruction], vectors: dict[int, tuple
for target in ins.targets:
labels.setdefault(target, label_for(target))
return labels
def collect_function_entries(
instructions: Iterable[Instruction],
vectors: dict[int, tuple[str, int]],
) -> set[int]:
entries = {target for _name, target in vectors.values()}
for ins in instructions:
if ins.kind == "call":
entries.update(ins.targets)
return entries
def assign_functions(instructions: dict[int, Instruction], entries: set[int]) -> dict[int, int]:
owners: dict[int, int] = {}
current: int | None = None
for address in sorted(instructions):
if address in entries:
current = address
if current is not None:
owners[address] = current
if instructions[address].kind in {"return", "rte", "sleep"}:
current = None
return owners
def build_functions(
instructions: dict[int, Instruction],
vectors: dict[int, tuple[str, int]],
labels: dict[int, str],
) -> list[dict[str, object]]:
entries = collect_function_entries(instructions.values(), vectors)
owners = assign_functions(instructions, entries)
vector_sources: dict[int, list[str]] = {}
for _vector_addr, (name, target) in vectors.items():
vector_sources.setdefault(target, []).append(name)
functions: dict[int, dict[str, object]] = {}
for address, owner in owners.items():
ins = instructions[address]
function = functions.setdefault(
owner,
{
"start": owner,
"label": labels.get(owner, label_for(owner)),
"sources": vector_sources.get(owner, []),
"instruction_count": 0,
"end": owner,
"calls": [],
"unresolved_calls": 0,
},
)
function["instruction_count"] = int(function["instruction_count"]) + 1
function["end"] = max(int(function["end"]), ins.address + max(ins.size, 1) - 1)
if ins.kind == "call":
if ins.targets:
calls = function["calls"]
assert isinstance(calls, list)
for target in ins.targets:
if target not in calls:
calls.append(target)
else:
function["unresolved_calls"] = int(function["unresolved_calls"]) + 1
return [functions[start] for start in sorted(functions)]
def build_call_graph(
instructions: dict[int, Instruction],
vectors: dict[int, tuple[str, int]],
labels: dict[int, str],
) -> dict[str, object]:
entries = collect_function_entries(instructions.values(), vectors)
owners = assign_functions(instructions, entries)
nodes = build_functions(instructions, vectors, labels)
edges: list[dict[str, object]] = []
seen: set[tuple[int, int]] = set()
for ins in instructions.values():
if ins.kind != "call" or not ins.targets:
continue
source = owners.get(ins.address)
if source is None:
continue
for target in ins.targets:
key = (source, target)
if key in seen:
continue
seen.add(key)
edges.append(
{
"from": source,
"from_label": labels.get(source, label_for(source)),
"to": target,
"to_label": labels.get(target, label_for(target)),
"call_site": ins.address,
},
)
return {"nodes": nodes, "edges": sorted(edges, key=lambda edge: (edge["from"], edge["to"]))}