Subject: dlsym RTLD_NEXT fix
To: None <tech-userlevel@netbsd.org>
From: Nick Hudson <skrll@netbsd.org>
List: tech-userlevel
Date: 08/06/2003 22:55:28
--Boundary-00=_QlXM/r4tNvIK+TS
Content-Type: text/plain;
  charset="us-ascii"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

dlsym(RTLD_NEXT,...) is currently broken. The reason is dlsym uses 
__builtin_return_address(0) to determine the caller's shared object, but the 
call is done via the dlsym stub in either crt0 or libc.

The attached fix is based on how FreeBSD ld.so resolves the dlsym and friends 
symbols directly to the ld.so versions. This solution has a number of 
benefits including

	- backwards compatibility maintained and existing binaries are fixed.
	- __mainprog_obj can be removed from crt0.o
	- we do the same thing as FreeBSD

OK to commit?

Nick



--Boundary-00=_QlXM/r4tNvIK+TS
Content-Type: text/x-diff;
  charset="us-ascii";
  name="dlsym.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="dlsym.diff"

Index: lib/csu/common_elf/common.c
===================================================================
RCS file: /cvsroot/src/lib/csu/common_elf/common.c,v
retrieving revision 1.12
diff -u -r1.12 common.c
--- lib/csu/common_elf/common.c	2003/07/26 19:24:26	1.12
+++ lib/csu/common_elf/common.c	2003/08/06 20:45:15
@@ -74,13 +74,4 @@
 	atexit(cleanup);
 }
 
-#ifdef __weak_alias
-__weak_alias(dlopen,_dlopen);
-__weak_alias(dlclose,_dlclose);
-__weak_alias(dlsym,_dlsym);
-__weak_alias(dlerror,_dlerror);
-__weak_alias(dladdr,_dladdr);
-#endif
-
-#include <dlfcn_stubs.c>
 #endif /* DYNAMIC */
Index: lib/csu/common_elf/common.h
===================================================================
RCS file: /cvsroot/src/lib/csu/common_elf/common.h,v
retrieving revision 1.8
diff -u -r1.8 common.h
--- lib/csu/common_elf/common.h	2003/07/26 19:24:26	1.8
+++ lib/csu/common_elf/common.h	2003/08/06 20:45:15
@@ -75,8 +75,6 @@
 #ifdef DYNAMIC
 void	_rtld_setup __P((void (*)(void), const Obj_Entry *obj));
 
-const Obj_Entry *__mainprog_obj;
-
 /*
  * Arrange for _DYNAMIC to be weak and undefined (and therefore to show up
  * as being at address zero, unless something else defines it).  That way,
Index: lib/libc/dlfcn/dlfcn_elf.c
===================================================================
RCS file: /cvsroot/src/lib/libc/dlfcn/dlfcn_elf.c,v
retrieving revision 1.3
diff -u -r1.3 dlfcn_elf.c
--- lib/libc/dlfcn/dlfcn_elf.c	2002/07/20 08:54:04	1.3
+++ lib/libc/dlfcn/dlfcn_elf.c	2003/08/06 20:45:15
@@ -35,11 +35,6 @@
 #define ELFSIZE ARCH_ELFSIZE
 #include "rtld.h"
 
-#ifdef __weak_extern
-__weak_extern(__mainprog_obj)
-#endif
-extern const Obj_Entry *__mainprog_obj;
-
 #ifdef __weak_alias
 __weak_alias(dlopen,__dlopen)
 __weak_alias(dlclose,__dlclose)
@@ -47,5 +42,55 @@
 __weak_alias(dlerror,__dlerror)
 __weak_alias(dladdr,__dladdr)
 #endif
+
+/*
+ * For ELF, the dynamic linker directly resolves references to its
+ * services to functions inside the dynamic linker itself.  These
+ * weak-symbol stubs are necessary so that "ld" won't complain about
+ * undefined symbols.  The stubs are executed only when the program is
+ * linked statically, or when a given service isn't implemented in the
+ * dynamic linker.  They must return an error if called, and they must
+ * be weak symbols so that the dynamic linker can override them.
+ */
+
+static char dlfcn_error[] = "Service unavailable";
+
+/*ARGSUSED*/
+void *
+dlopen(const char *name, int mode)
+{
+
+	return NULL;
+}
+
+/*ARGSUSED*/
+int
+dlclose(void *fd)
+{
+
+	return -1;
+}
+
+/*ARGSUSED*/
+void *
+dlsym(void *handle, const char *name)
+{
+
+	return NULL;
+}
+
+/*ARGSUSED*/
+__aconst char *
+dlerror()
+{
+
+	return dlfcn_error;
+}
+
+/*ARGSUSED*/
+int
+dladdr(const void *addr, Dl_info *dli)
+{
 
-#include <dlfcn_stubs.c>
+	return 0;
+}
Index: libexec/ld.elf_so/reloc.c
===================================================================
RCS file: /cvsroot/src/libexec/ld.elf_so/reloc.c,v
retrieving revision 1.80
diff -u -r1.80 reloc.c
--- libexec/ld.elf_so/reloc.c	2003/07/24 10:12:26	1.80
+++ libexec/ld.elf_so/reloc.c	2003/08/06 20:45:15
@@ -196,11 +196,11 @@
 		obj->version = RTLD_VERSION;
 
 		/* Fill in the dynamic linker entry points. */
