int internal_function __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf) { size_t shstrndx = SHN_UNDEF; int result = 0; Elf_Scn *scn = elf_nextscn (elf, NULL); if (scn == NULL) { /* No sections, have to look for phdrs. */ GElf_Ehdr ehdr_mem; GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); size_t phnum; if (unlikely (ehdr == NULL) || unlikely (elf_getphdrnum (elf, &phnum) != 0)) { __libdwfl_seterrno (DWFL_E_LIBELF); return -1; } for (size_t i = 0; result == 0 && i < phnum; ++i) { GElf_Phdr phdr_mem; GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); if (likely (phdr != NULL) && phdr->p_type == PT_NOTE) result = check_notes (mod, set, elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz, ELF_T_NHDR), phdr->p_vaddr + mod->main.bias); } } else do { GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); if (likely (shdr != NULL) && shdr->sh_type == SHT_NOTE) { /* Determine the right sh_addr in this module. */ GElf_Addr vaddr = 0; if (!(shdr->sh_flags & SHF_ALLOC)) vaddr = NO_VADDR; else if (mod->e_type != ET_REL) vaddr = shdr->sh_addr + mod->main.bias; else if (__libdwfl_relocate_value (mod, elf, &shstrndx, elf_ndxscn (scn), &vaddr)) vaddr = NO_VADDR; result = check_notes (mod, set, elf_getdata (scn, NULL), vaddr); } } while (result == 0 && (scn = elf_nextscn (elf, scn)) != NULL); return result; }
uint8_t *dump_program_data(Elf *elf_object, int *size) { uint8_t *buffer = NULL; Elf_Data *data = NULL; size_t phdr_num; size_t max_paddr = 0; GElf_Phdr phdr; *size = 0; int ret = elf_getphdrnum(elf_object, &phdr_num); if (ret) { printf("Problem during ELF parsing\n"); return NULL; } if (phdr_num == 0) return NULL; for (int i = 0; i < phdr_num; i++) { if (gelf_getphdr(elf_object, i, &phdr) != &phdr) { printf("Problem during ELF parsing\n"); return NULL; } printf("Program header %d: addr 0x%08X,", i, (unsigned int)phdr.p_paddr); printf(" size 0x%08X\n", (unsigned int)phdr.p_filesz); if (phdr.p_paddr + phdr.p_filesz >= max_paddr) { max_paddr = phdr.p_paddr + phdr.p_filesz; buffer = realloc(buffer, max_paddr); } data = elf_getdata_rawchunk(elf_object, phdr.p_offset, phdr.p_filesz, ELF_T_BYTE); if (data != NULL) memcpy(buffer + phdr.p_paddr, data->d_buf, data->d_size); else { printf("Couldn't load program data chunk\n"); return NULL; } } *size = max_paddr; return buffer; }
/** Get the build-id (NT_GNU_BUILD_ID) from the ELF file * * This build-id may is used to locate an external debug (DWARF) file * for this ELF file. * * @param elf libelf handle for an ELF file * @return build-id for this ELF file (or an empty vector if none is found) */ static std::vector<char> get_build_id(Elf* elf) { #ifdef __linux // Summary: the GNU build ID is stored in a ("GNU, NT_GNU_BUILD_ID) note // found in a PT_NOTE entry in the program header table. size_t phnum; if (elf_getphdrnum (elf, &phnum) != 0) xbt_die("Could not read program headers"); // Iterate over the program headers and find the PT_NOTE ones: for (size_t i = 0; i < phnum; ++i) { GElf_Phdr phdr_temp; GElf_Phdr *phdr = gelf_getphdr(elf, i, &phdr_temp); if (phdr->p_type != PT_NOTE) continue; Elf_Data* data = elf_getdata_rawchunk(elf, phdr->p_offset, phdr->p_filesz, ELF_T_NHDR); // Iterate over the notes and find the NT_GNU_BUILD_ID one: size_t pos = 0; while (pos < data->d_size) { GElf_Nhdr nhdr; // Location of the name within Elf_Data: size_t name_pos; size_t desc_pos; pos = gelf_getnote(data, pos, &nhdr, &name_pos, &desc_pos); // A build ID note is identified by the pair ("GNU", NT_GNU_BUILD_ID) // (a namespace and a type within this namespace): if (nhdr.n_type == NT_GNU_BUILD_ID && nhdr.n_namesz == sizeof("GNU") && memcmp((char*) data->d_buf + name_pos, "GNU", sizeof("GNU")) == 0) { XBT_DEBUG("Found GNU/NT_GNU_BUILD_ID note"); char* start = (char*) data->d_buf + desc_pos; char* end = (char*) start + nhdr.n_descsz; return std::vector<char>(start, end); } } } #endif return std::vector<char>(); }
short get_signal_number(Elf *e, const char *elf_file) { const char NOTE_CORE[] = "CORE"; size_t nphdr; if (elf_getphdrnum(e, &nphdr) != 0) { warn_elf("elf_getphdrnum"); return 0; } /* Go through phdrs, look for prstatus note */ int i; for (i = 0; i < nphdr; i++) { GElf_Phdr phdr; if (gelf_getphdr(e, i, &phdr) != &phdr) { warn_elf("gelf_getphdr"); continue; } if (phdr.p_type != PT_NOTE) { continue; } Elf_Data *data, *name_data, *desc_data; GElf_Nhdr nhdr; size_t note_offset = 0; size_t name_offset, desc_offset; /* Elf_Data buffers are freed when elf_end is called. */ data = elf_getdata_rawchunk(e, phdr.p_offset, phdr.p_filesz, ELF_T_NHDR); if (!data) { warn_elf("elf_getdata_rawchunk"); continue; } while ((note_offset = gelf_getnote(data, note_offset, &nhdr, &name_offset, &desc_offset)) != 0) { /* printf("Note: type:%x name:%x+%d desc:%x+%d\n", nhdr.n_type, name_offset, nhdr.n_namesz, desc_offset, nhdr.n_descsz); */ if (nhdr.n_type != NT_PRSTATUS || nhdr.n_namesz < sizeof(NOTE_CORE)) continue; name_data = elf_getdata_rawchunk(e, phdr.p_offset + name_offset, nhdr.n_namesz, ELF_T_BYTE); desc_data = elf_getdata_rawchunk(e, phdr.p_offset + desc_offset, nhdr.n_descsz, ELF_T_BYTE); if (!(name_data && desc_data)) continue; if (name_data->d_size < sizeof(NOTE_CORE)) continue; if (strcmp(NOTE_CORE, name_data->d_buf)) continue; if (desc_data->d_size != sizeof(struct elf_prstatus)) { warn("PRSTATUS core note of size %zu found, expected size: %zu", desc_data->d_size, sizeof(struct elf_prstatus)); continue; } struct elf_prstatus *prstatus = (struct elf_prstatus*)desc_data->d_buf; short signal = prstatus->pr_cursig; if (signal) return signal; } } return 0; }
/* Try to find a dynamic symbol table via phdrs. */ static void find_dynsym (Dwfl_Module *mod) { GElf_Ehdr ehdr_mem; GElf_Ehdr *ehdr = gelf_getehdr (mod->main.elf, &ehdr_mem); for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i) { GElf_Phdr phdr_mem; GElf_Phdr *phdr = gelf_getphdr (mod->main.elf, i, &phdr_mem); if (phdr == NULL) break; if (phdr->p_type == PT_DYNAMIC) { /* Examine the dynamic section for the pointers we need. */ Elf_Data *data = elf_getdata_rawchunk (mod->main.elf, phdr->p_offset, phdr->p_filesz, ELF_T_DYN); if (data == NULL) continue; enum { i_symtab, i_strtab, i_hash, i_gnu_hash, i_max }; GElf_Addr addrs[i_max] = { 0, }; GElf_Xword strsz = 0; size_t n = data->d_size / gelf_fsize (mod->main.elf, ELF_T_DYN, 1, EV_CURRENT); for (size_t j = 0; j < n; ++j) { GElf_Dyn dyn_mem; GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem); if (dyn != NULL) switch (dyn->d_tag) { case DT_SYMTAB: addrs[i_symtab] = dyn->d_un.d_ptr; continue; case DT_HASH: addrs[i_hash] = dyn->d_un.d_ptr; continue; case DT_GNU_HASH: addrs[i_gnu_hash] = dyn->d_un.d_ptr; continue; case DT_STRTAB: addrs[i_strtab] = dyn->d_un.d_ptr; continue; case DT_STRSZ: strsz = dyn->d_un.d_val; continue; default: continue; case DT_NULL: break; } break; } /* Translate pointers into file offsets. */ GElf_Off offs[i_max] = { 0, }; find_offsets (mod->main.elf, ehdr, i_max, addrs, offs); /* Figure out the size of the symbol table. */ if (offs[i_hash] != 0) { /* In the original format, .hash says the size of .dynsym. */ size_t entsz = SH_ENTSIZE_HASH (ehdr); data = elf_getdata_rawchunk (mod->main.elf, offs[i_hash] + entsz, entsz, entsz == 4 ? ELF_T_WORD : ELF_T_XWORD); if (data != NULL) mod->syments = (entsz == 4 ? *(const GElf_Word *) data->d_buf : *(const GElf_Xword *) data->d_buf); } if (offs[i_gnu_hash] != 0 && mod->syments == 0) { /* In the new format, we can derive it with some work. */ const struct { Elf32_Word nbuckets; Elf32_Word symndx; Elf32_Word maskwords; Elf32_Word shift2; } *header; data = elf_getdata_rawchunk (mod->main.elf, offs[i_gnu_hash], sizeof *header, ELF_T_WORD); if (data != NULL) { header = data->d_buf; Elf32_Word nbuckets = header->nbuckets; Elf32_Word symndx = header->symndx; GElf_Off buckets_at = (offs[i_gnu_hash] + sizeof *header + (gelf_getclass (mod->main.elf) * sizeof (Elf32_Word) * header->maskwords)); data = elf_getdata_rawchunk (mod->main.elf, buckets_at, nbuckets * sizeof (Elf32_Word), ELF_T_WORD); if (data != NULL && symndx < nbuckets) { const Elf32_Word *const buckets = data->d_buf; Elf32_Word maxndx = symndx; for (Elf32_Word bucket = 0; bucket < nbuckets; ++bucket) if (buckets[bucket] > maxndx) maxndx = buckets[bucket]; GElf_Off hasharr_at = (buckets_at + nbuckets * sizeof (Elf32_Word)); hasharr_at += (maxndx - symndx) * sizeof (Elf32_Word); do { data = elf_getdata_rawchunk (mod->main.elf, hasharr_at, sizeof (Elf32_Word), ELF_T_WORD); if (data != NULL && (*(const Elf32_Word *) data->d_buf & 1u)) { mod->syments = maxndx + 1; break; } ++maxndx; hasharr_at += sizeof (Elf32_Word); } while (data != NULL); } } } if (offs[i_strtab] > offs[i_symtab] && mod->syments == 0) mod->syments = ((offs[i_strtab] - offs[i_symtab]) / gelf_fsize (mod->main.elf, ELF_T_SYM, 1, EV_CURRENT)); if (mod->syments > 0) { mod->symdata = elf_getdata_rawchunk (mod->main.elf, offs[i_symtab], gelf_fsize (mod->main.elf, ELF_T_SYM, mod->syments, EV_CURRENT), ELF_T_SYM); if (mod->symdata != NULL) { mod->symstrdata = elf_getdata_rawchunk (mod->main.elf, offs[i_strtab], strsz, ELF_T_BYTE); if (mod->symstrdata == NULL) mod->symdata = NULL; } if (mod->symdata == NULL) mod->symerr = DWFL_E (LIBELF, elf_errno ()); else { mod->symfile = &mod->main; mod->symerr = DWFL_E_NOERROR; } return; } } } }
static const uint8_t * parse_eh_frame_hdr (const uint8_t *hdr, size_t hdr_size, GElf_Addr hdr_vaddr, const GElf_Ehdr *ehdr, GElf_Addr *eh_frame_vaddr, size_t *table_entries, uint8_t *table_encoding) { const uint8_t *h = hdr; if (*h++ != 1) /* version */ return (void *) -1l; uint8_t eh_frame_ptr_encoding = *h++; uint8_t fde_count_encoding = *h++; uint8_t fde_table_encoding = *h++; if (eh_frame_ptr_encoding == DW_EH_PE_omit) return (void *) -1l; /* Dummy used by read_encoded_value. */ Elf_Data_Scn dummy_cfi_hdr_data = { .d = { .d_buf = (void *) hdr, .d_size = hdr_size } }; Dwarf_CFI dummy_cfi = { .e_ident = ehdr->e_ident, .datarel = hdr_vaddr, .frame_vaddr = hdr_vaddr, .data = &dummy_cfi_hdr_data, }; if (unlikely (read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h, eh_frame_vaddr))) return (void *) -1l; if (fde_count_encoding != DW_EH_PE_omit) { Dwarf_Word fde_count; if (unlikely (read_encoded_value (&dummy_cfi, fde_count_encoding, &h, &fde_count))) return (void *) -1l; if (fde_count != 0 && (size_t) fde_count == fde_count && fde_table_encoding != DW_EH_PE_omit && (fde_table_encoding &~ DW_EH_PE_signed) != DW_EH_PE_uleb128) { *table_entries = fde_count; *table_encoding = fde_table_encoding; return h; } } return NULL; } static Dwarf_CFI * getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr) { if (unlikely (phdr->p_filesz < 4)) goto invalid; Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz, ELF_T_BYTE); if (data == NULL) { invalid_hdr: invalid: /* XXX might be read error or corrupt phdr */ __libdw_seterrno (DWARF_E_INVALID_CFI); return NULL; } Dwarf_Addr eh_frame_ptr; size_t search_table_entries; uint8_t search_table_encoding; const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz, phdr->p_vaddr, ehdr, &eh_frame_ptr, &search_table_entries, &search_table_encoding); if (search_table == (void *) -1l) goto invalid_hdr; Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset; Dwarf_Word eh_frame_size = 0; /* XXX we have no way without section headers to know the size of the .eh_frame data. Calculate the largest it might possibly be. This won't be wasteful if the file is already mmap'd, but if it isn't it might be quite excessive. */ size_t filesize; if (elf_rawfile (elf, &filesize) != NULL) eh_frame_size = filesize - eh_frame_offset; data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE); if (data == NULL) { __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */ return NULL; } Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr); if (cfi != NULL) { cfi->data = (Elf_Data_Scn *) data; if (search_table != NULL) { cfi->search_table = search_table; cfi->search_table_vaddr = phdr->p_vaddr; cfi->search_table_encoding = search_table_encoding; cfi->search_table_entries = search_table_entries; } } return cfi; } /* Search the phdrs for PT_GNU_EH_FRAME. */ static Dwarf_CFI * getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr) { size_t phnum; if (unlikely (elf_getphdrnum (elf, &phnum) != 0)) return NULL; for (size_t i = 0; i < phnum; ++i) { GElf_Phdr phdr_mem; GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); if (unlikely (phdr == NULL)) return NULL; if (phdr->p_type == PT_GNU_EH_FRAME) return getcfi_gnu_eh_frame (elf, ehdr, phdr); } __libdw_seterrno (DWARF_E_NO_DWARF); return NULL; }