void elfcreator_end(ElfCreator *ctor) { GElf_Phdr phdr_mem, *phdr; int m,n; for (m = 0; (phdr = gelf_getphdr(ctor->oldelf, m, &phdr_mem)) != NULL; m++) /* XXX this should check if an entry is needed */; gelf_newphdr(ctor->elf, m); elf_update(ctor->elf, ELF_C_NULL); update_dyn_cache(ctor); for (n = 0; n < m; n++) { /* XXX this should check if an entry is needed */ phdr = gelf_getphdr(ctor->oldelf, n, &phdr_mem); if (ctor->dynshdr && phdr->p_type == PT_DYNAMIC) phdr->p_offset = ctor->dynshdr->sh_offset; gelf_update_phdr(ctor->elf, n, phdr); } fixup_dynamic(ctor); clear(ctor, 0); free(ctor); }
/* * Search through PHT saving the beginning and ending segment offsets */ static int build_segment_table(Elf * elf, GElf_Ehdr * ehdr) { unsigned int i; if ((b_e_seg_table = (Seg_Table *) calloc(ehdr->e_phnum, sizeof (Seg_Table))) == NULL) { error_message(MALLOC_ERROR, PLAIN_ERROR, (char *)0, prog); mcs_exit(FAILURE); } for (i = 0; i < ehdr->e_phnum; i++) { GElf_Phdr ph; (void) gelf_getphdr(elf, i, &ph); /* * remember the note SEGMENTS index so that we can * re-set it's p_offset later if needed. */ if (ph.p_type == PT_NOTE) notesegndx = i; b_e_seg_table[i].p_offset = ph.p_offset; b_e_seg_table[i].p_memsz = ph.p_offset + ph.p_memsz; b_e_seg_table[i].p_filesz = ph.p_offset + ph.p_filesz; } return (SUCCESS); }
static GElf_Addr get_txtorigin(Elf *elf, char *filename) { GElf_Ehdr ehdr; GElf_Phdr phdr; GElf_Half ndx; GElf_Addr txt_origin = 0; bool first_load_seg = TRUE; if (gelf_getehdr(elf, &ehdr) == NULL) { (void) fprintf(stderr, "%s: can't read ELF header of %s\n", cmdname, filename); exit(ERR_ELF); } for (ndx = 0; ndx < ehdr.e_phnum; ndx++) { if (gelf_getphdr(elf, ndx, &phdr) == NULL) continue; if ((phdr.p_type == PT_LOAD) && !(phdr.p_flags & PF_W)) { if (first_load_seg || phdr.p_vaddr < txt_origin) txt_origin = phdr.p_vaddr; if (first_load_seg) first_load_seg = FALSE; } } return (txt_origin); }
/* Open libelf FILE->fd and compute the load base of ELF as loaded in MOD. When we return success, FILE->elf and FILE->bias are set up. */ static inline Dwfl_Error open_elf (Dwfl_Module *mod, struct dwfl_file *file) { if (file->elf == NULL) { /* If there was a pre-primed file name left that the callback left behind, try to open that file name. */ if (file->fd < 0 && file->name != NULL) file->fd = TEMP_FAILURE_RETRY (open64 (file->name, O_RDONLY)); if (file->fd < 0) return CBFAIL; file->elf = elf_begin (file->fd, ELF_C_READ_MMAP_PRIVATE, NULL); } if (unlikely (elf_kind (file->elf) != ELF_K_ELF)) { close (file->fd); file->fd = -1; return DWFL_E_BADELF; } GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (file->elf, &ehdr_mem); if (ehdr == NULL) { elf_error: close (file->fd); file->fd = -1; return DWFL_E (LIBELF, elf_errno ()); } /* The addresses in an ET_EXEC file are absolute. The lowest p_vaddr of the main file can differ from that of the debug file due to prelink. But that doesn't not change addresses that symbols, debuginfo, or sh_addr of any program sections refer to. */ file->bias = 0; if (mod->e_type != ET_EXEC) for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i) { GElf_Phdr ph_mem; GElf_Phdr *ph = gelf_getphdr (file->elf, i, &ph_mem); if (ph == NULL) goto elf_error; if (ph->p_type == PT_LOAD) { file->bias = ((mod->low_addr & -ph->p_align) - (ph->p_vaddr & -ph->p_align)); break; } } mod->e_type = ehdr->e_type; /* Relocatable Linux kernels are ET_EXEC but act like ET_DYN. */ if (mod->e_type == ET_EXEC && file->bias != 0) mod->e_type = ET_DYN; return DWFL_E_NOERROR; }
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; }
/** * parse initial ELF file and collect essential program header information */ int ElfInject::collectPHInfo() { size_t phCount; GElf_Phdr phdr; info("collect program header information"); if (elf_getphdrnum(bin.elf, &phCount) != 0) { error("cannot extract program header information"); return -1; } for (size_t i=0; i<phCount; i++) { if (gelf_getphdr(bin.elf, i, &phdr) != &phdr) { error("cannot extract program header nr %d", i); return -1; } switch (phdr.p_type) { case PT_PHDR: bin.phPhdrIndex = i; debug("° program header PHDR: %d", i); break; case PT_DYNAMIC: bin.phDynamicIndex = i; debug("° program header DYNAMIC: %d", i); break; case PT_LOAD: if (!phdr.p_offset) { /* text segment */ bin.phLoadTextIndex = i; debug("° program header LOAD (.text): %d", i); } else if (!(phdr.p_flags & PF_X)) { /* data segment */ bin.phLoadDataIndex = i; debug("° program header LOAD (.data): %d", i); } break; case PT_GNU_STACK: bin.phGnuStackIndex = i; debug("° program header GNU_STACK: %d", i); break; case PT_GNU_RELRO: bin.phGnuRelroIndex = i; debug("° program header GNU_RELRO: %d", i); break; default: /* do nothing */ break; } } return 0; }
void set_pt_flags(int fd, uint16_t pt_flags) { Elf *elf; GElf_Phdr phdr; size_t i, phnum; if(elf_version(EV_CURRENT) == EV_NONE) { PyErr_SetString(PaxError, "set_pt_flags: library out of date"); return; } if((elf = elf_begin(fd, ELF_C_RDWR_MMAP, NULL)) == NULL) { PyErr_SetString(PaxError, "set_pt_flags: elf_begin() failed"); return; } if(elf_kind(elf) != ELF_K_ELF) { elf_end(elf); PyErr_SetString(PaxError, "set_pt_flags: elf_kind() failed: this is not an elf file."); return; } elf_getphdrnum(elf, &phnum); for(i=0; i<phnum; i++) { if(gelf_getphdr(elf, i, &phdr) != &phdr) { elf_end(elf); PyErr_SetString(PaxError, "set_pt_flags: gelf_getphdr() failed"); return; } if(phdr.p_type == PT_PAX_FLAGS) { phdr.p_flags = pt_flags; if(!gelf_update_phdr(elf, i, &phdr)) { elf_end(elf); PyErr_SetString(PaxError, "set_pt_flags: gelf_update_phdr() failed"); return; } } } elf_end(elf); }
int elf_utils_shift_contents(Elf *e, int start_offset, int shift_amount) { GElf_Ehdr ehdr; Elf_Scn *scn; GElf_Shdr shdr; size_t segment_count = 0, segndx; GElf_Phdr phdr; int bottom_section_offset = 0; ELF_ASSERT(gelf_getehdr(e, &ehdr)); if (ehdr.e_shoff >= start_offset) { ehdr.e_shoff += shift_amount; ELF_ASSERT(gelf_update_ehdr(e, &ehdr)); } scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { ELF_ASSERT(gelf_getshdr(scn, &shdr)); if (shdr.sh_offset >= start_offset) { shdr.sh_offset += shift_amount; ELF_ASSERT(gelf_update_shdr(scn, &shdr)); } if (shdr.sh_offset + shdr.sh_size > bottom_section_offset) { bottom_section_offset = shdr.sh_offset + shdr.sh_size; } } if (bottom_section_offset > ehdr.e_shoff) { ELF_ASSERT(gelf_getehdr(e, &ehdr)); ehdr.e_shoff = bottom_section_offset; ELF_ASSERT(gelf_update_ehdr(e, &ehdr)); } /* A bug in libelf means that getphdrnum will report failure in a new file. * However, it will still set segment_count, so we'll use it. */ ELF_ASSERT((elf_getphdrnum(e, &segment_count), segment_count > 0)); for (segndx = 0; segndx < segment_count; segndx++) { ELF_ASSERT(gelf_getphdr(e, segndx, &phdr)); if (phdr.p_offset >= start_offset) { phdr.p_offset += shift_amount; ELF_ASSERT(gelf_update_phdr(e, segndx, &phdr)); } } return 1; failure: return 0; }
//get next loadable program header GElf_Phdr* ELF_next_loadable_phdr(){ GElf_Phdr *phdr = malloc(sizeof(GElf_Phdr)); int phdr_num = ehdr.e_phnum; while(phdr_index < phdr_num){ if(gelf_getphdr(e, phdr_index, phdr) != phdr){ phdr_index = 0; fprintf(stderr, "getphdr() failed: %s.", elf_errmsg(-1)); exit(1); } phdr_index ++; if(phdr->p_type == PT_LOAD) return phdr; } phdr_index = 0; return NULL; }
//get loadable segment num int ELF_loadable_seg_num(){ GElf_Phdr phdr; int result = 0; int phdr_num = ehdr.e_phnum; while(phdr_index < phdr_num){ if(gelf_getphdr(e, phdr_index, &phdr) != &phdr){ fprintf(stderr, "getphdr() failed: %s.", elf_errmsg(-1)); phdr_index = 0; exit(1); } phdr_index ++; if(phdr.p_type == PT_LOAD) result++; } phdr_index = 0; return result; }
static void processProgHeaders(elfInfo *ei, GElf_Ehdr *ehdr) { for (size_t i = 0; i < ehdr->e_phnum; i++) { GElf_Phdr mem; GElf_Phdr *phdr = gelf_getphdr(ei->elf, i, &mem); if (phdr && phdr->p_type == PT_INTERP) { size_t maxsize; char * filedata = elf_rawfile(ei->elf, &maxsize); if (filedata && phdr->p_offset < maxsize) { ei->interp = rstrdup(filedata + phdr->p_offset); break; } } } }
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>(); }
static int has_dt_debug(Elf *elf, GElf_Ehdr *ehdr) { int i; for (i = 0; i < ehdr->e_phnum; i++) { GElf_Phdr phdr; GElf_Shdr shdr; Elf_Scn *scn; Elf_Data *data; unsigned int j; if (gelf_getphdr(elf, i, &phdr) == NULL) continue; if (phdr.p_type != PT_DYNAMIC) continue; scn = gelf_offscn(elf, phdr.p_offset); if (gelf_getshdr(scn, &shdr) == NULL) continue; if (shdr.sh_type != SHT_DYNAMIC) continue; if ((data = elf_getdata(scn, NULL)) == NULL) continue; for (j = 0; j < shdr.sh_size / gelf_fsize(elf, ELF_T_DYN, 1, EV_CURRENT); j++) { GElf_Dyn dyn; if (gelf_getdyn(data, j, &dyn)) { if (dyn.d_tag == DT_DEBUG) return 1; } } } return 0; }
static int read_phdr(struct elf32_info *info, FILE *in) { int i; if (info->file_ehdr.e_phnum > MAX_PHDRS) { printc_err("elf32: too many program headers: %d\n", info->file_ehdr.e_phnum); return -1; } for (i = 0; i < info->file_ehdr.e_phnum; i++) { GElf_Phdr *phdr = &info->file_phdrs[i]; if (gelf_getphdr(info->elf, i, phdr) != phdr) { printc_err("elf32: can't read phdr %d: %s\n", i, elf_errmsg(elf_errno())); } } return 0; }
/* Translate addresses into file offsets. OFFS[*] start out zero and remain zero if unresolved. */ static void find_offsets (Elf *elf, GElf_Addr main_bias, size_t phnum, size_t n, GElf_Addr addrs[n], GElf_Off offs[n]) { size_t unsolved = n; for (size_t i = 0; i < phnum; ++i) { GElf_Phdr phdr_mem; GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); if (phdr != NULL && phdr->p_type == PT_LOAD && phdr->p_memsz > 0) for (size_t j = 0; j < n; ++j) if (offs[j] == 0 && addrs[j] >= phdr->p_vaddr + main_bias && addrs[j] - (phdr->p_vaddr + main_bias) < phdr->p_filesz) { offs[j] = addrs[j] - (phdr->p_vaddr + main_bias) + phdr->p_offset; if (--unsolved == 0) break; } } }
long ElfData::getElfMem() { long totalMem = 0; int i; size_t n; /* Causes problems on intel C++ */ //assert(elf_getphnum(e, &n) == 1); GElf_Ehdr elfhdr; assert(gelf_getehdr(e, &elfhdr) != 0); n = (size_t) elfhdr.e_phnum; for (i = 0; i < n; i++) { if (gelf_getphdr(e, i, &phdr) != &phdr) return totalMem; totalMem += (long) phdr.p_memsz; } return totalMem; }
static const value_t * elf_get_phdrs(const value_t *this_fn, parser_state_t *state, const char *filename, int binfd, Elf *e, GElf_Ehdr *ehdr, void *arg) { const value_t *resval = NULL; size_t n; if (elf_getphdrnum(e, &n) != 0) parser_report(state, "ELF file has unknown number of segments - %s\n", elf_errmsg(-1)); else { size_t i; dir_t *partdir = dir_vec_new(); for (i = 0; i < n; i++) { GElf_Phdr phdr; dir_t *pdir = dir_id_new(); if (gelf_getphdr(e, i, &phdr) != &phdr) parser_report(state, "ELF segment %d unretrievable - %s\n", i, elf_errmsg(-1)); else { dir_string_set(pdir,"type" , value_int_new(phdr.p_type)); dir_string_set(pdir,"offset" , value_int_new(phdr.p_offset)); dir_string_set(pdir,"vaddr" , value_int_new(phdr.p_vaddr)); dir_string_set(pdir,"paddr" , value_int_new(phdr.p_paddr)); dir_string_set(pdir,"filesz" , value_int_new(phdr.p_filesz)); dir_string_set(pdir,"memsz" , value_int_new(phdr.p_memsz)); dir_string_set(pdir,"flags" , value_int_new(phdr.p_flags)); dir_string_set(pdir,"align" , value_int_new(phdr.p_align)); dir_int_set(partdir, i, dir_value(pdir)); } } resval = dir_value(partdir); } return resval; }
int elf_utils_copy(Elf *dest, Elf *source) { GElf_Ehdr ehdr; Elf_Scn *dst_scn, *src_scn; GElf_Shdr shdr; Elf_Data *dst_data, *src_data; size_t segment_count, segndx; GElf_Phdr phdr; ELF_ASSERT(elf_flagelf(dest, ELF_C_SET, ELF_F_LAYOUT)); ELF_ASSERT(gelf_getehdr(source, &ehdr)); ELF_ASSERT(gelf_newehdr(dest, gelf_getclass(source))); ELF_ASSERT(gelf_update_ehdr(dest, &ehdr)); src_scn = NULL; while ((src_scn = elf_nextscn(source, src_scn)) != NULL) { ELF_ASSERT(gelf_getshdr(src_scn, &shdr)); ELF_ASSERT(dst_scn = elf_newscn(dest)); ELF_ASSERT(gelf_update_shdr(dst_scn, &shdr)); src_data = NULL; while ((src_data = elf_getdata(src_scn, src_data)) != NULL) { ELF_ASSERT(dst_data = elf_newdata(dst_scn)); memcpy(dst_data, src_data, sizeof(Elf_Data)); } } ELF_ASSERT(elf_getphdrnum(source, &segment_count) == 0); ELF_ASSERT(gelf_newphdr(dest, segment_count)); for (segndx = 0; segndx < segment_count; segndx++) { ELF_ASSERT(gelf_getphdr(source, segndx, &phdr)); ELF_ASSERT(gelf_update_phdr(dest, segndx, &phdr)); } return 1; failure: return 0; }
int find_rewritable_segment(elf_data_t *elf, inject_data_t *inject, char **err) { int ret; size_t i, n; /* Get number of program headers */ ret = elf_getphdrnum(elf->e, &n); if(ret != 0) { (*err) = "cannot find any program headers"; return -1; } /* Look for a rewritable program header */ for(i = 0; i < n; i++) { if(!gelf_getphdr(elf->e, i, &inject->phdr)) { (*err) = "failed to get program header"; return -1; } switch(inject->phdr.p_type) { case PT_NOTE: inject->pidx = i; return 0; case PT_NULL: case PT_LOAD: case PT_DYNAMIC: case PT_INTERP: case PT_SHLIB: case PT_PHDR: case PT_TLS: default: break; } } (*err) = "cannot find segment to rewrite"; return -1; }
static int init_phdrs() { Elf* e = g.e; size_t phdr_num = g.phdrnum; GElf_Phdr* buf = (GElf_Phdr*) malloc(phdr_num * sizeof(GElf_Phdr)); if (buf == NULL) { errx (EXIT_FAILURE , "malloc failed with GElf_Phdr*"); return -1; } int i; for (i=0; i<phdr_num; i++) { //GElf_Phdr* phdr = (GElf_Phdr*) malloc(sizeof(GElf_Phdr)); GElf_Phdr* phdr = buf + i; if (gelf_getphdr (e, i, phdr) != phdr) { errx(EXIT_FAILURE, " gelf_getphdr() failed : %s.", elf_errmsg(-1)); } } g.phdrs = buf; return 0; }
int elfu_eCheck(Elf *e) { int elfclass; GElf_Ehdr ehdr; GElf_Phdr *phdrs = NULL; GElf_Shdr *shdrs = NULL; size_t i, j, numPhdr, numShdr; int retval = 0; assert(e); elfclass = gelf_getclass(e); if (elfclass == ELFCLASSNONE) { ELFU_WARNELF("getclass"); goto ERROR; } if (!gelf_getehdr(e, &ehdr)) { ELFU_WARNELF("gelf_getehdr"); goto ERROR; } if (ehdr.e_machine != EM_386 && ehdr.e_machine != EM_X86_64) { ELFU_WARN("Sorry, only x86-32 and x86-64 ELF files are supported at the moment.\n"); goto ERROR; } if (elf_getphdrnum(e, &numPhdr)) { ELFU_WARNELF("elf_getphdrnum"); goto ERROR; } if (elf_getshdrnum(e, &numShdr)) { ELFU_WARNELF("elf_getshdrnum"); goto ERROR; } if (numPhdr > 0) { phdrs = malloc(numPhdr * sizeof(GElf_Phdr)); if (!phdrs) { ELFU_WARN("elfu_eCheck: malloc() failed for phdrs.\n"); goto ERROR; } /* Attempt to load all PHDRs at once to catch any errors early */ for (i = 0; i < numPhdr; i++) { GElf_Phdr phdr; if (gelf_getphdr(e, i, &phdr) != &phdr) { ELFU_WARN("gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1)); goto ERROR; } phdrs[i] = phdr; } /* Check that LOAD PHDR memory ranges do not overlap, and that others * are either fully contained in a LOAD range, or not at all. */ for (i = 0; i < numPhdr; i++) { if (phdrs[i].p_type != PT_LOAD) { continue; } for (j = 0; j < numPhdr; j++) { if (j == i || phdrs[j].p_type != PT_LOAD) { continue; } if (OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz, phdrs[j].p_vaddr, phdrs[j].p_memsz)) { if (phdrs[j].p_type == PT_LOAD) { ELFU_WARN("elfu_eCheck: Found LOAD PHDRs that overlap in memory.\n"); goto ERROR; } else if (!FULLY_OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz, phdrs[j].p_vaddr, phdrs[j].p_memsz)) { ELFU_WARN("elfu_eCheck: PHDRs %d and %d partially overlap in memory.\n", i, j); goto ERROR; } } } } } if (numShdr > 1) { /* SHDRs should not overlap with PHDRs. */ if (OVERLAPPING(ehdr.e_shoff, numShdr * ehdr.e_shentsize, ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) { ELFU_WARN("elfu_eCheck: SHDRs overlap with PHDRs.\n"); goto ERROR; } shdrs = malloc(numShdr * sizeof(GElf_Shdr)); if (!shdrs) { ELFU_WARN("elfu_eCheck: malloc() failed for shdrs.\n"); goto ERROR; } /* Attempt to load all SHDRs at once to catch any errors early */ for (i = 1; i < numShdr; i++) { Elf_Scn *scn; GElf_Shdr shdr; scn = elf_getscn(e, i); if (!scn) { ELFU_WARN("elf_getscn() failed for #%d: %s\n", i, elf_errmsg(-1)); } if (gelf_getshdr(scn, &shdr) != &shdr) { ELFU_WARNELF("gelf_getshdr"); goto ERROR; } shdrs[i] = shdr; } /* Check that Section memory ranges do not overlap. * NB: Section 0 is reserved and thus ignored. */ for (i = 1; i < numShdr; i++) { /* Section should not overlap with EHDR. */ if (shdrs[i].sh_offset == 0) { ELFU_WARN("elfu_eCheck: Section %d overlaps with EHDR.\n", i); goto ERROR; } /* Section should not overlap with PHDRs. */ if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]), ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) { ELFU_WARN("elfu_eCheck: Section %d overlaps with PHDR.\n", i); goto ERROR; } /* Section should not overlap with SHDRs. */ if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]), ehdr.e_shoff, numShdr * ehdr.e_shentsize)) { ELFU_WARN("elfu_eCheck: Section %d overlaps with SHDRs.\n", i); goto ERROR; } for (j = 1; j < numShdr; j++) { if (j == i) { continue; } /* Sections must not overlap in memory. */ if (shdrs[i].sh_addr != 0 && shdrs[j].sh_addr != 0 && OVERLAPPING(shdrs[i].sh_addr, shdrs[i].sh_size, shdrs[j].sh_addr, shdrs[j].sh_size)) { ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in memory.\n", i, j); goto ERROR; } /* Sections must not overlap in file. */ if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]), shdrs[j].sh_offset, SCNFILESIZE(&shdrs[j]))) { ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in file.\n", i, j); goto ERROR; } /* We may not have more than one symbol table */ if (shdrs[i].sh_type == SHT_SYMTAB && shdrs[j].sh_type == SHT_SYMTAB) { ELFU_WARN("elfu_eCheck: Found more than one SYMTAB section.\n"); goto ERROR; } /* We may not have more than one dynamic symbol table */ if (shdrs[i].sh_type == SHT_DYNSYM && shdrs[j].sh_type == SHT_DYNSYM) { ELFU_WARN("elfu_eCheck: Found more than one DYNSYM section.\n"); goto ERROR; } } /* Section addr/offset should match parent PHDR. * Find parent PHDR: */ for (j = 0; j < numPhdr; j++) { if (PHDR_CONTAINS_SCN_IN_MEMORY(&phdrs[j], &shdrs[i])) { GElf_Off shoff = phdrs[j].p_offset + (shdrs[i].sh_addr - phdrs[j].p_vaddr); if ((shdrs[i].sh_offset != shoff && shdrs[i].sh_type != SHT_NOBITS) || !PHDR_CONTAINS_SCN_IN_FILE(&phdrs[j], &shdrs[i])) { ELFU_WARN("elfu_eCheck: SHDR %d and PHDR %d report conflicting file/memory regions.\n", i, j); goto ERROR; } } } /* sh_link members should not point to sections out of range. */ if (shdrs[i].sh_link >= numShdr) { ELFU_WARN("elfu_eCheck: Bogus sh_link in SHDR %d.\n", i); } } } DONE: if (phdrs) { free(phdrs); } if (shdrs) { free(shdrs); } return retval; ERROR: ELFU_WARN("elfu_eCheck: Errors found.\n"); retval = -1; goto DONE; }
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; }
/* Search an ELF file for a ".gnu_debuglink" section. */ static const char * find_debuglink (Elf *elf, GElf_Word *crc) { size_t shstrndx; if (elf_getshstrndx (elf, &shstrndx) < 0) return NULL; Elf_Scn *scn = NULL; while ((scn = elf_nextscn (elf, scn)) != NULL) { GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); if (shdr == NULL) return NULL; const char *name = elf_strptr (elf, shstrndx, shdr->sh_name); if (name == NULL) return NULL; if (!strcmp (name, ".gnu_debuglink")) break; } if (scn == NULL) return NULL; /* Found the .gnu_debuglink section. Extract its contents. */ Elf_Data *rawdata = elf_rawdata (scn, NULL); if (rawdata == NULL) return NULL; Elf_Data crcdata = { .d_type = ELF_T_WORD, .d_buf = crc, .d_size = sizeof *crc, .d_version = EV_CURRENT, }; Elf_Data conv = { .d_type = ELF_T_WORD, .d_buf = rawdata->d_buf + rawdata->d_size - sizeof *crc, .d_size = sizeof *crc, .d_version = EV_CURRENT, }; GElf_Ehdr ehdr_mem; GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); if (ehdr == NULL) return NULL; Elf_Data *d = gelf_xlatetom (elf, &crcdata, &conv, ehdr->e_ident[EI_DATA]); if (d == NULL) return NULL; assert (d == &crcdata); return rawdata->d_buf; } /* Find the separate debuginfo file for this module and open libelf on it. When we return success, MOD->debug is set up. */ static Dwfl_Error find_debuginfo (Dwfl_Module *mod) { if (mod->debug.elf != NULL) return DWFL_E_NOERROR; GElf_Word debuglink_crc = 0; const char *debuglink_file = find_debuglink (mod->main.elf, &debuglink_crc); mod->debug.fd = (*mod->dwfl->callbacks->find_debuginfo) (MODCB_ARGS (mod), mod->main.name, debuglink_file, debuglink_crc, &mod->debug.name); return open_elf (mod, &mod->debug); } /* Try to find a symbol table in FILE. Returns DWFL_E_NOERROR if a proper one is found. Returns DWFL_E_NO_SYMTAB if not, but still sets results for SHT_DYNSYM. */ static Dwfl_Error load_symtab (struct dwfl_file *file, struct dwfl_file **symfile, Elf_Scn **symscn, Elf_Scn **xndxscn, size_t *syments, GElf_Word *strshndx) { bool symtab = false; Elf_Scn *scn = NULL; while ((scn = elf_nextscn (file->elf, scn)) != NULL) { GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem); if (shdr != NULL) switch (shdr->sh_type) { case SHT_SYMTAB: symtab = true; *symscn = scn; *symfile = file; *strshndx = shdr->sh_link; *syments = shdr->sh_size / shdr->sh_entsize; if (*xndxscn != NULL) return DWFL_E_NOERROR; break; case SHT_DYNSYM: if (symtab) break; /* Use this if need be, but keep looking for SHT_SYMTAB. */ *symscn = scn; *symfile = file; *strshndx = shdr->sh_link; *syments = shdr->sh_size / shdr->sh_entsize; break; case SHT_SYMTAB_SHNDX: *xndxscn = scn; if (symtab) return DWFL_E_NOERROR; break; default: break; } } if (symtab) /* We found one, though no SHT_SYMTAB_SHNDX to go with it. */ return DWFL_E_NOERROR; /* We found no SHT_SYMTAB, so any SHT_SYMTAB_SHNDX was bogus. We might have found an SHT_DYNSYM and set *SYMSCN et al though. */ *xndxscn = NULL; return DWFL_E_NO_SYMTAB; } /* Translate addresses into file offsets. OFFS[*] start out zero and remain zero if unresolved. */ static void find_offsets (Elf *elf, const GElf_Ehdr *ehdr, size_t n, GElf_Addr addrs[n], GElf_Off offs[n]) { size_t unsolved = n; for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i) { GElf_Phdr phdr_mem; GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); if (phdr != NULL && phdr->p_type == PT_LOAD && phdr->p_memsz > 0) for (size_t j = 0; j < n; ++j) if (offs[j] == 0 && addrs[j] >= phdr->p_vaddr && addrs[j] - phdr->p_vaddr < phdr->p_filesz) { offs[j] = addrs[j] - phdr->p_vaddr + phdr->p_offset; if (--unsolved == 0) break; } } }
/* 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; } } } }
/* * Given a core dump file, this function maps program headers to segments. */ static int handle_core(char const *name, Elf *elf, GElf_Ehdr *elfhdr) { GElf_Phdr phdr; uint32_t i; char *core_cmdline; const char *seg_name; if (name == NULL || elf == NULL || elfhdr == NULL) return (RETURN_DATAERR); if (elfhdr->e_shnum != 0 || elfhdr->e_type != ET_CORE) return (RETURN_DATAERR); seg_name = core_cmdline = NULL; if (style == STYLE_SYSV) sysv_header(name, NULL); else berkeley_header(); for (i = 0; i < elfhdr->e_phnum; i++) { if (gelf_getphdr(elf, i, &phdr) != NULL) { if (phdr.p_type == PT_NOTE) { handle_phdr(elf, elfhdr, &phdr, i, "note"); handle_core_note(elf, elfhdr, &phdr, &core_cmdline); } else { switch(phdr.p_type) { case PT_NULL: seg_name = "null"; break; case PT_LOAD: seg_name = "load"; break; case PT_DYNAMIC: seg_name = "dynamic"; break; case PT_INTERP: seg_name = "interp"; break; case PT_SHLIB: seg_name = "shlib"; break; case PT_PHDR: seg_name = "phdr"; break; case PT_GNU_EH_FRAME: seg_name = "eh_frame_hdr"; break; case PT_GNU_STACK: seg_name = "stack"; break; default: seg_name = "segment"; } handle_phdr(elf, elfhdr, &phdr, i, seg_name); } } } if (style == STYLE_BERKELEY) { if (core_cmdline != NULL) { berkeley_footer(core_cmdline, name, "core file invoked as"); } else { berkeley_footer(core_cmdline, name, "core file"); } } else { sysv_footer(); if (core_cmdline != NULL) { (void) printf(" (core file invoked as %s)\n\n", core_cmdline); } else { (void) printf(" (core file)\n\n"); } } free(core_cmdline); return (RETURN_OK); }
int main (int argc, char *argv[]) { Elf *elf; int fd; GElf_Ehdr ehdr; int cnt; fd = open (argv[1], O_RDONLY); if (fd == -1) { printf ("cannot open \"%s\": %s\n", argv[1], strerror (errno)); exit (1); } elf_version (EV_CURRENT); elf = elf_begin (fd, ELF_C_READ, NULL); if (elf == NULL) { printf ("cannot open ELF file: %s\n", elf_errmsg (-1)); exit (1); } if (elf_kind (elf) != ELF_K_ELF) { printf ("\"%s\" is not an ELF file\n", argv[1]); exit (1); } if (gelf_getehdr (elf, &ehdr) == NULL) { printf ("cannot get the ELF header: %s\n", elf_errmsg (-1)); exit (1); } printf ("idx type %*s %*s %*s %*s %*s align flags\n", gelf_getclass (elf) == ELFCLASS32 ? 9 : 17, "offset", gelf_getclass (elf) == ELFCLASS32 ? 10 : 18, "vaddr", gelf_getclass (elf) == ELFCLASS32 ? 10 : 18, "paddr", gelf_getclass (elf) == ELFCLASS32 ? 9 : 12, "filesz", gelf_getclass (elf) == ELFCLASS32 ? 9 : 12, "memsz"); for (cnt = 0; cnt < ehdr.e_phnum; ++cnt) { static const char *typenames[] = { [PT_NULL] = "NULL", [PT_LOAD] = "LOAD", [PT_DYNAMIC] = "DYNAMIC", [PT_INTERP] = "INTERP", [PT_NOTE] = "NOTE", [PT_SHLIB] = "SHLIB", [PT_PHDR] = "PHDR" }; GElf_Phdr mem; GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &mem); char buf[19]; const char *p_type = typenames[phdr->p_type]; /* If we don't know the name of the type we use the number value. */ if (phdr->p_type >= PT_NUM) { snprintf (buf, sizeof (buf), "%x", phdr->p_type); p_type = buf; } printf ("%3d %-7s %#0*llx %#0*llx %#0*llx %#0*llx %#0*llx %#6llx ", cnt, p_type, gelf_getclass (elf) == ELFCLASS32 ? 9 : 17, (unsigned long long int) phdr->p_offset, gelf_getclass (elf) == ELFCLASS32 ? 10 : 18, (unsigned long long int) phdr->p_vaddr, gelf_getclass (elf) == ELFCLASS32 ? 10 : 18, (unsigned long long int) phdr->p_paddr, gelf_getclass (elf) == ELFCLASS32 ? 9 : 12, (unsigned long long int) phdr->p_filesz, gelf_getclass (elf) == ELFCLASS32 ? 9 : 12, (unsigned long long int) phdr->p_memsz, (unsigned long long int) phdr->p_align); putc_unlocked ((phdr->p_flags & PF_X) ? 'X' : ' ', stdout); putc_unlocked ((phdr->p_flags & PF_W) ? 'W' : ' ', stdout); putc_unlocked ((phdr->p_flags & PF_R) ? 'R' : ' ', stdout); putc_unlocked ('\n', stdout); if (phdr->p_type == PT_INTERP) { /* We can show the user the name of the interpreter. */ size_t maxsize; char *filedata = elf_rawfile (elf, &maxsize); if (filedata != NULL && phdr->p_offset < maxsize) printf ("\t[Requesting program interpreter: %s]\n", filedata + phdr->p_offset); } } if (elf_end (elf) != 0) { printf ("error while freeing ELF descriptor: %s\n", elf_errmsg (-1)); exit (1); } return 0; }
dt_module_update(dtrace_hdl_t *dtp, struct kld_file_stat *k_stat) #endif { char fname[MAXPATHLEN]; struct stat64 st; int fd, err, bits; dt_module_t *dmp; const char *s; size_t shstrs; GElf_Shdr sh; Elf_Data *dp; Elf_Scn *sp; #if defined(sun) (void) snprintf(fname, sizeof (fname), "%s/%s/object", OBJFS_ROOT, name); #else GElf_Ehdr ehdr; GElf_Phdr ph; char name[MAXPATHLEN]; uintptr_t mapbase, alignmask; int i = 0; int is_elf_obj; (void) strlcpy(name, k_stat->name, sizeof(name)); (void) strlcpy(fname, k_stat->pathname, sizeof(fname)); #endif if ((fd = open(fname, O_RDONLY)) == -1 || fstat64(fd, &st) == -1 || (dmp = dt_module_create(dtp, name)) == NULL) { dt_dprintf("failed to open %s: %s\n", fname, strerror(errno)); (void) close(fd); return; } /* * Since the module can unload out from under us (and /system/object * will return ENOENT), tell libelf to cook the entire file now and * then close the underlying file descriptor immediately. If this * succeeds, we know that we can continue safely using dmp->dm_elf. */ dmp->dm_elf = elf_begin(fd, ELF_C_READ, NULL); err = elf_cntl(dmp->dm_elf, ELF_C_FDREAD); (void) close(fd); if (dmp->dm_elf == NULL || err == -1 || elf_getshdrstrndx(dmp->dm_elf, &shstrs) == -1) { dt_dprintf("failed to load %s: %s\n", fname, elf_errmsg(elf_errno())); dt_module_destroy(dtp, dmp); return; } switch (gelf_getclass(dmp->dm_elf)) { case ELFCLASS32: dmp->dm_ops = &dt_modops_32; bits = 32; break; case ELFCLASS64: dmp->dm_ops = &dt_modops_64; bits = 64; break; default: dt_dprintf("failed to load %s: unknown ELF class\n", fname); dt_module_destroy(dtp, dmp); return; } #if defined(__FreeBSD__) mapbase = (uintptr_t)k_stat->address; gelf_getehdr(dmp->dm_elf, &ehdr); is_elf_obj = (ehdr.e_type == ET_REL); if (is_elf_obj) { dmp->dm_sec_offsets = malloc(ehdr.e_shnum * sizeof(*dmp->dm_sec_offsets)); if (dmp->dm_sec_offsets == NULL) { dt_dprintf("failed to allocate memory\n"); dt_module_destroy(dtp, dmp); return; } } #endif /* * Iterate over the section headers locating various sections of * interest and use their attributes to flesh out the dt_module_t. */ for (sp = NULL; (sp = elf_nextscn(dmp->dm_elf, sp)) != NULL; ) { if (gelf_getshdr(sp, &sh) == NULL || sh.sh_type == SHT_NULL || (s = elf_strptr(dmp->dm_elf, shstrs, sh.sh_name)) == NULL) continue; /* skip any malformed sections */ #if defined(__FreeBSD__) if (sh.sh_size == 0) continue; if (sh.sh_type == SHT_PROGBITS || sh.sh_type == SHT_NOBITS) { alignmask = sh.sh_addralign - 1; mapbase += alignmask; mapbase &= ~alignmask; sh.sh_addr = mapbase; if (is_elf_obj) dmp->dm_sec_offsets[elf_ndxscn(sp)] = sh.sh_addr; mapbase += sh.sh_size; } #endif if (strcmp(s, ".text") == 0) { dmp->dm_text_size = sh.sh_size; dmp->dm_text_va = sh.sh_addr; } else if (strcmp(s, ".data") == 0) { dmp->dm_data_size = sh.sh_size; dmp->dm_data_va = sh.sh_addr; } else if (strcmp(s, ".bss") == 0) { dmp->dm_bss_size = sh.sh_size; dmp->dm_bss_va = sh.sh_addr; } else if (strcmp(s, ".info") == 0 && (dp = elf_getdata(sp, NULL)) != NULL) { bcopy(dp->d_buf, &dmp->dm_info, MIN(sh.sh_size, sizeof (dmp->dm_info))); } else if (strcmp(s, ".filename") == 0 && (dp = elf_getdata(sp, NULL)) != NULL) { (void) strlcpy(dmp->dm_file, dp->d_buf, sizeof (dmp->dm_file)); } } dmp->dm_flags |= DT_DM_KERNEL; #if defined(sun) dmp->dm_modid = (int)OBJFS_MODID(st.st_ino); #else /* * Include .rodata and special sections into .text. * This depends on default section layout produced by GNU ld * for ELF objects and libraries: * [Text][R/O data][R/W data][Dynamic][BSS][Non loadable] */ dmp->dm_text_size = dmp->dm_data_va - dmp->dm_text_va; #if defined(__i386__) /* * Find the first load section and figure out the relocation * offset for the symbols. The kernel module will not need * relocation, but the kernel linker modules will. */ for (i = 0; gelf_getphdr(dmp->dm_elf, i, &ph) != NULL; i++) { if (ph.p_type == PT_LOAD) { dmp->dm_reloc_offset = k_stat->address - ph.p_vaddr; break; } } #endif #endif if (dmp->dm_info.objfs_info_primary) dmp->dm_flags |= DT_DM_PRIMARY; dt_dprintf("opened %d-bit module %s (%s) [%d]\n", bits, dmp->dm_name, dmp->dm_file, dmp->dm_modid); }
DSO * fdopen_dso (int fd, const char *name) { Elf *elf = NULL; GElf_Ehdr ehdr; GElf_Addr last_off; int i, j, k, last, *sections, *invsections; DSO *dso = NULL; struct PLArch *plarch; extern struct PLArch __start_pl_arch[], __stop_pl_arch[]; elf = elf_begin (fd, ELF_C_READ, NULL); if (elf == NULL) { error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1)); goto error_out; } if (elf_kind (elf) != ELF_K_ELF) { error (0, 0, "\"%s\" is not an ELF file", name); goto error_out; } if (gelf_getehdr (elf, &ehdr) == NULL) { error (0, 0, "cannot get the ELF header: %s", elf_errmsg (-1)); goto error_out; } if (ehdr.e_type != ET_DYN && ehdr.e_type != ET_EXEC) { error (0, 0, "\"%s\" is not a shared library", name); goto error_out; } if (ehdr.e_shnum == 0) { GElf_Phdr phdr; /* Check for UPX compressed executables. */ if (ehdr.e_type == ET_EXEC && ehdr.e_phnum > 0 && (gelf_getphdr (elf, 0, &phdr), phdr.p_type == PT_LOAD) && phdr.p_filesz >= 256 && phdr.p_filesz <= 4096 && phdr.p_offset == 0 && ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize < phdr.p_filesz) { char *buf = alloca (phdr.p_filesz); size_t start = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; if (pread (fd, buf, phdr.p_filesz, 0) == phdr.p_filesz && memmem (buf + start, phdr.p_filesz - start, "UPX!", 4) != NULL) { error (0, 0, "\"%s\" is UPX compressed executable", name); goto error_out; } } error (0, 0, "\"%s\" has no section headers", name); goto error_out; } /* Allocate DSO structure. Leave place for additional 20 new section headers. */ dso = (DSO *) malloc (sizeof(DSO) + (ehdr.e_shnum + 20) * sizeof(GElf_Shdr) + (ehdr.e_phnum + 1) * sizeof(GElf_Phdr) + (ehdr.e_shnum + 20) * sizeof(Elf_Scn *)); if (!dso) { error (0, ENOMEM, "Could not open DSO"); goto error_out; } elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT | ELF_F_PERMISSIVE); memset (dso, 0, sizeof(DSO)); dso->elf = elf; dso->ehdr = ehdr; dso->phdr = (GElf_Phdr *) &dso->shdr[ehdr.e_shnum + 20]; dso->scn = (Elf_Scn **) &dso->phdr[ehdr.e_phnum + 1]; switch (ehdr.e_ident[EI_CLASS]) { case ELFCLASS32: dso->mask = 0xffffffff; break; case ELFCLASS64: dso->mask = 0xffffffffffffffffULL; break; } for (i = 0; i < ehdr.e_phnum; ++i) gelf_getphdr (elf, i, dso->phdr + i); dso->fd = fd; for (i = 0, j = 0; i < ehdr.e_shnum; ++i) { dso->scn[i] = elf_getscn (elf, i); gelfx_getshdr (elf, dso->scn[i], dso->shdr + i); if ((dso->shdr[i].sh_flags & SHF_ALLOC) && dso->shdr[i].sh_type != SHT_NOBITS) j = 1; } if (j == 0) { /* If all ALLOC sections are SHT_NOBITS, then this is a stripped-to-file debuginfo. Skip it silently. */ goto error_out; } sections = (int *) alloca (dso->ehdr.e_shnum * sizeof (int) * 2); sections[0] = 0; for (i = 1, j = 1, k = dso->ehdr.e_shnum, last = -1; i < dso->ehdr.e_shnum; ++i) if (RELOCATE_SCN (dso->shdr[i].sh_flags)) { last = i; sections[j++] = i; } else sections[--k] = i; assert (j == k); section_cmp_dso = dso; qsort (sections + k, dso->ehdr.e_shnum - k, sizeof (*sections), section_cmp); invsections = sections + dso->ehdr.e_shnum; invsections[0] = 0; for (i = 1, j = 0; i < ehdr.e_shnum; ++i) { if (i != sections[i]) { j = 1; dso->scn[i] = elf_getscn (elf, sections[i]); gelfx_getshdr (elf, dso->scn[i], dso->shdr + i); } invsections[sections[i]] = i; } if (j) { dso->move = init_section_move (dso); if (dso->move == NULL) goto error_out; memcpy (dso->move->old_to_new, invsections, dso->ehdr.e_shnum * sizeof (int)); memcpy (dso->move->new_to_old, sections, dso->ehdr.e_shnum * sizeof (int)); } last_off = 0; for (i = 1; i < ehdr.e_shnum; ++i) { if (dso->shdr[i].sh_link >= ehdr.e_shnum) { error (0, 0, "%s: bogus sh_link value %d", name, dso->shdr[i].sh_link); goto error_out; } dso->shdr[i].sh_link = invsections[dso->shdr[i].sh_link]; if (dso->shdr[i].sh_type == SHT_REL || dso->shdr[i].sh_type == SHT_RELA || (dso->shdr[i].sh_flags & SHF_INFO_LINK)) { if (dso->shdr[i].sh_info >= ehdr.e_shnum) { error (0, 0, "%s: bogus sh_info value %d", name, dso->shdr[i].sh_info); goto error_out; } dso->shdr[i].sh_info = invsections[dso->shdr[i].sh_info]; } /* Some linkers mess up sh_offset fields for empty or nobits sections. */ if (RELOCATE_SCN (dso->shdr[i].sh_flags) && (dso->shdr[i].sh_size == 0 || dso->shdr[i].sh_type == SHT_NOBITS)) { for (j = i + 1; j < ehdr.e_shnum; ++j) if (! RELOCATE_SCN (dso->shdr[j].sh_flags)) break; else if (dso->shdr[j].sh_size != 0 && dso->shdr[j].sh_type != SHT_NOBITS) break; dso->shdr[i].sh_offset = (last_off + dso->shdr[i].sh_addralign - 1) & ~(dso->shdr[i].sh_addralign - 1); if (j < ehdr.e_shnum && dso->shdr[i].sh_offset > dso->shdr[j].sh_offset) { GElf_Addr k; for (k = dso->shdr[i].sh_addralign - 1; k; ) { k >>= 1; dso->shdr[i].sh_offset = (last_off + k) & ~k; if (dso->shdr[i].sh_offset <= dso->shdr[j].sh_offset) break; } } last_off = dso->shdr[i].sh_offset; } else
/* Load ELF 'filename', parse the .eh_frame contents, and for each entry in the * second argument check whether its address is contained in the range of some * Frame Description Entry. If it does, fill in the function range of the * entry. In other words, try to assign start address and length of function * corresponding to each backtrace entry. We'll need that for the disassembly. * * Fails quietly - we should still be able to use the build ids. * * I wonder if this is really better than parsing eu-readelf text output. */ static GHashTable *elf_iterate_fdes(const char *filename, GList *entries, Elf *e) { const unsigned char *e_ident; Elf_Data *scn_data; GElf_Shdr shdr; GElf_Phdr phdr; size_t phnum; GHashTable *retval = NULL; /* NULL = error */ e_ident = (unsigned char *)elf_getident(e, NULL); if (e_ident == NULL) { VERB1 log_elf_error("elf_getident", filename); return NULL; } /* Look up the .eh_frame section */ if (!xelf_section_by_name(e, ".eh_frame", filename, &scn_data, &shdr)) { VERB1 log("Section .eh_frame not found in %s", filename); return NULL; } /* Get the address at which the executable segment is loaded. If the * .eh_frame addresses are absolute, this is used to convert them to * relative to the beginning of executable segment. We are looking for the * first LOAD segment that is executable, I hope this is sufficient. */ if (elf_getphdrnum(e, &phnum) != 0) { VERB1 log_elf_error("elf_getphdrnum", filename); return NULL; } uintptr_t exec_base; int i; for (i = 0; i < phnum; i++) { if (gelf_getphdr(e, i, &phdr) != &phdr) { VERB1 log_elf_error("gelf_getphdr", filename); return NULL; } if (phdr.p_type == PT_LOAD && phdr.p_flags & PF_X) { exec_base = (uintptr_t)phdr.p_vaddr; goto base_found; } } VERB1 log("Can't determine executable base for '%s'", filename); return NULL; base_found: VERB2 log("Executable base: %jx", (uintmax_t)exec_base); /* We now have a handle to .eh_frame data. We'll use dwarf_next_cfi to * iterate through all FDEs looking for those matching the addresses we * have. * Some info on .eh_frame can be found at http://www.airs.com/blog/archives/460 * and in DWARF documentation for .debug_frame. The initial_location and * address_range decoding is 'inspired' by elfutils source. * XXX: If this linear scan is too slow, we can do binary search on * .eh_frame_hdr -- see http://www.airs.com/blog/archives/462 */ int ret; Dwarf_Off cfi_offset; Dwarf_Off cfi_offset_next = 0; Dwarf_CFI_Entry cfi; struct cie_encoding { Dwarf_Off cie_offset; int ptr_len; bool pcrel; } *cie; GList *cie_list = NULL; /* Init hash table * keys are pointers to integers which we allocate with malloc * values stored directly */ GHashTable *hash = g_hash_table_new_full(g_int64_hash, g_int64_equal, free, NULL); while(1) { cfi_offset = cfi_offset_next; ret = dwarf_next_cfi(e_ident, scn_data, 1, cfi_offset, &cfi_offset_next, &cfi); if (ret > 0) { /* We're at the end. */ break; } if (ret < 0) { /* Error. If cfi_offset_next was updated, we may skip the * erroneous cfi. */ if (cfi_offset_next > cfi_offset) { continue; } VERB1 log("dwarf_next_cfi failed for %s: %s", filename, dwarf_errmsg(-1)); goto ret_free; } if (dwarf_cfi_cie_p(&cfi)) { /* Current CFI is a CIE. We store its offset and FDE encoding * attributes to be used when reading FDEs. */ /* Default FDE encoding (i.e. no R in augmentation string) is * DW_EH_PE_absptr. */ cie = btp_mallocz(sizeof(*cie)); cie->cie_offset = cfi_offset; cie->ptr_len = encoded_size(DW_EH_PE_absptr, e_ident); /* Search the augmentation data for FDE pointer encoding. * Unfortunately, 'P' can come before 'R' (which we are looking * for), so we may have to parse the whole thing. See the * abovementioned blog post for details. */ const char *aug = cfi.cie.augmentation; const uint8_t *augdata = cfi.cie.augmentation_data; bool skip_cie = 0; if (*aug == 'z') { aug++; } while (*aug != '\0') { if(*aug == 'R') { cie->ptr_len = encoded_size(*augdata, e_ident); if (cie->ptr_len != 4 && cie->ptr_len != 8) { VERB1 log("Unknown FDE encoding (CIE %jx) in %s", (uintmax_t)cfi_offset, filename); skip_cie = 1; } if ((*augdata & 0x70) == DW_EH_PE_pcrel) { cie->pcrel = 1; } break; } else if (*aug == 'L') { augdata++; } else if (*aug == 'P') { unsigned size = encoded_size(*augdata, e_ident); if (size == 0) { VERB1 log("Unknown size for personality encoding in %s", filename); skip_cie = 1; break; } augdata += (size + 1); } else { VERB1 log("Unknown augmentation char in %s", filename); skip_cie = 1; break; } aug++; } if (skip_cie) { free(cie); continue; } cie_list = g_list_append(cie_list, cie); } else { /* Current CFI is an FDE. */ GList *it = cie_list; cie = NULL; /* Find the CIE data that we should have saved earlier. XXX: We can * use hash table/tree to speed up the search, the number of CIEs * should usally be very low though. */ while (it != NULL) { cie = it->data; /* In .eh_frame, CIE_pointer is relative, but libdw converts it * to absolute offset. */ if(cfi.fde.CIE_pointer == cie->cie_offset) { break; /* Found. */ } it = g_list_next(it); } if (it == NULL) { VERB1 log("CIE not found for FDE %jx in %s", (uintmax_t)cfi_offset, filename); continue; } /* Read the two numbers we need and if they are PC-relative, * compute the offset from VMA base */ uintptr_t initial_location = fde_read_address(cfi.fde.start, cie->ptr_len); uintptr_t address_range = fde_read_address(cfi.fde.start+cie->ptr_len, cie->ptr_len); if (cie->pcrel) { /* We need to determine how long is the 'length' (and * consequently CIE id) field of this FDE -- it can be either 4 * or 12 bytes long. */ uintptr_t length = fde_read_address(scn_data->d_buf + cfi_offset, 4); uintptr_t skip = (length == 0xffffffffUL ? 12 : 4); uintptr_t mask = (cie->ptr_len == 4 ? 0xffffffffUL : 0xffffffffffffffffUL); initial_location += (uintptr_t)shdr.sh_offset + (uintptr_t)cfi_offset + 2*skip; initial_location &= mask; } else { /* Assuming that not pcrel means absolute address (what if the file is a library?). * Convert to text-section-start-relative. */ initial_location -= exec_base; } /* Insert the pair into hash */ uintptr_t *key = addr_alloc(initial_location + exec_base); g_hash_table_insert(hash, key, (gpointer)address_range); VERB3 log("FDE start: 0x%jx length: %u", (uintmax_t)*key, (unsigned)address_range); /* Iterate through the backtrace entries and check each address * member whether it belongs into the range given by current FDE. */ for (it = entries; it != NULL; it = g_list_next(it)) { struct backtrace_entry *entry = it->data; if (initial_location <= entry->build_id_offset && entry->build_id_offset < initial_location + address_range) { /* Convert to before-relocation absolute addresses, disassembler uses those. */ entry->function_initial_loc = exec_base + initial_location; entry->function_length = address_range; /*TODO: remove the entry from the list to save a bit of time in next iteration?*/ } } } } retval = hash; /* success */ ret_free: list_free_with_free(cie_list); if (retval == NULL) g_hash_table_destroy(hash); return retval; }