These are the FD-PIC patches necessary to get ld.so working on the
Blackfin. These were originally written by Alex Oliva for the FRV and
updated to work with current uClibc sources by myself.
I'm posting these for review; I intend to check them in in a few days if
there are no objections.
Note that I can't really test these yet since upstream uClibc isn't
entirely working yet on the Blackfin, but at least for ld.so it's now
near-identical to the working code in our repository.
Bernd
Subject: _dl_malloc/_dl_free changes for FD-PIC.
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;
Subject: Deal with underscore prefixes in ld.so
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;
}
Subject: FD-PIC: Symbol lookup changes.
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
@@ -107,6 +107,9 @@ extern struct elf_resolve * _dl_add_elf_
extern char * _dl_find_hash(const char * name, struct dyn_elf * rpnt1,
struct elf_resolve *mytpnt, int type_class);
+extern char * _dl_find_hash_mod(const char * name, struct dyn_elf * rpnt,
+ struct elf_resolve *mytpnt, int type_class,
+ struct elf_resolve **tpntp);
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,9 @@ _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_find_hash(const char *name, struct dyn_elf *rpnt,
+ struct elf_resolve *mytpnt, int type_class,
+ struct elf_resolve **tpntp)
{
struct elf_resolve *tpnt = NULL;
ElfW(Sym) *symtab;
@@ -265,7 +267,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 +329,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);
+ if (tpntp)
+ *tpntp = tpnt;
+ return DL_FIND_HASH_VALUE (tpnt, type_class, sym);
default: /* Local symbols not handled here */
break;
}
}
- return weak_result;
+ if (weak_sym) {
+ if (tpntp)
+ *tpntp = weak_tpnt;
+ return DL_FIND_HASH_VALUE (weak_tpnt, type_class, weak_sym);
+ }
+ if (tpntp)
+ *tpntp = NULL;
+ return NULL;
+}
+
+char *_dl_find_hash(const char *name, struct dyn_elf *rpnt,
+ struct elf_resolve *mytpnt, int type_class)
+{
+ return _dl_find_hash_mod(name, rpnt, mytpnt, type_class, NULL);
}
Index: uClibc/ldso/libdl/libdl.c
===================================================================
--- uClibc.orig/ldso/libdl/libdl.c
+++ uClibc/ldso/libdl/libdl.c
@@ -497,7 +497,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.
Subject: FD-PIC: Add a function desciptor hash table.
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
Subject: FD-PIC: several more target macros.
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,19 @@ 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... */
+#if defined(DL_BOOT_COMPUTE_GOT)
+ DL_BOOT_COMPUTE_GOT(got);
+#else
got = elf_machine_dynamic();
+#endif
+
+ /* Now, finally, fix up the location of the dynamic stuff */
+#ifdef DL_BOOT_COMPUTE_DYN
+ DL_BOOT_COMPUTE_DYN (dpnt, got, load_addr);
+#else
dpnt = (ElfW(Dyn) *) DL_RELOC_ADDR(load_addr, got);
+#endif
+
SEND_EARLY_STDERR_DEBUG("First Dynamic section entry=");
SEND_ADDRESS_STDERR_DEBUG(dpnt, 1);
_dl_memset(tpnt, 0, sizeof(struct elf_resolve));
@@ -304,7 +315,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
@@ -130,9 +130,10 @@ 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)
+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
+ 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;
Subject: FD-PIC: DL_BOOT to DL_START name change
Cope with a name change: DL_BOOT got renamed to DL_START.
Index: uClibc/ldso/ldso/bfin/dl-startup.h
===================================================================
--- uClibc.orig/ldso/ldso/bfin/dl-startup.h
+++ uClibc/ldso/ldso/bfin/dl-startup.h
@@ -86,7 +86,8 @@ __asm__(
" .size __dl_boot,.-__dl_boot\n"
);
-#define DL_BOOT(X) \
+#undef DL_START
+#define DL_START(X) \
static void __attribute__ ((used)) \
_dl_start (Elf32_Addr dl_boot_got_pointer, \
struct elf32_fdpic_loadmap *dl_boot_progmap, \
Subject: FD-PIC: final part
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
@@ -130,6 +130,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.
*/
@@ -188,4 +194,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
@@ -584,7 +584,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;
@@ -710,6 +710,10 @@ int dladdr(const void *__address, Dl_inf
_dl_if_debug_print("__address: %p __info: %p\n", __address, __info);
+#ifdef DL_LOOKUP_ADDRESS
+ __address = DL_LOOKUP_ADDRESS (__address);
+#endif
+
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