-		obj->dlopen = _rtld_dlopen;
-		obj->dlsym = _rtld_dlsym;
-		obj->dlerror = _rtld_dlerror;
-		obj->dlclose = _rtld_dlclose;
-		obj->dladdr = _rtld_dladdr;
+		obj->dlopen = dlopen;
+		obj->dlsym = dlsym;
+		obj->dlerror = dlerror;
+		obj->dlclose = dlclose;
+		obj->dladdr = dladdr;
 
 		dbg(("fixing up PLTGOT"));
 		/* Set the special PLTGOT entries. */
Index: libexec/ld.elf_so/rtld.c
===================================================================
RCS file: /cvsroot/src/libexec/ld.elf_so/rtld.c,v
retrieving revision 1.98
diff -u -r1.98 rtld.c
--- libexec/ld.elf_so/rtld.c	2003/07/24 10:12:26	1.98
+++ libexec/ld.elf_so/rtld.c	2003/08/06 20:45:16
@@ -433,6 +433,9 @@
 	real_environ = _rtld_objmain_sym("environ");
 	if (real_environ)
 		*real_environ = environ;
+	/*
+	 * Set __mainprog_obj for old binaries.
+	 */
 	real___mainprog_obj = _rtld_objmain_sym("__mainprog_obj");
 	if (real___mainprog_obj)
 		*real___mainprog_obj = _rtld_objmain;
@@ -458,7 +461,7 @@
 void
 _rtld_die(void)
 {
-	const char *msg = _rtld_dlerror();
+	const char *msg = dlerror();
 
 	if (msg == NULL)
 		msg = "Fatal error";
@@ -573,7 +576,7 @@
 }
 
 int
-_rtld_dlclose(void *handle)
+dlclose(void *handle)
 {
 	Obj_Entry *root = _rtld_dlcheck(handle);
 
@@ -593,7 +596,7 @@
 }
 
 char *
-_rtld_dlerror(void)
+dlerror(void)
 {
 	char *msg = error_message;
 
@@ -602,7 +605,7 @@
 }
 
 void *
-_rtld_dlopen(const char *name, int mode)
+dlopen(const char *name, int mode)
 {
 	Obj_Entry **old_obj_tail = _rtld_objtail;
 	Obj_Entry *obj = NULL;
@@ -659,7 +662,7 @@
 }
 
 void *
-_rtld_dlsym(void *handle, const char *name)
+dlsym(void *handle, const char *name)
 {
 	const Obj_Entry *obj;
 	unsigned long hash;
@@ -745,7 +748,7 @@
 }
 
 int
