Subject: MIPS ld.elf_so garbage
To: None <tech-userlevel@netbsd.org>
From: Charles M. Hannum <abuse@spamalicious.com>
List: tech-userlevel
Date: 09/25/2002 06:14:44
(Emphasis on the word `garbage'.)

I looked into the problem of not being able to do lazy binding on MIPS
some more, and found that the linker problems that caused it have
actually been fixed.  However, all NetBSD executables and shared
libraries built before 2001/08/14 suffer this problem.

So, I have come up with a crappy heuristic for detecting old binaries,
that allows us to DTRT with new ones, but remain compatible.  It seems
to work with a small number of test cases.

Are there any objections to this?


Index: mips_reloc.c
===================================================================
RCS file: /cvsroot/basesrc/libexec/ld.elf_so/arch/mips/mips_reloc.c,v
retrieving revision 1.35
diff -u -r1.35 mips_reloc.c
--- mips_reloc.c	2002/09/25 03:57:15	1.35
+++ mips_reloc.c	2002/09/25 06:20:07
@@ -35,6 +35,8 @@
 #include "debug.h"
 #include "rtld.h"
 
+#define SUPPORT_OLD_BROKEN_LD
+
 void _rtld_bind_start(void);
 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
 caddr_t _rtld_bind_mips(Elf_Word, Elf_Addr, Elf_Addr, Elf_Addr);
@@ -178,10 +180,22 @@
 	const Elf_Sym *sym, *def;
 	const Obj_Entry *defobj;
 	int i;
+#ifdef SUPPORT_OLD_BROKEN_LD
+	int broken;
+#endif
 
 	if (self)
 		return 0;
 
+#ifdef SUPPORT_OLD_BROKEN_LD
+	broken = 0;
+	sym = obj->symtab;
+	for (i = 1; i < 12; i++)
+		if (sym[i].st_info == ELF_ST_INFO(STB_LOCAL, STT_NOTYPE))
+			broken = 1;
+	dbg(("%s: broken=%d", obj->path, broken));
+#endif
+
 	i = (got[1] & 0x80000000) ? 2 : 1;
 	/* Relocate the local GOT entries */
 	got += i;
@@ -193,8 +207,9 @@
 		rdbg((" doing got %d sym %p (%s, %x)", i - obj->gotsym, sym,
 		    sym->st_name + obj->strtab, *got));
 
+#ifdef SUPPORT_OLD_BROKEN_LD
 		if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
-		    sym->st_shndx == SHN_UNDEF) {
+		    broken && sym->st_shndx == SHN_UNDEF) {
 			/*
 			 * XXX DANGER WILL ROBINSON!
 			 * You might think this is stupid, as it intentionally
@@ -204,13 +219,16 @@
 			 * function pointers to be resolved immediately.  This
 			 * is supposed to be done automatically by the linker,
 			 * by not outputting a PLT slot and setting st_value
-			 * to 0, but GNU ld does not do so reliably.
+			 * to 0 if there are non-PLT references, but older
+			 * versions of GNU ld do not do this.
 			 */
 			def = _rtld_find_symdef(i, obj, &defobj, true);
 			if (def == NULL)
 				return -1;
 			*got = def->st_value + (Elf_Addr)defobj->relocbase;
-		} else if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
+		} else
+#endif
+		if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
 		    sym->st_value != 0) {
 			/*
 			 * If there are non-PLT references to the function,
@@ -274,18 +292,16 @@
 				 * zero, so we need to add in the start address
 				 * of the section.
 				 *
-				 * We assume that all section-relative relocs
-				 * with contents less than the start of the
-				 * section need to be adjusted; this should
-				 * work with both old and new shlibs.
-				 *
 				 * --rkb, Oct 6, 2001
 				 */
 				tmp = *where;
 
 				if (def->st_info ==
-				    ELF_ST_INFO(STB_LOCAL, STT_SECTION) &&
-				    tmp < def->st_value)
+				    ELF_ST_INFO(STB_LOCAL, STT_SECTION)
+#ifdef SUPPORT_OLD_BROKEN_LD
+				    && !broken
+#endif
+				    )
 					tmp += (Elf_Addr)def->st_value;
 
 				tmp += (Elf_Addr)obj->relocbase;