diff options
70 files changed, 2614 insertions, 1001 deletions
diff --git a/bits/link.h b/bits/link.h index 470b4d3e5f..6b4f811c25 100644 --- a/bits/link.h +++ b/bits/link.h @@ -1,4 +1 @@ -struct link_map_machine - { - /* empty by default */ - }; +#error "Architecture-specific definition needed." diff --git a/bits/linkmap.h b/bits/linkmap.h new file mode 100644 index 0000000000..470b4d3e5f --- /dev/null +++ b/bits/linkmap.h @@ -0,0 +1,4 @@ +struct link_map_machine + { + /* empty by default */ + }; diff --git a/csu/elf-init.c b/csu/elf-init.c index d4e0b3fb88..691e27fab7 100644 --- a/csu/elf-init.c +++ b/csu/elf-init.c @@ -1,5 +1,5 @@ /* Startup support for ELF initializers/finalizers in the main executable. - Copyright (C) 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -86,9 +86,13 @@ __libc_csu_init (void) #endif } +/* This function should not be used anymore. We run the executable's + destructor now just like any other. We cannot remove the function, + though. */ void __libc_csu_fini (void) { +#if 0 #ifdef HAVE_INITFINI_ARRAY size_t i = __fini_array_end - __fini_array_start; while (i-- > 0) @@ -96,4 +100,5 @@ __libc_csu_fini (void) #endif _fini (); +#endif } diff --git a/dlfcn/Makefile b/dlfcn/Makefile index ed20ae5ccd..7b538fed2b 100644 --- a/dlfcn/Makefile +++ b/dlfcn/Makefile @@ -20,8 +20,8 @@ subdir := dlfcn headers := bits/dlfcn.h dlfcn.h extra-libs := libdl libdl-routines := dlopen dlclose dlsym dlvsym dlerror dladdr dladdr1 dlinfo \ - dlmopen -routines := $(patsubst %,s%,$(libdl-routines)) + dlmopen dlfcn +routines := $(patsubst %,s%,$(filter-out dlfcn,$(libdl-routines))) elide-routines.os := $(routines) distribute := dlopenold.c glreflib1.c glreflib2.c failtestmod.c \ defaultmod1.c defaultmod2.c errmsg1mod.c modatexit.c \ @@ -34,7 +34,7 @@ include ../Makeconfig ifeq ($(versioning),yes) libdl-routines += dlopenold -libdl-shared-only-routines := dlopenold +libdl-shared-only-routines := dlopenold dlfcn endif ifeq (yes,$(build-shared)) @@ -65,8 +65,6 @@ generated := $(modules-names:=.so) include ../Rules -LDFLAGS-dl.so = -Wl,-dynamic-linker,$(slibdir)/$(rtld-installed-name) - test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(modules-names))) $(test-modules): $(objpfx)%.so: $(objpfx)%.os $(common-objpfx)shlib.lds $(build-module) diff --git a/dlfcn/dlclose.c b/dlfcn/dlclose.c index 3ddedcffbe..5a344f31ca 100644 --- a/dlfcn/dlclose.c +++ b/dlfcn/dlclose.c @@ -19,6 +19,7 @@ 02111-1307 USA. */ #include <dlfcn.h> +#include <ldsodefs.h> #if !defined SHARED && defined IS_IN_libdl @@ -33,7 +34,7 @@ dlclose (void *handle) static void dlclose_doit (void *handle) { - _dl_close (handle); + GLRO(dl_close) (handle); } int diff --git a/dlfcn/dlfcn.c b/dlfcn/dlfcn.c new file mode 100644 index 0000000000..2f177d3e35 --- /dev/null +++ b/dlfcn/dlfcn.c @@ -0,0 +1,33 @@ +/* Load a shared object at run time. + Copyright (C) 1995,96,97,98,99,2000,2003,2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <dlfcn.h> + + +int __dlfcn_argc attribute_hidden; +char **__dlfcn_argv attribute_hidden; + + +void +__attribute ((constuctor)) +init (int argc, char *argv[]) +{ + __dlfcn_argc = argc; + __dlfcn_argv = argv; +} diff --git a/dlfcn/dlmopen.c b/dlfcn/dlmopen.c index 5fd6543655..0a6d47ea2e 100644 --- a/dlfcn/dlmopen.c +++ b/dlfcn/dlmopen.c @@ -21,6 +21,7 @@ #include <errno.h> #include <libintl.h> #include <stddef.h> +#include <unistd.h> #include <ldsodefs.h> #if !defined SHARED && defined IS_IN_libdl @@ -61,8 +62,10 @@ dlmopen_doit (void *a) # endif GLRO(dl_signal_error) (EINVAL, NULL, NULL, N_("invalid namespace")); - args->new = _dl_open (args->file ?: "", args->mode | __RTLD_DLOPEN, - args->caller, args->nsid); + args->new = GLRO(dl_open) (args->file ?: "", args->mode | __RTLD_DLOPEN, + args->caller, + args->nsid, __dlfcn_argc, __dlfcn_argv, + __environ); } diff --git a/dlfcn/dlopen.c b/dlfcn/dlopen.c index 6381ffc9b1..1e2111e71f 100644 --- a/dlfcn/dlopen.c +++ b/dlfcn/dlopen.c @@ -19,6 +19,8 @@ #include <dlfcn.h> #include <stddef.h> +#include <unistd.h> +#include <ldsodefs.h> #if !defined SHARED && defined IS_IN_libdl @@ -56,8 +58,10 @@ dlopen_doit (void *a) { struct dlopen_args *args = (struct dlopen_args *) a; - args->new = _dl_open (args->file ?: "", args->mode | __RTLD_DLOPEN, - args->caller, args->file == NULL ? LM_ID_BASE : NS); + args->new = GLRO(dl_open) (args->file ?: "", args->mode | __RTLD_DLOPEN, + args->caller, + args->file == NULL ? LM_ID_BASE : NS, + __dlfcn_argc, __dlfcn_argv, __environ); } diff --git a/dlfcn/dlopenold.c b/dlfcn/dlopenold.c index 148716cdb0..8dae1c40ce 100644 --- a/dlfcn/dlopenold.c +++ b/dlfcn/dlopenold.c @@ -19,6 +19,8 @@ #include <dlfcn.h> #include <stddef.h> +#include <unistd.h> +#include <ldsodefs.h> /* This file is for compatibility with glibc 2.0. Compile it only if versioning is used. */ @@ -50,8 +52,10 @@ dlopen_doit (void *a) { struct dlopen_args *args = (struct dlopen_args *) a; - args->new = _dl_open (args->file ?: "", args->mode | __RTLD_DLOPEN, - args->caller, args->file == NULL ? LM_ID_BASE : NS); + args->new = GLRO(dl_open) (args->file ?: "", args->mode | __RTLD_DLOPEN, + args->caller, + args->file == NULL ? LM_ID_BASE : NS, + __dlfcn_argc, __dlfcn_argv, __environ); } extern void *__dlopen_nocheck (const char *file, int mode); diff --git a/elf/Makefile b/elf/Makefile index 87172d367d..b2e2bb28ae 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -21,7 +21,7 @@ subdir := elf headers = elf.h bits/elfclass.h link.h -routines = $(dl-routines) dl-open dl-close dl-support dl-iteratephdr \ +routines = $(dl-routines) dl-support dl-iteratephdr \ dl-addr enbl-secure dl-profstub \ dl-origin dl-libc dl-sym dl-tsd @@ -30,7 +30,7 @@ routines = $(dl-routines) dl-open dl-close dl-support dl-iteratephdr \ dl-routines = $(addprefix dl-,load cache lookup object reloc deps \ runtime error init fini debug misc \ version profile conflict tls origin \ - execstack caller) + execstack caller open close trampoline) all-dl-routines = $(dl-routines) $(sysdep-dl-routines) # But they are absent from the shared libc, because that code is in ld.so. elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin @@ -83,7 +83,8 @@ distribute := rtld-Rules \ tst-array2dep.c tst-piemod1.c \ tst-execstack-mod.c tst-dlmodcount.c \ check-textrel.c dl-sysdep.h test-dlopenrpathmod.c \ - tst-deep1mod1.c tst-deep1mod2.c tst-deep1mod3.c + tst-deep1mod1.c tst-deep1mod2.c tst-deep1mod3.c \ + tst-auditmod1.c CFLAGS-dl-runtime.c = -fexceptions -fasynchronous-unwind-tables CFLAGS-dl-lookup.c = -fexceptions -fasynchronous-unwind-tables @@ -154,7 +155,8 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \ circleload1 tst-tls3 tst-tls4 tst-tls5 tst-tls6 tst-tls7 tst-tls8 \ tst-tls10 tst-tls11 tst-tls12 tst-tls13 tst-tls14 tst-align \ $(tests-execstack-$(have-z-execstack)) tst-dlmodcount \ - tst-dlopenrpath tst-deep1 tst-dlmopen1 tst-dlmopen2 tst-dlmopen3 + tst-dlopenrpath tst-deep1 tst-dlmopen1 tst-dlmopen2 tst-dlmopen3 \ + tst-audit1 # reldep9 test-srcs = tst-pathopt tests-vis-yes = vismain @@ -188,7 +190,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ reldep9mod1 reldep9mod2 reldep9mod3 \ tst-alignmod $(modules-execstack-$(have-z-execstack)) \ tst-dlopenrpathmod tst-deep1mod1 tst-deep1mod2 tst-deep1mod3 \ - tst-dlmopen1mod + tst-dlmopen1mod tst-auditmod1 ifeq (yes,$(have-initfini-array)) modules-names += tst-array2dep endif @@ -773,3 +775,6 @@ $(objpfx)tst-dlmopen2.out: $(objpfx)tst-dlmopen1mod.so $(objpfx)tst-dlmopen3: $(libdl) $(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so + +$(objpfx)tst-audit1.out: $(objpfx)tst-auditmod1.so +tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so diff --git a/elf/Versions b/elf/Versions index e24b2de04c..aaacf3e4ea 100644 --- a/elf/Versions +++ b/elf/Versions @@ -19,7 +19,7 @@ libc { %endif GLIBC_PRIVATE { # functions used in other libraries - _dl_open; _dl_close; _dl_addr; + _dl_addr; _dl_sym; _dl_vsym; _dl_open_hook; __libc_dlopen_mode; __libc_dlsym; __libc_dlclose; diff --git a/elf/dl-close.c b/elf/dl-close.c index c823b17642..cf926ae566 100644 --- a/elf/dl-close.c +++ b/elf/dl-close.c @@ -23,6 +23,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include <bits/libc-lock.h> #include <ldsodefs.h> #include <sys/types.h> @@ -99,7 +100,6 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp, void -internal_function _dl_close (void *_map) { struct reldep_list @@ -112,6 +112,7 @@ _dl_close (void *_map) } *reldeps = NULL; struct link_map **list; struct link_map *map = _map; + Lmid_t ns = map->l_ns; unsigned int i; unsigned int *new_opencount; #ifdef USE_TLS @@ -139,8 +140,8 @@ _dl_close (void *_map) { /* There are still references to this object. Do nothing more. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) - GLRO(dl_debug_printf) ("\nclosing file=%s; opencount == %u\n", - map->l_name, map->l_opencount); + _dl_debug_printf ("\nclosing file=%s; opencount == %u\n", + map->l_name, map->l_opencount); /* Decrement the object's reference counter, not the dependencies'. */ --map->l_opencount; @@ -268,13 +269,17 @@ _dl_close (void *_map) for (i = 0; list[i] != NULL; ++i) { struct link_map *imap = list[i]; + + /* All elements must be in the same namespace. */ + assert (imap->l_ns == ns); + if (new_opencount[i] == 0 && imap->l_type == lt_loaded && (imap->l_flags_1 & DF_1_NODELETE) == 0) { /* When debugging print a message first. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0)) - GLRO(dl_debug_printf) ("\ncalling fini: %s [%lu]\n\n", - imap->l_name, imap->l_ns); + _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", + imap->l_name, ns); /* Call its termination function. Do not do it for half-cooked objects. */ @@ -299,6 +304,22 @@ _dl_close (void *_map) + imap->l_info[DT_FINI]->d_un.d_ptr))) (); } +#ifdef SHARED + /* Auditing checkpoint: we have a new object. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->objclose != NULL) + /* Return value is ignored. */ + (void) afct->objclose (&imap->l_audit[cnt].cookie); + + afct = afct->next; + } + } +#endif + /* This object must not be used anymore. We must remove the reference from the scope. */ unsigned int j; @@ -365,9 +386,30 @@ _dl_close (void *_map) assert (imap->l_type == lt_loaded || imap->l_opencount > 0); } +#ifdef SHARED + /* Auditing checkpoint: we will start deleting objects. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct link_map *head = GL(dl_ns)[ns]._ns_loaded; + struct audit_ifaces *afct = GLRO(dl_audit); + /* Do not call the functions for any auditing object. */ + if (head->l_auditing == 0) + { + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&head->l_audit[cnt].cookie, LA_ACT_DELETE); + + afct = afct->next; + } + } + } +#endif + /* Notify the debugger we are about to remove some loaded objects. */ - _r_debug.r_state = RT_DELETE; - GLRO(dl_debug_state) (); + struct r_debug *r = _dl_debug_initialize (0); + r->r_state = RT_DELETE; + _dl_debug_state (); #ifdef USE_TLS size_t tls_free_start; @@ -389,21 +431,19 @@ _dl_close (void *_map) if (__builtin_expect (imap->l_global, 0)) { /* This object is in the global scope list. Remove it. */ - unsigned int cnt - = GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_nlist; + unsigned int cnt = GL(dl_ns)[ns]._ns_main_searchlist->r_nlist; do --cnt; - while (GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_list[cnt] - != imap); + while (GL(dl_ns)[ns]._ns_main_searchlist->r_list[cnt] != imap); /* The object was already correctly registered. */ while (++cnt - < GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_nlist) - GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_list[cnt - 1] - = GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_list[cnt]; + < GL(dl_ns)[ns]._ns_main_searchlist->r_nlist) + GL(dl_ns)[ns]._ns_main_searchlist->r_list[cnt - 1] + = GL(dl_ns)[ns]._ns_main_searchlist->r_list[cnt]; - --GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_nlist; + --GL(dl_ns)[ns]._ns_main_searchlist->r_nlist; } #ifdef USE_TLS @@ -412,9 +452,10 @@ _dl_close (void *_map) { any_tls = true; - if (! remove_slotinfo (imap->l_tls_modid, - GL(dl_tls_dtv_slotinfo_list), 0, - imap->l_init_called)) + if (GL(dl_tls_dtv_slotinfo_list) != NULL + && ! remove_slotinfo (imap->l_tls_modid, + GL(dl_tls_dtv_slotinfo_list), 0, + imap->l_init_called)) /* All dynamically loaded modules with TLS are unloaded. */ GL(dl_tls_max_dtv_idx) = GL(dl_tls_static_nelem); @@ -499,12 +540,12 @@ _dl_close (void *_map) else { #ifdef SHARED - assert (imap->l_ns != LM_ID_BASE); + assert (ns != LM_ID_BASE); #endif - GL(dl_ns)[imap->l_ns]._ns_loaded = imap->l_next; + GL(dl_ns)[ns]._ns_loaded = imap->l_next; } - --GL(dl_ns)[imap->l_ns]._ns_nloaded; + --GL(dl_ns)[ns]._ns_nloaded; if (imap->l_next != NULL) imap->l_next->l_prev = imap->l_prev; @@ -579,16 +620,36 @@ _dl_close (void *_map) if (any_tls) { if (__builtin_expect (++GL(dl_tls_generation) == 0, 0)) - __libc_fatal (_("TLS generation counter wrapped! Please report as described in <http://www.gnu.org/software/libc/bugs.html>.")); + _dl_fatal_printf ("TLS generation counter wrapped! Please report as described in <http://www.gnu.org/software/libc/bugs.html>.\n"); if (tls_free_end == GL(dl_tls_static_used)) GL(dl_tls_static_used) = tls_free_start; } #endif +#ifdef SHARED + /* Auditing checkpoint: we have deleted all objects. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct link_map *head = GL(dl_ns)[ns]._ns_loaded; + /* Do not call the functions for any auditing object. */ + if (head->l_auditing == 0) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&head->l_audit[cnt].cookie, LA_ACT_CONSISTENT); + + afct = afct->next; + } + } + } +#endif + /* Notify the debugger those objects are finalized and gone. */ - _r_debug.r_state = RT_CONSISTENT; - GLRO(dl_debug_state) (); + r->r_state = RT_CONSISTENT; + _dl_debug_state (); /* Now we can perhaps also remove the modules for which we had dependencies because of symbol lookup. */ @@ -612,7 +673,6 @@ _dl_close (void *_map) /* Release the lock. */ __rtld_lock_unlock_recursive (GL(dl_load_lock)); } -libc_hidden_def (_dl_close) #ifdef USE_TLS diff --git a/elf/dl-debug.c b/elf/dl-debug.c index bd6ee69189..39234720b0 100644 --- a/elf/dl-debug.c +++ b/elf/dl-debug.c @@ -34,7 +34,7 @@ struct r_debug * internal_function _dl_debug_initialize (ElfW(Addr) ldbase) { - if (_r_debug.r_brk == 0) + if (_r_debug.r_brk == 0 || ldbase != 0) { /* Tell the debugger where to find the map of loaded objects. */ _r_debug.r_version = 1 /* R_DEBUG_VERSION XXX */; diff --git a/elf/dl-fini.c b/elf/dl-fini.c index f43f4a00ed..8fc5dbf123 100644 --- a/elf/dl-fini.c +++ b/elf/dl-fini.c @@ -53,7 +53,12 @@ _dl_fini (void) /* Protect against concurrent loads and unloads. */ __rtld_lock_lock_recursive (GL(dl_load_lock)); + unsigned int nmaps = 0; unsigned int nloaded = GL(dl_ns)[cnt]._ns_nloaded; + /* No need to do anything for empty namespaces or those used for + auditing DSOs. */ + if (nloaded == 0 || GL(dl_ns)[cnt]._ns_loaded->l_auditing) + goto out; /* XXX Could it be (in static binaries) that there is no object loaded? */ @@ -76,6 +81,7 @@ _dl_fini (void) unsigned int i; struct link_map *l; + assert (nloaded != 0 || GL(dl_ns)[cnt]._ns_loaded == NULL); for (l = GL(dl_ns)[cnt]._ns_loaded, i = 0; l != NULL; l = l->l_next) /* Do not handle ld.so in secondary namespaces. */ if (l == l->l_real) @@ -90,7 +96,7 @@ _dl_fini (void) } assert (cnt != LM_ID_BASE || i == nloaded); assert (cnt == LM_ID_BASE || i == nloaded || i == nloaded - 1); - unsigned int nmaps = i; + nmaps = i; if (nmaps != 0) { @@ -163,6 +169,7 @@ _dl_fini (void) high and will be decremented in this loop. So we release the lock so that some code which might be called from a destructor can directly or indirectly access the lock. */ + out: __rtld_lock_unlock_recursive (GL(dl_load_lock)); /* 'maps' now contains the objects in the right order. Now call the @@ -176,38 +183,49 @@ _dl_fini (void) /* Make sure nothing happens if we are called twice. */ l->l_init_called = 0; - /* Don't call the destructors for objects we are not - supposed to. */ - if (l->l_name[0] == '\0' && l->l_type == lt_executable) - continue; - /* Is there a destructor function? */ - if (l->l_info[DT_FINI_ARRAY] == NULL - && l->l_info[DT_FINI] == NULL) - continue; - - /* When debugging print a message first. */ - if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, - 0)) - _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", - l->l_name[0] ? l->l_name : rtld_progname, - cnt); - - /* First see whether an array is given. */ - if (l->l_info[DT_FINI_ARRAY] != NULL) + if (l->l_info[DT_FINI_ARRAY] != NULL + || l->l_info[DT_FINI] != NULL) { - ElfW(Addr) *array = - (ElfW(Addr) *) (l->l_addr - + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr); - unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val - / sizeof (ElfW(Addr))); - while (i-- > 0) - ((fini_t) array[i]) (); + /* When debugging print a message first. */ + if (__builtin_expect (GLRO(dl_debug_mask) + & DL_DEBUG_IMPCALLS, 0)) + _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", + l->l_name[0] ? l->l_name : rtld_progname, + cnt); + + /* First see whether an array is given. */ + if (l->l_info[DT_FINI_ARRAY] != NULL) + { + ElfW(Addr) *array = + (ElfW(Addr) *) (l->l_addr + + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr); + unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val + / sizeof (ElfW(Addr))); + while (i-- > 0) + ((fini_t) array[i]) (); + } + + /* Next try the old-style destructor. */ + if (l->l_info[DT_FINI] != NULL) + ((fini_t) DL_DT_FINI_ADDRESS (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr)) (); } - /* Next try the old-style destructor. */ - if (l->l_info[DT_FINI] != NULL) - ((fini_t) DL_DT_FINI_ADDRESS (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr)) (); +#ifdef SHARED + /* Auditing checkpoint: another object closed. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->objclose != NULL) + /* Return value is ignored. */ + (void) afct->objclose (&l->l_audit[cnt].cookie); + + afct = afct->next; + } + } +#endif } /* Correct the previous increment. */ diff --git a/elf/dl-init.c b/elf/dl-init.c index e700dffb3a..e7b67570fd 100644 --- a/elf/dl-init.c +++ b/elf/dl-init.c @@ -93,7 +93,6 @@ _dl_init (struct link_map *main_map, int argc, char **argv, char **env) { ElfW(Dyn) *preinit_array = main_map->l_info[DT_PREINIT_ARRAY]; ElfW(Dyn) *preinit_array_size = main_map->l_info[DT_PREINIT_ARRAYSZ]; - struct r_debug *r; unsigned int i; if (__builtin_expect (GL(dl_initfirst) != NULL, 0)) @@ -120,13 +119,6 @@ _dl_init (struct link_map *main_map, int argc, char **argv, char **env) ((init_t) addrs[cnt]) (argc, argv, env); } - /* Notify the debugger we have added some objects. We need to call - _dl_debug_initialize in a static program in case dynamic linking has - not been used before. */ - r = _dl_debug_initialize (0); - r->r_state = RT_ADD; - _dl_debug_state (); - /* Stupid users forced the ELF specification to be changed. It now says that the dynamic loader is responsible for determining the order in which the constructors have to run. The constructors @@ -141,10 +133,6 @@ _dl_init (struct link_map *main_map, int argc, char **argv, char **env) while (i-- > 0) call_init (main_map->l_initfini[i], argc, argv, env); - /* Notify the debugger all new objects are now ready to go. */ - r->r_state = RT_CONSISTENT; - _dl_debug_state (); - #ifndef HAVE_INLINED_SYSCALLS /* Finished starting up. */ INTUSE(_dl_starting_up) = 0; diff --git a/elf/dl-libc.c b/elf/dl-libc.c index 8a3f542e01..5e76069139 100644 --- a/elf/dl-libc.c +++ b/elf/dl-libc.c @@ -22,6 +22,11 @@ #include <stdlib.h> #include <ldsodefs.h> +extern int __libc_argc attribute_hidden; +extern char **__libc_argv attribute_hidden; + +extern char **__environ; + /* The purpose of this file is to provide wrappers around the dynamic linker error mechanism (similar to dlopen() et al in libdl) which are usable from within libc. Generally we want to throw away the @@ -77,7 +82,8 @@ do_dlopen (void *ptr) { struct do_dlopen_args *args = (struct do_dlopen_args *) ptr; /* Open and relocate the shared object. */ - args->map = _dl_open (args->name, args->mode, NULL, __LM_ID_CALLER); + args->map = GLRO(dl_open) (args->name, args->mode, NULL, __LM_ID_CALLER, + __libc_argc, __libc_argv, __environ); } static void @@ -93,7 +99,7 @@ do_dlsym (void *ptr) static void do_dlclose (void *ptr) { - _dl_close ((struct link_map *) ptr); + GLRO(dl_close) ((struct link_map *) ptr); } /* This code is to support __libc_dlopen from __libc_dlopen'ed shared @@ -109,7 +115,7 @@ struct dl_open_hook #ifdef SHARED extern struct dl_open_hook *_dl_open_hook; libc_hidden_proto (_dl_open_hook); -struct dl_open_hook *_dl_open_hook __attribute__((nocommon)); +struct dl_open_hook *_dl_open_hook __attribute__ ((nocommon)); libc_hidden_data_def (_dl_open_hook); #else static void @@ -119,7 +125,7 @@ do_dlsym_private (void *ptr) struct r_found_version vers; vers.name = "GLIBC_PRIVATE"; vers.hidden = 1; - /* vers.hash = _dl_elf_hash (version); */ + /* vers.hash = _dl_elf_hash (vers.name); */ vers.hash = 0x0963cf85; vers.filename = NULL; diff --git a/elf/dl-load.c b/elf/dl-load.c index eb1a7919fb..7f11b62bf4 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -827,6 +827,8 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp, /* Initialize to keep the compiler happy. */ const char *errstring = NULL; int errval = 0; + struct r_debug *r = _dl_debug_initialize (0); + bool make_consistent = false; /* Get file information. */ if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st) < 0, 0)) @@ -835,6 +837,12 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp, call_lose_errno: errval = errno; call_lose: + if (make_consistent) + { + r->r_state = RT_CONSISTENT; + _dl_debug_state (); + } + lose (errval, fd, name, realname, l, errstring); } @@ -905,6 +913,39 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp, } #endif + /* Signal that we are going to add new objects. */ + if (r->r_state == RT_CONSISTENT) + { +#ifdef SHARED + /* Auditing checkpoint: we are going to add new objects. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct link_map *head = GL(dl_ns)[nsid]._ns_loaded; + /* Do not call the functions for any auditing object. */ + if (head->l_auditing == 0) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&head->l_audit[cnt].cookie, LA_ACT_ADD); + + afct = afct->next; + } + } + } +#endif + + /* Notify the debugger we have added some objects. We need to + call _dl_debug_initialize in a static program in case dynamic + linking has not been used before. */ + r->r_state = RT_ADD; + _dl_debug_state (); + make_consistent = true; + } + else + assert (r->r_state == RT_ADD); + /* Enter the new object in the list of loaded objects. */ l = _dl_new_object (realname, name, l_type, loader, mode, nsid); if (__builtin_expect (l == NULL, 0)) @@ -1044,7 +1085,7 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp, } # ifdef SHARED - if (l->l_prev == NULL) + if (l->l_prev == NULL || (mode && __RTLD_AUDIT) != 0) /* We are loading the executable itself when the dynamic linker was executed directly. The setup will happen later. */ break; @@ -1424,6 +1465,26 @@ cannot enable executable stack as shared object requires"); add_name_to_object (l, ((const char *) D_PTR (l, l_info[DT_STRTAB]) + l->l_info[DT_SONAME]->d_un.d_val)); +#ifdef SHARED + /* Auditing checkpoint: we have a new object. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->objopen != NULL) + { + l->l_audit[cnt].bindflags + = afct->objopen (l, nsid, &l->l_audit[cnt].cookie); + + l->l_audit_any_plt |= l->l_audit[cnt].bindflags != 0; + } + + afct = afct->next; + } + } +#endif + return l; } @@ -1471,7 +1532,8 @@ print_search_path (struct r_search_path_elem **list, this could mean there is something wrong in the installation and the user might want to know about this. */ static int -open_verify (const char *name, struct filebuf *fbp) +open_verify (const char *name, struct filebuf *fbp, struct link_map *loader, + int whatcode) { /* This is the expected ELF header. */ #define ELF32_CLASS ELFCLASS32 @@ -1500,13 +1562,34 @@ open_verify (const char *name, struct filebuf *fbp) ElfW(Word) type; char vendor[4]; } expected_note = { 4, 16, 1, "GNU" }; - int fd; /* Initialize it to make the compiler happy. */ const char *errstring = NULL; int errval = 0; +#ifdef SHARED + /* Give the auditing libraries a chance. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0) && whatcode != 0 + && loader->l_auditing == 0) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->objsearch != NULL) + { + name = afct->objsearch (name, &loader->l_audit[cnt].cookie, + whatcode); + if (name == NULL) + /* Ignore the path. */ + return -1; + } + + afct = afct->next; + } + } +#endif + /* Open the file. We always open files read-only. */ - fd = __open (name, O_RDONLY); + int fd = __open (name, O_RDONLY); if (fd != -1) { ElfW(Ehdr) *ehdr; @@ -1664,7 +1747,7 @@ open_verify (const char *name, struct filebuf *fbp) static int open_path (const char *name, size_t namelen, int preloaded, struct r_search_path_struct *sps, char **realname, - struct filebuf *fbp) + struct filebuf *fbp, struct link_map *loader, int whatcode) { struct r_search_path_elem **dirs = sps->dirs; char *buf; @@ -1708,12 +1791,16 @@ open_path (const char *name, size_t namelen, int preloaded, if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)) _dl_debug_printf (" trying file=%s\n", buf); - fd = open_verify (buf, fbp); + fd = open_verify (buf, fbp, loader, whatcode); if (this_dir->status[cnt] == unknown) { if (fd != -1) this_dir->status[cnt] = existing; - else + /* Do not update the directory information when loading + auditing code. We must try to disturb the program as + little as possible. */ + else if (loader == NULL + || GL(dl_ns)[loader->l_ns]._ns_loaded->l_audit == 0) { /* We failed to open machine dependent library. Let's test whether there is any directory at all. */ @@ -1731,7 +1818,7 @@ open_path (const char *name, size_t namelen, int preloaded, } /* Remember whether we found any existing directory. */ - here_any |= this_dir->status[cnt] == existing; + here_any |= this_dir->status[cnt] != nonexisting; if (fd != -1 && __builtin_expect (preloaded, 0) && INTUSE(__libc_enable_secure)) @@ -1847,6 +1934,32 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, loader->l_name[0] ? loader->l_name : rtld_progname, loader->l_ns); +#ifdef SHARED + /* Give the auditing libraries a chance to change the name before we + try anything. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0) + && (loader == NULL || loader->l_auditing == 0)) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->objsearch != NULL) + { + name = afct->objsearch (name, &loader->l_audit[cnt].cookie, + LA_SER_ORIG); + if (name == NULL) + { + /* Do not try anything further. */ + fd = -1; + goto no_file; + } + } + + afct = afct->next; + } + } +#endif + if (strchr (name, '/') == NULL) { /* Search for NAME in several places. */ @@ -1867,7 +1980,7 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, for (l = loader; fd == -1 && l; l = l->l_loader) if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH")) fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs, - &realname, &fb); + &realname, &fb, loader, LA_SER_RUNPATH); /* If dynamically linked, try the DT_RPATH of the executable itself. NB: we do this for lookups in any namespace. */ @@ -1877,21 +1990,24 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, if (l && l->l_type != lt_loaded && l != loader && cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH")) fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs, - &realname, &fb); + &realname, &fb, loader ?: l, LA_SER_RUNPATH); } } /* Try the LD_LIBRARY_PATH environment variable. */ if (fd == -1 && env_path_list.dirs != (void *) -1) fd = open_path (name, namelen, preloaded, &env_path_list, - &realname, &fb); + &realname, &fb, + loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded, + LA_SER_LIBPATH); /* Look at the RUNPATH information for this binary. */ if (fd == -1 && loader != NULL && cache_rpath (loader, &loader->l_runpath_dirs, DT_RUNPATH, "RUNPATH")) fd = open_path (name, namelen, preloaded, - &loader->l_runpath_dirs, &realname, &fb); + &loader->l_runpath_dirs, &realname, &fb, loader, + LA_SER_RUNPATH); if (fd == -1 && (__builtin_expect (! preloaded, 1) @@ -1939,7 +2055,9 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, if (cached != NULL) { - fd = open_verify (cached, &fb); + fd = open_verify (cached, + &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded, + LA_SER_CONFIG); if (__builtin_expect (fd != -1, 1)) { realname = local_strdup (cached); @@ -1959,7 +2077,7 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, || __builtin_expect (!(l->l_flags_1 & DF_1_NODEFLIB), 1)) && rtld_search_dirs.dirs != (void *) -1) fd = open_path (name, namelen, preloaded, &rtld_search_dirs, - &realname, &fb); + &realname, &fb, l, LA_SER_DEFAULT); /* Add another newline when we are tracing the library loading. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)) @@ -1975,12 +2093,16 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, fd = -1; else { - fd = open_verify (realname, &fb); + fd = open_verify (realname, &fb, + loader ?: GL(dl_ns)[nsid]._ns_loaded, 0); if (__builtin_expect (fd, 0) == -1) free (realname); } } +#ifdef SHARED + no_file: +#endif /* In case the LOADER information has only been provided to get to the appropriate RUNPATH/RPATH information we do not need it anymore. */ diff --git a/elf/dl-object.c b/elf/dl-object.c index b46ebdc33f..86f7a8e4d9 100644 --- a/elf/dl-object.c +++ b/elf/dl-object.c @@ -39,14 +39,24 @@ _dl_new_object (char *realname, const char *libname, int type, size_t libname_len = strlen (libname) + 1; struct link_map *new; struct libname_list *newname; +#ifdef SHARED + /* We create the map for the executable before we know whether we have + auditing libraries and if yes, how many. Assume the worst. */ + unsigned int naudit = GLRO(dl_naudit) ?: ((mode & __RTLD_OPENEXEC) + ? DL_NNS : 0); + size_t audit_space = naudit * sizeof (new->l_audit[0]); +#else +# define audit_space 0 +#endif - new = (struct link_map *) calloc (sizeof (*new) + sizeof (*newname) - + libname_len, 1); + new = (struct link_map *) calloc (sizeof (*new) + audit_space + + sizeof (*newname) + libname_len, 1); if (new == NULL) return NULL; new->l_real = new; - new->l_libname = newname = (struct libname_list *) (new + 1); + new->l_libname = newname = (struct libname_list *) ((char *) (new + 1) + + audit_space); newname->name = (char *) memcpy (newname + 1, libname, libname_len); /* newname->next = NULL; We use calloc therefore not necessary. */ newname->dont_free = 1; @@ -59,6 +69,14 @@ _dl_new_object (char *realname, const char *libname, int type, #endif new->l_ns = nsid; +#ifdef SHARED + for (unsigned int cnt = 0; cnt < naudit; ++cnt) + { + new->l_audit[cnt].cookie = (uintptr_t) new; + /* new->l_audit[cnt].bindflags = 0; */ + } +#endif + /* new->l_global = 0; We use calloc therefore not necessary. */ /* Use the 'l_scope_mem' array by default for the the 'l_scope' diff --git a/elf/dl-open.c b/elf/dl-open.c index 7e890ad7f7..9da7523dc1 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -49,11 +49,6 @@ weak_extern (BP_SYM (_dl_sysdep_start)) extern int __libc_multiple_libcs; /* Defined in init-first.c. */ -extern int __libc_argc attribute_hidden; -extern char **__libc_argv attribute_hidden; - -extern char **__environ; - /* Undefine the following for debugging. */ /* #define SCOPE_DEBUG 1 */ #ifdef SCOPE_DEBUG @@ -74,6 +69,10 @@ struct dl_open_args struct link_map *map; /* Namespace ID. */ Lmid_t nsid; + /* Original parameters to the program and the current environment. */ + int argc; + char **argv; + char **env; }; @@ -115,8 +114,8 @@ add_to_global (struct link_map *new) { GL(dl_ns)[new->l_ns]._ns_global_scope_alloc = 0; nomem: - GLRO(dl_signal_error) (ENOMEM, new->l_libname->name, NULL, - N_("cannot extend global scope")); + _dl_signal_error (ENOMEM, new->l_libname->name, NULL, + N_("cannot extend global scope")); return 1; } @@ -171,13 +170,16 @@ dl_open_worker (void *a) int lazy; unsigned int i; #ifdef USE_TLS - bool any_tls; + bool any_tls = false; #endif struct link_map *call_map = NULL; + assert (_dl_debug_initialize (0)->r_state == RT_CONSISTENT); + /* Check whether _dl_open() has been called from a valid DSO. */ - if (__check_caller (args->caller_dl_open, allow_libc|allow_libdl) != 0) - GLRO(dl_signal_error) (0, "dlopen", NULL, N_("invalid caller")); + if (__check_caller (args->caller_dl_open, + allow_libc|allow_libdl|allow_ldso) != 0) + _dl_signal_error (0, "dlopen", NULL, N_("invalid caller")); /* Determine the caller's map if necessary. This is needed in case we have a DST, when we don't know the namespace ID we have to put @@ -226,10 +228,10 @@ dl_open_worker (void *a) char *new_file; /* DSTs must not appear in SUID/SGID programs. */ - if (__libc_enable_secure) + if (INTUSE(__libc_enable_secure)) /* This is an error. */ - GLRO(dl_signal_error) (0, "dlopen", NULL, - N_("DST not allowed in SUID/SGID programs")); + _dl_signal_error (0, "dlopen", NULL, + N_("DST not allowed in SUID/SGID programs")); /* Determine how much space we need. We have to allocate the @@ -244,8 +246,8 @@ dl_open_worker (void *a) /* If the substitution failed don't try to load. */ if (*new_file == '\0') - GLRO(dl_signal_error) (0, "dlopen", NULL, - N_("empty dynamic string token substitution")); + _dl_signal_error (0, "dlopen", NULL, + N_("empty dynamic string token substitution")); /* Now we have a new file name. */ file = new_file; @@ -256,8 +258,8 @@ dl_open_worker (void *a) } /* Load the named object. */ - args->map = new = GLRO(dl_map_object) (call_map, file, 0, lt_loaded, 0, - mode | __RTLD_CALLMAP, args->nsid); + args->map = new = _dl_map_object (call_map, file, 0, lt_loaded, 0, + mode | __RTLD_CALLMAP, args->nsid); /* If the pointer returned is NULL this means the RTLD_NOLOAD flag is set and the object is not already loaded. */ @@ -279,8 +281,8 @@ dl_open_worker (void *a) { /* Let the user know about the opencount. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) - GLRO(dl_debug_printf) ("opening file=%s [%lu]; opencount=%u\n\n", - new->l_name, new->l_ns, new->l_opencount); + _dl_debug_printf ("opening file=%s [%lu]; opencount=%u\n\n", + new->l_name, new->l_ns, new->l_opencount); /* If the user requested the object to be in the global namespace but it is not so far, add it now. */ @@ -296,23 +298,50 @@ dl_open_worker (void *a) /* Increment just the reference counter of the object. */ ++new->l_opencount; + assert (_dl_debug_initialize (0)->r_state == RT_CONSISTENT); + return; } /* Load that object's dependencies. */ - GLRO(dl_map_object_deps) (new, NULL, 0, 0, - mode & (__RTLD_DLOPEN | RTLD_DEEPBIND)); + _dl_map_object_deps (new, NULL, 0, 0, + mode & (__RTLD_DLOPEN | RTLD_DEEPBIND)); /* So far, so good. Now check the versions. */ for (i = 0; i < new->l_searchlist.r_nlist; ++i) if (new->l_searchlist.r_list[i]->l_real->l_versions == NULL) - (void) GLRO(dl_check_map_versions) (new->l_searchlist.r_list[i]->l_real, - 0, 0); + (void) _dl_check_map_versions (new->l_searchlist.r_list[i]->l_real, + 0, 0); #ifdef SCOPE_DEBUG show_scope (new); #endif +#ifdef SHARED + /* Auditing checkpoint: we have added all objects. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct link_map *head = GL(dl_ns)[new->l_ns]._ns_loaded; + /* Do not call the functions for any auditing object. */ + if (head->l_auditing == 0) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&head->l_audit[cnt].cookie, LA_ACT_CONSISTENT); + + afct = afct->next; + } + } + } +#endif + + /* Notify the debugger all new objects are now ready to go. */ + struct r_debug *r = _dl_debug_initialize (0); + r->r_state = RT_CONSISTENT; + _dl_debug_state (); + /* Only do lazy relocation if `LD_BIND_NOW' is not set. */ lazy = (mode & RTLD_BINDING_MASK) == RTLD_LAZY && GLRO(dl_lazy); @@ -336,12 +365,12 @@ dl_open_worker (void *a) start the profiling. */ struct link_map *old_profile_map = GL(dl_profile_map); - GLRO(dl_relocate_object) (l, l->l_scope, 1, 1); + _dl_relocate_object (l, l->l_scope, 1, 1); if (old_profile_map == NULL && GL(dl_profile_map) != NULL) { /* We must prepare the profiling. */ - GLRO(dl_start_profile) (); + _dl_start_profile (); /* Prevent unloading the object. */ GL(dl_profile_map)->l_flags_1 |= DF_1_NODELETE; @@ -349,7 +378,7 @@ dl_open_worker (void *a) } else #endif - GLRO(dl_relocate_object) (l, l->l_scope, lazy, 0); + _dl_relocate_object (l, l->l_scope, lazy, 0); } if (l == new) @@ -357,22 +386,6 @@ dl_open_worker (void *a) l = l->l_prev; } -#ifdef USE_TLS - /* Do static TLS initialization now if it has been delayed because - the TLS template might not be fully relocated at _dl_allocate_static_tls - time. */ - for (l = new; l; l = l->l_next) - if (l->l_need_tls_init) - { - l->l_need_tls_init = 0; - GL(dl_init_static_tls) (l); - } - - /* We normally don't bump the TLS generation counter. There must be - actually a need to do this. */ - any_tls = false; -#endif - /* Increment the open count for all dependencies. If the file is not loaded as a dependency here add the search list of the newly loaded object to the scope. */ @@ -412,8 +425,8 @@ dl_open_worker (void *a) newp = (struct r_scope_elem **) malloc (new_size * sizeof (struct r_scope_elem *)); if (newp == NULL) - GLRO(dl_signal_error) (ENOMEM, "dlopen", NULL, - N_("cannot create scope list")); + _dl_signal_error (ENOMEM, "dlopen", NULL, + N_("cannot create scope list")); imap->l_scope = memcpy (newp, imap->l_scope, cnt * sizeof (imap->l_scope[0])); } @@ -423,8 +436,8 @@ dl_open_worker (void *a) realloc (imap->l_scope, new_size * sizeof (struct r_scope_elem *)); if (newp == NULL) - GLRO(dl_signal_error) (ENOMEM, "dlopen", NULL, - N_("cannot create scope list")); + _dl_signal_error (ENOMEM, "dlopen", NULL, + N_("cannot create scope list")); imap->l_scope = newp; } @@ -441,76 +454,35 @@ dl_open_worker (void *a) > 0, 0)) { /* Now that we know the object is loaded successfully add - modules containing TLS data to the dtv info table. We + modules containing TLS data to the slot info table. We might have to increase its size. */ - struct dtv_slotinfo_list *listp; - struct dtv_slotinfo_list *prevp; - size_t idx = new->l_searchlist.r_list[i]->l_tls_modid; - - assert (new->l_searchlist.r_list[i]->l_type == lt_loaded); + _dl_add_to_slotinfo (new->l_searchlist.r_list[i]); - /* Find the place in the dtv slotinfo list. */ - listp = GL(dl_tls_dtv_slotinfo_list); - prevp = NULL; /* Needed to shut up gcc. */ - do + if (new->l_searchlist.r_list[i]->l_need_tls_init) { - /* Does it fit in the array of this list element? */ - if (idx < listp->len) - break; - idx -= listp->len; - prevp = listp; - listp = listp->next; + new->l_searchlist.r_list[i]->l_need_tls_init = 0; +# ifdef SHARED + /* Update the slot information data for at least the + generation of the DSO we are allocating data for. */ + _dl_update_slotinfo (new->l_searchlist.r_list[i]->l_tls_modid); +# endif + + GL(dl_init_static_tls) (new->l_searchlist.r_list[i]); + assert (new->l_searchlist.r_list[i]->l_need_tls_init == 0); } - while (listp != NULL); - - if (listp == NULL) - { - /* When we come here it means we have to add a new element - to the slotinfo list. And the new module must be in - the first slot. */ - assert (idx == 0); - - listp = prevp->next = (struct dtv_slotinfo_list *) - malloc (sizeof (struct dtv_slotinfo_list) - + TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo)); - if (listp == NULL) - { - /* We ran out of memory. We will simply fail this - call but don't undo anything we did so far. The - application will crash or be terminated anyway very - soon. */ - - /* We have to do this since some entries in the dtv - slotinfo array might already point to this - generation. */ - ++GL(dl_tls_generation); - - GLRO(dl_signal_error) (ENOMEM, "dlopen", NULL, N_("\ -cannot create TLS data structures")); - } - - listp->len = TLS_SLOTINFO_SURPLUS; - listp->next = NULL; - memset (listp->slotinfo, '\0', - TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo)); - } - - /* Add the information into the slotinfo data structure. */ - listp->slotinfo[idx].map = new->l_searchlist.r_list[i]; - listp->slotinfo[idx].gen = GL(dl_tls_generation) + 1; /* We have to bump the generation counter. */ any_tls = true; } /* Bump the generation number if necessary. */ - if (any_tls) - if (__builtin_expect (++GL(dl_tls_generation) == 0, 0)) - __libc_fatal (_("TLS generation counter wrapped! Please report this.")); + if (any_tls && __builtin_expect (++GL(dl_tls_generation) == 0, 0)) + _dl_fatal_printf (N_("\ +TLS generation counter wrapped! Please report this.")); #endif /* Run the initializer functions of new objects. */ - GLRO(dl_init) (new, __libc_argc, __libc_argv, __environ); + _dl_init (new, args->argc, args->argv, args->env); /* Now we can make the new map available in the global scope. */ if (mode & RTLD_GLOBAL) @@ -532,14 +504,14 @@ cannot create TLS data structures")); /* Let the user know about the opencount. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) - GLRO(dl_debug_printf) ("opening file=%s [%lu]; opencount=%u\n\n", - new->l_name, new->l_ns, new->l_opencount); + _dl_debug_printf ("opening file=%s [%lu]; opencount=%u\n\n", + new->l_name, new->l_ns, new->l_opencount); } void * -internal_function -_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid) +_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid, + int argc, char *argv[], char *env[]) { struct dl_open_args args; const char *objname; @@ -548,12 +520,13 @@ _dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid) if ((mode & RTLD_BINDING_MASK) == 0) /* One of the flags must be set. */ - GLRO(dl_signal_error) (EINVAL, file, NULL, - N_("invalid mode for dlopen()")); + _dl_signal_error (EINVAL, file, NULL, N_("invalid mode for dlopen()")); /* Make sure we are alone. */ __rtld_lock_lock_recursive (GL(dl_load_lock)); + assert (_dl_debug_initialize (0)->r_state == RT_CONSISTENT); + if (nsid == LM_ID_NEWLM) { /* Find a new namespace. */ @@ -566,16 +539,18 @@ _dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid) /* No more namespace available. */ __rtld_lock_unlock_recursive (GL(dl_load_lock)); - GLRO(dl_signal_error) (EINVAL, file, NULL, N_("\ + _dl_signal_error (EINVAL, file, NULL, N_("\ no more namespaces available for dlmopen()")); } } /* Never allow loading a DSO in a namespace which is empty. Such - direct placements is only causing problems. */ + direct placements is only causing problems. Also don't allow + loading into a namespace used for auditing. */ else if (nsid != LM_ID_BASE && nsid != __LM_ID_CALLER - && GL(dl_ns)[nsid]._ns_nloaded == 0) - GLRO(dl_signal_error) (EINVAL, file, NULL, - N_("invalid target namespace in dlmopen()")); + && (GL(dl_ns)[nsid]._ns_nloaded == 0 + || GL(dl_ns)[nsid]._ns_loaded->l_auditing)) + _dl_signal_error (EINVAL, file, NULL, + N_("invalid target namespace in dlmopen()")); args.file = file; args.mode = mode; @@ -583,11 +558,14 @@ no more namespaces available for dlmopen()")); args.caller_dl_open = RETURN_ADDRESS (0); args.map = NULL; args.nsid = nsid; - errcode = GLRO(dl_catch_error) (&objname, &errstring, dl_open_worker, &args); + args.argc = argc; + args.argv = argv; + args.env = env; + errcode = _dl_catch_error (&objname, &errstring, dl_open_worker, &args); #ifndef MAP_COPY /* We must munmap() the cache file. */ - GLRO(dl_unload_cache) (); + _dl_unload_cache (); #endif /* Release the lock. */ @@ -603,21 +581,22 @@ no more namespaces available for dlmopen()")); state if relocation failed, for example. */ if (args.map) { - unsigned int i; - /* Increment open counters for all objects since this sometimes has not happened yet. */ if (args.map->l_searchlist.r_list[0]->l_opencount == 0) - for (i = 0; i < args.map->l_searchlist.r_nlist; ++i) + for (unsigned int i = 0; i < args.map->l_searchlist.r_nlist; ++i) ++args.map->l_searchlist.r_list[i]->l_opencount; #ifdef USE_TLS - /* Maybe some of the modules which were loaded uses TLS. + /* Maybe some of the modules which were loaded use TLS. Since it will be removed in the following _dl_close call - we have to mark the dtv array as having gaps to fill - the holes. This is a pessimistic assumption which won't - hurt if not true. */ - GL(dl_tls_dtv_gaps) = true; + we have to mark the dtv array as having gaps to fill the + holes. This is a pessimistic assumption which won't hurt + if not true. There is no need to do this when we are + loading the auditing DSOs since TLS has not yet been set + up. */ + if ((mode & __RTLD_AUDIT) == 0) + GL(dl_tls_dtv_gaps) = true; #endif _dl_close (args.map); @@ -639,20 +618,23 @@ no more namespaces available for dlmopen()")); memcpy (local_errstring, errstring, len_errstring); } - if (errstring != _dl_out_of_memory) + if (errstring != INTUSE(_dl_out_of_memory)) free ((char *) errstring); + assert (_dl_debug_initialize (0)->r_state == RT_CONSISTENT); + /* Reraise the error. */ - GLRO(dl_signal_error) (errcode, objname, NULL, local_errstring); + _dl_signal_error (errcode, objname, NULL, local_errstring); } + assert (_dl_debug_initialize (0)->r_state == RT_CONSISTENT); + #ifndef SHARED DL_STATIC_INIT (args.map); #endif return args.map; } -libc_hidden_def (_dl_open) #ifdef SCOPE_DEBUG diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c index 4004316c5a..b680683198 100644 --- a/elf/dl-reloc.c +++ b/elf/dl-reloc.c @@ -48,8 +48,6 @@ void internal_function __attribute_noinline__ _dl_allocate_static_tls (struct link_map *map) { - size_t offset; - /* If the alignment requirements are too high fail. */ if (map->l_tls_align > GL(dl_tls_static_align)) { @@ -71,15 +69,15 @@ cannot allocate memory in static TLS block")); n = (freebytes - blsize) / map->l_tls_align; - offset = GL(dl_tls_static_used) + (freebytes - n * map->l_tls_align - - map->l_tls_firstbyte_offset); + size_t offset = GL(dl_tls_static_used) + (freebytes - n * map->l_tls_align + - map->l_tls_firstbyte_offset); map->l_tls_offset = GL(dl_tls_static_used) = offset; # elif TLS_DTV_AT_TP size_t used; size_t check; - offset = roundup (GL(dl_tls_static_used), map->l_tls_align); + size_t offset = roundup (GL(dl_tls_static_used), map->l_tls_align); used = offset + map->l_tls_blocksize; check = used; /* dl_tls_static_used includes the TCB at the beginning. */ @@ -93,8 +91,20 @@ cannot allocate memory in static TLS block")); # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" # endif - if (map->l_relocated) - GL(dl_init_static_tls) (map); + /* If the object is not yet relocated we cannot initialize the + static TLS region. Delay it. */ + if (map->l_real->l_relocated) + { +#ifdef SHARED + if (__builtin_expect (THREAD_DTV()[0].counter != GL(dl_tls_generation), + 0)) + /* Update the slot information data for at least the generation of + the DSO we are allocating data for. */ + (void) _dl_update_slotinfo (map->l_tls_modid); +#endif + + GL(dl_init_static_tls) (map); + } else map->l_need_tls_init = 1; } @@ -114,7 +124,8 @@ _dl_nothread_init_static_tls (struct link_map *map) # endif /* Fill in the DTV slot so that a later LD/GD access will find it. */ - THREAD_DTV ()[map->l_tls_modid].pointer = dest; + THREAD_DTV ()[map->l_tls_modid].pointer.val = dest; + THREAD_DTV ()[map->l_tls_modid].pointer.is_static = true; /* Initialize the memory. */ memset (__mempcpy (dest, map->l_tls_initimage, map->l_tls_initimage_size), @@ -137,11 +148,17 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], /* Initialize it to make the compiler happy. */ const char *errstring = NULL; +#ifdef SHARED + /* If we are auditing, install the same handlers we need for profiling. */ + consider_profiling |= GLRO(dl_audit) != NULL; +#endif + if (l->l_relocated) return; /* If DT_BIND_NOW is set relocate all references in this object. We do not do this if we are profiling, of course. */ + // XXX Correct for auditing? if (!consider_profiling && __builtin_expect (l->l_info[DT_BIND_NOW] != NULL, 0)) lazy = 0; @@ -225,29 +242,6 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], l->l_lookup_cache.ret = (*ref); \ l->l_lookup_cache.value = _lr; })) \ : l) -#define RESOLVE(ref, version, r_type) \ - (ELFW(ST_BIND) ((*ref)->st_info) != STB_LOCAL \ - ? ((__builtin_expect ((*ref) == l->l_lookup_cache.sym, 0) \ - && elf_machine_type_class (r_type) == l->l_lookup_cache.type_class) \ - ? (bump_num_cache_relocations (), \ - (*ref) = l->l_lookup_cache.ret, \ - l->l_lookup_cache.value) \ - : ({ lookup_t _lr; \ - int _tc = elf_machine_type_class (r_type); \ - l->l_lookup_cache.type_class = _tc; \ - l->l_lookup_cache.sym = (*ref); \ - const struct r_found_version *v = NULL; \ - int flags = DL_LOOKUP_ADD_DEPENDENCY; \ - if ((version) != NULL && (version)->hash != 0) \ - { \ - v = (version); \ - flags = 0; \ - } \ - _lr = _dl_lookup_symbol_x (strtab + (*ref)->st_name, l, (ref), \ - scope, v, _tc, flags, NULL); \ - l->l_lookup_cache.ret = (*ref); \ - l->l_lookup_cache.value = _lr; })) \ - : l->l_addr) /* This macro is used as a callback from elf_machine_rel{a,} when a static TLS reloc is about to be performed. Since (in dl-load.c) we @@ -276,20 +270,19 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], will be NULL. */ if (l->l_info[DT_PLTRELSZ] == NULL) { - errstring = N_("%s: profiler found no PLTREL in object %s\n"); + errstring = N_("%s: no PLTREL found in object %s\n"); fatal: _dl_fatal_printf (errstring, rtld_progname ?: "<program name unknown>", l->l_name); } - l->l_reloc_result = - (ElfW(Addr) *) calloc (sizeof (ElfW(Addr)), - l->l_info[DT_PLTRELSZ]->d_un.d_val); + l->l_reloc_result = calloc (sizeof (l->l_reloc_result[0]), + l->l_info[DT_PLTRELSZ]->d_un.d_val); if (l->l_reloc_result == NULL) { errstring = N_("\ -%s: profiler out of memory shadowing PLTREL of %s\n"); +%s: out of memory to store relocation results for %s\n"); goto fatal; } } diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c index a0aecda2e8..0fa8d55a17 100644 --- a/elf/dl-runtime.c +++ b/elf/dl-runtime.c @@ -1,5 +1,5 @@ /* On-demand PLT fixup for shared objects. - Copyright (C) 1995-2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1995-2002, 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -51,15 +51,15 @@ function. */ #ifndef ELF_MACHINE_NO_PLT -static ElfW(Addr) -__attribute ((used, noinline)) ARCH_FIXUP_ATTRIBUTE -fixup ( +ElfW(Addr) +__attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE +_dl_fixup ( # ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS - ELF_MACHINE_RUNTIME_FIXUP_ARGS, + ELF_MACHINE_RUNTIME_FIXUP_ARGS, # endif - /* GKM FIXME: Fix trampoline to pass bounds so we can do - without the `__unbounded' qualifier. */ - struct link_map *__unbounded l, ElfW(Word) reloc_offset) + /* GKM FIXME: Fix trampoline to pass bounds so we can do + without the `__unbounded' qualifier. */ + struct link_map *__unbounded l, ElfW(Word) reloc_offset) { const ElfW(Sym) *const symtab = (const void *) D_PTR (l, l_info[DT_SYMTAB]); @@ -80,8 +80,6 @@ fixup ( if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) { const struct r_found_version *version = NULL; - // XXX Why exactly do we have the differentiation of the flags here? - int flags = DL_LOOKUP_ADD_DEPENDENCY; if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL) { @@ -91,8 +89,6 @@ fixup ( version = &l->l_versions[ndx]; if (version->hash == 0) version = NULL; - else - flags = 0; } result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, @@ -109,9 +105,7 @@ fixup ( /* We already found the symbol. The module (and therefore its load address) is also known. */ value = l->l_addr + sym->st_value; -#ifdef DL_LOOKUP_RETURNS_MAP result = l; -#endif } /* And now perhaps the relocation addend. */ @@ -127,45 +121,45 @@ fixup ( #if !defined PROF && !defined ELF_MACHINE_NO_PLT && !__BOUNDED_POINTERS__ -static ElfW(Addr) -__attribute ((used, noinline)) ARCH_FIXUP_ATTRIBUTE -profile_fixup ( +ElfW(Addr) +__attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE +_dl_profile_fixup ( #ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS - ELF_MACHINE_RUNTIME_FIXUP_ARGS, + ELF_MACHINE_RUNTIME_FIXUP_ARGS, #endif - struct link_map *l, ElfW(Word) reloc_offset, ElfW(Addr) retaddr) + struct link_map *l, ElfW(Word) reloc_offset, + ElfW(Addr) retaddr, const void *regs, long int *framesizep) { void (*mcount_fct) (ElfW(Addr), ElfW(Addr)) = INTUSE(_dl_mcount); - ElfW(Addr) *resultp; - lookup_t result; - ElfW(Addr) value; /* This is the address in the array where we store the result of previous relocations. */ - resultp = &l->l_reloc_result[reloc_offset / sizeof (PLTREL)]; + struct reloc_result *reloc_result + = &l->l_reloc_result[reloc_offset / sizeof (PLTREL)]; + ElfW(Addr) *resultp = &reloc_result->addr; - value = *resultp; + ElfW(Addr) value = *resultp; if (value == 0) { /* This is the first time we have to relocate this object. */ const ElfW(Sym) *const symtab = (const void *) D_PTR (l, l_info[DT_SYMTAB]); - const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]); + const char *strtab = (const char *) D_PTR (l, l_info[DT_STRTAB]); const PLTREL *const reloc = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset); - const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)]; + const ElfW(Sym) *refsym = &symtab[ELFW(R_SYM) (reloc->r_info)]; + const ElfW(Sym) *defsym = refsym; + lookup_t result; /* Sanity check that we're really looking at a PLT relocation. */ assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT); /* Look up the target symbol. If the symbol is marked STV_PROTECTED don't look in the global scope. */ - if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) + if (__builtin_expect (ELFW(ST_VISIBILITY) (refsym->st_other), 0) == 0) { const struct r_found_version *version = NULL; - // XXX Why exactly do we have the differentiation of the flags here? - int flags = DL_LOOKUP_ADD_DEPENDENCY; if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL) { @@ -175,11 +169,9 @@ profile_fixup ( version = &l->l_versions[ndx]; if (version->hash == 0) version = NULL; - else - flags = 0; } - result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, + result = _dl_lookup_symbol_x (strtab + refsym->st_name, l, &defsym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, DL_LOOKUP_ADD_DEPENDENCY, NULL); @@ -187,25 +179,185 @@ profile_fixup ( /* Currently result contains the base load address (or link map) of the object that defines sym. Now add in the symbol offset. */ - value = (sym ? LOOKUP_VALUE_ADDRESS (result) + sym->st_value : 0); + value = (defsym != NULL + ? LOOKUP_VALUE_ADDRESS (result) + defsym->st_value : 0); } else { /* We already found the symbol. The module (and therefore its load address) is also known. */ - value = l->l_addr + sym->st_value; -#ifdef DL_LOOKUP_RETURNS_MAP + value = l->l_addr + refsym->st_value; result = l; -#endif } /* And now perhaps the relocation addend. */ value = elf_machine_plt_value (l, reloc, value); +#ifdef SHARED + /* Auditing checkpoint: we have a new binding. Provide the + auditing libraries the possibility to change the value and + tell us whether further auditing is wanted. */ + if (defsym != NULL && GLRO(dl_naudit) > 0) + { + reloc_result->bound = result; + /* Compute index of the symbol entry in the symbol table of + the DSO with the definition. */ + reloc_result->boundndx = (defsym + - (ElfW(Sym) *) D_PTR (result, + l_info[DT_SYMTAB])); + + /* Determine whether any of the two participating DSOs is + interested in auditing. */ + if ((l->l_audit_any_plt | result->l_audit_any_plt) != 0) + { + unsigned int altvalue = 0; + struct audit_ifaces *afct = GLRO(dl_audit); + /* Synthesize a symbol record where the st_value field is + the result. */ + ElfW(Sym) sym = *defsym; + sym.st_value = value; + + /* Keep track whether there is any interest in tracing + the call in the lower two bits. */ + assert (DL_NNS * 2 <= sizeof (reloc_result->flags) * 8); + assert ((LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT) == 3); + reloc_result->enterexit = LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT; + + const char *strtab2 = (const void *) D_PTR (result, + l_info[DT_STRTAB]); + + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + /* XXX Check whether both DSOs must request action or + only one */ + if ((l->l_audit[cnt].bindflags & LA_FLG_BINDFROM) != 0 + && (result->l_audit[cnt].bindflags & LA_FLG_BINDTO) != 0) + { + unsigned int flags = altvalue; + if (afct->symbind != NULL) + { + uintptr_t new_value + = afct->symbind (&sym, reloc_result->boundndx, + &l->l_audit[cnt].cookie, + &result->l_audit[cnt].cookie, + &flags, + strtab2 + defsym->st_name); + if (new_value != (uintptr_t) sym.st_value) + { + altvalue = LA_SYMB_ALTVALUE; + sym.st_value = new_value; + } + } + + /* Remember the results for every audit library and + store a summary in the first two bits. */ + reloc_result->enterexit + &= flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT); + reloc_result->enterexit + |= ((flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT)) + << ((cnt + 1) * 2)); + } + else + /* If the bind flags say this auditor is not interested, + set the bits manually. */ + reloc_result->enterexit + |= ((LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT) + << ((cnt + 1) * 2)); + + afct = afct->next; + } + + reloc_result->flags = altvalue; + value = sym.st_value; + } + else + /* Set all bits since this symbol binding is not interesting. */ + reloc_result->enterexit = (1u << DL_NNS) - 1; + } +#endif + /* Store the result for later runs. */ if (__builtin_expect (! GLRO(dl_bind_not), 1)) *resultp = value; } + /* By default we do not call the pltexit function. */ + long int framesize = -1; + +#ifdef SHARED + /* Auditing checkpoint: report the PLT entering and allow the + auditors to change the value. */ + if (value != 0 && GLRO(dl_naudit) > 0 + /* Don't do anything if no auditor wants to intercept this call. */ + && (reloc_result->enterexit & LA_SYMB_NOPLTENTER) == 0) + { + ElfW(Sym) *defsym = ((ElfW(Sym) *) D_PTR (reloc_result->bound, + l_info[DT_SYMTAB]) + + reloc_result->boundndx); + + /* Set up the sym parameter. */ + ElfW(Sym) sym = *defsym; + sym.st_value = value; + + /* Get the symbol name. */ + const char *strtab = (const void *) D_PTR (reloc_result->bound, + l_info[DT_STRTAB]); + const char *symname = strtab + sym.st_name; + + /* Keep track of overwritten addresses. */ + unsigned int altvalue = reloc_result->flags; + + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->ARCH_LA_PLTENTER != NULL + && (reloc_result->enterexit + & (LA_SYMB_NOPLTENTER << (2 * (cnt + 1)))) == 0) + { + unsigned int flags = altvalue; + long int new_framesize = -1; + uintptr_t new_value + = afct->ARCH_LA_PLTENTER (&sym, reloc_result->boundndx, + &l->l_audit[cnt].cookie, + &reloc_result->bound->l_audit[cnt].cookie, + regs, &flags, symname, + &new_framesize); + if (new_value != (uintptr_t) sym.st_value) + { + altvalue = LA_SYMB_ALTVALUE; + sym.st_value = new_value; + } + + /* Remember the results for every audit library and + store a summary in the first two bits. */ + reloc_result->enterexit + |= ((flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT)) + << (2 * (cnt + 1))); + + if ((reloc_result->enterexit & (LA_SYMB_NOPLTEXIT + << (2 * (cnt + 1)))) + == 0 && new_framesize != -1 && framesize != -2) + { + /* If this is the first call providing information, + use it. */ + if (framesize == -1) + framesize = new_framesize; + /* If two pltenter calls provide conflicting information, + use the larger value. */ + else if (new_framesize != framesize) + framesize = MAX (new_framesize, framesize); + } + } + + afct = afct->next; + } + + value = sym.st_value; + } +#endif + + /* Store the frame size information. */ + *framesizep = framesize; + (*mcount_fct) (retaddr, value); return value; @@ -214,9 +366,45 @@ profile_fixup ( #endif /* PROF && ELF_MACHINE_NO_PLT */ -/* This macro is defined in dl-machine.h to define the entry point called - by the PLT. The `fixup' function above does the real work, but a little - more twiddling is needed to get the stack right and jump to the address - finally resolved. */ +#include <stdio.h> +void +ARCH_FIXUP_ATTRIBUTE +_dl_call_pltexit (struct link_map *l, ElfW(Word) reloc_offset, + const void *inregs, void *outregs) +{ +#ifdef SHARED + /* This is the address in the array where we store the result of previous + relocations. */ + // XXX Maybe the bound information must be stored on the stack since + // XXX with bind_not a new value could have been stored in the meantime. + struct reloc_result *reloc_result + = &l->l_reloc_result[reloc_offset / sizeof (PLTREL)]; + ElfW(Sym) *defsym = ((ElfW(Sym) *) D_PTR (reloc_result->bound, + l_info[DT_SYMTAB]) + + reloc_result->boundndx); + + /* Set up the sym parameter. */ + ElfW(Sym) sym = *defsym; + + /* Get the symbol name. */ + const char *strtab = (const void *) D_PTR (reloc_result->bound, + l_info[DT_STRTAB]); + const char *symname = strtab + sym.st_name; + + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->ARCH_LA_PLTEXIT != NULL + && (reloc_result->enterexit + & (LA_SYMB_NOPLTEXIT >> (2 * cnt))) == 0) + { + afct->ARCH_LA_PLTEXIT (&sym, reloc_result->boundndx, + &l->l_audit[cnt].cookie, + &reloc_result->bound->l_audit[cnt].cookie, + inregs, outregs, symname); + } -ELF_MACHINE_RUNTIME_TRAMPOLINE + afct = afct->next; + } +#endif +} diff --git a/elf/dl-sym.c b/elf/dl-sym.c index ba00ef56f4..ca83daf21d 100644 --- a/elf/dl-sym.c +++ b/elf/dl-sym.c @@ -116,14 +116,69 @@ RTLD_NEXT used in code not dynamically loaded")); if (ref != NULL) { + void *value; + #if defined USE_TLS && defined SHARED if (ELFW(ST_TYPE) (ref->st_info) == STT_TLS) /* The found symbol is a thread-local storage variable. Return the address for to the current thread. */ - return _dl_tls_symaddr (result, ref); + value = _dl_tls_symaddr (result, ref); + else +#endif + value = DL_SYMBOL_ADDRESS (result, ref); + +#ifdef SHARED + /* Auditing checkpoint: we have a new binding. Provide the + auditing libraries the possibility to change the value and + tell us whether further auditing is wanted. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + const char *strtab = (const char *) D_PTR (result, + l_info[DT_STRTAB]); + /* Compute index of the symbol entry in the symbol table of + the DSO with the definition. */ + unsigned int ndx = (ref - (ElfW(Sym) *) D_PTR (result, + l_info[DT_SYMTAB])); + + if ((match->l_audit_any_plt | result->l_audit_any_plt) != 0) + { + unsigned int altvalue = 0; + struct audit_ifaces *afct = GLRO(dl_audit); + /* Synthesize a symbol record where the st_value field is + the result. */ + ElfW(Sym) sym = *ref; + sym.st_value = (ElfW(Addr)) value; + + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->symbind != NULL + && ((match->l_audit[cnt].bindflags & LA_FLG_BINDFROM) + != 0 + || ((result->l_audit[cnt].bindflags & LA_FLG_BINDTO) + != 0))) + { + unsigned int flags = altvalue | LA_SYMB_DLSYM; + uintptr_t new_value + = afct->symbind (&sym, ndx, + &match->l_audit[cnt].cookie, + &result->l_audit[cnt].cookie, + &flags, strtab + ref->st_name); + if (new_value != (uintptr_t) sym.st_value) + { + altvalue = LA_SYMB_ALTVALUE; + sym.st_value = new_value; + } + } + + afct = afct->next; + } + + value = (void *) sym.st_value; + } + } #endif - return DL_SYMBOL_ADDRESS (result, ref); + return value; } return NULL; diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h index f9559dc59c..5118144174 100644 --- a/elf/dynamic-link.h +++ b/elf/dynamic-link.h @@ -20,7 +20,7 @@ #include <elf.h> #include <assert.h> -#ifdef RESOLVE +#ifdef RESOLVE_MAP /* We pass reloc_addr as a pointer to void, as opposed to a pointer to ElfW(Addr), because not all architectures can assume that the relocated address is properly aligned, whereas the compiler is @@ -64,7 +64,7 @@ elf_machine_lazy_rel (struct link_map *map, /* Read the dynamic section at DYN and fill in INFO with indices DT_*. */ -#ifndef RESOLVE +#ifndef RESOLVE_MAP static #else auto @@ -199,7 +199,7 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp) #endif } -#ifdef RESOLVE +#ifdef RESOLVE_MAP # ifdef RTLD_BOOTSTRAP # define ELF_DURING_STARTUP (1) diff --git a/elf/link.h b/elf/link.h index 6d5ad9d98c..fdda019cbe 100644 --- a/elf/link.h +++ b/elf/link.h @@ -1,6 +1,6 @@ /* Data structure for communication from the run-time dynamic linker for loaded ELF shared objects. - Copyright (C) 1995-1999, 2000, 2001, 2004 Free Software Foundation, Inc. + Copyright (C) 1995-2001, 2004, 2005 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -33,6 +33,7 @@ #define _ElfW_1(e,w,t) e##w##t #include <bits/elfclass.h> /* Defines __ELF_NATIVE_CLASS. */ +#include <bits/link.h> /* Rendezvous structure used by the run-time dynamic linker to communicate details of shared object loading to the debugger. If the executable's @@ -94,6 +95,47 @@ struct link_map #ifdef __USE_GNU +/* Version numbers for la_version handshake interface. */ +#define LAV_CURRENT 1 + +/* Activity types signaled through la_activity. */ +enum + { + LA_ACT_CONSISTENT, /* Link map consistent again. */ + LA_ACT_ADD, /* New object will be added. */ + LA_ACT_DELETE /* Objects will be removed. */ + }; + +/* Values representing origin of name for dynamic loading. */ +enum + { + LA_SER_ORIG = 0x01, /* Original name. */ + LA_SER_LIBPATH = 0x02, /* Directory from LD_LIBRARY_PATH. */ + LA_SER_RUNPATH = 0x04, /* Directory from RPATH/RUNPATH. */ + LA_SER_CONFIG = 0x08, /* Found through ldconfig. */ + LA_SER_DEFAULT = 0x40, /* Default directory. */ + LA_SER_SECURE = 0x80 /* Unused. */ + }; + +/* Values for la_objopen return value. */ +enum + { + LA_FLG_BINDTO = 0x01, /* Audit symbols bound to this object. */ + LA_FLG_BINDFROM = 0x02 /* Audit symbols bound from this object. */ + }; + +/* Values for la_symbind flags parameter. */ +enum + { + LA_SYMB_NOPLTENTER = 0x01, /* la_pltenter will not be called. */ + LA_SYMB_NOPLTEXIT = 0x02, /* la_pltexit will not be called. */ + LA_SYMB_STRUCTCALL = 0x04, /* Return value is a structure. */ + LA_SYMB_DLSYM = 0x08, /* Binding due to dlsym call. */ + LA_SYMB_ALTVALUE = 0x10 /* Value has been changed by a previous + la_symbind call. */ + }; + + struct dl_phdr_info { ElfW(Addr) dlpi_addr; @@ -114,9 +156,28 @@ struct dl_phdr_info __BEGIN_DECLS -extern int dl_iterate_phdr (int (*callback) (struct dl_phdr_info *info, - size_t size, void *data), - void *data); +extern int dl_iterate_phdr (int (*__callback) (struct dl_phdr_info *, + size_t, void *), + void *__data); + + +/* Prototypes for the ld.so auditing interfaces. These are not + defined anywhere in ld.so but instead have to be provided by the + auditing DSO. */ +extern unsigned int la_version (unsigned int __version); +extern void la_activity (uintptr_t *__cookie, unsigned int __flag); +extern char *la_objsearch (const char *__name, uintptr_t *__cookie, + unsigned int __flag); +extern unsigned int la_objopen (struct link_map *__map, Lmid_t __lmid, + uintptr_t *__cookie); +extern void la_preinit (uintptr_t *__cookie); +extern uintptr_t la_symbind32 (Elf32_Sym *__sym, unsigned int __ndx, + uintptr_t *__refcook, uintptr_t *__defcook, + unsigned int *__flags, const char *__symname); +extern uintptr_t la_symbind64 (Elf64_Sym *__sym, unsigned int __ndx, + uintptr_t *__refcook, uintptr_t *__defcook, + unsigned int *__flags, const char *__symname); +extern unsigned int la_objclose (uintptr_t *__cookie); __END_DECLS diff --git a/elf/rtld.c b/elf/rtld.c index de46956a6a..1d08c932f2 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -79,6 +79,13 @@ INTDEF(_dl_argv) /* Nonzero if we were run directly. */ unsigned int _dl_skip_args attribute_relro attribute_hidden; +/* List of auditing DSOs. */ +static struct audit_list +{ + const char *name; + struct audit_list *next; +} *audit_list; + #ifndef HAVE_INLINED_SYSCALLS /* Set nonzero during loading and initialization of executable and libraries, cleared before the executable's entry point runs. This @@ -126,25 +133,14 @@ struct rtld_global_ro _rtld_global_ro attribute_relro = ._dl_fpu_control = _FPU_DEFAULT, /* Function pointers. */ - ._dl_get_origin = _dl_get_origin, - ._dl_dst_count = _dl_dst_count, - ._dl_dst_substitute = _dl_dst_substitute, - ._dl_map_object = _dl_map_object, - ._dl_map_object_deps = _dl_map_object_deps, - ._dl_relocate_object = _dl_relocate_object, - ._dl_check_map_versions = _dl_check_map_versions, - ._dl_init = _dl_init, - ._dl_debug_state = _dl_debug_state, -#ifndef MAP_COPY - ._dl_unload_cache = _dl_unload_cache, -#endif ._dl_debug_printf = _dl_debug_printf, ._dl_catch_error = _dl_catch_error, ._dl_signal_error = _dl_signal_error, - ._dl_start_profile = _dl_start_profile, ._dl_mcount = _dl_mcount_internal, ._dl_lookup_symbol_x = _dl_lookup_symbol_x, - ._dl_check_caller = _dl_check_caller + ._dl_check_caller = _dl_check_caller, + ._dl_open = _dl_open, + ._dl_close = _dl_close }; /* If we would use strong_alias here the compiler would see a non-hidden definition. This would undo the effect of the previous @@ -472,7 +468,7 @@ _dl_start (void *arg) while (remaining-- > 0) *p++ = '\0'; } -#endif +# endif /* Install the pointer to the dtv. */ @@ -514,6 +510,7 @@ _dl_start (void *arg) data access using the global offset table. */ ELF_DYNAMIC_RELOCATE (&bootstrap_map, 0, 0); + bootstrap_map.l_relocated = 1; } /* Please note that we don't allow profiling of this object and @@ -566,6 +563,19 @@ struct map_args struct link_map *map; }; +struct dlmopen_args +{ + const char *fname; + struct link_map *map; +}; + +struct lookup_args +{ + const char *name; + struct link_map *map; + void *result; +}; + /* Arguments to version_check_doit. */ struct version_check_args { @@ -591,6 +601,28 @@ map_doit (void *a) } static void +dlmopen_doit (void *a) +{ + struct dlmopen_args *args = (struct dlmopen_args *) a; + args->map = _dl_open (args->fname, RTLD_LAZY | __RTLD_DLOPEN | __RTLD_AUDIT, + dl_main, LM_ID_NEWLM, _dl_argc, INTUSE(_dl_argv), + __environ); +} + +static void +lookup_doit (void *a) +{ + struct lookup_args *args = (struct lookup_args *) a; + const ElfW(Sym) *ref = NULL; + args->result = NULL; + lookup_t l = _dl_lookup_symbol_x (args->name, args->map, &ref, + args->map->l_local_scope, NULL, 0, + DL_LOOKUP_RETURN_NEWEST, NULL); + if (ref != NULL) + args->result = DL_SYMBOL_ADDRESS (l, ref); +} + +static void version_check_doit (void *a) { struct version_check_args *args = (struct version_check_args *) a; @@ -648,6 +680,80 @@ match_version (const char *string, struct link_map *map) return 0; } +#ifdef USE_TLS +static bool tls_init_tp_called; + +static void * +init_tls (void) +{ + /* Number of elements in the static TLS block. */ + GL(dl_tls_static_nelem) = GL(dl_tls_max_dtv_idx); + + /* Do not do this twice. The audit interface might have required + the DTV interfaces to be set up early. */ + if (GL(dl_initial_dtv) != NULL) + return NULL; + + /* Allocate the array which contains the information about the + dtv slots. We allocate a few entries more than needed to + avoid the need for reallocation. */ + size_t nelem = GL(dl_tls_max_dtv_idx) + 1 + TLS_SLOTINFO_SURPLUS; + + /* Allocate. */ + GL(dl_tls_dtv_slotinfo_list) = (struct dtv_slotinfo_list *) + calloc (sizeof (struct dtv_slotinfo_list) + + nelem * sizeof (struct dtv_slotinfo), 1); + /* No need to check the return value. If memory allocation failed + the program would have been terminated. */ + + struct dtv_slotinfo *slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo; + GL(dl_tls_dtv_slotinfo_list)->len = nelem; + GL(dl_tls_dtv_slotinfo_list)->next = NULL; + + /* Fill in the information from the loaded modules. No namespace + but the base one can be filled at this time. */ + assert (GL(dl_ns)[LM_ID_BASE + 1]._ns_loaded == NULL); + int i = 0; + for (struct link_map *l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; l != NULL; + l = l->l_next) + if (l->l_tls_blocksize != 0) + { + /* This is a module with TLS data. Store the map reference. + The generation counter is zero. */ + slotinfo[i].map = l; + /* slotinfo[i].gen = 0; */ + ++i; + } + assert (i == GL(dl_tls_max_dtv_idx)); + + /* Compute the TLS offsets for the various blocks. */ + _dl_determine_tlsoffset (); + + /* Construct the static TLS block and the dtv for the initial + thread. For some platforms this will include allocating memory + for the thread descriptor. The memory for the TLS block will + never be freed. It should be allocated accordingly. The dtv + array can be changed if dynamic loading requires it. */ + void *tcbp = _dl_allocate_tls_storage (); + if (tcbp == NULL) + _dl_fatal_printf ("\ +cannot allocate TLS data structures for initial thread"); + + /* Store for detection of the special case by __tls_get_addr + so it knows not to pass this dtv to the normal realloc. */ + GL(dl_initial_dtv) = GET_DTV (tcbp); + + /* And finally install it for the main thread. If ld.so itself uses + TLS we know the thread pointer was initialized earlier. */ + const char *lossage = TLS_INIT_TP (tcbp, USE___THREAD); + if (__builtin_expect (lossage != NULL, 0)) + _dl_fatal_printf ("cannot set up thread-local storage: %s\n", lossage); + tls_init_tp_called = true; + + return tcbp; +} +#endif + #ifdef _LIBC_REENTRANT /* _dl_error_catch_tsd points to this for the single-threaded case. It's reset by the thread library for multithreaded programs. */ @@ -702,7 +808,7 @@ dl_main (const ElfW(Phdr) *phdr, hp_timing_t diff; #endif #ifdef USE_TLS - void *tcbp; + void *tcbp = NULL; #endif #ifdef _LIBC_REENTRANT @@ -826,6 +932,7 @@ of this helper program; chances are you did not intend to run this program.\n\ objects. */ _dl_init_paths (library_path); + /* The initialization of _dl_stack_flags done below assumes the executable's PT_GNU_STACK may have been honored by the kernel, and so a PT_GNU_STACK with PF_X set means the stack started out with @@ -887,10 +994,10 @@ of this helper program; chances are you did not intend to run this program.\n\ { /* Create a link_map for the executable itself. This will be what dlopen on "" returns. */ - _dl_new_object ((char *) "", "", lt_executable, NULL, 0, LM_ID_BASE); - main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; - if (main_map == NULL) - _dl_fatal_printf ("cannot allocate memory for link map\n"); + main_map + = _dl_new_object ((char *) "", "", lt_executable, NULL, 0, LM_ID_BASE); + assert (main_map != NULL); + assert (main_map == GL(dl_ns)[LM_ID_BASE]._ns_loaded); main_map->l_phdr = phdr; main_map->l_phnum = phnum; main_map->l_entry = *user_entry; @@ -991,8 +1098,9 @@ of this helper program; chances are you did not intend to run this program.\n\ main_map->l_text_end = allocend; } break; -#ifdef USE_TLS + case PT_TLS: +#ifdef USE_TLS if (ph->p_memsz > 0) { /* Note that in the case the dynamic linker we duplicate work @@ -1012,8 +1120,12 @@ of this helper program; chances are you did not intend to run this program.\n\ /* This image gets the ID one. */ GL(dl_tls_max_dtv_idx) = main_map->l_tls_modid = 1; } - break; +#else + _dl_fatal_printf ("\ +ld.so does not support TLS, but program uses it!\n"); #endif + break; + case PT_GNU_STACK: GL(dl_stack_flags) = ph->p_flags; break; @@ -1045,6 +1157,26 @@ of this helper program; chances are you did not intend to run this program.\n\ else assert (GL(dl_rtld_map).l_libname); /* How else did we get here? */ + /* If the current libname is different from the SONAME, add the + latter as well. */ + if (GL(dl_rtld_map).l_info[DT_SONAME] != NULL + && strcmp (GL(dl_rtld_map).l_libname->name, + (const char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB]) + + GL(dl_rtld_map).l_info[DT_SONAME]->d_un.d_val) != 0) + { + static struct libname_list newname; + newname.name = ((char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB]) + + GL(dl_rtld_map).l_info[DT_SONAME]->d_un.d_ptr); + newname.next = NULL; + newname.dont_free = 1; + + assert (GL(dl_rtld_map).l_libname->next == NULL); + GL(dl_rtld_map).l_libname->next = &newname; + } + /* The ld.so must be relocated since otherwise loading audit modules + will fail since they reuse the very same ld.so. */ + assert (GL(dl_rtld_map).l_relocated); + if (! rtld_is_main) { /* Extract the contents of the dynamic section for easy access. */ @@ -1074,6 +1206,10 @@ of this helper program; chances are you did not intend to run this program.\n\ objects. */ _dl_init_paths (library_path); + /* Initialize _r_debug. */ + struct r_debug *r = _dl_debug_initialize (GL(dl_rtld_map).l_addr); + r->r_state = RT_CONSISTENT; + /* Put the link_map for ourselves on the chain so it can be found by name. Note that at this point the global chain of link maps contains exactly one element, which is pointed to by dl_loaded. */ @@ -1101,6 +1237,7 @@ of this helper program; chances are you did not intend to run this program.\n\ GL(dl_rtld_map).l_phdr = rtld_phdr; GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum; + /* PT_GNU_RELRO is usually the last phdr. */ size_t cnt = rtld_ehdr->e_phnum; while (cnt-- > 0) @@ -1111,6 +1248,204 @@ of this helper program; chances are you did not intend to run this program.\n\ break; } +#ifdef USE_TLS + /* Add the dynamic linker to the TLS list if it also uses TLS. */ + if (GL(dl_rtld_map).l_tls_blocksize != 0) + /* Assign a module ID. Do this before loading any audit modules. */ + GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid (); +#endif + + /* If we have auditing DSOs to load, do it now. */ + if (__builtin_expect (audit_list != NULL, 0)) + { + /* Iterate over all entries in the list. The order is important. */ + struct audit_ifaces *last_audit = NULL; + struct audit_list *al = audit_list->next; + do + { +#ifdef USE_TLS + int tls_idx = GL(dl_tls_max_dtv_idx); + + /* Now it is time to determine the layout of the static TLS + block and allocate it for the initial thread. Note that we + always allocate the static block, we never defer it even if + no DF_STATIC_TLS bit is set. The reason is that we know + glibc will use the static model. */ +# ifndef TLS_INIT_TP_EXPENSIVE +# define TLS_INIT_TP_EXPENSIVE 0 +# endif + + /* Since we start using the auditing DSOs right away we need to + initialize the data structures now. */ + if (!TLS_INIT_TP_EXPENSIVE) + tcbp = init_tls (); +#endif + struct dlmopen_args dlmargs; + dlmargs.fname = al->name; + dlmargs.map = NULL; + + const char *objname; + const char *err_str = NULL; + (void) _dl_catch_error (&objname, &err_str, dlmopen_doit, &dlmargs); + if (__builtin_expect (err_str != NULL, 0)) + { + not_loaded: + _dl_error_printf ("\ +ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", + al->name, err_str); + free ((char *) err_str); + } + else + { + struct lookup_args largs; + largs.name = "la_version"; + largs.map = dlmargs.map; + + /* Check whether the interface version matches. */ + (void) _dl_catch_error (&objname, &err_str, lookup_doit, &largs); + + unsigned int (*laversion) (unsigned int); + unsigned int lav; + if (err_str == NULL + && (laversion = largs.result) != NULL + && (lav = laversion (LAV_CURRENT)) > 0 + && lav <= LAV_CURRENT) + { + /* Allocate structure for the callback function pointers. + This call can never fail. */ + union + { + struct audit_ifaces ifaces; +#define naudit_ifaces 8 + void (*fptr[naudit_ifaces]) (void); + } *newp = malloc (sizeof (*newp)); + + /* Names of the auditing interfaces. All in one + long string. */ + static const char audit_iface_names[] = + "la_activity\0" + "la_objsearch\0" + "la_objopen\0" + "la_preinit\0" +#if __ELF_NATIVE_CLASS == 32 + "la_symbind32\0" +#elif __ELF_NATIVE_CLASS == 64 + "la_symbind64\0" +#else +# error "__ELF_NATIVE_CLASS must be defined" +#endif +#define STRING(s) __STRING (s) + "la_" STRING (ARCH_LA_PLTENTER) "\0" + "la_" STRING (ARCH_LA_PLTEXIT) "\0" + "la_objclose\0"; + unsigned int cnt = 0; + const char *cp = audit_iface_names; + do + { + largs.name = cp; + (void) _dl_catch_error (&objname, &err_str, lookup_doit, + &largs); + + /* Store the pointer. */ + if (err_str == NULL && largs.result != NULL) + { + newp->fptr[cnt] = largs.result; + + /* The dynamic linker link map is statically + allocated, initialize the data now. */ + GL(dl_rtld_map).l_audit[cnt].cookie + = (intptr_t) &GL(dl_rtld_map); + } + else + newp->fptr[cnt] = NULL; + ++cnt; + + cp = (char *) rawmemchr (cp, '\0') + 1; + } + while (*cp != '\0'); + assert (cnt == naudit_ifaces); + + /* Now append the new auditing interface to the list. */ + newp->ifaces.next = NULL; + if (last_audit == NULL) + last_audit = GLRO(dl_audit) = &newp->ifaces; + else + last_audit = last_audit->next = &newp->ifaces; + ++GLRO(dl_naudit); + + /* Mark the DSO as being used for auditing. */ + dlmargs.map->l_auditing = 1; + } + else + { + /* We cannot use the DSO, it does not have the + appropriate interfaces or it expects something + more recent. */ +#ifndef NDEBUG + Lmid_t ns = dlmargs.map->l_ns; +#endif + _dl_close (dlmargs.map); + + /* Make sure the namespace has been cleared entirely. */ + assert (GL(dl_ns)[ns]._ns_loaded == NULL); + assert (GL(dl_ns)[ns]._ns_nloaded == 0); + +#ifdef USE_TLS + GL(dl_tls_max_dtv_idx) = tls_idx; +#endif + goto not_loaded; + } + } + + al = al->next; + } + while (al != audit_list->next); + + /* If we have any auditing modules, announce that we already + have two objects loaded. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct link_map *ls[2] = { main_map, &GL(dl_rtld_map) }; + + for (unsigned int outer = 0; outer < 2; ++outer) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->objopen != NULL) + { + ls[outer]->l_audit[cnt].bindflags + = afct->objopen (ls[outer], LM_ID_BASE, + &ls[outer]->l_audit[cnt].cookie); + + ls[outer]->l_audit_any_plt + |= ls[outer]->l_audit[cnt].bindflags != 0; + } + + afct = afct->next; + } + } + } + } + + /* We start adding objects. */ + r->r_state = RT_ADD; + _dl_debug_state (); + + /* Auditing checkpoint: we are ready to signal that the initial map + is being constructed. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&main_map->l_audit[cnt].cookie, LA_ACT_ADD); + + afct = afct->next; + } + } + /* We have two ways to specify objects to preload: via environment variable and via the file /etc/ld.so.preload. The latter can also be used when security is enabled. */ @@ -1310,6 +1645,9 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", && ph->p_vaddr + ph->p_memsz >= l->l_text_end) l->l_text_end = ph->p_vaddr + ph->p_memsz; } + else + /* There must be no TLS segment. */ + assert (ph->p_type != PT_TLS); } l->l_map_start = (ElfW(Addr)) GLRO(dl_sysinfo_dso); l->l_addr = l->l_map_start - l->l_addr; @@ -1425,20 +1763,6 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", } #ifdef USE_TLS - /* Now it is time to determine the layout of the static TLS block - and allocate it for the initial thread. Note that we always - allocate the static block, we never defer it even if no - DF_STATIC_TLS bit is set. The reason is that we know glibc will - use the static model. First add the dynamic linker to the list - if it also uses TLS. */ - if (GL(dl_rtld_map).l_tls_blocksize != 0) - /* Assign a module ID. */ - GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid (); - -# ifndef TLS_INIT_TP_EXPENSIVE -# define TLS_INIT_TP_EXPENSIVE 0 -# endif - /* We do not initialize any of the TLS functionality unless any of the initial modules uses TLS. This makes dynamic loading of modules with TLS impossible, but to support it requires either eagerly doing setup @@ -1446,57 +1770,9 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", an old kernel that can't perform TLS_INIT_TP, even if no TLS is ever used. Trying to do it lazily is too hairy to try when there could be multiple threads (from a non-TLS-using libpthread). */ - if (!TLS_INIT_TP_EXPENSIVE || GL(dl_tls_max_dtv_idx) > 0) - { - struct link_map *l; - size_t nelem; - struct dtv_slotinfo *slotinfo; - - /* Number of elements in the static TLS block. */ - GL(dl_tls_static_nelem) = GL(dl_tls_max_dtv_idx); - - /* Allocate the array which contains the information about the - dtv slots. We allocate a few entries more than needed to - avoid the need for reallocation. */ - nelem = GL(dl_tls_max_dtv_idx) + 1 + TLS_SLOTINFO_SURPLUS; - - /* Allocate. */ - GL(dl_tls_dtv_slotinfo_list) = (struct dtv_slotinfo_list *) - malloc (sizeof (struct dtv_slotinfo_list) - + nelem * sizeof (struct dtv_slotinfo)); - /* No need to check the return value. If memory allocation failed - the program would have been terminated. */ - - slotinfo = memset (GL(dl_tls_dtv_slotinfo_list)->slotinfo, '\0', - nelem * sizeof (struct dtv_slotinfo)); - GL(dl_tls_dtv_slotinfo_list)->len = nelem; - GL(dl_tls_dtv_slotinfo_list)->next = NULL; - - /* Fill in the information from the loaded modules. */ - for (l = main_map, i = 0; l != NULL; l = l->l_next) - if (l->l_tls_blocksize != 0) - /* This is a module with TLS data. Store the map reference. - The generation counter is zero. */ - slotinfo[++i].map = l; - assert (i == GL(dl_tls_max_dtv_idx)); - - /* Compute the TLS offsets for the various blocks. */ - _dl_determine_tlsoffset (); - - /* Construct the static TLS block and the dtv for the initial - thread. For some platforms this will include allocating memory - for the thread descriptor. The memory for the TLS block will - never be freed. It should be allocated accordingly. The dtv - array can be changed if dynamic loading requires it. */ - tcbp = _dl_allocate_tls_storage (); - if (tcbp == NULL) - _dl_fatal_printf ("\ -cannot allocate TLS data structures for initial thread"); - - /* Store for detection of the special case by __tls_get_addr - so it knows not to pass this dtv to the normal realloc. */ - GL(dl_initial_dtv) = GET_DTV (tcbp); - } + bool was_tls_init_tp_called = tls_init_tp_called; + if (tcbp == NULL && (!TLS_INIT_TP_EXPENSIVE || GL(dl_tls_max_dtv_idx) > 0)) + tcbp = init_tls (); #endif if (__builtin_expect (mode, normal) != normal) @@ -1777,8 +2053,6 @@ cannot allocate TLS data structures for initial thread"); } - /* Initialize _r_debug. */ - struct r_debug *r = _dl_debug_initialize (GL(dl_rtld_map).l_addr); { struct link_map *l = main_map; @@ -1813,8 +2087,6 @@ cannot allocate TLS data structures for initial thread"); if (prelinked) { - struct link_map *l; - if (main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)] != NULL) { ElfW(Rela) *conflict, *conflictend; @@ -1837,11 +2109,15 @@ cannot allocate TLS data structures for initial thread"); /* Mark all the objects so we know they have been already relocated. */ - for (l = main_map; l != NULL; l = l->l_next) + for (struct link_map *l = main_map; l != NULL; l = l->l_next) { l->l_relocated = 1; if (l->l_relro_size) _dl_protect_relro (l); + + /* Add object to slot information data if necessasy. */ + if (l->l_tls_blocksize != 0 && tls_init_tp_called) + _dl_add_to_slotinfo (l); } _dl_sysdep_start_cleanup (); @@ -1857,7 +2133,6 @@ cannot allocate TLS data structures for initial thread"); the dynamic linker out of order because it has no copy relocs (we know that because it is self-contained). */ - struct link_map *l; int consider_profiling = GLRO(dl_profile) != NULL; #ifndef HP_TIMING_NONAVAIL hp_timing_t start; @@ -1868,7 +2143,7 @@ cannot allocate TLS data structures for initial thread"); /* If we are profiling we also must do lazy reloaction. */ GLRO(dl_lazy) |= consider_profiling; - l = main_map; + struct link_map *l = main_map; while (l->l_next) l = l->l_next; @@ -1890,6 +2165,10 @@ cannot allocate TLS data structures for initial thread"); _dl_relocate_object (l, l->l_scope, GLRO(dl_lazy), consider_profiling); + /* Add object to slot information data if necessasy. */ + if (l->l_tls_blocksize != 0 && tls_init_tp_called) + _dl_add_to_slotinfo (l); + l = l->l_prev; } while (l); @@ -1917,6 +2196,8 @@ cannot allocate TLS data structures for initial thread"); /* There was an explicit ref to the dynamic linker as a shared lib. Re-relocate ourselves with user-controlled symbol definitions. */ HP_TIMING_NOW (start); + /* Mark the link map as not yet relocated again. */ + GL(dl_rtld_map).l_relocated = 0; _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope, 0, 0); HP_TIMING_NOW (stop); HP_TIMING_DIFF (add, start, stop); @@ -1931,6 +2212,9 @@ cannot allocate TLS data structures for initial thread"); #ifdef USE_TLS if (GL(dl_tls_max_dtv_idx) > 0 || USE___THREAD || !TLS_INIT_TP_EXPENSIVE) { + if (!was_tls_init_tp_called && GL(dl_tls_max_dtv_idx) > 0) + ++GL(dl_tls_generation); + /* Now that we have completed relocation, the initializer data for the TLS blocks has its final values and we can copy them into the main thread's TLS area, which we allocated above. */ @@ -1938,16 +2222,42 @@ cannot allocate TLS data structures for initial thread"); /* And finally install it for the main thread. If ld.so itself uses TLS we know the thread pointer was initialized earlier. */ - const char *lossage = TLS_INIT_TP (tcbp, USE___THREAD); - if (__builtin_expect (lossage != NULL, 0)) - _dl_fatal_printf ("cannot set up thread-local storage: %s\n", lossage); + if (! tls_init_tp_called) + { + const char *lossage = TLS_INIT_TP (tcbp, USE___THREAD); + if (__builtin_expect (lossage != NULL, 0)) + _dl_fatal_printf ("cannot set up thread-local storage: %s\n", + lossage); + } } else #endif NONTLS_INIT_TP; - /* Notify the debugger that all objects are now mapped in. */ - r->r_state = RT_ADD; +#ifdef SHARED + /* Auditing checkpoint: we have added all objects. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct link_map *head = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + /* Do not call the functions for any auditing object. */ + if (head->l_auditing == 0) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&head->l_audit[cnt].cookie, LA_ACT_CONSISTENT); + + afct = afct->next; + } + } + } +#endif + + /* Notify the debugger all new objects are now ready to go. We must re-get + the address since by now the variable might be in another object. */ + r = _dl_debug_initialize (0); + r->r_state = RT_CONSISTENT; _dl_debug_state (); #ifndef MAP_COPY @@ -2079,6 +2389,32 @@ a filename can be specified using the LD_DEBUG_OUTPUT environment variable.\n"); } } +static void +process_dl_audit (char *str) +{ + /* The parameter is a colon separated list of DSO names. */ + char *p; + + while ((p = (strsep) (&str, ":")) != NULL) + if (p[0] != '\0' + && (__builtin_expect (! INTUSE(__libc_enable_secure), 1) + || strchr (p, '/') == NULL)) + { + /* This is using the local malloc, not the system malloc. The + memory can never be freed. */ + struct audit_list *newp = malloc (sizeof (*newp)); + newp->name = p; + + if (audit_list == NULL) + audit_list = newp->next = newp; + else + { + newp->next = audit_list->next; + audit_list = audit_list->next = newp; + } + } +} + /* Process all environments variables the dynamic linker must recognize. Since all of them start with `LD_' we are a bit smarter while finding all the entries. */ @@ -2121,7 +2457,12 @@ process_envvars (enum mode *modep) case 5: /* Debugging of the dynamic linker? */ if (memcmp (envline, "DEBUG", 5) == 0) - process_dl_debug (&envline[6]); + { + process_dl_debug (&envline[6]); + break; + } + if (memcmp (envline, "AUDIT", 5) == 0) + process_dl_audit (&envline[6]); break; case 7: diff --git a/elf/tst-audit1.c b/elf/tst-audit1.c new file mode 100644 index 0000000000..63656b4ee9 --- /dev/null +++ b/elf/tst-audit1.c @@ -0,0 +1 @@ +#include "../io/pwd.c" diff --git a/elf/tst-auditmod1.c b/elf/tst-auditmod1.c new file mode 100644 index 0000000000..108b08a22f --- /dev/null +++ b/elf/tst-auditmod1.c @@ -0,0 +1,153 @@ +#include <dlfcn.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <gnu/lib-names.h> + + +unsigned int +la_version (unsigned int v) +{ + setlinebuf (stdout); + + printf ("version: %u\n", v); + + char buf[20]; + sprintf (buf, "%u", v); + + return v; +} + +void +la_activity (uintptr_t *cookie, unsigned int flag) +{ + if (flag == LA_ACT_CONSISTENT) + printf ("activity: consistent\n"); + else if (flag == LA_ACT_ADD) + printf ("activity: add\n"); + else if (flag == LA_ACT_DELETE) + printf ("activity: delete\n"); + else + printf ("activity: unknown activity %u\n", flag); +} + +char * +la_objsearch (const char *name, uintptr_t *cookie, unsigned int flag) +{ + char buf[100]; + const char *flagstr; + if (flag == LA_SER_ORIG) + flagstr = "LA_SET_ORIG"; + else if (flag == LA_SER_LIBPATH) + flagstr = "LA_SER_LIBPATH"; + else if (flag == LA_SER_RUNPATH) + flagstr = "LA_SER_RUNPATH"; + else if (flag == LA_SER_CONFIG) + flagstr = "LA_SER_CONFIG"; + else if (flag == LA_SER_DEFAULT) + flagstr = "LA_SER_DEFAULT"; + else if (flag == LA_SER_SECURE) + flagstr = "LA_SER_SECURE"; + else + { + sprintf (buf, "unknown flag %d", flag); + flagstr = buf; + } + printf ("objsearch: %s, %s\n", name, flagstr); + + return (char *) name; +} + +unsigned int +la_objopen (struct link_map *l, Lmid_t lmid, uintptr_t *cookie) +{ + printf ("objopen: %ld, %s\n", lmid, l->l_name); + + return 3; +} + +void +la_preinit (uintptr_t *cookie) +{ + printf ("preinit\n"); +} + +unsigned int +la_objclose (uintptr_t *cookie) +{ + printf ("objclose\n"); + return 0; +} + +uintptr_t +la_symbind32 (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, unsigned int *flags, const char *symname) +{ + printf ("symbind32: symname=%s, st_value=%#lx, ndx=%u, flags=%u\n", + symname, (long int) sym->st_value, ndx, *flags); + + return sym->st_value; +} + +uintptr_t +la_symbind64 (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, unsigned int *flags, const char *symname) +{ + printf ("symbind64: symname=%s, st_value=%#lx, ndx=%u, flags=%u\n", + symname, (long int) sym->st_value, ndx, *flags); + + return sym->st_value; +} + +#ifdef __i386__ +Elf32_Addr +la_i86_gnu_pltenter (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, La_i86_regs *regs, + unsigned int *flags, const char *symname, + long int *framesizep) +{ + printf ("i86_pltenter: symname=%s, st_value=%#lx, ndx=%u, flags=%u\n", + symname, (long int) sym->st_value, ndx, *flags); + + return sym->st_value; +} + +unsigned int +la_i86_gnu_pltexit (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, const La_i86_regs *inregs, + La_i86_retval *outregs, const char *symname) +{ + printf ("i86_pltexit: symname=%s, st_value=%#lx, ndx=%u, retval=%tu\n", + symname, (long int) sym->st_value, ndx, outregs->lrv_eax); + + return 0; +} +#endif + + +#ifdef __x86_64__ +uintptr_t +la_x86_64_gnu_pltenter (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, La_x86_64_regs *regs, + unsigned int *flags, const char *symname, + long int *framesizep) +{ + printf ("x86_64_pltenter: symname=%s, st_value=%#lx, ndx=%u, flags=%u\n", + symname, (long int) sym->st_value, ndx, *flags); + + return sym->st_value; +} + +unsigned int +la_x86_64_gnu_pltexit (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, const La_x86_64_regs *inregs, + La_x86_64_retval *outregs, const char *symname) +{ + printf ("x86_64_pltexit: symname=%s, st_value=%#lx, ndx=%u, retval=%tu\n", + symname, (long int) sym->st_value, ndx, outregs->lrv_rax); + + return 0; +} +#endif diff --git a/include/dlfcn.h b/include/dlfcn.h index bfa1b9041b..460c037ed1 100644 --- a/include/dlfcn.h +++ b/include/dlfcn.h @@ -7,9 +7,24 @@ #define __RTLD_SPROF 0x40000000 #define __RTLD_OPENEXEC 0x20000000 #define __RTLD_CALLMAP 0x10000000 +#define __RTLD_AUDIT 0x08000000 #define __LM_ID_CALLER -2 +#ifdef SHARED +/* Locally stored program arguments. */ +extern int __dlfcn_argc attribute_hidden; +extern char **__dlfcn_argv attribute_hidden; +#else +/* These variables are defined and initialized in the startup code. */ +extern int __libc_argc attribute_hidden; +extern char **__libc_argv attribute_hidden; + +# define __dlfcn_argc __libc_argc +# define __dlfcn_argv __libc_argv +#endif + + /* Now define the internal interfaces. */ #define __libc_dlopen(name) \ @@ -29,18 +44,8 @@ extern int _dl_addr (const void *address, Dl_info *info, libc_hidden_proto (_dl_addr) #endif -/* Open the shared object NAME, relocate it, and run its initializer if it - hasn't already been run. MODE is as for `dlopen' (see <dlfcn.h>). If - the object is already opened, returns its existing map. */ -extern void *_dl_open (const char *name, int mode, const void *caller, - Lmid_t nsid) - internal_function; -libc_hidden_proto (_dl_open) - /* Close an object previously opened by _dl_open. */ -extern void _dl_close (void *map) - internal_function; -libc_hidden_proto (_dl_close) +extern void _dl_close (void *map) attribute_hidden; /* Look up NAME in shared object HANDLE (which may be RTLD_DEFAULT or RTLD_NEXT). WHO is the calling function, for RTLD_NEXT. Returns diff --git a/include/link.h b/include/link.h index 3078b72a87..49e0e1b041 100644 --- a/include/link.h +++ b/include/link.h @@ -34,6 +34,7 @@ #include <bits/elfclass.h> /* Defines __ELF_NATIVE_CLASS. */ #include <bits/link.h> +#include <bits/linkmap.h> #include <dl-lookupcfg.h> #include <tls.h> /* Defines USE_TLS. */ @@ -199,6 +200,10 @@ struct link_map should be called on this link map when relocation finishes. */ unsigned int l_used:1; /* Nonzero if the DSO is used. */ + unsigned int l_auditing:1; /* Nonzero if the DSO is used in auditing. */ + unsigned int l_audit_any_plt:1; /* Nonzero if at least one audit module + is interested in the PLT interception.*/ + /* Array with version names. */ unsigned int l_nversions; struct r_found_version *l_versions; @@ -207,7 +212,14 @@ struct link_map struct r_search_path_struct l_rpath_dirs; /* Collected results of relocation while profiling. */ - ElfW(Addr) *l_reloc_result; + struct reloc_result + { + ElfW(Addr) addr; + struct link_map *bound; + unsigned int boundndx; + uint32_t enterexit; + unsigned int flags; + } *l_reloc_result; /* Pointer to the version information if available. */ ElfW(Versym) *l_versyms; @@ -263,11 +275,7 @@ struct link_map { const ElfW(Sym) *sym; int type_class; -#ifdef DL_LOOKUP_RETURNS_MAP struct link_map *value; -#else - ElfW(Addr) value; -#endif const ElfW(Sym) *ret; } l_lookup_cache; @@ -297,8 +305,65 @@ struct link_map done. */ ElfW(Addr) l_relro_addr; size_t l_relro_size; + + /* Audit information. This array apparent must be the last in the + structure. Never add something after it. */ + struct auditstate + { + uintptr_t cookie; + unsigned int bindflags; + } l_audit[0]; + }; + +/* Version numbers for la_version handshake interface. */ +#define LAV_CURRENT 1 + +/* Activity types signaled through la_activity. */ +enum + { + LA_ACT_CONSISTENT, + LA_ACT_ADD, + LA_ACT_DELETE + }; + +/* Values representing origin of name for dynamic loading. */ +enum + { + LA_SER_ORIG = 0x01, /* Original name. */ + LA_SER_LIBPATH = 0x02, /* Directory from LD_LIBRARY_PATH. */ + LA_SER_RUNPATH = 0x04, /* Directory from RPATH/RUNPATH. */ + LA_SER_CONFIG = 0x08, /* Found through ldconfig. */ + LA_SER_DEFAULT = 0x40, /* Default directory. */ + LA_SER_SECURE = 0x80 /* Unused. */ + }; + +/* Values for la_objopen return value. */ +enum + { + LA_FLG_BINDTO = 0x01, /* Audit symbols bound to this object. */ + LA_FLG_BINDFROM = 0x02 /* Audit symbols bound from this object. */ }; +/* Values for la_symbind flags parameter. */ +enum + { + LA_SYMB_NOPLTENTER = 0x01, /* la_pltenter will not be called. */ + LA_SYMB_NOPLTEXIT = 0x02, /* la_pltexit will not be called. */ + LA_SYMB_STRUCTCALL = 0x04, /* Return value is a structure. */ + LA_SYMB_DLSYM = 0x08, /* Binding due to dlsym call. */ + LA_SYMB_ALTVALUE = 0x10 /* Value has been changed by a previous + la_symbind call. */ + }; + +#if __ELF_NATIVE_CLASS == 32 +# define symbind symbind32 +# define pltenter plt +#elif __ELF_NATIVE_CLASS == 64 +# define symbind symbind64 +#else +# error "__ELF_NATIVE_CLASS must be defined" +#endif + struct dl_phdr_info { ElfW(Addr) dlpi_addr; diff --git a/nptl/ChangeLog b/nptl/ChangeLog index 19ce0d7e1e..61aca263b3 100644 --- a/nptl/ChangeLog +++ b/nptl/ChangeLog @@ -1,3 +1,18 @@ +2005-01-06 Ulrich Drepper <drepper@redhat.com> + + * allocatestack.c (init_one_static_tls): Adjust initialization of DTV + entry for static tls deallocation fix. + * sysdeps/alpha/tls.h (dtv_t): Change pointer type to be struct which + also contains information whether the memory pointed to is static + TLS or not. + * sysdeps/i386/tls.h: Likewise. + * sysdeps/ia64/tls.h: Likewise. + * sysdeps/powerpc/tls.h: Likewise. + * sysdeps/s390/tls.h: Likewise. + * sysdeps/sh/tls.h: Likewise. + * sysdeps/sparc/tls.h: Likewise. + * sysdeps/x86_64/tls.h: Likewise. + 2004-12-27 Ulrich Drepper <drepper@redhat.com> * init.c (__pthread_initialize_minimal_internal): Use __sigemptyset. diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c index 8875209a11..fcb6c6e475 100644 --- a/nptl/allocatestack.c +++ b/nptl/allocatestack.c @@ -920,7 +920,8 @@ init_one_static_tls (struct pthread *curp, struct link_map *map) # endif /* Fill in the DTV slot so that a later LD/GD access will find it. */ - dtv[map->l_tls_modid].pointer = dest; + dtv[map->l_tls_modid].pointer.val = dest; + dtv[map->l_tls_modid].pointer.is_static = true; /* Initialize the memory. */ memset (__mempcpy (dest, map->l_tls_initimage, map->l_tls_initimage_size), diff --git a/nptl/sysdeps/alpha/tls.h b/nptl/sysdeps/alpha/tls.h index bc6630953f..fa3c832a68 100644 --- a/nptl/sysdeps/alpha/tls.h +++ b/nptl/sysdeps/alpha/tls.h @@ -1,5 +1,5 @@ /* Definition for thread-local data handling. NPTL/Alpha version. - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -23,6 +23,7 @@ # include <dl-sysdep.h> #ifndef __ASSEMBLER__ +# include <stdbool.h> # include <stddef.h> # include <stdint.h> @@ -30,7 +31,11 @@ typedef union dtv { size_t counter; - void *pointer; + struct + { + void *val; + bool is_static; + } pointer; } dtv_t; #else /* __ASSEMBLER__ */ diff --git a/nptl/sysdeps/i386/tls.h b/nptl/sysdeps/i386/tls.h index 945a4c71d6..e243f8b2cf 100644 --- a/nptl/sysdeps/i386/tls.h +++ b/nptl/sysdeps/i386/tls.h @@ -22,6 +22,7 @@ #include <dl-sysdep.h> #ifndef __ASSEMBLER__ +# include <stdbool.h> # include <stddef.h> # include <stdint.h> # include <stdlib.h> @@ -32,7 +33,11 @@ typedef union dtv { size_t counter; - void *pointer; + struct + { + void *val; + bool is_static; + } pointer; } dtv_t; diff --git a/nptl/sysdeps/ia64/tls.h b/nptl/sysdeps/ia64/tls.h index 4591a415c0..a435f966ca 100644 --- a/nptl/sysdeps/ia64/tls.h +++ b/nptl/sysdeps/ia64/tls.h @@ -1,5 +1,5 @@ /* Definition for thread-local data handling. nptl/IA-64 version. - Copyright (C) 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -22,6 +22,7 @@ #include <dl-sysdep.h> #ifndef __ASSEMBLER__ +# include <stdbool.h> # include <stddef.h> # include <stdint.h> # include <stdlib.h> @@ -32,7 +33,11 @@ typedef union dtv { size_t counter; - void *pointer; + struct + { + void *val; + bool is_static; + } pointer; } dtv_t; diff --git a/nptl/sysdeps/powerpc/tls.h b/nptl/sysdeps/powerpc/tls.h index ce7f5bd53d..a7f69074e9 100644 --- a/nptl/sysdeps/powerpc/tls.h +++ b/nptl/sysdeps/powerpc/tls.h @@ -1,5 +1,5 @@ /* Definition for thread-local data handling. NPTL/PowerPC version. - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -23,6 +23,7 @@ # include <dl-sysdep.h> #ifndef __ASSEMBLER__ +# include <stdbool.h> # include <stddef.h> # include <stdint.h> @@ -30,7 +31,11 @@ typedef union dtv { size_t counter; - void *pointer; + struct + { + void *val; + bool is_static; + } pointer; } dtv_t; #else /* __ASSEMBLER__ */ diff --git a/nptl/sysdeps/s390/tls.h b/nptl/sysdeps/s390/tls.h index c9b991df32..e93f3d080c 100644 --- a/nptl/sysdeps/s390/tls.h +++ b/nptl/sysdeps/s390/tls.h @@ -1,5 +1,5 @@ /* Definition for thread-local data handling. NPTL/s390 version. - Copyright (C) 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -22,6 +22,7 @@ #include <dl-sysdep.h> #ifndef __ASSEMBLER__ +# include <stdbool.h> # include <stddef.h> # include <stdint.h> # include <stdlib.h> @@ -32,7 +33,11 @@ typedef union dtv { size_t counter; - void *pointer; + struct + { + void *val; + bool is_static; + } pointer; } dtv_t; diff --git a/nptl/sysdeps/sh/tls.h b/nptl/sysdeps/sh/tls.h index db490ab7ee..e883bae993 100644 --- a/nptl/sysdeps/sh/tls.h +++ b/nptl/sysdeps/sh/tls.h @@ -1,5 +1,5 @@ /* Definition for thread-local data handling. NPTL/SH version. - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -23,6 +23,7 @@ # include <dl-sysdep.h> #ifndef __ASSEMBLER__ +# include <stdbool.h> # include <stddef.h> # include <stdint.h> @@ -30,7 +31,11 @@ typedef union dtv { size_t counter; - void *pointer; + struct + { + void *val; + bool is_static; + } pointer; } dtv_t; typedef struct diff --git a/nptl/sysdeps/sparc/tls.h b/nptl/sysdeps/sparc/tls.h index 8980f9fc0c..8f54a0bb23 100644 --- a/nptl/sysdeps/sparc/tls.h +++ b/nptl/sysdeps/sparc/tls.h @@ -1,5 +1,5 @@ /* Definitions for thread-local data handling. NPTL/sparc version. - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2005 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -22,6 +22,7 @@ #include <dl-sysdep.h> #ifndef __ASSEMBLER__ +# include <stdbool.h> # include <stddef.h> # include <stdint.h> # include <stdlib.h> @@ -31,7 +32,11 @@ typedef union dtv { size_t counter; - void *pointer; + struct + { + void *val; + bool is_static; + } pointer; } dtv_t; typedef struct diff --git a/nptl/sysdeps/x86_64/tls.h b/nptl/sysdeps/x86_64/tls.h index 3d6111f4e3..12da9dc81f 100644 --- a/nptl/sysdeps/x86_64/tls.h +++ b/nptl/sysdeps/x86_64/tls.h @@ -1,5 +1,5 @@ /* Definition for thread-local data handling. nptl/x86_64 version. - Copyright (C) 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -22,6 +22,7 @@ #include <asm/prctl.h> /* For ARCH_SET_FS. */ #ifndef __ASSEMBLER__ +# include <stdbool.h> # include <stddef.h> # include <stdint.h> # include <stdlib.h> @@ -31,7 +32,11 @@ typedef union dtv { size_t counter; - void *pointer; + struct + { + void *val; + bool is_static; + } pointer; } dtv_t; diff --git a/sysdeps/arm/bits/link.h b/sysdeps/arm/bits/link.h index 648976d7d2..e69de29bb2 100644 --- a/sysdeps/arm/bits/link.h +++ b/sysdeps/arm/bits/link.h @@ -1,4 +0,0 @@ -struct link_map_machine - { - Elf32_Addr plt; /* Address of .plt */ - }; diff --git a/sysdeps/arm/bits/linkmap.h b/sysdeps/arm/bits/linkmap.h new file mode 100644 index 0000000000..648976d7d2 --- /dev/null +++ b/sysdeps/arm/bits/linkmap.h @@ -0,0 +1,4 @@ +struct link_map_machine + { + Elf32_Addr plt; /* Address of .plt */ + }; diff --git a/sysdeps/generic/bits/link.h b/sysdeps/generic/bits/link.h index 470b4d3e5f..6b4f811c25 100644 --- a/sysdeps/generic/bits/link.h +++ b/sysdeps/generic/bits/link.h @@ -1,4 +1 @@ -struct link_map_machine - { - /* empty by default */ - }; +#error "Architecture-specific definition needed." diff --git a/sysdeps/generic/bits/linkmap.h b/sysdeps/generic/bits/linkmap.h new file mode 100644 index 0000000000..470b4d3e5f --- /dev/null +++ b/sysdeps/generic/bits/linkmap.h @@ -0,0 +1,4 @@ +struct link_map_machine + { + /* empty by default */ + }; diff --git a/sysdeps/generic/dl-lookupcfg.h b/sysdeps/generic/dl-lookupcfg.h index f48cb0a844..39c430b41e 100644 --- a/sysdeps/generic/dl-lookupcfg.h +++ b/sysdeps/generic/dl-lookupcfg.h @@ -1,5 +1,5 @@ /* Configuration of lookup functions. - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -17,16 +17,4 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -/* Some platforms need more information from the symbol lookup function - than just the address. But this is not generally the case. - - However, because of how _dl_sym and _dl_tls_symaddr are written, every - platform needs it when we support TLS. */ - -#include <tls.h> /* Defines USE_TLS (or doesn't). */ - -#ifdef USE_TLS -# define DL_LOOKUP_RETURNS_MAP -#else -# undef DL_LOOKUP_RETURNS_MAP -#endif +/* Nothing special. */ diff --git a/sysdeps/generic/dl-tls.c b/sysdeps/generic/dl-tls.c index 2282dda9cc..10b7f2c65a 100644 --- a/sysdeps/generic/dl-tls.c +++ b/sysdeps/generic/dl-tls.c @@ -1,5 +1,5 @@ /* Thread-local storage handling in the ELF dynamic linker. Generic version. - Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -18,6 +18,8 @@ 02111-1307 USA. */ #include <assert.h> +#include <errno.h> +#include <libintl.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> @@ -116,10 +118,9 @@ void internal_function _dl_determine_tlsoffset (void) { - struct dtv_slotinfo *slotinfo; size_t max_align = TLS_TCB_ALIGN; - size_t offset, freetop = 0, freebottom = 0; - size_t cnt; + size_t freetop = 0; + size_t freebottom = 0; /* The first element of the dtv slot info list is allocated. */ assert (GL(dl_tls_dtv_slotinfo_list) != NULL); @@ -127,7 +128,7 @@ _dl_determine_tlsoffset (void) dl_tls_dtv_slotinfo_list list. */ assert (GL(dl_tls_dtv_slotinfo_list)->next == NULL); - slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo; + struct dtv_slotinfo *slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo; /* Determining the offset of the various parts of the static TLS block has several dependencies. In addition we have to work @@ -159,9 +160,9 @@ _dl_determine_tlsoffset (void) # if TLS_TCB_AT_TP /* We simply start with zero. */ - offset = 0; + size_t offset = 0; - for (cnt = 1; slotinfo[cnt].map != NULL; ++cnt) + for (size_t cnt = 0; slotinfo[cnt].map != NULL; ++cnt) { assert (cnt < GL(dl_tls_dtv_slotinfo_list)->len); @@ -206,9 +207,9 @@ _dl_determine_tlsoffset (void) + TLS_TCB_SIZE); # elif TLS_DTV_AT_TP /* The TLS blocks start right after the TCB. */ - offset = TLS_TCB_SIZE; + size_t offset = TLS_TCB_SIZE; - for (cnt = 1; slotinfo[cnt].map != NULL; ++cnt) + for (size_t cnt = 0; slotinfo[cnt].map != NULL; ++cnt) { assert (cnt < GL(dl_tls_dtv_slotinfo_list)->len); @@ -225,8 +226,8 @@ _dl_determine_tlsoffset (void) if (off + slotinfo[cnt].map->l_tls_blocksize - firstbyte <= freetop) { slotinfo[cnt].map->l_tls_offset = off - firstbyte; - freebottom = off + slotinfo[cnt].map->l_tls_blocksize - - firstbyte; + freebottom = (off + slotinfo[cnt].map->l_tls_blocksize + - firstbyte); continue; } } @@ -357,14 +358,14 @@ _dl_allocate_tls_storage (void) /* Clear the TCB data structure. We can't ask the caller (i.e. libpthread) to do it, because we will initialize the DTV et al. */ - memset (result, 0, TLS_TCB_SIZE); + memset (result, '\0', TLS_TCB_SIZE); # elif TLS_DTV_AT_TP result = (char *) result + size - GL(dl_tls_static_size); /* Clear the TCB data structure and TLS_PRE_TCB_SIZE bytes before it. We can't ask the caller (i.e. libpthread) to do it, because we will initialize the DTV et al. */ - memset ((char *) result - TLS_PRE_TCB_SIZE, 0, + memset ((char *) result - TLS_PRE_TCB_SIZE, '\0', TLS_PRE_TCB_SIZE + TLS_TCB_SIZE); # endif @@ -388,10 +389,11 @@ _dl_allocate_tls_init (void *result) dtv_t *dtv = GET_DTV (result); struct dtv_slotinfo_list *listp; size_t total = 0; + size_t maxgen = 0; - /* We have to look prepare the dtv for all currently loaded - modules using TLS. For those which are dynamically loaded we - add the values indicating deferred allocation. */ + /* We have to prepare the dtv for all currently loaded modules using + TLS. For those which are dynamically loaded we add the values + indicating deferred allocation. */ listp = GL(dl_tls_dtv_slotinfo_list); while (1) { @@ -411,11 +413,16 @@ _dl_allocate_tls_init (void *result) /* Unused entry. */ continue; + /* Keep track of the maximum generation number. This might + not be the generation counter. */ + maxgen = MAX (maxgen, listp->slotinfo[cnt].gen); + if (map->l_tls_offset == NO_TLS_OFFSET) { /* For dynamically loaded modules we simply store the value indicating deferred allocation. */ - dtv[map->l_tls_modid].pointer = TLS_DTV_UNALLOCATED; + dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED; + dtv[map->l_tls_modid].pointer.is_static = false; continue; } @@ -431,7 +438,8 @@ _dl_allocate_tls_init (void *result) # endif /* Copy the initialization image and clear the BSS part. */ - dtv[map->l_tls_modid].pointer = dest; + dtv[map->l_tls_modid].pointer.val = dest; + dtv[map->l_tls_modid].pointer.is_static = true; memset (__mempcpy (dest, map->l_tls_initimage, map->l_tls_initimage_size), '\0', map->l_tls_blocksize - map->l_tls_initimage_size); @@ -445,6 +453,9 @@ _dl_allocate_tls_init (void *result) assert (listp != NULL); } + /* The DTV version is up-to-date now. */ + dtv[0].counter = maxgen; + return result; } rtld_hidden_def (_dl_allocate_tls_init) @@ -466,6 +477,12 @@ _dl_deallocate_tls (void *tcb, bool dealloc_tcb) { dtv_t *dtv = GET_DTV (tcb); + /* We need to free the memory allocated for non-static TLS. */ + for (size_t cnt = 0; cnt < dtv[-1].counter; ++cnt) + if (! dtv[1 + cnt].pointer.is_static + && dtv[1 + cnt].pointer.val != TLS_DTV_UNALLOCATED) + free (dtv[1 + cnt].pointer.val); + /* The array starts with dtv[-1]. */ #ifdef SHARED if (dtv != GL(dl_initial_dtv)) @@ -524,166 +541,172 @@ allocate_and_init (struct link_map *map) } -/* The generic dynamic and local dynamic model cannot be used in - statically linked applications. */ -void * -__tls_get_addr (GET_ADDR_ARGS) +struct link_map * +_dl_update_slotinfo (unsigned long int req_modid) { - dtv_t *dtv = THREAD_DTV (); struct link_map *the_map = NULL; - void *p; + dtv_t *dtv = THREAD_DTV (); - if (__builtin_expect (dtv[0].counter != GL(dl_tls_generation), 0)) + /* The global dl_tls_dtv_slotinfo array contains for each module + index the generation counter current when the entry was created. + This array never shrinks so that all module indices which were + valid at some time can be used to access it. Before the first + use of a new module index in this function the array was extended + appropriately. Access also does not have to be guarded against + modifications of the array. It is assumed that pointer-size + values can be read atomically even in SMP environments. It is + possible that other threads at the same time dynamically load + code and therefore add to the slotinfo list. This is a problem + since we must not pick up any information about incomplete work. + The solution to this is to ignore all dtv slots which were + created after the one we are currently interested. We know that + dynamic loading for this module is completed and this is the last + load operation we know finished. */ + unsigned long int idx = req_modid; + struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list); + + while (idx >= listp->len) { - struct dtv_slotinfo_list *listp; - size_t idx; - - /* The global dl_tls_dtv_slotinfo array contains for each module - index the generation counter current when the entry was - created. This array never shrinks so that all module indices - which were valid at some time can be used to access it. - Before the first use of a new module index in this function - the array was extended appropriately. Access also does not - have to be guarded against modifications of the array. It is - assumed that pointer-size values can be read atomically even - in SMP environments. It is possible that other threads at - the same time dynamically load code and therefore add to the - slotinfo list. This is a problem since we must not pick up - any information about incomplete work. The solution to this - is to ignore all dtv slots which were created after the one - we are currently interested. We know that dynamic loading - for this module is completed and this is the last load - operation we know finished. */ - idx = GET_ADDR_MODULE; - listp = GL(dl_tls_dtv_slotinfo_list); - while (idx >= listp->len) - { - idx -= listp->len; - listp = listp->next; - } + idx -= listp->len; + listp = listp->next; + } - if (dtv[0].counter < listp->slotinfo[idx].gen) + if (dtv[0].counter < listp->slotinfo[idx].gen) + { + /* The generation counter for the slot is higher than what the + current dtv implements. We have to update the whole dtv but + only those entries with a generation counter <= the one for + the entry we need. */ + size_t new_gen = listp->slotinfo[idx].gen; + size_t total = 0; + + /* We have to look through the entire dtv slotinfo list. */ + listp = GL(dl_tls_dtv_slotinfo_list); + do { - /* The generation counter for the slot is higher than what - the current dtv implements. We have to update the whole - dtv but only those entries with a generation counter <= - the one for the entry we need. */ - size_t new_gen = listp->slotinfo[idx].gen; - size_t total = 0; - - /* We have to look through the entire dtv slotinfo list. */ - listp = GL(dl_tls_dtv_slotinfo_list); - do + for (size_t cnt = total == 0 ? 1 : 0; cnt < listp->len; ++cnt) { - size_t cnt; - - for (cnt = total == 0 ? 1 : 0; cnt < listp->len; ++cnt) + size_t gen = listp->slotinfo[cnt].gen; + + if (gen > new_gen) + /* This is a slot for a generation younger than the + one we are handling now. It might be incompletely + set up so ignore it. */ + continue; + + /* If the entry is older than the current dtv layout we + know we don't have to handle it. */ + if (gen <= dtv[0].counter) + continue; + + /* If there is no map this means the entry is empty. */ + struct link_map *map = listp->slotinfo[cnt].map; + if (map == NULL) { - size_t gen = listp->slotinfo[cnt].gen; - struct link_map *map; - size_t modid; - - if (gen > new_gen) - /* This is a slot for a generation younger than - the one we are handling now. It might be - incompletely set up so ignore it. */ - continue; - - /* If the entry is older than the current dtv layout - we know we don't have to handle it. */ - if (gen <= dtv[0].counter) - continue; - - /* If there is no map this means the entry is empty. */ - map = listp->slotinfo[cnt].map; - if (map == NULL) + /* If this modid was used at some point the memory + might still be allocated. */ + if (! dtv[total + cnt].pointer.is_static + && dtv[total + cnt].pointer.val != TLS_DTV_UNALLOCATED) { - /* If this modid was used at some point the memory - might still be allocated. */ - if (dtv[total + cnt].pointer != TLS_DTV_UNALLOCATED) - { - free (dtv[total + cnt].pointer); - dtv[total + cnt].pointer = TLS_DTV_UNALLOCATED; - } - - continue; + free (dtv[total + cnt].pointer.val); + dtv[total + cnt].pointer.val = TLS_DTV_UNALLOCATED; } - /* Check whether the current dtv array is large enough. */ - modid = map->l_tls_modid; - assert (total + cnt == modid); - if (dtv[-1].counter < modid) + continue; + } + + /* Check whether the current dtv array is large enough. */ + size_t modid = map->l_tls_modid; + assert (total + cnt == modid); + if (dtv[-1].counter < modid) + { + /* Reallocate the dtv. */ + dtv_t *newp; + size_t newsize = GL(dl_tls_max_dtv_idx) + DTV_SURPLUS; + size_t oldsize = dtv[-1].counter; + + assert (map->l_tls_modid <= newsize); + + if (dtv == GL(dl_initial_dtv)) { - /* Reallocate the dtv. */ - dtv_t *newp; - size_t newsize = GL(dl_tls_max_dtv_idx) + DTV_SURPLUS; - size_t oldsize = dtv[-1].counter; - - assert (map->l_tls_modid <= newsize); - - if (dtv == GL(dl_initial_dtv)) - { - /* This is the initial dtv that was allocated - during rtld startup using the dl-minimal.c - malloc instead of the real malloc. We can't - free it, we have to abandon the old storage. */ - - newp = malloc ((2 + newsize) * sizeof (dtv_t)); - if (newp == NULL) - oom (); - memcpy (newp, &dtv[-1], oldsize * sizeof (dtv_t)); - } - else - { - newp = realloc (&dtv[-1], - (2 + newsize) * sizeof (dtv_t)); - if (newp == NULL) - oom (); - } - - newp[0].counter = newsize; - - /* Clear the newly allocated part. */ - memset (newp + 2 + oldsize, '\0', - (newsize - oldsize) * sizeof (dtv_t)); - - /* Point dtv to the generation counter. */ - dtv = &newp[1]; - - /* Install this new dtv in the thread data - structures. */ - INSTALL_NEW_DTV (dtv); + /* This is the initial dtv that was allocated + during rtld startup using the dl-minimal.c + malloc instead of the real malloc. We can't + free it, we have to abandon the old storage. */ + + newp = malloc ((2 + newsize) * sizeof (dtv_t)); + if (newp == NULL) + oom (); + memcpy (newp, &dtv[-1], oldsize * sizeof (dtv_t)); } + else + { + newp = realloc (&dtv[-1], + (2 + newsize) * sizeof (dtv_t)); + if (newp == NULL) + oom (); + } + + newp[0].counter = newsize; + + /* Clear the newly allocated part. */ + memset (newp + 2 + oldsize, '\0', + (newsize - oldsize) * sizeof (dtv_t)); + + /* Point dtv to the generation counter. */ + dtv = &newp[1]; - /* If there is currently memory allocate for this - dtv entry free it. */ - /* XXX Ideally we will at some point create a memory - pool. */ - if (dtv[modid].pointer != TLS_DTV_UNALLOCATED) - /* Note that free is called for NULL is well. We - deallocate even if it is this dtv entry we are - supposed to load. The reason is that we call - memalign and not malloc. */ - free (dtv[modid].pointer); - - /* This module is loaded dynamically- We defer - memory allocation. */ - dtv[modid].pointer = TLS_DTV_UNALLOCATED; - - if (modid == GET_ADDR_MODULE) - the_map = map; + /* Install this new dtv in the thread data + structures. */ + INSTALL_NEW_DTV (dtv); } - total += listp->len; + /* If there is currently memory allocate for this + dtv entry free it. */ + /* XXX Ideally we will at some point create a memory + pool. */ + if (! dtv[modid].pointer.is_static + && dtv[modid].pointer.val != TLS_DTV_UNALLOCATED) + /* Note that free is called for NULL is well. We + deallocate even if it is this dtv entry we are + supposed to load. The reason is that we call + memalign and not malloc. */ + free (dtv[modid].pointer.val); + + /* This module is loaded dynamically- We defer memory + allocation. */ + dtv[modid].pointer.is_static = false; + dtv[modid].pointer.val = TLS_DTV_UNALLOCATED; + + if (modid == req_modid) + the_map = map; } - while ((listp = listp->next) != NULL); - /* This will be the new maximum generation counter. */ - dtv[0].counter = new_gen; + total += listp->len; } + while ((listp = listp->next) != NULL); + + /* This will be the new maximum generation counter. */ + dtv[0].counter = new_gen; } - p = dtv[GET_ADDR_MODULE].pointer; + return the_map; +} + + +/* The generic dynamic and local dynamic model cannot be used in + statically linked applications. */ +void * +__tls_get_addr (GET_ADDR_ARGS) +{ + dtv_t *dtv = THREAD_DTV (); + struct link_map *the_map = NULL; + void *p; + + if (__builtin_expect (dtv[0].counter != GL(dl_tls_generation), 0)) + the_map = _dl_update_slotinfo (GET_ADDR_MODULE); + + p = dtv[GET_ADDR_MODULE].pointer.val; if (__builtin_expect (p == TLS_DTV_UNALLOCATED, 0)) { @@ -703,11 +726,74 @@ __tls_get_addr (GET_ADDR_ARGS) the_map = listp->slotinfo[idx].map; } - p = dtv[GET_ADDR_MODULE].pointer = allocate_and_init (the_map); + p = dtv[GET_ADDR_MODULE].pointer.val = allocate_and_init (the_map); + dtv[GET_ADDR_MODULE].pointer.is_static = false; } return (char *) p + GET_ADDR_OFFSET; } # endif + + +void +_dl_add_to_slotinfo (struct link_map *l) +{ + /* Now that we know the object is loaded successfully add + modules containing TLS data to the dtv info table. We + might have to increase its size. */ + struct dtv_slotinfo_list *listp; + struct dtv_slotinfo_list *prevp; + size_t idx = l->l_tls_modid; + + /* Find the place in the dtv slotinfo list. */ + listp = GL(dl_tls_dtv_slotinfo_list); + prevp = NULL; /* Needed to shut up gcc. */ + do + { + /* Does it fit in the array of this list element? */ + if (idx < listp->len) + break; + idx -= listp->len; + prevp = listp; + listp = listp->next; + } + while (listp != NULL); + + if (listp == NULL) + { + /* When we come here it means we have to add a new element + to the slotinfo list. And the new module must be in + the first slot. */ + assert (idx == 0); + + listp = prevp->next = (struct dtv_slotinfo_list *) + malloc (sizeof (struct dtv_slotinfo_list) + + TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo)); + if (listp == NULL) + { + /* We ran out of memory. We will simply fail this + call but don't undo anything we did so far. The + application will crash or be terminated anyway very + soon. */ + + /* We have to do this since some entries in the dtv + slotinfo array might already point to this + generation. */ + ++GL(dl_tls_generation); + + _dl_signal_error (ENOMEM, "dlopen", NULL, N_("\ +cannot create TLS data structures")); + } + + listp->len = TLS_SLOTINFO_SURPLUS; + listp->next = NULL; + memset (listp->slotinfo, '\0', + TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo)); + } + + /* Add the information into the slotinfo data structure. */ + listp->slotinfo[idx].map = l; + listp->slotinfo[idx].gen = GL(dl_tls_generation) + 1; +} #endif /* use TLS */ diff --git a/sysdeps/generic/dl-trampoline.c b/sysdeps/generic/dl-trampoline.c new file mode 100644 index 0000000000..3ca89f3879 --- /dev/null +++ b/sysdeps/generic/dl-trampoline.c @@ -0,0 +1 @@ +#error "Architecture specific PLT trampolines must be defined." diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index ec68e1a565..4a181f4009 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -52,23 +52,15 @@ __BEGIN_DECLS most architectures the entry is already relocated - but for some not and we need to relocate at access time. */ #ifdef DL_RO_DYN_SECTION -# define D_PTR(map,i) (map->i->d_un.d_ptr + map->l_addr) +# define D_PTR(map, i) ((map)->i->d_un.d_ptr + (map)->l_addr) #else -# define D_PTR(map,i) map->i->d_un.d_ptr +# define D_PTR(map, i) (map)->i->d_un.d_ptr #endif -/* On some platforms more information than just the address of the symbol - is needed from the lookup functions. In this case we return the whole - link map. */ -#ifdef DL_LOOKUP_RETURNS_MAP +/* Result of the lookup functions and how to retrieve the base address. */ typedef struct link_map *lookup_t; # define LOOKUP_VALUE(map) map -# define LOOKUP_VALUE_ADDRESS(map) (map ? map->l_addr : 0) -#else -typedef ElfW(Addr) lookup_t; -# define LOOKUP_VALUE(map) map->l_addr -# define LOOKUP_VALUE_ADDRESS(address) address -#endif +# define LOOKUP_VALUE_ADDRESS(map) ((map) ? (map)->l_addr : 0) /* on some architectures a pointer to a function is not just a pointer to the actual code of the function but rather an architecture @@ -182,6 +174,55 @@ enum allowmask }; +/* Type for list of auditing interfaces. */ +struct La_i86_regs; +struct La_i86_retval; +struct La_x86_64_regs; +struct La_x86_64_retval; + + +struct audit_ifaces +{ + void (*activity) (uintptr_t *, unsigned int); + char *(*objsearch) (const char *, uintptr_t *, unsigned int); + unsigned int (*objopen) (struct link_map *, Lmid_t, uintptr_t *); + void (*preinit) (uintptr_t *); + union + { + uintptr_t (*symbind32) (Elf32_Sym *, unsigned int, uintptr_t *, + uintptr_t *, unsigned int *, const char *); + uintptr_t (*symbind64) (Elf64_Sym *, unsigned int, uintptr_t *, + uintptr_t *, unsigned int *, const char *); + }; + union + { + uintptr_t (*i86_gnu_pltenter) (Elf32_Sym *, unsigned int, uintptr_t *, + uintptr_t *, const struct La_i86_regs *, + unsigned int *, const char *name, + long int *framesizep); + uintptr_t (*x86_64_gnu_pltenter) (Elf64_Sym *, unsigned int, uintptr_t *, + uintptr_t *, + const struct La_x86_64_regs *, + unsigned int *, const char *name, + long int *framesizep); + }; + union + { + unsigned int (*i86_gnu_pltexit) (Elf32_Sym *, unsigned int, uintptr_t *, + uintptr_t *, const struct La_i86_regs *, + struct La_i86_retval *, const char *); + unsigned int (*x86_64_gnu_pltexit) (Elf64_Sym *, unsigned int, uintptr_t *, + uintptr_t *, + const struct La_x86_64_regs *, + struct La_x86_64_retval *, + const char *); + }; + unsigned int (*objclose) (uintptr_t *); + + struct audit_ifaces *next; +}; + + /* Test whether given NAME matches any of the names of the given object. */ extern int _dl_name_match_p (const char *__name, struct link_map *__map) internal_function; @@ -224,7 +265,7 @@ struct rtld_global #endif EXTERN struct link_namespaces { - /* And a pointer to the map for the main map. */ + /* A pointer to the map for the main map. */ struct link_map *_ns_loaded; /* Number of object in the _dl_loaded list. */ unsigned int _ns_nloaded; @@ -277,8 +318,12 @@ struct rtld_global EXTERN void **(*_dl_error_catch_tsd) (void) __attribute__ ((const)); #endif - /* Structure describing the dynamic linker itself. */ + /* Structure describing the dynamic linker itself. We need to + reserve memory for the data the audit libraries need. */ EXTERN struct link_map _dl_rtld_map; +#ifdef SHARED + struct auditstate audit_data[DL_NNS]; +#endif #if defined SHARED && defined _LIBC_REENTRANT \ && defined __rtld_lock_default_lock_recursive @@ -311,6 +356,7 @@ struct rtld_global struct dtv_slotinfo { size_t gen; + bool is_static; struct link_map *map; } slotinfo[0]; } *_dl_tls_dtv_slotinfo_list; @@ -483,32 +529,12 @@ struct rtld_global_ro call the function instead of going through the PLT. The result is that we can avoid exporting the functions and we do not jump PLT relocations in libc.so. */ - const char *(*_dl_get_origin) (void); - size_t (*_dl_dst_count) (const char *, int); - char *(*_dl_dst_substitute) (struct link_map *, const char *, char *, int); - struct link_map *(internal_function *_dl_map_object) (struct link_map *, - const char *, int, - int, int, int, Lmid_t); - void (internal_function *_dl_map_object_deps) (struct link_map *, - struct link_map **, - unsigned int, int, int); - void (*_dl_relocate_object) (struct link_map *, struct r_scope_elem *[], - int, int); - int (internal_function *_dl_check_map_versions) (struct link_map *, int, - int); - void (internal_function *_dl_init) (struct link_map *, int, char **, - char **); - void (*_dl_debug_state) (void); -#ifndef MAP_COPY - void (*_dl_unload_cache) (void); -#endif void (*_dl_debug_printf) (const char *, ...) __attribute__ ((__format__ (__printf__, 1, 2))); int (internal_function *_dl_catch_error) (const char **, const char **, void (*) (void *), void *); void (internal_function *_dl_signal_error) (int, const char *, const char *, const char *); - void (internal_function *_dl_start_profile) (void); void (*_dl_mcount) (ElfW(Addr) frompc, ElfW(Addr) selfpc); lookup_t (internal_function *_dl_lookup_symbol_x) (const char *, struct link_map *, @@ -518,7 +544,13 @@ struct rtld_global_ro int, int, struct link_map *); int (*_dl_check_caller) (const void *, enum allowmask); + void *(*_dl_open) (const char *file, int mode, const void *caller_dlopen, + Lmid_t nsid, int argc, char *argv[], char *env[]); + void (*_dl_close) (void *map); + /* List of auditing interfaces. */ + struct audit_ifaces *_dl_audit; + unsigned int _dl_naudit; }; # define __rtld_global_attribute__ # ifdef IS_IN_rtld @@ -911,6 +943,20 @@ extern char *_dl_dst_substitute (struct link_map *l, const char *name, extern int _dl_check_caller (const void *caller, enum allowmask mask) attribute_hidden; +/* Open the shared object NAME, relocate it, and run its initializer if it + hasn't already been run. MODE is as for `dlopen' (see <dlfcn.h>). If + the object is already opened, returns its existing map. */ +extern void *_dl_open (const char *name, int mode, const void *caller, + Lmid_t nsid, int argc, char *argv[], char *env[]) + attribute_hidden; + +/* Add module to slot information data. */ +extern void _dl_add_to_slotinfo (struct link_map *l) attribute_hidden; + +/* Update slot information data for at least the generation of the + module with the given index. */ +extern struct link_map *_dl_update_slotinfo (unsigned long int req_modid); + __END_DECLS #endif /* ldsodefs.h */ diff --git a/sysdeps/generic/libc-start.c b/sysdeps/generic/libc-start.c index ad5ebe0911..5bb8a9b352 100644 --- a/sysdeps/generic/libc-start.c +++ b/sysdeps/generic/libc-start.c @@ -80,6 +80,10 @@ STATIC int LIBC_START_MAIN (int (*main) (int, char **, char ** void *__unbounded stack_end) __attribute__ ((noreturn)); + +/* Note: the fini parameter is ignored here. It used to be registered + with __cxa_atexit. This had the disadvantage that finalizers were + called in more than one place. */ STATIC int LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL), int argc, char *__unbounded *__unbounded ubp_av, @@ -158,10 +162,6 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL), __libc_init_first (argc, argv, __environ); #endif - /* Register the destructor of the program, if any. */ - if (fini) - __cxa_atexit ((void (*) (void *)) fini, NULL, NULL); - #ifndef SHARED /* Some security at this point. Prevent starting a SUID binary where the standard file descriptors are not opened. We have to do this @@ -184,6 +184,22 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL), ); #ifdef SHARED + /* Auditing checkpoint: we have a new object. */ + if (__builtin_expect (GLRO(dl_naudit) > 0, 0)) + { + struct audit_ifaces *afct = GLRO(dl_audit); + struct link_map *head = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->preinit != NULL) + afct->preinit (&head->l_audit[cnt].cookie); + + afct = afct->next; + } + } +#endif + +#ifdef SHARED if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0)) GLRO(dl_debug_printf) ("\ntransferring control: %s\n\n", argv[0]); #endif diff --git a/sysdeps/generic/libc-tls.c b/sysdeps/generic/libc-tls.c index b5ecc36436..b88ede06a2 100644 --- a/sysdeps/generic/libc-tls.c +++ b/sysdeps/generic/libc-tls.c @@ -1,5 +1,5 @@ /* Initialization code for TLS in statically linked application. - Copyright (C) 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -178,17 +178,18 @@ __libc_setup_tls (size_t tcbsize, size_t tcbalign) /* Initialize the TLS block. */ # if TLS_TCB_AT_TP - static_dtv[2].pointer = ((char *) tlsblock + tcb_offset - - roundup (memsz, align ?: 1)); + static_dtv[2].pointer.val = ((char *) tlsblock + tcb_offset + - roundup (memsz, align ?: 1)); static_map.l_tls_offset = roundup (memsz, align ?: 1); # elif TLS_DTV_AT_TP - static_dtv[2].pointer = (char *) tlsblock + tcb_offset; + static_dtv[2].pointer.val = (char *) tlsblock + tcb_offset; static_map.l_tls_offset = tcb_offset; # else # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" # endif + static_dtv[2].pointer.is_static = true; /* sbrk gives us zero'd memory, so we don't need to clear the remainder. */ - memcpy (static_dtv[2].pointer, initimage, filesz); + memcpy (static_dtv[2].pointer.val, initimage, filesz); /* Install the pointer to the dtv. */ diff --git a/sysdeps/generic/unsecvars.h b/sysdeps/generic/unsecvars.h index eb77b260d8..a7378b742f 100644 --- a/sysdeps/generic/unsecvars.h +++ b/sysdeps/generic/unsecvars.h @@ -2,18 +2,19 @@ all stuffed in a single string which means they have to be terminated with a '\0' explicitly. */ #define UNSECURE_ENVVARS \ - "LD_PRELOAD\0" \ - "LD_LIBRARY_PATH\0" \ - "LD_ORIGIN_PATH\0" \ + "GCONV_PATH\0" \ + "GETCONF_DIR\0" \ + "HOSTALIASES\0" \ + "LD_AUDIT\0" \ "LD_DEBUG\0" \ "LD_DEBUG_OUTPUT\0" \ - "LD_PROFILE\0" \ - "LD_USE_LOAD_BIAS\0" \ "LD_DYNAMIC_WEAK\0" \ + "LD_LIBRARY_PATH\0" \ + "LD_ORIGIN_PATH\0" \ + "LD_PRELOAD\0" \ + "LD_PROFILE\0" \ "LD_SHOW_AUXV\0" \ - "GCONV_PATH\0" \ - "GETCONF_DIR\0" \ - "HOSTALIASES\0" \ + "LD_USE_LOAD_BIAS\0" \ "LOCALDOMAIN\0" \ "LOCPATH\0" \ "MALLOC_TRACE\0" \ diff --git a/sysdeps/hppa/bits/link.h b/sysdeps/hppa/bits/link.h index 54842b2299..e69de29bb2 100644 --- a/sysdeps/hppa/bits/link.h +++ b/sysdeps/hppa/bits/link.h @@ -1,6 +0,0 @@ -/* Used to store the function descriptor table */ -struct link_map_machine - { - size_t fptr_table_len; - ElfW(Addr) *fptr_table; - }; diff --git a/sysdeps/hppa/bits/linkmap.h b/sysdeps/hppa/bits/linkmap.h new file mode 100644 index 0000000000..54842b2299 --- /dev/null +++ b/sysdeps/hppa/bits/linkmap.h @@ -0,0 +1,6 @@ +/* Used to store the function descriptor table */ +struct link_map_machine + { + size_t fptr_table_len; + ElfW(Addr) *fptr_table; + }; diff --git a/sysdeps/hppa/dl-lookupcfg.h b/sysdeps/hppa/dl-lookupcfg.h index d393b3e427..84436e7c56 100644 --- a/sysdeps/hppa/dl-lookupcfg.h +++ b/sysdeps/hppa/dl-lookupcfg.h @@ -1,5 +1,5 @@ /* Configuration of lookup functions. - Copyright (C) 2000 Free Software Foundation, Inc. + Copyright (C) 2000, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -17,9 +17,6 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -/* Like IA-64, PA-RISC needs more information from the symbol lookup - function than just the address. */ -#define DL_LOOKUP_RETURNS_MAP #define ELF_FUNCTION_PTR_IS_SPECIAL #define DL_UNMAP_IS_SPECIAL @@ -66,4 +63,3 @@ void _dl_unmap (struct link_map *map); ((Elf32_Addr)(addr) & 2 ? (addr) : DL_AUTO_FUNCTION_ADDRESS (map, addr)) #define DL_DT_FINI_ADDRESS(map, addr) \ ((Elf32_Addr)(addr) & 2 ? (addr) : DL_AUTO_FUNCTION_ADDRESS (map, addr)) - diff --git a/sysdeps/i386/bits/link.h b/sysdeps/i386/bits/link.h index 3be9b7eae8..985d040413 100644 --- a/sysdeps/i386/bits/link.h +++ b/sysdeps/i386/bits/link.h @@ -1,5 +1,60 @@ -struct link_map_machine - { - Elf32_Addr plt; /* Address of .plt + 0x16 */ - Elf32_Addr gotplt; /* Address of .got + 0x0c */ - }; +/* Copyright (C) 2004, 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _LINK_H +# error "Never include <bits/link.h> directly; use <link.h> instead." +#endif + + +/* Registers for entry into PLT on IA-32. */ +typedef struct La_i86_regs +{ + uint32_t lr_edx; + uint32_t lr_ecx; + uint32_t lr_eax; + uint32_t lr_ebp; + uint32_t lr_esp; +} La_i86_regs; + +/* Return values for calls from PLT on IA-32. */ +typedef struct La_i86_retval +{ + uint32_t lrv_eax; + uint32_t lrv_edx; + long double lrv_st0; + long double lrv_st1; +} La_i86_retval; + + +__BEGIN_DECLS + +extern Elf32_Addr la_i86_gnu_pltenter (Elf32_Sym *__sym, unsigned int __ndx, + uintptr_t *__refcook, + uintptr_t *__defcook, + La_i86_regs *__regs, + unsigned int *__flags, + const char *__symname, + long int *__framesizep); +extern unsigned int la_i86_gnu_pltexit (Elf32_Sym *__sym, unsigned int __ndx, + uintptr_t *__refcook, + uintptr_t *__defcook, + const La_i86_regs *__inregs, + La_i86_retval *__outregs, + const char *symname); + +__END_DECLS diff --git a/sysdeps/i386/bits/linkmap.h b/sysdeps/i386/bits/linkmap.h new file mode 100644 index 0000000000..3be9b7eae8 --- /dev/null +++ b/sysdeps/i386/bits/linkmap.h @@ -0,0 +1,5 @@ +struct link_map_machine + { + Elf32_Addr plt; /* Address of .plt + 0x16 */ + Elf32_Addr gotplt; /* Address of .got + 0x0c */ + }; diff --git a/sysdeps/i386/dl-machine.h b/sysdeps/i386/dl-machine.h index c48d9d325e..e1cc10e9cc 100644 --- a/sysdeps/i386/dl-machine.h +++ b/sysdeps/i386/dl-machine.h @@ -129,7 +129,8 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) { got[2] = (Elf32_Addr) &_dl_runtime_profile; - if (_dl_name_match_p (GLRO(dl_profile), l)) + if (GLRO(dl_profile) != NULL + && _dl_name_match_p (GLRO(dl_profile), l)) /* This is the object we are looking for. Say that we really want profiling and the timers are started. */ GL(dl_profile_map) = l; @@ -154,112 +155,18 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) destroys the passed register information. */ /* GKM FIXME: Fix trampoline to pass bounds so we can do without the `__unbounded' qualifier. */ -#define ARCH_FIXUP_ATTRIBUTE __attribute__ ((regparm (3), unused)) +#define ARCH_FIXUP_ATTRIBUTE __attribute__ ((regparm (3), stdcall, unused)) -static ElfW(Addr) fixup (struct link_map *__unbounded l, - ElfW(Word) reloc_offset) +extern ElfW(Addr) _dl_fixup (struct link_map *__unbounded l, + ElfW(Word) reloc_offset) ARCH_FIXUP_ATTRIBUTE; -static ElfW(Addr) profile_fixup (struct link_map *l, ElfW(Word) reloc_offset, - ElfW(Addr) retaddr) +extern ElfW(Addr) _dl_profile_fixup (struct link_map *l, + ElfW(Word) reloc_offset, + ElfW(Addr) retaddr, const void *regs, + long int *framesizep) ARCH_FIXUP_ATTRIBUTE; # endif -/* This code is used in dl-runtime.c to call the `fixup' function - and then redirect to the address it returns. */ -# if !defined PROF && !__BOUNDED_POINTERS__ -# define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\ - .text\n\ - .globl _dl_runtime_resolve\n\ - .type _dl_runtime_resolve, @function\n\ - " CFI_STARTPROC "\n\ - .align 16\n\ -_dl_runtime_resolve:\n\ - " CFI_ADJUST_CFA_OFFSET (8) "\n\ - pushl %eax # Preserve registers otherwise clobbered.\n\ - " CFI_ADJUST_CFA_OFFSET (4) "\n\ - pushl %ecx\n\ - " CFI_ADJUST_CFA_OFFSET (4) "\n\ - pushl %edx\n\ - " CFI_ADJUST_CFA_OFFSET (4) "\n\ - movl 16(%esp), %edx # Copy args pushed by PLT in register. Note\n\ - movl 12(%esp), %eax # that `fixup' takes its parameters in regs.\n\ - call fixup # Call resolver.\n\ - popl %edx # Get register content back.\n\ - " CFI_ADJUST_CFA_OFFSET (-4) "\n\ - popl %ecx\n\ - " CFI_ADJUST_CFA_OFFSET (-4) "\n\ - xchgl %eax, (%esp) # Get %eax contents end store function address.\n\ - ret $8 # Jump to function address.\n\ - " CFI_ENDPROC "\n\ - .size _dl_runtime_resolve, .-_dl_runtime_resolve\n\ -\n\ - .globl _dl_runtime_profile\n\ - .type _dl_runtime_profile, @function\n\ - " CFI_STARTPROC "\n\ - .align 16\n\ -_dl_runtime_profile:\n\ - " CFI_ADJUST_CFA_OFFSET (8) "\n\ - pushl %eax # Preserve registers otherwise clobbered.\n\ - " CFI_ADJUST_CFA_OFFSET (4) "\n\ - pushl %ecx\n\ - " CFI_ADJUST_CFA_OFFSET (4) "\n\ - pushl %edx\n\ - " CFI_ADJUST_CFA_OFFSET (4) "\n\ - movl 20(%esp), %ecx # Load return address\n\ - movl 16(%esp), %edx # Copy args pushed by PLT in register. Note\n\ - movl 12(%esp), %eax # that `fixup' takes its parameters in regs.\n\ - call profile_fixup # Call resolver.\n\ - popl %edx # Get register content back.\n\ - " CFI_ADJUST_CFA_OFFSET (-4) "\n\ - popl %ecx\n\ - " CFI_ADJUST_CFA_OFFSET (-4) "\n\ - xchgl %eax, (%esp) # Get %eax contents end store function address.\n\ - ret $8 # Jump to function address.\n\ - " CFI_ENDPROC "\n\ - .size _dl_runtime_profile, .-_dl_runtime_profile\n\ - .previous\n\ -"); -# else -# define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\n\ - .text\n\ - .globl _dl_runtime_resolve\n\ - .globl _dl_runtime_profile\n\ - .type _dl_runtime_resolve, @function\n\ - .type _dl_runtime_profile, @function\n\ - " CFI_STARTPROC "\n\ - .align 16\n\ -_dl_runtime_resolve:\n\ -_dl_runtime_profile:\n\ - " CFI_ADJUST_CFA_OFFSET (8) "\n\ - pushl %eax # Preserve registers otherwise clobbered.\n\ - " CFI_ADJUST_CFA_OFFSET (4) "\n\ - pushl %ecx\n\ - " CFI_ADJUST_CFA_OFFSET (4) "\n\ - pushl %edx\n\ - " CFI_ADJUST_CFA_OFFSET (4) "\n\ - movl 16(%esp), %edx # Push the arguments for `fixup'\n\ - movl 12(%esp), %eax\n\ - pushl %edx\n\ - " CFI_ADJUST_CFA_OFFSET (4) "\n\ - pushl %eax\n\ - " CFI_ADJUST_CFA_OFFSET (4) "\n\ - call fixup # Call resolver.\n\ - popl %edx # Pop the parameters\n\ - " CFI_ADJUST_CFA_OFFSET (-4) "\n\ - popl %ecx\n\ - " CFI_ADJUST_CFA_OFFSET (-4) "\n\ - popl %edx # Get register content back.\n\ - " CFI_ADJUST_CFA_OFFSET (-4) "\n\ - popl %ecx\n\ - " CFI_ADJUST_CFA_OFFSET (-4) "\n\ - xchgl %eax, (%esp) # Get %eax contents end store function address.\n\ - ret $8 # Jump to function address.\n\ - " CFI_ENDPROC "\n\ - .size _dl_runtime_resolve, .-_dl_runtime_resolve\n\ - .size _dl_runtime_profile, .-_dl_runtime_profile\n\ - .previous\n\ -"); -# endif #endif /* Mask identifying addresses reserved for the user program, @@ -375,9 +282,14 @@ elf_machine_plt_value (struct link_map *map, const Elf32_Rel *reloc, return value; } + +/* Names of the architecture-specific auditing callback functions. */ +#define ARCH_LA_PLTENTER i86_gnu_pltenter +#define ARCH_LA_PLTEXIT i86_gnu_pltexit + #endif /* !dl_machine_h */ -#ifdef RESOLVE +#ifdef RESOLVE_MAP /* The i386 never uses Elf32_Rela relocations for the dynamic linker. Prelinked libraries may use Elf32_Rela though. */ @@ -422,7 +334,7 @@ elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc, #endif /* !RTLD_BOOTSTRAP and have no -z combreloc */ { const Elf32_Sym *const refsym = sym; -#if defined USE_TLS && !defined RTLD_BOOTSTRAP +#ifndef RTLD_BOOTSTRAP struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type); Elf32_Addr value = sym == NULL ? 0 : sym_map->l_addr + sym->st_value; #else @@ -549,14 +461,8 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc, # ifndef RESOLVE_CONFLICT_FIND_MAP const Elf32_Sym *const refsym = sym; # endif -# ifdef USE_TLS struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type); Elf32_Addr value = sym == NULL ? 0 : sym_map->l_addr + sym->st_value; -# else - Elf32_Addr value = RESOLVE (&sym, version, r_type); - if (sym != NULL) - value += sym->st_value; -# endif switch (ELF32_R_TYPE (reloc->r_info)) { @@ -692,4 +598,4 @@ elf_machine_lazy_rela (struct link_map *map, #endif /* !RTLD_BOOTSTRAP */ -#endif /* RESOLVE */ +#endif /* RESOLVE_MAP */ diff --git a/sysdeps/i386/dl-trampoline.S b/sysdeps/i386/dl-trampoline.S new file mode 100644 index 0000000000..80dd300e86 --- /dev/null +++ b/sysdeps/i386/dl-trampoline.S @@ -0,0 +1,182 @@ +/* PLT trampolines. i386 version. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <sysdep.h> + + .text + .globl _dl_runtime_resolve + .type _dl_runtime_resolve, @function + cfi_startproc + .align 16 +_dl_runtime_resolve: + cfi_adjust_cfa_offset (8) + pushl %eax # Preserve registers otherwise clobbered. + cfi_adjust_cfa_offset (4) + pushl %ecx + cfi_adjust_cfa_offset (4) + pushl %edx + cfi_adjust_cfa_offset (4) + movl 16(%esp), %edx # Copy args pushed by PLT in register. Note + movl 12(%esp), %eax # that `fixup' takes its parameters in regs. + call _dl_fixup # Call resolver. + popl %edx # Get register content back. + cfi_adjust_cfa_offset (-4) + popl %ecx + cfi_adjust_cfa_offset (-4) + xchgl %eax, (%esp) # Get %eax contents end store function address. + ret $8 # Jump to function address. + cfi_endproc + .size _dl_runtime_resolve, .-_dl_runtime_resolve + + + .globl _dl_runtime_profile + .type _dl_runtime_profile, @function + cfi_startproc + .align 16 +_dl_runtime_profile: + cfi_adjust_cfa_offset (8) + pushl %esp + cfi_adjust_cfa_offset (4) + addl $8, (%esp) # Account for the pushed PLT data + pushl %ebp + cfi_adjust_cfa_offset (4) + pushl %eax # Preserve registers otherwise clobbered. + cfi_adjust_cfa_offset (4) + pushl %ecx + cfi_adjust_cfa_offset (4) + pushl %edx + cfi_adjust_cfa_offset (4) + movl %esp, %ecx + subl $8, %esp + cfi_adjust_cfa_offset (8) + movl $-1, 4(%esp) + leal 4(%esp), %edx + movl %edx, (%esp) + pushl %ecx # Address of the register structure + cfi_adjust_cfa_offset (4) + movl 40(%esp), %ecx # Load return address + movl 36(%esp), %edx # Copy args pushed by PLT in register. Note + movl 32(%esp), %eax # that `fixup' takes its parameters in regs. + call _dl_profile_fixup # Call resolver. + cfi_adjust_cfa_offset (-8) + movl (%esp), %edx + testl %edx, %edx + jns 1f + popl %edx + cfi_adjust_cfa_offset (-4) + popl %edx # Get register content back. + cfi_adjust_cfa_offset (-4) + popl %ecx + cfi_adjust_cfa_offset (-4) + xchgl %eax, (%esp) # Get %eax contents end store function address. + ret $16 # Jump to function address. + + /* + +32 return address + +28 PLT1 + +24 PLT2 + +20 %esp + +16 %ebp + +12 %eax + +8 %ecx + +4 %edx + %esp free + */ + cfi_adjust_cfa_offset (12) +1: movl %ebx, (%esp) + cfi_rel_offset (3, 0) + movl %edx, %ebx # This is the frame buffer size + pushl %edi + cfi_adjust_cfa_offset (4) + cfi_rel_offset (7, 0) + pushl %esi + cfi_adjust_cfa_offset (4) + cfi_rel_offset (6, 0) + leal 44(%esp), %esi + movl %ebx, %ecx + movl %esp, %edi + subl %ebx, %edi + andl $0xfffffff0, %edi # Align stack + movl %esp, %ebx + cfi_def_cfa_register (3) + movl %edi, %esp + shrl $2, %ecx + rep + movsl + movl (%edi), %esi + cfi_restore (6) + movl 4(%edi), %edi + cfi_restore (7) + /* + %ebx+40 return address + %ebx+36 PLT1 + %ebx+32 PLT2 + %ebx+28 %esp + %ebx+24 %ebp + %ebx+20 %eax + %ebx+16 %ecx + %ebx+12 %edx + %ebx+8 %ebx + %ebx+4 free + %ebx free + %esp copied stack frame + */ + movl %eax, (%ebx) + movl 12(%ebx), %edx + movl 16(%ebx), %ecx + movl 20(%ebx), %eax + call *(%ebx) + movl %ebx, %esp + cfi_def_cfa_register (4) + movl 8(%esp), %ebx + cfi_restore (3) + /* + +40 return address + +36 PLT1 + +32 PLT2 + +28 %esp + +24 %ebp + +20 %eax + +16 %ecx + +12 %edx + +8 free + +4 free + %esp free + */ + subl $20, %esp + cfi_adjust_cfa_offset (20) + movl %eax, (%esp) + movl %edx, 4(%esp) + fstpt 8(%esp) + fstpt 20(%esp) + pushl %esp + cfi_adjust_cfa_offset (4) + leal 36(%esp), %ecx + movl 56(%esp), %eax + movl 60(%esp), %edx + call _dl_call_pltexit + movl (%esp), %eax + movl 4(%esp), %edx + fldt 20(%esp) + fldt 8(%esp) + addl $60, %esp + cfi_adjust_cfa_offset (-60) + ret + cfi_endproc + .size _dl_runtime_profile, .-_dl_runtime_profile diff --git a/sysdeps/ia64/bits/link.h b/sysdeps/ia64/bits/link.h index 7f8b0550d9..e69de29bb2 100644 --- a/sysdeps/ia64/bits/link.h +++ b/sysdeps/ia64/bits/link.h @@ -1,5 +0,0 @@ -struct link_map_machine - { - size_t fptr_table_len; - Elf64_Addr *fptr_table; - }; diff --git a/sysdeps/ia64/bits/linkmap.h b/sysdeps/ia64/bits/linkmap.h new file mode 100644 index 0000000000..7f8b0550d9 --- /dev/null +++ b/sysdeps/ia64/bits/linkmap.h @@ -0,0 +1,5 @@ +struct link_map_machine + { + size_t fptr_table_len; + Elf64_Addr *fptr_table; + }; diff --git a/sysdeps/ia64/dl-lookupcfg.h b/sysdeps/ia64/dl-lookupcfg.h index 0ae3dd68ba..ddf405b551 100644 --- a/sysdeps/ia64/dl-lookupcfg.h +++ b/sysdeps/ia64/dl-lookupcfg.h @@ -1,5 +1,5 @@ /* Configuration of lookup functions. - Copyright (C) 2000, 2001, 2003 Free Software Foundation, Inc. + Copyright (C) 2000, 2001, 2003, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -17,9 +17,6 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -/* The ia64 need more information from the symbol lookup function - than just the address. */ -#define DL_LOOKUP_RETURNS_MAP #define ELF_FUNCTION_PTR_IS_SPECIAL #define DL_UNMAP_IS_SPECIAL diff --git a/sysdeps/linkmap.h b/sysdeps/linkmap.h new file mode 100644 index 0000000000..470b4d3e5f --- /dev/null +++ b/sysdeps/linkmap.h @@ -0,0 +1,4 @@ +struct link_map_machine + { + /* empty by default */ + }; diff --git a/sysdeps/powerpc/powerpc64/dl-lookupcfg.h b/sysdeps/powerpc/powerpc64/dl-lookupcfg.h index e502941015..e69de29bb2 100644 --- a/sysdeps/powerpc/powerpc64/dl-lookupcfg.h +++ b/sysdeps/powerpc/powerpc64/dl-lookupcfg.h @@ -1,22 +0,0 @@ -/* Configuration of lookup functions. PowerPC64 version. - Copyright (C) 2002 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ - -/* Return the symbol map from the symbol lookup function. */ - -#define DL_LOOKUP_RETURNS_MAP 1 diff --git a/sysdeps/s390/bits/link.h b/sysdeps/s390/bits/link.h index fc1fba363a..e69de29bb2 100644 --- a/sysdeps/s390/bits/link.h +++ b/sysdeps/s390/bits/link.h @@ -1,13 +0,0 @@ -#if __WORDSIZE == 64 -struct link_map_machine - { - Elf64_Addr plt; /* Address of .plt + 0x2e */ - Elf64_Addr gotplt; /* Address of .got + 0x18 */ - }; -#else -struct link_map_machine - { - Elf32_Addr plt; /* Address of .plt + 0x2c */ - Elf32_Addr gotplt; /* Address of .got + 0x0c */ - }; -#endif diff --git a/sysdeps/s390/bits/linkmap.h b/sysdeps/s390/bits/linkmap.h new file mode 100644 index 0000000000..fc1fba363a --- /dev/null +++ b/sysdeps/s390/bits/linkmap.h @@ -0,0 +1,13 @@ +#if __WORDSIZE == 64 +struct link_map_machine + { + Elf64_Addr plt; /* Address of .plt + 0x2e */ + Elf64_Addr gotplt; /* Address of .got + 0x18 */ + }; +#else +struct link_map_machine + { + Elf32_Addr plt; /* Address of .plt + 0x2c */ + Elf32_Addr gotplt; /* Address of .got + 0x0c */ + }; +#endif diff --git a/sysdeps/sh/bits/link.h b/sysdeps/sh/bits/link.h index bb2fbb5f16..e69de29bb2 100644 --- a/sysdeps/sh/bits/link.h +++ b/sysdeps/sh/bits/link.h @@ -1,5 +0,0 @@ -struct link_map_machine - { - Elf32_Addr plt; /* Address of .plt + 36 */ - Elf32_Addr gotplt; /* Address of .got + 0x0c */ - }; diff --git a/sysdeps/sh/bits/linkmap.h b/sysdeps/sh/bits/linkmap.h new file mode 100644 index 0000000000..bb2fbb5f16 --- /dev/null +++ b/sysdeps/sh/bits/linkmap.h @@ -0,0 +1,5 @@ +struct link_map_machine + { + Elf32_Addr plt; /* Address of .plt + 36 */ + Elf32_Addr gotplt; /* Address of .got + 0x0c */ + }; diff --git a/sysdeps/x86_64/bits/link.h b/sysdeps/x86_64/bits/link.h index 8ea7157156..87c17eb406 100644 --- a/sysdeps/x86_64/bits/link.h +++ b/sysdeps/x86_64/bits/link.h @@ -1,14 +1,112 @@ -#if __WORDSIZE == 64 -struct link_map_machine - { - Elf64_Addr plt; /* Address of .plt + 0x16 */ - Elf64_Addr gotplt; /* Address of .got + 0x18 */ - }; +/* Copyright (C) 2004, 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _LINK_H +# error "Never include <bits/link.h> directly; use <link.h> instead." +#endif + + +#if __ELF_NATIVE_CLASS == 32 +/* Registers for entry into PLT on IA-32. */ +typedef struct La_i86_regs +{ + uint32_t lr_edx; + uint32_t lr_ecx; + uint32_t lr_eax; + uint32_t lr_ebp; + uint32_t lr_esp; +} La_i86_regs; + +/* Return values for calls from PLT on IA-32. */ +typedef struct La_i86_retval +{ + uint32_t lrv_eax; + uint32_t lrv_edx; + long double lrv_st0; + long double lrv_st1; +} La_i86_retval; + + +__BEGIN_DECLS + +extern Elf32_Addr la_i86_gnu_pltenter (Elf32_Sym *__sym, unsigned int __ndx, + uintptr_t *__refcook, + uintptr_t *__defcook, + La_i86_regs *__regs, + unsigned int *__flags, + const char *__symname, + long int *__framesizep); +extern unsigned int la_i86_gnu_pltexit (Elf32_Sym *__sym, unsigned int __ndx, + uintptr_t *__refcook, + uintptr_t *__defcook, + const La_i86_regs *__inregs, + La_i86_retval *__outregs, + const char *symname); + +__END_DECLS #else -struct link_map_machine - { - Elf32_Addr plt; /* Address of .plt + 0x16 */ - Elf32_Addr gotplt; /* Address of .got + 0x0c */ - }; + +/* Registers for entry into PLT on x86-64. */ +typedef float La_x86_64_xmm __attribute__ ((__mode__ (__V4SF__))); +typedef struct La_x86_64_regs +{ + uint64_t lr_rdx; + uint64_t lr_r8; + uint64_t lr_r9; + uint64_t lr_rcx; + uint64_t lr_rsi; + uint64_t lr_rdi; + uint64_t lr_rbp; + uint64_t lr_rsp; + La_x86_64_xmm lr_xmm[8]; +} La_x86_64_regs; + +/* Return values for calls from PLT on x86-64. */ +typedef struct La_x86_64_retval +{ + uint64_t lrv_rax; + uint64_t lrv_rdx; + La_x86_64_xmm lrv_xmm0; + La_x86_64_xmm lrv_xmm1; + long double lrv_st0; + long double lrv_st1; +} La_x86_64_retval; + + +__BEGIN_DECLS + +extern Elf64_Addr la_x86_64_gnu_pltenter (Elf64_Sym *__sym, + unsigned int __ndx, + uintptr_t *__refcook, + uintptr_t *__defcook, + La_x86_64_regs *__regs, + unsigned int *__flags, + const char *__symname, + long int *__framesizep); +extern unsigned int la_x86_64_gnu_pltexit (Elf64_Sym *__sym, + unsigned int __ndx, + uintptr_t *__refcook, + uintptr_t *__defcook, + const La_x86_64_regs *__inregs, + La_x86_64_retval *__outregs, + const char *symname); + +__END_DECLS + #endif diff --git a/sysdeps/x86_64/bits/linkmap.h b/sysdeps/x86_64/bits/linkmap.h new file mode 100644 index 0000000000..8ea7157156 --- /dev/null +++ b/sysdeps/x86_64/bits/linkmap.h @@ -0,0 +1,14 @@ +#if __WORDSIZE == 64 +struct link_map_machine + { + Elf64_Addr plt; /* Address of .plt + 0x16 */ + Elf64_Addr gotplt; /* Address of .got + 0x18 */ + }; + +#else +struct link_map_machine + { + Elf32_Addr plt; /* Address of .plt + 0x16 */ + Elf32_Addr gotplt; /* Address of .got + 0x0c */ + }; +#endif diff --git a/sysdeps/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h index b932f51d15..18bff95dcd 100644 --- a/sysdeps/x86_64/dl-machine.h +++ b/sysdeps/x86_64/dl-machine.h @@ -116,7 +116,8 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) { got[2] = (Elf64_Addr) &_dl_runtime_profile; - if (_dl_name_match_p (GLRO(dl_profile), l)) + if (GLRO(dl_profile) != NULL + && _dl_name_match_p (GLRO(dl_profile), l)) /* This is the object we are looking for. Say that we really want profiling and the timers are started. */ GL(dl_profile_map) = l; @@ -130,128 +131,6 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) return lazy; } -/* This code is used in dl-runtime.c to call the `fixup' function - and then redirect to the address it returns. */ -#ifndef PROF -# define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\n\ - .text\n\ - .globl _dl_runtime_resolve\n\ - .type _dl_runtime_resolve, @function\n\ - .align 16\n\ - " CFI_STARTPROC "\n\ -_dl_runtime_resolve:\n\ - subq $56,%rsp\n\ - " CFI_ADJUST_CFA_OFFSET(72)" # Incorporate PLT\n\ - movq %rax,(%rsp) # Preserve registers otherwise clobbered.\n\ - movq %rcx,8(%rsp)\n\ - movq %rdx,16(%rsp)\n\ - movq %rsi,24(%rsp)\n\ - movq %rdi,32(%rsp)\n\ - movq %r8,40(%rsp)\n\ - movq %r9,48(%rsp)\n\ - movq 64(%rsp), %rsi # Copy args pushed by PLT in register.\n\ - movq %rsi,%r11 # Multiply by 24\n\ - addq %r11,%rsi\n\ - addq %r11,%rsi\n\ - shlq $3, %rsi\n\ - movq 56(%rsp), %rdi # %rdi: link_map, %rsi: reloc_offset\n\ - call fixup # Call resolver.\n\ - movq %rax, %r11 # Save return value\n\ - movq 48(%rsp),%r9 # Get register content back.\n\ - movq 40(%rsp),%r8\n\ - movq 32(%rsp),%rdi\n\ - movq 24(%rsp),%rsi\n\ - movq 16(%rsp),%rdx\n\ - movq 8(%rsp),%rcx\n\ - movq (%rsp),%rax\n\ - addq $72,%rsp # Adjust stack(PLT did 2 pushes)\n\ - " CFI_ADJUST_CFA_OFFSET(-72)" \n\ - jmp *%r11 # Jump to function address.\n\ - " CFI_ENDPROC "\n\ - .size _dl_runtime_resolve, .-_dl_runtime_resolve\n\ -\n\ - .globl _dl_runtime_profile\n\ - .type _dl_runtime_profile, @function\n\ - .align 16\n\ - " CFI_STARTPROC "\n\ -_dl_runtime_profile:\n\ - subq $56,%rsp\n\ - " CFI_ADJUST_CFA_OFFSET(72)" # Incorporate PLT\n\ - movq %rax,(%rsp) # Preserve registers otherwise clobbered.\n\ - movq %rcx,8(%rsp)\n\ - movq %rdx,16(%rsp)\n\ - movq %rsi,24(%rsp)\n\ - movq %rdi,32(%rsp)\n\ - movq %r8,40(%rsp)\n\ - movq %r9,48(%rsp)\n\ - movq 72(%rsp), %rdx # Load return address if needed\n\ - movq 64(%rsp), %rsi # Copy args pushed by PLT in register.\n\ - movq %rsi,%r11 # Multiply by 24\n\ - addq %r11,%rsi\n\ - addq %r11,%rsi\n\ - shlq $3, %rsi\n\ - movq 56(%rsp), %rdi # %rdi: link_map, %rsi: reloc_offset\n\ - call profile_fixup # Call resolver.\n\ - movq %rax, %r11 # Save return value\n\ - movq 48(%rsp),%r9 # Get register content back.\n\ - movq 40(%rsp),%r8\n\ - movq 32(%rsp),%rdi\n\ - movq 24(%rsp),%rsi\n\ - movq 16(%rsp),%rdx\n\ - movq 8(%rsp),%rcx\n\ - movq (%rsp),%rax\n\ - addq $72,%rsp # Adjust stack\n\ - " CFI_ADJUST_CFA_OFFSET(-72)"\n\ - jmp *%r11 # Jump to function address.\n\ - " CFI_ENDPROC "\n\ - .size _dl_runtime_profile, .-_dl_runtime_profile\n\ - .previous\n\ -"); -#else -# define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ("\n\ - .text\n\ - .globl _dl_runtime_resolve\n\ - .globl _dl_runtime_profile\n\ - .type _dl_runtime_resolve, @function\n\ - .type _dl_runtime_profile, @function\n\ - .align 16\n\ - " CFI_STARTPROC "\n\ -_dl_runtime_resolve:\n\ -_dl_runtime_profile:\n\ - subq $56,%rsp\n\ - " CFI_ADJUST_CFA_OFFSET(72)" # Incorporate PLT\n\ - movq %rax,(%rsp) # Preserve registers otherwise clobbered.\n\ - movq %rcx,8(%rsp)\n\ - movq %rdx,16(%rsp)\n\ - movq %rsi,24(%rsp)\n\ - movq %rdi,32(%rsp)\n\ - movq %r8,40(%rsp)\n\ - movq %r9,48(%rsp)\n\ - movq 64(%rsp), %rsi # Copy args pushed by PLT in register.\n\ - movq %rsi,%r11 # Multiply by 24\n\ - addq %r11,%rsi\n\ - addq %r11,%rsi\n\ - shlq $3, %rsi\n\ - movq 56(%rsp), %rdi # %rdi: link_map, %rsi: reloc_offset\n\ - call fixup # Call resolver.\n\ - movq %rax, %r11 # Save return value\n\ - movq 48(%rsp),%r9 # Get register content back.\n\ - movq 40(%rsp),%r8\n\ - movq 32(%rsp),%rdi\n\ - movq 24(%rsp),%rsi\n\ - movq 16(%rsp),%rdx\n\ - movq 8(%rsp),%rcx\n\ - movq (%rsp),%rax\n\ - addq $72,%rsp # Adjust stack\n\ - " CFI_ADJUST_CFA_OFFSET(-72)"\n\ - jmp *%r11 # Jump to function address.\n\ - " CFI_ENDPROC "\n\ - .size _dl_runtime_resolve, .-_dl_runtime_resolve\n\ - .size _dl_runtime_profile, .-_dl_runtime_profile\n\ - .previous\n\ -"); -#endif - /* Initial entry point code for the dynamic linker. The C function `_dl_start' is the real entry point; its return value is the user program's entry point. */ @@ -348,9 +227,14 @@ elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc, return value; } + +/* Names of the architecture-specific auditing callback functions. */ +#define ARCH_LA_PLTENTER x86_64_gnu_pltenter +#define ARCH_LA_PLTEXIT x86_64_gnu_pltexit + #endif /* !dl_machine_h */ -#ifdef RESOLVE +#ifdef RESOLVE_MAP /* Perform the relocation specified by RELOC and SYM (which is fully resolved). MAP is the object containing the reloc. */ @@ -390,7 +274,7 @@ elf_machine_rela (struct link_map *map, const Elf64_Rela *reloc, #ifndef RTLD_BOOTSTRAP const Elf64_Sym *const refsym = sym; #endif -#if defined USE_TLS && !defined RTLD_BOOTSTRAP +#ifndef RTLD_BOOTSTRAP struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type); Elf64_Addr value = (sym == NULL ? 0 : (Elf64_Addr) sym_map->l_addr + sym->st_value); @@ -553,4 +437,4 @@ elf_machine_lazy_rel (struct link_map *map, _dl_reloc_bad_type (map, r_type, 1); } -#endif /* RESOLVE */ +#endif /* RESOLVE_MAP */ diff --git a/sysdeps/x86_64/dl-trampoline.S b/sysdeps/x86_64/dl-trampoline.S new file mode 100644 index 0000000000..eb46f29cf2 --- /dev/null +++ b/sysdeps/x86_64/dl-trampoline.S @@ -0,0 +1,188 @@ +/* PLT trampolines. x86-64 version. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <sysdep.h> + + .text + .globl _dl_runtime_resolve + .type _dl_runtime_resolve, @function + .align 16 + cfi_startproc +_dl_runtime_resolve: + subq $56,%rsp + cfi_adjust_cfa_offset(72) # Incorporate PLT + movq %rax,(%rsp) # Preserve registers otherwise clobbered. + movq %rcx, 8(%rsp) + movq %rdx, 16(%rsp) + movq %rsi, 24(%rsp) + movq %rdi, 32(%rsp) + movq %r8, 40(%rsp) + movq %r9, 48(%rsp) + movq 64(%rsp), %rsi # Copy args pushed by PLT in register. + movq %rsi, %r11 # Multiply by 24 + addq %r11, %rsi + addq %r11, %rsi + shlq $3, %rsi + movq 56(%rsp), %rdi # %rdi: link_map, %rsi: reloc_offset + call _dl_fixup # Call resolver. + movq %rax, %r11 # Save return value + movq 48(%rsp), %r9 # Get register content back. + movq 40(%rsp), %r8 + movq 32(%rsp), %rdi + movq 24(%rsp), %rsi + movq 16(%rsp), %rdx + movq 8(%rsp), %rcx + movq (%rsp), %rax + addq $72, %rsp # Adjust stack(PLT did 2 pushes) + cfi_adjust_cfa_offset(-72) + jmp *%r11 # Jump to function address. + cfi_endproc + .size _dl_runtime_resolve, .-_dl_runtime_resolve + + + + .globl _dl_runtime_profile + .type _dl_runtime_profile, @function + .align 16 + cfi_startproc +_dl_runtime_profile: + subq $80, %rsp + cfi_adjust_cfa_offset(96) # Incorporate PLT + movq %rax, (%rsp) # Preserve registers otherwise clobbered. + movq %rdx, 8(%rsp) + movq %r8, 16(%rsp) + movq %r9, 24(%rsp) + movq %rcx, 32(%rsp) + movq %rsi, 40(%rsp) + movq %rdi, 48(%rsp) + movq %rbp, 56(%rsp) # Information for auditors. + leaq 96(%rsp), %rax + movq %rax, 64(%rsp) + leaq 8(%rsp), %rcx + movq 96(%rsp), %rdx # Load return address if needed + movq 88(%rsp), %rsi # Copy args pushed by PLT in register. + movq %rsi,%r11 # Multiply by 24 + addq %r11,%rsi + addq %r11,%rsi + shlq $3, %rsi + movq 80(%rsp), %rdi # %rdi: link_map, %rsi: reloc_offset + leaq 72(%rsp), %r8 + call _dl_profile_fixup # Call resolver. + movq %rax, %r11 # Save return value + movq 8(%rsp), %rdx # Get back register content. + movq 16(%rsp), %r8 + movq 24(%rsp), %r9 + movq (%rsp),%rax + movq 72(%rsp), %r10 + testq %r10, %r10 + jns 1f + movq 32(%rsp), %rcx + movq 40(%rsp), %rsi + movq 48(%rsp), %rdi + addq $96,%rsp # Adjust stack + cfi_adjust_cfa_offset (-96) + jmp *%r11 # Jump to function address. + + /* + +96 return address + +88 PLT2 + +80 PLT1 + +72 free + +64 %rsp + +56 %rbp + +48 %rdi + +40 %rsi + +32 %rcx + +24 %r9 + +16 %r8 + +8 %rdx + %esp %rax + */ + cfi_adjust_cfa_offset (96) +1: movq %rbx, 72(%rsp) + cfi_rel_offset (1, 72) + leaq 104(%rsp), %rsi + movq %rsp, %rbx + cfi_def_cfa_register (1) + subq %r10, %rsp + movq %rsp, %rdi + movq %r10, %rcx + shrq $3, %rcx + rep + movsq + andq $0xfffffffffffffff0, %rsp + movq 32(%rbx), %rcx + movq 40(%rbx), %rsi + movq 48(%rbx), %rdi + call *%r11 + movq %rbx, %rsp + cfi_def_cfa_register (7) + subq $72, %rsp + cfi_adjust_cfa_offset (72) + movq %rsp, %rcx + movq %rax, (%rcx) + movq %rdx, 8(%rcx) + /* Even though the stack is correctly aligned to allow using movaps + we use movups. Some callers might provide an incorrectly aligned + stack and we do not want to have it blow up here. */ + movups %xmm0, 16(%rcx) + movups %xmm1, 32(%rcx) + fstpt 48(%rcx) + fstpt 64(%rcx) + /* + +168 return address + +160 PLT2 + +152 PLT1 + +144 free + +136 %rsp + +128 %rbp + +120 %rdi + +112 %rsi + +104 %rcx + +96 %r9 + +88 %r8 + +80 %rdx + +64 %st1 result + +48 %st result + +32 %xmm1 result + +16 %xmm0 result + +8 %rdx result + %esp %rax result + */ + leaq 80(%rsp), %rdx + movq 144(%rsp), %rbx + cfi_restore (1) + movq 160(%rsp), %rsi # Copy args pushed by PLT in register. + movq %rsi,%r11 # Multiply by 24 + addq %r11,%rsi + addq %r11,%rsi + shlq $3, %rsi + movq 152(%rsp), %rdi # %rdi: link_map, %rsi: reloc_offset + call _dl_call_pltexit + movq (%rsp), %rax + movq 8(%rsp), %rdx + movups 16(%rsp), %xmm0 + movups 32(%rsp), %xmm1 + fldt 64(%rsp) + fldt 48(%rsp) + addq $168, %rsp + cfi_adjust_cfa_offset (-168) + retq + cfi_endproc + .size _dl_runtime_profile, .-_dl_runtime_profile |