diff options
author | Stephen Webb <swebb@blackberry.com> | 2022-07-23 22:30:23 -0400 |
---|---|---|
committer | Stephen M. Webb <stephen.webb@bregmasoft.ca> | 2022-07-26 21:44:26 -0400 |
commit | e96db56e586ad7a1c8d27593382b365f775a398f (patch) | |
tree | ab6bcc10a8504204d1f52b379d2ddc7cb1efb1ff /src/coredump/_UCD_get_proc_name.c | |
parent | 10e093db21b73e98ba8485b4b0696d330bf8dfdf (diff) | |
download | libunwind-e96db56e586ad7a1c8d27593382b365f775a398f.tar.gz |
Stop assuming .text and .eh_frame segment
The coredump remote was architected on the assumption that the .text and
.eh_frame sections were mapped onto the same segment, and that that segment was
always the first PT_LOAD segment in an ELF file. Well, that was never a valid
assumption, and moderns releases of various toolchains have started splitting
the PT_LOAD segments for security reasons.
This change implements an M:N mapping of PT_LOAD segments in a coredump file to
backing ELF files and calculates and adjusts offsets appropriately. Because the
backing files get mapped in a lot of file I/O operations have been replaced with
simple memory reads. Once a backing file is memory mapped is stays mapped until
the address space is destroyed.
The ucd_*.[ch] files contain only functions that should not be exposed through
the public API so they;re not mangled using the UB naming schedule because I
just bring myself to write code with undefined behaviour.
Reformatted some of the changed files using `astyle --style=gnu` for internal
consistency withing the file.
Fixes #363
Diffstat (limited to 'src/coredump/_UCD_get_proc_name.c')
-rw-r--r-- | src/coredump/_UCD_get_proc_name.c | 79 |
1 files changed, 64 insertions, 15 deletions
diff --git a/src/coredump/_UCD_get_proc_name.c b/src/coredump/_UCD_get_proc_name.c index cd5ee892..aeec3c9b 100644 --- a/src/coredump/_UCD_get_proc_name.c +++ b/src/coredump/_UCD_get_proc_name.c @@ -24,6 +24,57 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "_UCD_lib.h" #include "_UCD_internal.h" +#if defined(HAVE_ELF_H) +# include <elf.h> +#elif defined(HAVE_SYS_ELF_H) +# include <sys/elf.h> +#endif + + +static off_t +_get_text_offset (uint8_t *image) +{ + off_t offset = 0; + typedef union + { + Elf32_Ehdr h32; + Elf64_Ehdr h64; + } elf_header_t; + + elf_header_t *elf_header = (elf_header_t *)image; + bool _64bits = (elf_header->h32.e_ident[EI_CLASS] == ELFCLASS64); + off_t e_phofs = _64bits ? elf_header->h64.e_phoff : elf_header->h32.e_phoff; + unsigned e_phnum = _64bits ? elf_header->h64.e_phnum : elf_header->h32.e_phnum; + + for (unsigned i = 0; i < e_phnum; ++i) + { + if (_64bits) + { + Elf64_Phdr *phdr = (Elf64_Phdr *) (image + e_phofs); + + if (phdr[i].p_type == PT_LOAD && (phdr[i].p_flags & PF_X) == PF_X) + { + offset = phdr[i].p_offset; + break; + } + } + + else + { + Elf32_Phdr *phdr = (Elf32_Phdr *) (image + e_phofs); + + if ((phdr[i].p_flags & PF_X) == PF_X) + { + offset = phdr[i].p_offset; + break; + } + } + } + + Debug (4, "returning offset %ld\n", (long)offset); + return offset; +} + /* Find the ELF image that contains IP and return the "closest" procedure name, if there is one. With some caching, this could be @@ -31,30 +82,29 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ sensitive to the performance of this routine, why bother... */ static int elf_w (CD_get_proc_name) (struct UCD_info *ui, unw_addr_space_t as, unw_word_t ip, - char *buf, size_t buf_len, unw_word_t *offp) + char *buf, size_t buf_len, unw_word_t *offp) { unsigned long segbase, mapoff; int ret; - - /* We're about to map an elf image. If there is an elf image currently mapped, - then make sure to unmap it. */ - invalidate_edi(&ui->edi); - + /* We're about to map an elf image. + The call will unmap memory it doesn't own, so just null it out and avoid + that. */ + ui->edi.ei.image = NULL; + invalidate_edi (&ui->edi); /* Used to be tdep_get_elf_image() in ptrace unwinding code */ - coredump_phdr_t *cphdr = _UCD_get_elf_image(ui, ip); + coredump_phdr_t *cphdr = _UCD_get_elf_image (ui, ip); + if (!cphdr) { - Debug(1, "returns error: _UCD_get_elf_image failed\n"); + Debug (1, "returns error: _UCD_get_elf_image failed\n"); return -UNW_ENOINFO; } - /* segbase: where it is mapped in virtual memory */ - /* mapoff: offset in the file */ - segbase = cphdr->p_vaddr; - /*mapoff = phdr->p_offset; WRONG! phdr->p_offset is the offset in COREDUMP file */ - mapoff = 0; + segbase = 0; /* everything is relative to the beginning of the ELF file */ + mapoff = 0; + /* Adjust IP to be relative to start of the .text section of the ELF file */ + ip = ip - cphdr->p_vaddr + _get_text_offset (ui->edi.ei.image); ret = elf_w (get_proc_name_in_image) (as, &ui->edi.ei, segbase, mapoff, ip, buf, buf_len, offp); - return ret; } @@ -63,7 +113,6 @@ _UCD_get_proc_name (unw_addr_space_t as, unw_word_t ip, char *buf, size_t buf_len, unw_word_t *offp, void *arg) { struct UCD_info *ui = arg; - #if UNW_ELF_CLASS == UNW_ELFCLASS64 return _Uelf64_CD_get_proc_name (ui, as, ip, buf, buf_len, offp); #elif UNW_ELF_CLASS == UNW_ELFCLASS32 |