diff options
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 |