map_s *elf_set_breakpoints(proc_s *proc) { elf_info_s *elf = elf_symbols(proc->fd); map_s *brkp = map_init(32, cmp_addr); for (size_t i = 0; i < elf->replt_count; ++i) { void *ret; GElf_Rel rel; GElf_Rela rela; const char *name; GElf_Sym sym; GElf_Addr addr; /* local relocation entries */ if (elf->replt->d_type == ELF_T_REL) { ret = gelf_getrel(elf->replt, i, &rel); rela.r_offset = rel.r_offset; rela.r_info = rel.r_info; rela.r_addend = 0; } /* external relocation entries */ else ret = gelf_getrela(elf->replt, i, &rela); gelf_getsym(elf->dynsym, ELF64_R_SYM(rela.r_info), &sym); name = elf->dynstr + sym.st_name; addr = elf->plt_addr + (i + 1) * 16; breakpoint_create(brkp, addr, name, proc->pid); } return brkp; }
static void _dwarf_elf_apply_reloc(Dwarf_Debug dbg, void *buf, Elf_Data *rel_data, Elf_Data *symtab_data, int endian) { Dwarf_Unsigned type; GElf_Rela rela; GElf_Sym sym; size_t symndx; uint64_t offset; int size, j; j = 0; while (gelf_getrela(rel_data, j++, &rela) != NULL) { symndx = GELF_R_SYM(rela.r_info); type = GELF_R_TYPE(rela.r_info); if (gelf_getsym(symtab_data, symndx, &sym) == NULL) continue; offset = rela.r_offset; size = _dwarf_get_reloc_size(dbg, type); if (endian == ELFDATA2MSB) _dwarf_write_msb(buf, &offset, rela.r_addend, size); else _dwarf_write_lsb(buf, &offset, rela.r_addend, size); } }
static void _dwarf_elf_apply_rela_reloc(Dwarf_Debug dbg, void *buf, Elf_Data *rel_data, Elf_Data *symtab_data, int endian) { GElf_Rela rela; int j; j = 0; while (gelf_getrela(rel_data, j++, &rela) != NULL) _dwarf_elf_write_reloc(dbg, symtab_data, endian, buf, rela.r_offset, rela.r_info, rela.r_addend, 0); }
static GHashTable* parse_plt(Elf *e, const char *filename) { GElf_Shdr shdr; Elf_Data *plt_data; uintptr_t plt_base; size_t plt_section_index = xelf_section_by_name(e, ".plt", filename, &plt_data, &shdr); if (plt_section_index == 0) { VERB1 log("No .plt section found for %s", filename); return NULL; } plt_base = shdr.sh_addr; /* Find the relocation section for .plt (typically .rela.plt), together * with its symbol and string table */ Elf_Data *rela_plt_data = NULL; Elf_Data *plt_symbols = NULL; size_t stringtable = 0; Elf_Scn *scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) != &shdr) { VERB1 log_elf_error("gelf_getshdr", filename); continue; } if (shdr.sh_type == SHT_RELA && shdr.sh_info == plt_section_index) { rela_plt_data = elf_getdata(scn, NULL); if (rela_plt_data == NULL) { VERB1 log_elf_error("elf_getdata", filename); break; } /* Get symbol section for .rela.plt */ Elf_Scn *symscn = elf_getscn(e, shdr.sh_link); if (symscn == NULL) { VERB1 log_elf_error("elf_getscn", filename); break; } plt_symbols = elf_getdata(symscn, NULL); if (plt_symbols == NULL) { VERB1 log_elf_error("elf_getdata", filename); break; } /* Get string table for the symbol table. */ if (gelf_getshdr(symscn, &shdr) != &shdr) { VERB1 log_elf_error("gelf_getshdr", filename); break; } stringtable = shdr.sh_link; break; } } if (stringtable == 0) { VERB1 log("Unable to read symbol table for .plt for file %s", filename); return NULL; } /* Init hash table * keys are pointers to integers which we allocate with malloc * values are owned by libelf, so we don't need to free them */ GHashTable *hash = g_hash_table_new_full(g_int64_hash, g_int64_equal, free, 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> */ unsigned plt_offset; uint32_t *plt_index; GElf_Rela rela; GElf_Sym symb; for (plt_offset = 16; plt_offset < plt_data->d_size; plt_offset += 16) { plt_index = (uint32_t*)(plt_data->d_buf + plt_offset + 7); if(gelf_getrela(rela_plt_data, *plt_index, &rela) != &rela) { VERB1 log_elf_error("gelf_getrela", filename); continue; } if(gelf_getsym(plt_symbols, GELF_R_SYM(rela.r_info), &symb) != &symb) { VERB1 log_elf_error("gelf_getsym", filename); continue; } char *symbol = elf_strptr(e, stringtable, symb.st_name); uintptr_t *addr = addr_alloc((uintptr_t)(plt_base + plt_offset)); VERB3 log("[%02x] %jx: %s", *plt_index, (uintptr_t)(*addr), symbol); g_hash_table_insert(hash, addr, symbol); } return hash; }
static gboolean handle_dwarf2_section (DebuginfoData *data, GHashTable *files, GError **error) { Elf_Data *e_data; int i; debug_section_t *debug_sections; ptr_size = 0; if (data->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) { do_read_16 = buf_read_ule16; do_read_32 = buf_read_ule32; } else if (data->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) { do_read_16 = buf_read_ube16; do_read_32 = buf_read_ube32; } else { return flatpak_fail (error, "%s: Wrong ELF data encoding", data->filename); } debug_sections = data->debug_sections; if (debug_sections[DEBUG_INFO].data != NULL) { unsigned char *ptr, *endcu, *endsec; uint32_t value; struct abbrev_tag *t; g_autofree REL *relbuf = NULL; if (debug_sections[DEBUG_INFO].relsec) { Elf_Scn *scn; int ndx, maxndx; GElf_Rel rel; GElf_Rela rela; GElf_Sym sym; GElf_Addr base = data->shdr[debug_sections[DEBUG_INFO].sec].sh_addr; Elf_Data *symdata = NULL; int rtype; i = debug_sections[DEBUG_INFO].relsec; scn = data->scns[i]; e_data = elf_getdata (scn, NULL); g_assert (e_data != NULL && e_data->d_buf != NULL); g_assert (elf_getdata (scn, e_data) == NULL); g_assert (e_data->d_off == 0); g_assert (e_data->d_size == data->shdr[i].sh_size); maxndx = data->shdr[i].sh_size / data->shdr[i].sh_entsize; relbuf = g_malloc (maxndx * sizeof (REL)); reltype = data->shdr[i].sh_type; symdata = elf_getdata (data->scns[data->shdr[i].sh_link], NULL); g_assert (symdata != NULL && symdata->d_buf != NULL); g_assert (elf_getdata (data->scns[data->shdr[i].sh_link], symdata) == NULL); g_assert (symdata->d_off == 0); g_assert (symdata->d_size == data->shdr[data->shdr[i].sh_link].sh_size); for (ndx = 0, relend = relbuf; ndx < maxndx; ++ndx) { if (data->shdr[i].sh_type == SHT_REL) { gelf_getrel (e_data, ndx, &rel); rela.r_offset = rel.r_offset; rela.r_info = rel.r_info; rela.r_addend = 0; } else { gelf_getrela (e_data, ndx, &rela); } gelf_getsym (symdata, ELF64_R_SYM (rela.r_info), &sym); /* Relocations against section symbols are uninteresting in REL. */ if (data->shdr[i].sh_type == SHT_REL && sym.st_value == 0) continue; /* Only consider relocations against .debug_str, .debug_line and .debug_abbrev. */ if (sym.st_shndx != debug_sections[DEBUG_STR].sec && sym.st_shndx != debug_sections[DEBUG_LINE].sec && sym.st_shndx != debug_sections[DEBUG_ABBREV].sec) continue; rela.r_addend += sym.st_value; rtype = ELF64_R_TYPE (rela.r_info); switch (data->ehdr.e_machine) { case EM_SPARC: case EM_SPARC32PLUS: case EM_SPARCV9: if (rtype != R_SPARC_32 && rtype != R_SPARC_UA32) goto fail; break; case EM_386: if (rtype != R_386_32) goto fail; break; case EM_PPC: case EM_PPC64: if (rtype != R_PPC_ADDR32 && rtype != R_PPC_UADDR32) goto fail; break; case EM_S390: if (rtype != R_390_32) goto fail; break; case EM_IA_64: if (rtype != R_IA64_SECREL32LSB) goto fail; break; case EM_X86_64: if (rtype != R_X86_64_32) goto fail; break; case EM_ALPHA: if (rtype != R_ALPHA_REFLONG) goto fail; break; #if defined(EM_AARCH64) && defined(R_AARCH64_ABS32) case EM_AARCH64: if (rtype != R_AARCH64_ABS32) goto fail; break; #endif case EM_68K: if (rtype != R_68K_32) goto fail; break; default: fail: return flatpak_fail (error, "%s: Unhandled relocation %d in .debug_info section", data->filename, rtype); } relend->ptr = debug_sections[DEBUG_INFO].data + (rela.r_offset - base); relend->addend = rela.r_addend; ++relend; } if (relbuf == relend) { g_free (relbuf); relbuf = NULL; relend = NULL; } else { qsort (relbuf, relend - relbuf, sizeof (REL), rel_cmp); } } ptr = debug_sections[DEBUG_INFO].data; relptr = relbuf; endsec = ptr + debug_sections[DEBUG_INFO].size; while (ptr != NULL && ptr < endsec) { g_autoptr(GHashTable) abbrev = NULL; if (ptr + 11 > endsec) return flatpak_fail (error, "%s: .debug_info CU header too small", data->filename); endcu = ptr + 4; endcu += read_32 (ptr); if (endcu == ptr + 0xffffffff) return flatpak_fail (error, "%s: 64-bit DWARF not supported", data->filename); if (endcu > endsec) return flatpak_fail (error, "%s: .debug_info too small", data->filename); cu_version = read_16 (ptr); if (cu_version != 2 && cu_version != 3 && cu_version != 4) return flatpak_fail (error, "%s: DWARF version %d unhandled", data->filename, cu_version); value = read_32_relocated (ptr); if (value >= debug_sections[DEBUG_ABBREV].size) { if (debug_sections[DEBUG_ABBREV].data == NULL) return flatpak_fail (error, "%s: .debug_abbrev not present", data->filename); else return flatpak_fail (error, "%s: DWARF CU abbrev offset too large", data->filename); } if (ptr_size == 0) { ptr_size = read_1 (ptr); if (ptr_size != 4 && ptr_size != 8) return flatpak_fail (error, "%s: Invalid DWARF pointer size %d", data->filename, ptr_size); } else if (read_1 (ptr) != ptr_size) { return flatpak_fail (error, "%s: DWARF pointer size differs between CUs", data->filename); } abbrev = read_abbrev (data, debug_sections[DEBUG_ABBREV].data + value); while (ptr < endcu) { guint entry = read_uleb128 (ptr); if (entry == 0) continue; t = g_hash_table_lookup (abbrev, GINT_TO_POINTER (entry)); if (t == NULL) { g_warning ("%s: Could not find DWARF abbreviation %d", data->filename, entry); } else { ptr = handle_attributes (data, ptr, t, files, error); if (ptr == NULL) return FALSE; } } } } return TRUE; }
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 */ }
static Dwfl_Error relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr, size_t shstrndx, struct reloc_symtab_cache *reloc_symtab, Elf_Scn *scn, GElf_Shdr *shdr, Elf_Scn *tscn, bool debugscn, bool partial) { /* First, fetch the name of the section these relocations apply to. */ GElf_Shdr tshdr_mem; GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem); const char *tname = elf_strptr (relocated, shstrndx, tshdr->sh_name); if (tname == NULL) return DWFL_E_LIBELF; if (unlikely (tshdr->sh_type == SHT_NOBITS) || unlikely (tshdr->sh_size == 0)) /* No contents to relocate. */ return DWFL_E_NOERROR; if (debugscn && ! ebl_debugscn_p (mod->ebl, tname)) /* This relocation section is not for a debugging section. Nothing to do here. */ return DWFL_E_NOERROR; /* Fetch the section data that needs the relocations applied. */ Elf_Data *tdata = elf_rawdata (tscn, NULL); if (tdata == NULL) return DWFL_E_LIBELF; /* Apply one relocation. Returns true for any invalid data. */ Dwfl_Error relocate (GElf_Addr offset, const GElf_Sxword *addend, int rtype, int symndx) { /* First see if this is a reloc we can handle. If we are skipping it, don't bother resolving the symbol. */ if (unlikely (rtype == 0)) /* In some odd situations, the linker can leave R_*_NONE relocs behind. This is probably bogus ld -r behavior, but the only cases it's known to appear in are harmless: DWARF data referring to addresses in a section that has been discarded. So we just pretend it's OK without further relocation. */ return DWFL_E_NOERROR; Elf_Type type = ebl_reloc_simple_type (mod->ebl, rtype); if (unlikely (type == ELF_T_NUM)) return DWFL_E_BADRELTYPE; /* First, resolve the symbol to an absolute value. */ GElf_Addr value; if (symndx == STN_UNDEF) /* When strip removes a section symbol referring to a section moved into the debuginfo file, it replaces that symbol index in relocs with STN_UNDEF. We don't actually need the symbol, because those relocs are always references relative to the nonallocated debugging sections, which start at zero. */ value = 0; else { GElf_Sym sym; GElf_Word shndx; Dwfl_Error error = relocate_getsym (mod, relocated, reloc_symtab, symndx, &sym, &shndx); if (unlikely (error != DWFL_E_NOERROR)) return error; if (shndx == SHN_UNDEF || shndx == SHN_COMMON) { /* Maybe we can figure it out anyway. */ error = resolve_symbol (mod, reloc_symtab, &sym, shndx); if (error != DWFL_E_NOERROR && !(error == DWFL_E_RELUNDEF && shndx == SHN_COMMON)) return error; } value = sym.st_value; } /* These are the types we can relocate. */ #define TYPES DO_TYPE (BYTE, Byte); DO_TYPE (HALF, Half); \ DO_TYPE (WORD, Word); DO_TYPE (SWORD, Sword); \ DO_TYPE (XWORD, Xword); DO_TYPE (SXWORD, Sxword) size_t size; switch (type) { #define DO_TYPE(NAME, Name) \ case ELF_T_##NAME: \ size = sizeof (GElf_##Name); \ break TYPES; #undef DO_TYPE default: return DWFL_E_BADRELTYPE; } if (offset + size > tdata->d_size) return DWFL_E_BADRELOFF; #define DO_TYPE(NAME, Name) GElf_##Name Name; union { TYPES; } tmpbuf; #undef DO_TYPE Elf_Data tmpdata = { .d_type = type, .d_buf = &tmpbuf, .d_size = size, .d_version = EV_CURRENT, }; Elf_Data rdata = { .d_type = type, .d_buf = tdata->d_buf + offset, .d_size = size, .d_version = EV_CURRENT, }; /* XXX check for overflow? */ if (addend) { /* For the addend form, we have the value already. */ value += *addend; switch (type) { #define DO_TYPE(NAME, Name) \ case ELF_T_##NAME: \ tmpbuf.Name = value; \ break TYPES; #undef DO_TYPE default: abort (); } } else { /* Extract the original value and apply the reloc. */ Elf_Data *d = gelf_xlatetom (relocated, &tmpdata, &rdata, ehdr->e_ident[EI_DATA]); if (d == NULL) return DWFL_E_LIBELF; assert (d == &tmpdata); switch (type) { #define DO_TYPE(NAME, Name) \ case ELF_T_##NAME: \ tmpbuf.Name += (GElf_##Name) value; \ break TYPES; #undef DO_TYPE default: abort (); } } /* Now convert the relocated datum back to the target format. This will write into rdata.d_buf, which points into the raw section data being relocated. */ Elf_Data *s = gelf_xlatetof (relocated, &rdata, &tmpdata, ehdr->e_ident[EI_DATA]); if (s == NULL) return DWFL_E_LIBELF; assert (s == &rdata); /* We have applied this relocation! */ return DWFL_E_NOERROR; } /* Fetch the relocation section and apply each reloc in it. */ Elf_Data *reldata = elf_getdata (scn, NULL); if (reldata == NULL) return DWFL_E_LIBELF; Dwfl_Error result = DWFL_E_NOERROR; bool first_badreltype = true; inline void check_badreltype (void) { if (first_badreltype) { first_badreltype = false; if (ebl_get_elfmachine (mod->ebl) == EM_NONE) /* This might be because ebl_openbackend failed to find any libebl_CPU.so library. Diagnose that clearly. */ result = DWFL_E_UNKNOWN_MACHINE; } } size_t nrels = shdr->sh_size / shdr->sh_entsize; size_t complete = 0; if (shdr->sh_type == SHT_REL) for (size_t relidx = 0; !result && relidx < nrels; ++relidx) { GElf_Rel rel_mem, *r = gelf_getrel (reldata, relidx, &rel_mem); if (r == NULL) return DWFL_E_LIBELF; result = relocate (r->r_offset, NULL, GELF_R_TYPE (r->r_info), GELF_R_SYM (r->r_info)); check_badreltype (); if (partial) switch (result) { case DWFL_E_NOERROR: /* We applied the relocation. Elide it. */ memset (&rel_mem, 0, sizeof rel_mem); gelf_update_rel (reldata, relidx, &rel_mem); ++complete; break; case DWFL_E_BADRELTYPE: case DWFL_E_RELUNDEF: /* We couldn't handle this relocation. Skip it. */ result = DWFL_E_NOERROR; break; default: break; } } else for (size_t relidx = 0; !result && relidx < nrels; ++relidx) { GElf_Rela rela_mem, *r = gelf_getrela (reldata, relidx, &rela_mem); if (r == NULL) return DWFL_E_LIBELF; result = relocate (r->r_offset, &r->r_addend, GELF_R_TYPE (r->r_info), GELF_R_SYM (r->r_info)); check_badreltype (); if (partial) switch (result) { case DWFL_E_NOERROR: /* We applied the relocation. Elide it. */ memset (&rela_mem, 0, sizeof rela_mem); gelf_update_rela (reldata, relidx, &rela_mem); ++complete; break; case DWFL_E_BADRELTYPE: case DWFL_E_RELUNDEF: /* We couldn't handle this relocation. Skip it. */ result = DWFL_E_NOERROR; break; default: break; } } if (likely (result == DWFL_E_NOERROR)) { if (!partial || complete == nrels) /* Mark this relocation section as being empty now that we have done its work. This affects unstrip -R, so e.g. it emits an empty .rela.debug_info along with a .debug_info that has already been fully relocated. */ nrels = 0; else if (complete != 0) { /* We handled some of the relocations but not all. We've zeroed out the ones we processed. Now remove them from the section. */ size_t next = 0; if (shdr->sh_type == SHT_REL) for (size_t relidx = 0; relidx < nrels; ++relidx) { GElf_Rel rel_mem; GElf_Rel *r = gelf_getrel (reldata, relidx, &rel_mem); if (r->r_info != 0 || r->r_offset != 0) { if (next != relidx) gelf_update_rel (reldata, next, r); ++next; } } else for (size_t relidx = 0; relidx < nrels; ++relidx) { GElf_Rela rela_mem; GElf_Rela *r = gelf_getrela (reldata, relidx, &rela_mem); if (r->r_info != 0 || r->r_offset != 0 || r->r_addend != 0) { if (next != relidx) gelf_update_rela (reldata, next, r); ++next; } } nrels = next; } shdr->sh_size = reldata->d_size = nrels * shdr->sh_entsize; gelf_update_shdr (scn, shdr); } return result; } Dwfl_Error internal_function __libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile, bool debug) { assert (mod->e_type == ET_REL); GElf_Ehdr ehdr_mem; const GElf_Ehdr *ehdr = gelf_getehdr (debugfile, &ehdr_mem); if (ehdr == NULL) return DWFL_E_LIBELF; size_t d_shstrndx; if (elf_getshdrstrndx (debugfile, &d_shstrndx) < 0) return DWFL_E_LIBELF; RELOC_SYMTAB_CACHE (reloc_symtab); /* Look at each section in the debuginfo file, and process the relocation sections for debugging sections. */ Dwfl_Error result = DWFL_E_NOERROR; Elf_Scn *scn = NULL; while (result == DWFL_E_NOERROR && (scn = elf_nextscn (debugfile, scn)) != NULL) { GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) && shdr->sh_size != 0) { /* It's a relocation section. */ Elf_Scn *tscn = elf_getscn (debugfile, shdr->sh_info); if (unlikely (tscn == NULL)) result = DWFL_E_LIBELF; else result = relocate_section (mod, debugfile, ehdr, d_shstrndx, &reloc_symtab, scn, shdr, tscn, debug, !debug); } } return result; }
struct library_symbol * read_elf(Process *proc) { struct ltelf lte[MAX_LIBRARIES + 1]; size_t i; struct opt_x_t *xptr; struct opt_x_t *opt_x_loc = opt_x; struct library_symbol **lib_tail = NULL; int exit_out = 0; int count = 0; debug(DEBUG_FUNCTION, "read_elf(file=%s)", proc->filename); memset(lte, 0, sizeof(lte)); library_symbols = NULL; library_num = 0; proc->libdl_hooked = 0; if (do_init_elf(lte, proc->filename)) return NULL; memcpy(&main_lte, lte, sizeof(struct ltelf)); if (opt_p && opt_p->pid > 0) { linkmap_init(proc, lte); proc->libdl_hooked = 1; } proc->e_machine = lte->ehdr.e_machine; for (i = 0; i < library_num; ++i) { if (do_init_elf(<e[i + 1], library[i])) error(EXIT_FAILURE, errno, "Can't open \"%s\"", library[i]); } if (!options.no_plt) { #ifdef __mips__ // MIPS doesn't use the PLT and the GOT entries get changed // on startup. proc->need_to_reinitialize_breakpoints = 1; for(i=lte->mips_gotsym; i<lte->dynsym_count;i++){ GElf_Sym sym; const char *name; GElf_Addr addr = arch_plt_sym_val(lte, i, 0); if (gelf_getsym(lte->dynsym, i, &sym) == NULL){ error(EXIT_FAILURE, 0, "Couldn't get relocation from \"%s\"", proc->filename); } name=lte->dynstr+sym.st_name; if(ELF64_ST_TYPE(sym.st_info) != STT_FUNC){ debug(2,"sym %s not a function",name); continue; } add_library_symbol(addr, name, &library_symbols, 0, ELF64_ST_BIND(sym.st_info) != 0); if (!lib_tail) lib_tail = &(library_symbols->next); } #else for (i = 0; i < lte->relplt_count; ++i) { GElf_Rel rel; GElf_Rela rela; GElf_Sym sym; GElf_Addr addr; void *ret; const char *name; if (lte->relplt->d_type == ELF_T_REL) { ret = gelf_getrel(lte->relplt, i, &rel); rela.r_offset = rel.r_offset; rela.r_info = rel.r_info; rela.r_addend = 0; } else ret = gelf_getrela(lte->relplt, i, &rela); if (ret == NULL || ELF64_R_SYM(rela.r_info) >= lte->dynsym_count || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela.r_info), &sym) == NULL) error(EXIT_FAILURE, 0, "Couldn't get relocation from \"%s\"", proc->filename); #ifdef PLT_REINITALISATION_BP if (!sym.st_value && PLTs_initialized_by_here) proc->need_to_reinitialize_breakpoints = 1; #endif name = lte->dynstr + sym.st_name; count = library_num ? library_num+1 : 0; if (in_load_libraries(name, lte, count, NULL)) { enum toplt pltt; if (sym.st_value == 0 && lte->plt_stub_vma != 0) { pltt = LS_TOPLT_EXEC; addr = lte->plt_stub_vma + PPC_PLT_STUB_SIZE * i; } else { pltt = PLTS_ARE_EXECUTABLE(lte) ? LS_TOPLT_EXEC : LS_TOPLT_POINT; addr = arch_plt_sym_val(lte, i, &rela); } add_library_symbol(addr, name, &library_symbols, pltt, ELF64_ST_BIND(sym.st_info) == STB_WEAK); if (!lib_tail) lib_tail = &(library_symbols->next); } } #endif // !__mips__ #ifdef PLT_REINITALISATION_BP struct opt_x_t *main_cheat; if (proc->need_to_reinitialize_breakpoints) { /* Add "PLTs_initialized_by_here" to opt_x list, if not already there. */ main_cheat = (struct opt_x_t *)malloc(sizeof(struct opt_x_t)); if (main_cheat == NULL) error(EXIT_FAILURE, 0, "Couldn't allocate memory"); main_cheat->next = opt_x_loc; main_cheat->found = 0; main_cheat->name = PLTs_initialized_by_here; for (xptr = opt_x_loc; xptr; xptr = xptr->next) if (strcmp(xptr->name, PLTs_initialized_by_here) == 0 && main_cheat) { free(main_cheat); main_cheat = NULL; break; } if (main_cheat) opt_x_loc = main_cheat; } #endif } else { lib_tail = &library_symbols; } for (i = 0; i < lte->symtab_count; ++i) { GElf_Sym sym; GElf_Addr addr; const char *name; if (gelf_getsym(lte->symtab, i, &sym) == NULL) error(EXIT_FAILURE, 0, "Couldn't get symbol from \"%s\"", proc->filename); name = lte->strtab + sym.st_name; addr = sym.st_value; if (!addr) continue; for (xptr = opt_x_loc; xptr; xptr = xptr->next) if (xptr->name && strcmp(xptr->name, name) == 0) { /* FIXME: Should be able to use &library_symbols as above. But when you do, none of the real library symbols cause breaks. */ add_library_symbol(opd2addr(lte, addr), name, lib_tail, LS_TOPLT_NONE, 0); xptr->found = 1; break; } } unsigned found_count = 0; for (xptr = opt_x_loc; xptr; xptr = xptr->next) { if (xptr->found) continue; GElf_Sym sym; GElf_Addr addr; if (in_load_libraries(xptr->name, lte, library_num+1, &sym)) { debug(2, "found symbol %s @ %#" PRIx64 ", adding it.", xptr->name, sym.st_value); addr = sym.st_value; if (ELF32_ST_TYPE (sym.st_info) == STT_FUNC) { add_library_symbol(addr, xptr->name, lib_tail, LS_TOPLT_NONE, 0); xptr->found = 1; found_count++; } } if (found_count == opt_x_cnt){ debug(2, "done, found everything: %d\n", found_count); break; } } for (xptr = opt_x_loc; xptr; xptr = xptr->next) if ( ! xptr->found) { char *badthing = "WARNING"; #ifdef PLT_REINITALISATION_BP if (strcmp(xptr->name, PLTs_initialized_by_here) == 0) { if (lte->ehdr.e_entry) { add_library_symbol ( opd2addr (lte, lte->ehdr.e_entry), PLTs_initialized_by_here, lib_tail, 1, 0); fprintf (stderr, "WARNING: Using e_ent" "ry from elf header (%p) for " "address of \"%s\"\n", (void*) (long) lte->ehdr.e_entry, PLTs_initialized_by_here); continue; } badthing = "ERROR"; exit_out = 1; } #endif fprintf (stderr, "%s: Couldn't find symbol \"%s\" in file \"%s\" assuming it will be loaded by libdl!" "\n", badthing, xptr->name, proc->filename); } if (exit_out) { exit (1); } for (i = 0; i < library_num + 1; ++i) do_close_elf(<e[i]); return library_symbols; }