diff options
Diffstat (limited to 'ldso')
-rw-r--r-- | ldso/include/dl-defs.h | 2 | ||||
-rw-r--r-- | ldso/include/dl-elf.h | 18 | ||||
-rw-r--r-- | ldso/include/dl-syscall.h | 2 | ||||
-rw-r--r-- | ldso/ldso/Makefile.in | 1 | ||||
-rw-r--r-- | ldso/ldso/bfin/dl-syscalls.h | 12 | ||||
-rw-r--r-- | ldso/ldso/c6x/dl-debug.h | 49 | ||||
-rw-r--r-- | ldso/ldso/c6x/dl-inlines.h | 91 | ||||
-rw-r--r-- | ldso/ldso/c6x/dl-startup.h | 122 | ||||
-rw-r--r-- | ldso/ldso/c6x/dl-syscalls.h | 25 | ||||
-rw-r--r-- | ldso/ldso/c6x/dl-sysdep.h | 209 | ||||
-rw-r--r-- | ldso/ldso/c6x/elfinterp.c | 296 | ||||
-rw-r--r-- | ldso/ldso/c6x/resolve.S | 68 | ||||
-rw-r--r-- | ldso/ldso/dl-elf.c | 50 | ||||
-rw-r--r-- | ldso/ldso/i386/elfinterp.c | 2 | ||||
-rw-r--r-- | ldso/ldso/ldso.c | 14 | ||||
-rw-r--r-- | ldso/ldso/sparc/elfinterp.c | 2 | ||||
-rw-r--r-- | ldso/ldso/x86_64/elfinterp.c | 2 | ||||
-rw-r--r-- | ldso/libdl/Makefile.in | 1 | ||||
-rw-r--r-- | ldso/libdl/libdl.c | 5 |
19 files changed, 948 insertions, 23 deletions
diff --git a/ldso/include/dl-defs.h b/ldso/include/dl-defs.h index cbbaa3cea..d387c0bb9 100644 --- a/ldso/include/dl-defs.h +++ b/ldso/include/dl-defs.h @@ -212,7 +212,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. diff --git a/ldso/include/dl-elf.h b/ldso/include/dl-elf.h index 40c88b9da..908cb63ce 100644 --- a/ldso/include/dl-elf.h +++ b/ldso/include/dl-elf.h @@ -200,8 +200,13 @@ unsigned int __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) - /* Don't adjust .dynamic unnecessarily. */ - if (load_off != 0) { + /* 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 __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); @@ -212,6 +217,15 @@ unsigned int __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info 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); + + /* Initialize loadmap dsbt info. */ + load_off.map->dsbt_table = dynamic_info[DT_DSBT_BASE_IDX]; + load_off.map->dsbt_size = dynamic_info[DT_DSBT_SIZE_IDX]; + load_off.map->dsbt_index = dynamic_info[DT_DSBT_INDEX_IDX]; +#endif #undef ADJUST_DYN_INFO return rtld_flags; } diff --git a/ldso/include/dl-syscall.h b/ldso/include/dl-syscall.h index 982bc9bf6..da3ac7f6f 100644 --- a/ldso/include/dl-syscall.h +++ b/ldso/include/dl-syscall.h @@ -95,7 +95,7 @@ static __always_inline _syscall0(gid_t, _dl_getegid) # 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) #define __NR__dl_readlink __NR_readlink static __always_inline _syscall3(int, _dl_readlink, const char *, path, char *, buf, diff --git a/ldso/ldso/Makefile.in b/ldso/ldso/Makefile.in index c9dcebd60..ad8edbb65 100644 --- a/ldso/ldso/Makefile.in +++ b/ldso/ldso/Makefile.in @@ -30,6 +30,7 @@ CFLAGS-ldso/ldso/$(TARGET_ARCH)/ := $(CFLAGS-ldso) CFLAGS-ldso.c := -DLDSO_ELFINTERP=\"$(TARGET_ARCH)/elfinterp.c\" $(CFLAGS-ldso) +LDFLAGS-$(UCLIBC_FORMAT_DSBT_ELF)-$(UCLIBC_LDSO_NAME).so := -Wl,--dsbt-index=1 ifneq ($(SUPPORT_LD_DEBUG),y) LDFLAGS-$(UCLIBC_LDSO_NAME).so := $(LDFLAGS) else diff --git a/ldso/ldso/bfin/dl-syscalls.h b/ldso/ldso/bfin/dl-syscalls.h index 916d0f84e..f74cf1ad3 100644 --- a/ldso/ldso/bfin/dl-syscalls.h +++ b/ldso/ldso/bfin/dl-syscalls.h @@ -31,7 +31,7 @@ extern int _dl_errno; #if DYNAMIC_LOADER_IN_SIMULATOR #define __NR___syscall_mmap2 __NR_mmap2 static __inline__ _syscall6(__ptr_t, __syscall_mmap2, __ptr_t, addr, - size_t, len, int, prot, int, flags, int, fd, off_t, offset); + size_t, len, int, prot, int, flags, int, fd, off_t, offset) /* Make sure we don't get another definition of _dl_mmap from the machine-independent code. */ @@ -155,7 +155,7 @@ _dl_mmap(__ptr_t addr, size_t len, int prot, int flags, int fd, __off_t offset) static __always_inline unsigned long _dl_read(int fd, const void *buf, unsigned long count); static __always_inline _syscall3(__off_t, __syscall_lseek, int, fd, __off_t, offset, - int, whence); + int, whence) static __always_inline ssize_t _dl_pread(int fd, void *buf, size_t count, off_t offset) { @@ -178,7 +178,7 @@ _dl_pread(int fd, void *buf, size_t count, off_t offset) #else #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); + 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) @@ -191,18 +191,18 @@ _dl_pread(int fd, void *buf, size_t count, off_t offset) #ifdef __NR_sram_alloc #define __NR__dl_sram_alloc __NR_sram_alloc static __always_inline _syscall2(__ptr_t, _dl_sram_alloc, - size_t, len, unsigned long, flags); + size_t, len, unsigned long, flags) #endif #ifdef __NR_sram_free #define __NR__dl_sram_free __NR_sram_free -static __always_inline _syscall1(int, _dl_sram_free, __ptr_t, addr); +static __always_inline _syscall1(int, _dl_sram_free, __ptr_t, addr) #endif #ifdef __NR_dma_memcpy #define __NR__dl_dma_memcpy __NR_dma_memcpy static __always_inline _syscall3(__ptr_t, _dl_dma_memcpy, - __ptr_t, dest, __ptr_t, src, size_t, len); + __ptr_t, dest, __ptr_t, src, size_t, len) #endif #define __UCLIBC_MMAP_HAS_6_ARGS__ diff --git a/ldso/ldso/c6x/dl-debug.h b/ldso/ldso/c6x/dl-debug.h new file mode 100644 index 000000000..9c6edab0a --- /dev/null +++ b/ldso/ldso/c6x/dl-debug.h @@ -0,0 +1,49 @@ +/* C6X DSBT ELF shared library loader suppport. + * + * Copyright (C) 2010 Texas Instruments Incorporated + * Contributed by Mark Salter <msalter@redhat.com> + * + * All rights reserved. + * + * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + */ + +static const char *_dl_reltypes_tab[] = +{ + "R_C6000_NONE", /* 0 */ + "R_C6000_ABS32", + "R_C6000_ABS16", + "R_C6000_ABS8", + "R_C6000_PCR_S21", + "R_C6000_PCR_S12", /* 5 */ + "R_C6000_PCR_S10", + "R_C6000_PCR_S7", + "R_C6000_ABS_S16", + "R_C6000_ABS_L16", + "R_C6000_ABS_H16", /* 10 */ + "R_C6000_SBR_U15_B", + "R_C6000_SBR_U15_H", + "R_C6000_SBR_U15_W", + "R_C6000_SBR_S16", + "R_C6000_SBR_L16_B", /* 15 */ + "R_C6000_SBR_L16_H", + "R_C6000_SBR_L16_W", + "R_C6000_SBR_H16_B", + "R_C6000_SBR_H16_H", + "R_C6000_SBR_H16_W", /* 20 */ + "R_C6000_SBR_GOT_U15_W", + "R_C6000_SBR_GOT_L16_W", + "R_C6000_SBR_GOT_H16_W", + "R_C6000_DSBT_INDEX", + "R_C6000_PREL31", /* 25 */ + "R_C6000_COPY", + "R_C6000_JUMP_SLOT", + "R_C6000_SBR_GOT32", + "R_C6000_PCR_H16", + "R_C6000_PCR_L16", /* 30 */ +#if 0 + "R_C6000_ALIGN", /* 253 */ + "R_C6000_FPHEAD", /* 254 */ + "R_C6000_NOCMP", /* 255 */ +#endif +}; diff --git a/ldso/ldso/c6x/dl-inlines.h b/ldso/ldso/c6x/dl-inlines.h new file mode 100644 index 000000000..d8fb42c55 --- /dev/null +++ b/ldso/ldso/c6x/dl-inlines.h @@ -0,0 +1,91 @@ +/* Copyright (C) 2010 Texas Instruments Incorporated + * Contributed by Mark Salter <msalter@redhat.com> + * + * Borrowed heavily from frv arch: + * Copyright (C) 2003, 2004 Red Hat, Inc. + * + * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + */ + +/* Figure out whether the given address is in one of the mapped + segments. */ +static __always_inline int +__dl_addr_in_loadaddr (void *p, struct elf32_dsbt_loadaddr loadaddr) +{ + struct elf32_dsbt_loadmap *map = loadaddr.map; + int c; + + for (c = 0; c < map->nsegs; c++) + if ((void*)map->segs[c].addr <= p + && (char*)p < (char*)map->segs[c].addr + map->segs[c].p_memsz) + return 1; + + return 0; +} + +/* Figure out how many LOAD segments there are in the given headers, + and allocate a block for the load map big enough for them. + got_value will be properly initialized later on, with INIT_GOT. */ +static __always_inline int +__dl_init_loadaddr (struct elf32_dsbt_loadaddr *loadaddr, Elf32_Phdr *ppnt, + int pcnt) +{ + int count = 0, i; + size_t size; + + for (i = 0; i < pcnt; i++) + if (ppnt[i].p_type == PT_LOAD) + count++; + + size = sizeof (struct elf32_dsbt_loadmap) + + sizeof (struct elf32_dsbt_loadseg) * count; + loadaddr->map = _dl_malloc (size); + if (! loadaddr->map) + _dl_exit (-1); + + loadaddr->map->version = 0; + loadaddr->map->nsegs = 0; + + return count; +} + +/* Incrementally initialize a load map. */ +static __always_inline void +__dl_init_loadaddr_hdr (struct elf32_dsbt_loadaddr loadaddr, void *addr, + Elf32_Phdr *phdr, int maxsegs) +{ + struct elf32_dsbt_loadseg *segdata; + + if (loadaddr.map->nsegs == maxsegs) + _dl_exit (-1); + + segdata = &loadaddr.map->segs[loadaddr.map->nsegs++]; + segdata->addr = (Elf32_Addr) addr; + segdata->p_vaddr = phdr->p_vaddr; + segdata->p_memsz = phdr->p_memsz; + +#if defined (__SUPPORT_LD_DEBUG__) + { + if (_dl_debug) + _dl_dprintf(_dl_debug_file, "%i: mapped %x at %x, size %x\n", + loadaddr.map->nsegs-1, + segdata->p_vaddr, segdata->addr, segdata->p_memsz); + } +#endif +} + +static __always_inline void +__dl_loadaddr_unmap (struct elf32_dsbt_loadaddr loadaddr) +{ + int i; + + for (i = 0; i < loadaddr.map->nsegs; i++) + _dl_munmap ((void*)loadaddr.map->segs[i].addr, + loadaddr.map->segs[i].p_memsz); + + /* _dl_unmap is only called for dlopen()ed libraries, for which + calling free() is safe, or before we've completed the initial + relocation, in which case calling free() is probably pointless, + but still safe. */ + _dl_free (loadaddr.map); +} diff --git a/ldso/ldso/c6x/dl-startup.h b/ldso/ldso/c6x/dl-startup.h new file mode 100644 index 000000000..b9ea30409 --- /dev/null +++ b/ldso/ldso/c6x/dl-startup.h @@ -0,0 +1,122 @@ +/* Copyright (C) 2010 Texas Instruments Incorporated + * Contributed by Mark Salter <msalter@redhat.com> + * + * Borrowed heavily from frv arch: + * Copyright (C) 2003 Red Hat, Inc. + * + * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + */ + +#undef DL_START +#define DL_START(X) \ +int \ +_dl_start (unsigned placeholder, \ + struct elf32_dsbt_loadmap *dl_boot_progmap, \ + struct elf32_dsbt_loadmap *dl_boot_ldsomap, \ + Elf32_Dyn *dl_boot_ldso_dyn_pointer, \ + X) + +/* + * On entry, the kernel has set up the stack thusly: + * + * 0(sp) pad0 + * 4(sp) pad1 + * 8(sp) argc + * 12(sp) argv[0] + * ... + * (4*(argc+3))(sp) NULL + * (4*(argc+4))(sp) envp[0] + * ... + * NULL + * + * Register values are unspecified, except: + * + * B4 --> executable loadmap address + * A6 --> interpreter loadmap address + * B6 --> dynamic section address + * B14 --> our DP setup by kernel + * + * NB: DSBT index is always 0 for the executable + * and 1 for the interpreter + */ + +__asm__(" .text\n" + ".globl _start\n" + "_start:\n" + " B .S2 _dl_start\n" + " STW .D2T2 B14, *+B14[1]\n" + " ADD .D1X B15,8,A8\n" + " ADDKPC .S2 ret_from_dl,B3,2\n" + "ret_from_dl:\n" + " B .S2X A4\n" + " || LDW .D2T2 *+B14[0],B14\n" + " ADDKPC .S2 __dl_fini,B0,0\n" + " MV .S1X B0,A4\n" + " NOP\n" + " NOP\n" + " NOP\n" + "__dl_fini:\n" + " LDW .D2T2 *+B14[1],B14\n" + " NOP 4\n" + " LDW .D2T1 *+B14($GOT(_dl_fini)), A0\n" + " NOP 4\n" + " BNOP .S2X A0, 5\n"); + +__asm__(" .text\n" + "__c6x_cache_sync:\n" + " MVK .S2 330,B0\n" + " SWE\n" + " NOP\n" + " BNOP .S2 B3,5\n" + " NOP\n" + " NOP\n" + " NOP\n" + " NOP\n" + "\n" +); + +/* + * Get a pointer to the argv array. On many platforms this can be just + * the address of the first argument, on other platforms we need to + * do something a little more subtle here. + */ +#define GET_ARGV(ARGVP, ARGS) ARGVP = (((unsigned long*) ARGS) + 1) + +struct elf32_dsbt_loadmap; + +/* + * Here is a macro to perform a relocation. This is only used when + * bootstrapping the dynamic loader. RELP is the relocation that we + * are performing, REL is the pointer to the address we are relocating. + * SYMBOL is the symbol involved in the relocation, and LOAD is the + * load address. + */ +#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD,SYMTAB) \ + switch(ELF32_R_TYPE((RELP)->r_info)){ \ + case R_C6000_ABS_L16: \ + { \ + unsigned int opcode = *(REL); \ + unsigned int v = (SYMBOL) + (RELP)->r_addend; \ + opcode &= ~0x7fff80; \ + opcode |= ((v & 0xffff) << 7); \ + *(REL) = opcode; \ + } \ + break; \ + case R_C6000_ABS_H16: \ + { \ + unsigned int opcode = *(REL); \ + unsigned int v = (SYMBOL) + (RELP)->r_addend; \ + opcode &= ~0x7fff80; \ + opcode |= ((v >> 9) & 0x7fff80); \ + *(REL) = opcode; \ + } \ + break; \ + case R_C6000_ABS32: \ + *(REL) = (SYMBOL) + (RELP)->r_addend; \ + break; \ + default: \ + _dl_exit(1); \ + } + +extern void __c6x_cache_sync(unsigned long start, unsigned long end) + attribute_hidden; diff --git a/ldso/ldso/c6x/dl-syscalls.h b/ldso/ldso/c6x/dl-syscalls.h new file mode 100644 index 000000000..9ff3358b0 --- /dev/null +++ b/ldso/ldso/c6x/dl-syscalls.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2010 Texas Instruments Incorporated + * Contributed by Mark Salter <msalter@redhat.com> + * + * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + */ + +/* 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);} +#include <sys/mman.h> + +#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)); +} +#endif diff --git a/ldso/ldso/c6x/dl-sysdep.h b/ldso/ldso/c6x/dl-sysdep.h new file mode 100644 index 000000000..8f1b122d3 --- /dev/null +++ b/ldso/ldso/c6x/dl-sysdep.h @@ -0,0 +1,209 @@ +/* Copyright (C) 2010 Texas Instruments Incorporated + * Contributed by Mark Salter <msalter@redhat.com> + * + * Borrowed heavily from frv arch: + * Copyright (C) 2003, 2004 Red Hat, Inc. + * + * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + */ + +#include <bits/elf-dsbt.h> + +/* + * Define this if the system uses RELOCA. + */ +#define ELF_USES_RELOCA 1 + +/* JMPREL relocs are inside the DT_RELA table. */ +/* Actually looks like a linker bug sets DT_JMPREL anyway */ +#define ELF_MACHINE_PLTREL_OVERLAP 1 + +#undef DL_NO_COPY_RELOCS + +#define HAVE_DL_INLINES_H + + +/* + * Various assembly language/system dependent hacks that are required + * so that we can minimize the amount of platform specific code. + */ + +/* Initialization sequence for the GOT. */ +#define INIT_GOT(GOT_BASE,MODULE) \ +{ \ + GOT_BASE[0] = (unsigned long) _dl_linux_resolve; \ + GOT_BASE[1] = (unsigned long) MODULE; \ +} + +/* Here we define the magic numbers that this dynamic loader should accept */ +#define MAGIC1 EM_TI_C6000 +#undef MAGIC2 + +/* Used for error messages */ +#define ELF_TARGET "C6000" + +/* Need bootstrap relocations */ +#define ARCH_NEEDS_BOOTSTRAP_RELOCS + +struct elf_resolve; + +extern int _dl_linux_resolve(void) attribute_hidden; + +struct funcdesc_ht; +struct elf32_dsbt_loadaddr; + +/* We must force strings used early in the bootstrap into the text + segment (const data), such that they are referenced relative to + the DP register rather than through the GOT which will not have + been relocated when these are used. */ +#undef SEND_EARLY_STDERR +#define SEND_EARLY_STDERR(S) \ + do { static char __s[] = (S); SEND_STDERR (__s); } while (0) + +#define DL_LOADADDR_TYPE struct elf32_dsbt_loadaddr + +#define DL_RELOC_ADDR(LOADADDR, ADDR) \ + ((ElfW(Addr))__reloc_pointer ((void*)(ADDR), (LOADADDR).map)) + +#define DL_INIT_LOADADDR_BOOT(LOADADDR, BASEADDR) \ + do { \ + struct elf32_dsbt_loadmap *map; \ + map = dl_boot_ldsomap ?: dl_boot_progmap; \ + if (map->version != 0) { \ + SEND_EARLY_STDERR ("Invalid loadmap version number\n"); \ + _dl_exit(-1); \ + } \ + if (map->nsegs < 2) { \ + SEND_EARLY_STDERR ("Invalid segment count in loadmap\n"); \ + _dl_exit(-1); \ + } \ + (LOADADDR).map = map; \ + } while(0) + +#define DL_INIT_LOADADDR_PROG(LOADADDR, BASEADDR) \ + do { \ + if (dl_boot_progmap->version != 0) { \ + SEND_EARLY_STDERR ("Invalid loadmap version number\n"); \ + _dl_exit(-1); \ + } \ + if (dl_boot_progmap->nsegs < 2) { \ + SEND_EARLY_STDERR ("Invalid segment count in loadmap\n"); \ + _dl_exit(-1); \ + } \ + (LOADADDR).map = dl_boot_progmap; \ + } while(0) + +#define DL_INIT_LOADADDR_EXTRA_DECLS \ + int dl_init_loadaddr_load_count; + +#define DL_INIT_LOADADDR(LOADADDR, BASEADDR, PHDR, PHDRCNT) \ + (dl_init_loadaddr_load_count = \ + __dl_init_loadaddr (&(LOADADDR), (PHDR), (PHDRCNT))) + +#define DL_INIT_LOADADDR_HDR(LOADADDR, ADDR, PHDR) \ + (__dl_init_loadaddr_hdr ((LOADADDR), (ADDR), (PHDR), \ + dl_init_loadaddr_load_count)) + +#define DL_LOADADDR_UNMAP(LOADADDR, LEN) \ + (__dl_loadaddr_unmap ((LOADADDR))) + +#define DL_LIB_UNMAP(LIB, LEN) \ + (__dl_loadaddr_unmap ((LIB)->loadaddr)) + +#define DL_LOADADDR_BASE(LOADADDR) \ + ((LOADADDR).map->dsbt_table) + +#define DL_ADDR_IN_LOADADDR(ADDR, TPNT, TFROM) \ + (! (TFROM) && __dl_addr_in_loadaddr ((void*)(ADDR), (TPNT)->loadaddr)) + + +/* We only support loading DSBT relocatable shared libraries. + It probably wouldn't be too hard to support loading statically + linked executables that require relocation.*/ +#define DL_CHECK_LIB_TYPE(epnt, piclib, _dl_progname, libname) \ +do \ +{ \ + (piclib) = 2; \ +} \ +while (0) + +/* We want want to apply all relocations in the interpreter during + bootstrap. Because of this, we have to skip the interpreter + relocations in _dl_parse_relocation_information(), see + elfinterp.c. */ +#define DL_SKIP_BOOTSTRAP_RELOC(SYMTAB, INDEX, STRTAB) 0 + +#ifdef __NR_pread64 +#define _DL_PREAD(FD, BUF, SIZE, OFFSET) \ + (_dl_pread((FD), (BUF), (SIZE), (OFFSET))) +#endif + +#define DL_GET_READY_TO_RUN_EXTRA_PARMS \ + , struct elf32_dsbt_loadmap *dl_boot_progmap \ + , struct elf32_dsbt_loadmap *dl_boot_ldsomap +#define DL_GET_READY_TO_RUN_EXTRA_ARGS \ + , dl_boot_progmap \ + , dl_boot_ldsomap + + +/* + * Compute the GOT address. + * Also setup program and interpreter DSBT table entries. + */ +#define DL_BOOT_COMPUTE_GOT(GOT) \ + do { \ + unsigned long *ldso_dsbt, *prog_dsbt; \ + ldso_dsbt = dl_boot_ldsomap->dsbt_table; \ + prog_dsbt = dl_boot_progmap->dsbt_table; \ + ldso_dsbt[0] = prog_dsbt[0] = (unsigned long)prog_dsbt; \ + ldso_dsbt[1] = prog_dsbt[1] = (unsigned long)ldso_dsbt; \ + (GOT) = ldso_dsbt + dl_boot_ldsomap->dsbt_size; \ + } while(0) + +#define DL_BOOT_COMPUTE_DYN(dpnt, got, load_addr) \ + ((dpnt) = dl_boot_ldso_dyn_pointer) + + +#ifdef __USE_GNU +# include <link.h> +#else +# define __USE_GNU +# include <link.h> +# undef __USE_GNU +#endif + +static __always_inline Elf32_Addr +elf_machine_load_address (void) +{ + /* this is never an issue on DSBT systems */ + return 0; +} + +static __always_inline void +elf_machine_relative (DL_LOADADDR_TYPE load_off, const Elf32_Addr rel_addr, + Elf32_Word relative_count) +{ +} + +/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry, so + PLT entries should not be allowed to define the value. + ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one + of the main executable's symbols, as for a COPY reloc. */ +#define elf_machine_type_class(type) \ + ((((type) == R_C6000_JUMP_SLOT) * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_C6000_COPY) * ELF_RTYPE_CLASS_COPY)) + +#define ARCH_NUM 3 +#define DT_DSBT_BASE_IDX (DT_NUM + OS_NUM) +#define DT_DSBT_SIZE_IDX (DT_NUM + OS_NUM + 1) +#define DT_DSBT_INDEX_IDX (DT_NUM + OS_NUM + 2) + +#define ARCH_DYNAMIC_INFO(dpnt, dynamic, debug_addr) \ +do { \ +if (dpnt->d_tag == DT_C6000_DSBT_BASE) \ + dynamic[DT_DSBT_BASE_IDX] = dpnt->d_un.d_val; \ +else if (dpnt->d_tag == DT_C6000_DSBT_SIZE) \ + dynamic[DT_DSBT_SIZE_IDX] = dpnt->d_un.d_val; \ +else if (dpnt->d_tag == DT_C6000_DSBT_INDEX) \ + dynamic[DT_DSBT_INDEX_IDX] = dpnt->d_un.d_val; \ +} while (0) diff --git a/ldso/ldso/c6x/elfinterp.c b/ldso/ldso/c6x/elfinterp.c new file mode 100644 index 000000000..7c79171ce --- /dev/null +++ b/ldso/ldso/c6x/elfinterp.c @@ -0,0 +1,296 @@ +/* TI C64X DSBT ELF shared library loader suppport + * Copyright (C) 2010 Texas Instruments Incorporated + * Contributed by Mark Salter <msalter@redhat.com> + * + * Borrowed heavily from frv arch: + * Copyright (C) 2003, 2004 Red Hat, Inc. + * Contributed by Alexandre Oliva <aoliva@redhat.com> + * Lots of code copied from ../i386/elfinterp.c, so: + * Copyright (c) 1994-2000 Eric Youngdale, Peter MacDonald, + * David Engel, Hongjiu Lu and Mitch D'Souza + * Copyright (C) 2001-2002, Erik Andersen + * All rights reserved. + * + * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + */ + +#include <features.h> + +/* Program to load an ELF binary on a linux system, and run it. + References to symbols in sharable libraries can be resolved by either + an ELF sharable library or a linux style of shared library. */ + +/* Disclaimer: I have never seen any AT&T source code for SVr4, nor have + I ever taken any courses on internals. This program was developed using + information available through the book "UNIX SYSTEM V RELEASE 4, + Programmers guide: Ansi C and Programming Support Tools", which did + a more than adequate job of explaining everything required to get this + working. */ + +extern void __c6x_cache_sync(unsigned long start, unsigned long end) + attribute_hidden; + +static void +_dl_c6x_flush_relocs(struct elf32_dsbt_loadmap *map) +{ + unsigned long s, e; + s = map->segs[0].addr; + e = s + map->segs[0].p_memsz; + __c6x_cache_sync(s, e); + s = map->segs[1].addr; + e = s + map->segs[1].p_memsz; + __c6x_cache_sync(s, e); +} + + +attribute_hidden +char * +_dl_linux_resolver (struct elf_resolve *tpnt, int reloc_entry) +{ + ELF_RELOC *this_reloc; + char *strtab; + ElfW(Sym) *symtab; + int symtab_index; + char *rel_addr; + char *new_addr; + char **got_addr; + char *symname; + + rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL]; + + this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_entry); + symtab_index = ELF_R_SYM(this_reloc->r_info); + + symtab = (Elf32_Sym *) tpnt->dynamic_info[DT_SYMTAB]; + strtab = (char *) tpnt->dynamic_info[DT_STRTAB]; + symname = strtab + symtab[symtab_index].st_name; + + /* Address of GOT entry fix up */ + got_addr = (char **) DL_RELOC_ADDR(tpnt->loadaddr, this_reloc->r_offset); + + /* Get the address to be used to fill in the GOT entry. */ + new_addr = _dl_find_hash(symname, tpnt->symbol_scope, tpnt, + ELF_RTYPE_CLASS_PLT, NULL); + if (unlikely(!new_addr)) { + _dl_dprintf(2, "%s: can't resolve symbol '%s' in lib '%s'.\n", _dl_progname, symname, tpnt->libname); + _dl_exit(1); + } + + +#if defined (__SUPPORT_LD_DEBUG__) + if (_dl_debug_bindings) { + _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname); + if (_dl_debug_detail) + _dl_dprintf(_dl_debug_file, + "\n\tpatched %x ==> %x @ %x\n", + *got_addr, new_addr, got_addr); + } + if (!_dl_debug_nofixups) { + *got_addr = new_addr; + } +#else + *got_addr = new_addr; +#endif + + return new_addr; +} + +static int +_dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope, + unsigned long rel_addr, unsigned long rel_size, + int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope, + ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)) +{ + unsigned int i; + char *strtab; + Elf32_Sym *symtab; + ELF_RELOC *rpnt; + int symtab_index; + + /* Now parse the relocation information */ + rpnt = (ELF_RELOC *)rel_addr; + rel_size = rel_size / sizeof(ELF_RELOC); + + symtab = (Elf32_Sym *)tpnt->dynamic_info[DT_SYMTAB]; + strtab = (char *)tpnt->dynamic_info[DT_STRTAB]; + + for (i = 0; i < rel_size; i++, rpnt++) { + int res; + + symtab_index = ELF32_R_SYM(rpnt->r_info); + debug_sym(symtab,strtab,symtab_index); + debug_reloc(symtab,strtab,rpnt); + + res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab); + + if (res==0) continue; + + _dl_dprintf(2, "\n%s: ",_dl_progname); + + if (symtab_index) + _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name); + + if (res <0) { + int reloc_type = ELF32_R_TYPE(rpnt->r_info); +#if defined (__SUPPORT_LD_DEBUG__) + _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type)); +#else + _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type); +#endif + _dl_exit(-res); + } else if (res >0) { + _dl_dprintf(2, "can't resolve symbol\n"); + return res; + } + } + _dl_c6x_flush_relocs(tpnt->loadaddr.map); + return 0; +} + +static int +_dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, + ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab) +{ + int reloc_type; + int symtab_index; + char *symname; + unsigned long *reloc_addr; + unsigned long symbol_addr, sym_val; + long reloc_addend; + unsigned long old_val, new_val; + + reloc_addr = (unsigned long *)(intptr_t) + DL_RELOC_ADDR (tpnt->loadaddr, rpnt->r_offset); + + reloc_type = ELF32_R_TYPE(rpnt->r_info); + reloc_addend = rpnt->r_addend; + symtab_index = ELF32_R_SYM(rpnt->r_info); + symbol_addr = 0; + symname = strtab + symtab[symtab_index].st_name; + + if (ELF32_ST_BIND (symtab[symtab_index].st_info) == STB_LOCAL) { + symbol_addr = (unsigned long) + DL_RELOC_ADDR (tpnt->loadaddr, symtab[symtab_index].st_value); + } else { + symbol_addr = (unsigned long) _dl_find_hash(strtab + symtab[symtab_index].st_name, + scope, tpnt, elf_machine_type_class(reloc_type), + NULL); + /* + * We want to allow undefined references to weak symbols - this might + * have been intentional. We should not be linking local symbols + * here, so all bases should be covered. + */ + + if (!symbol_addr && ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK) { + _dl_dprintf (2, "%s: can't resolve symbol '%s'\n", + _dl_progname, strtab + symtab[symtab_index].st_name); + _dl_exit (1); + } + } + old_val = *reloc_addr; + sym_val = symbol_addr + reloc_addend; + + switch (reloc_type) { + case R_C6000_NONE: + break; + case R_C6000_ABS32: + case R_C6000_JUMP_SLOT: + new_val = sym_val; + *reloc_addr = sym_val; + break; + case R_C6000_ABS_L16: + new_val = (old_val & ~0x007fff80) | ((sym_val & 0xffff) << 7); + *reloc_addr = new_val; + break; + case R_C6000_ABS_H16: + new_val = (old_val & ~0x007fff80) | ((sym_val >> 9) & 0x007fff80); + *reloc_addr = new_val; + break; + case R_C6000_PCR_S21: + new_val = sym_val - (((unsigned long)reloc_addr) & ~31); + *reloc_addr = (old_val & ~0x0fffff80) | (((new_val >> 2) & 0x1fffff) << 7); + break; + case R_C6000_COPY: + if (symbol_addr) { +#if defined (__SUPPORT_LD_DEBUG__) + if (_dl_debug_move) + _dl_dprintf(_dl_debug_file, + "\n%s move %d bytes from %x to %x", + symname, symtab[symtab_index].st_size, + symbol_addr, reloc_addr); +#endif + + _dl_memcpy((char *)reloc_addr, + (char *)symbol_addr, + symtab[symtab_index].st_size); + } + break; + default: + return -1; /*call _dl_exit(1) */ + } +#if defined (__SUPPORT_LD_DEBUG__) + if (_dl_debug_reloc && _dl_debug_detail && reloc_type != R_C6000_NONE) { + _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n", old_val, new_val, reloc_addr); + } +#endif + return 0; +} + +static int +_dl_do_lazy_reloc (struct elf_resolve *tpnt, + struct dyn_elf *scope attribute_unused, + ELF_RELOC *rpnt, ElfW(Sym) *symtab attribute_unused, + char *strtab attribute_unused) +{ + int reloc_type; + unsigned long *reloc_addr; + unsigned long old_val; + + reloc_addr = (unsigned long *) DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_offset); + reloc_type = ELF_R_TYPE(rpnt->r_info); + + old_val = *reloc_addr; + + switch (reloc_type) { + case R_C6000_NONE: + break; + case R_C6000_JUMP_SLOT: + *reloc_addr = DL_RELOC_ADDR(tpnt->loadaddr, old_val); + break; + default: + return -1; + } + +#if defined (__SUPPORT_LD_DEBUG__) + if (_dl_debug_reloc && _dl_debug_detail) + _dl_dprintf(_dl_debug_file, "\n\tpatched: %x ==> %x @ %x\n", + old_val, *reloc_addr, reloc_addr); +#endif + + return 0; +} + +void +_dl_parse_lazy_relocation_information +(struct dyn_elf *rpnt, unsigned long rel_addr, unsigned long rel_size) +{ + _dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc); +} + +int +_dl_parse_relocation_information +(struct dyn_elf *rpnt, unsigned long rel_addr, unsigned long rel_size) +{ + return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc); +} + +/* We don't have copy relocs. */ +int +_dl_parse_copy_information +(struct dyn_elf *rpnt, + unsigned long rel_addr, + unsigned long rel_size) +{ + return 0; +} + diff --git a/ldso/ldso/c6x/resolve.S b/ldso/ldso/c6x/resolve.S new file mode 100644 index 000000000..ce3cbe793 --- /dev/null +++ b/ldso/ldso/c6x/resolve.S @@ -0,0 +1,68 @@ +;; +;; Copyright (C) 2010 Texas Instruments Incorporated +;; Mark Salter <msalter@redhat.com> +;; +;; Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. +;; + +;; The function below is tail-called by resolver stubs when a +;; lazily-bound function is called. It must preserve all +;; registers that could be used to pass arguments to the actual +;; function. + +;; _dl_linux_resolver() figures out where the jump symbol is +;; _really_ supposed to have jumped to and returns that to us. +;; Once we have that, we prepare to tail-call the actual +;; function, clean up after ourselves, restoring the original +;; arguments, then jump to the fixed up address. */ + +; resolver stub - called from PLT to resolve target address and update GOT +; +; B0 : reloc offset (bytes from DT_RELPLT) +; B1 : module pointer, loaded from GOT[1] +; DP : caller's DP +; A4,B4, etc: callee's arguments +; B3 : return address + + .text + .align 5 + .global _dl_linux_resolve +_dl_linux_resolve: + stw .d2t2 B14, *B15--[2] + stdw .d2t1 A15:A14, *B15-- + stdw .d2t2 B13:B12, *B15-- + stdw .d2t1 A13:A12, *B15-- + stdw .d2t2 B11:B10, *B15-- + stdw .d2t1 A11:A10, *B15-- + stdw .d2t2 B9:B8, *B15-- + stdw .d2t1 A9:A8, *B15-- + stdw .d2t2 B7:B6, *B15-- + stdw .d2t1 A7:A6, *B15-- + stdw .d2t2 B5:B4, *B15-- + stdw .d2t1 A5:A4, *B15-- + stdw .d2t2 B3:B2, *B15-- + stdw .d2t1 A3:A2, *B15-- + + ; call lookup routine + MV .S1X B1, A4 ; arg 1: module id +|| MV .S2 B0,B4 ; arg 2: reloc offset + CALLP .S2 _dl_linux_resolver, B3 ; returns &f in A4 + MV .S2X A4,B0 ; &f + + lddw .d2t1 *++B15, A3:A2 + lddw .d2t2 *++B15, B3:B2 + lddw .d2t1 *++B15, A5:A4 + lddw .d2t2 *++B15, B5:B4 + lddw .d2t1 *++B15, A7:A6 + lddw .d2t2 *++B15, B7:B6 + lddw .d2t1 *++B15, A9:A8 + lddw .d2t2 *++B15, B9:B8 + lddw .d2t1 *++B15, A11:A10 + lddw .d2t2 *++B15, B11:B10 + lddw .d2t1 *++B15, A13:A12 + lddw .d2t2 *++B15, B13:B12 + lddw .d2t1 *++B15, A15:A14 + ldw .d2t2 *++B15[2], B14 + + B .S2 B0 ; tail-call f + NOP 5 diff --git a/ldso/ldso/dl-elf.c b/ldso/ldso/dl-elf.c index 5d49e073b..a881b7df7 100644 --- a/ldso/ldso/dl-elf.c +++ b/ldso/ldso/dl-elf.c @@ -188,7 +188,7 @@ unsigned long _dl_error_number; unsigned long _dl_internal_error_number; struct elf_resolve *_dl_load_shared_library(int secure, struct dyn_elf **rpnt, - struct elf_resolve *tpnt, char *full_libname, int __attribute__((unused)) trace_loaded_objects) + struct elf_resolve *tpnt, char *full_libname, int attribute_unused trace_loaded_objects) { char *pnt; struct elf_resolve *tpnt1; @@ -826,6 +826,54 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, INIT_GOT(lpnt, tpnt); } +#ifdef __DSBT__ + /* Handle DSBT initialization */ + { + struct elf_resolve *t, *ref = NULL; + int idx = tpnt->loadaddr.map->dsbt_index; + unsigned *dsbt = tpnt->loadaddr.map->dsbt_table; + + if (idx == 0) { + /* This DSO has not been assigned an index */ + _dl_dprintf(2, "%s: '%s' is missing a dsbt index assignment!\n", + _dl_progname, libname); + _dl_exit(1); + } + + /* + * Setup dsbt slot for this module in dsbt of all modules. + */ + for (t = _dl_loaded_modules; t; t = t->next) { + /* find a dsbt table from another module */ + if (ref == NULL && t != tpnt) { + ref = t; + + /* make sure index is not already used */ + if (t->loadaddr.map->dsbt_table[idx]) { + struct elf_resolve *dup; + char *dup_name; + + for (dup = _dl_loaded_modules; dup; dup = dup->next) + if (dup != tpnt && dup->loadaddr.map->dsbt_index == idx) + break; + if (dup) + dup_name = dup->libname; + else if (idx == 1) + dup_name = "runtime linker"; + else + dup_name = "unknown library"; + _dl_dprintf(2, "%s: '%s' dsbt index %d already used by %s!\n", + _dl_progname, libname, idx, dup_name); + _dl_exit(1); + } + } + t->loadaddr.map->dsbt_table[idx] = (unsigned)dsbt; + } + if (ref) + _dl_memcpy(dsbt, ref->loadaddr.map->dsbt_table, + tpnt->loadaddr.map->dsbt_size * sizeof(unsigned *)); + } +#endif _dl_if_debug_dprint("\n\tfile='%s'; generating link map\n", libname); _dl_if_debug_dprint("\t\tdynamic: %x base: %x\n", dynamic_addr, DL_LOADADDR_BASE(lib_loadaddr)); _dl_if_debug_dprint("\t\t entry: %x phdr: %x phnum: %x\n\n", diff --git a/ldso/ldso/i386/elfinterp.c b/ldso/ldso/i386/elfinterp.c index 33c14402a..39c4e1d06 100644 --- a/ldso/ldso/i386/elfinterp.c +++ b/ldso/ldso/i386/elfinterp.c @@ -162,7 +162,7 @@ _dl_do_reloc(struct elf_resolve *tpnt, struct r_scope_elem *scope, int reloc_type; int symtab_index; char *symname; - struct elf_resolve *tls_tpnt = 0; + struct elf_resolve *tls_tpnt = NULL; unsigned long *reloc_addr; unsigned long symbol_addr; #if defined (__SUPPORT_LD_DEBUG__) diff --git a/ldso/ldso/ldso.c b/ldso/ldso/ldso.c index 2ff441194..775acdd15 100644 --- a/ldso/ldso/ldso.c +++ b/ldso/ldso/ldso.c @@ -119,15 +119,13 @@ extern void _start(void); #ifdef __UCLIBC_HAS_SSP__ # include <dl-osinfo.h> -uintptr_t stack_chk_guard; +static uintptr_t stack_chk_guard; # ifndef THREAD_SET_STACK_GUARD /* Only exported for architectures that don't store the stack guard canary * in local thread area. */ uintptr_t __stack_chk_guard attribute_relro; -# ifdef __UCLIBC_HAS_SSP_COMPAT__ -strong_alias(__stack_chk_guard,__guard) -# endif -# elif __UCLIBC_HAS_SSP_COMPAT__ +# endif +# ifdef __UCLIBC_HAS_SSP_COMPAT__ uintptr_t __guard attribute_relro; # endif #endif @@ -1165,12 +1163,12 @@ of this helper program; chances are you did not intend to run this program.\n\ stack_chk_guard = _dl_setup_stack_chk_guard (); # ifdef THREAD_SET_STACK_GUARD THREAD_SET_STACK_GUARD (stack_chk_guard); -# ifdef __UCLIBC_HAS_SSP_COMPAT__ - __guard = stack_chk_guard; -# endif # else __stack_chk_guard = stack_chk_guard; # endif +# ifdef __UCLIBC_HAS_SSP_COMPAT__ + __guard = stack_chk_guard; +# endif #endif #ifdef __LDSO_PRELINK_SUPPORT__ diff --git a/ldso/ldso/sparc/elfinterp.c b/ldso/ldso/sparc/elfinterp.c index 3c928073b..60a0161be 100644 --- a/ldso/ldso/sparc/elfinterp.c +++ b/ldso/ldso/sparc/elfinterp.c @@ -170,7 +170,7 @@ _dl_do_reloc(struct elf_resolve *tpnt, struct r_scope_elem *scope, int reloc_type; int symtab_index; char *symname; - struct elf_resolve *tls_tpnt = 0; + struct elf_resolve *tls_tpnt = NULL; struct symbol_ref sym_ref; ElfW(Addr) *reloc_addr; ElfW(Addr) symbol_addr; diff --git a/ldso/ldso/x86_64/elfinterp.c b/ldso/ldso/x86_64/elfinterp.c index a36b1154c..44e2c66d8 100644 --- a/ldso/ldso/x86_64/elfinterp.c +++ b/ldso/ldso/x86_64/elfinterp.c @@ -157,7 +157,7 @@ _dl_do_reloc(struct elf_resolve *tpnt, struct r_scope_elem *scope, int reloc_type; int symtab_index; char *symname; - struct elf_resolve *tls_tpnt = 0; + struct elf_resolve *tls_tpnt = NULL; struct symbol_ref sym_ref; ElfW(Addr) *reloc_addr; ElfW(Addr) symbol_addr; diff --git a/ldso/libdl/Makefile.in b/ldso/libdl/Makefile.in index 39db7a876..152185e37 100644 --- a/ldso/libdl/Makefile.in +++ b/ldso/libdl/Makefile.in @@ -19,6 +19,7 @@ endif CFLAGS-libdl.c := -DLDSO_ELFINTERP=\"$(TARGET_ARCH)/elfinterp.c\" +LDFLAGS-$(UCLIBC_FORMAT_DSBT_ELF)-libdl.so := -Wl,--dsbt-index=3 LDFLAGS-libdl.so := $(LDFLAGS) -Wl,-fini,dl_cleanup LIBS-libdl.so := $(LIBS) $(ldso) diff --git a/ldso/libdl/libdl.c b/ldso/libdl/libdl.c index 5007a7e65..83dfd0898 100644 --- a/ldso/libdl/libdl.c +++ b/ldso/libdl/libdl.c @@ -651,7 +651,6 @@ void *dlsym(void *vhandle, const char *name) ElfW(Addr) from; struct dyn_elf *rpnt; void *ret; - struct elf_resolve *tls_tpnt = NULL; struct symbol_ref sym_ref = { NULL, NULL }; /* Nastiness to support underscore prefixes. */ #ifdef __UCLIBC_UNDERSCORES__ @@ -973,6 +972,10 @@ static int do_dlclose(void *vhandle, int need_fini) free(tpnt); } } + for (rpnt1 = handle->next; rpnt1; rpnt1 = rpnt1_tmp) { + rpnt1_tmp = rpnt1->next; + free(rpnt1); + } free(handle->init_fini.init_fini); free(handle); |