diff options
-rw-r--r-- | extra/Configs/Config.in | 17 | ||||
-rw-r--r-- | ldso/include/dl-elf.h | 2 | ||||
-rw-r--r-- | ldso/include/dl-hash.h | 4 | ||||
-rw-r--r-- | ldso/include/ldso.h | 2 | ||||
-rw-r--r-- | ldso/ldso/dl-elf.c | 37 | ||||
-rw-r--r-- | ldso/ldso/dl-startup.c | 6 | ||||
-rw-r--r-- | ldso/ldso/ldso.c | 128 | ||||
-rw-r--r-- | libc/misc/internals/__uClibc_main.c | 18 |
8 files changed, 199 insertions, 15 deletions
diff --git a/extra/Configs/Config.in b/extra/Configs/Config.in index e8d522d71..97211864b 100644 --- a/extra/Configs/Config.in +++ b/extra/Configs/Config.in @@ -342,6 +342,23 @@ config LDSO_BASE_FILENAME WARNING: Changing the default prefix could cause problems with binutils' ld ! +config LDSO_STANDALONE_SUPPORT + bool "Dynamic linker stand-alone mode support" + depends on HAVE_SHARED + default n + help + The dynamic linker can be run either indirectly through running some + dynamically linked program or library (in which case no command line + options to the dynamic linker can be passed and, in the ELF case, the + dynamic linker which is stored in the .interp section of the program + is executed) or directly by running: + + /lib/ld-uClibc.so.* [OPTIONS] [PROGRAM [ARGUMENTS]] + + Stand-alone execution is a prerequisite for adding prelink + capabilities to uClibc dynamic linker, as well useful for testing an + updated version of the dynamic linker without breaking the system. + config UCLIBC_STATIC_LDCONFIG bool "Link ldconfig statically" depends on HAVE_SHARED diff --git a/ldso/include/dl-elf.h b/ldso/include/dl-elf.h index cbb2100b1..dc4af7bce 100644 --- a/ldso/include/dl-elf.h +++ b/ldso/include/dl-elf.h @@ -35,7 +35,7 @@ extern struct elf_resolve * _dl_load_shared_library(int secure, struct dyn_elf **rpnt, struct elf_resolve *tpnt, char *full_libname, int trace_loaded_objects); extern struct elf_resolve * _dl_load_elf_shared_library(int secure, - struct dyn_elf **rpnt, char *libname); + struct dyn_elf **rpnt, const char *libname); extern struct elf_resolve *_dl_check_if_named_library_is_loaded(const char *full_libname, int trace_loaded_objects); extern int _dl_linux_resolve(void); diff --git a/ldso/include/dl-hash.h b/ldso/include/dl-hash.h index 34333f40f..edef9d81b 100644 --- a/ldso/include/dl-hash.h +++ b/ldso/include/dl-hash.h @@ -60,6 +60,10 @@ struct elf_resolve { #endif ElfW(Addr) mapaddr; +#ifdef __LDSO_STANDALONE_SUPPORT__ + /* Store the entry point from the ELF header (e_entry) */ + ElfW(Addr) l_entry; +#endif enum {elf_lib, elf_executable,program_interpreter, loaded_file} libtype; struct dyn_elf * symbol_scope; unsigned short usage_count; diff --git a/ldso/include/ldso.h b/ldso/include/ldso.h index 69b5dd75a..120889216 100644 --- a/ldso/include/ldso.h +++ b/ldso/include/ldso.h @@ -144,7 +144,7 @@ extern void _dl_dprintf(int, const char *, ...); # 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, +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 DL_GET_READY_TO_RUN_EXTRA_PARMS); diff --git a/ldso/ldso/dl-elf.c b/ldso/ldso/dl-elf.c index 5562e0784..2a77587db 100644 --- a/ldso/ldso/dl-elf.c +++ b/ldso/ldso/dl-elf.c @@ -322,7 +322,7 @@ goof: */ struct elf_resolve *_dl_load_elf_shared_library(int secure, - struct dyn_elf **rpnt, char *libname) + struct dyn_elf **rpnt, const char *libname) { ElfW(Ehdr) *epnt; unsigned long dynamic_addr = 0; @@ -397,11 +397,15 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, return NULL; } - if ((epnt->e_type != ET_DYN) || (epnt->e_machine != MAGIC1 + if ((epnt->e_type != ET_DYN +#ifdef __LDSO_STANDALONE_SUPPORT__ + && epnt->e_type != ET_EXEC +#endif + ) || (epnt->e_machine != MAGIC1 #ifdef MAGIC2 && epnt->e_machine != MAGIC2 #endif - )) + )) { _dl_internal_error_number = (epnt->e_type != ET_DYN ? LD_ERROR_NOTDYN : LD_ERROR_NOTMAGIC); @@ -462,6 +466,11 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, ppnt++; } +#ifdef __LDSO_STANDALONE_SUPPORT__ + if (epnt->e_type == ET_EXEC) + piclib = 0; +#endif + DL_CHECK_LIB_TYPE (epnt, piclib, _dl_progname, libname); maxvma = (maxvma + ADDR_ALIGN) & PAGE_ALIGN; @@ -701,7 +710,11 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, dpnt = (ElfW(Dyn) *) dynamic_addr; _dl_memset(dynamic_info, 0, sizeof(dynamic_info)); +#ifdef __LDSO_STANDALONE_SUPPORT__ + rtld_flags = _dl_parse_dynamic_info(dpnt, dynamic_info, NULL, piclib ? lib_loadaddr : 0); +#else rtld_flags = _dl_parse_dynamic_info(dpnt, dynamic_info, NULL, lib_loadaddr); +#endif /* If the TEXTREL is set, this means that we need to make the pages writable before we perform relocations. Do this now. They get set back again later. */ @@ -734,6 +747,9 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, tpnt->ppnt = (ElfW(Phdr) *) DL_RELOC_ADDR(tpnt->loadaddr, epnt->e_phoff); tpnt->n_phent = epnt->e_phnum; tpnt->rtld_flags |= rtld_flags; +#ifdef __LDSO_STANDALONE_SUPPORT__ + tpnt->l_entry = epnt->e_entry; +#endif #if defined(USE_TLS) && USE_TLS if (tlsppnt) { @@ -755,7 +771,11 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, tpnt->l_tls_modid = _dl_next_tls_modid (); /* We know the load address, so add it to the offset. */ +#ifdef __LDSO_STANDALONE_SUPPORT__ + if ((tpnt->l_tls_initimage != NULL) && piclib) +#else if (tpnt->l_tls_initimage != NULL) +#endif { # ifdef __SUPPORT_LD_DEBUG_EARLY__ unsigned int tmp = (unsigned int) tpnt->l_tls_initimage; @@ -772,7 +792,12 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, /* * Add this object into the symbol chain */ - if (*rpnt) { + if (*rpnt +#ifdef __LDSO_STANDALONE_SUPPORT__ + /* Do not create a new chain entry for the main executable */ + && (*rpnt)->dyn +#endif + ) { (*rpnt)->next = _dl_malloc(sizeof(struct dyn_elf)); _dl_memset((*rpnt)->next, 0, sizeof(struct dyn_elf)); (*rpnt)->next->prev = (*rpnt); @@ -791,7 +816,11 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, (*rpnt)->dyn = tpnt; tpnt->symbol_scope = _dl_symbol_tables; tpnt->usage_count++; +#ifdef __LDSO_STANDALONE_SUPPORT__ + tpnt->libtype = (epnt->e_type == ET_DYN) ? elf_lib : elf_executable; +#else tpnt->libtype = elf_lib; +#endif /* * OK, the next thing we need to do is to insert the dynamic linker into diff --git a/ldso/ldso/dl-startup.c b/ldso/ldso/dl-startup.c index a51b583a4..2aefa6d74 100644 --- a/ldso/ldso/dl-startup.c +++ b/ldso/ldso/dl-startup.c @@ -321,12 +321,12 @@ 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_EXTRA_ARGS); + _dl_elf_main = (int (*)(int, char **, char **)) + _dl_get_ready_to_run(tpnt, (DL_LOADADDR_TYPE) header, auxvt, envp, argv + DL_GET_READY_TO_RUN_EXTRA_ARGS); /* Transfer control to the application. */ SEND_STDERR_DEBUG("transfering control to application @ "); - _dl_elf_main = (int (*)(int, char **, char **)) auxvt[AT_ENTRY].a_un.a_val; SEND_ADDRESS_STDERR_DEBUG(_dl_elf_main, 1); #if !defined(START) diff --git a/ldso/ldso/ldso.c b/ldso/ldso/ldso.c index ea4ad0f1c..bb394714f 100644 --- a/ldso/ldso/ldso.c +++ b/ldso/ldso/ldso.c @@ -70,8 +70,18 @@ char *_dl_debug_bindings = NULL; int _dl_debug_file = 2; #endif -/* Needed for standalone execution. */ +#if defined (__LDSO_STANDALONE_SUPPORT__) && defined (__sh__) +/* Not hidden, needed for standalone execution. */ +/* + * FIXME: align dl_start for SH to other archs so that we can keep this symbol + * hidden and we don't need to handle in __uClibc_main + */ + +unsigned long _dl_skip_args = 0; +#else unsigned long attribute_hidden _dl_skip_args = 0; +#endif + const char *_dl_progname = UCLIBC_LDSO; /* The name of the executable being run */ #include "dl-startup.c" #include "dl-symbols.c" @@ -274,9 +284,8 @@ static void __attribute__ ((destructor)) __attribute_used__ _dl_fini(void) } } -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(Addr) app_mapaddr = 0; @@ -327,10 +336,12 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, _dl_progname = argv[0]; } +#ifndef __LDSO_STANDALONE_SUPPORT__ if (_start == (void *) auxvt[AT_ENTRY].a_un.a_val) { - _dl_dprintf(_dl_debug_file, "Standalone execution is not supported yet\n"); + _dl_dprintf(_dl_debug_file, "Standalone execution is not enabled\n"); _dl_exit(1); } +#endif /* Start to build the tables of the modules that are required for * this beast to run. We start with the basic executable, and then @@ -382,6 +393,102 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, _dl_init_static_tls = &_dl_nothread_init_static_tls; #endif +#ifdef __LDSO_STANDALONE_SUPPORT__ + if (_start == (void *) auxvt[AT_ENTRY].a_un.a_val) { + char *ptmp; + unsigned int *aux_dat = (unsigned int *) argv; + int argc = aux_dat[-1]; + + tpnt->libname = argv[0]; + while (argc > 1) + if (! _dl_strcmp (argv[1], "--library-path") && argc > 2) { + _dl_library_path = argv[2]; + _dl_skip_args += 2; + argc -= 2; + argv += 2; + } else + break; + + /* + * If we have no further argument the program was called incorrectly. + * Grant the user some education. + */ + + if (argc < 2) { + _dl_dprintf(1, "\ +Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\ +You have invoked `ld.so', the helper program for shared library executables.\n\ +This program usually lives in the file `/lib/ld.so', and special directives\n\ +in executable files using ELF shared libraries tell the system's program\n\ +loader to load the helper program from this file. This helper program loads\n\ +the shared libraries needed by the program executable, prepares the program\n\ +to run, and runs it. You may invoke this helper program directly from the\n\ +command line to load and run an ELF executable file; this is like executing\n\ +that file itself, but always uses this helper program from the file you\n\ +specified, instead of the helper program file specified in the executable\n\ +file you run. This is mostly of use for maintainers to test new versions\n\ +of this helper program; chances are you did not intend to run this program.\n\ +\n\ + --library-path PATH use given PATH instead of content of the environment\n\ + variable LD_LIBRARY_PATH\n"); + _dl_exit(1); + } + + ++_dl_skip_args; + ++argv; + _dl_progname = argv[0]; + + _dl_symbol_tables = rpnt = _dl_zalloc(sizeof(struct dyn_elf)); + /* + * It needs to load the _dl_progname and to map it + * Usually it is the main application launched by means of the ld.so + * but it could be also a shared object (when ld.so used for tracing) + * We keep the misleading app_tpnt name to avoid variable pollution + */ + app_tpnt = _dl_load_elf_shared_library(_dl_secure, &rpnt, _dl_progname); + if (!app_tpnt) { + _dl_dprintf(_dl_debug_file, "can't load '%s'\n", _dl_progname); + _dl_exit(16); + } + /* + * FIXME: it needs to properly handle a PIE executable + * Usually for a main application, loadaddr is computed as difference + * between auxvt entry points and phdr, so if it is not 0, that it is a + * PIE executable. In this case instead we need to set the loadaddr to 0 + * because we are actually mapping the ELF for the main application by + * ourselves. So the PIE case must be checked. + */ + + app_tpnt->rtld_flags = unlazy | RTLD_GLOBAL; + + if (app_tpnt->libtype == elf_executable) + app_tpnt->loadaddr = 0; + + /* + * This is used by gdb to locate the chain of shared libraries that are + * currently loaded. + */ + debug_addr = _dl_zalloc(sizeof(struct r_debug)); + ppnt = (ElfW(Phdr) *)app_tpnt->ppnt; + for (i = 0; i < app_tpnt->n_phent; i++, ppnt++) { + if (ppnt->p_type == PT_DYNAMIC) { + dpnt = (ElfW(Dyn) *) DL_RELOC_ADDR(app_tpnt->loadaddr, ppnt->p_vaddr); + _dl_parse_dynamic_info(dpnt, app_tpnt->dynamic_info, debug_addr, app_tpnt->loadaddr); + } + } + + /* Store the path where the shared lib loader was found + * for later use + */ + _dl_ldsopath = _dl_strdup(tpnt->libname); + ptmp = _dl_strrchr(_dl_ldsopath, '/'); + if (ptmp != _dl_ldsopath) + *ptmp = '\0'; + + _dl_debug_early("Lib Loader: (%x) %s\n", (unsigned) DL_LOADADDR_BASE(tpnt->loadaddr), tpnt->libname); + } else { +#endif + /* At this point we are now free to examine the user application, * and figure out which libraries are supposed to be called. Until * we have this list, we will not be completely ready for dynamic @@ -538,6 +645,10 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, } #endif +#ifdef __LDSO_STANDALONE_SUPPORT__ + } /* ! ldso standalone mode */ +#endif + #ifdef __SUPPORT_LD_DEBUG__ _dl_debug = _dl_getenv("LD_DEBUG", envp); if (_dl_debug) { @@ -1084,6 +1195,13 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, /* Notify the debugger that all objects are now mapped in. */ _dl_debug_addr->r_state = RT_CONSISTENT; _dl_debug_state(); + +#ifdef __LDSO_STANDALONE_SUPPORT__ + if (_start == (void *) auxvt[AT_ENTRY].a_un.a_val) + return (void *) app_tpnt->l_entry; + else +#endif + return (void *) auxvt[AT_ENTRY].a_un.a_val; } #include "dl-hash.c" diff --git a/libc/misc/internals/__uClibc_main.c b/libc/misc/internals/__uClibc_main.c index 58f6643b2..36c0c6c63 100644 --- a/libc/misc/internals/__uClibc_main.c +++ b/libc/misc/internals/__uClibc_main.c @@ -153,6 +153,10 @@ extern void (*__fini_array_end []) (void) attribute_hidden; # endif #endif +#if defined (__LDSO_STANDALONE_SUPPORT__) && defined (SHARED) && defined __sh__ +extern unsigned long _dl_skip_args; +#endif + attribute_hidden const char *__uclibc_progname = ""; #ifdef __UCLIBC_HAS_PROGRAM_INVOCATION_NAME__ const char *program_invocation_short_name = ""; @@ -341,11 +345,23 @@ void __uClibc_main(int (*main)(int, char **, char **), int argc, __rtld_fini = rtld_fini; +#if defined __LDSO_STANDALONE_SUPPORT__ && defined SHARED && defined __sh__ + /* + * Skip ld.so and its arguments + * Other archs except for SH do this in _dl_start before passing + * control to the application. + * FIXME: align SH _dl_start to other archs and remove this from here, + * so that we can keep the visibility hidden. + */ + argc -= _dl_skip_args; + argv += _dl_skip_args; +#endif + /* The environment begins right after argv. */ __environ = &argv[argc + 1]; /* If the first thing after argv is the arguments - * the the environment is empty. */ + * then the environment is empty. */ if ((char *) __environ == *argv) { /* Make __environ point to the NULL at argv[argc] */ __environ = &argv[argc]; |