Carmelo Amoroso wrote:

> please have a look at the nptl branch where already is the updated 
> _dl_find_hash
> for NPTL using the same extra parameter you need. There is also a wrapper
> around it as suggested by Jocke.
> I think there is a way to use the interface for nptl into your case.
> In these days I'm able to check the code.

The code on the nptl branch looks similar to what I have, but I'm not
too happy about the following:
1. dl_find_hash is a wrapper around dl_lookup_hash, but it always takes
the tls_tpntp argument, whether the target has USE_TLS or not.  It would
make much more sense for TLS- (or FDPIC-)specific code that needs to
have the tpnt returned to call dl_lookup_hash directly, and use the
dl_find_hash interface for old code that can remain unchanged.
2. Having a function's signature depend on an #ifdef is really unclean
in my eyes.  I think it would be better to always have the extra
argument, and ignore it if it is NULL, as in my (well, Alex's) patch.

For now I've modified the patch to make it look similar to the NPTL
version, so I've kept the #ifdef around.  I'd prefer to go back and
eliminate it if I can get consensus.

A new series of patches is attached.  I've already committed number 6
which was just a small correction inside a bfin/ directory.


Bernd
-- 
This footer brought to you by insane German lawmakers.
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 40368
Geschaeftsfuehrer Thomas Wessel, William A. Martin, Margaret Seif
Add a new function _dl_free.  Ensure we always get back a full page from
mmap.
Reset _dl_malloc_function and _dl_free_function when libdl is initialized.

Index: uClibc/ldso/include/ldso.h
===================================================================
--- uClibc.orig/ldso/include/ldso.h
+++ uClibc/ldso/include/ldso.h
@@ -99,7 +99,8 @@ extern int   _dl_debug_file;
 #define NULL ((void *) 0)
 #endif
 
-extern void *_dl_malloc(int size);
+extern void *_dl_malloc(size_t size);
+extern void _dl_free(void *);
 extern char *_dl_getenv(const char *symbol, char **envp);
 extern void _dl_unsetenv(const char *symbol, char **envp);
 extern char *_dl_strdup(const char *string);
Index: uClibc/ldso/ldso/ldso.c
===================================================================
--- uClibc.orig/ldso/ldso/ldso.c
+++ uClibc/ldso/ldso/ldso.c
@@ -50,6 +50,7 @@ int _dl_errno                  = 0;	/* W
 size_t _dl_pagesize            = 0;	/* Store the page size for use later */
 struct r_debug *_dl_debug_addr = NULL;	/* Used to communicate with the gdb debugger */
 void *(*_dl_malloc_function) (size_t size) = NULL;
+void (*_dl_free_function) (void *p) = NULL;
 
 #ifdef __SUPPORT_LD_DEBUG__
 char *_dl_debug           = 0;
@@ -884,7 +885,7 @@ static int _dl_suid_ok(void)
 	return 0;
 }
 
