Clean up the linker a bit, remove prelinking support.
Also make the errors more readable, since none of us seemed to know what they actually meant. The new style is still as verbose as the old, but that's probably necessary in the absence of chained exceptions in C. Here's what you'd see if you try to boot after removing libsurfaceflinger.so: 32267 32267 E AndroidRuntime: java.lang.UnsatisfiedLinkError: Cannot load library: (linker.c:1629, pid 32259) soinfo_link_image: could not load library "libsystem_server.so" needed by "libandroid_servers.so"; caused by (linker.c:1629, pid 32259) soinfo_link_image: could not load library "libsurfaceflinger.so" needed by "libsystem_server.so"; caused by (linker.c:709, pid 32259) load_library: library "libsurfaceflinger.so" not found This patch also fixes almost all of the compiler warnings. Change-Id: I64bb59aed6d4e039c15ea45be2367f319ef879f8
This commit is contained in:
parent
1c861728e5
commit
4688279db5
@ -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…
x
Reference in New Issue
Block a user