diff --git a/libr/bin/bfile.c b/libr/bin/bfile.c index ea4ab6949089f..783416f2c71e2 100644 --- a/libr/bin/bfile.c +++ b/libr/bin/bfile.c @@ -749,6 +749,7 @@ R_IPI RBinFile *r_bin_file_new(RBin *bin, const char *file, ut64 file_sz, RBinFi bf->rbin = bin; bf->file = file ? r_arena_push_str (arena, file) : NULL; bf->rawstr = opt->rawstr; + bf->inmem = opt->inmem; bf->fd = opt->fd; bf->curxtr = opt->pluginname? r_libstore_find_name_in (bin->libstore, bin->libstore->xtrs, opt->pluginname) : NULL; if ((st64)file_sz < 0) { diff --git a/libr/bin/bin.c b/libr/bin/bin.c index 212c07cdde2ea..366c054f64273 100644 --- a/libr/bin/bin.c +++ b/libr/bin/bin.c @@ -319,6 +319,7 @@ R_API bool r_bin_reload(RBin *bin, ut32 bf_id, ut64 baseaddr) { RBinFileOptions opt; r_bin_file_options_init (&opt, bf->fd, baseaddr, bf->loadaddr, bin->options.rawstr); opt.filename = bf->file; + opt.inmem = bf->inmem; if (!bf->buf) { r_bin_file_delete (bin, bf->id); return false; @@ -507,6 +508,7 @@ R_API bool r_bin_open_io(RBin *bin, RBinFileOptions *opt) { if (buf) { // reading from the backing file on disk, not process memory is_debugger = false; + opt->inmem = false; opt->loadaddr = 0; opt->sz = 0; } diff --git a/libr/bin/format/elf/elf.c b/libr/bin/format/elf/elf.c index 7261f553cfe58..87ef5ba8cc8ff 100644 --- a/libr/bin/format/elf/elf.c +++ b/libr/bin/format/elf/elf.c @@ -490,8 +490,9 @@ static Elf_(Phdr) *get_dynamic_segment(ELFOBJ *eo) { if (p->p_type != PT_DYNAMIC) { continue; } - if (p->p_filesz > eo->size || p->p_offset > eo->size - || p->p_offset + sizeof (Elf_(Dyn)) > eo->size) { + // init_dynamic_section bounds a memory image through v2p + if (!eo->inmem && (p->p_filesz > eo->size || p->p_offset > eo->size + || p->p_offset + sizeof (Elf_(Dyn)) > eo->size)) { return NULL; } return p; @@ -547,6 +548,36 @@ static inline size_t get_maximum_number_of_dynamic_entries(ut64 dyn_size) { return dyn_size / sizeof (Elf_(Dyn)); } +static bool backing_at(ELFOBJ *eo, ut64 vaddr, ut64 *off, ut64 *left); + +// glibc rewrites these in place to runtime addresses in a loaded image +static inline bool is_loader_rebased(ut64 tag) { + switch (tag) { + case DT_HASH: + case DT_PLTGOT: + case DT_STRTAB: + case DT_SYMTAB: + case DT_RELA: + case DT_REL: + case DT_JMPREL: + case DT_RELR: + case DT_VERSYM: + case DT_GNU_HASH: + return true; + } + return false; +} + +static ut64 dyn_link_ptr(ELFOBJ *eo, ut64 tag, ut64 ptr) { + if (!eo->inmem || eo->user_baddr == UT64_MAX || !is_loader_rebased (tag)) { + return ptr; + } + const ut64 bias = eo->user_baddr - eo->memory_base; + ut64 off, left; + return (bias && !backing_at (eo, ptr, &off, &left) && backing_at (eo, ptr - bias, &off, &left)) + ? ptr - bias: ptr; +} + static bool fill_dynamic_entry(ELFOBJ *eo, ut64 entry_offset, Elf_(Dyn) *d) { ut8 sdyn[sizeof (Elf_(Dyn))] = {0}; int len = r_buf_read_at (eo->b, entry_offset, sdyn, sizeof (sdyn)); @@ -555,7 +586,8 @@ static bool fill_dynamic_entry(ELFOBJ *eo, ut64 entry_offset, Elf_(Dyn) *d) { } int j = 0; // required because its used in a macro d->d_tag = R_BIN_ELF_READWORD (sdyn, j); - d->d_un.d_ptr = R_BIN_ELF_READWORD (sdyn, j); + const ut64 value = R_BIN_ELF_READWORD (sdyn, j); + d->d_un.d_ptr = dyn_link_ptr (eo, d->d_tag, value); return true; } @@ -716,20 +748,32 @@ static void fill_dynamic_entries(ELFOBJ *eo, ut64 loaded_offset, ut64 dyn_size) } } -// the file-backed bytes of the PT_LOAD holding vaddr, from vaddr on -static ut64 loaded_bytes_at(ELFOBJ *eo, ut64 vaddr) { +static bool segment_backing(ELFOBJ *eo, const Elf_(Phdr) *p, ut64 vaddr, ut64 *off, ut64 *left) { + if (p->p_type != PT_LOAD || vaddr < p->p_vaddr) { + return false; + } + const ut64 delta = vaddr - p->p_vaddr; + const ut64 span = eo->inmem? p->p_memsz: p->p_filesz; + if (span > UT64_MAX - p->p_vaddr || delta >= span || (eo->inmem && p->p_vaddr < eo->memory_base)) { + return false; + } + const ut64 base = eo->inmem? p->p_vaddr - eo->memory_base: p->p_offset; + if (base > eo->size || delta >= eo->size - base) { + return false; + } + *off = base + delta; + *left = R_MIN (span - delta, eo->size - *off); + return true; +} + +static bool backing_at(ELFOBJ *eo, ut64 vaddr, ut64 *off, ut64 *left) { size_t i; for (i = 0; i < eo->phnum; i++) { - const Elf_(Phdr) *p = &eo->phdr[i]; - if (p->p_type != PT_LOAD || vaddr < p->p_vaddr || vaddr - p->p_vaddr >= p->p_filesz - || p->p_filesz > UT64_MAX - p->p_vaddr || p->p_offset >= eo->size) { - continue; + if (segment_backing (eo, &eo->phdr[i], vaddr, off, left)) { + return true; } - const ut64 end = R_MIN (p->p_filesz, eo->size - p->p_offset); - const ut64 at = vaddr - p->p_vaddr; - return at < end? end - at: 0; } - return 0; + return false; } static ut64 reloc_section_size_at(ELFOBJ *eo, ut64 vaddr, ut32 sh_type) { @@ -749,7 +793,8 @@ static Elf_(Xword) reloc_read_size(ELFOBJ *eo, ut64 vaddr, Elf_(Xword) size, ut3 if (vaddr == R_BIN_ELF_ADDR_MAX || !size) { return size; } - const ut64 loaded = loaded_bytes_at (eo, vaddr); + ut64 off, loaded = 0; + backing_at (eo, vaddr, &off, &loaded); if (size <= loaded) { return size; } @@ -777,7 +822,7 @@ static int init_dynamic_section(ELFOBJ *eo) { ut64 dyn_size = dyn_phdr->p_filesz; - if (!dyn_size || loaded_offset + dyn_size > eo->size) { + if (!dyn_size || loaded_offset > eo->size || dyn_size > eo->size - loaded_offset) { return false; } @@ -1704,6 +1749,21 @@ static void relro_insdb(ELFOBJ *eo) { /* Look down */ static void sdb_init_const(ELFOBJ *eo); +static bool init_memory_base(ELFOBJ *eo) { + const ut64 headers_end = eo->ehdr.e_phoff + ((ut64)eo->phnum * sizeof (Elf_(Phdr))); + size_t i; + for (i = 0; eo->phdr && i < eo->phnum; i++) { + const Elf_(Phdr) *p = &eo->phdr[i]; + if (p->p_type == PT_LOAD && !p->p_offset && p->p_filesz >= headers_end) { + eo->memory_base = p->p_vaddr; + // a loader never maps the section table + eo->ehdr.e_shnum = 0; + return true; + } + } + return false; +} + static bool elf_init(ELFOBJ *eo) { // eo is not an ELF if (!init_ehdr (eo)) { @@ -1715,6 +1775,12 @@ static bool elf_init(ELFOBJ *eo) { if (!init_phdr (eo) && !is_bin_etrel (eo)) { R_LOG_DEBUG ("Cannot initialize program headers"); } + if (eo->inmem && !init_memory_base (eo)) { + if (eo->ehdr.e_type == ET_EXEC || eo->ehdr.e_type == ET_DYN) { + R_LOG_WARN ("No PT_LOAD maps the ELF header, reading it as a file"); + } + eo->inmem = false; + } if (eo->ehdr.e_type != ET_CORE) { if (!init_shdr (eo)) { @@ -2373,6 +2439,10 @@ char *Elf_(intrp)(ELFOBJ *eo) { } ut64 addr = p->p_offset; + ut64 left; + if (eo->inmem && (!backing_at (eo, p->p_vaddr, &addr, &left) || left < p->p_filesz)) { + return NULL; + } int sz = p->p_filesz; sdb_num_set (eo->kv, "elf_header.intrp_addr", addr, 0); sdb_num_set (eo->kv, "elf_header.intrp_size", sz, 0); @@ -4474,7 +4544,7 @@ static bool parse_pt_dynamic(RBinFile *bf, RBinSection *ptr) { switch (entry.d_tag) { case DT_RELR: R_LOG_DEBUG ("RELR section found at 0x%08"PFMT64x, entry.d_un.d_ptr); - eo->dyn_info.dt_relr = entry.d_un.d_ptr; + eo->dyn_info.dt_relr = dyn_link_ptr (eo, entry.d_tag, entry.d_un.d_ptr); break; case DT_RELRSZ: R_LOG_DEBUG ("RELR section size: 0x%08"PFMT64x, entry.d_un.d_val); @@ -4667,6 +4737,16 @@ static bool _add_sections_from_phdr(RBinFile *bf, ELFOBJ *eo, bool *found_load) ptr->vsize = phdr[i].p_memsz; ptr->paddr = phdr[i].p_offset; ptr->vaddr = phdr[i].p_vaddr; + if (eo->inmem) { + ut64 left = 0; + const bool backed = phdr[i].p_type == PT_LOAD + ? segment_backing (eo, &phdr[i], ptr->vaddr, &ptr->paddr, &left) + : backing_at (eo, ptr->vaddr, &ptr->paddr, &left); + if (!backed && 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); + } ptr->perm = phdr[i].p_flags; // perm are rwx like x=1, w=2, r=4, aka no need to convert from r2's R_PERM ptr->is_segment = true; @@ -5327,7 +5407,7 @@ static RVecRBinElfSymbol *parse_gnu_debugdata(ELFOBJ *eo, size_t *ret_size) { ut8 *odata = r_sys_unxz (data, size, &osize); if (odata) { RBuffer *newelf = r_buf_new_with_pointers (odata, osize, false); - ELFOBJ* newobj = Elf_(new_buf) (newelf, eo->user_baddr, false); + ELFOBJ* newobj = Elf_(new_buf) (newelf, eo->user_baddr, false, false); RVecRBinElfSymbol *symbols = NULL; if (newobj) { newobj->limit = eo->limit; @@ -5974,7 +6054,7 @@ void Elf_(free)(ELFOBJ* eo) { free (eo); } -ELFOBJ* Elf_(new_buf)(RBuffer *buf, ut64 baddr, bool verbose) { +ELFOBJ* Elf_(new_buf)(RBuffer *buf, ut64 baddr, bool verbose, bool inmem) { ELFOBJ *eo = R_NEW0 (ELFOBJ); RVecRBinTrycatch_init (&eo->trycatch); eo->kv = sdb_new0 (); @@ -5982,6 +6062,7 @@ ELFOBJ* Elf_(new_buf)(RBuffer *buf, ut64 baddr, bool verbose) { eo->verbose = verbose; eo->b = r_ref (buf); eo->user_baddr = baddr; + eo->inmem = inmem; if (!elf_init (eo)) { Elf_(free) (eo); return NULL; @@ -5999,6 +6080,10 @@ static int is_in_vphdr(Elf_(Phdr) *p, ut64 addr) { ut64 Elf_(p2v)(ELFOBJ *eo, ut64 paddr) { R_RETURN_VAL_IF_FAIL (eo, UT64_MAX); + if (eo->inmem) { + const ut64 vaddr = paddr + eo->memory_base; + return Elf_(v2p) (eo, vaddr) == paddr? vaddr: UT64_MAX; + } if (eo->phdr) { // When multiple PT_LOAD segments overlap in file-offset space (UPX) // p_vaddr match so p2v/v2p round-trip at the real code region. @@ -6036,6 +6121,10 @@ ut64 Elf_(p2v)(ELFOBJ *eo, ut64 paddr) { ut64 Elf_(v2p)(ELFOBJ *eo, ut64 vaddr) { R_RETURN_VAL_IF_FAIL (eo, UT64_MAX); + if (eo->inmem) { + ut64 off, left; + return backing_at (eo, vaddr, &off, &left)? off: UT64_MAX; + } if (eo->phdr) { size_t i; for (i = 0; i < eo->phnum; i++) { diff --git a/libr/bin/format/elf/elf.h b/libr/bin/format/elf/elf.h index 5efe78b00686b..f6d771459728e 100644 --- a/libr/bin/format/elf/elf.h +++ b/libr/bin/format/elf/elf.h @@ -173,9 +173,11 @@ struct Elf_(obj_t) { ut64 size; ut64 baddr; ut64 user_baddr; + ut64 memory_base; ut64 boffset; int endian; bool verbose; + bool inmem; bool load_unnamed; bool has_nobtcfi; bool has_nx; @@ -260,7 +262,7 @@ const RVecRBinElfField *Elf_(load_fields)(struct Elf_(obj_t) *bin); char *Elf_(get_rpath)(struct Elf_(obj_t) *bin); struct Elf_(obj_t)* Elf_(new)(const char* file, bool verbose); -struct Elf_(obj_t)* Elf_(new_buf)(RBuffer *buf, ut64 user_baddr, bool verbose); +struct Elf_(obj_t)* Elf_(new_buf)(RBuffer *buf, ut64 user_baddr, bool verbose, bool inmem); void Elf_(free)(struct Elf_(obj_t)* bin); ut64 Elf_(resize_section)(RBinFile *bf, const char *name, ut64 size); diff --git a/libr/bin/p/bin_elf.inc.c b/libr/bin/p/bin_elf.inc.c index 4911402aee04c..ba1858dc38062 100644 --- a/libr/bin/p/bin_elf.inc.c +++ b/libr/bin/p/bin_elf.inc.c @@ -40,8 +40,9 @@ static Sdb* get_sdb(RBinFile *bf) { } static bool load(RBinFile *bf, RBuffer *buf, ut64 loadaddr) { - ELFOBJ *res = Elf_(new_buf) (buf, bf->user_baddr, bf->rbin->options.verbose); + ELFOBJ *res = Elf_(new_buf) (buf, bf->user_baddr, bf->rbin->options.verbose, bf->inmem); if (res) { + bf->inmem = res->inmem; // so a reload keeps the layout it settled on // sdb_ns_set (sdb, "info", res->kv); res->limit = bf->rbin->options.limit; res->load_unnamed = bf->rbin->options.load_unnamed; diff --git a/libr/bin/p/bin_mdt.c b/libr/bin/p/bin_mdt.c index 567e3151072e7..039d92f9edb6b 100644 --- a/libr/bin/p/bin_mdt.c +++ b/libr/bin/p/bin_mdt.c @@ -185,7 +185,7 @@ static RBinMdtPart *load_segment_part(ELFOBJ *header, int idx) { magic[2] == ELFMAG2 && magic[3] == ELFMAG3) { part->format = R_BIN_MDT_PART_ELF; // Load nested ELF - part->obj.elf = Elf_(new_buf) (vfile_buffer, 0, false); + part->obj.elf = Elf_(new_buf) (vfile_buffer, 0, false, false); // symbols are read from the nested ELF in symbols_vec } else if ((segment->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_SIGNATURE) { part->format = R_BIN_MDT_PART_MBN; @@ -231,7 +231,7 @@ static bool load(RBinFile *bf, RBuffer *b, ut64 loadaddr) { mdt->name = strdup (bf->file ? r_file_basename (bf->file) : "firmware"); // Load header ELF - mdt->header = Elf_(new_buf) (b, 0, false); + mdt->header = Elf_(new_buf) (b, 0, false, false); if (!mdt->header) { R_LOG_ERROR ("Failed to parse .mdt ELF header"); goto error; diff --git a/libr/core/cconfig.c b/libr/core/cconfig.c index be19f6c7cc683..9ee0d9d77f7b5 100644 --- a/libr/core/cconfig.c +++ b/libr/core/cconfig.c @@ -4417,6 +4417,7 @@ R_API int r_core_config_init(RCore *core) { 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.inmem", "false", "read oba input as a mapped memory image"); SETICB ("bin.maxsymlen", 0, &cb_binmaxsymlen, "maximum length for symbol names"); SETICB ("bin.str.min", 0, &cb_binminstr, "minimum string length for r_bin"); SETICB ("bin.str.max", 0, &cb_binmaxstr, "maximum string length for r_bin"); diff --git a/libr/core/cmd_open.inc.c b/libr/core/cmd_open.inc.c index 2fa0f7ed6302a..fbafa4a5662f9 100644 --- a/libr/core/cmd_open.inc.c +++ b/libr/core/cmd_open.inc.c @@ -95,7 +95,7 @@ static RCoreHelpMessage help_msg_omb = { static RCoreHelpMessage help_msg_oba = { "Usage: oba", "[addr] ([filename])", "Load bininfo and update flags", "oba", " [addr]", "open bin info from the given address", - "oba", " [addr] [baddr]", "open file and load bin info at given address", + "oba", " [addr] [baddr]", "open file and load bin info at given address (bin.inmem reads it as a memory image)", "oba", " [addr] [/abs/filename|base64:filename]", "open file and load bin info at given address", NULL }; @@ -111,7 +111,7 @@ static RCoreHelpMessage help_msg_ob = { "ob--", "", "delete the last binfile", "ob.", " ([addr])", "show bfid at current address", "ob=", "", "show ascii art table having the list of open files", - "oba", " [addr] [baddr]", "open file and load bin info at given address", + "oba", " [addr] [baddr]", "open file and load bin info at given address (bin.inmem reads it as a memory image)", "oba", " [addr] [filename]", "open file and load bin info at given address", "oba", " [addr]", "open bin info from the given address", "obf", " ([file|base64:file])", "load bininfo for current file (useful for r2 -n)", @@ -301,34 +301,19 @@ static void cmd_oba(RCore *core, const char *input) { R_LOG_ERROR ("Cannot oba open '%s'", r_str_trim_head_ro (filename)); } else if (encoded_filename) { R_LOG_ERROR ("Cannot oba open decoded filename"); - } else if (R_STR_ISNOTEMPTY (filename)) { - ut64 baddr = r_num_math (core->num, filename); + } else { ut64 addr = r_num_math (core->num, input + 2); // mapaddr + ut64 baddr = R_STR_ISNOTEMPTY (filename)? r_num_math (core->num, filename): addr; int fd = r_io_fd_get_current (core->io); RIODesc *desc = r_io_desc_get (core->io, fd); if (desc) { RBinFileOptions opt; r_bin_file_options_init (&opt, desc->fd, baddr, addr, rawstr); + opt.inmem = r_config_get_b (core->config, "bin.inmem"); opt.sz = oba_memsize (core->dbg, addr); if (!opt.sz) { - opt.sz = 1024 * 1024; - } - r_bin_open_io (core->bin, &opt); - oba_finish_load (core); - r_core_cmd0 (core, ".is*"); - } else { - R_LOG_ERROR ("No file to load bin from?"); - } - } else { - ut64 addr = r_num_math (core->num, input + 2); - int fd = r_io_fd_get_current (core->io); - RIODesc *desc = r_io_desc_get (core->io, fd); - if (desc) { - RBinFileOptions opt; - r_bin_file_options_init (&opt, desc->fd, addr, addr, rawstr); - opt.sz = oba_memsize (core->dbg, addr); - if (!opt.sz) { - opt.sz = 1024 * 1024; + const ut64 desc_size = r_io_desc_size (desc); + opt.sz = (opt.inmem && addr < desc_size)? desc_size - addr: 1024 * 1024; } r_bin_open_io (core->bin, &opt); oba_finish_load (core); diff --git a/libr/include/r_bin.h b/libr/include/r_bin.h index 91f4d16058c18..6b36361d67fa7 100644 --- a/libr/include/r_bin.h +++ b/libr/include/r_bin.h @@ -534,6 +534,7 @@ typedef struct r_bin_file_options_t { int rawstr; bool nofuncstarts; bool skip_symbols; // skip symbol loading (e.g., for companion debug files) + bool inmem; const char *filename; } RBinFileOptions; @@ -594,6 +595,7 @@ typedef struct r_bin_file_t { } dwarf_metadata; struct r_bin_t *rbin; int string_count; + bool inmem; RArena *arena; } RBinFile; diff --git a/test/db/formats/elf/reloc b/test/db/formats/elf/reloc index ef4cc6da2c1ad..84e2208e2c76d 100644 --- a/test/db/formats/elf/reloc +++ b/test/db/formats/elf/reloc @@ -1800,3 +1800,329 @@ EXPECT=< 0x0100b8a0 DATA:r-- EOF RUN + +NAME=ELF: a memory-layout image lists the relocs of its file +FILE=malloc://16440 +ARGS=-e bin.inmem=true +CMDS=<