void * drsym_obj_mod_init_pre(byte *map_base, size_t file_size) { elf_info_t *mod; Elf_Scn *symtab_scn; Elf_Scn *strtab_scn; Elf_Shdr *symtab_shdr; mod = dr_global_alloc(sizeof(*mod)); memset(mod, 0, sizeof(*mod)); mod->map_base = map_base; mod->elf = elf_memory((char *)map_base, file_size); symtab_scn = find_elf_section_by_name(mod->elf, ".symtab"); strtab_scn = find_elf_section_by_name(mod->elf, ".strtab"); if (symtab_scn != NULL) { mod->debug_kind |= DRSYM_SYMBOLS | DRSYM_ELF_SYMTAB; if (strtab_scn != NULL) { symtab_shdr = elf_getshdr(symtab_scn); mod->strtab_idx = elf_ndxscn(strtab_scn); mod->num_syms = symtab_shdr->sh_size / symtab_shdr->sh_entsize; /* This assumes that the ELF file uses the same representation conventions * as the current machine, which is reasonable considering this module is * probably loaded in the current process. */ mod->syms = (Elf_Sym*)(((char*) mod->map_base) + symtab_shdr->sh_offset); } } else { /* XXX i#672: there may still be dwarf2 or stabs sections even if the * symtable is stripped and we could do symbol lookup via dwarf2 */ } if (find_elf_section_by_name(mod->elf, ".debug_line") != NULL) { mod->debug_kind |= DRSYM_LINE_NUMS | DRSYM_DWARF_LINE; } return (void *) mod; }
/* Return the path contained in the .gnu_debuglink section or NULL if we cannot * find it. * * XXX: There's also a CRC in here that we could use to warn if the files are * out of sync. */ const char * drsym_obj_debuglink_section(void *mod_in) { elf_info_t *mod = (elf_info_t *) mod_in; Elf_Shdr *section_header = elf_getshdr(find_elf_section_by_name(mod->elf, ".gnu_debuglink")); if (section_header == NULL) { NOTIFY_ELF(); return NULL; } return ((char*) mod->map_base) + section_header->sh_offset; }
struct sr_elf_fde * sr_elf_get_eh_frame(const char *filename, char **error_message) { #ifdef WITH_ELFUTILS /* Open the input file. */ int fd = open(filename, O_RDONLY); if (fd < 0) { *error_message = sr_asprintf("Failed to open file %s: %s", filename, strerror(errno)); return NULL; } /* Initialize libelf on the opened file. */ Elf *elf = elf_begin(fd, ELF_C_READ, NULL); if (!elf) { *error_message = sr_asprintf("Failed to run elf_begin on file %s: %s", filename, elf_errmsg(-1)); close(fd); return NULL; } unsigned char *e_ident = (unsigned char *)elf_getident(elf, NULL); if (!e_ident) { *error_message = sr_asprintf("elf_getident failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } /* Look up the .eh_frame section */ GElf_Shdr shdr; Elf_Data *section_data; char *find_section_error_message; if (!find_elf_section_by_name(elf, ".eh_frame", §ion_data, &shdr, &find_section_error_message)) { *error_message = sr_asprintf("Failed to find .eh_frame section for %s: %s", filename, find_section_error_message); free(find_section_error_message); elf_end(elf); close(fd); 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. */ size_t phnum; if (elf_getphdrnum(elf, &phnum) != 0) { *error_message = sr_asprintf("elf_getphdrnum failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } uint64_t exec_base = -1; int i; for (i = 0; i < phnum; i++) { GElf_Phdr phdr; if (gelf_getphdr(elf, i, &phdr) != &phdr) { *error_message = sr_asprintf("gelf_getphdr failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } if (phdr.p_type == PT_LOAD && phdr.p_flags & PF_X) { exec_base = phdr.p_vaddr; break; } } if (-1 == exec_base) { *error_message = sr_asprintf("Can't determine executable base for %s", filename); elf_end(elf); close(fd); return NULL; } /* 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. * * @todo If this linear scan is too slow, we can do binary search * on .eh_frame_hdr -- see http://www.airs.com/blog/archives/462 */ struct sr_elf_fde *result = NULL, *last = NULL; struct cie *cie_list = NULL, *cie_list_last = NULL; Dwarf_Off cfi_offset_next = 0; while (true) { Dwarf_CFI_Entry cfi; Dwarf_Off cfi_offset = cfi_offset_next; int ret = dwarf_next_cfi(e_ident, section_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; *error_message = sr_asprintf("dwarf_next_cfi failed for %s: %s", filename, dwarf_errmsg(-1)); cie_free(cie_list); sr_elf_eh_frame_free(result); elf_end(elf); close(fd); return NULL; } 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. */ char *cie_error_message; struct cie *cie = read_cie(&cfi, cfi_offset, e_ident, &cie_error_message); if (!cie) { *error_message = sr_asprintf("CIE reading failed for %s: %s", filename, cie_error_message); free(cie_error_message); cie_free(cie_list); sr_elf_eh_frame_free(result); elf_end(elf); close(fd); return NULL; } /* Append the newly parsed CIE to our list of CIEs. */ if (cie_list) { cie_list_last->next = cie; cie_list_last = cie; } else cie_list = cie_list_last = cie; } else { /* Current CFI is an FDE. */ struct cie *cie = cie_list; /* Find the CIE data that we should have saved earlier. */ while (cie) { /* In .eh_frame, CIE_pointer is relative, but libdw converts it * to absolute offset. */ if (cfi.fde.CIE_pointer == cie->cie_offset) break; /* Found. */ cie = cie->next; } if (!cie) { *error_message = sr_asprintf("CIE not found for FDE %jx in %s", (uintmax_t)cfi_offset, filename); cie_free(cie_list); sr_elf_eh_frame_free(result); elf_end(elf); close(fd); return NULL; } /* Read the two numbers we need and if they are PC-relative, * compute the offset from VMA base */ uint64_t initial_location = fde_read_address(cfi.fde.start, cie->ptr_len); uint64_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. */ uint64_t length = fde_read_address(section_data->d_buf + cfi_offset, 4); uint64_t skip = (length == 0xffffffffUL ? 12 : 4); uint64_t mask = (cie->ptr_len == 4 ? 0xffffffffUL : 0xffffffffffffffffUL); initial_location += shdr.sh_offset + 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; } struct sr_elf_fde *fde = sr_malloc(sizeof(struct sr_elf_fde)); fde->exec_base = exec_base; fde->start_address = initial_location; fde->length = address_range; fde->next = NULL; /* Append the newly parsed Frame Description Entry to our * list of FDEs. */ if (result) { last->next = fde; last = fde; } else result = last = fde; } } cie_free(cie_list); elf_end(elf); close(fd); return result; #else /* WITH_ELFUTILS */ *error_message = sr_asprintf("satyr compiled without elfutils"); return NULL; #endif /* WITH_ELFUTILS */ }
struct sr_elf_plt_entry * sr_elf_get_procedure_linkage_table(const char *filename, char **error_message) { #ifdef WITH_ELFUTILS /* Open the input file. */ int fd = open(filename, O_RDONLY); if (fd < 0) { *error_message = sr_asprintf("Failed to open file %s: %s", filename, strerror(errno)); return NULL; } /* Initialize libelf on the opened file. */ Elf *elf = elf_begin(fd, ELF_C_READ, NULL); if (!elf) { *error_message = sr_asprintf("Failed to run elf_begin on file %s: %s", filename, elf_errmsg(-1)); close(fd); return NULL; } /* Find the .plt section. */ GElf_Shdr shdr; Elf_Data *plt_data; char *find_section_error_message; size_t plt_section_index = find_elf_section_by_name(elf, ".plt", &plt_data, &shdr, &find_section_error_message); if (0 == plt_section_index) { *error_message = sr_asprintf("Failed to find .plt section for %s: %s", filename, find_section_error_message); free(find_section_error_message); elf_end(elf); close(fd); return NULL; } /* Find the relocation section for .plt (typically .rela.plt), together * with its symbol and string table */ uint64_t plt_base = shdr.sh_addr; Elf_Data *rela_plt_data = NULL; Elf_Data *plt_symbols = NULL; size_t stringtable = 0; Elf_Scn *section = NULL; while ((section = elf_nextscn(elf, section)) != NULL) { if (gelf_getshdr(section, &shdr) != &shdr) { *error_message = sr_asprintf("gelf_getshdr failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } if (shdr.sh_type == SHT_RELA && shdr.sh_info == plt_section_index) { rela_plt_data = elf_getdata(section, NULL); if (!rela_plt_data) { *error_message = sr_asprintf("elf_getdata failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } /* Get symbol section for .rela.plt */ Elf_Scn *symbol_section = elf_getscn(elf, shdr.sh_link); if (!symbol_section) { *error_message = sr_asprintf("elf_getscn failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } plt_symbols = elf_getdata(symbol_section, NULL); if (!plt_symbols) { *error_message = sr_asprintf("elf_getdata failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } /* Get string table for the symbol table. */ if (gelf_getshdr(symbol_section, &shdr) != &shdr) { *error_message = sr_asprintf("gelf_getshdr failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } stringtable = shdr.sh_link; break; } } if (0 == stringtable) { *error_message = sr_asprintf("Unable to read symbol table for .plt for file %s", filename); elf_end(elf); close(fd); return NULL; } /* PLT looks like this (see also AMD64 ABI, page 78): * * Disassembly of section .plt: * * 0000003463e01010 <attr_removef@plt-0x10>: * 3463e01010: ff 35 2a 2c 20 00 pushq 0x202c2a(%rip) <-- here is plt_base * 3463e01016: ff 25 2c 2c 20 00 jmpq *0x202c2c(%rip) each "slot" is 16B wide * 3463e0101c: 0f 1f 40 00 nopl 0x0(%rax) 0-th slot is skipped * * 0000003463e01020 <attr_removef@plt>: * 3463e01020: ff 25 2a 2c 20 00 jmpq *0x202c2a(%rip) * 3463e01026: 68 00 00 00 00 pushq $0x0 <-- this is the number we want * 3463e0102b: e9 e0 ff ff ff jmpq 3463e01010 <_init+0x18> * * 0000003463e01030 <fgetxattr@plt>: * 3463e01030: ff 25 22 2c 20 00 jmpq *0x202c22(%rip) * 3463e01036: 68 01 00 00 00 pushq $0x1 * 3463e0103b: e9 d0 ff ff ff jmpq 3463e01010 <_init+0x18> */ struct sr_elf_plt_entry *result = NULL, *last = NULL; for (unsigned plt_offset = 16; plt_offset < plt_data->d_size; plt_offset += 16) { uint32_t *plt_index = (uint32_t*)(plt_data->d_buf + plt_offset + 7); GElf_Rela rela; if (gelf_getrela(rela_plt_data, *plt_index, &rela) != &rela) { *error_message = sr_asprintf("gelf_getrela failed for %s: %s", filename, elf_errmsg(-1)); sr_elf_procedure_linkage_table_free(result); elf_end(elf); close(fd); return NULL; } GElf_Sym symb; if (gelf_getsym(plt_symbols, GELF_R_SYM(rela.r_info), &symb) != &symb) { *error_message = sr_asprintf("gelf_getsym failed for %s: %s", filename, elf_errmsg(-1)); sr_elf_procedure_linkage_table_free(result); elf_end(elf); close(fd); return NULL; } struct sr_elf_plt_entry *entry = sr_malloc(sizeof(struct sr_elf_plt_entry)); entry->symbol_name = sr_strdup(elf_strptr(elf, stringtable, symb.st_name)); entry->address = (uint64_t)(plt_base + plt_offset); entry->next = NULL; if (result) { last->next = entry; last = entry; } else result = last = entry; } elf_end(elf); close(fd); return result; #else /* WITH_ELFUTILS */ *error_message = sr_asprintf("satyr compiled without elfutils"); return NULL; #endif /* WITH_ELFUTILS */ }