Skip to content

perf(viv): optimize elf symbol resolution - #3182

Open
tryhard-26 wants to merge 1 commit into
mandiant:masterfrom
tryhard-26:perf/viv-elf-symbol-lookup-and-type-validation
Open

tryhard-26 wants to merge 1 commit into
mandiant:masterfrom
tryhard-26:perf/viv-elf-symbol-lookup-and-type-validation

Conversation

@tryhard-26

Copy link
Copy Markdown

in vivisect elf feature extraction (viv/function.py and viv/insn.py), symbol types were checked using sym_info & 0x2 != 0 in a linear loop over all symbols on every call instruction and function. because elf encodes symbol types in the lower 4 bits of st_info as an enum not a bitmask, testing bit 1 falsely treated section headers (0x3) and thread-local storage variables (0x6) as callable functions and apis, since both have bit 1 set. repeatedly re-scanning the entire symbol table and calling get_name() on every instruction also created a quadratic bottleneck, causing over 2.3 million redundant string decodings across small test binaries.

this pr adds get_elf_symbol_functions in viv.helpers to validate symbol types using (symbol.info & 0xf) in (2, 10) and pre-indexes callable symbols by address into the workspace cache. this converts instruction and function symbol lookups into o(1) dictionary lookups, safely skips malformed symbol names instead of crashing, and stops non-function symbols from leaking into extracted features.

benchmarked against 5 elf fixtures already in tests/data:

sample size get_name() calls before after lookup time before after speedup false positives removed
microsocks.elf_ 30.9 kb 29,988 82 13.0 ms 0.17 ms 78.1x 0
055da8e6ccfe5a9380231ea04b850e18.elf_ 16.4 kb 1,139 15 0.4 ms 0.05 ms 6.8x 27
7351f8a40c5450557b24622417fc478d.elf_ 52.6 kb 231,093 145 94.1 ms 0.33 ms 286.0x 26
2bf18d0403677378adad9001b1243211.elf_ 101.6 kb 1,150,500 420 426.1 ms 0.69 ms 617.3x 14
e17e6a79ed614f5468d0eed758629697.elf_ 195.2 kb 956,265 147 556.7 ms 0.86 ms 648.8x 3
total 2,368,985 809 1,090.3 ms 2.1 ms 519.9x 70

call counts and false positive counts above are deterministic, timings are single-run on my machine [macbook air m4 16gigs] and will vary by environment.

all tests pass, new unit tests covering symbol type filtering, corrupt symbol recovery, and caching have been added

ruff check, ruff format, and deptry all pass cleanly.

no linked gh issue as such

used gemini a bit to help debug the type masking logic

idk whether changelog update is needed or not

checklist

  • no changelog update needed
  • new tests added
  • no documentation update needed
  • this submission includes ai-generated code and i have provided details in the description

@tryhard-26
tryhard-26 force-pushed the perf/viv-elf-symbol-lookup-and-type-validation branch 2 times, most recently from b974f17 to 0232db1 Compare September 26, 2026 03:35
@tryhard-26

Copy link
Copy Markdown
Author

@williballenthin

STT_FUNC = 0x2
if sym_value == fh.address and sym_info & STT_FUNC != 0:
yield FunctionName(sym_name), fh.address
funcs = get_elf_symbol_functions(fh.ctx["cache"], fh.inner.vw.parsedbin)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd prefer to split fetching the items from setting the cache, otherwise looks good!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@williballenthin made the changes!

@tryhard-26
tryhard-26 force-pushed the perf/viv-elf-symbol-lookup-and-type-validation branch from 0232db1 to 8e88e01 Compare September 28, 2026 04:42
@tryhard-26

Copy link
Copy Markdown
Author

btw @williballenthin

heads up, this will trip the 7351f-elf feature snapshot test. Drift is intended tho as the old sym_info & 0x2 check let unnamed STT_SECTION (type 3) symbols through, so the old snapshot has 5 empty-name features: api("") at 0x4088e0 and function-name("") at 0x4016d8, 0x4016f0, 0x401c50 and 0x408988. those are exactly the false positives this pr removes. the real names at those addresses (_init, _start, _fini) are still extracted. a pr is needed to capa-testfiles to regenerate the fixture and bump the submodule here.

@williballenthin

Copy link
Copy Markdown
Collaborator

good point, thanks for calling that out.

would you please open that PR @tryhard-26?

tryhard-26 added a commit to tryhard-26/capa-testfiles that referenced this pull request Sep 28, 2026
mandiant/capa#3182 checks elf symbol types with (st_info & 0xf) in
(STT_FUNC, STT_GNU_IFUNC) instead of st_info & 0x2, which wrongly let
unnamed STT_SECTION (type 3) symbols through. this drops 5 empty-name
false positives from the snapshot: api("") at 0x4088e0 and
function-name("") at 0x4016d8, 0x4016f0, 0x401c50 and 0x408988. no
other features change.
@tryhard-26
tryhard-26 force-pushed the perf/viv-elf-symbol-lookup-and-type-validation branch from 122f201 to 5fd2901 Compare September 28, 2026 13:11
@tryhard-26

Copy link
Copy Markdown
Author

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants