Skip to content

Commit

Permalink
More dynamic linker cleanup.
Browse files Browse the repository at this point in the history
I still want to break linker_format out into its own library so we can reuse
it for malloc debugging and so forth. (There are many similar pieces of code
in bionic, but the linker's one seems to be the most complete/functional.)

Change-Id: If3721853d28937c8e821ca1d23cf200e228a409a
  • Loading branch information
enh-google committed Oct 30, 2012
1 parent 06b5961 commit 18a206c
Show file tree
Hide file tree
Showing 13 changed files with 336 additions and 405 deletions.
11 changes: 5 additions & 6 deletions linker/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,19 @@ include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
arch/$(TARGET_ARCH)/begin.S \
debugger.c \
debugger.cpp \
dlfcn.cpp \
linker.cpp \
linker_environ.c \
linker_format.c \
linker_phdr.c \
rt.c
linker_environ.cpp \
linker_format.cpp \
linker_phdr.cpp \
rt.cpp

LOCAL_LDFLAGS := -shared

LOCAL_CFLAGS += -fno-stack-protector \
-Wstrict-overflow=5 \
-fvisibility=hidden \
-std=gnu99 \
-Wall -Wextra

# Set LINKER_DEBUG to either 1 or 0
Expand Down
85 changes: 35 additions & 50 deletions linker/debugger.c → linker/debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
* SUCH DAMAGE.
*/

#include "linker.h"
#include "linker_format.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
Expand All @@ -36,45 +39,35 @@
#include <sys/socket.h>
#include <sys/un.h>

extern int tgkill(int tgid, int tid, int sig);
#include <private/logd.h>

void notify_gdb_of_libraries();
extern "C" int tgkill(int tgid, int tid, int sig);

#define DEBUGGER_SOCKET_NAME "android:debuggerd"

typedef enum {
enum debugger_action_t {
// dump a crash
DEBUGGER_ACTION_CRASH,
// dump a tombstone file
DEBUGGER_ACTION_DUMP_TOMBSTONE,
// dump a backtrace only back to the socket
DEBUGGER_ACTION_DUMP_BACKTRACE,
} debugger_action_t;
};

/* message sent over the socket */
typedef struct {
struct debugger_msg_t {
debugger_action_t action;
pid_t tid;
} debugger_msg_t;

#define RETRY_ON_EINTR(ret,cond) \
do { \
ret = (cond); \
} while (ret < 0 && errno == EINTR)
};

// see man(2) prctl, specifically the section about PR_GET_NAME
#define MAX_TASK_NAME_LEN (16)

static int socket_abstract_client(const char *name, int type)
{
struct sockaddr_un addr;
size_t namelen;
socklen_t alen;
int s, err;

namelen = strlen(name);
static int socket_abstract_client(const char* name, int type) {
sockaddr_un addr;

// Test with length +1 for the *initial* '\0'.
size_t namelen = strlen(name);
if ((namelen + 1) > sizeof(addr.sun_path)) {
errno = EINVAL;
return -1;
Expand All @@ -86,28 +79,27 @@ static int socket_abstract_client(const char *name, int type)
* Note: The path in this case is *not* supposed to be
* '\0'-terminated. ("man 7 unix" for the gory details.)
*/
memset (&addr, 0, sizeof addr);
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_LOCAL;
addr.sun_path[0] = 0;
memcpy(addr.sun_path + 1, name, namelen);

alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;

s = socket(AF_LOCAL, type, 0);
if(s < 0) return -1;
int s = socket(AF_LOCAL, type, 0);
if (s == -1) {
return -1;
}

RETRY_ON_EINTR(err,connect(s, (struct sockaddr *) &addr, alen));
if (err < 0) {
int err = TEMP_FAILURE_RETRY(connect(s, (sockaddr*) &addr, alen));
if (err == -1) {
close(s);
s = -1;
}

return s;
}

#include "linker_format.h"
#include <../libc/private/logd.h>

/*
* Writes a summary of the signal to the log file. We do this so that, if
* for some reason we're not able to contact debuggerd, there is still some
Expand All @@ -116,15 +108,9 @@ static int socket_abstract_client(const char *name, int type)
* We could be here as a result of native heap corruption, or while a
* mutex is being held, so we don't want to use any libc functions that
* could allocate memory or hold a lock.
*
* "info" will be NULL if the siginfo_t information was not available.
*/
static void logSignalSummary(int signum, const siginfo_t* info)
{
char buffer[128];
char threadname[MAX_TASK_NAME_LEN + 1]; // one more for termination

char* signame;
static void logSignalSummary(int signum, const siginfo_t* info) {
const char* signame;
switch (signum) {
case SIGILL: signame = "SIGILL"; break;
case SIGABRT: signame = "SIGABRT"; break;
Expand All @@ -138,13 +124,17 @@ static void logSignalSummary(int signum, const siginfo_t* info)
default: signame = "???"; break;
}

char threadname[MAX_TASK_NAME_LEN + 1]; // one more for termination
if (prctl(PR_GET_NAME, (unsigned long)threadname, 0, 0, 0) != 0) {
strcpy(threadname, "<name unknown>");
} else {
// short names are null terminated by prctl, but the manpage
// implies that 16 byte names are not.
threadname[MAX_TASK_NAME_LEN] = 0;
}

char buffer[128];
// "info" will be NULL if the siginfo_t information was not available.
if (info != NULL) {
format_buffer(buffer, sizeof(buffer),
"Fatal signal %d (%s) at 0x%08x (code=%d), thread %d (%s)",
Expand All @@ -161,8 +151,7 @@ static void logSignalSummary(int signum, const siginfo_t* info)
/*
* Returns true if the handler for signal "signum" has SA_SIGINFO set.
*/
static bool haveSiginfo(int signum)
{
static bool haveSiginfo(int signum) {
struct sigaction oldact, newact;

memset(&newact, 0, sizeof(newact));
Expand All @@ -188,11 +177,8 @@ static bool haveSiginfo(int signum)
* Catches fatal signals so we can ask debuggerd to ptrace us before
* we crash.
*/
void debugger_signal_handler(int n, siginfo_t* info, void* unused __attribute__((unused)))
{
void debugger_signal_handler(int n, siginfo_t* info, void*) {
char msgbuf[128];
unsigned tid;
int s;

/*
* It's possible somebody cleared the SA_SIGINFO flag, which would mean
Expand All @@ -204,8 +190,8 @@ void debugger_signal_handler(int n, siginfo_t* info, void* unused __attribute__(

logSignalSummary(n, info);

tid = gettid();
s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM);
pid_t tid = gettid();
int s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM);

if (s >= 0) {
/* debugger knows our pid from the credentials on the
Expand All @@ -217,14 +203,14 @@ void debugger_signal_handler(int n, siginfo_t* info, void* unused __attribute__(
debugger_msg_t msg;
msg.action = DEBUGGER_ACTION_CRASH;
msg.tid = tid;
RETRY_ON_EINTR(ret, write(s, &msg, sizeof(msg)));
ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg)));
if (ret == sizeof(msg)) {
/* if the write failed, there is no point to read on
* the file descriptor. */
RETRY_ON_EINTR(ret, read(s, &tid, 1));
int savedErrno = errno;
ret = TEMP_FAILURE_RETRY(read(s, &tid, 1));
int saved_errno = errno;
notify_gdb_of_libraries();
errno = savedErrno;
errno = saved_errno;
}

if (ret < 0) {
Expand Down Expand Up @@ -266,8 +252,7 @@ void debugger_signal_handler(int n, siginfo_t* info, void* unused __attribute__(
}
}

void debugger_init()
{
void debugger_init() {
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_sigaction = debugger_signal_handler;
Expand Down
73 changes: 23 additions & 50 deletions linker/linker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ static soinfo *sonext = &libdl_info;
static soinfo *somain; /* main process, always the one after libdl_info */
#endif


static char ldpaths_buf[LDPATH_BUFSIZE];
static const char *ldpaths[LDPATH_MAX + 1];

Expand All @@ -108,9 +107,6 @@ int debug_verbosity;

static int pid;

/* This boolean is set if the program being loaded is setuid */
static bool program_is_setuid;

enum RelocationKind {
kRelocAbsolute = 0,
kRelocRelative,
Expand Down Expand Up @@ -257,8 +253,7 @@ static void notify_gdb_of_unload(soinfo* info) {
rtld_db_dlactivity();
}

extern "C" void notify_gdb_of_libraries()
{
void notify_gdb_of_libraries() {
_r_debug.r_state = RT_ADD;
rtld_db_dlactivity();
_r_debug.r_state = RT_CONSISTENT;
Expand Down Expand Up @@ -1689,7 +1684,7 @@ static int soinfo_link_image(soinfo *si)
ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-02:23.stdio.asc
*/
if (program_is_setuid) {
if (get_AT_SECURE()) {
nullify_closed_stdio();
}
notify_gdb_of_load(si);
Expand Down Expand Up @@ -1750,11 +1745,6 @@ static unsigned __linker_init_post_relocation(unsigned **elfdata, unsigned linke
int argc = (int) *elfdata;
char **argv = (char**) (elfdata + 1);
unsigned *vecs = (unsigned*) (argv + argc + 1);
unsigned *v;
soinfo *si;
int i;
const char *ldpath_env = NULL;
const char *ldpreload_env = NULL;

/* NOTE: we store the elfdata pointer on a special location
* of the temporary TLS area in order to pass it to
Expand All @@ -1773,53 +1763,36 @@ static unsigned __linker_init_post_relocation(unsigned **elfdata, unsigned linke
gettimeofday(&t0, 0);
#endif

/* Initialize environment functions, and get to the ELF aux vectors table */
// Initialize environment functions, and get to the ELF aux vectors table.
vecs = linker_env_init(vecs);

/* Check auxv for AT_SECURE first to see if program is setuid, setgid,
has file caps, or caused a SELinux/AppArmor domain transition. */
for (v = vecs; v[0]; v += 2) {
if (v[0] == AT_SECURE) {
/* kernel told us whether to enable secure mode */
program_is_setuid = v[1];
goto sanitize;
}
}

/* Kernel did not provide AT_SECURE - fall back on legacy test. */
program_is_setuid = (getuid() != geteuid()) || (getgid() != getegid());

sanitize:
/* Sanitize environment if we're loading a setuid program */
if (program_is_setuid) {
linker_env_secure();
}

debugger_init();

/* Get a few environment variables */
{
// Get a few environment variables.
#if LINKER_DEBUG
const char* env;
env = linker_env_get("DEBUG"); /* XXX: TODO: Change to LD_DEBUG */
if (env)
{
const char* env = linker_env_get("LD_DEBUG");
if (env != NULL) {
debug_verbosity = atoi(env);
}
}
#endif

/* Normally, these are cleaned by linker_env_secure, but the test
* against program_is_setuid doesn't cost us anything */
if (!program_is_setuid) {
ldpath_env = linker_env_get("LD_LIBRARY_PATH");
ldpreload_env = linker_env_get("LD_PRELOAD");
}
// Normally, these are cleaned by linker_env_init, but the test
// doesn't cost us anything.
const char* ldpath_env = NULL;
const char* ldpreload_env = NULL;
if (!get_AT_SECURE()) {
ldpath_env = linker_env_get("LD_LIBRARY_PATH");
ldpreload_env = linker_env_get("LD_PRELOAD");
}

INFO("[ android linker & debugger ]\n");
DEBUG("%5d elfdata @ 0x%08x\n", pid, (unsigned)elfdata);

si = soinfo_alloc(argv[0]);
if(si == 0) {
exit(-1);
soinfo* si = soinfo_alloc(argv[0]);
if (si == NULL) {
exit(EXIT_FAILURE);
}

/* bootstrap the link map, the main exe always needs to be first */
Expand Down Expand Up @@ -1858,7 +1831,7 @@ static unsigned __linker_init_post_relocation(unsigned **elfdata, unsigned linke
insert_soinfo_into_debug_map(&linker_soinfo);

/* extract information passed from the kernel */
while(vecs[0] != 0){
while (vecs[0] != 0){
switch(vecs[0]){
case AT_PHDR:
si->phdr = (Elf32_Phdr*) vecs[1];
Expand Down Expand Up @@ -1899,12 +1872,12 @@ static unsigned __linker_init_post_relocation(unsigned **elfdata, unsigned linke
char errmsg[] = "CANNOT LINK EXECUTABLE\n";
write(2, __linker_dl_err_buf, strlen(__linker_dl_err_buf));
write(2, errmsg, sizeof(errmsg));
exit(-1);
exit(EXIT_FAILURE);
}

soinfo_call_preinit_constructors(si);

for(i = 0; preloads[i] != NULL; i++) {
for (size_t i = 0; preloads[i] != NULL; ++i) {
soinfo_call_constructors(preloads[i]);
}

Expand Down Expand Up @@ -2049,7 +2022,7 @@ extern "C" unsigned __linker_init(unsigned **elfdata) {
//
// This situation should never occur unless the linker itself
// is corrupt.
exit(-1);
exit(EXIT_FAILURE);
}

// We have successfully fixed our own relocations. It's safe to run
Expand Down
Loading

0 comments on commit 18a206c

Please sign in to comment.