-void *_dl_malloc(int size)
+void *_dl_malloc(size_t size)
 {
 	void *retval;
 
@@ -895,9 +896,26 @@ void *_dl_malloc(int size)
 	if (_dl_malloc_function)
 		return (*_dl_malloc_function) (size);
 
-	if (_dl_malloc_addr - _dl_mmap_zero + (unsigned)size > _dl_pagesize) {
+	if (_dl_malloc_addr - _dl_mmap_zero + size > _dl_pagesize) {
+		size_t rounded_size;
+
+		/* Since the above assumes we get a full page even if
+		   we request less than that, make sure we request a
+		   full page, since uClinux may give us less than than
+		   a full page.  We might round even
+		   larger-than-a-page sizes, but we end up never
+		   reusing _dl_mmap_zero/_dl_malloc_addr in that case,
+		   so we don't do it.
+
+		   The actual page size doesn't really matter; as long
+		   as we're self-consistent here, we're safe.  */
+		if (size < _dl_pagesize)
+			rounded_size = (size + _dl_pagesize - 1) & _dl_pagesize;
+		else
+			rounded_size = size;
+
 		_dl_debug_early("mmapping more memory\n");
-		_dl_mmap_zero = _dl_malloc_addr = _dl_mmap((void *) 0, size,
+		_dl_mmap_zero = _dl_malloc_addr = _dl_mmap((void *) 0, rounded_size,
 				PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 		if (_dl_mmap_check_error(_dl_mmap_zero)) {
 			_dl_dprintf(2, "%s: mmap of a spare page failed!\n", _dl_progname);
@@ -916,5 +934,11 @@ void *_dl_malloc(int size)
 	return retval;
 }
 
+void
+_dl_free (void *p) {
+	if (_dl_free_function)
+		(*_dl_free_function) (p);
+}
+
 #include "dl-hash.c"
 #include "dl-elf.c"
Index: uClibc/ldso/libdl/libdl.c
===================================================================
--- uClibc.orig/ldso/libdl/libdl.c
+++ uClibc/ldso/libdl/libdl.c
@@ -32,6 +32,7 @@
 
 #include <ldso.h>
 #include <stdio.h>
+#include <string.h>
 
 
 #ifdef SHARED
@@ -51,6 +52,7 @@ extern struct elf_resolve *_dl_loaded_mo
 extern struct r_debug *_dl_debug_addr;
 extern unsigned long _dl_error_number;
 extern void *(*_dl_malloc_function)(size_t);
+extern void (*_dl_free_function) (void *p);
 extern void _dl_run_init_array(struct elf_resolve *);
 extern void _dl_run_fini_array(struct elf_resolve *);
 #ifdef __LDSO_CACHE_SUPPORT__
@@ -67,6 +69,9 @@ extern char *_dl_debug;
 
 #else /* SHARED */
 
+#define _dl_malloc malloc
+#define _dl_free free
+
 /* When libdl is linked as a static library, we need to replace all
  * the symbols that otherwise would have been loaded in from ldso... */
 
@@ -83,13 +88,15 @@ char *_dl_debug_bindings  = 0;
 int   _dl_debug_file      = 2;
 #endif
 const char *_dl_progname       = "";        /* Program name */
+void *(*_dl_malloc_function)(size_t);
+void (*_dl_free_function) (void *p);
 char *_dl_library_path         = 0;         /* Where we look for libraries */
 char *_dl_ldsopath             = 0;         /* Location of the shared lib loader */
 int _dl_errno                  = 0;         /* We can't use the real errno in ldso */
 size_t _dl_pagesize            = PAGE_SIZE; /* Store the page size for use later */
 /* This global variable is also to communicate with debuggers such as gdb. */
 struct r_debug *_dl_debug_addr = NULL;
-#define _dl_malloc malloc
+
 #include "../ldso/dl-array.c"
 #include "../ldso/dl-debug.c"
 #include LDSO_ELFINTERP
@@ -157,6 +164,7 @@ void *dlopen(const char *libname, int fl
 	struct init_fini_list *tmp, *runp, *runp2, *dep_list;
 	unsigned int nlist, i;
 	struct elf_resolve **init_fini_list;
+	static int _dl_init = 0;
 
 	/* A bit of sanity checking... */
 	if (!(flag & (RTLD_LAZY|RTLD_NOW))) {
@@ -166,6 +174,11 @@ void *dlopen(const char *libname, int fl
 
 	from = (ElfW(Addr)) __builtin_return_address(0);
 
+	if (!_dl_init) {
+		_dl_init++;
+		_dl_malloc_function = malloc;
+		_dl_free_function = free;
+	}
 	/* Cover the trivial case first */
 	if (!libname)
 		return _dl_symbol_tables;
ld.so changes to deal with targets that prepend an underscore to symbol
names.
Index: uClibc/ldso/include/dl-defs.h
===================================================================
--- uClibc.orig/ldso/include/dl-defs.h
+++ uClibc/ldso/include/dl-defs.h
@@ -175,4 +175,10 @@ typedef struct {
 # define DL_MALLOC_ALIGN (__WORDSIZE / 8)
 #endif
 
+#ifdef __UCLIBC_NO_UNDERSCORES__
+#define __C_SYMBOL_PREFIX__ ""
+#else
+#define __C_SYMBOL_PREFIX__ "_"
+#endif
+
 #endif	/* _LD_DEFS_H */
Index: uClibc/ldso/ldso/ldso.c
===================================================================
--- uClibc.orig/ldso/ldso/ldso.c
+++ uClibc/ldso/ldso/ldso.c
@@ -773,7 +773,7 @@ void _dl_get_ready_to_run(struct elf_res
 	 * ld.so.1, so we have to look up each symbol individually.
 	 */
 
-	_dl_envp = (unsigned long *) (intptr_t) _dl_find_hash("__environ", _dl_symbol_tables, NULL, 0);
+	_dl_envp = (unsigned long *) (intptr_t) _dl_find_hash(__C_SYMBOL_PREFIX__ "__environ", _dl_symbol_tables, NULL, 0);
 	if (_dl_envp)
 		*_dl_envp = (unsigned long) envp;
 
@@ -828,8 +828,8 @@ void _dl_get_ready_to_run(struct elf_res
 	}
 
 	/* Find the real malloc function and make ldso functions use that from now on */
-	 _dl_malloc_function = (void* (*)(size_t)) (intptr_t) _dl_find_hash("malloc",
-			 _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT);
+	_dl_malloc_function = (void* (*)(size_t)) (intptr_t) _dl_find_hash(__C_SYMBOL_PREFIX__ "malloc",
+			_dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT);
 
 	/* Notify the debugger that all objects are now mapped in.  */
 	_dl_debug_addr->r_state = RT_CONSISTENT;
Index: uClibc/ldso/libdl/libdl.c
===================================================================
--- uClibc.orig/ldso/libdl/libdl.c
+++ uClibc/ldso/libdl/libdl.c
@@ -446,7 +446,22 @@ void *dlsym(void *vhandle, const char *n
 	ElfW(Addr) from;
 	struct dyn_elf *rpnt;
 	void *ret;
-
+	char tmp_buf[80];
+	char *name2 = tmp_buf;
+#ifndef __UCLIBC_NO_UNDERSCORES__
+	/* Nastiness to support underscore prefixes.  */
+	size_t nlen = strlen (name) + 1;
+	if (nlen + 1 > sizeof (tmp_buf))
+	    name2 = malloc (nlen + 1);
+	if (name2 == 0) {
+	    _dl_error_number = LD_ERROR_MMAP_FAILED;
+	    return 0;
+	}
+	name2[0] = '_';
+	memcpy (name2 + 1, name, nlen);
+#else
+	const char *name2 = name;
+#endif
 	handle = (struct dyn_elf *) vhandle;
 
 	/* First of all verify that we have a real handle
@@ -460,7 +475,8 @@ void *dlsym(void *vhandle, const char *n
 				break;
 		if (!rpnt) {
 			_dl_error_number = LD_BAD_HANDLE;
-			return NULL;
+			ret = NULL;
+			goto out;
 		}
 	} else if (handle == RTLD_NEXT) {
 		/*
@@ -484,13 +500,18 @@ void *dlsym(void *vhandle, const char *n
 	tpnt = NULL;
 	if (handle == _dl_symbol_tables)
 	   tpnt = handle->dyn; /* Only search RTLD_GLOBAL objs if global object */
-	ret = _dl_find_hash((char*)name, handle, tpnt, 0);
+	ret = _dl_find_hash(name2, handle, tpnt, 0);
 
 	/*
 	 * Nothing found.
 	 */
 	if (!ret)
 		_dl_error_number = LD_NO_SYMBOL;
+out:
+#ifndef __UCLIBC_NO_UNDERSCORES__
+	if (name2 != tmp_buf)
+		free (name2);
+#endif
 	return ret;
 }
 
FD-PIC changes: Add a new _dl_find_hash_mod function which is just like
_dl_find_hash, but also gives the caller a pointer to the module where
the symbol was found.  Introduce ELF_RTYPE_CLASS_DLSYM for lookups from
libdl.

Index: uClibc/ldso/include/dl-defs.h
===================================================================
--- uClibc.orig/ldso/include/dl-defs.h
+++ uClibc/ldso/include/dl-defs.h
@@ -181,4 +181,11 @@ typedef struct {
 #define __C_SYMBOL_PREFIX__ "_"
 #endif
 
+/* Define this if you want to modify the VALUE returned by
+   _dl_find_hash for this reloc TYPE.  TPNT is the module in which the
+   matching SYM was found.  */
+#ifndef DL_FIND_HASH_VALUE
+# define DL_FIND_HASH_VALUE(TPNT, TYPE, SYM) (DL_RELOC_ADDR ((SYM)->st_value, (TPNT)->loadaddr))
+#endif
+
 #endif	/* _LD_DEFS_H */
Index: uClibc/ldso/include/dl-elf.h
===================================================================
--- uClibc.orig/ldso/include/dl-elf.h
+++ uClibc/ldso/include/dl-elf.h
@@ -183,6 +183,11 @@ void __dl_parse_dynamic_info(ElfW(Dyn) *
 #endif
 #define ELF_RTYPE_CLASS_PLT	(0x1)
 
+/* dlsym() calls _dl_find_hash with this value, that enables
+   DL_FIND_HASH_VALUE to return something different than the symbol
+   itself, e.g., a function descriptor.  */
+#define ELF_RTYPE_CLASS_DLSYM 0x80000000
+
 
 /* Convert between the Linux flags for page protections and the
    ones specified in the ELF standard. */
Index: uClibc/ldso/include/dl-hash.h
===================================================================
--- uClibc.orig/ldso/include/dl-hash.h
+++ uClibc/ldso/include/dl-hash.h
@@ -105,8 +105,22 @@ extern struct elf_resolve * _dl_add_elf_
 	DL_LOADADDR_TYPE loadaddr, unsigned long * dynamic_info,
 	unsigned long dynamic_addr, unsigned long dynamic_size);
 
-extern char * _dl_find_hash(const char * name, struct dyn_elf * rpnt1,
-			    struct elf_resolve *mytpnt, int type_class);
+extern char * _dl_lookup_hash(const char * name, struct dyn_elf * rpnt,
+			      struct elf_resolve *mytpnt, int type_class,
+#ifdef __FDPIC__
+			      struct elf_resolve **tpntp
+#endif
+			      );
+
+static __always_inline char *_dl_find_hash(const char *name, struct dyn_elf *rpnt,
+					   struct elf_resolve *mytpnt, int type_class)
+{
+#ifdef __FDPIC__
+	return _dl_lookup_hash(name, rpnt, mytpnt, type_class, NULL);
+#else
+	return _dl_lookup_hash(name, rpnt, mytpnt, type_class);
+#endif
+}
 
 extern int _dl_linux_dynamic_link(void);
 
Index: uClibc/ldso/ldso/dl-hash.c
===================================================================
--- uClibc.orig/ldso/ldso/dl-hash.c
+++ uClibc/ldso/ldso/dl-hash.c
@@ -257,7 +257,12 @@ _dl_lookup_sysv_hash(struct elf_resolve 
  * This function resolves externals, and this is either called when we process
  * relocations or when we call an entry in the PLT table for the first time.
  */
-char *_dl_find_hash(const char *name, struct dyn_elf *rpnt, struct elf_resolve *mytpnt, int type_class)
+char *_dl_lookup_hash(const char *name, struct dyn_elf *rpnt,
+		      struct elf_resolve *mytpnt, int type_class,
+#ifdef __FDPIC__
+		      struct elf_resolve **tpntp
+#endif
+		      )
 {
 	struct elf_resolve *tpnt = NULL;
 	ElfW(Sym) *symtab;
@@ -265,7 +270,8 @@ char *_dl_find_hash(const char *name, st
 	unsigned long elf_hash_number = 0xffffffff;
 	const ElfW(Sym) *sym = NULL;
 
-	char *weak_result = NULL;
+	const ElfW(Sym) *weak_sym = 0;
+	struct elf_resolve *weak_tpnt = 0;
 
 #ifdef __LDSO_GNU_HASH_SUPPORT__
 	unsigned long gnu_hash_number = _dl_gnu_hash((const unsigned char *)name);
@@ -326,15 +332,32 @@ char *_dl_find_hash(const char *name, st
 #if 0
 /* Perhaps we should support old style weak symbol handling
  * per what glibc does when you export LD_DYNAMIC_WEAK */
-				if (!weak_result)
-					weak_result = (char *) DL_RELOC_ADDR(tpnt->loadaddr, sym->st_value);
+				if (!weak_sym) {
+					weak_tpnt = tpnt;
+					weak_sym = sym;
+				}
 				break;
 #endif
 			case STB_GLOBAL:
-				return (char*) DL_RELOC_ADDR(tpnt->loadaddr, sym->st_value);
+#ifdef __FDPIC__
+				if (tpntp)
+					*tpntp = tpnt;
+#endif
+				return DL_FIND_HASH_VALUE (tpnt, type_class, sym);
 			default:	/* Local symbols not handled here */
 				break;
 		}
 	}
-	return weak_result;
+	if (weak_sym) {
+#ifdef __FDPIC__
+		if (tpntp)
+			*tpntp = weak_tpnt;
+#endif
+		return DL_FIND_HASH_VALUE (weak_tpnt, type_class, weak_sym);
+	}
+#ifdef __FDPIC__
+	if (tpntp)
+		*tpntp = NULL;
+#endif
+	return NULL;
 }
Index: uClibc/ldso/libdl/libdl.c
===================================================================
--- uClibc.orig/ldso/libdl/libdl.c
+++ uClibc/ldso/libdl/libdl.c
@@ -500,7 +500,7 @@ void *dlsym(void *vhandle, const char *n
 	tpnt = NULL;
 	if (handle == _dl_symbol_tables)
 	   tpnt = handle->dyn; /* Only search RTLD_GLOBAL objs if global object */
-	ret = _dl_find_hash(name2, handle, tpnt, 0);
+	ret = _dl_find_hash(name2, handle, tpnt, ELF_RTYPE_CLASS_DLSYM);
 
 	/*
 	 * Nothing found.
Add a hash table for function descriptors on FD-PIC targets.
Index: uClibc/ldso/include/dl-hash.h
===================================================================
--- uClibc.orig/ldso/include/dl-hash.h
+++ uClibc/ldso/include/dl-hash.h
@@ -89,6 +89,13 @@ struct elf_resolve {
    * we don't have to calculate it every time, which requires a divide */
   unsigned long data_words;
 #endif
+
+#if defined __FRV_FDPIC__ || defined __BFIN_FDPIC__
+  /* Every loaded module holds a hashtable of function descriptors of
+     functions defined in it, such that it's easy to release the
+     memory when the module is dlclose()d.  */
+  struct funcdesc_ht *funcdesc_ht;
+#endif
 };
 
 #define RELOCS_DONE	    0x000001
A couple more target macros for ld.so to deal with FD-PIC support.  We need
special code to compute the initial got and dpnt, and we need to pass extra
arguments to _dl_get_ready_to_run.

Index: uClibc/ldso/include/ldso.h
===================================================================
--- uClibc.orig/ldso/include/ldso.h
+++ uClibc/ldso/include/ldso.h
@@ -106,7 +106,17 @@ extern void _dl_unsetenv(const char *sym
 extern char *_dl_strdup(const char *string);
 extern void _dl_dprintf(int, const char *, ...);
 
+#ifndef DL_GET_READY_TO_RUN_EXTRA_PARMS
+# define DL_GET_READY_TO_RUN_EXTRA_PARMS
+#endif
+#ifndef DL_GET_READY_TO_RUN_EXTRA_ARGS
+# define DL_GET_READY_TO_RUN_EXTRA_ARGS
+#endif
+
 extern void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr,
-                                 ElfW(auxv_t) auxvt[AT_EGID + 1], char **envp, char **argv);
+		ElfW(auxv_t) auxvt[AT_EGID + 1], char **envp, char **argv
+		DL_GET_READY_TO_RUN_EXTRA_PARMS);
+
+#include <dl-inlines.h>
 
 #endif /* _LDSO_H_ */
Index: uClibc/ldso/ldso/dl-startup.c
===================================================================
--- uClibc.orig/ldso/ldso/dl-startup.c
+++ uClibc/ldso/ldso/dl-startup.c
@@ -192,8 +192,11 @@ DL_START(unsigned long args)
 	 * we can take advantage of the magic offset register, if we
 	 * happen to know what that is for this architecture.  If not,
 	 * we can always read stuff out of the ELF file to find it... */
-	got = elf_machine_dynamic();
-	dpnt = (ElfW(Dyn) *) DL_RELOC_ADDR(load_addr, got);
+	DL_BOOT_COMPUTE_GOT(got);
+
+	/* Now, finally, fix up the location of the dynamic stuff */
+	DL_BOOT_COMPUTE_DYN (dpnt, got, load_addr);
+
 	SEND_EARLY_STDERR_DEBUG("First Dynamic section entry=");
 	SEND_ADDRESS_STDERR_DEBUG(dpnt, 1);
 	_dl_memset(tpnt, 0, sizeof(struct elf_resolve));
@@ -304,7 +307,8 @@ DL_START(unsigned long args)
 
 	__rtld_stack_end = (void *)(argv - 1);
 
-	_dl_get_ready_to_run(tpnt, load_addr, auxvt, envp, argv);
+	_dl_get_ready_to_run(tpnt, load_addr, auxvt, envp, argv
+			     DL_GET_READY_TO_RUN_EXTRA_ARGS);
 
 
 	/* Transfer control to the application.  */
Index: uClibc/ldso/ldso/ldso.c
===================================================================
--- uClibc.orig/ldso/ldso/ldso.c
+++ uClibc/ldso/ldso/ldso.c
@@ -131,8 +131,9 @@ static void __attribute__ ((destructor))
 }
 
 void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr,
-                          ElfW(auxv_t) auxvt[AT_EGID + 1], char **envp,
-                          char **argv)
+			  ElfW(auxv_t) auxvt[AT_EGID + 1], char **envp,
+			  char **argv
+			  DL_GET_READY_TO_RUN_EXTRA_PARMS)
 {
 	ElfW(Phdr) *ppnt;
 	ElfW(Dyn) *dpnt;
@@ -313,7 +314,7 @@ void _dl_get_ready_to_run(struct elf_res
 			/* OK, we have what we need - slip this one into the list. */
 			app_tpnt = _dl_add_elf_hash_table(_dl_progname, app_tpnt->loadaddr,
 					app_tpnt->dynamic_info,
-					DL_RELOC_ADDR(app_tpnt->loadaddr, ppnt->p_vaddr),
+					(unsigned long) DL_RELOC_ADDR(app_tpnt->loadaddr, ppnt->p_vaddr),
 					ppnt->p_filesz);
 			_dl_loaded_modules->libtype = elf_executable;
 			_dl_loaded_modules->ppnt = (ElfW(Phdr) *) auxvt[AT_PHDR].a_un.a_val;
@@ -345,7 +346,7 @@ void _dl_get_ready_to_run(struct elf_res
 			if (ptmp != _dl_ldsopath)
 				*ptmp = '\0';
 
-			_dl_debug_early("Lib Loader: (%x) %s\n", DL_LOADADDR_BASE(tpnt->loadaddr), tpnt->libname);
+			_dl_debug_early("Lib Loader: (%x) %s\n", (unsigned) DL_LOADADDR_BASE(tpnt->loadaddr), tpnt->libname);
 		}
 	}
 	app_tpnt->relro_addr = relro_addr;
Index: uClibc/ldso/include/dl-defs.h
===================================================================
--- uClibc.orig/ldso/include/dl-defs.h
+++ uClibc/ldso/include/dl-defs.h
@@ -95,6 +95,20 @@ typedef struct {
 	((LOADADDR) + (ADDR))
 #endif
 
+/* Initialize the location of the dynamic addr.  This is only called
+ * from DL_START, so additional arguments passed to it may be referenced.  */
+#ifndef DL_BOOT_COMPUTE_DYN
+#define DL_BOOT_COMPUTE_DYN(DPNT, GOT, LOAD_ADDR) \
+    ((DPNT) = ((ElfW(Dyn) *) DL_RELOC_ADDR(load_addr, got)))
+#endif
+
+/* Initialize the location of the global offset table.  This is only called
+ * from DL_START, so additional arguments passed to it may be referenced.  */
+#ifndef DL_BOOT_COMPUTE_GOT
+#define DL_BOOT_COMPUTE_GOT(GOT) \
+    ((GOT) = elf_machine_dynamic())
+#endif
+
 /* Initialize a LOADADDR representing the loader itself.  It's only
  * called from DL_BOOT, so additional arguments passed to it may be
  * referenced.
The final parts of ld.so FD-PIC support.  These are mostly the changes
necessary to deal with loading the libraries into memory.  A couple new
target macros are defined for this purpose.

Index: uClibc/ldso/include/dl-defs.h
===================================================================
--- uClibc.orig/ldso/include/dl-defs.h
+++ uClibc/ldso/include/dl-defs.h
@@ -110,7 +110,7 @@ typedef struct {
 #endif
 
 /* Initialize a LOADADDR representing the loader itself.  It's only
- * called from DL_BOOT, so additional arguments passed to it may be
+ * called from DL_START, so additional arguments passed to it may be
  * referenced.
  */
 #ifndef DL_INIT_LOADADDR_BOOT
@@ -144,6 +144,12 @@ typedef struct {
 	((LOADADDR) = (DL_LOADADDR_TYPE)(BASEADDR))
 #endif
 
+/* Update LOADADDR with information about PHDR, just mapped to the
+   given ADDR.  */
+#ifndef DL_INIT_LOADADDR_HDR
+# define DL_INIT_LOADADDR_HDR(LOADADDR, ADDR, PHDR) /* Do nothing.  */
+#endif
+
 /* Convert a DL_LOADADDR_TYPE to an identifying pointer.  Used mostly
  * for debugging.
  */
@@ -166,6 +172,13 @@ typedef struct {
 	 && (!(TFROM) || (TFROM)->loadaddr < (TPNT)->loadaddr))
 #endif
 
+/* This is called from dladdr() to give targets that use function descriptors
+ * a chance to map a function descriptor's address to the function's entry
+ * point before trying to find in which library it's defined.  */
+#ifndef DL_LOOKUP_ADDRESS
+#define DL_LOOKUP_ADDRESS(ADDRESS) (ADDRESS)
+#endif
+
 /* Use this macro to convert a pointer to a function's entry point to
  * a pointer to function.  The pointer is assumed to have already been
  * relocated.  LOADADDR is passed because it may contain additional
@@ -202,4 +215,40 @@ typedef struct {
 # define DL_FIND_HASH_VALUE(TPNT, TYPE, SYM) (DL_RELOC_ADDR ((SYM)->st_value, (TPNT)->loadaddr))
 #endif
 
+/* Unmap all previously-mapped segments accumulated in LOADADDR.
+   Generally used when an error occurs during loading.  */
+#ifndef DL_LOADADDR_UNMAP
+# define DL_LOADADDR_UNMAP(LOADADDR, LEN) \
+  _dl_munmap((char *) (LOADADDR), (LEN))
+#endif
+
+/* Similar to DL_LOADADDR_UNMAP, but used for libraries that have been
+   dlopen()ed successfully, when they're dlclose()d.  */
+#ifndef DL_LIB_UNMAP
+# define DL_LIB_UNMAP(LIB, LEN) (DL_LOADADDR_UNMAP ((LIB)->loadaddr, (LEN)))
+#endif
+
+/* Define this to verify that a library named LIBNAME, whose ELF
+   headers are pointed to by EPNT, is suitable for dynamic linking.
+   If it is not, print an error message (optional) and return NULL.
+   If the library can have its segments relocated independently,
+   arrange for PICLIB to be set to 2.  If all segments have to be
+   relocated by the same amount, set it to 1.  If it has to be loaded
+   at physical addresses as specified in the program headers, set it
+   to 0.  A reasonable (?) guess for PICLIB will already be in place,
+   so it is safe to do nothing here.  */
+#ifndef DL_CHECK_LIB_TYPE
+# define DL_CHECK_LIB_TYPE(EPNT, PICLIB, PROGNAME, LIBNAME) (void)0
+#endif
+
+/* Define this if you have special segment.  */
+#ifndef DL_IS_SPECIAL_SEGMENT
+# define DL_IS_SPECIAL_SEGMENT(EPNT, PPNT) 0
+#endif
+
+/* Define this if you want to use special method to map the segment.  */
+#ifndef DL_MAP_SEGMENT
+# define DL_MAP_SEGMENT(EPNT, PPNT, INFILE, FLAGS) 0
+#endif
+
 #endif	/* _LD_DEFS_H */
Index: uClibc/ldso/ldso/dl-elf.c
===================================================================
--- uClibc.orig/ldso/ldso/dl-elf.c
+++ uClibc/ldso/ldso/dl-elf.c
@@ -354,6 +354,7 @@ struct elf_resolve *_dl_load_elf_shared_
 	DL_LOADADDR_TYPE lib_loadaddr;
 	DL_INIT_LOADADDR_EXTRA_DECLS
 
+	libaddr = 0;
 	infile = _dl_open(libname, O_RDONLY, 0);
 	if (infile < 0) {
 		_dl_internal_error_number = LD_ERROR_NOFILE;
@@ -449,6 +450,8 @@ struct elf_resolve *_dl_load_elf_shared_
 		ppnt++;
 	}
 
+	DL_CHECK_LIB_TYPE (epnt, piclib, _dl_progname, libname);
+
 	maxvma = (maxvma + ADDR_ALIGN) & ~ADDR_ALIGN;
 	minvma = minvma & ~0xffffU;
 
@@ -456,17 +459,19 @@ struct elf_resolve *_dl_load_elf_shared_
 	if (!piclib)
 		flags |= MAP_FIXED;
 
-	status = (char *) _dl_mmap((char *) (piclib ? 0 : minvma),
-			maxvma - minvma, PROT_NONE, flags | MAP_ANONYMOUS, -1, 0);
-	if (_dl_mmap_check_error(status)) {
-		_dl_dprintf(2, "%s:%i: can't map '%s'\n", _dl_progname, __LINE__, libname);
-		_dl_internal_error_number = LD_ERROR_MMAP_FAILED;
-		_dl_close(infile);
-		_dl_munmap(header, _dl_pagesize);
-		return NULL;
+	if (piclib == 0 || piclib == 1) {
+		status = (char *) _dl_mmap((char *) (piclib ? 0 : minvma),
+				maxvma - minvma, PROT_NONE, flags | MAP_ANONYMOUS, -1, 0);
+		if (_dl_mmap_check_error(status)) {
+			_dl_dprintf(2, "%s:%i: can't map '%s'\n", _dl_progname, __LINE__, libname);
+			_dl_internal_error_number = LD_ERROR_MMAP_FAILED;
+			_dl_close(infile);
+			_dl_munmap(header, _dl_pagesize);
+			return NULL;
+		}
+		libaddr = (unsigned long) status;
+		flags |= MAP_FIXED;
 	}
-	libaddr = (unsigned long) status;
-	flags |= MAP_FIXED;
 
 	/* Get the memory to store the library */
 	ppnt = (ElfW(Phdr) *)(intptr_t) & header[epnt->e_phoff];
@@ -474,11 +479,24 @@ struct elf_resolve *_dl_load_elf_shared_
 	DL_INIT_LOADADDR(lib_loadaddr, libaddr, ppnt, epnt->e_phnum);
 
 	for (i = 0; i < epnt->e_phnum; i++) {
+		if (DL_IS_SPECIAL_SEGMENT (epnt, ppnt)) {
+			char *addr;
+
+			addr = DL_MAP_SEGMENT (epnt, ppnt, infile, flags);
+			if (addr == NULL)
+				goto cant_map;
+
+			DL_INIT_LOADADDR_HDR (lib_loadaddr, addr, ppnt);
+			ppnt++;
+			continue;
+		}
 		if (ppnt->p_type == PT_GNU_RELRO) {
 			relro_addr = ppnt->p_vaddr;
 			relro_size = ppnt->p_memsz;
 		}
 		if (ppnt->p_type == PT_LOAD) {
+			char *tryaddr;
+			ssize_t size;
 
 			/* See if this is a PIC library. */
 			if (i == 0 && ppnt->p_vaddr > 0x1000000) {
@@ -489,53 +507,155 @@ struct elf_resolve *_dl_load_elf_shared_
 			if (ppnt->p_flags & PF_W) {
 				unsigned long map_size;
 				char *cpnt;
+				char *piclib2map = 0;
 
-				status = (char *) _dl_mmap((char *) ((piclib ? libaddr : 0) +
-							(ppnt->p_vaddr & PAGE_ALIGN)), (ppnt->p_vaddr & ADDR_ALIGN)
-						+ ppnt->p_filesz, LXFLAGS(ppnt->p_flags), flags, infile,
-						ppnt->p_offset & OFFS_ALIGN);
-
-				if (_dl_mmap_check_error(status)) {
+				if (piclib == 2 &&
+				    /* We might be able to avoid this
+				       call if memsz doesn't require
+				       an additional page, but this
+				       would require mmap to always
+				       return page-aligned addresses
+				       and a whole number of pages
+				       allocated.  Unfortunately on
+				       uClinux may return misaligned
+				       addresses and may allocate
+				       partial pages, so we may end up
+				       doing unnecessary mmap calls.
+
+				       This is what we could do if we
+				       knew mmap would always return
+				       aligned pages:
+
+				    ((ppnt->p_vaddr + ppnt->p_filesz
+				      + ADDR_ALIGN)
+				     & PAGE_ALIGN)
+				    < ppnt->p_vaddr + ppnt->p_memsz)
+
+				       Instead, we have to do this:  */
+				    ppnt->p_filesz < ppnt->p_memsz)
+				  {
+				    piclib2map = (char *)
+				      _dl_mmap(0, (ppnt->p_vaddr & ADDR_ALIGN)
+					       + ppnt->p_memsz,
+					       LXFLAGS(ppnt->p_flags),
+					       flags | MAP_ANONYMOUS, -1, 0);
+				    if (_dl_mmap_check_error(piclib2map))
+				      goto cant_map;
+				    DL_INIT_LOADADDR_HDR
+				      (lib_loadaddr, piclib2map
+				       + (ppnt->p_vaddr & ADDR_ALIGN), ppnt);
+				  }
+
+				tryaddr = piclib == 2 ? piclib2map
+				  : ((char*) (piclib ? libaddr : 0) +
+				     (ppnt->p_vaddr & PAGE_ALIGN));
+
+				size = (ppnt->p_vaddr & ADDR_ALIGN)
+				  + ppnt->p_filesz;
+
+				/* For !MMU, mmap to fixed address will fail.
+				   So instead of desperately call mmap and fail,
+				   we set status to MAP_FAILED to save a call
+				   to mmap ().  */
+#ifndef __ARCH_HAS_MMU__
+				if (piclib2map == 0)
+#endif
+				  status = (char *) _dl_mmap
+				    (tryaddr, size, LXFLAGS(ppnt->p_flags),
+				     flags | (piclib2map ? MAP_FIXED : 0),
+				     infile, ppnt->p_offset & OFFS_ALIGN);
+#ifndef __ARCH_HAS_MMU__
+				else
+				  status = MAP_FAILED;
+#endif
+#ifdef _DL_PREAD
+				if (_dl_mmap_check_error(status) && piclib2map
+				    && (_DL_PREAD (infile, tryaddr, size,
+						   ppnt->p_offset & OFFS_ALIGN)
+					== size))
+				  status = tryaddr;
+#endif
+				if (_dl_mmap_check_error(status)
+				    || (tryaddr && tryaddr != status)) {
+				cant_map:
 					_dl_dprintf(2, "%s:%i: can't map '%s'\n",
 							_dl_progname, __LINE__, libname);
 					_dl_internal_error_number = LD_ERROR_MMAP_FAILED;
-					_dl_munmap((char *) libaddr, maxvma - minvma);
+					DL_LOADADDR_UNMAP (lib_loadaddr, maxvma - minvma);
 					_dl_close(infile);
 					_dl_munmap(header, _dl_pagesize);
 					return NULL;
 				}
 
-				/* Pad the last page with zeroes. */
-				cpnt = (char *) (status + (ppnt->p_vaddr & ADDR_ALIGN) +
-						ppnt->p_filesz);
-				while (((unsigned long) cpnt) & ADDR_ALIGN)
-					*cpnt++ = 0;
-
-				/* I am not quite sure if this is completely
-				 * correct to do or not, but the basic way that
-				 * we handle bss segments is that we mmap
-				 * /dev/zero if there are any pages left over
-				 * that are not mapped as part of the file */
-
-				map_size = (ppnt->p_vaddr + ppnt->p_filesz + ADDR_ALIGN) & PAGE_ALIGN;
-
-				if (map_size < ppnt->p_vaddr + ppnt->p_memsz)
-					status = (char *) _dl_mmap((char *) map_size +
-							(piclib ? libaddr : 0),
-							ppnt->p_vaddr + ppnt->p_memsz - map_size,
-							LXFLAGS(ppnt->p_flags), flags | MAP_ANONYMOUS, -1, 0);
-			} else
-				status = (char *) _dl_mmap((char *) (ppnt->p_vaddr & PAGE_ALIGN)
-						+ (piclib ? libaddr : 0), (ppnt->p_vaddr & ADDR_ALIGN) +
-						ppnt->p_filesz, LXFLAGS(ppnt->p_flags), flags,
-						infile, ppnt->p_offset & OFFS_ALIGN);
-			if (_dl_mmap_check_error(status)) {
-				_dl_dprintf(2, "%s:%i: can't map '%s'\n", _dl_progname, __LINE__, libname);
-				_dl_internal_error_number = LD_ERROR_MMAP_FAILED;
-				_dl_munmap((char *) libaddr, maxvma - minvma);
-				_dl_close(infile);
-				_dl_munmap(header, _dl_pagesize);
-				return NULL;
+				if (! piclib2map)
+				  DL_INIT_LOADADDR_HDR
+				    (lib_loadaddr, status
+				     + (ppnt->p_vaddr & ADDR_ALIGN), ppnt);
+
+				/* Now we want to allocate and
+				   zero-out any data from the end of
+				   the region we mapped in from the
+				   file (filesz) to the end of the
+				   loadable segment (memsz).  We may
+				   need additional pages for memsz,
+				   that we map in below, and we can
+				   count on the kernel to zero them
+				   out, but we have to zero out stuff
+				   in the last page that we mapped in
+				   from the file.  However, we can't
+				   assume to have actually obtained
+				   full pages from the kernel, since
+				   we didn't ask for them, and uClibc
+				   may not give us full pages for
+				   small allocations.  So only zero
+				   out up to memsz or the end of the
+				   page, whichever comes first.  */
+
+				/* CPNT is the beginning of the memsz
+				   portion not backed by filesz.  */
+				cpnt = (char *) (status + size);
+
+				/* MAP_SIZE is the address of the
+				   beginning of the next page.  */
+				map_size = (ppnt->p_vaddr + ppnt->p_filesz
+					    + ADDR_ALIGN) & PAGE_ALIGN;
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+				_dl_memset (cpnt, 0,
+					    MIN (map_size
+						 - (ppnt->p_vaddr
+						    + ppnt->p_filesz),
+						 ppnt->p_memsz
+						 - ppnt->p_filesz));
+
+				if (map_size < ppnt->p_vaddr + ppnt->p_memsz
+				    && !piclib2map) {
+					status = (char *) _dl_mmap(tryaddr = map_size +
+						(char*)(piclib ? libaddr : 0),
+						ppnt->p_vaddr + ppnt->p_memsz - map_size,
+						LXFLAGS(ppnt->p_flags), flags | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+					if (_dl_mmap_check_error(status)
+					    || tryaddr != status)
+						goto cant_map;
+				}
+			} else {
+				status = (char *) _dl_mmap
+				  (tryaddr = piclib == 2 ? 0 :
+				   (char *) (ppnt->p_vaddr & PAGE_ALIGN)
+				   + (piclib ? libaddr : 0),
+				   size = (ppnt->p_vaddr & ADDR_ALIGN) +
+				   ppnt->p_filesz, LXFLAGS(ppnt->p_flags),
+				   flags | (piclib == 2 ? MAP_EXECUTABLE
+					    | MAP_DENYWRITE : 0),
+				   infile, ppnt->p_offset & OFFS_ALIGN);
+				if (_dl_mmap_check_error(status)
+				    || (tryaddr && tryaddr != status))
+				  goto cant_map;
+				DL_INIT_LOADADDR_HDR
+				  (lib_loadaddr, status
+				   + (ppnt->p_vaddr & ADDR_ALIGN), ppnt);
 			}
 
 			/* if (libaddr == 0 && piclib) {
Index: uClibc/ldso/libdl/libdl.c
===================================================================
--- uClibc.orig/ldso/libdl/libdl.c
+++ uClibc/ldso/libdl/libdl.c
@@ -587,7 +587,7 @@ static int do_dlclose(void *vhandle, int
 				if (end < ppnt->p_vaddr + ppnt->p_memsz)
 					end = ppnt->p_vaddr + ppnt->p_memsz;
 			}
-			_dl_munmap((void*)tpnt->loadaddr, end);
+			DL_LIB_UNMAP (tpnt, end);
 			/* Free elements in RTLD_LOCAL scope list */ 
 			for (runp = tpnt->rtld_local; runp; runp = tmp) {
 				tmp = runp->next;
@@ -713,6 +713,8 @@ int dladdr(const void *__address, Dl_inf
 
 	_dl_if_debug_print("__address: %p  __info: %p\n", __address, __info);
 
+	__address = DL_LOOKUP_ADDRESS (__address);
+
 	for (rpnt = _dl_loaded_modules; rpnt; rpnt = rpnt->next) {
 		struct elf_resolve *tpnt;
 
_______________________________________________
uClibc mailing list
[email protected]
http://busybox.net/cgi-bin/mailman/listinfo/uclibc

Reply via email to