Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions decbench/metrics/type_match.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,8 +528,9 @@ def _get_location(die: Any, dwarfinfo: Any) -> tuple[list[int], bool]:
if op.op_name == "DW_OP_fbreg":
offsets.append(op.args[0] + 16)

elif loc_attr.form == "DW_FORM_sec_offset":
elif loc_attr.form in ("DW_FORM_sec_offset", "DW_FORM_loclistx"):
loclists = dwarfinfo.location_lists()
# pyelftools has resolved a loclistx index to a section offset in value.
loclist = loclists.get_location_list_at_offset(loc_attr.value, die=die)

for entry in loclist:
Expand Down Expand Up @@ -841,7 +842,7 @@ class TypeMatchMetric(Metric):
display_name = "Type Correctness"
description = "Accuracy of variable type recovery vs DWARF ground truth"

cache_version = "18"
cache_version = "20"

weight = 1.0
lower_is_better = False
Expand Down
4 changes: 2 additions & 2 deletions docs/metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ Ground truth is the function's OWN parameters and locals: the variables of an
inlined callee (`DW_TAG_inlined_subroutine`, at `-O2` mostly the parameters of
glibc's `_FORTIFY_SOURCE` wrappers such as `printf`'s `__fmt`) are not scored,
since the compiled function usually has no storage for them.
Current `cache_version="18"`, bumped for scoped syntax-aware declaration parsing,
aggregate/qualifier normalization, and DWARF restrict-type passthrough.
Current `cache_version="20"`, bumped to include DWARF 5 indexed location lists
(`DW_FORM_loclistx`) when extracting ground-truth variables.
The per-function key covers source and
decompiler address evidence, argument/stack anchors, recovered types, source
selection diagnostics, and the matching thresholds.
Expand Down
28 changes: 28 additions & 0 deletions tests/test_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,34 @@ def __init__(self, index: int, *, entry: bool, exit: bool):
class TestTypeMatchMetric:
"""Tests for the type match metric."""

@pytest.mark.parametrize("form", ["DW_FORM_sec_offset", "DW_FORM_loclistx"])
@pytest.mark.parametrize(
("expression", "expected"),
[([0x50], ([], True)), ([0x91, 0x78], ([8], True)), ([], ([], False))],
)
def test_location_lists_use_decoded_offset(
self, form: str, expression: list[int], expected: tuple[list[int], bool]
) -> None:
from types import SimpleNamespace

from elftools.dwarf.structs import DWARFStructs

from decbench.metrics.type_match import _get_location

attribute = SimpleNamespace(form=form, value=0x38, raw_value=2)
die = SimpleNamespace(attributes={"DW_AT_location": attribute})

def read_list(offset: int, *, die: SimpleNamespace) -> list[SimpleNamespace]:
assert offset == 0x38
assert die.attributes["DW_AT_location"] is attribute
return [SimpleNamespace(loc_expr=expression)]

dwarf = SimpleNamespace(
structs=DWARFStructs(little_endian=True, dwarf_format=32, address_size=8),
location_lists=lambda: SimpleNamespace(get_location_list_at_offset=read_list),
)
assert _get_location(die, dwarf) == expected

def test_type_match_registration(self) -> None:
MetricRegistry.clear()
from decbench.metrics.type_match import TypeMatchMetric
Expand Down