diff options
Diffstat (limited to 'ldso/include')
| -rw-r--r-- | ldso/include/dl-defs.h | 49 | ||||
| -rw-r--r-- | ldso/include/dl-elf.h | 118 | ||||
| -rw-r--r-- | ldso/include/dl-hash.h | 102 | ||||
| -rw-r--r-- | ldso/include/dl-string.h | 95 | ||||
| -rw-r--r-- | ldso/include/dl-syscall.h | 211 | ||||
| -rw-r--r-- | ldso/include/dlfcn.h | 4 | ||||
| -rw-r--r-- | ldso/include/inline-hashtab.h | 265 | ||||
| -rw-r--r-- | ldso/include/ldso.h | 77 | ||||
| -rw-r--r-- | ldso/include/ldsodefs.h | 154 | ||||
| -rw-r--r-- | ldso/include/tlsdeschtab.h | 121 |
10 files changed, 966 insertions, 230 deletions
diff --git a/ldso/include/dl-defs.h b/ldso/include/dl-defs.h index 791d068bb..bedfa977e 100644 --- a/ldso/include/dl-defs.h +++ b/ldso/include/dl-defs.h @@ -5,8 +5,8 @@ * GNU Lesser General Public License version 2.1 or later. */ -#ifndef _LD_DEFS_H -#define _LD_DEFS_H +#ifndef _DL_DEFS_H +#define _DL_DEFS_H #define FLAG_ANY -1 #define FLAG_TYPE_MASK 0x00ff @@ -72,6 +72,13 @@ typedef struct { #endif +#ifdef _LIBC +#ifndef __ARCH_HAS_NO_SHARED__ +/* arch specific defines */ +#include <dl-sysdep.h> +#endif +#endif + /* Provide a means for a port to pass additional arguments to the _dl_start function. */ #ifndef DL_START @@ -99,7 +106,7 @@ typedef struct { * from DL_START, so additional arguments passed to it may be referenced. */ #ifndef DL_BOOT_COMPUTE_DYN #define DL_BOOT_COMPUTE_DYN(DPNT, GOT, LOAD_ADDR) \ - ((DPNT) = ((ElfW(Dyn) *) DL_RELOC_ADDR(load_addr, got))) + ((DPNT) = ((ElfW(Dyn) *) DL_RELOC_ADDR(LOAD_ADDR, GOT))) #endif /* Initialize the location of the global offset table. This is only called @@ -179,6 +186,14 @@ typedef struct { #define DL_LOOKUP_ADDRESS(ADDRESS) (ADDRESS) #endif +/* On some architectures dladdr can't use st_size of all symbols this way. */ +#define DL_ADDR_SYM_MATCH(SYM_ADDR, SYM, MATCHSYM, ADDR) \ + ((ADDR) >= (SYM_ADDR) \ + && ((((SYM)->st_shndx == SHN_UNDEF || (SYM)->st_size == 0) \ + && (ADDR) == (SYM_ADDR)) \ + || (ADDR) < (SYM_ADDR) + (SYM)->st_size) \ + && (!(MATCHSYM) || MATCHSYM < (SYM_ADDR))) + /* Use this macro to convert a pointer to a function's entry point to * a pointer to function. The pointer is assumed to have already been * relocated. LOADADDR is passed because it may contain additional @@ -212,7 +227,7 @@ typedef struct { _dl_find_hash for this reloc TYPE. TPNT is the module in which the matching SYM was found. */ #ifndef DL_FIND_HASH_VALUE -# define DL_FIND_HASH_VALUE(TPNT, TYPE, SYM) (DL_RELOC_ADDR ((SYM)->st_value, (TPNT)->loadaddr)) +# define DL_FIND_HASH_VALUE(TPNT, TYPE, SYM) (DL_RELOC_ADDR ((TPNT)->loadaddr, (SYM)->st_value)) #endif /* Unmap all previously-mapped segments accumulated in LOADADDR. @@ -225,7 +240,7 @@ typedef struct { /* Similar to DL_LOADADDR_UNMAP, but used for libraries that have been dlopen()ed successfully, when they're dlclose()d. */ #ifndef DL_LIB_UNMAP -# define DL_LIB_UNMAP(LIB, LEN) (DL_LOADADDR_UNMAP ((LIB)->loadaddr, (LEN))) +# define DL_LIB_UNMAP(LIB, LEN) (DL_LOADADDR_UNMAP ((LIB)->mapaddr, (LEN))) #endif /* Define this to verify that a library named LIBNAME, whose ELF @@ -251,4 +266,26 @@ typedef struct { # define DL_MAP_SEGMENT(EPNT, PPNT, INFILE, FLAGS) 0 #endif -#endif /* _LD_DEFS_H */ +/* Define this to declare the library offset. */ +#ifndef DL_DEF_LIB_OFFSET +# define DL_DEF_LIB_OFFSET static unsigned long _dl_library_offset +#endif + +/* Define this to get the library offset. */ +#ifndef DL_GET_LIB_OFFSET +# define DL_GET_LIB_OFFSET() _dl_library_offset +#endif + +/* Define this to set the library offset as difference beetwen the mapped + library address and the smallest virtual address of the first PT_LOAD + segment. */ +#ifndef DL_SET_LIB_OFFSET +# define DL_SET_LIB_OFFSET(offset) (_dl_library_offset = (offset)) +#endif + +/* Define this to get the real object's runtime address. */ +#ifndef DL_GET_RUN_ADDR +# define DL_GET_RUN_ADDR(loadaddr, mapaddr) (mapaddr) +#endif + +#endif /* _DL_DEFS_H */ diff --git a/ldso/include/dl-elf.h b/ldso/include/dl-elf.h index 076678cfc..80625fd5b 100644 --- a/ldso/include/dl-elf.h +++ b/ldso/include/dl-elf.h @@ -5,18 +5,22 @@ * GNU Lesser General Public License version 2.1 or later. */ -#ifndef LINUXELF_H -#define LINUXELF_H +#ifndef _DL_ELF_H +#define _DL_ELF_H +#include <features.h> +#include <bits/wordsize.h> #include <dl-string.h> /* before elf.h to get ELF_USES_RELOCA right */ #include <elf.h> #include <link.h> +#include <dl-defs.h> +#include <dlfcn.h> -/* Forward declarations for stuff defined in ld_hash.h */ +/* Forward declarations for stuff defined in dl-hash.h */ struct dyn_elf; struct elf_resolve; +struct r_scope_elem; -#include <dl-defs.h> #ifdef __LDSO_CACHE_SUPPORT__ extern int _dl_map_cache(void); extern int _dl_unmap_cache(void); @@ -25,21 +29,18 @@ static __inline__ void _dl_map_cache(void) { } static __inline__ void _dl_unmap_cache(void) { } #endif - -/* Function prototypes for non-static stuff in readelflib1.c */ +/* Function prototypes for non-static stuff in elfinterp.c */ extern void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt, unsigned long rel_addr, unsigned long rel_size); extern int _dl_parse_relocation_information(struct dyn_elf *rpnt, - unsigned long rel_addr, unsigned long rel_size); -extern struct elf_resolve * _dl_load_shared_library(int secure, + struct r_scope_elem *scope, unsigned long rel_addr, unsigned long rel_size); +extern struct elf_resolve * _dl_load_shared_library(unsigned int rflags, struct dyn_elf **rpnt, struct elf_resolve *tpnt, char *full_libname, int trace_loaded_objects); -extern struct elf_resolve * _dl_load_elf_shared_library(int secure, - struct dyn_elf **rpnt, char *libname); -extern struct elf_resolve *_dl_check_if_named_library_is_loaded(const char *full_libname, - int trace_loaded_objects); +extern struct elf_resolve * _dl_load_elf_shared_library(unsigned int rflags, + struct dyn_elf **rpnt, const char *libname); extern int _dl_linux_resolve(void); -extern int _dl_fixup(struct dyn_elf *rpnt, int flag); +extern int _dl_fixup(struct dyn_elf *rpnt, struct r_scope_elem *scope, int flag); extern void _dl_protect_relro (struct elf_resolve *l); /* @@ -84,33 +85,58 @@ extern void _dl_protect_relro (struct elf_resolve *l); #endif /* OS and/or GNU dynamic extensions */ + +#define OS_NUM_BASE 1 /* for DT_RELOCCOUNT */ + #ifdef __LDSO_GNU_HASH_SUPPORT__ -# define OS_NUM 2 /* for DT_RELOCCOUNT and DT_GNU_HASH entries */ +# define OS_NUM_GNU_HASH 1 /* for DT_GNU_HASH entry */ +#else +# define OS_NUM_GNU_HASH 0 +#endif + +#ifdef __LDSO_PRELINK_SUPPORT__ +# define OS_NUM_PRELINK 6 /* for DT_GNU_PRELINKED entry */ #else -# define OS_NUM 1 /* for DT_RELOCCOUNT entry */ +# define OS_NUM_PRELINK 0 #endif +#define OS_NUM (OS_NUM_BASE + OS_NUM_GNU_HASH + OS_NUM_PRELINK) + #ifndef ARCH_DYNAMIC_INFO /* define in arch specific code, if needed */ # define ARCH_NUM 0 #endif -#define DYNAMIC_SIZE (DT_NUM+OS_NUM+ARCH_NUM) +#define DYNAMIC_SIZE (DT_NUM + OS_NUM + ARCH_NUM) /* Keep ARCH specific entries into dynamic section at the end of the array */ #define DT_RELCONT_IDX (DYNAMIC_SIZE - OS_NUM - ARCH_NUM) #ifdef __LDSO_GNU_HASH_SUPPORT__ /* GNU hash comes just after the relocation count */ # define DT_GNU_HASH_IDX (DT_RELCONT_IDX + 1) +#else +# define DT_GNU_HASH_IDX DT_RELCONT_IDX #endif -extern void _dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[], - void *debug_addr, DL_LOADADDR_TYPE load_off); +#ifdef __LDSO_PRELINK_SUPPORT__ +/* GNU prelink comes just after the GNU hash if present */ +#define DT_GNU_PRELINKED_IDX (DT_GNU_HASH_IDX + 1) +#define DT_GNU_CONFLICT_IDX (DT_GNU_HASH_IDX + 2) +#define DT_GNU_CONFLICTSZ_IDX (DT_GNU_HASH_IDX + 3) +#define DT_GNU_LIBLIST_IDX (DT_GNU_HASH_IDX + 4) +#define DT_GNU_LIBLISTSZ_IDX (DT_GNU_HASH_IDX + 5) +#define DT_CHECKSUM_IDX (DT_GNU_HASH_IDX + 6) +#endif + +extern unsigned int _dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[], + void *debug_addr, DL_LOADADDR_TYPE load_off); static __always_inline -void __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[], - void *debug_addr, DL_LOADADDR_TYPE load_off) +unsigned int __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[], + void *debug_addr, DL_LOADADDR_TYPE load_off) { + unsigned int rtld_flags = 0; + for (; dpnt->d_tag; dpnt++) { if (dpnt->d_tag < DT_NUM) { dynamic_info[dpnt->d_tag] = dpnt->d_un.d_val; @@ -138,13 +164,30 @@ void __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[], } else if (dpnt->d_tag < DT_LOPROC) { if (dpnt->d_tag == DT_RELOCCOUNT) dynamic_info[DT_RELCONT_IDX] = dpnt->d_un.d_val; - if (dpnt->d_tag == DT_FLAGS_1 && - (dpnt->d_un.d_val & DF_1_NOW)) - dynamic_info[DT_BIND_NOW] = 1; + if (dpnt->d_tag == DT_FLAGS_1) { + if (dpnt->d_un.d_val & DF_1_NOW) + dynamic_info[DT_BIND_NOW] = 1; + if (dpnt->d_un.d_val & DF_1_NODELETE) + rtld_flags |= RTLD_NODELETE; + } #ifdef __LDSO_GNU_HASH_SUPPORT__ if (dpnt->d_tag == DT_GNU_HASH) dynamic_info[DT_GNU_HASH_IDX] = dpnt->d_un.d_ptr; #endif +#ifdef __LDSO_PRELINK_SUPPORT__ + if (dpnt->d_tag == DT_GNU_PRELINKED) + dynamic_info[DT_GNU_PRELINKED_IDX] = dpnt->d_un.d_val; + if (dpnt->d_tag == DT_GNU_CONFLICT) + dynamic_info[DT_GNU_CONFLICT_IDX] = dpnt->d_un.d_ptr; + if (dpnt->d_tag == DT_GNU_CONFLICTSZ) + dynamic_info[DT_GNU_CONFLICTSZ_IDX] = dpnt->d_un.d_val; + if (dpnt->d_tag == DT_GNU_LIBLIST) + dynamic_info[DT_GNU_LIBLIST_IDX] = dpnt->d_un.d_ptr; + if (dpnt->d_tag == DT_GNU_LIBLISTSZ) + dynamic_info[DT_GNU_LIBLISTSZ_IDX] = dpnt->d_un.d_val; + if (dpnt->d_tag == DT_CHECKSUM) + dynamic_info[DT_CHECKSUM_IDX] = dpnt->d_un.d_val; +#endif } #ifdef ARCH_DYNAMIC_INFO else { @@ -157,16 +200,29 @@ void __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[], if (dynamic_info[tag]) \ dynamic_info[tag] = (unsigned long) DL_RELOC_ADDR(load_off, dynamic_info[tag]); \ } while (0) - ADJUST_DYN_INFO(DT_HASH, load_off); - ADJUST_DYN_INFO(DT_PLTGOT, load_off); - ADJUST_DYN_INFO(DT_STRTAB, load_off); - ADJUST_DYN_INFO(DT_SYMTAB, load_off); - ADJUST_DYN_INFO(DT_RELOC_TABLE_ADDR, load_off); - ADJUST_DYN_INFO(DT_JMPREL, load_off); + /* Don't adjust .dynamic unnecessarily. For FDPIC targets, + we'd have to walk all the loadsegs to find out if it was + actually unnecessary, so skip this optimization. */ +#if !defined __FRV_FDPIC__ && !defined __BFIN_FDPIC__ && !defined __DSBT__ + if (load_off != 0) +#endif + { + ADJUST_DYN_INFO(DT_HASH, load_off); + ADJUST_DYN_INFO(DT_PLTGOT, load_off); + ADJUST_DYN_INFO(DT_STRTAB, load_off); + ADJUST_DYN_INFO(DT_SYMTAB, load_off); + ADJUST_DYN_INFO(DT_RELOC_TABLE_ADDR, load_off); + ADJUST_DYN_INFO(DT_JMPREL, load_off); #ifdef __LDSO_GNU_HASH_SUPPORT__ - ADJUST_DYN_INFO(DT_GNU_HASH_IDX, load_off); + ADJUST_DYN_INFO(DT_GNU_HASH_IDX, load_off); +#endif + } +#ifdef __DSBT__ + /* Get the mapped address of the DSBT base. */ + ADJUST_DYN_INFO(DT_DSBT_BASE_IDX, load_off); #endif #undef ADJUST_DYN_INFO + return rtld_flags; } /* Reloc type classes as returned by elf_machine_type_class(). @@ -196,4 +252,4 @@ void __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[], (((X) & PF_X) ? PROT_EXEC : 0)) -#endif /* LINUXELF_H */ +#endif /* _DL_ELF_H */ diff --git a/ldso/include/dl-hash.h b/ldso/include/dl-hash.h index 9b87783fa..d6282bb0c 100644 --- a/ldso/include/dl-hash.h +++ b/ldso/include/dl-hash.h @@ -5,8 +5,8 @@ * GNU Lesser General Public License version 2.1 or later. */ -#ifndef _LD_HASH_H_ -#define _LD_HASH_H_ +#ifndef _DL_HASH_H +#define _DL_HASH_H #ifndef RTLD_NEXT #define RTLD_NEXT ((void*)-1) @@ -25,6 +25,19 @@ struct dyn_elf { struct dyn_elf * prev; }; +struct symbol_ref { + const ElfW(Sym) *sym; + struct elf_resolve *tpnt; +}; + +/* Structure to describe a single list of scope elements. The lookup + functions get passed an array of pointers to such structures. */ +struct r_scope_elem { + struct elf_resolve **r_list; /* Array of maps for the scope. */ + unsigned int r_nlist; /* Number of entries in the scope. */ + struct r_scope_elem *next; +}; + struct elf_resolve { /* These entries must be in this order to be compatible with the interface used by gdb to obtain the list of symbols. */ @@ -34,9 +47,41 @@ struct elf_resolve { struct elf_resolve * next; struct elf_resolve * prev; /* Nothing after this address is used by gdb. */ - ElfW(Addr) mapaddr; /* Address at which ELF segments (either main app and DSO) are mapped into */ + +#if defined(USE_TLS) && USE_TLS + /* Thread-local storage related info. */ + + /* Start of the initialization image. */ + void *l_tls_initimage; + /* Size of the initialization image. */ + size_t l_tls_initimage_size; + /* Size of the TLS block. */ + size_t l_tls_blocksize; + /* Alignment requirement of the TLS block. */ + size_t l_tls_align; + /* Offset of first byte module alignment. */ + size_t l_tls_firstbyte_offset; +# ifndef NO_TLS_OFFSET +# define NO_TLS_OFFSET 0 +# endif + /* For objects present at startup time: offset in the static TLS block. */ + ptrdiff_t l_tls_offset; + /* Index of the module in the dtv array. */ + size_t l_tls_modid; + /* Nonzero if _dl_init_static_tls should be called for this module */ + unsigned int l_need_tls_init:1; + /* Address of TLS descriptor hash table. */ + void *l_tlsdesc_table; +#endif + + ElfW(Addr) mapaddr; +#ifdef __LDSO_STANDALONE_SUPPORT__ + /* Store the entry point from the ELF header (e_entry) */ + ElfW(Addr) l_entry; +#endif enum {elf_lib, elf_executable,program_interpreter, loaded_file} libtype; - struct dyn_elf * symbol_scope; + /* This is the local scope of the shared object */ + struct r_scope_elem symbol_scope; unsigned short usage_count; unsigned short int init_flag; unsigned long rtld_flags; /* RTLD_GLOBAL, RTLD_NOW etc. */ @@ -90,12 +135,18 @@ struct elf_resolve { unsigned long data_words; #endif -#ifdef __FDPIC__ +#if defined(__FRV_FDPIC__) || defined(__BFIN_FDPIC__) /* Every loaded module holds a hashtable of function descriptors of functions defined in it, such that it's easy to release the memory when the module is dlclose()d. */ struct funcdesc_ht *funcdesc_ht; #endif +#ifdef __DSBT__ + /* Information for DSBT */ + void **dsbt_table; + unsigned long dsbt_size; + unsigned long dsbt_index; +#endif }; #define RELOCS_DONE 0x000001 @@ -103,43 +154,21 @@ struct elf_resolve { #define INIT_FUNCS_CALLED 0x000004 #define FINI_FUNCS_CALLED 0x000008 #define DL_OPENED 0x000010 +#define DL_RESERVED 0x000020 extern struct dyn_elf * _dl_symbol_tables; extern struct elf_resolve * _dl_loaded_modules; -extern struct dyn_elf * _dl_handles; +extern struct dyn_elf * _dl_handles; extern struct elf_resolve * _dl_add_elf_hash_table(const char * libname, DL_LOADADDR_TYPE loadaddr, unsigned long * dynamic_info, unsigned long dynamic_addr, unsigned long dynamic_size); -extern char * _dl_lookup_hash(const char * name, struct dyn_elf * rpnt, - struct elf_resolve *mytpnt, int type_class -#ifdef __FDPIC__ - , struct elf_resolve **tpntp -#endif - ); - -static __always_inline char *_dl_find_hash(const char *name, struct dyn_elf *rpnt, - struct elf_resolve *mytpnt, int type_class) -{ -#ifdef __FDPIC__ - return _dl_lookup_hash(name, rpnt, mytpnt, type_class, NULL); -#else - return _dl_lookup_hash(name, rpnt, mytpnt, type_class); -#endif -} - -extern int _dl_linux_dynamic_link(void); +extern char *_dl_find_hash(const char *name, struct r_scope_elem *scope, + struct elf_resolve *mytpnt, int type_class, + struct symbol_ref *symbol); extern char * _dl_library_path; -extern char * _dl_not_lazy; - -static __inline__ int _dl_symbol(char * name) -{ - if (name[0] != '_' || name[1] != 'd' || name[2] != 'l' || name[3] != '_') - return 0; - return 1; -} #define LD_ERROR_NOFILE 1 #define LD_ERROR_NOZERO 2 @@ -148,8 +177,9 @@ static __inline__ int _dl_symbol(char * name) #define LD_ERROR_NOTDYN 5 #define LD_ERROR_MMAP_FAILED 6 #define LD_ERROR_NODYNAMIC 7 -#define LD_WRONG_RELOCS 8 -#define LD_BAD_HANDLE 9 -#define LD_NO_SYMBOL 10 +#define LD_ERROR_TLS_FAILED 8 +#define LD_WRONG_RELOCS 9 +#define LD_BAD_HANDLE 10 +#define LD_NO_SYMBOL 11 -#endif /* _LD_HASH_H_ */ +#endif /* _DL_HASH_H */ diff --git a/ldso/include/dl-string.h b/ldso/include/dl-string.h index 746bd91c6..14ae617c3 100644 --- a/ldso/include/dl-string.h +++ b/ldso/include/dl-string.h @@ -5,12 +5,16 @@ * GNU Lesser General Public License version 2.1 or later. */ -#ifndef _LINUX_STRING_H_ -#define _LINUX_STRING_H_ +#ifndef _DL_STRING_H +#define _DL_STRING_H -#include <dl-sysdep.h> /* for do_rem */ #include <features.h> +#define __need_NULL +#include <stddef.h> + +#include <dl-defs.h> /* for do_rem by dl-sysdep.h */ + /* provide some sane defaults */ #ifndef do_rem # define do_rem(result, n, base) ((result) = (n) % (base)) @@ -19,26 +23,8 @@ # define do_div_10(result, remain) ((result) /= 10) #endif -static size_t _dl_strlen(const char * str); -static char *_dl_strcat(char *dst, const char *src); -static char * _dl_strcpy(char * dst,const char *src); -static int _dl_strcmp(const char * s1,const char * s2); -static int _dl_strncmp(const char * s1,const char * s2,size_t len); -static char * _dl_strchr(const char * str,int c); -static char *_dl_strrchr(const char *str, int c); -static char *_dl_strstr(const char *s1, const char *s2); -static void * _dl_memcpy(void * dst, const void * src, size_t len); -static int _dl_memcmp(const void * s1,const void * s2,size_t len); -static void *_dl_memset(void * str,int c,size_t len); -static char *_dl_get_last_path_component(char *path); -static char *_dl_simple_ltoa(char * local, unsigned long i); -static char *_dl_simple_ltoahex(char * local, unsigned long i); - -#ifndef NULL -#define NULL ((void *) 0) -#endif - -static __always_inline size_t _dl_strlen(const char * str) +#ifdef IS_IN_rtld +static __always_inline size_t _dl_strlen(const char *str) { register const char *ptr = (char *) str-1; while (*++ptr) @@ -59,7 +45,7 @@ static __always_inline char * _dl_strcat(char *dst, const char *src) return dst; } -static __always_inline char * _dl_strcpy(char * dst,const char *src) +static __always_inline char * _dl_strcpy(char *dst, const char *src) { register char *ptr = dst; @@ -70,7 +56,7 @@ static __always_inline char * _dl_strcpy(char * dst,const char *src) return ptr; } -static __always_inline int _dl_strcmp(const char * s1,const char * s2) +static __always_inline int _dl_strcmp(const char *s1, const char *s2) { register unsigned char c1, c2; s1--;s2--; @@ -84,23 +70,7 @@ static __always_inline int _dl_strcmp(const char * s1,const char * s2) return c1 - c2; } -static __always_inline int _dl_strncmp(const char * s1,const char * s2,size_t len) -{ - register unsigned char c1 = '\0'; - register unsigned char c2 = '\0'; - - s1--;s2--; - while (len > 0) { - c1 = (unsigned char) *++s1; - c2 = (unsigned char) *++s2; - if (c1 == '\0' || c1 != c2) - return c1 - c2; - len--; - } - return c1 - c2; -} - -static __always_inline char * _dl_strchr(const char * str,int c) +static __always_inline char * _dl_strchr(const char *str, int c) { register char ch; str--; @@ -147,7 +117,7 @@ static __always_inline char * _dl_strstr(const char *s1, const char *s2) } while (1); } -static __always_inline void * _dl_memcpy(void * dst, const void * src, size_t len) +static __always_inline void * _dl_memcpy(void *dst, const void *src, size_t len) { register char *a = dst-1; register const char *b = src-1; @@ -159,7 +129,7 @@ static __always_inline void * _dl_memcpy(void * dst, const void * src, size_t le return dst; } -static __always_inline int _dl_memcmp(const void * s1,const void * s2,size_t len) +static __always_inline int _dl_memcmp(const void *s1, const void *s2, size_t len) { unsigned char *c1 = (unsigned char *)s1-1; unsigned char *c2 = (unsigned char *)s2-1; @@ -172,7 +142,7 @@ static __always_inline int _dl_memcmp(const void * s1,const void * s2,size_t len return 0; } -#if defined(powerpc) +#if defined(__powerpc__) /* Will generate smaller and faster code due to loop unrolling.*/ static __always_inline void * _dl_memset(void *to, int c, size_t n) { @@ -200,7 +170,7 @@ lessthan4: return to; } #else -static __always_inline void * _dl_memset(void * str,int c,size_t len) +static __always_inline void * _dl_memset(void *str, int c, size_t len) { register char *a = str; @@ -228,11 +198,25 @@ static __always_inline char * _dl_get_last_path_component(char *path) ;/* empty */ return ptr == path ? ptr : ptr+1; } - +#else /* IS_IN_rtld */ +# include <string.h> +# define _dl_strlen strlen +# define _dl_strcat strcat +# define _dl_strcpy strcpy +# define _dl_strcmp strcmp +# define _dl_strchr strchr +# define _dl_strrchr strrchr +# define _dl_strstr strstr +# define _dl_memcpy memcpy +# define _dl_memcmp memcmp +# define _dl_memset memset +#endif /* IS_IN_rtld */ + +#if defined IS_IN_rtld || defined __SUPPORT_LD_DEBUG__ /* Early on, we can't call printf, so use this to print out * numbers using the SEND_STDERR() macro. Avoid using mod * or using long division */ -static __always_inline char * _dl_simple_ltoa(char * local, unsigned long i) +static __always_inline char * _dl_simple_ltoa(char *local, unsigned long i) { /* 20 digits plus a null terminator should be good for * 64-bit or smaller ints (2^64 - 1)*/ @@ -246,8 +230,10 @@ static __always_inline char * _dl_simple_ltoa(char * local, unsigned long i) } while (i > 0); return p; } +#endif -static __always_inline char * _dl_simple_ltoahex(char * local, unsigned long i) +#ifdef IS_IN_rtld +static __always_inline char * _dl_simple_ltoahex(char *local, unsigned long i) { /* 16 digits plus a leading "0x" plus a null terminator, * should be good for 64-bit or smaller ints */ @@ -266,9 +252,6 @@ static __always_inline char * _dl_simple_ltoahex(char * local, unsigned long i) return p; } - - - /* The following macros may be used in dl-startup.c to debug * ldso before ldso has fixed itself up to make function calls */ @@ -285,8 +268,8 @@ static __always_inline char * _dl_simple_ltoahex(char * local, unsigned long i) /* On some arches constant strings are referenced through the GOT. * This requires that load_addr must already be defined... */ #if defined(mc68000) || defined(__arm__) || defined(__thumb__) || \ - defined(__mips__) || defined(__sh__) || defined(__powerpc__) || \ - defined(__avr32__) || defined(__xtensa__) + defined(__sh__) || defined(__powerpc__) || \ + defined(__avr32__) || defined(__xtensa__) || defined(__sparc__) || defined(__microblaze__) # define CONSTANT_STRING_GOT_FIXUP(X) \ if ((X) < (const char *) load_addr) (X) += load_addr # define NO_EARLY_SEND_STDERR @@ -362,4 +345,6 @@ static __always_inline char * _dl_simple_ltoahex(char * local, unsigned long i) # define SEND_ADDRESS_STDERR_DEBUG(X, add_a_newline) #endif -#endif +#endif /* IS_IN_rtld */ + +#endif /* _DL_STRING_H */ diff --git a/ldso/include/dl-syscall.h b/ldso/include/dl-syscall.h index 1cbbbad0f..5528ba6a0 100644 --- a/ldso/include/dl-syscall.h +++ b/ldso/include/dl-syscall.h @@ -5,51 +5,40 @@ * GNU Lesser General Public License version 2.1 or later. */ -#ifndef _LD_SYSCALL_H_ -#define _LD_SYSCALL_H_ +#ifndef _DL_SYSCALL_H +#define _DL_SYSCALL_H + +/* We can't use the real errno in ldso, since it has not yet + * been dynamicly linked in yet. */ +#include "sys/syscall.h" +extern int _dl_errno; +#undef __set_errno +#define __set_errno(X) {(_dl_errno) = (X);} /* Pull in the arch specific syscall implementation */ #include <dl-syscalls.h> /* For MAP_ANONYMOUS -- differs between platforms */ #define _SYS_MMAN_H 1 #include <bits/mman.h> + +#ifdef __ARCH_HAS_DEPRECATED_SYSCALLS__ /* Pull in whatever this particular arch's kernel thinks the kernel version of * struct stat should look like. It turns out that each arch has a different * opinion on the subject, and different kernel revs use different names... */ -#if defined(__sparc_v9__) && (__WORDSIZE == 64) -#define kernel_stat64 stat -#else #define kernel_stat stat -#endif #include <bits/kernel_stat.h> #include <bits/kernel_types.h> -/* _dl_open() parameters */ -#define O_RDONLY 00 -#define O_WRONLY 01 -#define O_RDWR 02 -#define O_CREAT 0100 - -/* Encoding of the file mode. */ -#define S_IFMT 0170000 /* These bits determine file type. */ - -/* File types. */ -#define S_IFDIR 0040000 /* Directory. */ -#define S_IFCHR 0020000 /* Character device. */ -#define S_IFBLK 0060000 /* Block device. */ -#define S_IFREG 0100000 /* Regular file. */ -#define S_IFIFO 0010000 /* FIFO. */ -#define S_IFLNK 0120000 /* Symbolic link. */ -#define S_IFSOCK 0140000 /* Socket. */ - /* Protection bits. */ #define S_ISUID 04000 /* Set user ID on execution. */ #define S_ISGID 02000 /* Set group ID on execution. */ -#define S_ISVTX 01000 /* Save swapped text after use (sticky). */ -#define S_IREAD 0400 /* Read by owner. */ -#define S_IWRITE 0200 /* Write by owner. */ -#define S_IEXEC 0100 /* Execute by owner. */ +#else +/* 1. common-generic ABI doesn't need kernel_stat translation + * 3. S_IS?ID already provided by stat.h + */ +#include <sys/stat.h> +#endif /* Here are the definitions for some syscalls that are used @@ -59,107 +48,140 @@ dynamic linking at all, so we cannot return any error codes. We just punt if there is an error. */ #define __NR__dl_exit __NR_exit -static __always_inline _syscall1(void, _dl_exit, int, status); +static __always_inline attribute_noreturn __cold void _dl_exit(int status) +{ + INLINE_SYSCALL(_dl_exit, 1, status); +#if __GNUC_PREREQ(4, 5) + __builtin_unreachable(); /* shut up warning: 'noreturn' function does return*/ +#else + while (1); +#endif +} #define __NR__dl_close __NR_close -static __always_inline _syscall1(int, _dl_close, int, fd); +static __always_inline _syscall1(int, _dl_close, int, fd) -#define __NR__dl_open __NR_open +#if defined __NR_openat && !defined __NR_open +static __always_inline int _dl_open(const char *fn, + int flags, __kernel_mode_t mode) +{ + return INLINE_SYSCALL(openat, 4, AT_FDCWD, fn, flags, mode); +} + +#elif defined __NR_open +# define __NR__dl_open __NR_open static __always_inline _syscall3(int, _dl_open, const char *, fn, int, flags, - __kernel_mode_t, mode); + __kernel_mode_t, mode) +#endif #define __NR__dl_write __NR_write static __always_inline _syscall3(unsigned long, _dl_write, int, fd, - const void *, buf, unsigned long, count); + const void *, buf, unsigned long, count) #define __NR__dl_read __NR_read static __always_inline _syscall3(unsigned long, _dl_read, int, fd, - const void *, buf, unsigned long, count); + const void *, buf, unsigned long, count) #define __NR__dl_mprotect __NR_mprotect static __always_inline _syscall3(int, _dl_mprotect, const void *, addr, - unsigned long, len, int, prot); + unsigned long, len, int, prot) -#define __NR__dl_stat __NR_stat +#if defined __NR_fstatat64 && !defined __NR_stat +# define __NR__dl_fstatat64 __NR_fstatat64 +static __always_inline _syscall4(int, _dl_fstatat64, int, fd, const char *, + fn, struct stat *, stat, int, flags) + +static __always_inline int _dl_stat(const char *file_name, + struct stat *buf) +{ + return _dl_fstatat64(AT_FDCWD, file_name, buf, 0); +} +#elif defined __NR_stat +# define __NR__dl_stat __NR_stat static __always_inline _syscall2(int, _dl_stat, const char *, file_name, - struct stat *, buf); + struct stat *, buf) +#endif -#define __NR__dl_fstat __NR_fstat -static __always_inline _syscall2(int, _dl_fstat, int, fd, struct stat *, buf); +#if defined __NR_fstat64 && !defined __NR_fstat +# define __NR__dl_fstat __NR_fstat64 +#elif defined __NR_fstat +# define __NR__dl_fstat __NR_fstat +#endif +static __always_inline _syscall2(int, _dl_fstat, int, fd, struct stat *, buf) #define __NR__dl_munmap __NR_munmap -static __always_inline _syscall2(int, _dl_munmap, void *, start, unsigned long, length); +static __always_inline _syscall2(int, _dl_munmap, void *, start, unsigned long, length) #ifdef __NR_getxuid # define __NR_getuid __NR_getxuid #endif #define __NR__dl_getuid __NR_getuid -static __always_inline _syscall0(uid_t, _dl_getuid); +static __always_inline _syscall0(uid_t, _dl_getuid) #ifndef __NR_geteuid # define __NR_geteuid __NR_getuid #endif #define __NR__dl_geteuid __NR_geteuid -static __always_inline _syscall0(uid_t, _dl_geteuid); +static __always_inline _syscall0(uid_t, _dl_geteuid) #ifdef __NR_getxgid # define __NR_getgid __NR_getxgid #endif #define __NR__dl_getgid __NR_getgid -static __always_inline _syscall0(gid_t, _dl_getgid); +static __always_inline _syscall0(gid_t, _dl_getgid) #ifndef __NR_getegid # define __NR_getegid __NR_getgid #endif #define __NR__dl_getegid __NR_getegid -static __always_inline _syscall0(gid_t, _dl_getegid); +static __always_inline _syscall0(gid_t, _dl_getegid) #ifdef __NR_getxpid # define __NR_getpid __NR_getxpid #endif #define __NR__dl_getpid __NR_getpid -static __always_inline _syscall0(gid_t, _dl_getpid); +static __always_inline _syscall0(gid_t, _dl_getpid) + +#if defined __NR_readlinkat +# define __NR__dl_readlink __NR_readlinkat +static __always_inline _syscall4(int, _dl_readlink, int, id, const char *, path, + char *, buf, size_t, bufsiz) +#endif -#define __NR__dl_readlink __NR_readlink -static __always_inline _syscall3(int, _dl_readlink, const char *, path, char *, buf, - size_t, bufsiz); +#ifdef __NR_pread64 +#define __NR___syscall_pread __NR_pread64 +static __always_inline _syscall5(ssize_t, __syscall_pread, int, fd, void *, buf, + size_t, count, off_t, offset_hi, off_t, offset_lo) + +static __always_inline ssize_t +_dl_pread(int fd, void *buf, size_t count, off_t offset) +{ + return __syscall_pread(fd, buf, count, offset, offset >> 31); +} +#elif defined __NR_pread +#define __NR___syscall_pread __NR_pread +static __always_inline _syscall5(ssize_t, __syscall_pread, int, fd, void *, buf, + size_t, count, off_t, offset_hi, off_t, offset_lo) + +static __always_inline ssize_t +_dl_pread(int fd, void *buf, size_t count, off_t offset) +{ + return __syscall_pread(fd, buf, count, __LONG_LONG_PAIR(offset >> 31, offset)); +} +#endif #ifdef __UCLIBC_HAS_SSP__ # include <sys/time.h> # define __NR__dl_gettimeofday __NR_gettimeofday static __always_inline _syscall2(int, _dl_gettimeofday, struct timeval *, tv, # ifdef __USE_BSD - struct timezone *, tz); + struct timezone * # else - void *, tz); + void * # endif + , tz) #endif - -/* handle all the fun mmap intricacies */ -#if (defined(__UCLIBC_MMAP_HAS_6_ARGS__) && defined(__NR_mmap)) || !defined(__NR_mmap2) -# define _dl_MAX_ERRNO 4096 -# define _dl_mmap_check_error(__res) \ - (((long)__res) < 0 && ((long)__res) >= -_dl_MAX_ERRNO) -#else -# define MAP_FAILED ((void *) -1) -# define _dl_mmap_check_error(X) (((void *)X) == MAP_FAILED) -#endif - -/* first try mmap(), syscall6() style */ -#if defined(__UCLIBC_MMAP_HAS_6_ARGS__) && defined(__NR_mmap) - -# define __NR__dl_mmap __NR_mmap -static __always_inline _syscall6(void *, _dl_mmap, void *, start, size_t, length, - int, prot, int, flags, int, fd, off_t, offset); - -/* then try mmap2() */ -#elif defined(__NR_mmap2) - -# define __NR___syscall_mmap2 __NR_mmap2 -static __always_inline _syscall6(__ptr_t, __syscall_mmap2, __ptr_t, addr, size_t, len, - int, prot, int, flags, int, fd, off_t, offset); - /* Some architectures always use 12 as page shift for mmap2() eventhough the * real PAGE_SHIFT != 12. Other architectures use the same value as * PAGE_SHIFT... @@ -168,36 +190,41 @@ static __always_inline _syscall6(__ptr_t, __syscall_mmap2, __ptr_t, addr, size_t # define MMAP2_PAGE_SHIFT 12 #endif -static __always_inline void * _dl_mmap(void * addr, unsigned long size, int prot, - int flags, int fd, unsigned long offset) +#define MAP_FAILED ((void *) -1) +#define _dl_mmap_check_error(X) (((void *)X) == MAP_FAILED) + +static __always_inline +void *_dl_mmap(void *addr, unsigned long size, int prot, + int flags, int fd, unsigned long offset) { +#if defined(__UCLIBC_MMAP_HAS_6_ARGS__) && defined(__NR_mmap) + /* first try mmap(), syscall6() style */ + return (void *)INLINE_SYSCALL(mmap, 6, addr, size, prot, flags, fd, offset); + +#elif defined(__NR_mmap2) && !defined (__mcoldfire__) + /* then try mmap2() */ + unsigned long shifted; + if (offset & ((1 << MMAP2_PAGE_SHIFT) - 1)) return MAP_FAILED; - return __syscall_mmap2(addr, size, prot, flags, - fd, (off_t) (offset >> MMAP2_PAGE_SHIFT)); -} -/* finally, fall back to mmap(), syscall1() style */ -#elif defined(__NR_mmap) + /* gcc needs help with putting things onto the stack */ + shifted = offset >> MMAP2_PAGE_SHIFT; + return (void *)INLINE_SYSCALL(mmap2, 6, addr, size, prot, flags, fd, shifted); -# define __NR__dl_mmap_real __NR_mmap -static __always_inline _syscall1(void *, _dl_mmap_real, unsigned long *, buffer); -static __always_inline void * _dl_mmap(void * addr, unsigned long size, int prot, - int flags, int fd, unsigned long offset) -{ +#elif defined(__NR_mmap) + /* finally, fall back to mmap(), syscall1() style */ unsigned long buffer[6]; - buffer[0] = (unsigned long) addr; buffer[1] = (unsigned long) size; buffer[2] = (unsigned long) prot; buffer[3] = (unsigned long) flags; buffer[4] = (unsigned long) fd; buffer[5] = (unsigned long) offset; - return (void *) _dl_mmap_real(buffer); -} - + return (void *)INLINE_SYSCALL(mmap, 1, buffer); #else # error "Your architecture doesn't seem to provide mmap() !?" #endif +} -#endif /* _LD_SYSCALL_H_ */ +#endif /* _DL_SYSCALL_H */ diff --git a/ldso/include/dlfcn.h b/ldso/include/dlfcn.h index 03afd34fb..5cdd6be53 100644 --- a/ldso/include/dlfcn.h +++ b/ldso/include/dlfcn.h @@ -19,9 +19,9 @@ `dladdr'. */ typedef struct { - __const char *dli_fname; /* File name of defining object. */ + const char *dli_fname; /* File name of defining object. */ void *dli_fbase; /* Load address of that object. */ - __const char *dli_sname; /* Name of nearest symbol. */ + const char *dli_sname; /* Name of nearest symbol. */ void *dli_saddr; /* Exact value of nearest symbol. */ } Dl_info; diff --git a/ldso/include/inline-hashtab.h b/ldso/include/inline-hashtab.h new file mode 100644 index 000000000..4a4812027 --- /dev/null +++ b/ldso/include/inline-hashtab.h @@ -0,0 +1,265 @@ +/* + * The hashcode handling code below is heavily inspired in libiberty's + * hashtab code, but with most adaptation points and support for + * deleting elements removed. + * + * Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + * Contributed by Vladimir Makarov (vmakarov@cygnus.com). + */ + +#ifndef INLINE_HASHTAB_H +# define INLINE_HASHTAB_H 1 + +static __always_inline unsigned long +higher_prime_number(unsigned long n) +{ + /* These are primes that are near, but slightly smaller than, a power of two. */ + static const unsigned long primes[] = { + 7, + 13, + 31, + 61, + 127, + 251, + 509, + 1021, + 2039, + 4093, + 8191, + 16381, + 32749, + 65521, + 131071, + 262139, + 524287, + 1048573, + 2097143, + 4194301, + 8388593, + 16777213, + 33554393, + 67108859, + 134217689, + 268435399, + 536870909, + 1073741789, + /* 4294967291 */ + ((unsigned long) 2147483647) + ((unsigned long) 2147483644), + }; + const unsigned long *low = &primes[0]; + const unsigned long *high = &primes[ARRAY_SIZE(primes)]; + + while (low != high) { + const unsigned long *mid = low + (high - low) / 2; + if (n > *mid) + low = mid + 1; + else + high = mid; + } + +#if 0 + /* If we've run out of primes, abort. */ + if (n > *low) { + fprintf(stderr, "Cannot find prime bigger than %lu\n", n); + abort(); + } +#endif + + return *low; +} + +struct funcdesc_ht +{ + /* Table itself */ + void **entries; + + /* Current size (in entries) of the hash table */ + size_t size; + + /* Current number of elements */ + size_t n_elements; +}; + +static __always_inline struct funcdesc_ht * +htab_create(void) +{ + struct funcdesc_ht *ht = _dl_malloc(sizeof(*ht)); + size_t ent_size; + + if (!ht) + return NULL; + ht->size = 3; + ent_size = sizeof(void *) * ht->size; + ht->entries = _dl_malloc(ent_size); + if (!ht->entries) + return NULL; + + ht->n_elements = 0; + _dl_memset(ht->entries, 0, ent_size); + + return ht; +} + +/* + * This is only called from _dl_loadaddr_unmap, so it's safe to call + * _dl_free(). See the discussion below. + */ +static __always_inline void +htab_delete(struct funcdesc_ht *htab) +{ + size_t i; + + for (i = htab->size - 1; i >= 0; i--) + if (htab->entries[i]) + _dl_free(htab->entries[i]); + + _dl_free(htab->entries); + _dl_free(htab); +} + +/* + * Similar to htab_find_slot, but without several unwanted side effects: + * - Does not call htab->eq_f when it finds an existing entry. + * - Does not change the count of elements/searches/collisions in the + * hash table. + * This function also assumes there are no deleted entries in the table. + * HASH is the hash value for the element to be inserted. + */ +static __always_inline void ** +find_empty_slot_for_expand(struct funcdesc_ht *htab, int hash) +{ + size_t size = htab->size; + unsigned int index = hash % size; + void **slot = htab->entries + index; + int hash2; + + if (!*slot) + return slot; + + hash2 = 1 + hash % (size - 2); + for (;;) { + index += hash2; + if (index >= size) + index -= size; + + slot = htab->entries + index; + if (!*slot) + return slot; + } +} + +/* + * The following function changes size of memory allocated for the + * entries and repeatedly inserts the table elements. The occupancy + * of the table after the call will be about 50%. Naturally the hash + * table must already exist. Remember also that the place of the + * table entries is changed. If memory allocation failures are allowed, + * this function will return zero, indicating that the table could not be + * expanded. If all goes well, it will return a non-zero value. + */ +static __always_inline int +htab_expand(struct funcdesc_ht *htab, int (*hash_fn) (void *)) +{ + void **oentries; + void **olimit; + void **p; + void **nentries; + size_t nsize; + + oentries = htab->entries; + olimit = oentries + htab->size; + + /* + * Resize only when table after removal of unused elements is either + * too full or too empty. + */ + if (htab->n_elements * 2 > htab->size) + nsize = higher_prime_number(htab->n_elements * 2); + else + nsize = htab->size; + + nentries = _dl_malloc(sizeof(*nentries) * nsize); + _dl_memset(nentries, 0, sizeof(*nentries) * nsize); + if (nentries == NULL) + return 0; + htab->entries = nentries; + htab->size = nsize; + + p = oentries; + do { + if (*p) + *find_empty_slot_for_expand(htab, hash_fn(*p)) = *p; + p++; + } while (p < olimit); + +#if 0 + /* + * We can't tell whether this was allocated by the _dl_malloc() + * built into ld.so or malloc() in the main executable or libc, + * and calling free() for something that wasn't malloc()ed could + * do Very Bad Things (TM). Take the conservative approach + * here, potentially wasting as much memory as actually used by + * the hash table, even if multiple growths occur. That's not + * so bad as to require some overengineered solution that would + * enable us to keep track of how it was allocated. + */ + _dl_free(oentries); +#endif + return 1; +} + +/* + * This function searches for a hash table slot containing an entry + * equal to the given element. To delete an entry, call this with + * INSERT = 0, then call htab_clear_slot on the slot returned (possibly + * after doing some checks). To insert an entry, call this with + * INSERT = 1, then write the value you want into the returned slot. + * When inserting an entry, NULL may be returned if memory allocation + * fails. + */ +static __always_inline void ** +htab_find_slot(struct funcdesc_ht *htab, void *ptr, int insert, + int (*hash_fn)(void *), int (*eq_fn)(void *, void *)) +{ + unsigned int index; + int hash, hash2; + size_t size; + void **entry; + + if (htab->size * 3 <= htab->n_elements * 4 && + htab_expand(htab, hash_fn) == 0) + return NULL; + + hash = hash_fn(ptr); + + size = htab->size; + index = hash % size; + + entry = &htab->entries[index]; + if (!*entry) + goto empty_entry; + else if (eq_fn(*entry, ptr)) + return entry; + + hash2 = 1 + hash % (size - 2); + for (;;) { + index += hash2; + if (index >= size) + index -= size; + + entry = &htab->entries[index]; + if (!*entry) + goto empty_entry; + else if (eq_fn(*entry, ptr)) + return entry; + } + + empty_entry: + if (!insert) + return NULL; + + htab->n_elements++; + return entry; +} + +#endif diff --git a/ldso/include/ldso.h b/ldso/include/ldso.h index 35a72fc5e..e237885b9 100644 --- a/ldso/include/ldso.h +++ b/ldso/include/ldso.h @@ -5,8 +5,8 @@ * GNU Lesser General Public License version 2.1 or later. */ -#ifndef _LDSO_H_ -#define _LDSO_H_ +#ifndef _LDSO_H +#define _LDSO_H #include <features.h> @@ -27,18 +27,46 @@ /* Pull in compiler and arch stuff */ #include <stdlib.h> #include <stdarg.h> +#include <stddef.h> /* for ptrdiff_t */ +#include <stdbool.h> +#define _FCNTL_H +/* We need this if arch has only new syscalls defined */ +#ifndef AT_FDCWD +#define AT_FDCWD -100 +#endif /* AT_FDCWD */ +#include <bits/fcntl.h> #include <bits/wordsize.h> /* Pull in the arch specific type information */ #include <sys/types.h> /* Pull in the arch specific page size */ #include <bits/uClibc_page.h> +/* Pull in the MIN macro */ +#include <sys/param.h> /* Pull in the ldso syscalls and string functions */ +#ifndef __ARCH_HAS_NO_SHARED__ #include <dl-syscall.h> #include <dl-string.h> /* Now the ldso specific headers */ #include <dl-elf.h> +#ifdef __UCLIBC_HAS_TLS__ +/* Defines USE_TLS */ +#include <tls.h> +#endif #include <dl-hash.h> +/* common align masks, if not specified by sysdep headers */ +#ifndef ADDR_ALIGN +#define ADDR_ALIGN (_dl_pagesize - 1) +#endif + +#ifndef PAGE_ALIGN +#define PAGE_ALIGN (~ADDR_ALIGN) +#endif + +#ifndef OFFS_ALIGN +#define OFFS_ALIGN (PAGE_ALIGN & ~(1ul << (sizeof(_dl_pagesize) * 8 - 1))) +#endif + /* For INIT/FINI dependency sorting. */ struct init_fini_list { struct init_fini_list *next; @@ -48,10 +76,25 @@ struct init_fini_list { /* Global variables used within the shared library loader */ extern char *_dl_library_path; /* Where we look for libraries */ extern char *_dl_preload; /* Things to be loaded before the libs */ -extern char *_dl_ldsopath; /* Where the shared lib loader was found */ +#ifdef __LDSO_SEARCH_INTERP_PATH__ +extern const char *_dl_ldsopath; /* Where the shared lib loader was found */ +#endif extern const char *_dl_progname; /* The name of the executable being run */ -extern int _dl_secure; /* Are we dealing with setuid stuff? */ extern size_t _dl_pagesize; /* Store the page size for use later */ +#ifdef __LDSO_PRELINK_SUPPORT__ +extern char *_dl_trace_prelink; /* Library for prelinking trace */ +extern struct elf_resolve *_dl_trace_prelink_map; /* Library map for prelinking trace */ +#else +#define _dl_trace_prelink 0 +#endif +#ifdef __DSBT__ +extern void **_dl_ldso_dsbt; +#endif + +#if defined(USE_TLS) && USE_TLS +extern void _dl_add_to_slotinfo (struct link_map *l); +extern void ** __attribute__ ((const)) _dl_initial_error_catch_tsd (void); +#endif #ifdef __SUPPORT_LD_DEBUG__ extern char *_dl_debug; @@ -63,13 +106,14 @@ extern char *_dl_debug_nofixups; extern char *_dl_debug_bindings; extern int _dl_debug_file; # define __dl_debug_dprint(fmt, args...) \ - _dl_dprintf(_dl_debug_file, "%s:%i: " fmt, __FUNCTION__, __LINE__, ## args); + _dl_dprintf(_dl_debug_file, "%s:%i: " fmt, __func__, __LINE__, ## args); # define _dl_if_debug_dprint(fmt, args...) \ do { if (_dl_debug) __dl_debug_dprint(fmt, ## args); } while (0) #else # define __dl_debug_dprint(fmt, args...) do {} while (0) # define _dl_if_debug_dprint(fmt, args...) do {} while (0) -# define _dl_debug_file 2 +/* disabled on purpose, _dl_debug_file should be guarded by __SUPPORT_LD_DEBUG__ +# define _dl_debug_file 2*/ #endif /* __SUPPORT_LD_DEBUG__ */ #ifdef IS_IN_rtld @@ -100,11 +144,24 @@ extern int _dl_debug_file; #endif extern void *_dl_malloc(size_t size); +extern void *_dl_calloc(size_t __nmemb, size_t __size); +extern void *_dl_realloc(void *__ptr, size_t __size); extern void _dl_free(void *); extern char *_dl_getenv(const char *symbol, char **envp); extern void _dl_unsetenv(const char *symbol, char **envp); +#ifdef IS_IN_rtld extern char *_dl_strdup(const char *string); extern void _dl_dprintf(int, const char *, ...); +#else +# include <string.h> +# define _dl_strdup strdup +# include <stdio.h> +# ifdef __USE_GNU +# define _dl_dprintf dprintf +# else +# define _dl_dprintf(fd, fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#endif #ifndef DL_GET_READY_TO_RUN_EXTRA_PARMS # define DL_GET_READY_TO_RUN_EXTRA_PARMS @@ -113,7 +170,7 @@ extern void _dl_dprintf(int, const char *, ...); # define DL_GET_READY_TO_RUN_EXTRA_ARGS #endif -extern void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, +extern void *_dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, ElfW(auxv_t) auxvt[AT_EGID + 1], char **envp, char **argv DL_GET_READY_TO_RUN_EXTRA_PARMS); @@ -121,4 +178,8 @@ extern void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load #include <dl-inlines.h> #endif -#endif /* _LDSO_H_ */ +#else /* __ARCH_HAS_NO_SHARED__ */ +#include <dl-defs.h> +#endif + +#endif /* _LDSO_H */ diff --git a/ldso/include/ldsodefs.h b/ldso/include/ldsodefs.h new file mode 100644 index 000000000..9ae645c60 --- /dev/null +++ b/ldso/include/ldsodefs.h @@ -0,0 +1,154 @@ +#ifndef _LDSODEFS_H +#define _LDSODEFS_H 1 + +#include <bits/kernel-features.h> + +#include <features.h> +#include <tls.h> + +#ifdef __mips__ +/* The MIPS ABI specifies that the dynamic section has to be read-only. */ + +#define DL_RO_DYN_SECTION 1 + +/* TODO: Import in 64-bit relocations from glibc. */ +#endif + +#ifndef SHARED +# define EXTERN extern +#else +# ifdef IS_IN_rtld +# define EXTERN +# else +# define EXTERN extern +# endif +#endif + +/* Non-shared code has no support for multiple namespaces. */ +#ifdef SHARED +# define DL_NNS 16 +#else +# define DL_NNS 1 +#endif + +#define GL(x) _##x +#define GLRO(x) _##x + +/* Variable pointing to the end of the stack (or close to it). This value + must be constant over the runtime of the application. Some programs + might use the variable which results in copy relocations on some + platforms. But this does not matter, ld.so can always use the local + copy. */ +extern void *__libc_stack_end; + +/* Determine next available module ID. */ +extern size_t _dl_next_tls_modid (void) internal_function attribute_hidden; + +/* Calculate offset of the TLS blocks in the static TLS block. */ +extern void _dl_determine_tlsoffset (void) internal_function attribute_hidden; + +/* Set up the data structures for TLS, when they were not set up at startup. + Returns nonzero on malloc failure. + This is called from _dl_map_object_from_fd or by libpthread. */ +extern int _dl_tls_setup (void) internal_function; +rtld_hidden_proto (_dl_tls_setup) + +/* Allocate memory for static TLS block (unless MEM is nonzero) and dtv. */ +extern void *_dl_allocate_tls (void *mem) internal_function; + +/* Get size and alignment requirements of the static TLS block. */ +extern void _dl_get_tls_static_info (size_t *sizep, size_t *alignp) + internal_function; + +extern void _dl_allocate_static_tls (struct link_map *map) + internal_function attribute_hidden; +extern int _dl_try_allocate_static_tls (struct link_map* map) + internal_function attribute_hidden; + +/* Taken from glibc/elf/dl-reloc.c */ +#define CHECK_STATIC_TLS(sym_map) \ + do { \ + if (__builtin_expect ((sym_map)->l_tls_offset == NO_TLS_OFFSET, 0)) \ + _dl_allocate_static_tls (sym_map); \ + } while (0) +#define TRY_STATIC_TLS(sym_map) \ + (__builtin_expect ((sym_map)->l_tls_offset != NO_TLS_OFFSET, 1) \ + || _dl_try_allocate_static_tls (sym_map) == 0) + +/* These are internal entry points to the two halves of _dl_allocate_tls, + only used within rtld.c itself at startup time. */ +extern void *_dl_allocate_tls_storage (void) + internal_function attribute_hidden; +extern void *_dl_allocate_tls_init (void *) internal_function; + +/* Deallocate memory allocated with _dl_allocate_tls. */ +extern void _dl_deallocate_tls (void *tcb, bool dealloc_tcb) internal_function; + +extern void _dl_nothread_init_static_tls (struct link_map *) attribute_hidden; + +/* Highest dtv index currently needed. */ +EXTERN size_t _dl_tls_max_dtv_idx; +/* Flag signalling whether there are gaps in the module ID allocation. */ +EXTERN bool _dl_tls_dtv_gaps; +/* Information about the dtv slots. */ +EXTERN struct dtv_slotinfo_list +{ + size_t len; + struct dtv_slotinfo_list *next; + struct dtv_slotinfo + { + size_t gen; + bool is_static; + struct link_map *map; + } slotinfo[0]; +} *_dl_tls_dtv_slotinfo_list; +/* Number of modules in the static TLS block. */ +EXTERN size_t _dl_tls_static_nelem; +/* Size of the static TLS block. */ +EXTERN size_t _dl_tls_static_size; +/* Size actually allocated in the static TLS block. */ +EXTERN size_t _dl_tls_static_used; +/* Alignment requirement of the static TLS block. */ +EXTERN size_t _dl_tls_static_align; +/* Function pointer for catching TLS errors. */ +#if 1 /* def _LIBC_REENTRANT */ +EXTERN void **(*_dl_error_catch_tsd) (void) __attribute__ ((const)); +#endif + +/* Number of additional entries in the slotinfo array of each slotinfo + list element. A large number makes it almost certain take we never + have to iterate beyond the first element in the slotinfo list. */ +# define TLS_SLOTINFO_SURPLUS (62) + +/* Number of additional slots in the dtv allocated. */ +# define DTV_SURPLUS (14) + +/* Initial dtv of the main thread, not allocated with normal malloc. */ +EXTERN void *_dl_initial_dtv; +/* Generation counter for the dtv. */ +EXTERN size_t _dl_tls_generation; + +EXTERN void (*_dl_init_static_tls) (struct link_map *); + +/* We have the auxiliary vector. */ +#define HAVE_AUX_VECTOR + +/* We can assume that the kernel always provides the AT_UID, AT_EUID, + AT_GID, and AT_EGID values in the auxiliary vector from 2.4.0 or so on. */ +#if __ASSUME_AT_XID +# define HAVE_AUX_XID +#endif + +/* We can assume that the kernel always provides the AT_SECURE value + in the auxiliary vector from 2.5.74 or so on. */ +#if __ASSUME_AT_SECURE +# define HAVE_AUX_SECURE +#endif + +/* Starting with one of the 2.4.0 pre-releases the Linux kernel passes + up the page size information. */ +#if __ASSUME_AT_PAGESIZE +# define HAVE_AUX_PAGESIZE +#endif + +#endif diff --git a/ldso/include/tlsdeschtab.h b/ldso/include/tlsdeschtab.h new file mode 100644 index 000000000..056f859b7 --- /dev/null +++ b/ldso/include/tlsdeschtab.h @@ -0,0 +1,121 @@ +/* Hash table for TLS descriptors. + Copyright (C) 2005-2013 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Alexandre Oliva <aoliva@redhat.com> + + uClibc port by Baruch Siach <baruch@tkos.co.il> + + 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, see + <http://www.gnu.org/licenses/>. */ + +#ifndef TLSDESCHTAB_H +# define TLSDESCHTAB_H 1 + +# ifdef SHARED + +# include <inline-hashtab.h> + +inline static int +hash_tlsdesc (void *p) +{ + struct tlsdesc_dynamic_arg *td = p; + + /* We know all entries are for the same module, so ti_offset is the + only distinguishing entry. */ + return td->tlsinfo.ti_offset; +} + +inline static int +eq_tlsdesc (void *p, void *q) +{ + struct tlsdesc_dynamic_arg *tdp = p, *tdq = q; + + return tdp->tlsinfo.ti_offset == tdq->tlsinfo.ti_offset; +} + +inline static int +map_generation (struct link_map *map) +{ + size_t idx = map->l_tls_modid; + struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list); + + /* Find the place in the dtv slotinfo list. */ + do + { + /* Does it fit in the array of this list element? */ + if (idx < listp->len) + { + /* We should never get here for a module in static TLS, so + we can assume that, if the generation count is zero, we + still haven't determined the generation count for this + module. */ + if (listp->slotinfo[idx].gen) + return listp->slotinfo[idx].gen; + else + break; + } + idx -= listp->len; + listp = listp->next; + } + while (listp != NULL); + + /* If we get to this point, the module still hasn't been assigned an + entry in the dtv slotinfo data structures, and it will when we're + done with relocations. At that point, the module will get a + generation number that is one past the current generation, so + return exactly that. */ + return GL(dl_tls_generation) + 1; +} + +void * +internal_function +_dl_make_tlsdesc_dynamic (struct link_map *map, size_t ti_offset) +{ + struct funcdesc_ht *ht; + void **entry; + struct tlsdesc_dynamic_arg *td, test; + + ht = map->l_tlsdesc_table; + if (! ht) + { + ht = htab_create (); + if (! ht) + return 0; + map->l_tlsdesc_table = ht; + } + + test.tlsinfo.ti_module = map->l_tls_modid; + test.tlsinfo.ti_offset = ti_offset; + entry = htab_find_slot (ht, &test, 1, hash_tlsdesc, eq_tlsdesc); + if (entry == NULL) + _dl_exit(1); + if (*entry) + { + td = *entry; + return td; + } + + *entry = td = _dl_malloc (sizeof (struct tlsdesc_dynamic_arg)); + /* This may be higher than the map's generation, but it doesn't + matter much. Worst case, we'll have one extra DTV update per + thread. */ + td->gen_count = map_generation (map); + td->tlsinfo = test.tlsinfo; + + return td; +} + +# endif /* SHARED */ + +#endif |
