On 02/09/2015 03:24 AM, Eugene Rudoy wrote:
> I had no problems applying it on top of cf55336f3d. I however had to remove 
> an extra ")" before "&&" in the following fragment (part of 
> arch_dynlink_done) in order to get it compiled.

That was the only error it seems. I've regenerated the patch anyway.

Regards,
Faraz Shahbazker

---
 sysdeps/linux-gnu/mips/plt.c   |  109 ++++++++++++++++++++++++----------------
 sysdeps/linux-gnu/mips/trace.c |   93 ++++++++++++++++++++++++++++++++--
 2 files changed, 155 insertions(+), 47 deletions(-)

diff --git a/sysdeps/linux-gnu/mips/plt.c b/sysdeps/linux-gnu/mips/plt.c
index 84e2234..500d208 100644
--- a/sysdeps/linux-gnu/mips/plt.c
+++ b/sysdeps/linux-gnu/mips/plt.c
@@ -139,7 +139,7 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela 
*rela)
 {
        debug(1,"plt_addr %zx ndx %#zx",lte->arch.pltgot_addr, ndx);
 
-       if (mips_elf_is_cpic(lte->ehdr.e_flags)) {
+       if (mips_elf_is_cpic(lte->ehdr.e_flags) && lte->plt_addr != 0) {
                /* Return a pointer into the PLT.  */
                return lte->plt_addr + 16 * 2 + (ndx * 16);
        } else {
@@ -204,7 +204,7 @@ arch_elf_init(struct ltelf *lte, struct library *lib)
         * to pick up relocations to external functions.  Right now
         * function pointers from the main binary to external functions
         * can't be traced in CPIC mode.  */
-       if (mips_elf_is_cpic(lte->ehdr.e_flags)) {
+       if (mips_elf_is_cpic(lte->ehdr.e_flags) && lte->plt_addr != 0) {
                return 0; /* We are already done.  */
        }
 
@@ -358,11 +358,6 @@ arch_symbol_ret(struct process *proc, struct 
library_symbol *libsym)
 {
 }
 
-void
-arch_dynlink_done(struct process *proc)
-{
-}
-
 static int
 read_got_entry(struct process *proc, GElf_Addr addr, GElf_Addr *valp)
 {
@@ -385,16 +380,51 @@ struct mips_unresolve_data {
        GElf_Addr stub_addr;
 };
 
+void
+arch_dynlink_done(struct process *proc)
+{
+       struct library_symbol *libsym = NULL;
+
+       while ((libsym = proc_each_symbol(proc, libsym,
+                                         library_symbol_delayed_cb, NULL))) {
+               GElf_Addr got_entry_value = 0;
+               GElf_Addr resolved_value = libsym->arch.resolved_value;
+
+               if (read_got_entry(proc, libsym->arch.got_entry_addr,
+                                  &got_entry_value) < 0) {
+                       perror("dynlink_done: read got entry");
+                       return;
+               }
+
+               if ((got_entry_value != resolved_value) &&
+                   (got_entry_value != 0)) {
+                       libsym->arch.type = MIPS_PLT_NEED_UNRESOLVE;
+                       libsym->arch.data = malloc(sizeof *libsym->arch.data);
+                       if (libsym->arch.data == NULL) {
+                               perror("dynlink done: malloc unresolve data");
+                               return;
+                       }
+
+                       libsym->arch.data->self = libsym->arch.data;
+                       libsym->arch.data->got_entry_value = got_entry_value;
+                       libsym->arch.data->stub_addr = resolved_value;
+               }
+
+               if (proc_activate_delayed_symbol(proc, libsym) < 0)
+                       return;
+       }
+}
+
 enum plt_status
 arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte,
                        const char *a_name, GElf_Rela *rela, size_t ndx,
                        struct library_symbol **ret)
 {
-       if (mips_elf_is_cpic(lte->ehdr.e_flags))
+       if (mips_elf_is_cpic(lte->ehdr.e_flags) && (lte->plt_addr != 0))
                return PLT_DEFAULT;
 
-       GElf_Addr got_entry_addr = rela->r_offset;
-       GElf_Addr stub_addr = rela->r_addend;
+       GElf_Addr got_entry_addr = rela->r_offset + lte->bias;
+       GElf_Addr stub_addr = rela->r_addend + lte->bias;
 
        fprintf(stderr, "PLT-less arch_elf_add_plt_entry %s = %#llx\n",
                a_name, stub_addr);
@@ -406,37 +436,12 @@ arch_elf_add_plt_entry(struct process *proc, struct ltelf 
*lte,
                        a_name, stub_addr, strerror(errno));
                goto fail;
        }
-       libsym->arch.got_entry_addr = got_entry_addr;
-
-       GElf_Addr got_entry_value;
-       if (read_got_entry(proc, got_entry_addr, &got_entry_value) < 0)
-               goto fail;
-
-       fprintf(stderr, " + .got contains %#" PRIx64 "\n", got_entry_value);
-
-       if (got_entry_value == stub_addr || got_entry_value == 0) {
-               fprintf(stderr, "   + unresolved\n");
-               libsym->arch.type = MIPS_PLT_UNRESOLVED;
-               libsym->arch.resolved_value = stub_addr;
 
-       } else {
-               fprintf(stderr, "   + resolved\n");
-
-               /* Mark the symbol for later unresolving.  We may not
-                * do this right away, as this is called by ltrace
-                * core for all symbols, and only later filtered.  We
-                * only unresolve the symbol before the breakpoint is
-                * enabled.  */
-
-               libsym->arch.type = MIPS_PLT_NEED_UNRESOLVE;
-               libsym->arch.data = malloc(sizeof *libsym->arch.data);
-               if (libsym->arch.data == NULL)
-                       goto fail;
-
-               libsym->arch.data->self = libsym->arch.data;
-               libsym->arch.data->got_entry_value = got_entry_value;
-               libsym->arch.data->stub_addr = stub_addr;
-       }
+       fprintf(stderr, "%s unresolved\n", libsym->name);
+       libsym->arch.got_entry_addr = got_entry_addr;
+       libsym->arch.resolved_value = stub_addr;
+       libsym->arch.type = MIPS_PLT_UNRESOLVED;
+       libsym->delayed = 1;
 
        *ret = libsym;
        return PLT_OK;
@@ -474,6 +479,14 @@ arch_library_symbol_clone(struct library_symbol *retp,
                           struct library_symbol *libsym)
 {
        retp->arch = libsym->arch;
+       if (libsym->arch.type == MIPS_PLT_NEED_UNRESOLVE) {
+               assert(libsym->arch.data->self == libsym->arch.data);
+               retp->arch.data = malloc(sizeof *retp->arch.data);
+               if (retp->arch.data == NULL)
+                       return -1;
+               *retp->arch.data = *libsym->arch.data;
+               retp->arch.data->self = retp->arch.data;
+       }
        return 0;
 }
 
@@ -650,7 +663,9 @@ mips_stub_bp_retract(struct breakpoint *bp, struct process 
*proc)
        struct library_symbol *libsym = bp->libsym;
        assert(libsym != NULL);
 
-       fprintf(stderr, "May need to retract %s.\n", libsym->name);
+       /* Restore resolved .got entry before detaching */
+       unresolve_got_entry(proc, libsym->arch.got_entry_addr,
+                               libsym->arch.resolved_value);
 }
 
 static void
@@ -661,9 +676,15 @@ mips_stub_bp_install(struct breakpoint *bp, struct process 
*proc)
        assert(libsym != NULL);
 
        if (libsym->arch.type == MIPS_PLT_NEED_UNRESOLVE) {
-               assert(! "MIPS_PLT_NEED_UNRESOLVE unsupported");
-               abort();
-               /* Here comes unresolve code.  */
+               /* Re-route the got-entry to the stub and save resolved address
+                  for the break-point handler */
+               GElf_Addr got_entry_value = libsym->arch.data->got_entry_value;
+
+               if (unresolve_got_entry(proc, libsym->arch.got_entry_addr,
+                                          libsym->arch.data->stub_addr) < 0)
+                       return;
+               free(libsym->arch.data);
+               mark_as_resolved(libsym, got_entry_value);
        }
 }
 
diff --git a/sysdeps/linux-gnu/mips/trace.c b/sysdeps/linux-gnu/mips/trace.c
index 88e13ac..2a4fc7b 100644
--- a/sysdeps/linux-gnu/mips/trace.c
+++ b/sysdeps/linux-gnu/mips/trace.c
@@ -264,16 +264,100 @@ fail:
        return 0;
 }
 
+/**
+ * \param proc         The process to work on.
+ * \param pc           The current program counter
+ * \return newpc Array of next possible PC values
+ * \return nr  Count of next possible PC values
+ *
+ * An atomic Read-Modify-Write, starting with LL and ending with SC needs to
+ * be treated as a single instruction and stepped over, otherwise ERET issued
+ * within the SYSCALL will cause the write to fail, even for a single thread
+ * of execution. LL and SC must exist within a 2048-byte contiguous region.
+ *
+ * We record all outgoing branches from the LL-SC sequence and try to set
+ * breakpoints at their destinations.  Currently, ltrace only allows 2
+ * breakpoints per single step, so if there are more than one outgoing
+ * branches within the LL-SC sequence, single-stepping will eventually fail.
+ *
+ * The first possible PC value is always the instruction following the
+ * store-conditional, or if no SC is found within 2048 bytes, the next
+ * instruction following LL. Rest are destination addresses of outgoing
+ * branches within the LL-SC sequence.
+ */
+#define inrange(x,lo,hi) ((x)<=(hi) && (x)>=(lo))
+static int
+mips_atomic_next_pcs(struct process *proc, uint32_t lladdr, uint32_t *newpcs)
+{
+       int nr = 0;
+
+       uint32_t scaddr;
+       for (scaddr = lladdr + 4; scaddr - lladdr <= 2048; scaddr += 4) {
+               /* Found SC, now stepover trailing branch */
+               uint32_t inst;
+               if (proc_read_32(proc, (arch_addr_t)scaddr, &inst) >= 0 &&
+                   itype_op(inst) == 0x38) {
+                       newpcs[nr++] = scaddr + 4;
+                       break;
+               }
+       }
+
+       /* No SC within 2048 bytes, assume LL is standalone */
+       if (nr == 0) {
+               scaddr = 0;
+               newpcs[nr++] = lladdr + 4;
+       }
+
+       /* Scan LL<->SC range for branches going outside that range */
+       uint32_t spc;
+       for (spc = lladdr + 4; spc < scaddr; spc += 4) {
+               uint32_t scanpcs[2];
+               int snr = mips_next_pcs(proc, spc, scanpcs);
+
+               int i;
+               for (i = 0; i < snr; ++i) {
+                       if (!inrange(scanpcs[i], lladdr, scaddr)) {
+                               uint32_t *tmp = realloc(newpcs, (nr + 1) *
+                                                       sizeof *newpcs);
+                               if (tmp == NULL) {
+                                       perror("malloc atomic next pcs");
+                                       return -1;
+                               }
+
+                               newpcs = tmp;
+                               newpcs[nr++] = scanpcs[i];
+                       }
+               }
+       }
+
+       assert(nr > 0);
+       return nr;
+}
+
 enum sw_singlestep_status
 arch_sw_singlestep(struct process *proc, struct breakpoint *bp,
                   int (*add_cb)(arch_addr_t, struct sw_singlestep_data *),
                   struct sw_singlestep_data *add_cb_data)
 {
        uint32_t pc = (uint32_t) get_instruction_pointer(proc);
-       uint32_t newpcs[2];
+       uint32_t *newpcs;
        int nr;
+       uint32_t inst;
+
+       if (proc_read_32(proc, (arch_addr_t)pc, &inst) < 0)
+               return SWS_FAIL;
 
-       nr = mips_next_pcs(proc, pc, newpcs);
+       if ((newpcs = malloc(2 * sizeof *newpcs)) == NULL)
+               return SWS_FAIL;
+
+       /* Starting an atomic read-modify-write sequence */
+       if (itype_op(inst) == 0x30)
+               nr = mips_atomic_next_pcs(proc, pc, newpcs);
+       else
+               nr = mips_next_pcs(proc, pc, newpcs);
+
+       if (nr <= 0)
+               return SWS_FAIL;
 
        while (nr-- > 0) {
                arch_addr_t baddr = (arch_addr_t) newpcs[nr];
@@ -283,11 +367,14 @@ arch_sw_singlestep(struct process *proc, struct 
breakpoint *bp,
                        continue;
                }
 
-               if (add_cb(baddr, add_cb_data) < 0)
+               if (add_cb(baddr, add_cb_data) < 0) {
+                       free(newpcs);
                        return SWS_FAIL;
+               }
        }
 
        ptrace(PTRACE_SYSCALL, proc->pid, 0, 0);
+       free(newpcs);
        return SWS_OK;
 }
 
-- 
1.7.9.5


_______________________________________________
Ltrace-devel mailing list
[email protected]
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/ltrace-devel

Reply via email to