Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12:GA
ltrace
ltrace-ppc64le_git12.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File ltrace-ppc64le_git12.patch of Package ltrace
--- sysdeps/linux-gnu/ppc/arch.h.ori +++ sysdeps/linux-gnu/ppc/arch.h @@ -56,6 +56,9 @@ struct arch_ltelf_data { GElf_Addr opd_base; GElf_Xword opd_size; int secure_plt; + + Elf_Data *reladyn; + size_t reladyn_count; }; #define ARCH_HAVE_LIBRARY_DATA @@ -79,6 +82,10 @@ enum ppc64_plt_type { * corresponding PLT entry. The original is now saved in * RESOLVED_VALUE. */ PPC_PLT_RESOLVED, + + /* Very similar to PPC_PLT_UNRESOLVED, but for JMP_IREL + * slots. */ + PPC_PLT_IRELATIVE, }; #define ARCH_HAVE_LIBRARY_SYMBOL_DATA @@ -92,7 +99,10 @@ struct arch_library_symbol_data { #define ARCH_HAVE_BREAKPOINT_DATA struct arch_breakpoint_data { - /* We need this just for arch_breakpoint_init. */ + /* This is where we hide symbol for IRELATIVE breakpoint for + * the first time that it hits. This is NULL for normal + * breakpoints. */ + struct library_symbol *irel_libsym; }; #define ARCH_HAVE_PROCESS_DATA --- sysdeps/linux-gnu/ppc/plt.c.ori +++ sysdeps/linux-gnu/ppc/plt.c @@ -25,6 +25,7 @@ #include <errno.h> #include <inttypes.h> #include <assert.h> +#include <stdbool.h> #include <string.h> #include "proc.h" @@ -104,6 +105,21 @@ * through half the dynamic linker, we just let the thread run and hit * this breakpoint. When it hits, we know the PLT entry was resolved. * + * Another twist comes from tracing slots corresponding to + * R_PPC64_JMP_IREL relocations. These have no dedicated PLT entry. + * The calls are done directly from stubs, and the .plt entry + * (actually .iplt entry, these live in a special section) is resolved + * in advance before the binary starts. Because there's no PLT entry, + * we put the PLT breakpoints directly to the IFUNC resolver code, and + * then would like them to behave like ordinary PLT slots, including + * catching the point where these get resolved to unresolve them. So + * for the first call (which is the actual resolver call), we pretend + * that this breakpoint is artificial and has no associated symbol, + * and turn it on fully only after the first hit. Ideally we would + * trace that first call as well, but then the stepper, which tries to + * catch the point where the slot is resolved, would hit the return + * breakpoint and that's not currently handled well. + * * XXX TODO If we have hardware watch point, we might put a read watch * on .plt slot, and discover the offenders this way. I don't know * the details, but I assume at most a handful (like, one or two, if @@ -132,10 +148,48 @@ mark_as_resolved(struct library_symbol *libsym, GElf_Addr value) libsym->arch.resolved_value = value; } +static void +ppc32_delayed_symbol(struct library_symbol *libsym) +{ + /* arch_dynlink_done is called on attach as well. In that + * case some slots will have been resolved already. + * Unresolved PLT looks like this: + * + * <sleep@plt>: li r11,0 + * <sleep@plt+4>: b "resolve" + * + * "resolve" is another address in PLTGOT (the same block that + * all the PLT slots are it). When resolved, it looks either + * this way: + * + * <sleep@plt>: b 0xfea88d0 <sleep> + * + * Which is easy to detect. It can also look this way: + * + * <sleep@plt>: li r11,0 + * <sleep@plt+4>: b "dispatch" + * + * The "dispatch" address lies in PLTGOT as well. In current + * GNU toolchain, "dispatch" address is the same as PLTGOT + * address. We rely on this to figure out whether the address + * is resolved or not. */ + + uint32_t insn1 = libsym->arch.resolved_value >> 32; + uint32_t insn2 = (uint32_t) libsym->arch.resolved_value; + if ((insn1 & BRANCH_MASK) == B_INSN + || ((insn2 & BRANCH_MASK) == B_INSN + /* XXX double cast */ + && (ppc_branch_dest(libsym->enter_addr + 4, insn2) + == (arch_addr_t) (long) libsym->lib->arch.pltgot_addr))) + { + mark_as_resolved(libsym, libsym->arch.resolved_value); + } +} + void arch_dynlink_done(struct Process *proc) { - /* On PPC32 with BSS PLT, we need to enable delayed symbols. */ + /* We may need to activate delayed symbols. */ struct library_symbol *libsym = NULL; while ((libsym = proc_each_symbol(proc, libsym, library_symbol_delayed_cb, NULL))) { @@ -148,45 +202,35 @@ arch_dynlink_done(struct Process *proc) return; } - /* arch_dynlink_done is called on attach as well. In - * that case some slots will have been resolved - * already. Unresolved PLT looks like this: - * - * <sleep@plt>: li r11,0 - * <sleep@plt+4>: b "resolve" - * - * "resolve" is another address in PLTGOT (the same - * block that all the PLT slots are it). When - * resolved, it looks either this way: - * - * <sleep@plt>: b 0xfea88d0 <sleep> - * - * Which is easy to detect. It can also look this - * way: - * - * <sleep@plt>: li r11,0 - * <sleep@plt+4>: b "dispatch" - * - * The "dispatch" address lies in PLTGOT as well. In - * current GNU toolchain, "dispatch" address is the - * same as PLTGOT address. We rely on this to figure - * out whether the address is resolved or not. */ - uint32_t insn1 = libsym->arch.resolved_value >> 32; - uint32_t insn2 = (uint32_t)libsym->arch.resolved_value; - if ((insn1 & BRANCH_MASK) == B_INSN - || ((insn2 & BRANCH_MASK) == B_INSN - /* XXX double cast */ - && (ppc_branch_dest(libsym->enter_addr + 4, insn2) - == (void*)(long)libsym->lib->arch.pltgot_addr))) - mark_as_resolved(libsym, libsym->arch.resolved_value); + if (proc->e_machine == EM_PPC) + ppc32_delayed_symbol(libsym); + fprintf(stderr, "activating %s\n", libsym->name); if (proc_activate_delayed_symbol(proc, libsym) < 0) return; - /* XXX double cast */ - libsym->arch.plt_slot_addr - = (GElf_Addr)(uintptr_t)libsym->enter_addr; + if (proc->e_machine == EM_PPC) + /* XXX double cast */ + libsym->arch.plt_slot_addr + = (GElf_Addr) (uintptr_t) libsym->enter_addr; + } +} + +static bool +reloc_is_irelative(int machine, GElf_Rela *rela) +{ + bool irelative = false; + if (machine == EM_PPC64) { +#ifdef R_PPC64_JMP_IREL + irelative = GELF_R_TYPE(rela->r_info) == R_PPC64_JMP_IREL; +#endif + } else { + assert(machine == EM_PPC); +#ifdef R_PPC_IRELATIVE + irelative = GELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE; +#endif } + return irelative; } GElf_Addr @@ -199,10 +243,28 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) } else if (lte->ehdr.e_machine == EM_PPC) { return rela->r_offset; + /* Beyond this point, we are on PPC64, but don't have stub + * symbols. */ + + } else if (reloc_is_irelative(lte->ehdr.e_machine, rela)) { + + /* Put JMP_IREL breakpoint to resolver, since there's + * no dedicated PLT entry. */ + + assert(rela->r_addend != 0); + /* XXX double cast */ + arch_addr_t res_addr = (arch_addr_t) (uintptr_t) rela->r_addend; + if (arch_translate_address(lte, res_addr, &res_addr) < 0) { + fprintf(stderr, "Couldn't OPD-translate IRELATIVE " + "resolver address.\n"); + return 0; + } + /* XXX double cast */ + return (GElf_Addr) (uintptr_t) res_addr; + } else { - /* If we get here, we don't have stub symbols. In - * that case we put brakpoints to PLT entries the same - * as the PPC32 secure PLT case does. */ + /* We put brakpoints to PLT entries the same as the + * PPC32 secure PLT case does. */ assert(lte->arch.plt_stub_vma != 0); return lte->arch.plt_stub_vma + PPC64_PLT_STUB_SIZE * ndx; } @@ -380,6 +404,15 @@ nonzero_data(Elf_Data *data) return 0; } +static enum callback_status +reloc_copy_if_irelative(GElf_Rela *rela, void *data) +{ + struct ltelf *lte = data; + + return CBS_STOP_IF(reloc_is_irelative(lte->ehdr.e_machine, rela) + && VECT_PUSHBACK(<e->plt_relocs, rela) < 0); +} + int arch_elf_init(struct ltelf *lte, struct library *lib) { @@ -408,8 +441,7 @@ arch_elf_init(struct ltelf *lte, struct library *lib) } GElf_Addr glink_vma = get_glink_vma(lte, ppcgot, lte->plt_data); - assert(lte->relplt_size % 12 == 0); - size_t count = lte->relplt_size / 12; // size of RELA entry + size_t count = vect_size(<e->plt_relocs); lte->arch.plt_stub_vma = glink_vma - (GElf_Addr)count * PPC_PLT_STUB_SIZE; debug(1, "stub_vma is %#" PRIx64, lte->arch.plt_stub_vma); @@ -511,6 +543,35 @@ arch_elf_init(struct ltelf *lte, struct library *lib) } } + /* On PPC64, IRELATIVE relocations actually relocate .iplt + * section, and as such are stored in .rela.dyn (where all + * non-PLT relocations are stored) instead of .rela.plt. Add + * these to lte->plt_relocs. */ + extern int read_relplt(struct ltelf *lte, Elf_Scn *scn, GElf_Shdr *shdr, + struct vect *ret); + + GElf_Addr rela, relasz; + Elf_Scn *rela_sec; + GElf_Shdr rela_shdr; + if (lte->ehdr.e_machine == EM_PPC64 + && load_dynamic_entry(lte, DT_RELA, &rela) == 0 + && load_dynamic_entry(lte, DT_RELASZ, &relasz) == 0 + && elf_get_section_covering(lte, rela, &rela_sec, &rela_shdr) == 0 + && rela_sec != NULL) { + + struct vect v; + VECT_INIT(&v, GElf_Rela); + int ret = read_relplt(lte, rela_sec, &rela_shdr, &v); + if (ret >= 0 + && VECT_EACH(&v, GElf_Rela, NULL, + reloc_copy_if_irelative, lte) != NULL) + ret = -1; + + VECT_DESTROY(&v, GElf_Rela, NULL, NULL); + + if (ret < 0) + return ret; + } return 0; } @@ -569,8 +668,19 @@ arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, *ret = libsym; return plt_ok; + } + bool is_irelative = reloc_is_irelative(lte->ehdr.e_machine, rela); + char *name; + if (is_irelative) + name = linux_elf_find_irelative_name(lte, rela); + else + name = strdup(a_name); + + if (name == NULL) + return plt_fail; + /* PPC64. If we have stubs, we return a chain of breakpoint * sites, one for each stub that corresponds to this PLT * entry. */ @@ -578,7 +688,7 @@ arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, struct library_symbol **symp; for (symp = <e->arch.stubs; *symp != NULL; ) { struct library_symbol *sym = *symp; - if (strcmp(sym->name, a_name) != 0) { + if (strcmp(sym->name, name) != 0) { symp = &(*symp)->next; continue; } @@ -591,6 +701,7 @@ arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, if (chain != NULL) { *ret = chain; + free(name); return plt_ok; } @@ -603,16 +714,18 @@ arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, GElf_Addr plt_entry_addr = arch_plt_sym_val(lte, ndx, rela); GElf_Addr plt_slot_addr = rela->r_offset; + assert(plt_slot_addr >= lte->plt_addr || plt_slot_addr < lte->plt_addr + lte->plt_size); GElf_Addr plt_slot_value; - if (read_plt_slot_value(proc, plt_slot_addr, &plt_slot_value) < 0) + if (read_plt_slot_value(proc, plt_slot_addr, &plt_slot_value) < 0) { + free(name); return plt_fail; + } - char *name = strdup(a_name); struct library_symbol *libsym = malloc(sizeof(*libsym)); - if (name == NULL || libsym == NULL) { + if (libsym == NULL) { fprintf(stderr, "allocation for .plt slot: %s\n", strerror(errno)); fail: @@ -624,12 +685,13 @@ arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, /* XXX The double cast should be removed when * arch_addr_t becomes integral type. */ if (library_symbol_init(libsym, - (arch_addr_t)(uintptr_t)plt_entry_addr, + (arch_addr_t) (uintptr_t) plt_entry_addr, name, 1, LS_TOPLT_EXEC) < 0) goto fail; libsym->arch.plt_slot_addr = plt_slot_addr; - if (plt_slot_value == plt_entry_addr || plt_slot_value == 0) { + if (! is_irelative + && (plt_slot_value == plt_entry_addr || plt_slot_value == 0)) { libsym->arch.type = PPC_PLT_UNRESOLVED; libsym->arch.resolved_value = plt_entry_addr; @@ -647,7 +709,13 @@ arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, library_symbol_destroy(libsym); goto fail; } - mark_as_resolved(libsym, plt_slot_value); + + if (! is_irelative) { + mark_as_resolved(libsym, plt_slot_value); + } else { + libsym->arch.type = PPC_PLT_IRELATIVE; + libsym->arch.resolved_value = plt_entry_addr; + } } *ret = libsym; @@ -794,6 +914,15 @@ jump_to_entry_point(struct Process *proc, struct breakpoint *bp) static void ppc_plt_bp_continue(struct breakpoint *bp, struct Process *proc) { + /* If this is a first call through IREL breakpoint, enable the + * symbol so that it doesn't look like an artificial + * breakpoint anymore. */ + if (bp->libsym == NULL) { + assert(bp->arch.irel_libsym != NULL); + bp->libsym = bp->arch.irel_libsym; + bp->arch.irel_libsym = NULL; + } + switch (bp->libsym->arch.type) { struct Process *leader; void (*on_all_stopped)(struct process_stopping_handler *); @@ -806,6 +935,7 @@ ppc_plt_bp_continue(struct breakpoint *bp, struct Process *proc) assert(bp->libsym->lib->arch.bss_plt_prelinked == 0); /* Fall through. */ + case PPC_PLT_IRELATIVE: case PPC_PLT_UNRESOLVED: on_all_stopped = NULL; keep_stepping_p = NULL; @@ -932,6 +1062,8 @@ arch_library_symbol_clone(struct library_symbol *retp, int arch_breakpoint_init(struct Process *proc, struct breakpoint *bp) { + bp->arch.irel_libsym = NULL; + /* Artificial and entry-point breakpoints are plain. */ if (bp->libsym == NULL || bp->libsym->plt_type != LS_TOPLT_EXEC) return 0; @@ -951,6 +1020,14 @@ arch_breakpoint_init(struct Process *proc, struct breakpoint *bp) .on_retract = ppc_plt_bp_retract, }; breakpoint_set_callbacks(bp, &cbs); + + /* For JMP_IREL breakpoints, make the breakpoint look + * artificial by hiding the symbol. */ + if (bp->libsym->arch.type == PPC_PLT_IRELATIVE) { + bp->arch.irel_libsym = bp->libsym; + bp->libsym = NULL; + } + return 0; }
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor