Hi,

Dropped one variable, replacing pc with lladdr throughout.

I found and fixed 2 other issues while testing:

1. The check for whether GOT entries are already biased in
arch_elf_add_plt_entry needed update. There are 3 conditions under which
the GOT entries will be biased:
- Non-PIE ELFs obviously have biased GOT entries
- Pre-linked ELFs, likewise
- when ltrace attaches to an already running process

To capture the last one, I've added a check for whether proc->pid occurs 
in options.h:opt_p - not sure if there is a simpler way of performing
this check.

2. mips_unresolved_data is shallow copied for each cloned symbol. If a
child detaches whilst a symbol remains in NEED_UNRESOLVE state, then
we shouldn't free its unresolve_data because the parent/siblings may 
still need it. I've added a ref_count field to the struct to fix this.
I am not sure how(if at all) PPC tackles this problem.

Regards,
Faraz Shahbazker
---
 sysdeps/linux-gnu/mips/arch.h  |    2 +
 sysdeps/linux-gnu/mips/plt.c   |   55 ++++++++++++++++++++-----
 sysdeps/linux-gnu/mips/trace.c |   86 ++++++++++++++++++++++++++++++++++++++--
 3 files changed, 130 insertions(+), 13 deletions(-)

diff --git a/sysdeps/linux-gnu/mips/arch.h b/sysdeps/linux-gnu/mips/arch.h
index 16273d2..ffaf1b3 100644
--- a/sysdeps/linux-gnu/mips/arch.h
+++ b/sysdeps/linux-gnu/mips/arch.h
@@ -46,6 +46,8 @@ struct arch_ltelf_data {
 	size_t pltgot_addr;
 	size_t mips_local_gotno;
 	size_t mips_gotsym;
+	int    mips_pie_main;
+	int    mips_prelinked;
 };
 
 #define ARCH_HAVE_FIND_DL_DEBUG
diff --git a/sysdeps/linux-gnu/mips/plt.c b/sysdeps/linux-gnu/mips/plt.c
index 84e2234..1d2b099 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.  */
 	}
 
@@ -234,6 +234,8 @@ arch_elf_init(struct ltelf *lte, struct library *lib)
 			lte->arch.mips_local_gotno = dyn.d_un.d_val;
 		else if (dyn.d_tag == DT_MIPS_GOTSYM)
 			lte->arch.mips_gotsym = dyn.d_un.d_val;
+		else if (dyn.d_tag == DT_GNU_PRELINKED)
+			lte->arch.mips_prelinked = 1;
 	}
 
 	if (lte->arch.mips_gotsym > lte->dynsym_count) {
@@ -342,6 +344,11 @@ arch_elf_init(struct ltelf *lte, struct library *lib)
 			idx, (unsigned long) rela.r_addend, got_entry_addr);
 	}
 
+	/* For PIE main binaries, GOT entries are not biased until
+	 * we hit the entry-point. Mark such ltelfs for correction */
+	if (lte->ehdr.e_type == ET_DYN && lib->type == LT_LIBTYPE_MAIN)
+		lte->arch.mips_pie_main = 1;
+
 	/* Tell the generic code how many dynamic trace:able symbols
 	 * we've got.  */
 	// lte->relplt_count = lte->dynsym_count - lte->arch.mips_gotsym;
@@ -383,18 +390,30 @@ struct mips_unresolve_data {
 	struct mips_unresolve_data *self; /* A canary.  */
 	GElf_Addr got_entry_value;
 	GElf_Addr stub_addr;
+	int ref_count;
 };
 
+static int
+mips_proc_is_attached(struct process *proc)
+{
+	struct opt_p_t *it;
+	for (it = opt_p; it != NULL; it = it->next) {
+		if (proc->pid == it->pid)
+			return 1;
+	}
+	return 0;
+}
+
 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);
@@ -414,6 +433,11 @@ arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte,
 
 	fprintf(stderr, " + .got contains %#" PRIx64 "\n", got_entry_value);
 
+	/* For PIE main binaries, GOT entries are not yet biased */
+	if (lte->arch.mips_pie_main && !lte->arch.mips_prelinked &&
+	    !mips_proc_is_attached(proc))
+		got_entry_value += lte->bias;
+
 	if (got_entry_value == stub_addr || got_entry_value == 0) {
 		fprintf(stderr, "   + unresolved\n");
 		libsym->arch.type = MIPS_PLT_UNRESOLVED;
@@ -436,6 +460,7 @@ arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte,
 		libsym->arch.data->self = libsym->arch.data;
 		libsym->arch.data->got_entry_value = got_entry_value;
 		libsym->arch.data->stub_addr = stub_addr;
+		libsym->arch.data->ref_count = 0;
 	}
 
 	*ret = libsym;
@@ -464,7 +489,8 @@ arch_library_symbol_destroy(struct library_symbol *libsym)
 {
 	if (libsym->arch.type == MIPS_PLT_NEED_UNRESOLVE) {
 		assert(libsym->arch.data->self == libsym->arch.data);
-		free(libsym->arch.data);
+		if (libsym->arch.data->ref_count-- < 0)
+			free(libsym->arch.data);
 		libsym->arch.data = NULL;
 	}
 }
@@ -474,6 +500,8 @@ arch_library_symbol_clone(struct library_symbol *retp,
                           struct library_symbol *libsym)
 {
 	retp->arch = libsym->arch;
+	if (libsym->arch.type == MIPS_PLT_NEED_UNRESOLVE)
+		libsym->arch.data->ref_count++;
 	return 0;
 }
 
@@ -650,7 +678,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 +691,14 @@ 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;
+		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..23ceede 100644
--- a/sysdeps/linux-gnu/mips/trace.c
+++ b/sysdeps/linux-gnu/mips/trace.c
@@ -264,16 +264,93 @@ 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);
+
+		if (!inrange(scanpcs[0], lladdr, scaddr)) {
+			if ((newpcs = realloc(newpcs,
+					      (nr + 1) * sizeof(spc))) != NULL)
+				newpcs[nr++] = scanpcs[0];
+		}
+
+		if ((snr == 2) && !inrange(scanpcs[1], lladdr, scaddr)) {
+			if ((newpcs = realloc(newpcs,
+					      (nr + 1) * sizeof(spc))) != NULL)
+				newpcs[nr++] = scanpcs[1];
+		}
+	}
+
+	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;
 
-	nr = mips_next_pcs(proc, pc, newpcs);
+	if (proc_read_32(proc, (arch_addr_t)pc, &inst) < 0)
+		return SWS_FAIL;
+
+	newpcs = malloc(2 * sizeof(pc));
+
+	/* 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);
 
 	while (nr-- > 0) {
 		arch_addr_t baddr = (arch_addr_t) newpcs[nr];
@@ -283,11 +360,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