📄 dl-elf.c
字号:
/* vi: set sw=4 ts=4: *//* * This file contains the helper routines to load an ELF shared * library into memory and add the symbol table info to the chain. * * Copyright (C) 2000-2004 by Erik Andersen <andersen@codepoet.org> * Copyright (c) 1994-2000 Eric Youngdale, Peter MacDonald, * David Engel, Hongjiu Lu and Mitch D'Souza * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the above contributors may not be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#include "ldso.h"#ifdef __LDSO_CACHE_SUPPORT__static caddr_t _dl_cache_addr = NULL;static size_t _dl_cache_size = 0;int _dl_map_cache(void){ int fd; struct stat st; header_t *header; libentry_t *libent; int i, strtabsize; if (_dl_cache_addr == (caddr_t) - 1) return -1; else if (_dl_cache_addr != NULL) return 0; if (_dl_stat(LDSO_CACHE, &st) || (fd = _dl_open(LDSO_CACHE, O_RDONLY, 0)) < 0) { _dl_cache_addr = (caddr_t) - 1; /* so we won't try again */ return -1; } _dl_cache_size = st.st_size; _dl_cache_addr = (caddr_t) _dl_mmap(0, _dl_cache_size, PROT_READ, MAP_SHARED, fd, 0); _dl_close(fd); if (_dl_mmap_check_error(_dl_cache_addr)) { _dl_dprintf(2, "%s: can't map cache '%s'\n", _dl_progname, LDSO_CACHE); return -1; } header = (header_t *) _dl_cache_addr; if (_dl_cache_size < sizeof(header_t) || _dl_memcmp(header->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN) || _dl_memcmp(header->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN) || _dl_cache_size < (sizeof(header_t) + header->nlibs * sizeof(libentry_t)) || _dl_cache_addr[_dl_cache_size - 1] != '\0') { _dl_dprintf(2, "%s: cache '%s' is corrupt\n", _dl_progname, LDSO_CACHE); goto fail; } strtabsize = _dl_cache_size - sizeof(header_t) - header->nlibs * sizeof(libentry_t); libent = (libentry_t *) & header[1]; for (i = 0; i < header->nlibs; i++) { if (libent[i].sooffset >= strtabsize || libent[i].liboffset >= strtabsize) { _dl_dprintf(2, "%s: cache '%s' is corrupt\n", _dl_progname, LDSO_CACHE); goto fail; } } return 0;fail: _dl_munmap(_dl_cache_addr, _dl_cache_size); _dl_cache_addr = (caddr_t) - 1; return -1;}int _dl_unmap_cache(void){ if (_dl_cache_addr == NULL || _dl_cache_addr == (caddr_t) - 1) return -1;#if 1 _dl_munmap(_dl_cache_addr, _dl_cache_size); _dl_cache_addr = NULL;#endif return 0;}#endifvoid _dl_protect_relro (struct elf_resolve *l){ ElfW(Addr) start = ((l->loadaddr + l->relro_addr) & ~(_dl_pagesize - 1)); ElfW(Addr) end = ((l->loadaddr + l->relro_addr + l->relro_size) & ~(_dl_pagesize - 1));#if defined (__SUPPORT_LD_DEBUG__) if (_dl_debug) _dl_dprintf(2, "RELRO protecting %s: start:%x, end:%x\n", l->libname, start, end);#endif if (start != end && _dl_mprotect ((void *) start, end - start, PROT_READ) < 0) { _dl_dprintf(2, "%s: cannot apply additional memory protection after relocation", l->libname); _dl_exit(0); }}/* This function's behavior must exactly match that * in uClibc/ldso/util/ldd.c */static struct elf_resolve *search_for_named_library(const char *name, int secure, const char *path_list, struct dyn_elf **rpnt){ int i, count = 1; char *path, *path_n; char mylibname[2050]; struct elf_resolve *tpnt1; if (path_list==NULL) return NULL; /* We need a writable copy of this string */ path = _dl_strdup(path_list); if (!path) { _dl_dprintf(2, "Out of memory!\n"); _dl_exit(0); } /* Unlike ldd.c, don't bother to eliminate double //s */ /* Replace colons with zeros in path_list and count them */ for(i=_dl_strlen(path); i > 0; i--) { if (path[i]==':') { path[i]=0; count++; } } path_n = path; for (i = 0; i < count; i++) { _dl_strcpy(mylibname, path_n); _dl_strcat(mylibname, "/"); _dl_strcat(mylibname, name); if ((tpnt1 = _dl_load_elf_shared_library(secure, rpnt, mylibname)) != NULL) { return tpnt1; } path_n += (_dl_strlen(path_n) + 1); } return NULL;}/* Check if the named library is already loaded... */struct elf_resolve *_dl_check_if_named_library_is_loaded(const char *full_libname, int trace_loaded_objects){ const char *pnt, *pnt1; struct elf_resolve *tpnt1; const char *libname, *libname2; static const char libc[] = "libc.so."; static const char aborted_wrong_lib[] = "%s: aborted attempt to load %s!\n"; pnt = libname = full_libname;#if defined (__SUPPORT_LD_DEBUG__) if(_dl_debug) _dl_dprintf(_dl_debug_file, "Checking if '%s' is already loaded\n", full_libname);#endif /* quick hack to ensure mylibname buffer doesn't overflow. don't allow full_libname or any directory to be longer than 1024. */ if (_dl_strlen(full_libname) > 1024) return NULL; /* Skip over any initial initial './' and '/' stuff to * get the short form libname with no path garbage */ pnt1 = _dl_strrchr(pnt, '/'); if (pnt1) { libname = pnt1 + 1; } /* Make sure they are not trying to load the wrong C library! * This sometimes happens esp with shared libraries when the * library path is somehow wrong! */#define isdigit(c) (c >= '0' && c <= '9') if ((_dl_strncmp(libname, libc, 8) == 0) && _dl_strlen(libname) >=8 && isdigit(libname[8])) { /* Abort attempts to load glibc, libc5, etc */ if ( libname[8]!='0') { if (!trace_loaded_objects) { _dl_dprintf(2, aborted_wrong_lib, libname, _dl_progname); _dl_exit(1); } return NULL; } } /* Critical step! Weed out duplicates early to avoid * function aliasing, which wastes memory, and causes * really bad things to happen with weaks and globals. */ for (tpnt1 = _dl_loaded_modules; tpnt1; tpnt1 = tpnt1->next) { /* Skip over any initial initial './' and '/' stuff to * get the short form libname with no path garbage */ libname2 = tpnt1->libname; pnt1 = _dl_strrchr(libname2, '/'); if (pnt1) { libname2 = pnt1 + 1; } if (_dl_strcmp(libname2, libname) == 0) { /* Well, that was certainly easy */ return tpnt1; } } return NULL;}/* Used to return error codes back to dlopen et. al. */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){ char *pnt, *pnt1; struct elf_resolve *tpnt1; char *libname; _dl_internal_error_number = 0; libname = full_libname; /* quick hack to ensure mylibname buffer doesn't overflow. don't allow full_libname or any directory to be longer than 1024. */ if (_dl_strlen(full_libname) > 1024) goto goof; /* Skip over any initial initial './' and '/' stuff to * get the short form libname with no path garbage */ pnt1 = _dl_strrchr(libname, '/'); if (pnt1) { libname = pnt1 + 1; }#if 0 /* Critical step! Weed out duplicates early to avoid * function aliasing, which wastes memory, and causes * really bad things to happen with weaks and globals. */ if ((tpnt1=_dl_check_if_named_library_is_loaded(libname, trace_loaded_objects))!=NULL) return tpnt1;#endif#if defined (__SUPPORT_LD_DEBUG__) if(_dl_debug) _dl_dprintf(_dl_debug_file, "\tfind library='%s'; searching\n", libname);#endif /* If the filename has any '/', try it straight and leave it at that. For IBCS2 compatibility under linux, we substitute the string /usr/i486-sysv4/lib for /usr/lib in library names. */ if (libname != full_libname) {#if defined (__SUPPORT_LD_DEBUG__) if(_dl_debug) _dl_dprintf(_dl_debug_file, "\ttrying file='%s'\n", full_libname);#endif tpnt1 = _dl_load_elf_shared_library(secure, rpnt, full_libname); if (tpnt1) { return tpnt1; } //goto goof; } /* * The ABI specifies that RPATH is searched before LD_*_PATH or * the default path of /usr/lib. Check in rpath directories. */ for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) { if (tpnt->libtype == elf_executable) { pnt = (char *) tpnt->dynamic_info[DT_RPATH]; if (pnt) { pnt += (unsigned long) tpnt->loadaddr + tpnt->dynamic_info[DT_STRTAB];#if defined (__SUPPORT_LD_DEBUG__) if(_dl_debug) _dl_dprintf(_dl_debug_file, "\tsearching RPATH='%s'\n", pnt);#endif if ((tpnt1 = search_for_named_library(libname, secure, pnt, rpnt)) != NULL) { return tpnt1; } } } } /* Check in LD_{ELF_}LIBRARY_PATH, if specified and allowed */ if (_dl_library_path) {#if defined (__SUPPORT_LD_DEBUG__) if(_dl_debug) _dl_dprintf(_dl_debug_file, "\tsearching LD_LIBRARY_PATH='%s'\n", _dl_library_path);#endif if ((tpnt1 = search_for_named_library(libname, secure, _dl_library_path, rpnt)) != NULL) { return tpnt1; } } /* * Where should the cache be searched? There is no such concept in the * ABI, so we have some flexibility here. For now, search it before * the hard coded paths that follow (i.e before /lib and /usr/lib). */#ifdef __LDSO_CACHE_SUPPORT__ if (_dl_cache_addr != NULL && _dl_cache_addr != (caddr_t) - 1) { int i; header_t *header = (header_t *) _dl_cache_addr; libentry_t *libent = (libentry_t *) & header[1]; char *strs = (char *) &libent[header->nlibs];#if defined (__SUPPORT_LD_DEBUG__) if(_dl_debug) _dl_dprintf(_dl_debug_file, "\tsearching cache='%s'\n", LDSO_CACHE);#endif for (i = 0; i < header->nlibs; i++) { if ((libent[i].flags == LIB_ELF || libent[i].flags == LIB_ELF_LIBC0 || libent[i].flags == LIB_ELF_LIBC5) && _dl_strcmp(libname, strs + libent[i].sooffset) == 0 && (tpnt1 = _dl_load_elf_shared_library(secure, rpnt, strs + libent[i].liboffset))) return tpnt1; } }#endif /* Look for libraries wherever the shared library loader * was installed */#if defined (__SUPPORT_LD_DEBUG__) if(_dl_debug) _dl_dprintf(_dl_debug_file, "\tsearching ldso dir='%s'\n", _dl_ldsopath);#endif if ((tpnt1 = search_for_named_library(libname, secure, _dl_ldsopath, rpnt)) != NULL) { return tpnt1; } /* Lastly, search the standard list of paths for the library. This list must exactly match the list in uClibc/ldso/util/ldd.c */#if defined (__SUPPORT_LD_DEBUG__) if(_dl_debug) _dl_dprintf(_dl_debug_file, "\tsearching full lib path list\n");#endif if ((tpnt1 = search_for_named_library(libname, secure, UCLIBC_RUNTIME_PREFIX "lib:" UCLIBC_RUNTIME_PREFIX "usr/lib"#ifndef __LDSO_CACHE_SUPPORT__ ":" UCLIBC_RUNTIME_PREFIX "usr/X11R6/lib"#endif , rpnt) ) != NULL) { return tpnt1; }goof: /* Well, we shot our wad on that one. All we can do now is punt */ if (_dl_internal_error_number) _dl_error_number = _dl_internal_error_number; else _dl_error_number = LD_ERROR_NOFILE;#if defined (__SUPPORT_LD_DEBUG__) if(_dl_debug) _dl_dprintf(2, "Bummer: could not find '%s'!\n", libname);#endif return NULL;}/* * Read one ELF library into memory, mmap it into the correct locations and * add the symbol info to the symbol chain. Perform any relocations that * are required. */struct elf_resolve *_dl_load_elf_shared_library(int secure, struct dyn_elf **rpnt, char *libname){ ElfW(Ehdr) *epnt; unsigned long dynamic_addr = 0; Elf32_Dyn *dpnt; struct elf_resolve *tpnt; ElfW(Phdr) *ppnt; char *status, *header; unsigned long dynamic_info[DYNAMIC_SIZE]; unsigned long *lpnt; unsigned long libaddr; unsigned long minvma = 0xffffffff, maxvma = 0; int i, flags, piclib, infile; ElfW(Addr) relro_addr = 0; size_t relro_size = 0; /* If this file is already loaded, skip this step */ tpnt = _dl_check_hashed_files(libname); if (tpnt) { if (*rpnt) { (*rpnt)->next = (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); _dl_memset((*rpnt)->next, 0, sizeof(struct dyn_elf)); (*rpnt)->next->prev = (*rpnt); *rpnt = (*rpnt)->next; (*rpnt)->dyn = tpnt; tpnt->symbol_scope = _dl_symbol_tables; } tpnt->usage_count++; tpnt->libtype = elf_lib;#if defined (__SUPPORT_LD_DEBUG__) if(_dl_debug) _dl_dprintf(2, "file='%s'; already loaded\n", libname);#endif return tpnt; } /* If we are in secure mode (i.e. a setu/gid binary using LD_PRELOAD), we don't load the library if it isn't setuid. */ if (secure) { struct stat st; if (_dl_stat(libname, &st) || !(st.st_mode & S_ISUID)) return NULL; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -