Conversation
Loader._find_safe_rebase_addr tries three alignments in turn: the rebase granularity, a 4 KiB page, and a single byte. On AVR a page is a sixteenth of the whole 16-bit address space, so the first sixteen members of a static archive land one per page, every hole left over is smaller than a page, and the load fails for the first member too big for a hole while most of the space is empty. gcc-avr's libgcov.a places 27 of its 28 members and then raises "Ran out of room in address space" with 0xf90 of 0x10000 bytes in use. Scale that middle rung to the space it is placing into. The two constants already here are each a 4096th of the address space they suit -- 0x100000 of 2**32 and 0x1000 of 2**24 -- so min(0x1000, limit >> 12) generalises them rather than adding a third. 12 is also the largest shift that leaves every address space of 24 bits or wider with exactly the alignments it has today. Below 24 bits objects do move, which is the point: the same archive now packs all 28 members into 0x23f0 bytes. Which rungs the ladder has is still decided by the caller's rebase_granularity exactly as before, so a caller that already asks for 0x1000 or less gets an identical ladder. Placement is first-fit, so a finer rung is a better heuristic and not a guarantee: packing tighter can leave a differently shaped hole. Across 60,000 randomised sub-24-bit placements it placed one object fewer than master in 104, every one of them in a size population built out of page-boundary sizes and none in four ordinary populations including the real AVR member sizes. In the regime this is for -- 150 to 420 archive members in 64 KiB -- it placed more in 600 of 600 trials and fewer in none. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Loading Before -- placement gives up on the twenty-eighth member with the address space almost empty: angr/cle master 0e77adeAfter -- all 28 members are placed, and they take with this change |
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Validation record for head
How the A/B was runTwo source trees extracted with
Where this packs worse than masterThe change is not a strict improvement, and this is the measurement that shows it. At 16 bits with Randomised search, same seed on both arms so both see byte-identical trials, 5 to 80 objects per trial, five size families (log-uniform in two ranges, uniform, resampled from the 1,596 real AVR
TimingCPU time (
The alternative that was rejectedCapping the caller's
It buys nothing this change does not, regresses identically on the adversarial case, and changes more. Neighbours
CaveatsThe 114 AVR archives come from the Debian Ports build of session: sharpen |
|
Corpus decompilation diffs can be found at angr/dec-snapshots@master...angr/cle_833 |
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS
Problem
cle.Loadercannot load an AVR gcc runtime archive, and reports the 64 KiBaddress space full with a sixteenth of it in use:
The archive holds 28 members, which cle maps at
0x70(21 of them),0x110(6)and
0x1460(1). Twenty-seven are placed;_gcov_info_to_gcda.oat0x1460isnot. At that point
0xf90of0x10000bytes are occupied and0xf070is free,spread over sixteen gaps whose largest is
0xf90.It is not one archive. Debian Ports
gcc-avr1:14.2.0-2 ships 114 archives underusr/lib/gcc/avr/14.2.0/, and all 57libgcov.aamong them fail this way.Root cause
Loader._find_safe_rebase_addrtries each alignment in turn and takes the firstgap that fits:
The ladder is right; the constant in it is not. AVR's whole address space is
0x10000, so the 1 MiB granule never fits after the first object and the pagerung does all the work -- and a page is a sixteenth of the space. The first
sixteen members land at
0x0,0x1000...0xf000, one per page, each leavinga hole of at most
0xf90. Everything after that packs into the low holes at bytealignment, and the first member larger than a hole cannot go anywhere. The byte rung
below does not rescue this: by the time it runs, the page rung has already spent the
space.
Fix
Scale the page rung to the address space:
0x1000is an absolute number in a decision about proportion. The rung above itis already proportionate:
0x100000is a 4096th of a 32-bit address space and0x1000is a 4096th of a 24-bit one, solimit >> 12generalises the twoconstants already here instead of adding a third. It is
0x1000exactly whenlimit >= 2**24, so every address space of 24 bits or wider gets the alignmentsit gets today, and 12 is the largest shift with that property. Which rungs
exist is still decided by the caller's
rebase_granularityexactly as before, soa caller that asks for
0x1000or less gets an identical ladder. On AVR theladder becomes
[0x100000, 0x10, 1]and all 28 members fit in0x23f0bytes.A caller's own
rebase_granularityis deliberately not capped. Capping it alsofixes AVR, and it moves the addresses
cle/tests/test_relocated.pyandangr/tests/sim/test_accuracy.pyassert underrebase_granularity=0x1000000oni386, mips, ppc and armel, and moves 24-bit architectures as well. An explicit
granularity is a request; the rung the loader falls back to when that request
does not fit is cle's own choice, and is the part that was wrong.
Placement is first-fit, so a finer rung is a better heuristic and not a
guarantee: packing tighter can leave a differently shaped hole, and the
validation record has the measured rate both ways.
Testing
tests/test_rebase.py::test_archive_members_fill_a_16_bit_address_spaceloadsthe archive and asserts all 28 members are placed, disjoint, inside
2**16andeach findable through
find_object_containing. On the merge base it fails withCLEOperationError: Ran out of room in address spaceatcle/loader.py:1095;with this change it passes.
Over Debian's 114 AVR archives, the 57
libgcov.ago from 0 loaded to 57. Full detail, including the placement A/B and the cases wherethis packs worse, is in the validation record.
This needs the fixture in angr/binaries#228 merged first, or the new test
has no input to load.
Validation: #833 (comment)
session: sharpen