#include <ldso.h>
#include <stdio.h>
-#include <string.h> /* Needed for 'strstr' prototype' */
+#include <string.h>
#include <stdbool.h>
+#include <bits/uClibc_mutex.h>
#ifdef __UCLIBC_HAS_TLS__
#include <tls.h>
extern void _dl_add_to_slotinfo(struct link_map *l);
#endif
+/* TODO: get rid of global lock and use more finegrained locking, or
+ * perhaps RCU for the global structures */
+__UCLIBC_MUTEX_STATIC(_dl_mutex, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP);
+
#ifdef SHARED
# if defined(USE_TLS) && USE_TLS
# include <dl-tls.h>
/* When libdl is loaded as a shared library, we need to load in
* and use a pile of symbols from ldso... */
-
-extern struct elf_resolve * _dl_load_shared_library(int, struct dyn_elf **,
+#include <dl-elf.h>
+#if 0
+extern struct elf_resolve * _dl_load_shared_library(unsigned, struct dyn_elf **,
struct elf_resolve *, char *, int);
extern int _dl_fixup(struct dyn_elf *rpnt, struct r_scope_elem *scope, int lazy);
extern void _dl_protect_relro(struct elf_resolve * tpnt);
+#endif
extern int _dl_errno;
extern struct dyn_elf *_dl_symbol_tables;
extern struct dyn_elf *_dl_handles;
const char *_dl_progname = ""; /* Program name */
void *(*_dl_malloc_function)(size_t);
void (*_dl_free_function) (void *p);
+#ifdef __LDSO_LD_LIBRARY_PATH__
char *_dl_library_path = NULL; /* Where we look for libraries */
-char *_dl_ldsopath = NULL; /* Location of the shared lib loader */
+#endif
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. */
# define _dl_if_debug_print(fmt, args...) \
do { \
if (_dl_debug) \
- fprintf(stderr, "%s():%i: " fmt, __FUNCTION__, __LINE__, ## args); \
+ fprintf(stderr, "%s():%i: " fmt, __func__, __LINE__, ## args); \
} while (0)
#else
# define _dl_if_debug_print(fmt, args...)
}
#endif
-void dl_cleanup(void) __attribute__ ((destructor));
+#ifndef __LDSO_NO_CLEANUP__
+void dl_cleanup(void) attribute_hidden __attribute__ ((destructor));
void dl_cleanup(void)
{
struct dyn_elf *h, *n;
do_dlclose(h, 1);
}
}
+#endif
static ptrdiff_t _dl_build_local_scope (struct elf_resolve **list,
struct elf_resolve *map)
return p - list;
}
-void *dlopen(const char *libname, int flag)
+static void *do_dlopen(const char *libname, int flag)
{
struct elf_resolve *tpnt, *tfrom;
struct dyn_elf *dyn_chain, *rpnt = NULL, *dyn_ptr, *relro_ptr, *handle;
#endif
/* A bit of sanity checking... */
- if (!(flag & (RTLD_LAZY|RTLD_NOW))) {
+ if (!(flag & (RTLD_LAZY|RTLD_NOW|RTLD_NOLOAD))) {
_dl_error_number = LD_BAD_HANDLE;
return NULL;
}
if (getenv("LD_BIND_NOW"))
now_flag = RTLD_NOW;
-#ifndef SHARED
+#if !defined SHARED && defined __LDSO_LIBRARY_PATH__
/* When statically linked, the _dl_library_path is not yet initialized */
_dl_library_path = getenv("LD_LIBRARY_PATH");
#endif
/* Try to load the specified library */
_dl_if_debug_print("Trying to dlopen '%s', RTLD_GLOBAL:%d RTLD_NOW:%d\n",
(char*)libname, (flag & RTLD_GLOBAL ? 1:0), (now_flag & RTLD_NOW ? 1:0));
- tpnt = _dl_load_shared_library(0, &rpnt, tfrom, (char*)libname, 0);
+ tpnt = _dl_load_shared_library((flag & RTLD_NOLOAD) ? DL_RESOLVE_NOLOAD : 0,
+ &rpnt, tfrom, (char*)libname, 0);
if (tpnt == NULL) {
_dl_unmap_cache();
return NULL;
}
dyn_chain = (struct dyn_elf *) malloc(sizeof(struct dyn_elf));
- _dl_memset(dyn_chain, 0, sizeof(struct dyn_elf));
+ memset(dyn_chain, 0, sizeof(struct dyn_elf));
dyn_chain->dyn = tpnt;
tpnt->rtld_flags |= (flag & RTLD_GLOBAL);
/* This list is for dlsym() and relocation */
dyn_ptr->next = (struct dyn_elf *) malloc(sizeof(struct dyn_elf));
- _dl_memset (dyn_ptr->next, 0, sizeof (struct dyn_elf));
+ memset (dyn_ptr->next, 0, sizeof (struct dyn_elf));
dyn_ptr = dyn_ptr->next;
dyn_ptr->dyn = tpnt1;
/* Used to record RTLD_LOCAL scope */
* Now we go through and look for REL and RELA records that indicate fixups
* to the GOT tables. We need to do this in reverse order so that COPY
* directives work correctly */
+
+ /* Get the tail of the list */
+ for (ls = &_dl_loaded_modules->symbol_scope; ls && ls->next; ls = ls->next);
+
+ /* Extend the global scope by adding the local scope of the dlopened DSO. */
+ ls->next = &dyn_chain->dyn->symbol_scope;
#ifdef __mips__
/*
* Relocation of the GOT entries for MIPS have to be done
*/
_dl_perform_mips_global_got_relocations(tpnt, !now_flag);
#endif
- /* Get the tail of the list */
- for (ls = &_dl_loaded_modules->symbol_scope; ls && ls->next; ls = ls->next);
-
- /* Extend the global scope by adding the local scope of the dlopened DSO. */
- ls->next = &dyn_chain->dyn->symbol_scope;
if (_dl_fixup(dyn_chain, &_dl_loaded_modules->symbol_scope, now_flag))
goto oops;
return NULL;
}
-void *dlsym(void *vhandle, const char *name)
+void *dlopen(const char *libname, int flag)
+{
+ void *ret;
+
+ __UCLIBC_MUTEX_CONDITIONAL_LOCK(_dl_mutex, 1);
+ ret = do_dlopen(libname, flag);
+ __UCLIBC_MUTEX_CONDITIONAL_UNLOCK(_dl_mutex, 1);
+
+ return ret;
+}
+
+static void *do_dlsym(void *vhandle, const char *name, void *caller_address)
{
struct elf_resolve *tpnt, *tfrom;
struct dyn_elf *handle;
ElfW(Addr) from;
struct dyn_elf *rpnt;
void *ret;
- struct elf_resolve *tls_tpnt = NULL;
struct symbol_ref sym_ref = { NULL, NULL };
/* Nastiness to support underscore prefixes. */
#ifdef __UCLIBC_UNDERSCORES__
* dynamic loader itself, as it doesn't know
* how to properly treat it.
*/
- from = (ElfW(Addr)) __builtin_return_address(0);
+ from = (ElfW(Addr)) caller_address;
tfrom = NULL;
for (rpnt = _dl_symbol_tables; rpnt; rpnt = rpnt->next) {
tpnt = NULL;
if (handle == _dl_symbol_tables)
tpnt = handle->dyn; /* Only search RTLD_GLOBAL objs if global object */
- ret = _dl_find_hash(name2, &handle->dyn->symbol_scope, tpnt, 0, &sym_ref);
+ ret = _dl_find_hash(name2, &handle->dyn->symbol_scope, tpnt, ELF_RTYPE_CLASS_DLSYM, &sym_ref);
#if defined(USE_TLS) && USE_TLS && defined SHARED
if (sym_ref.sym && (ELF_ST_TYPE(sym_ref.sym->st_info) == STT_TLS) && (sym_ref.tpnt)) {
/* The found symbol is a thread-local storage variable.
- Return the address for to the current thread. */
+ Return its address for the current thread. */
ret = _dl_tls_symaddr ((struct link_map *)sym_ref.tpnt, (Elf32_Addr)ret);
}
#endif
return ret;
}
+void *dlsym(void *vhandle, const char *name)
+{
+ void *ret;
+
+ __UCLIBC_MUTEX_CONDITIONAL_LOCK(_dl_mutex, 1);
+ ret = do_dlsym(vhandle, name, __builtin_return_address(0));
+ __UCLIBC_MUTEX_CONDITIONAL_UNLOCK(_dl_mutex, 1);
+
+ return ret;
+}
+
#if 0
void *dlvsym(void *vhandle, const char *name, const char *version)
{
int (*dl_elf_fini) (void);
void (*dl_brk) (void);
struct dyn_elf *handle;
- unsigned int end;
+ unsigned int end = 0, start = 0xffffffff;
unsigned int i, j;
- struct r_scope_elem *ls;
+ struct r_scope_elem *ls, *ls_next = NULL;
+ struct elf_resolve **handle_rlist;
+
#if defined(USE_TLS) && USE_TLS
bool any_tls = false;
size_t tls_free_start = NO_TLS_OFFSET;
free(handle);
return 0;
}
+
+ /* Store the handle's local scope array for later removal */
+ handle_rlist = handle->dyn->symbol_scope.r_list;
+
+ /* Store references to the local scope entries for later removal */
+ for (ls = &_dl_loaded_modules->symbol_scope; ls && ls->next; ls = ls->next)
+ if (ls->next->r_list[0] == handle->dyn) {
+ break;
+ }
+ /* ls points to the previous local symbol scope */
+ if(ls && ls->next)
+ ls_next = ls->next->next;
+
/* OK, this is a valid handle - now close out the file */
for (j = 0; j < handle->init_fini.nlist; ++j) {
tpnt = handle->init_fini.init_fini[j];
i < tpnt->n_phent; ppnt++, i++) {
if (ppnt->p_type != PT_LOAD)
continue;
+ if (ppnt->p_vaddr < start)
+ start = ppnt->p_vaddr;
if (end < ppnt->p_vaddr + ppnt->p_memsz)
end = ppnt->p_vaddr + ppnt->p_memsz;
}
}
#endif
- DL_LIB_UNMAP (tpnt, end - tpnt->mapaddr);
+ end = (end + ADDR_ALIGN) & PAGE_ALIGN;
+ start = start & ~ADDR_ALIGN;
+ DL_LIB_UNMAP (tpnt, end - start);
/* Free elements in RTLD_LOCAL scope list */
for (runp = tpnt->rtld_local; runp; runp = tmp) {
tmp = runp->next;
}
}
- if (handle->dyn == tpnt) {
- /* Unlink the local scope from global one */
- for (ls = &_dl_loaded_modules->symbol_scope; ls; ls = ls->next)
- if (ls->next->r_list[0] == tpnt) {
- _dl_if_debug_print("removing symbol_scope: %s\n", tpnt->libname);
- break;
- }
- ls->next = ls->next->next;
- }
-
/* Next, remove tpnt from the global symbol table list */
if (_dl_symbol_tables) {
if (_dl_symbol_tables->dyn == tpnt) {
}
}
free(tpnt->libname);
- free(tpnt->symbol_scope.r_list);
+ if (handle->dyn != tpnt)
+ free(tpnt->symbol_scope.r_list);
free(tpnt);
}
}
+ /* Unlink and release the handle's local scope from global one */
+ if(ls)
+ ls->next = ls_next;
+ free(handle_rlist);
+
+ for (rpnt1 = handle->next; rpnt1; rpnt1 = rpnt1_tmp) {
+ rpnt1_tmp = rpnt1->next;
+ free(rpnt1);
+ }
free(handle->init_fini.init_fini);
free(handle);
int dlclose(void *vhandle)
{
- return do_dlclose(vhandle, 1);
+ int ret;
+
+ __UCLIBC_MUTEX_CONDITIONAL_LOCK(_dl_mutex, 1);
+ ret = do_dlclose(vhandle, 1);
+ __UCLIBC_MUTEX_CONDITIONAL_UNLOCK(_dl_mutex, 1);
+
+ return ret;
}
char *dlerror(void)
return 0;
}
-int dladdr(const void *__address, Dl_info * __info)
+static int do_dladdr(const void *__address, Dl_info * __info)
{
struct elf_resolve *pelf;
struct elf_resolve *rpnt;
ElfW(Addr) symbol_addr;
symbol_addr = (ElfW(Addr)) DL_RELOC_ADDR(pelf->loadaddr, symtab[si].st_value);
- if (symbol_addr <= (ElfW(Addr))__address && (!sf || sa < symbol_addr)) {
+ if ((symtab[si].st_shndx != SHN_UNDEF
+ || symtab[si].st_value != 0)
+ && ELF_ST_TYPE(symtab[si].st_info) != STT_TLS
+ && DL_ADDR_SYM_MATCH(symbol_addr, &symtab[si], sa,
+ (ElfW(Addr)) __address)) {
sa = symbol_addr;
sn = si;
sf = 1;
ElfW(Addr) symbol_addr;
symbol_addr = (ElfW(Addr)) DL_RELOC_ADDR(pelf->loadaddr, symtab[si].st_value);
- if (symbol_addr <= (ElfW(Addr))__address && (!sf || sa < symbol_addr)) {
+ if ((symtab[si].st_shndx != SHN_UNDEF
+ || symtab[si].st_value != 0)
+ && ELF_ST_TYPE(symtab[si].st_info) != STT_TLS
+ && DL_ADDR_SYM_MATCH(symbol_addr, &symtab[si], sa,
+ (ElfW(Addr)) __address)) {
sa = symbol_addr;
sn = si;
sf = 1;
}
}
#endif
+
+int dladdr(const void *__address, Dl_info * __info)
+{
+ int ret;
+
+ __UCLIBC_MUTEX_CONDITIONAL_LOCK(_dl_mutex, 1);
+ ret = do_dladdr(__address, __info);
+ __UCLIBC_MUTEX_CONDITIONAL_UNLOCK(_dl_mutex, 1);
+
+ return ret;
+}