summaryrefslogtreecommitdiff
path: root/library.c
diff options
context:
space:
mode:
Diffstat (limited to 'library.c')
-rw-r--r--library.c385
1 files changed, 315 insertions, 70 deletions
diff --git a/library.c b/library.c
index 594472b..2339153 100644
--- a/library.c
+++ b/library.c
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2011,2012,2013,2014 Petr Machata, Red Hat Inc.
* Copyright (C) 2001,2009 Juan Cespedes
* Copyright (C) 2006 Ian Wienand
*
@@ -20,6 +20,8 @@
* 02110-1301 USA
*/
+#include "config.h"
+
#include <stdlib.h>
#include <string.h>
#include <assert.h>
@@ -29,12 +31,36 @@
#include "callback.h"
#include "debug.h"
#include "dict.h"
+#include "vect.h"
#include "backend.h" // for arch_library_symbol_init, arch_library_init
-#ifndef ARCH_HAVE_LIBRARY_DATA
+static void
+library_exported_names_init(struct library_exported_names *names);
+
+#ifndef OS_HAVE_LIBRARY_DATA
+int
+os_library_init(struct library *lib)
+{
+ return 0;
+}
+
void
+os_library_destroy(struct library *lib)
+{
+}
+
+int
+os_library_clone(struct library *retp, struct library *lib)
+{
+ return 0;
+}
+#endif
+
+#ifndef ARCH_HAVE_LIBRARY_DATA
+int
arch_library_init(struct library *lib)
{
+ return 0;
}
void
@@ -42,9 +68,30 @@ arch_library_destroy(struct library *lib)
{
}
-void
+int
arch_library_clone(struct library *retp, struct library *lib)
{
+ return 0;
+}
+#endif
+
+#ifndef OS_HAVE_LIBRARY_SYMBOL_DATA
+int
+os_library_symbol_init(struct library_symbol *libsym)
+{
+ return 0;
+}
+
+void
+os_library_symbol_destroy(struct library_symbol *libsym)
+{
+}
+
+int
+os_library_symbol_clone(struct library_symbol *retp,
+ struct library_symbol *libsym)
+{
+ return 0;
}
#endif
@@ -68,46 +115,39 @@ arch_library_symbol_clone(struct library_symbol *retp,
}
#endif
-unsigned int
-target_address_hash(const void *key)
+size_t
+arch_addr_hash(const arch_addr_t *addr)
{
- /* XXX this assumes that key is passed by value. */
union {
arch_addr_t addr;
- unsigned int ints[sizeof(arch_addr_t)
- / sizeof(unsigned int)];
- } u = { .addr = (arch_addr_t)key };
+ int ints[sizeof(arch_addr_t)
+ / sizeof(unsigned int)];
+ } u = { .addr = *addr };
size_t i;
- unsigned int h = 0;
+ size_t h = 0;
for (i = 0; i < sizeof(u.ints) / sizeof(*u.ints); ++i)
- h ^= dict_key2hash_int((void *)(uintptr_t)u.ints[i]);
+ h ^= dict_hash_int(&u.ints[i]);
return h;
}
int
-target_address_cmp(const void *key1, const void *key2)
+arch_addr_eq(const arch_addr_t *addr1, const arch_addr_t *addr2)
{
- /* XXX this assumes that key is passed by value. */
- arch_addr_t addr1 = (arch_addr_t)key1;
- arch_addr_t addr2 = (arch_addr_t)key2;
- return addr1 < addr2 ? 1
- : addr1 > addr2 ? -1 : 0;
+ return *addr1 == *addr2;
}
-/* If the other symbol owns the name, we need to make the copy, so
- * that the life-times of the two symbols are not dependent on each
- * other. */
-static int
-strdup_if_owned(const char **retp, const char *str, int owned)
+int
+strdup_if(const char **retp, const char *str, int whether)
{
- if (!owned || str == NULL) {
- *retp = str;
- return 0;
- } else {
- *retp = strdup(str);
- return *retp != NULL ? 0 : -1;
+ if (whether && str != NULL) {
+ str = strdup(str);
+ if (str == NULL)
+ return -1;
}
+
+ *retp = str;
+ return 0;
}
static void
@@ -125,6 +165,7 @@ private_library_symbol_init(struct library_symbol *libsym,
libsym->latent = latent;
libsym->delayed = delayed;
libsym->enter_addr = (void *)(uintptr_t)addr;
+ libsym->proto = NULL;
}
static void
@@ -141,37 +182,55 @@ library_symbol_init(struct library_symbol *libsym,
private_library_symbol_init(libsym, addr, name, own_name,
type_of_plt, 0, 0);
- /* If arch init fails, we've already set libsym->name and
- * own_name. But we return failure, and the client code isn't
- * supposed to call library_symbol_destroy in such a case. */
- return arch_library_symbol_init(libsym);
+ if (os_library_symbol_init(libsym) < 0)
+ /* We've already set libsym->name and own_name. But
+ * we return failure, and the client code isn't
+ * supposed to call library_symbol_destroy in such
+ * case. */
+ return -1;
+
+ if (arch_library_symbol_init(libsym) < 0) {
+ os_library_symbol_destroy(libsym);
+ return -1;
+ }
+
+ return 0;
}
void
library_symbol_destroy(struct library_symbol *libsym)
{
if (libsym != NULL) {
- private_library_symbol_destroy(libsym);
arch_library_symbol_destroy(libsym);
+ os_library_symbol_destroy(libsym);
+ private_library_symbol_destroy(libsym);
}
}
int
library_symbol_clone(struct library_symbol *retp, struct library_symbol *libsym)
{
+ /* Make lifetimes of name stored at original independent of
+ * the one at the clone. */
const char *name;
- if (strdup_if_owned(&name, libsym->name, libsym->own_name) < 0)
+ if (strdup_if(&name, libsym->name, libsym->own_name) < 0)
return -1;
private_library_symbol_init(retp, libsym->enter_addr,
name, libsym->own_name, libsym->plt_type,
libsym->latent, libsym->delayed);
- if (arch_library_symbol_clone(retp, libsym) < 0) {
+ if (os_library_symbol_clone(retp, libsym) < 0) {
+ fail:
private_library_symbol_destroy(retp);
return -1;
}
+ if (arch_library_symbol_clone(retp, libsym) < 0) {
+ os_library_symbol_destroy(retp);
+ goto fail;
+ }
+
return 0;
}
@@ -230,6 +289,7 @@ private_library_init(struct library *lib, enum library_type type)
lib->base = 0;
lib->entry = 0;
lib->dyn_addr = 0;
+ lib->protolib = NULL;
lib->soname = NULL;
lib->own_soname = 0;
@@ -238,37 +298,223 @@ private_library_init(struct library *lib, enum library_type type)
lib->own_pathname = 0;
lib->symbols = NULL;
- lib->exported_names = NULL;
+ library_exported_names_init(&lib->exported_names);
+ lib->should_activate_latent = false;
lib->type = type;
+
+#if defined(HAVE_LIBDW)
+ lib->dwfl_module = NULL;
+#endif
}
-void
+int
library_init(struct library *lib, enum library_type type)
{
private_library_init(lib, type);
- arch_library_init(lib);
+
+ if (os_library_init(lib) < 0)
+ return -1;
+
+ if (arch_library_init(lib) < 0) {
+ os_library_destroy(lib);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+
+
+static void dtor_string(const char **tgt, void *data)
+{
+ free((char*)*tgt);
+}
+static int clone_vect(struct vect **to, const struct vect **from, void *data)
+{
+ *to = malloc(sizeof(struct vect));
+ if (*to == NULL)
+ return -1;
+
+ return
+ VECT_CLONE(*to, *from, const char*,
+ dict_clone_string,
+ dtor_string,
+ NULL);
+}
+static void dtor_vect(struct vect **tgt, void *data)
+{
+ VECT_DESTROY(*tgt, const char*, dtor_string, NULL);
+ free(*tgt);
+}
+
+static void
+library_exported_names_init(struct library_exported_names *names)
+{
+ DICT_INIT(&names->names,
+ const char*, uint64_t,
+ dict_hash_string, dict_eq_string, NULL);
+ DICT_INIT(&names->addrs,
+ uint64_t, struct vect*,
+ dict_hash_uint64, dict_eq_uint64, NULL);
+}
+
+static void
+library_exported_names_destroy(struct library_exported_names *names)
+{
+ DICT_DESTROY(&names->names,
+ const char*, uint64_t,
+ dtor_string, NULL, NULL);
+ DICT_DESTROY(&names->addrs,
+ uint64_t, struct vect*,
+ NULL, dtor_vect, NULL);
}
static int
-library_exported_name_clone(struct library_exported_name *retp,
- struct library_exported_name *exnm)
+library_exported_names_clone(struct library_exported_names *retp,
+ const struct library_exported_names *names)
+{
+ return (DICT_CLONE(&retp->names, &names->names,
+ const char*, uint64_t,
+ dict_clone_string, dtor_string,
+ NULL, NULL,
+ NULL) < 0 ||
+ DICT_CLONE(&retp->addrs, &names->addrs,
+ uint64_t, struct vect*,
+ NULL, NULL,
+ clone_vect, dtor_vect,
+ NULL) < 0) ? -1 : 0;
+}
+
+int library_exported_names_push(struct library_exported_names *names,
+ uint64_t addr, char *name,
+ int own_name )
{
- char *name = exnm->own_name ? strdup(exnm->name) : (char *)exnm->name;
+ // first, take ownership of the name, if it's not yet ours
+ if (!own_name)
+ name = strdup(name);
if (name == NULL)
return -1;
- retp->name = name;
- retp->own_name = exnm->own_name;
+
+ // push to the name->addr map
+ int result = DICT_INSERT(&names->names, &name, &addr);
+ if (result > 0) {
+ // This symbol is already present in the table. This library has
+ // multiple copies of this symbol (probably corresponding to
+ // different symbol versions). I should handle this gracefully
+ // at some point, but for now I simply ignore later instances of
+ // any particular symbol
+ free(name);
+ return 0;
+ }
+
+ if (result != 0)
+ return result;
+
+ // push to the addr->names map
+ // I get the alias vector. If it doesn't yet exist, I make it
+ struct vect *aliases;
+ struct vect **paliases = DICT_FIND_REF(&names->addrs,
+ &addr, struct vect*);
+
+ if (paliases == NULL) {
+ aliases = malloc(sizeof(struct vect));
+ if (aliases == NULL)
+ return -1;
+ VECT_INIT(aliases, const char*);
+ result = DICT_INSERT(&names->addrs, &addr, &aliases);
+ assert(result <= 0);
+ if (result < 0)
+ return result;
+ }
+ else
+ aliases = *paliases;
+
+ char *namedup = strdup(name);
+ if (namedup == NULL)
+ return -1;
+
+ result = vect_pushback(aliases, &namedup);
+ if (result != 0) {
+ free(namedup);
+ return result;
+ }
+
return 0;
}
+struct library_exported_names_each_alias_context
+{
+ enum callback_status (*inner_cb)(const char *, void *);
+ const char *origname;
+ void *data;
+};
+static enum callback_status
+library_exported_names_each_alias_cb(const char **name, void *data)
+{
+ struct library_exported_names_each_alias_context *context =
+ (struct library_exported_names_each_alias_context*)data;
+
+ // I do not report the original name we were asked about. Otherwise, any
+ // time the caller asks for aliases of symbol "sym", I'll always report
+ // "sym" along with any actual aliases
+ if (strcmp(*name, context->origname) == 0)
+ return CBS_CONT;
+
+ return context->inner_cb(*name, context->data);
+}
+
+const char** library_exported_names_each_alias(
+ struct library_exported_names *names,
+ const char *aliasname,
+ const char **name_start_after,
+ enum callback_status (*cb)(const char *,
+ void *),
+ void *data)
+{
+ // I have a symbol name. I look up its address, then get the list of
+ // aliased names
+ uint64_t *addr = DICT_FIND_REF(&names->names,
+ &aliasname, uint64_t);
+ if (addr == NULL)
+ return NULL;
+
+ // OK. I have an address. Get the list of symbols at this address
+ struct vect **aliases = DICT_FIND_REF(&names->addrs,
+ addr, struct vect*);
+ assert(aliases != NULL);
+
+ struct library_exported_names_each_alias_context context =
+ {.inner_cb = cb,
+ .origname = aliasname,
+ .data = data};
+ return VECT_EACH(*aliases, const char*, name_start_after,
+ library_exported_names_each_alias_cb, &context);
+}
+
+int library_exported_names_contains(struct library_exported_names *names,
+ const char *queryname)
+{
+ uint64_t *addr = DICT_FIND_REF(&names->names,
+ &queryname, uint64_t);
+ return (addr == NULL) ? 0 : 1;
+}
+
+
+
+
+
int
library_clone(struct library *retp, struct library *lib)
{
const char *soname = NULL;
const char *pathname;
- if (strdup_if_owned(&soname, lib->soname, lib->own_soname) < 0
- || strdup_if_owned(&pathname,
- lib->pathname, lib->own_pathname) < 0) {
+
+ /* Make lifetimes of strings stored at original independent of
+ * those at the clone. */
+ if (strdup_if(&soname, lib->soname, lib->own_soname) < 0
+ || strdup_if(&pathname, lib->pathname, lib->own_pathname) < 0) {
if (lib->own_soname)
free((char *)soname);
return -1;
@@ -277,9 +523,9 @@ library_clone(struct library *retp, struct library *lib)
private_library_init(retp, lib->type);
library_set_soname(retp, soname, lib->own_soname);
library_set_pathname(retp, pathname, lib->own_pathname);
- arch_library_clone(retp, lib);
retp->key = lib->key;
+ retp->should_activate_latent = lib->should_activate_latent;
/* Clone symbols. */
{
@@ -290,6 +536,7 @@ library_clone(struct library *retp, struct library *lib)
if (*nsymp == NULL
|| library_symbol_clone(*nsymp, it) < 0) {
free(*nsymp);
+ *nsymp = NULL;
fail:
/* Release what we managed to allocate. */
library_destroy(retp);
@@ -303,21 +550,24 @@ library_clone(struct library *retp, struct library *lib)
}
/* Clone exported names. */
- {
- struct library_exported_name *it;
- struct library_exported_name **nnamep = &retp->exported_names;
- for (it = lib->exported_names; it != NULL; it = it->next) {
- *nnamep = malloc(sizeof(**nnamep));
- if (*nnamep == NULL
- || library_exported_name_clone(*nnamep, it) < 0) {
- free(*nnamep);
- goto fail;
- }
- nnamep = &(*nnamep)->next;
- }
- *nnamep = NULL;
+ if (library_exported_names_clone(&retp->exported_names,
+ &lib->exported_names) != 0)
+ goto fail;
+
+ if (os_library_clone(retp, lib) < 0)
+ goto fail;
+
+ if (arch_library_clone(retp, lib) < 0) {
+ os_library_destroy(retp);
+ goto fail;
}
+#if defined(HAVE_LIBDW)
+ /* Wipe DWFL_MODULE, leave it to proc_add_library to
+ * initialize. */
+ lib->dwfl_module = NULL;
+#endif
+
return 0;
}
@@ -328,6 +578,8 @@ library_destroy(struct library *lib)
return;
arch_library_destroy(lib);
+ os_library_destroy(lib);
+
library_set_soname(lib, NULL, 0);
library_set_pathname(lib, NULL, 0);
@@ -340,14 +592,7 @@ library_destroy(struct library *lib)
}
/* Release exported names. */
- struct library_exported_name *it;
- for (it = lib->exported_names; it != NULL; ) {
- struct library_exported_name *next = it->next;
- if (it->own_name)
- free((char *)it->name);
- free(it);
- it = next;
- }
+ library_exported_names_destroy(&lib->exported_names);
}
void
@@ -412,7 +657,7 @@ library_add_symbol(struct library *lib, struct library_symbol *first)
}
enum callback_status
-library_named_cb(struct Process *proc, struct library *lib, void *name)
+library_named_cb(struct process *proc, struct library *lib, void *name)
{
if (name == lib->soname
|| strcmp(lib->soname, (char *)name) == 0)
@@ -422,7 +667,7 @@ library_named_cb(struct Process *proc, struct library *lib, void *name)
}
enum callback_status
-library_with_key_cb(struct Process *proc, struct library *lib, void *keyp)
+library_with_key_cb(struct process *proc, struct library *lib, void *keyp)
{
return lib->key == *(arch_addr_t *)keyp ? CBS_STOP : CBS_CONT;
}