FD-PIC patches for uClibc

Carmelo Amoroso carmelo73 at gmail.com
Sat Dec 1 07:53:25 UTC 2007


Bernd Schmidt wrote:
> 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.
Hi Bernd,
with the solution proposed into the nptl branch, you can keep the caller
of _dl_find_hash always the same. Where the extra tpnt parameter is not required,
independently from TLS or FDPIC code, like into ldso.c to lookup
some function (malloc), you can safely pass NULL for it.
In all other cases, you need simply to pass the extra tpnt, and the _dl_find_hash
wrapper will pass it to the real function _dl_lookup_hash accordingly.
This has been discussed in the past with Jocke to allow a simpler merge among
trunk and branch.

> 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.
> 

And this is true. You have always the extra argument passed using the _dl_find_hash
as wrapper. The ifdef into the signature is present only
into the _dl_lookup_hash. The only issue is that this case is actually into the branch
and yet not into the trunk... but can be added now that it needs.

> 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
Hopefully I could commit them next week... not access to svn right now.

regards,
Carmelo
> 
> 
> ------------------------------------------------------------------------
> 
> 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;
>  





More information about the uClibc mailing list