am 78129204
: Merge "Clean up the linker a bit, remove prelinking support."
* commit '7812920487070d392984f94c9f80006dad8c198a': Clean up the linker a bit, remove prelinking support.
This commit is contained in:
commit
f3ffb8fa6e
@ -59,6 +59,10 @@ enum {
|
|||||||
ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
|
ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
int __libc_android_log_write(int prio, const char* tag, const char* buffer);
|
int __libc_android_log_write(int prio, const char* tag, const char* buffer);
|
||||||
int __libc_android_log_print(int prio, const char *tag, const char *fmt, ...);
|
int __libc_android_log_print(int prio, const char *tag, const char *fmt, ...);
|
||||||
int __libc_android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap);
|
int __libc_android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap);
|
||||||
@ -66,4 +70,8 @@ int __libc_android_log_vprint(int prio, const char *tag, const char *fmt, va_lis
|
|||||||
void __libc_android_log_event_int(int32_t tag, int value);
|
void __libc_android_log_event_int(int32_t tag, int value);
|
||||||
void __libc_android_log_event_uid(int32_t tag);
|
void __libc_android_log_event_uid(int32_t tag);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* _ANDROID_BIONIC_LOGD_H */
|
#endif /* _ANDROID_BIONIC_LOGD_H */
|
||||||
|
@ -3,13 +3,13 @@ include $(CLEAR_VARS)
|
|||||||
|
|
||||||
LOCAL_SRC_FILES:= \
|
LOCAL_SRC_FILES:= \
|
||||||
arch/$(TARGET_ARCH)/begin.S \
|
arch/$(TARGET_ARCH)/begin.S \
|
||||||
linker.c \
|
debugger.c \
|
||||||
|
dlfcn.c \
|
||||||
|
linker.cpp \
|
||||||
linker_environ.c \
|
linker_environ.c \
|
||||||
linker_format.c \
|
linker_format.c \
|
||||||
linker_phdr.c \
|
linker_phdr.c \
|
||||||
rt.c \
|
rt.c
|
||||||
dlfcn.c \
|
|
||||||
debugger.c
|
|
||||||
|
|
||||||
LOCAL_LDFLAGS := -shared
|
LOCAL_LDFLAGS := -shared
|
||||||
|
|
||||||
@ -23,15 +23,14 @@ LOCAL_CFLAGS += -fno-stack-protector \
|
|||||||
#
|
#
|
||||||
LOCAL_CFLAGS += -DLINKER_DEBUG=0
|
LOCAL_CFLAGS += -DLINKER_DEBUG=0
|
||||||
|
|
||||||
# we need to access the Bionic private header <bionic_tls.h>
|
# We need to access Bionic private headers in the linker...
|
||||||
# in the linker; duplicate the HAVE_ARM_TLS_REGISTER definition
|
LOCAL_CFLAGS += -I$(LOCAL_PATH)/../libc/
|
||||||
# from the libc build
|
|
||||||
|
# ...one of which is <private/bionic_tls.h>, for which we
|
||||||
|
# need HAVE_ARM_TLS_REGISTER.
|
||||||
ifeq ($(TARGET_ARCH)-$(ARCH_ARM_HAVE_TLS_REGISTER),arm-true)
|
ifeq ($(TARGET_ARCH)-$(ARCH_ARM_HAVE_TLS_REGISTER),arm-true)
|
||||||
LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER
|
LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER
|
||||||
endif
|
endif
|
||||||
LOCAL_CFLAGS += \
|
|
||||||
-I$(LOCAL_PATH)/../libc/private \
|
|
||||||
-I$(LOCAL_PATH)/../libc/arch-$(TARGET_ARCH)/bionic
|
|
||||||
|
|
||||||
ifeq ($(TARGET_ARCH),arm)
|
ifeq ($(TARGET_ARCH),arm)
|
||||||
LOCAL_CFLAGS += -DANDROID_ARM_LINKER
|
LOCAL_CFLAGS += -DANDROID_ARM_LINKER
|
||||||
|
@ -8,44 +8,6 @@ This document provides several notes related to the design of the Android
|
|||||||
dynamic linker.
|
dynamic linker.
|
||||||
|
|
||||||
|
|
||||||
Prelinking:
|
|
||||||
-----------
|
|
||||||
|
|
||||||
System libraries in Android are internally prelinked, which means that
|
|
||||||
any internal relocations within them are stripped from the corresponding
|
|
||||||
shared object, in order to reduce size and speed up loading.
|
|
||||||
|
|
||||||
Such libraries can only be loaded at the very specific virtual memory address
|
|
||||||
they have been prelinked to (during the build process). The list of prelinked
|
|
||||||
system libraries and their corresponding virtual memory address is found in
|
|
||||||
the file:
|
|
||||||
|
|
||||||
build/core/prelink-linux-<arch>.map
|
|
||||||
|
|
||||||
It should be updated each time a new system library is added to the
|
|
||||||
system.
|
|
||||||
|
|
||||||
The prelink step happens at build time, and uses the 'soslim' and 'apriori'
|
|
||||||
tools:
|
|
||||||
|
|
||||||
- 'apriori' is the real prelink tool which removes relocations from the
|
|
||||||
shared object, however, it must be given a list of symbols to remove
|
|
||||||
from the file.
|
|
||||||
|
|
||||||
- 'soslim' is used to find symbols in an executable ELF file
|
|
||||||
and generate a list that can be passed to 'apriori'.
|
|
||||||
|
|
||||||
By default, these tools are only used to remove internal symbols from
|
|
||||||
libraries, though they have been designed to allow more aggressive
|
|
||||||
optimizations (e.g. 'global' prelinking and symbol stripping, which
|
|
||||||
prevent replacing individual system libraries though).
|
|
||||||
|
|
||||||
You can disable prelinking at build time by modifying your Android.mk with
|
|
||||||
a line like:
|
|
||||||
|
|
||||||
LOCAL_PRELINK_MODULE := false
|
|
||||||
|
|
||||||
|
|
||||||
Initialization and Termination functions:
|
Initialization and Termination functions:
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ static void logSignalSummary(int signum, const siginfo_t* info)
|
|||||||
* Catches fatal signals so we can ask debuggerd to ptrace us before
|
* Catches fatal signals so we can ask debuggerd to ptrace us before
|
||||||
* we crash.
|
* we crash.
|
||||||
*/
|
*/
|
||||||
void debugger_signal_handler(int n, siginfo_t* info, void* unused)
|
void debugger_signal_handler(int n, siginfo_t* info, void* unused __attribute__((unused)))
|
||||||
{
|
{
|
||||||
char msgbuf[128];
|
char msgbuf[128];
|
||||||
unsigned tid;
|
unsigned tid;
|
||||||
|
@ -26,25 +26,23 @@
|
|||||||
* SUCH DAMAGE.
|
* SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <linux/auxvec.h>
|
#include <linux/auxvec.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <sys/mman.h>
|
|
||||||
|
|
||||||
#include <sys/atomics.h>
|
#include <sys/atomics.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
/* special private C library header - see Android.mk */
|
// Private C library headers.
|
||||||
#include <bionic_tls.h>
|
#include <private/bionic_tls.h>
|
||||||
|
#include <private/logd.h>
|
||||||
|
|
||||||
#include "linker.h"
|
#include "linker.h"
|
||||||
#include "linker_debug.h"
|
#include "linker_debug.h"
|
||||||
@ -79,8 +77,8 @@
|
|||||||
* - linker hardcodes PAGE_SIZE and PAGE_MASK because the kernel
|
* - linker hardcodes PAGE_SIZE and PAGE_MASK because the kernel
|
||||||
* headers provide versions that are negative...
|
* headers provide versions that are negative...
|
||||||
* - allocate space for soinfo structs dynamically instead of
|
* - allocate space for soinfo structs dynamically instead of
|
||||||
* having a hard limit (64)
|
* having a hard limit (SO_MAX)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
static int soinfo_link_image(soinfo *si, unsigned wr_offset);
|
static int soinfo_link_image(soinfo *si, unsigned wr_offset);
|
||||||
@ -126,24 +124,30 @@ struct _link_stats linker_stats;
|
|||||||
unsigned bitmask[4096];
|
unsigned bitmask[4096];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define HOODLUM(name, ret, ...) \
|
// You shouldn't try to call memory-allocating functions in the dynamic linker.
|
||||||
ret name __VA_ARGS__ \
|
// Guard against the most obvious ones.
|
||||||
{ \
|
#define DISALLOW_ALLOCATION(return_type, name, ...) \
|
||||||
char errstr[] = "ERROR: " #name " called from the dynamic linker!\n"; \
|
return_type name __VA_ARGS__ \
|
||||||
write(2, errstr, sizeof(errstr)); \
|
{ \
|
||||||
abort(); \
|
const char* msg = "ERROR: " #name " called from the dynamic linker!\n"; \
|
||||||
|
__libc_android_log_write(ANDROID_LOG_FATAL, "linker", msg); \
|
||||||
|
write(2, msg, sizeof(msg)); \
|
||||||
|
abort(); \
|
||||||
}
|
}
|
||||||
HOODLUM(malloc, void *, (size_t size));
|
#define UNUSED __attribute__((unused))
|
||||||
HOODLUM(free, void, (void *ptr));
|
DISALLOW_ALLOCATION(void*, malloc, (size_t u UNUSED));
|
||||||
HOODLUM(realloc, void *, (void *ptr, size_t size));
|
DISALLOW_ALLOCATION(void, free, (void* u UNUSED));
|
||||||
HOODLUM(calloc, void *, (size_t cnt, size_t size));
|
DISALLOW_ALLOCATION(void*, realloc, (void* u1 UNUSED, size_t u2 UNUSED));
|
||||||
|
DISALLOW_ALLOCATION(void*, calloc, (size_t u1 UNUSED, size_t u2 UNUSED));
|
||||||
|
|
||||||
static char tmp_err_buf[768];
|
static char tmp_err_buf[768];
|
||||||
static char __linker_dl_err_buf[768];
|
static char __linker_dl_err_buf[768];
|
||||||
|
#define BASENAME(s) (strrchr(s, '/') != NULL ? strrchr(s, '/') + 1 : s)
|
||||||
#define DL_ERR(fmt, x...) \
|
#define DL_ERR(fmt, x...) \
|
||||||
do { \
|
do { \
|
||||||
format_buffer(__linker_dl_err_buf, sizeof(__linker_dl_err_buf), \
|
format_buffer(__linker_dl_err_buf, sizeof(__linker_dl_err_buf), \
|
||||||
"%s[%d]: " fmt, __func__, __LINE__, ##x); \
|
"(%s:%d, pid %d) %s: " fmt, \
|
||||||
|
BASENAME(__FILE__), __LINE__, pid, __func__, ##x); \
|
||||||
ERROR(fmt "\n", ##x); \
|
ERROR(fmt "\n", ##x); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
@ -156,7 +160,7 @@ const char *linker_get_error(void)
|
|||||||
* This function is an empty stub where GDB locates a breakpoint to get notified
|
* This function is an empty stub where GDB locates a breakpoint to get notified
|
||||||
* about linker activity.
|
* about linker activity.
|
||||||
*/
|
*/
|
||||||
extern void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity(void);
|
extern "C" void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity(void);
|
||||||
|
|
||||||
static struct r_debug _r_debug = {1, NULL, &rtld_db_dlactivity,
|
static struct r_debug _r_debug = {1, NULL, &rtld_db_dlactivity,
|
||||||
RT_CONSISTENT, 0};
|
RT_CONSISTENT, 0};
|
||||||
@ -243,7 +247,7 @@ void notify_gdb_of_unload(soinfo * info)
|
|||||||
pthread_mutex_unlock(&_r_debug_lock);
|
pthread_mutex_unlock(&_r_debug_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void notify_gdb_of_libraries()
|
extern "C" void notify_gdb_of_libraries()
|
||||||
{
|
{
|
||||||
_r_debug.r_state = RT_ADD;
|
_r_debug.r_state = RT_ADD;
|
||||||
rtld_db_dlactivity();
|
rtld_db_dlactivity();
|
||||||
@ -253,10 +257,8 @@ void notify_gdb_of_libraries()
|
|||||||
|
|
||||||
static soinfo *soinfo_alloc(const char *name)
|
static soinfo *soinfo_alloc(const char *name)
|
||||||
{
|
{
|
||||||
soinfo *si;
|
if (strlen(name) >= SOINFO_NAME_LEN) {
|
||||||
|
DL_ERR("library name \"%s\" too long", name);
|
||||||
if(strlen(name) >= SOINFO_NAME_LEN) {
|
|
||||||
DL_ERR("%5d library name %s too long", pid, name);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,15 +266,15 @@ static soinfo *soinfo_alloc(const char *name)
|
|||||||
done only by dlclose(), which is not likely to be used.
|
done only by dlclose(), which is not likely to be used.
|
||||||
*/
|
*/
|
||||||
if (!freelist) {
|
if (!freelist) {
|
||||||
if(socount == SO_MAX) {
|
if (socount == SO_MAX) {
|
||||||
DL_ERR("%5d too many libraries when loading %s", pid, name);
|
DL_ERR("too many libraries when loading \"%s\"", name);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
freelist = sopool + socount++;
|
freelist = sopool + socount++;
|
||||||
freelist->next = NULL;
|
freelist->next = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
si = freelist;
|
soinfo* si = freelist;
|
||||||
freelist = freelist->next;
|
freelist = freelist->next;
|
||||||
|
|
||||||
/* Make sure we get a clean block of soinfo */
|
/* Make sure we get a clean block of soinfo */
|
||||||
@ -287,8 +289,12 @@ static soinfo *soinfo_alloc(const char *name)
|
|||||||
return si;
|
return si;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void soinfo_free(soinfo *si)
|
static void soinfo_free(soinfo* si)
|
||||||
{
|
{
|
||||||
|
if (si == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
soinfo *prev = NULL, *trav;
|
soinfo *prev = NULL, *trav;
|
||||||
|
|
||||||
TRACE("%5d name %s: freeing soinfo @ %p\n", pid, si->name, si);
|
TRACE("%5d name %s: freeing soinfo @ %p\n", pid, si->name, si);
|
||||||
@ -300,7 +306,7 @@ static void soinfo_free(soinfo *si)
|
|||||||
}
|
}
|
||||||
if (trav == NULL) {
|
if (trav == NULL) {
|
||||||
/* si was not ni solist */
|
/* si was not ni solist */
|
||||||
DL_ERR("%5d name %s is not in solist!", pid, si->name);
|
DL_ERR("name \"%s\" is not in solist!", si->name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,17 +321,16 @@ static void soinfo_free(soinfo *si)
|
|||||||
|
|
||||||
const char *addr_to_name(unsigned addr)
|
const char *addr_to_name(unsigned addr)
|
||||||
{
|
{
|
||||||
soinfo *si;
|
for (soinfo* si = solist; si != 0; si = si->next) {
|
||||||
|
if ((addr >= si->base) && (addr < (si->base + si->size))) {
|
||||||
for(si = solist; si != 0; si = si->next){
|
|
||||||
if((addr >= si->base) && (addr < (si->base + si->size))) {
|
|
||||||
return si->name;
|
return si->name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ANDROID_ARM_LINKER
|
||||||
|
|
||||||
/* For a given PC, find the .so that it belongs to.
|
/* For a given PC, find the .so that it belongs to.
|
||||||
* Returns the base address of the .ARM.exidx section
|
* Returns the base address of the .ARM.exidx section
|
||||||
* for that .so, and the number of 8-byte entries
|
* for that .so, and the number of 8-byte entries
|
||||||
@ -335,7 +340,6 @@ const char *addr_to_name(unsigned addr)
|
|||||||
*
|
*
|
||||||
* This function is exposed via dlfcn.c and libdl.so.
|
* This function is exposed via dlfcn.c and libdl.so.
|
||||||
*/
|
*/
|
||||||
#ifdef ANDROID_ARM_LINKER
|
|
||||||
_Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount)
|
_Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount)
|
||||||
{
|
{
|
||||||
soinfo *si;
|
soinfo *si;
|
||||||
@ -350,7 +354,9 @@ _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount)
|
|||||||
*pcount = 0;
|
*pcount = 0;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(ANDROID_X86_LINKER) || defined(ANDROID_MIPS_LINKER)
|
#elif defined(ANDROID_X86_LINKER) || defined(ANDROID_MIPS_LINKER)
|
||||||
|
|
||||||
/* Here, we only have to provide a callback to iterate across all the
|
/* Here, we only have to provide a callback to iterate across all the
|
||||||
* loaded libraries. gcc_eh does the rest. */
|
* loaded libraries. gcc_eh does the rest. */
|
||||||
int
|
int
|
||||||
@ -372,6 +378,7 @@ dl_iterate_phdr(int (*cb)(struct dl_phdr_info *info, size_t size, void *data),
|
|||||||
}
|
}
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static Elf32_Sym *soinfo_elf_lookup(soinfo *si, unsigned hash, const char *name)
|
static Elf32_Sym *soinfo_elf_lookup(soinfo *si, unsigned hash, const char *name)
|
||||||
@ -454,8 +461,7 @@ soinfo_do_lookup(soinfo *si, const char *name, Elf32_Addr *offset)
|
|||||||
if(d[0] == DT_NEEDED){
|
if(d[0] == DT_NEEDED){
|
||||||
lsi = (soinfo *)d[1];
|
lsi = (soinfo *)d[1];
|
||||||
if (!validate_soinfo(lsi)) {
|
if (!validate_soinfo(lsi)) {
|
||||||
DL_ERR("%5d bad DT_NEEDED pointer in %s",
|
DL_ERR("bad DT_NEEDED pointer in \"%s\"", lsi->name);
|
||||||
pid, lsi->name);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,35 +644,33 @@ static int open_library(const char *name)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
// Returns 'true' if the library is prelinked or on failure so we error out
|
||||||
long mmap_addr;
|
// either way. We no longer support prelinking.
|
||||||
char tag[4]; /* 'P', 'R', 'E', ' ' */
|
static bool is_prelinked(int fd, const char* name)
|
||||||
} prelink_info_t;
|
|
||||||
|
|
||||||
/* Returns the requested base address if the library is prelinked,
|
|
||||||
* and 0 otherwise. */
|
|
||||||
static unsigned long
|
|
||||||
is_prelinked(int fd, const char *name)
|
|
||||||
{
|
{
|
||||||
off_t sz = lseek(fd, -sizeof(prelink_info_t), SEEK_END);
|
struct prelink_info_t {
|
||||||
|
long mmap_addr;
|
||||||
|
char tag[4]; // "PRE ".
|
||||||
|
};
|
||||||
|
|
||||||
|
off_t sz = lseek(fd, -sizeof(struct prelink_info_t), SEEK_END);
|
||||||
if (sz < 0) {
|
if (sz < 0) {
|
||||||
DL_ERR("lseek() failed!");
|
DL_ERR("lseek failed: %s", strerror(errno));
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
prelink_info_t info;
|
struct prelink_info_t info;
|
||||||
int rc = TEMP_FAILURE_RETRY(read(fd, &info, sizeof(info)));
|
int rc = TEMP_FAILURE_RETRY(read(fd, &info, sizeof(info)));
|
||||||
if (rc != sizeof(info)) {
|
if (rc != sizeof(info)) {
|
||||||
WARN("Could not read prelink_info_t structure for `%s`\n", name);
|
DL_ERR("could not read prelink_info_t structure for \"%s\":", name, strerror(errno));
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp(info.tag, "PRE ", 4)) {
|
if (memcmp(info.tag, "PRE ", 4) == 0) {
|
||||||
WARN("`%s` is not a prelinked library\n", name);
|
DL_ERR("prelinked libraries no longer supported: %s", name);
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
return (unsigned long)info.mmap_addr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* verify_elf_header
|
/* verify_elf_header
|
||||||
@ -697,101 +701,106 @@ verify_elf_header(const Elf32_Ehdr* hdr)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct scoped_fd {
|
||||||
|
~scoped_fd() {
|
||||||
|
if (fd != -1) {
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
static soinfo *
|
struct soinfo_ptr {
|
||||||
load_library(const char *name)
|
soinfo_ptr(const char* name) {
|
||||||
|
const char* bname = strrchr(name, '/');
|
||||||
|
ptr = soinfo_alloc(bname ? bname + 1 : name);
|
||||||
|
}
|
||||||
|
~soinfo_ptr() {
|
||||||
|
soinfo_free(ptr);
|
||||||
|
}
|
||||||
|
soinfo* release() {
|
||||||
|
soinfo* result = ptr;
|
||||||
|
ptr = NULL;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
soinfo* ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: rewrite linker_phdr.h to use a class, then lose this.
|
||||||
|
struct phdr_ptr {
|
||||||
|
phdr_ptr() : phdr_mmap(NULL) {}
|
||||||
|
~phdr_ptr() {
|
||||||
|
if (phdr_mmap != NULL) {
|
||||||
|
phdr_table_unload(phdr_mmap, phdr_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void* phdr_mmap;
|
||||||
|
Elf32_Addr phdr_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static soinfo* load_library(const char* name)
|
||||||
{
|
{
|
||||||
int fd = open_library(name);
|
// Open the file.
|
||||||
int ret, cnt;
|
scoped_fd fd;
|
||||||
unsigned ext_sz;
|
fd.fd = open_library(name);
|
||||||
unsigned req_base;
|
if (fd.fd == -1) {
|
||||||
const char *bname;
|
DL_ERR("library \"%s\" not found", name);
|
||||||
struct stat sb;
|
|
||||||
soinfo *si = NULL;
|
|
||||||
Elf32_Ehdr header[1];
|
|
||||||
int phdr_count;
|
|
||||||
void* phdr_mmap = NULL;
|
|
||||||
Elf32_Addr phdr_size;
|
|
||||||
const Elf32_Phdr* phdr_table;
|
|
||||||
|
|
||||||
void* load_start = NULL;
|
|
||||||
Elf32_Addr load_size = 0;
|
|
||||||
Elf32_Addr load_bias = 0;
|
|
||||||
|
|
||||||
if (fd == -1) {
|
|
||||||
DL_ERR("Library '%s' not found", name);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read the ELF header first */
|
// Read the ELF header.
|
||||||
ret = TEMP_FAILURE_RETRY(read(fd, (void*)header, sizeof(header)));
|
Elf32_Ehdr header[1];
|
||||||
|
int ret = TEMP_FAILURE_RETRY(read(fd.fd, (void*)header, sizeof(header)));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
DL_ERR("%5d can't read file %s: %s", pid, name, strerror(errno));
|
DL_ERR("can't read file \"%s\": %s", name, strerror(errno));
|
||||||
goto fail;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (ret != (int)sizeof(header)) {
|
if (ret != (int)sizeof(header)) {
|
||||||
DL_ERR("%5d too small to be an ELF executable: %s", pid, name);
|
DL_ERR("too small to be an ELF executable: %s", name);
|
||||||
goto fail;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (verify_elf_header(header) < 0) {
|
if (verify_elf_header(header) < 0) {
|
||||||
DL_ERR("%5d not a valid ELF executable: %s", pid, name);
|
DL_ERR("not a valid ELF executable: %s", name);
|
||||||
goto fail;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Then read the program header table */
|
// Read the program header table.
|
||||||
ret = phdr_table_load(fd, header->e_phoff, header->e_phnum,
|
const Elf32_Phdr* phdr_table;
|
||||||
&phdr_mmap, &phdr_size, &phdr_table);
|
phdr_ptr phdr_holder;
|
||||||
|
ret = phdr_table_load(fd.fd, header->e_phoff, header->e_phnum,
|
||||||
|
&phdr_holder.phdr_mmap, &phdr_holder.phdr_size, &phdr_table);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
DL_ERR("%5d can't load program header table: %s: %s", pid,
|
DL_ERR("can't load program header table: %s: %s", name, strerror(errno));
|
||||||
name, strerror(errno));
|
return NULL;
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
phdr_count = header->e_phnum;
|
size_t phdr_count = header->e_phnum;
|
||||||
|
|
||||||
/* Get the load extents and the prelinked load address, if any */
|
// Get the load extents.
|
||||||
ext_sz = phdr_table_get_load_size(phdr_table, phdr_count);
|
Elf32_Addr ext_sz = phdr_table_get_load_size(phdr_table, phdr_count);
|
||||||
|
TRACE("[ %5d - '%s' wants sz=0x%08x ]\n", pid, name, ext_sz);
|
||||||
if (ext_sz == 0) {
|
if (ext_sz == 0) {
|
||||||
DL_ERR("%5d no loadable segments in file: %s", pid, name);
|
DL_ERR("no loadable segments in file: %s", name);
|
||||||
goto fail;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
req_base = (unsigned) is_prelinked(fd, name);
|
// We no longer support pre-linked libraries.
|
||||||
if (req_base == (unsigned)-1) {
|
if (is_prelinked(fd.fd, name)) {
|
||||||
DL_ERR("%5d can't read end of library: %s: %s", pid, name,
|
return NULL;
|
||||||
strerror(errno));
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if (req_base != 0) {
|
|
||||||
TRACE("[ %5d - Prelinked library '%s' requesting base @ 0x%08x ]\n",
|
|
||||||
pid, name, req_base);
|
|
||||||
} else {
|
|
||||||
TRACE("[ %5d - Non-prelinked library '%s' found. ]\n", pid, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("[ %5d - '%s' (%s) wants base=0x%08x sz=0x%08x ]\n", pid, name,
|
// Reserve address space for all loadable segments.
|
||||||
(req_base ? "prelinked" : "not pre-linked"), req_base, ext_sz);
|
void* load_start = NULL;
|
||||||
|
Elf32_Addr load_size = 0;
|
||||||
/* Now configure the soinfo struct where we'll store all of our data
|
Elf32_Addr load_bias = 0;
|
||||||
* for the ELF object. If the loading fails, we waste the entry, but
|
|
||||||
* same thing would happen if we failed during linking. Configuring the
|
|
||||||
* soinfo struct here is a lot more convenient.
|
|
||||||
*/
|
|
||||||
bname = strrchr(name, '/');
|
|
||||||
si = soinfo_alloc(bname ? bname + 1 : name);
|
|
||||||
if (si == NULL)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
/* Reserve address space for all loadable segments */
|
|
||||||
ret = phdr_table_reserve_memory(phdr_table,
|
ret = phdr_table_reserve_memory(phdr_table,
|
||||||
phdr_count,
|
phdr_count,
|
||||||
req_base,
|
|
||||||
&load_start,
|
&load_start,
|
||||||
&load_size,
|
&load_size,
|
||||||
&load_bias);
|
&load_bias);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
DL_ERR("%5d Can't reserve %d bytes from 0x%08x in address space for %s: %s",
|
DL_ERR("can't reserve %d bytes in address space for \"%s\": %s",
|
||||||
pid, ext_sz, req_base, name, strerror(errno));
|
ext_sz, name, strerror(errno));
|
||||||
goto fail;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("[ %5d allocated memory for %s @ %p (0x%08x) ]\n",
|
TRACE("[ %5d allocated memory for %s @ %p (0x%08x) ]\n",
|
||||||
@ -800,14 +809,12 @@ load_library(const char *name)
|
|||||||
/* Map all the segments in our address space with default protections */
|
/* Map all the segments in our address space with default protections */
|
||||||
ret = phdr_table_load_segments(phdr_table,
|
ret = phdr_table_load_segments(phdr_table,
|
||||||
phdr_count,
|
phdr_count,
|
||||||
load_start,
|
|
||||||
load_size,
|
|
||||||
load_bias,
|
load_bias,
|
||||||
fd);
|
fd.fd);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
DL_ERR("%5d Can't map loadable segments for %s: %s",
|
DL_ERR("can't map loadable segments for \"%s\": %s",
|
||||||
pid, name, strerror(errno));
|
name, strerror(errno));
|
||||||
goto fail;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unprotect the segments, i.e. make them writable, to allow
|
/* Unprotect the segments, i.e. make them writable, to allow
|
||||||
@ -819,36 +826,30 @@ load_library(const char *name)
|
|||||||
phdr_count,
|
phdr_count,
|
||||||
load_bias);
|
load_bias);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
DL_ERR("%5d Can't unprotect loadable segments for %s: %s",
|
DL_ERR("can't unprotect loadable segments for \"%s\": %s",
|
||||||
pid, name, strerror(errno));
|
name, strerror(errno));
|
||||||
goto fail;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
si->base = (Elf32_Addr) load_start;
|
soinfo_ptr si(name);
|
||||||
si->size = load_size;
|
if (si.ptr == NULL) {
|
||||||
si->load_bias = load_bias;
|
return NULL;
|
||||||
si->flags = 0;
|
|
||||||
si->entry = 0;
|
|
||||||
si->dynamic = (unsigned *)-1;
|
|
||||||
si->phnum = phdr_count;
|
|
||||||
si->phdr = phdr_table_get_loaded_phdr(phdr_table, phdr_count, load_bias);
|
|
||||||
if (si->phdr == NULL) {
|
|
||||||
DL_ERR("%5d Can't find loaded PHDR for %s",
|
|
||||||
pid, name);
|
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
phdr_table_unload(phdr_mmap, phdr_size);
|
si.ptr->base = (Elf32_Addr) load_start;
|
||||||
close(fd);
|
si.ptr->size = load_size;
|
||||||
return si;
|
si.ptr->load_bias = load_bias;
|
||||||
|
si.ptr->flags = 0;
|
||||||
fail:
|
si.ptr->entry = 0;
|
||||||
if (si) soinfo_free(si);
|
si.ptr->dynamic = (unsigned *)-1;
|
||||||
if (phdr_mmap != NULL) {
|
si.ptr->phnum = phdr_count;
|
||||||
phdr_table_unload(phdr_mmap, phdr_size);
|
si.ptr->phdr = phdr_table_get_loaded_phdr(phdr_table, phdr_count, load_bias);
|
||||||
|
if (si.ptr->phdr == NULL) {
|
||||||
|
DL_ERR("can't find loaded PHDR for \"%s\"", name);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
close(fd);
|
|
||||||
return NULL;
|
return si.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
static soinfo *
|
static soinfo *
|
||||||
@ -891,11 +892,11 @@ soinfo *find_library(const char *name)
|
|||||||
for(si = solist; si != 0; si = si->next){
|
for(si = solist; si != 0; si = si->next){
|
||||||
if(!strcmp(bname, si->name)) {
|
if(!strcmp(bname, si->name)) {
|
||||||
if(si->flags & FLAG_ERROR) {
|
if(si->flags & FLAG_ERROR) {
|
||||||
DL_ERR("%5d '%s' failed to load previously", pid, bname);
|
DL_ERR("\"%s\" failed to load previously", bname);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if(si->flags & FLAG_LINKED) return si;
|
if(si->flags & FLAG_LINKED) return si;
|
||||||
DL_ERR("OOPS: %5d recursive link to '%s'", pid, si->name);
|
DL_ERR("OOPS: recursive link to \"%s\"", si->name);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -908,8 +909,7 @@ soinfo *find_library(const char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* TODO:
|
/* TODO:
|
||||||
* notify gdb of unload
|
* find a way to decrement libbase
|
||||||
* for non-prelinked libraries, find a way to decrement libbase
|
|
||||||
*/
|
*/
|
||||||
static void call_destructors(soinfo *si);
|
static void call_destructors(soinfo *si);
|
||||||
unsigned soinfo_unload(soinfo *si)
|
unsigned soinfo_unload(soinfo *si)
|
||||||
@ -925,9 +925,9 @@ unsigned soinfo_unload(soinfo *si)
|
|||||||
*/
|
*/
|
||||||
if (phdr_table_unprotect_gnu_relro(si->phdr, si->phnum,
|
if (phdr_table_unprotect_gnu_relro(si->phdr, si->phnum,
|
||||||
si->load_bias) < 0) {
|
si->load_bias) < 0) {
|
||||||
DL_ERR("%5d %s: could not undo GNU_RELRO protections. "
|
DL_ERR("%s: could not undo GNU_RELRO protections. "
|
||||||
"Expect a crash soon. errno=%d (%s)",
|
"Expect a crash soon. errno=%d (%s)",
|
||||||
pid, si->name, errno, strerror(errno));
|
si->name, errno, strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
for(d = si->dynamic; *d; d += 2) {
|
for(d = si->dynamic; *d; d += 2) {
|
||||||
@ -945,8 +945,8 @@ unsigned soinfo_unload(soinfo *si)
|
|||||||
soinfo_unload(lsi);
|
soinfo_unload(lsi);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
DL_ERR("%5d %s: could not unload dependent library",
|
DL_ERR("\"%s\": could not unload dependent library",
|
||||||
pid, si->name);
|
si->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -972,12 +972,10 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count)
|
|||||||
Elf32_Sym *symtab = si->symtab;
|
Elf32_Sym *symtab = si->symtab;
|
||||||
const char *strtab = si->strtab;
|
const char *strtab = si->strtab;
|
||||||
Elf32_Sym *s;
|
Elf32_Sym *s;
|
||||||
unsigned base;
|
|
||||||
Elf32_Addr offset;
|
Elf32_Addr offset;
|
||||||
Elf32_Rel *start = rel;
|
Elf32_Rel *start = rel;
|
||||||
unsigned idx;
|
|
||||||
|
|
||||||
for (idx = 0; idx < count; ++idx, ++rel) {
|
for (size_t idx = 0; idx < count; ++idx, ++rel) {
|
||||||
unsigned type = ELF32_R_TYPE(rel->r_info);
|
unsigned type = ELF32_R_TYPE(rel->r_info);
|
||||||
unsigned sym = ELF32_R_SYM(rel->r_info);
|
unsigned sym = ELF32_R_SYM(rel->r_info);
|
||||||
unsigned reloc = (unsigned)(rel->r_offset + si->load_bias);
|
unsigned reloc = (unsigned)(rel->r_offset + si->load_bias);
|
||||||
@ -997,7 +995,7 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count)
|
|||||||
reference.. */
|
reference.. */
|
||||||
s = &symtab[sym];
|
s = &symtab[sym];
|
||||||
if (ELF32_ST_BIND(s->st_info) != STB_WEAK) {
|
if (ELF32_ST_BIND(s->st_info) != STB_WEAK) {
|
||||||
DL_ERR("%5d cannot locate '%s'...\n", pid, sym_name);
|
DL_ERR("cannot locate \"%s\"...", sym_name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1043,8 +1041,8 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count)
|
|||||||
not found in run-time. */
|
not found in run-time. */
|
||||||
#endif /* ANDROID_ARM_LINKER */
|
#endif /* ANDROID_ARM_LINKER */
|
||||||
default:
|
default:
|
||||||
DL_ERR("%5d unknown weak reloc type %d @ %p (%d)\n",
|
DL_ERR("unknown weak reloc type %d @ %p (%d)",
|
||||||
pid, type, rel, (int) (rel - start));
|
type, rel, (int) (rel - start));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1052,8 +1050,8 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count)
|
|||||||
#if 0
|
#if 0
|
||||||
if((base == 0) && (si->base != 0)){
|
if((base == 0) && (si->base != 0)){
|
||||||
/* linking from libraries to main image is bad */
|
/* linking from libraries to main image is bad */
|
||||||
DL_ERR("%5d cannot locate '%s'...",
|
DL_ERR("cannot locate \"%s\"...",
|
||||||
pid, strtab + symtab[sym].st_name);
|
strtab + symtab[sym].st_name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -1140,8 +1138,8 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count)
|
|||||||
#endif /* ANDROID_*_LINKER */
|
#endif /* ANDROID_*_LINKER */
|
||||||
COUNT_RELOC(RELOC_RELATIVE);
|
COUNT_RELOC(RELOC_RELATIVE);
|
||||||
MARK(rel->r_offset);
|
MARK(rel->r_offset);
|
||||||
if(sym){
|
if (sym) {
|
||||||
DL_ERR("%5d odd RELATIVE form...", pid);
|
DL_ERR("odd RELATIVE form...", pid);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
TRACE_TYPE(RELO, "%5d RELO RELATIVE %08x <- +%08x\n", pid,
|
TRACE_TYPE(RELO, "%5d RELO RELATIVE %08x <- +%08x\n", pid,
|
||||||
@ -1180,8 +1178,8 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count)
|
|||||||
#endif /* ANDROID_ARM_LINKER */
|
#endif /* ANDROID_ARM_LINKER */
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DL_ERR("%5d unknown reloc type %d @ %p (%d)",
|
DL_ERR("unknown reloc type %d @ %p (%d)",
|
||||||
pid, type, rel, (int) (rel - start));
|
type, rel, (int) (rel - start));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1240,7 +1238,7 @@ int mips_relocate_got(struct soinfo *si)
|
|||||||
reference.. */
|
reference.. */
|
||||||
s = &symtab[g];
|
s = &symtab[g];
|
||||||
if (ELF32_ST_BIND(s->st_info) != STB_WEAK) {
|
if (ELF32_ST_BIND(s->st_info) != STB_WEAK) {
|
||||||
DL_ERR("%5d cannot locate '%s'...\n", pid, sym_name);
|
DL_ERR("cannot locate \"%s\"...", sym_name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
*got = 0;
|
*got = 0;
|
||||||
@ -1320,9 +1318,8 @@ void soinfo_call_constructors(soinfo *si)
|
|||||||
TRACE("[ %5d Done calling preinit_array for '%s' ]\n", pid, si->name);
|
TRACE("[ %5d Done calling preinit_array for '%s' ]\n", pid, si->name);
|
||||||
} else {
|
} else {
|
||||||
if (si->preinit_array) {
|
if (si->preinit_array) {
|
||||||
DL_ERR("%5d Shared library '%s' has a preinit_array table @ 0x%08x."
|
DL_ERR("shared library \"%s\" has a preinit_array table @ 0x%08x. "
|
||||||
" This is INVALID.", pid, si->name,
|
"This is INVALID.", si->name, (unsigned) si->preinit_array);
|
||||||
(unsigned)si->preinit_array);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1332,8 +1329,7 @@ void soinfo_call_constructors(soinfo *si)
|
|||||||
if(d[0] == DT_NEEDED){
|
if(d[0] == DT_NEEDED){
|
||||||
soinfo* lsi = (soinfo *)d[1];
|
soinfo* lsi = (soinfo *)d[1];
|
||||||
if (!validate_soinfo(lsi)) {
|
if (!validate_soinfo(lsi)) {
|
||||||
DL_ERR("%5d bad DT_NEEDED pointer in %s",
|
DL_ERR("bad DT_NEEDED pointer in \"%s\"", si->name);
|
||||||
pid, si->name);
|
|
||||||
} else {
|
} else {
|
||||||
soinfo_call_constructors(lsi);
|
soinfo_call_constructors(lsi);
|
||||||
}
|
}
|
||||||
@ -1383,7 +1379,7 @@ static int nullify_closed_stdio (void)
|
|||||||
|
|
||||||
dev_null = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
|
dev_null = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
|
||||||
if (dev_null < 0) {
|
if (dev_null < 0) {
|
||||||
DL_ERR("Cannot open /dev/null.");
|
DL_ERR("cannot open /dev/null: %s", strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
TRACE("[ %5d Opened /dev/null file-descriptor=%d]\n", pid, dev_null);
|
TRACE("[ %5d Opened /dev/null file-descriptor=%d]\n", pid, dev_null);
|
||||||
@ -1392,24 +1388,22 @@ static int nullify_closed_stdio (void)
|
|||||||
with /dev/null, dup /dev/null to it. */
|
with /dev/null, dup /dev/null to it. */
|
||||||
for (i = 0; i < 3; i++) {
|
for (i = 0; i < 3; i++) {
|
||||||
/* If it is /dev/null already, we are done. */
|
/* If it is /dev/null already, we are done. */
|
||||||
if (i == dev_null)
|
if (i == dev_null) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
TRACE("[ %5d Nullifying stdio file descriptor %d]\n", pid, i);
|
TRACE("[ %5d Nullifying stdio file descriptor %d]\n", pid, i);
|
||||||
/* The man page of fcntl does not say that fcntl(..,F_GETFL)
|
status = TEMP_FAILURE_RETRY(fcntl(i, F_GETFL));
|
||||||
can be interrupted but we do this just to be safe. */
|
|
||||||
do {
|
|
||||||
status = fcntl(i, F_GETFL);
|
|
||||||
} while (status < 0 && errno == EINTR);
|
|
||||||
|
|
||||||
/* If file is openned, we are good. */
|
/* If file is opened, we are good. */
|
||||||
if (status >= 0)
|
if (status != -1) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* The only error we allow is that the file descriptor does not
|
/* The only error we allow is that the file descriptor does not
|
||||||
exist, in which case we dup /dev/null to it. */
|
exist, in which case we dup /dev/null to it. */
|
||||||
if (errno != EBADF) {
|
if (errno != EBADF) {
|
||||||
DL_ERR("nullify_stdio: unhandled error %s", strerror(errno));
|
DL_ERR("fcntl failed: %s", strerror(errno));
|
||||||
return_value = -1;
|
return_value = -1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1417,12 +1411,9 @@ static int nullify_closed_stdio (void)
|
|||||||
/* Try dupping /dev/null to this stdio file descriptor and
|
/* Try dupping /dev/null to this stdio file descriptor and
|
||||||
repeat if there is a signal. Note that any errors in closing
|
repeat if there is a signal. Note that any errors in closing
|
||||||
the stdio descriptor are lost. */
|
the stdio descriptor are lost. */
|
||||||
do {
|
status = TEMP_FAILURE_RETRY(dup2(dev_null, i));
|
||||||
status = dup2(dev_null, i);
|
|
||||||
} while (status < 0 && errno == EINTR);
|
|
||||||
|
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
DL_ERR("nullify_stdio: dup2 error %s", strerror(errno));
|
DL_ERR("dup2 failed: %s", strerror(errno));
|
||||||
return_value = -1;
|
return_value = -1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1431,12 +1422,9 @@ static int nullify_closed_stdio (void)
|
|||||||
/* If /dev/null is not one of the stdio file descriptors, close it. */
|
/* If /dev/null is not one of the stdio file descriptors, close it. */
|
||||||
if (dev_null > 2) {
|
if (dev_null > 2) {
|
||||||
TRACE("[ %5d Closing /dev/null file-descriptor=%d]\n", pid, dev_null);
|
TRACE("[ %5d Closing /dev/null file-descriptor=%d]\n", pid, dev_null);
|
||||||
do {
|
status = TEMP_FAILURE_RETRY(close(dev_null));
|
||||||
status = close(dev_null);
|
if (status == -1) {
|
||||||
} while (status < 0 && errno == EINTR);
|
DL_ERR("close failed: %s", strerror(errno));
|
||||||
|
|
||||||
if (status < 0) {
|
|
||||||
DL_ERR("nullify_stdio: close error %s", strerror(errno));
|
|
||||||
return_value = -1;
|
return_value = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1464,7 +1452,7 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset)
|
|||||||
si->dynamic = phdr_table_get_dynamic_section(phdr, phnum, base);
|
si->dynamic = phdr_table_get_dynamic_section(phdr, phnum, base);
|
||||||
if (si->dynamic == NULL) {
|
if (si->dynamic == NULL) {
|
||||||
if (!relocating_linker) {
|
if (!relocating_linker) {
|
||||||
DL_ERR("%5d missing PT_DYNAMIC?!", pid);
|
DL_ERR("missing PT_DYNAMIC?!");
|
||||||
}
|
}
|
||||||
goto fail;
|
goto fail;
|
||||||
} else {
|
} else {
|
||||||
@ -1485,8 +1473,8 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset)
|
|||||||
/* We can't call DL_ERR if the linker's relocations haven't
|
/* We can't call DL_ERR if the linker's relocations haven't
|
||||||
* been performed yet */
|
* been performed yet */
|
||||||
if (!relocating_linker) {
|
if (!relocating_linker) {
|
||||||
DL_ERR("%5d Can't unprotect segments for %s: %s",
|
DL_ERR("can't unprotect segments for \"%s\": %s",
|
||||||
pid, si->name, strerror(errno));
|
si->name, strerror(errno));
|
||||||
}
|
}
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@ -1537,7 +1525,7 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset)
|
|||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case DT_RELA:
|
case DT_RELA:
|
||||||
DL_ERR("%5d DT_RELA not supported", pid);
|
DL_ERR("DT_RELA not supported");
|
||||||
goto fail;
|
goto fail;
|
||||||
case DT_INIT:
|
case DT_INIT:
|
||||||
si->init_func = (void (*)(void))(base + *d);
|
si->init_func = (void (*)(void))(base + *d);
|
||||||
@ -1633,7 +1621,7 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset)
|
|||||||
pid, si->base, si->strtab, si->symtab);
|
pid, si->base, si->strtab, si->symtab);
|
||||||
|
|
||||||
if((si->strtab == 0) || (si->symtab == 0)) {
|
if((si->strtab == 0) || (si->symtab == 0)) {
|
||||||
DL_ERR("%5d missing essential tables", pid);
|
DL_ERR("missing essential tables");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1645,8 +1633,8 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset)
|
|||||||
soinfo *lsi = find_library(ldpreload_names[i]);
|
soinfo *lsi = find_library(ldpreload_names[i]);
|
||||||
if(lsi == 0) {
|
if(lsi == 0) {
|
||||||
strlcpy(tmp_err_buf, linker_get_error(), sizeof(tmp_err_buf));
|
strlcpy(tmp_err_buf, linker_get_error(), sizeof(tmp_err_buf));
|
||||||
DL_ERR("%5d could not load needed library '%s' for '%s' (%s)",
|
DL_ERR("could not load library \"%s\" needed by \"%s\"; caused by %s",
|
||||||
pid, ldpreload_names[i], si->name, tmp_err_buf);
|
ldpreload_names[i], si->name, tmp_err_buf);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
lsi->refcount++;
|
lsi->refcount++;
|
||||||
@ -1660,8 +1648,8 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset)
|
|||||||
soinfo *lsi = find_library(si->strtab + d[1]);
|
soinfo *lsi = find_library(si->strtab + d[1]);
|
||||||
if(lsi == 0) {
|
if(lsi == 0) {
|
||||||
strlcpy(tmp_err_buf, linker_get_error(), sizeof(tmp_err_buf));
|
strlcpy(tmp_err_buf, linker_get_error(), sizeof(tmp_err_buf));
|
||||||
DL_ERR("%5d could not load needed library '%s' for '%s' (%s)",
|
DL_ERR("could not load library \"%s\" needed by \"%s\"; caused by %s",
|
||||||
pid, si->strtab + d[1], si->name, tmp_err_buf);
|
si->strtab + d[1], si->name, tmp_err_buf);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
/* Save the soinfo of the loaded DT_NEEDED library in the payload
|
/* Save the soinfo of the loaded DT_NEEDED library in the payload
|
||||||
@ -1699,15 +1687,15 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset)
|
|||||||
/* All relocations are done, we can protect our segments back to
|
/* All relocations are done, we can protect our segments back to
|
||||||
* read-only. */
|
* read-only. */
|
||||||
if (phdr_table_protect_segments(si->phdr, si->phnum, si->load_bias) < 0) {
|
if (phdr_table_protect_segments(si->phdr, si->phnum, si->load_bias) < 0) {
|
||||||
DL_ERR("%5d Can't protect segments for %s: %s",
|
DL_ERR("can't protect segments for \"%s\": %s",
|
||||||
pid, si->name, strerror(errno));
|
si->name, strerror(errno));
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We can also turn on GNU RELRO protection */
|
/* We can also turn on GNU RELRO protection */
|
||||||
if (phdr_table_protect_gnu_relro(si->phdr, si->phnum, si->load_bias) < 0) {
|
if (phdr_table_protect_gnu_relro(si->phdr, si->phnum, si->load_bias) < 0) {
|
||||||
DL_ERR("%5d Can't enable GNU RELRO protection for %s: %s",
|
DL_ERR("can't enable GNU RELRO protection for \"%s\": %s",
|
||||||
pid, si->name, strerror(errno));
|
si->name, strerror(errno));
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1717,8 +1705,9 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset)
|
|||||||
ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-02:23.stdio.asc
|
ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-02:23.stdio.asc
|
||||||
|
|
||||||
*/
|
*/
|
||||||
if (program_is_setuid)
|
if (program_is_setuid) {
|
||||||
nullify_closed_stdio ();
|
nullify_closed_stdio();
|
||||||
|
}
|
||||||
notify_gdb_of_load(si);
|
notify_gdb_of_load(si);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -1728,53 +1717,43 @@ fail:
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_library_path(const char *path, char *delim)
|
static void parse_path(const char* path, const char* delimiters,
|
||||||
|
const char** array, char* buf, size_t buf_size, size_t max_count)
|
||||||
{
|
{
|
||||||
size_t len;
|
if (path == NULL) {
|
||||||
char *ldpaths_bufp = ldpaths_buf;
|
return;
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
len = strlcpy(ldpaths_buf, path, sizeof(ldpaths_buf));
|
|
||||||
|
|
||||||
while (i < LDPATH_MAX && (ldpaths[i] = strsep(&ldpaths_bufp, delim))) {
|
|
||||||
if (*ldpaths[i] != '\0')
|
|
||||||
++i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Forget the last path if we had to truncate; this occurs if the 2nd to
|
size_t len = strlcpy(buf, path, buf_size);
|
||||||
* last char isn't '\0' (i.e. not originally a delim). */
|
|
||||||
if (i > 0 && len >= sizeof(ldpaths_buf) &&
|
|
||||||
ldpaths_buf[sizeof(ldpaths_buf) - 2] != '\0') {
|
|
||||||
ldpaths[i - 1] = NULL;
|
|
||||||
} else {
|
|
||||||
ldpaths[i] = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void parse_preloads(const char *path, char *delim)
|
size_t i = 0;
|
||||||
{
|
char* buf_p = buf;
|
||||||
size_t len;
|
while (i < max_count && (array[i] = strsep(&buf_p, delimiters))) {
|
||||||
char *ldpreloads_bufp = ldpreloads_buf;
|
if (*array[i] != '\0') {
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
len = strlcpy(ldpreloads_buf, path, sizeof(ldpreloads_buf));
|
|
||||||
|
|
||||||
while (i < LDPRELOAD_MAX && (ldpreload_names[i] = strsep(&ldpreloads_bufp, delim))) {
|
|
||||||
if (*ldpreload_names[i] != '\0') {
|
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Forget the last path if we had to truncate; this occurs if the 2nd to
|
// Forget the last path if we had to truncate; this occurs if the 2nd to
|
||||||
* last char isn't '\0' (i.e. not originally a delim). */
|
// last char isn't '\0' (i.e. wasn't originally a delimiter).
|
||||||
if (i > 0 && len >= sizeof(ldpreloads_buf) &&
|
if (i > 0 && len >= buf_size && buf[buf_size - 2] != '\0') {
|
||||||
ldpreloads_buf[sizeof(ldpreloads_buf) - 2] != '\0') {
|
array[i - 1] = NULL;
|
||||||
ldpreload_names[i - 1] = NULL;
|
|
||||||
} else {
|
} else {
|
||||||
ldpreload_names[i] = NULL;
|
array[i] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_LD_LIBRARY_PATH(const char* path) {
|
||||||
|
parse_path(path, ":", ldpaths,
|
||||||
|
ldpaths_buf, sizeof(ldpaths_buf), LDPATH_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_LD_PRELOAD(const char* path) {
|
||||||
|
// We have historically supported ':' as well as ' ' in LD_PRELOAD.
|
||||||
|
parse_path(path, " :", ldpreload_names,
|
||||||
|
ldpreloads_buf, sizeof(ldpreloads_buf), LDPRELOAD_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This code is called after the linker has linked itself and
|
* This code is called after the linker has linked itself and
|
||||||
* fixed it's own GOT. It is safe to make references to externs
|
* fixed it's own GOT. It is safe to make references to externs
|
||||||
@ -1917,13 +1896,9 @@ sanitize:
|
|||||||
si->dynamic = (unsigned *)-1;
|
si->dynamic = (unsigned *)-1;
|
||||||
si->refcount = 1;
|
si->refcount = 1;
|
||||||
|
|
||||||
/* Use LD_LIBRARY_PATH if we aren't setuid/setgid */
|
// Use LD_LIBRARY_PATH and LD_PRELOAD (but only if we aren't setuid/setgid).
|
||||||
if (ldpath_env)
|
parse_LD_LIBRARY_PATH(ldpath_env);
|
||||||
parse_library_path(ldpath_env, ":");
|
parse_LD_PRELOAD(ldpreload_env);
|
||||||
|
|
||||||
if (ldpreload_env) {
|
|
||||||
parse_preloads(ldpreload_env, " :");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(soinfo_link_image(si, 0)) {
|
if(soinfo_link_image(si, 0)) {
|
||||||
char errmsg[] = "CANNOT LINK EXECUTABLE\n";
|
char errmsg[] = "CANNOT LINK EXECUTABLE\n";
|
||||||
@ -2047,7 +2022,7 @@ get_elf_exec_load_bias(const Elf32_Ehdr* elf)
|
|||||||
* relocations, any attempt to reference an extern variable, extern
|
* relocations, any attempt to reference an extern variable, extern
|
||||||
* function, or other GOT reference will generate a segfault.
|
* function, or other GOT reference will generate a segfault.
|
||||||
*/
|
*/
|
||||||
unsigned __linker_init(unsigned **elfdata) {
|
extern "C" unsigned __linker_init(unsigned **elfdata) {
|
||||||
unsigned linker_addr = find_linker_base(elfdata);
|
unsigned linker_addr = find_linker_base(elfdata);
|
||||||
Elf32_Ehdr *elf_hdr = (Elf32_Ehdr *) linker_addr;
|
Elf32_Ehdr *elf_hdr = (Elf32_Ehdr *) linker_addr;
|
||||||
Elf32_Phdr *phdr =
|
Elf32_Phdr *phdr =
|
@ -34,6 +34,10 @@
|
|||||||
#include <elf.h>
|
#include <elf.h>
|
||||||
#include <sys/exec_elf.h>
|
#include <sys/exec_elf.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
#undef PAGE_MASK
|
#undef PAGE_MASK
|
||||||
#undef PAGE_SIZE
|
#undef PAGE_SIZE
|
||||||
#define PAGE_SIZE 4096
|
#define PAGE_SIZE 4096
|
||||||
@ -106,7 +110,7 @@ typedef struct soinfo soinfo;
|
|||||||
|
|
||||||
struct soinfo
|
struct soinfo
|
||||||
{
|
{
|
||||||
const char name[SOINFO_NAME_LEN];
|
char name[SOINFO_NAME_LEN];
|
||||||
const Elf32_Phdr *phdr;
|
const Elf32_Phdr *phdr;
|
||||||
int phnum;
|
int phnum;
|
||||||
unsigned entry;
|
unsigned entry;
|
||||||
@ -244,4 +248,8 @@ _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount);
|
|||||||
int dl_iterate_phdr(int (*cb)(struct dl_phdr_info *, size_t, void *), void *);
|
int dl_iterate_phdr(int (*cb)(struct dl_phdr_info *, size_t, void *), void *);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -28,6 +28,10 @@
|
|||||||
#ifndef LINKER_ENVIRON_H
|
#ifndef LINKER_ENVIRON_H
|
||||||
#define LINKER_ENVIRON_H
|
#define LINKER_ENVIRON_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Call this function before anything else. 'vecs' must be the pointer
|
/* Call this function before anything else. 'vecs' must be the pointer
|
||||||
* to the environment block in the ELF data block. The function returns
|
* to the environment block in the ELF data block. The function returns
|
||||||
* the start of the aux vectors after the env block.
|
* the start of the aux vectors after the env block.
|
||||||
@ -47,8 +51,12 @@ extern void linker_env_unset(const char* name);
|
|||||||
* after this function. */
|
* after this function. */
|
||||||
extern const char* linker_env_get(const char* name);
|
extern const char* linker_env_get(const char* name);
|
||||||
|
|
||||||
/* Remove unsecure environment variables. This should be used when
|
/* Remove insecure environment variables. This should be used when
|
||||||
* running setuid programs. */
|
* running setuid programs. */
|
||||||
extern void linker_env_secure(void);
|
extern void linker_env_secure(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* LINKER_ENVIRON_H */
|
#endif /* LINKER_ENVIRON_H */
|
||||||
|
@ -312,7 +312,7 @@ static int log_vprint(int prio, const char *tag, const char *fmt, va_list args)
|
|||||||
|
|
||||||
#else /* !CUSTOM_LOG_VPRINT */
|
#else /* !CUSTOM_LOG_VPRINT */
|
||||||
|
|
||||||
extern int __libc_android_log_vprint(int prio, const char* tag, const char* format, va_list ap);
|
extern "C" int __libc_android_log_vprint(int prio, const char* tag, const char* format, va_list ap);
|
||||||
|
|
||||||
#endif /* !CUSTOM_LOG_VPRINT */
|
#endif /* !CUSTOM_LOG_VPRINT */
|
||||||
|
|
||||||
|
@ -31,6 +31,10 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Formatting routines for the dynamic linker's debug traces */
|
/* Formatting routines for the dynamic linker's debug traces */
|
||||||
/* We want to avoid dragging the whole C library fprintf() */
|
/* We want to avoid dragging the whole C library fprintf() */
|
||||||
/* implementation into the dynamic linker since this creates */
|
/* implementation into the dynamic linker since this creates */
|
||||||
@ -38,4 +42,8 @@
|
|||||||
|
|
||||||
int format_buffer(char *buffer, size_t bufsize, const char *format, ...);
|
int format_buffer(char *buffer, size_t bufsize, const char *format, ...);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* _LINKER_FORMAT_H */
|
#endif /* _LINKER_FORMAT_H */
|
||||||
|
@ -180,24 +180,25 @@ void phdr_table_unload(void* phdr_mmap, Elf32_Addr phdr_memsize)
|
|||||||
* This returns 0 if there are no loadable segments.
|
* This returns 0 if there are no loadable segments.
|
||||||
*/
|
*/
|
||||||
Elf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table,
|
Elf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table,
|
||||||
int phdr_count)
|
size_t phdr_count)
|
||||||
{
|
{
|
||||||
int nn;
|
|
||||||
|
|
||||||
Elf32_Addr min_vaddr = 0xFFFFFFFFU;
|
Elf32_Addr min_vaddr = 0xFFFFFFFFU;
|
||||||
Elf32_Addr max_vaddr = 0x00000000U;
|
Elf32_Addr max_vaddr = 0x00000000U;
|
||||||
|
|
||||||
for (nn = 0; nn < phdr_count; nn++) {
|
for (size_t i = 0; i < phdr_count; ++i) {
|
||||||
const Elf32_Phdr* phdr = &phdr_table[nn];
|
const Elf32_Phdr* phdr = &phdr_table[i];
|
||||||
|
|
||||||
if (phdr->p_type != PT_LOAD)
|
if (phdr->p_type != PT_LOAD) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (phdr->p_vaddr < min_vaddr)
|
if (phdr->p_vaddr < min_vaddr) {
|
||||||
min_vaddr = phdr->p_vaddr;
|
min_vaddr = phdr->p_vaddr;
|
||||||
|
}
|
||||||
|
|
||||||
if (phdr->p_vaddr + phdr->p_memsz > max_vaddr)
|
if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) {
|
||||||
max_vaddr = phdr->p_vaddr + phdr->p_memsz;
|
max_vaddr = phdr->p_vaddr + phdr->p_memsz;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (min_vaddr > max_vaddr) {
|
if (min_vaddr > max_vaddr) {
|
||||||
@ -217,8 +218,6 @@ Elf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table,
|
|||||||
* Input:
|
* Input:
|
||||||
* phdr_table -> program header table
|
* phdr_table -> program header table
|
||||||
* phdr_count -> number of entries in the tables
|
* phdr_count -> number of entries in the tables
|
||||||
* required_base -> for prelinked libraries, mandatory load address
|
|
||||||
* of the first loadable segment. 0 otherwise.
|
|
||||||
* Output:
|
* Output:
|
||||||
* load_start -> first page of reserved address space range
|
* load_start -> first page of reserved address space range
|
||||||
* load_size -> size in bytes of reserved address space range
|
* load_size -> size in bytes of reserved address space range
|
||||||
@ -229,26 +228,19 @@ Elf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table,
|
|||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
phdr_table_reserve_memory(const Elf32_Phdr* phdr_table,
|
phdr_table_reserve_memory(const Elf32_Phdr* phdr_table,
|
||||||
int phdr_count,
|
size_t phdr_count,
|
||||||
Elf32_Addr required_base,
|
void** load_start,
|
||||||
void** load_start,
|
Elf32_Addr* load_size,
|
||||||
Elf32_Addr* load_size,
|
Elf32_Addr* load_bias)
|
||||||
Elf32_Addr* load_bias)
|
|
||||||
{
|
{
|
||||||
Elf32_Addr size = phdr_table_get_load_size(phdr_table, phdr_count);
|
Elf32_Addr size = phdr_table_get_load_size(phdr_table, phdr_count);
|
||||||
void* start;
|
|
||||||
int nn, mmap_flags;
|
|
||||||
|
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
||||||
if (required_base != 0)
|
void* start = mmap(NULL, size, PROT_NONE, mmap_flags, -1, 0);
|
||||||
mmap_flags |= MAP_FIXED;
|
|
||||||
|
|
||||||
start = mmap((void*)required_base, size, PROT_NONE, mmap_flags, -1, 0);
|
|
||||||
if (start == MAP_FAILED) {
|
if (start == MAP_FAILED) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -257,8 +249,8 @@ phdr_table_reserve_memory(const Elf32_Phdr* phdr_table,
|
|||||||
*load_size = size;
|
*load_size = size;
|
||||||
*load_bias = 0;
|
*load_bias = 0;
|
||||||
|
|
||||||
for (nn = 0; nn < phdr_count; nn++) {
|
for (size_t i = 0; i < phdr_count; ++i) {
|
||||||
const Elf32_Phdr* phdr = &phdr_table[nn];
|
const Elf32_Phdr* phdr = &phdr_table[i];
|
||||||
if (phdr->p_type == PT_LOAD) {
|
if (phdr->p_type == PT_LOAD) {
|
||||||
*load_bias = (Elf32_Addr)start - PAGE_START(phdr->p_vaddr);
|
*load_bias = (Elf32_Addr)start - PAGE_START(phdr->p_vaddr);
|
||||||
break;
|
break;
|
||||||
@ -274,8 +266,6 @@ phdr_table_reserve_memory(const Elf32_Phdr* phdr_table,
|
|||||||
* Input:
|
* Input:
|
||||||
* phdr_table -> program header table
|
* phdr_table -> program header table
|
||||||
* phdr_count -> number of entries in the table
|
* phdr_count -> number of entries in the table
|
||||||
* load_start -> start address of reserved memory range.
|
|
||||||
* load_size -> size of reserved memory range.
|
|
||||||
* load_bias -> load offset.
|
* load_bias -> load offset.
|
||||||
* fd -> input file descriptor.
|
* fd -> input file descriptor.
|
||||||
*
|
*
|
||||||
@ -285,8 +275,6 @@ phdr_table_reserve_memory(const Elf32_Phdr* phdr_table,
|
|||||||
int
|
int
|
||||||
phdr_table_load_segments(const Elf32_Phdr* phdr_table,
|
phdr_table_load_segments(const Elf32_Phdr* phdr_table,
|
||||||
int phdr_count,
|
int phdr_count,
|
||||||
void* load_start,
|
|
||||||
Elf32_Addr load_size,
|
|
||||||
Elf32_Addr load_bias,
|
Elf32_Addr load_bias,
|
||||||
int fd)
|
int fd)
|
||||||
{
|
{
|
||||||
|
@ -37,6 +37,10 @@
|
|||||||
|
|
||||||
#include "linker.h"
|
#include "linker.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
/* See linker_phdr.c for all usage documentation */
|
/* See linker_phdr.c for all usage documentation */
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -52,21 +56,18 @@ phdr_table_unload(void* phdr_mmap, Elf32_Addr phdr_memsize);
|
|||||||
|
|
||||||
Elf32_Addr
|
Elf32_Addr
|
||||||
phdr_table_get_load_size(const Elf32_Phdr* phdr_table,
|
phdr_table_get_load_size(const Elf32_Phdr* phdr_table,
|
||||||
int phdr_count);
|
size_t phdr_count);
|
||||||
|
|
||||||
int
|
int
|
||||||
phdr_table_reserve_memory(const Elf32_Phdr* phdr_table,
|
phdr_table_reserve_memory(const Elf32_Phdr* phdr_table,
|
||||||
int phdr_count,
|
size_t phdr_count,
|
||||||
Elf32_Addr required_base,
|
void** load_start,
|
||||||
void** load_start,
|
Elf32_Addr* load_size,
|
||||||
Elf32_Addr* load_size,
|
Elf32_Addr* load_bias);
|
||||||
Elf32_Addr* load_bias);
|
|
||||||
|
|
||||||
int
|
int
|
||||||
phdr_table_load_segments(const Elf32_Phdr* phdr_table,
|
phdr_table_load_segments(const Elf32_Phdr* phdr_table,
|
||||||
int phdr_count,
|
int phdr_count,
|
||||||
void* load_start,
|
|
||||||
Elf32_Addr load_size,
|
|
||||||
Elf32_Addr load_bias,
|
Elf32_Addr load_bias,
|
||||||
int fd);
|
int fd);
|
||||||
|
|
||||||
@ -109,4 +110,8 @@ phdr_table_get_dynamic_section(const Elf32_Phdr* phdr_table,
|
|||||||
int phdr_count,
|
int phdr_count,
|
||||||
Elf32_Addr load_bias);
|
Elf32_Addr load_bias);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* LINKER_PHDR_H */
|
#endif /* LINKER_PHDR_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user