eh_frame function hints are only loaded when load_debug_info is set, so CFGFast's eh_frame seeding is inert by default
- Lingua principale
- Python
- Stelle
- 485
- Fork
- 135
- Merge medio
- 2g 2h
- PR unite (30g)
- 15
Descrizione
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS
`.eh_frame` function hints are only collected when the loader was asked for
debug information, which it is not by default. Every consumer of
`Backend.function_hints` therefore sees an empty list for an ordinary
`angr.Project(path)`, including `CFGFast`, whose `eh_frame=True` option is
documented as "Retrieve function starts (and maybe sizes later) from the
.eh_frame of ELF binaries" and is on by default.
```python
# cle/backends/elf/elf.py
if self.has_dwarf_info and self.loader._load_debug_info:
...
if dwarf.has_EH_CFI():
self._load_function_hints_from_fde(dwarf, FunctionHintSource.EH_FRAME)
# cle/loader.py
load_debug_info: bool = False,
```
`.eh_frame` is not debug information in the sense that flag guards. It is an
allocated section, present after stripping, mapped at run time, and reading its
FDE headers needs neither `.debug_info` nor `.debug_abbrev`. Requiring the
debug-info opt-in to see it means the common case — a stripped release binary,
where the FDE headers are the only remaining record of where functions start —
is exactly the case where the hints are unavailable, while a binary carrying
full DWARF, where the symbol table already answers the question, is the case
where they are loaded.
### What it costs
Adding the FDE-derived hints to `main_object.function_hints` after loading and
changing nothing else, then running `CFGFast(normalize=True,
data_references=False, resolve_indirect_jumps=True, force_complete_scan=False)`,
counting `.eh_frame` FDE `initial_location` values with no block starting on
them. All of these are stripped shared objects and executables from a corpus
sweep, none of them with a `.symtab`:
```
arch FDEs entries with no block, default with the hints
RISCV64 1068 22 0
RISCV64 390 19 1
SuperH 257 39 3
X86 1519 42 3
Loongarch 148 36 24
S390X 58 16 4
```
A six-FDE x86-64 shared object shows the shape: `function_hints` is empty by
default and the entries at `0x4014c0` and `0x402000` are not block starts; with
`load_debug_info=True` there are six hints and both become block starts. That
code — `endbr64; push %r15; ...` — is reached only through a registration table,
so nothing in the binary points at it and `.eh_frame` is the only place its
entry is written down.
It does not fix everything: on several ppc64, ppc32, s390x and x86 objects the
count does not move, because the linear scan has already claimed the entry as
part of a block that began in the preceding alignment padding. That is a
separate defect, filed against angr.
### The relocatable caveat
#597 records that hints derived this way are wrong for stripped relocatable
objects, whose FDE addresses are all zero before a linker assigns them. That
argues for excluding `ET_REL`, not for tying `.eh_frame` to `load_debug_info`.
Two directions, both a call for whoever owns the loader: read the FDE headers
unconditionally for non-relocatable ELFs and accept the parse cost on every
load, or keep them behind an opt-in but a separate one, so that
`CFGFast(eh_frame=True)` is not silently inert.
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Valutazione
Questa issue non è ancora stata valutata.