Explorar o código

ldso: bias DT_PLTGOT in the dynamic section on i386 and nios2

libgcc's unwinder reads DT_PLTGOT straight out of the object's dynamic
section to get the DW_EH_PE_datarel base, and assumes the loader has
already added the load bias -- unwind-dw2-fde-dip.c says so:

    /* For dynamically linked executables and shared libraries,
       DT_PLTGOT is the gp value for that object.  */
	  data->dbase = (void *) dyn->d_un.d_ptr;
    #if defined __linux__
	  /* On IA-32 Linux, _DYNAMIC is writable and GLIBC has
	     relocated it.  */

glibc does that in elf_get_dynamic_info(): it walks the DT_* entries and
adds l_addr to the pointer-valued ones in place.  uClibc-ng only adjusted
its own dynamic_info[] copy, so every object with a non-zero load bias
handed libgcc a link-time GOT address.

That breaks nios2, whose gcc emits DW_EH_PE_datarel for PIC code: the
LSDA pointer of a PIE resolved to a link-time address, and
tst-cancelx4-pie died with SIGSEGV inside __gcc_personality_v0 while the
same test without -pie passed.  i386 is the other target whose libgcc
reads DT_PLTGOT, but its gcc picks pcrel encodings for PIC, so nothing
there depends on the value.

Confined to those two, because writing to the dynamic section is not free
everywhere: on mips it is a read-only page, and storing there kills every
dynamically linked program with SIGSEGV -- the DT_DEBUG store a few lines
above skips mips for exactly that reason.  What is written is the already
adjusted value, so the store is idempotent and an object with a zero bias
keeps the section it was linked with.

Signed-off-by: Ramin Moussavi <lordrasmus@gmail.com>
ramin hai 3 semanas
pai
achega
9d10483c18
Modificáronse 1 ficheiros con 16 adicións e 0 borrados
  1. 16 0
      ldso/include/dl-elf.h

+ 16 - 0
ldso/include/dl-elf.h

@@ -135,6 +135,7 @@ unsigned int __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info
                                      void *debug_addr, DL_LOADADDR_TYPE load_off)
 {
 	unsigned int rtld_flags = 0;
+	ElfW(Dyn) *dyn_start = dpnt;
 
 	for (; dpnt->d_tag; dpnt++) {
 		if (dpnt->d_tag < DT_NUM) {
@@ -214,6 +215,21 @@ unsigned int __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info
 		ADJUST_DYN_INFO(DT_JMPREL, load_off);
 #ifdef __LDSO_GNU_HASH_SUPPORT__
 		ADJUST_DYN_INFO(DT_GNU_HASH_IDX, load_off);
+#endif
+#if defined __i386__ || defined __nios2__
+		/* libgcc's unwinder reads DT_PLTGOT out of the section itself
+		   for the DW_EH_PE_datarel base, and only on these two
+		   targets; elsewhere the section is left alone -- on mips it
+		   is even read-only, like the DT_DEBUG store above.  */
+		if (dynamic_info[DT_PLTGOT]) {
+			ElfW(Dyn) *dp;
+
+			for (dp = dyn_start; dp->d_tag != DT_NULL; dp++)
+				if (dp->d_tag == DT_PLTGOT) {
+					dp->d_un.d_ptr = dynamic_info[DT_PLTGOT];
+					break;
+				}
+		}
 #endif
 	}
 #ifdef __DSBT__