-_rtld_dladdr(const void *addr, Dl_info *info)
+dladdr(const void *addr, Dl_info *info)
 {
 	const Obj_Entry *obj;
 	const Elf_Sym *def, *best_def;
Index: libexec/ld.elf_so/rtld.h
===================================================================
RCS file: /cvsroot/src/libexec/ld.elf_so/rtld.h,v
retrieving revision 1.69
diff -u -r1.69 rtld.h
--- libexec/ld.elf_so/rtld.h	2003/07/24 10:12:26	1.69
+++ libexec/ld.elf_so/rtld.h	2003/08/06 20:45:16
@@ -211,15 +211,20 @@
 extern Elf_Sym _rtld_sym_zero;
 
 /* rtld.c */
+
+/*
+ * We export these symbols using _rtld_symbol_lookup and is_exported.
+ */
+char *dlerror(void);
+void *dlopen(const char *, int);
+void *dlsym(void *, const char *);
+int dlclose(void *);
+int dladdr(const void *, Dl_info *);
+
 void _rtld_error(const char *, ...)
      __attribute__((__format__(__printf__,1,2)));
 void _rtld_die(void);
-char *_rtld_dlerror(void);
-void *_rtld_dlopen(const char *, int);
 void *_rtld_objmain_sym(const char *);
-void *_rtld_dlsym(void *, const char *);
-int _rtld_dlclose(void *);
-int _rtld_dladdr(const void *, Dl_info *);
 void _rtld_debug_state(void);
 void _rtld_linkmap_add(Obj_Entry *);
 void _rtld_linkmap_delete(Obj_Entry *);
Index: libexec/ld.elf_so/search.c
===================================================================
RCS file: /cvsroot/src/libexec/ld.elf_so/search.c,v
retrieving revision 1.17
diff -u -r1.17 search.c
--- libexec/ld.elf_so/search.c	2003/07/24 10:12:26	1.17
+++ libexec/ld.elf_so/search.c	2003/08/06 20:45:17
@@ -127,7 +127,7 @@
 	}
 	dbg((" Searching for \"%s\" (%p)", name, refobj));
 
-	tmperrorp = _rtld_dlerror();
+	tmperrorp = dlerror();
 	if (tmperrorp != NULL) {
 		strncpy(tmperror, tmperrorp, sizeof tmperror);
 		tmperrorp = tmperror;
@@ -165,7 +165,7 @@
 	if (tmperrorp)
 		_rtld_error("%s", tmperror);
 	else
-		(void)_rtld_dlerror();
+		(void)dlerror();
 	return obj;
 
 found:
Index: libexec/ld.elf_so/symbol.c
===================================================================
RCS file: /cvsroot/src/libexec/ld.elf_so/symbol.c,v
retrieving revision 1.32
diff -u -r1.32 symbol.c
--- libexec/ld.elf_so/symbol.c	2003/08/05 19:41:53	1.32
+++ libexec/ld.elf_so/symbol.c	2003/08/06 20:45:17
@@ -53,6 +53,29 @@
 #include "debug.h"
 #include "rtld.h"
 
+static bool
+_rtld_is_exported(const Elf_Sym *def)
+{
+	static Elf_Addr _rtld_exports[] = {
+		(Elf_Addr)dlopen,
+		(Elf_Addr)dlclose,
+		(Elf_Addr)dlsym,
+		(Elf_Addr)dlerror,
+		(Elf_Addr)dladdr,
+		NULL
+	};
+	int i;
+
+	Elf_Addr value;
+	value = (Elf_Addr)(_rtld_objself.relocbase + def->st_value);
+
+	for (i = 0; _rtld_exports[i] != NULL; i++) {
+		if (value == _rtld_exports[i])
+			return true;
+	}
+	return false;
+}
+
 /*
  * Hash function for symbol table lookup.  Don't even think about changing
  * this.  It is specified by the System V ABI.
@@ -226,6 +249,20 @@
 	}
 	
 	/*
+	 * Search the dynamic linker itself, and possibly resolve the
+	 * symbol from there.  This is how the application links to
+	 * dynamic linker services such as dlopen.  Only the values listed
+	 * in the "_rtld_exports" array can be resolved from the dynamic linker.
+	 */
+	if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
+		symp = _rtld_symlook_obj(name, hash, &_rtld_objself, in_plt);
+		if (symp != NULL && _rtld_is_exported(symp)) {
+			def = symp;
+			defobj = &_rtld_objself;
+		}
+	}
+	
+	/*
 	 * If we found no definition and the reference is weak, treat the
 	 * symbol as having the value zero.
 	 */
@@ -234,7 +271,7 @@
 		def = &_rtld_sym_zero;
 		defobj = _rtld_objmain;
 	}
-	
+
 	if (def != NULL)
 		*defobj_out = defobj;
 	else {
Index: usr.bin/ldd/ldd_elf/ldd.c
===================================================================
RCS file: /cvsroot/src/usr.bin/ldd/ldd_elf/ldd.c,v
retrieving revision 1.19
diff -u -r1.19 ldd.c
--- usr.bin/ldd/ldd_elf/ldd.c	2003/05/28 20:45:39	1.19
+++ usr.bin/ldd/ldd_elf/ldd.c	2003/08/06 20:45:17
@@ -197,7 +197,7 @@
 }
 
 char *
-_rtld_dlerror()
+dlerror()
 {
 	char *msg = error_message;
 	error_message = NULL;

--Boundary-00=_QlXM/r4tNvIK+TS--