[Ltrace-devel] [PATCH] Tracing PLT-less MIPS binaries

Faraz Shahbazker faraz.shahbazker at imgtec.com
Tue Jan 20 20:53:00 UTC 2015


Enclosed patch goes on top of pmachata/mips to fix mips32 trace for PLT-less executables. I have added the missing bits for pre-linked ELF and introduced bias corrections where needed. prelink doesn't work correctly, but results for the general case seem to have improved.

I am not sure why pure CPIC (flags: CPIC & !PIC) is being used as an indicator for whether an executable has PLT, but it doesn't seem to work correctly. It is possible to build pure CPIC ELFs without PLT on MIPS. I've modified the sources to check whether lte->plt_addr is non-zero in addition to mips_elf_is_cpic(). As far as I can make out, simply checking plt_addr should be sufficient and mips_elf_is_cpic() can be dropped altogether.

fprintfs are left in place for now; replace with calls to debug() if everything else looks good.

Regards,
Faraz Shahbazker

Test results on mipsel
======================
** Results at branch-point (94773bf0b1adbe60e8fe05c85cfcec7aa4941a13):
all tests built with -mplt:
# of expected passes		179
# of unexpected failures	141
# of unsupported tests		1

all tests built with with -mno-plt:
# of expected passes		47
# of unexpected failures	267
# of unsupported tests		1

** Results with patch:
all tests built with -mplt:
# of expected passes		198
# of unexpected failures	193
# of unsupported tests		1

all tests built with with -mno-plt:
# of expected passes		199
# of unexpected failures	192
# of unsupported tests		1


PATCH follows
=============

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..090c365 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;
@@ -390,11 +397,11 @@ 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 +421,10 @@ 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)
+		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;
@@ -650,7 +661,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 +674,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..1e462d0 100644
--- a/sysdeps/linux-gnu/mips/trace.c
+++ b/sysdeps/linux-gnu/mips/trace.c
@@ -277,6 +277,39 @@ arch_sw_singlestep(struct process *proc, struct breakpoint *bp,
 
 	while (nr-- > 0) {
 		arch_addr_t baddr = (arch_addr_t) newpcs[nr];
+		arch_addr_t lladdr = baddr;
+		uint32_t inst;
+
+		/* 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.
+		 *
+		 * Note: this check is sloppy, in that in only scans forward
+		 * linearly, instead of exploring all possible paths using
+		 * mips_next_pcs; this is deemed sufficient for now, as it
+		 * captures how atomic RMWs are typically coded. */
+		proc_read_32(proc, lladdr, &inst);
+		/* Found LL(linked-load), scan ahead for SC(store-conditional) */
+		if (itype_op(inst) == 0x30) {
+			while (lladdr+=4)  {
+				proc_read_32(proc, lladdr, &inst);
+				/* Found SC, now stepover trailing branch */
+				if (itype_op(inst) == 0x38) {
+					uint32_t postrmw[2];
+					if (mips_next_pcs(proc, (uint32_t)lladdr+4,
+							  postrmw) > 0)
+						baddr = (arch_addr_t)postrmw[0];
+					break;
+				}
+
+				/* No SC within 2048b, assume LL is standalone */
+				if (lladdr - baddr > 2048)
+					break;
+			}
+		}
+
 		/* Not sure what to do here. We've already got a bp?  */
 		if (DICT_HAS_KEY(proc->leader->breakpoints, &baddr)) {
 			fprintf(stderr, "skip %p %p\n", baddr, add_cb_data);




More information about the Ltrace-devel mailing list