Conversation
trufae
left a comment
There was a problem hiding this comment.
Three fixes needed before merging: the lost overflow guard makes relocation bounds disagree with reads, overlapping LOADs get the wrong sizes, and the public struct changes need an ABI bump. Details and reproductions are inline.
The two cleanup suggestions remove 22 implementation lines. Prefer bin.mem; bin.memimg is the more explicit alternative.
Built this head and ran all 11 new r2r tests successfully. The two reproduced ELF cases are not covered by those tests.
| bool nofuncstarts; | ||
| bool skip_symbols; // skip symbol loading (e.g., for companion debug files) | ||
| const char *filename; | ||
| bool memlayout; |
There was a problem hiding this comment.
[P1] Bump R2_ABIVERSION for this public layout change. On x86-64, RBinFileOptions grows from 56 to 64 bytes, and r_bin_file_options_init() clears sizeof (*opt). An existing plugin allocating the old struct will therefore have 8 bytes overwritten past its allocation. RBinFile also grows, but libr/include/r_lib.h still declares ABI 150, so the plugin loader cannot reject incompatible builds.
| const ut64 delta = vaddr - p->p_vaddr; | ||
| const ut64 span = eo->memory_layout? p->p_memsz: p->p_filesz; | ||
| if (delta >= span || (eo->memory_layout && p->p_vaddr < eo->memory_base)) { |
There was a problem hiding this comment.
[P2] Restore the overflow rejection removed from loaded_bytes_at(). In file mode this helper accepts a wrapping PT_LOAD that Elf_(v2p) rejects, so the bound and the read can select different segments.
Reproduced with x86_64-tailrela.so in a malloc buffer, bin.memlayout=false:
wv8 0x154 @ 0xd0
wv8 0x10d38 @ 0x88
wv8 0xffffffffffffffff @ 0x98
oba 0 0x10000
irThe first write alone returns one complete relocation. Adding the wrapping LOAD returns two: the second has only 4 bytes left in the LOAD actually selected by v2p, but read_reloc() reads all 24. Preserve the old p_filesz > UT64_MAX - p_vaddr rejection in the shared helper and add this regression.
| if (!backing_at (eo, ptr->vaddr, &ptr->paddr, &left) && ptr->vaddr >= eo->memory_base) { | ||
| ptr->paddr = ptr->vaddr - eo->memory_base; | ||
| } | ||
| ptr->size = phdr[i].p_type == PT_LOAD? left: R_MIN (ptr->size, left); |
There was a problem hiding this comment.
[P2] Use the current header to bound a PT_LOAD, rather than searching for the first LOAD containing its address. With x86_64-tailrela-memimg, set wv8 0xd00 @ 0x68 before oba 0 0x10000: iSS~LOAD reports LOAD1 size 0x600 instead of 0x50, and LOAD2 size 0x100 instead of 0x238. Both sizes come from the overlapping LOAD0.
Call segment_backing() with &phdr[i] for LOAD entries; keep the containing-LOAD lookup for the other header types. This fixes the sizes and removes the repeated full header scan for each LOAD. Add the overlap case to the r2r tests.
| if (desc) { | ||
| RBinFileOptions opt; | ||
| r_bin_file_options_init (&opt, desc->fd, baddr, addr, rawstr); | ||
| opt.memlayout = r_config_get_b (core->config, "bin.memlayout"); |
There was a problem hiding this comment.
Merge the two memory-loading branches here. After the existing file/base64 cases, compute addr once and select the optional base:
ut64 baddr = R_STR_ISNOTEMPTY (filename)? r_num_math (core->num, filename): addr;Then keep one copy of the descriptor lookup, options initialization, size fallback, open and finalization. The two branches otherwise do exactly the same work. This removes 18 lines, including the duplicate config lookup, without adding a helper.
| static ut64 loaded_bytes_at(ELFOBJ *eo, ut64 vaddr) { | ||
| ut64 off, left; | ||
| return backing_at (eo, vaddr, &off, &left)? left: 0; |
There was a problem hiding this comment.
This is now a forwarding wrapper with one caller. Remove it and replace its call in reloc_read_size() with:
ut64 off, loaded = 0;
backing_at (eo, vaddr, &off, &loaded);The helper only writes the outputs on success, so the initialized zero preserves the failure behavior. This removes another 4 lines while keeping the bounds calculation shared.
| SETB ("bin.relocs", "true", "load relocs information at startup if available"); | ||
| SETB ("bin.relocs.apply", "false", "apply reloc information"); | ||
| SETB ("bin.relocs.xrefs", "true", "register xrefs from reloc information"); | ||
| SETB ("bin.memlayout", "false", "oba <addr> [baddr] reads the buffer as a memory image (segments at vaddr - base)"); |
There was a problem hiding this comment.
Prefer bin.mem for the shorter name, or bin.memimg if you want the memory-image meaning explicit. My choice is bin.mem, with help text read oba input as a mapped memory image. Use the chosen name consistently in the config and the new options/file fields; no alias is needed for an unreleased option.
|
Also rebase |
64ec25b to
592be2e
Compare
|
Thanks, all addressed, rebased onto master:
Not covered here:
|
Description
An ELF copied out of a running process has each
PT_LOADatp_vaddr - base, not at its file offset, and r2 has no way to read it that way, so its relocs come back empty. This is the follow-up @trufae asked for when merging #26740. The image below isx86_64-tailrela.solaid out like that, with its section table zeroed (radareorg/radare2-testbins#147):With this PR and
-e bin.mem=trueit lists the eight relocs of the original file.bin.memtellsoba <addr> [baddr]the buffer is a memory image; it travels asRBinFileOptions.memintoRBinFileand survivesr_bin_reload. Nothing infers the layout from ELF fields. Both new fields sit in existing padding, so neither struct changes size;R2_ABIVERSIONis bumped to 151 for the layout change.segment_backing(), as sketched in Bound ELF reloc tables by the bytes that are loaded ##bin #26740, is the one place a vaddr becomes a buffer offset:p_vaddr - memory_basewithinp_memsz, clamped to the bytes present.v2p,p2v, the reloc-table bound, the segment sections andPT_INTERPall use it, so the bound and the read can no longer disagree. APT_LOADwhose span wraps is rejected, as before, and eachPT_LOADsection is sized by its own header. File layout keeps its existing paths.memory_baseis the vaddr of thePT_LOADthat maps the ELF header and program headers; without one the image is read as a file, with a warning. A loader never maps the section table, so the header'se_shnumis ignored in this mode..dynamicwas rebased in place by the loader (glibc may write runtime addresses into thed_ptrtags); the debugger's process-memory fallback inr_bin_open_iois left in file layout until that is handled. The ppc64 ELFv1 stub scan still reads code atp_offset.The new tests are in
test/db/formats/elf/reloc. One applies the #26740 recipe to an existing PIE. Others cut the image in the middle of the reloc table and at its end. Two more cut it inside a segment and exactly at the start of one. One keeps a header that still names its section table and one moves thePT_DYNAMICfile offset outside the image; another givesPT_DYNAMICa size that would wrap, which is rejected. Your two reproductions are cases too: a wrappingPT_LOADno longer bounds the reloc table in file mode, and overlappingPT_LOADs each keep their own size. Another checks that a reload keeps the layout. Two controls keep the output of master. One opens the same image without the flag and the other opens a plain file with it. An ELF whose header is not mapped by anyPT_LOADprints the same as it does without the flag and adds a warning.