summaryrefslogtreecommitdiff
path: root/ltrace-elf.c
diff options
context:
space:
mode:
Diffstat (limited to 'ltrace-elf.c')
-rw-r--r--ltrace-elf.c800
1 files changed, 561 insertions, 239 deletions
diff --git a/ltrace-elf.c b/ltrace-elf.c
index c571d2a..24d02f0 100644
--- a/ltrace-elf.c
+++ b/ltrace-elf.c
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2006,2010,2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2006,2010,2011,2012,2013 Petr Machata, Red Hat Inc.
* Copyright (C) 2010 Zachary T Welch, CodeSourcery
* Copyright (C) 2010 Joe Damato
* Copyright (C) 1997,1998,2001,2004,2007,2008,2009 Juan Cespedes
@@ -36,10 +36,12 @@
#include <gelf.h>
#include <inttypes.h>
#include <search.h>
+#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <strings.h>
#include <unistd.h>
#include "backend.h"
@@ -63,49 +65,50 @@ arch_elf_destroy(struct ltelf *lte)
}
#endif
-int
-default_elf_add_plt_entry(struct Process *proc, struct ltelf *lte,
- const char *a_name, GElf_Rela *rela, size_t ndx,
- struct library_symbol **ret)
+#ifndef OS_HAVE_ADD_PLT_ENTRY
+enum plt_status
+os_elf_add_plt_entry(struct process *proc, struct ltelf *lte,
+ const char *a_name, GElf_Rela *rela, size_t ndx,
+ struct library_symbol **ret)
{
- char *name = strdup(a_name);
- if (name == NULL) {
- fail_message:
- fprintf(stderr, "Couldn't create symbol for PLT entry: %s\n",
- strerror(errno));
- fail:
- free(name);
- return -1;
- }
-
- GElf_Addr addr = arch_plt_sym_val(lte, ndx, rela);
-
- struct library_symbol *libsym = malloc(sizeof(*libsym));
- if (libsym == NULL)
- goto fail_message;
-
- /* XXX The double cast should be removed when
- * arch_addr_t becomes integral type. */
- arch_addr_t taddr = (arch_addr_t)
- (uintptr_t)(addr + lte->bias);
-
- if (library_symbol_init(libsym, taddr, name, 1, LS_TOPLT_EXEC) < 0) {
- free(libsym);
- goto fail;
- }
-
- libsym->next = *ret;
- *ret = libsym;
- return 0;
+ return PLT_DEFAULT;
}
+#endif
#ifndef ARCH_HAVE_ADD_PLT_ENTRY
enum plt_status
-arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte,
+arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte,
const char *a_name, GElf_Rela *rela, size_t ndx,
struct library_symbol **ret)
{
- return plt_default;
+ return PLT_DEFAULT;
+}
+#endif
+
+#ifndef OS_HAVE_ADD_FUNC_ENTRY
+enum plt_status
+os_elf_add_func_entry(struct process *proc, struct ltelf *lte,
+ const GElf_Sym *sym,
+ arch_addr_t addr, const char *name,
+ struct library_symbol **ret)
+{
+ if (GELF_ST_TYPE(sym->st_info) != STT_FUNC) {
+ *ret = NULL;
+ return PLT_OK;
+ } else {
+ return PLT_DEFAULT;
+ }
+}
+#endif
+
+#ifndef ARCH_HAVE_ADD_FUNC_ENTRY
+enum plt_status
+arch_elf_add_func_entry(struct process *proc, struct ltelf *lte,
+ const GElf_Sym *sym,
+ arch_addr_t addr, const char *name,
+ struct library_symbol **ret)
+{
+ return PLT_DEFAULT;
}
#endif
@@ -140,8 +143,9 @@ elf_get_section_if(struct ltelf *lte, Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr,
return 0;
}
}
- return -1;
+ *tgt_sec = NULL;
+ return 0;
}
static int
@@ -202,23 +206,83 @@ elf_get_section_named(struct ltelf *lte, const char *name,
&name_p, &data);
}
-static int
-need_data(Elf_Data *data, GElf_Xword offset, GElf_Xword size)
+static struct elf_each_symbol_t
+each_symbol_in(Elf_Data *symtab, const char *strtab, size_t count,
+ unsigned i,
+ enum callback_status (*cb)(GElf_Sym *symbol,
+ const char *name, void *data),
+ void *data)
+{
+ for (; i < count; ++i) {
+ GElf_Sym sym;
+ if (gelf_getsym(symtab, i, &sym) == NULL)
+ return (struct elf_each_symbol_t){ i, -2 };
+
+ switch (cb(&sym, strtab + sym.st_name, data)) {
+ case CBS_FAIL:
+ return (struct elf_each_symbol_t){ i, -1 };
+ case CBS_STOP:
+ return (struct elf_each_symbol_t){ i + 1, 0 };
+ case CBS_CONT:
+ break;
+ }
+ }
+
+ return (struct elf_each_symbol_t){ 0, 0 };
+}
+
+/* N.B.: gelf_getsym takes integer argument. Since negative values
+ * are invalid as indices, we can use the extra bit to encode which
+ * symbol table we are looking into. ltrace currently doesn't handle
+ * more than two symbol tables anyway, nor does it handle the xindex
+ * stuff. */
+struct elf_each_symbol_t
+elf_each_symbol(struct ltelf *lte, unsigned start_after,
+ enum callback_status (*cb)(GElf_Sym *symbol,
+ const char *name, void *data),
+ void *data)
+{
+ unsigned index = start_after == 0 ? 0 : start_after >> 1;
+
+ /* Go through static symbol table first. */
+ if ((start_after & 0x1) == 0) {
+ struct elf_each_symbol_t st
+ = each_symbol_in(lte->symtab, lte->strtab,
+ lte->symtab_count, index, cb, data);
+
+ /* If the iteration stopped prematurely, bail out. */
+ if (st.restart != 0)
+ return ((struct elf_each_symbol_t)
+ { st.restart << 1, st.status });
+ }
+
+ struct elf_each_symbol_t st
+ = each_symbol_in(lte->dynsym, lte->dynstr, lte->dynsym_count,
+ index, cb, data);
+ if (st.restart != 0)
+ return ((struct elf_each_symbol_t)
+ { st.restart << 1 | 0x1, st.status });
+
+ return (struct elf_each_symbol_t){ 0, 0 };
+}
+
+int
+elf_can_read_next(Elf_Data *data, GElf_Xword offset, GElf_Xword size)
{
assert(data != NULL);
if (data->d_size < size || offset > data->d_size - size) {
debug(1, "Not enough data to read %"PRId64"-byte value"
" at offset %"PRId64".", size, offset);
- return -1;
+ return 0;
}
- return 0;
+ return 1;
}
#define DEF_READER(NAME, SIZE) \
int \
NAME(Elf_Data *data, GElf_Xword offset, uint##SIZE##_t *retp) \
{ \
- if (!need_data(data, offset, SIZE / 8) < 0) \
+ if (!elf_can_read_next(data, offset, SIZE / 8)) \
return -1; \
\
if (data->d_buf == NULL) /* NODATA section */ { \
@@ -235,18 +299,73 @@ need_data(Elf_Data *data, GElf_Xword offset, GElf_Xword size)
return 0; \
}
+DEF_READER(elf_read_u8, 8)
DEF_READER(elf_read_u16, 16)
DEF_READER(elf_read_u32, 32)
DEF_READER(elf_read_u64, 64)
#undef DEF_READER
+#define DEF_READER(NAME, SIZE) \
+ int \
+ NAME(Elf_Data *data, GElf_Xword *offset, uint##SIZE##_t *retp) \
+ { \
+ int rc = elf_read_u##SIZE(data, *offset, retp); \
+ if (rc < 0) \
+ return rc; \
+ *offset += SIZE / 8; \
+ return 0; \
+ }
+
+DEF_READER(elf_read_next_u8, 8)
+DEF_READER(elf_read_next_u16, 16)
+DEF_READER(elf_read_next_u32, 32)
+DEF_READER(elf_read_next_u64, 64)
+
+#undef DEF_READER
+
+int
+elf_read_next_uleb128(Elf_Data *data, GElf_Xword *offset, uint64_t *retp)
+{
+ uint64_t result = 0;
+ int shift = 0;
+ int size = 8 * sizeof result;
+
+ while (1) {
+ uint8_t byte;
+ if (elf_read_next_u8(data, offset, &byte) < 0)
+ return -1;
+
+ uint8_t payload = byte & 0x7f;
+ result |= (uint64_t)payload << shift;
+ shift += 7;
+ if (shift > size && byte != 0x1)
+ return -1;
+ if ((byte & 0x80) == 0)
+ break;
+ }
+
+ if (retp != NULL)
+ *retp = result;
+ return 0;
+}
+
int
-open_elf(struct ltelf *lte, const char *filename)
+elf_read_uleb128(Elf_Data *data, GElf_Xword offset, uint64_t *retp)
{
+ return elf_read_next_uleb128(data, &offset, retp);
+}
+
+int
+ltelf_init(struct ltelf *lte, const char *filename)
+{
+ memset(lte, 0, sizeof *lte);
lte->fd = open(filename, O_RDONLY);
- if (lte->fd == -1)
+ if (lte->fd == -1) {
+ fprintf(stderr, "Can't open %s: %s\n", filename,
+ strerror(errno));
return 1;
+ }
elf_version(EV_CURRENT);
@@ -293,9 +412,20 @@ open_elf(struct ltelf *lte, const char *filename)
exit(EXIT_FAILURE);
}
+ VECT_INIT(&lte->plt_relocs, GElf_Rela);
+
return 0;
}
+void
+ltelf_destroy(struct ltelf *lte)
+{
+ debug(DEBUG_FUNCTION, "close_elf()");
+ elf_end(lte->elf);
+ close(lte->fd);
+ VECT_DESTROY(&lte->plt_relocs, GElf_Rela, NULL, NULL);
+}
+
static void
read_symbol_table(struct ltelf *lte, const char *filename,
Elf_Scn *scn, GElf_Shdr *shdr, const char *name,
@@ -316,7 +446,7 @@ read_symbol_table(struct ltelf *lte, const char *filename,
if (scn == NULL || gelf_getshdr(scn, &shdr2) == NULL) {
fprintf(stderr, "Couldn't get header of section"
" #%d from \"%s\": %s\n",
- shdr2.sh_link, filename, elf_errmsg(-1));
+ shdr->sh_link, filename, elf_errmsg(-1));
exit(EXIT_FAILURE);
}
@@ -333,13 +463,118 @@ read_symbol_table(struct ltelf *lte, const char *filename,
}
static int
-do_init_elf(struct ltelf *lte, const char *filename)
+rel_to_rela(struct ltelf *lte, const GElf_Rel *rel, GElf_Rela *rela)
+{
+ rela->r_offset = rel->r_offset;
+ rela->r_info = rel->r_info;
+
+ Elf_Scn *sec;
+ GElf_Shdr shdr;
+ if (elf_get_section_covering(lte, rel->r_offset, &sec, &shdr) < 0
+ || sec == NULL)
+ return -1;
+
+ Elf_Data *data = elf_loaddata(sec, &shdr);
+ if (data == NULL)
+ return -1;
+
+ GElf_Xword offset = rel->r_offset - shdr.sh_addr - data->d_off;
+ uint64_t value;
+ if (lte->ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
+ uint32_t tmp;
+ if (elf_read_u32(data, offset, &tmp) < 0)
+ return -1;
+ value = tmp;
+ } else if (elf_read_u64(data, offset, &value) < 0) {
+ return -1;
+ }
+
+ rela->r_addend = value;
+ return 0;
+}
+
+int
+elf_read_relocs(struct ltelf *lte, Elf_Scn *scn, GElf_Shdr *shdr,
+ struct vect *rela_vec)
+{
+ if (vect_reserve_additional(rela_vec, lte->ehdr.e_shnum) < 0)
+ return -1;
+
+ Elf_Data *relplt = elf_loaddata(scn, shdr);
+ if (relplt == NULL) {
+ fprintf(stderr, "Couldn't load .rel*.plt data.\n");
+ return -1;
+ }
+
+ if ((shdr->sh_size % shdr->sh_entsize) != 0) {
+ fprintf(stderr, ".rel*.plt size (%" PRIx64 "d) not a multiple "
+ "of its sh_entsize (%" PRIx64 "d).\n",
+ shdr->sh_size, shdr->sh_entsize);
+ return -1;
+ }
+
+ GElf_Xword relplt_count = shdr->sh_size / shdr->sh_entsize;
+ GElf_Xword i;
+ for (i = 0; i < relplt_count; ++i) {
+ GElf_Rela rela;
+ if (relplt->d_type == ELF_T_REL) {
+ GElf_Rel rel;
+ if (gelf_getrel(relplt, i, &rel) == NULL
+ || rel_to_rela(lte, &rel, &rela) < 0)
+ return -1;
+
+ } else if (gelf_getrela(relplt, i, &rela) == NULL) {
+ return -1;
+ }
+
+ if (VECT_PUSHBACK(rela_vec, &rela) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+elf_load_dynamic_entry(struct ltelf *lte, int tag, GElf_Addr *valuep)
+{
+ Elf_Scn *scn;
+ GElf_Shdr shdr;
+ if (elf_get_section_type(lte, SHT_DYNAMIC, &scn, &shdr) < 0
+ || scn == NULL) {
+ fail:
+ fprintf(stderr, "Couldn't get SHT_DYNAMIC: %s\n",
+ elf_errmsg(-1));
+ return -1;
+ }
+
+ Elf_Data *data = elf_loaddata(scn, &shdr);
+ if (data == NULL)
+ goto fail;
+
+ size_t j;
+ for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) {
+ GElf_Dyn dyn;
+ if (gelf_getdyn(data, j, &dyn) == NULL)
+ goto fail;
+
+ if(dyn.d_tag == tag) {
+ *valuep = dyn.d_un.d_ptr;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int
+ltelf_read_elf(struct ltelf *lte, const char *filename)
{
int i;
GElf_Addr relplt_addr = 0;
GElf_Addr soname_offset = 0;
+ GElf_Xword relplt_size = 0;
- debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename);
+ debug(DEBUG_FUNCTION, "ltelf_read_elf(filename=%s)", filename);
debug(1, "Reading ELF from %s...", filename);
for (i = 1; i < lte->ehdr.e_shnum; ++i) {
@@ -398,7 +633,7 @@ do_init_elf(struct ltelf *lte, const char *filename)
if (dyn.d_tag == DT_JMPREL)
relplt_addr = dyn.d_un.d_ptr;
else if (dyn.d_tag == DT_PLTRELSZ)
- lte->relplt_size = dyn.d_un.d_val;
+ relplt_size = dyn.d_un.d_val;
else if (dyn.d_tag == DT_SONAME)
soname_offset = dyn.d_un.d_val;
}
@@ -431,14 +666,9 @@ do_init_elf(struct ltelf *lte, const char *filename)
if (!relplt_addr || !lte->plt_addr) {
debug(1, "%s has no PLT relocations", filename);
- lte->relplt = NULL;
- lte->relplt_count = 0;
- } else if (lte->relplt_size == 0) {
+ } else if (relplt_size == 0) {
debug(1, "%s has unknown PLT size", filename);
- lte->relplt = NULL;
- lte->relplt_count = 0;
} else {
-
for (i = 1; i < lte->ehdr.e_shnum; ++i) {
Elf_Scn *scn;
GElf_Shdr shdr;
@@ -451,12 +681,9 @@ do_init_elf(struct ltelf *lte, const char *filename)
exit(EXIT_FAILURE);
}
if (shdr.sh_addr == relplt_addr
- && shdr.sh_size == lte->relplt_size) {
- lte->relplt = elf_getdata(scn, NULL);
- lte->relplt_count =
- shdr.sh_size / shdr.sh_entsize;
- if (lte->relplt == NULL
- || elf_getdata(scn, lte->relplt) != NULL) {
+ && shdr.sh_size == relplt_size) {
+ if (elf_read_relocs(lte, scn, &shdr,
+ &lte->plt_relocs) < 0) {
fprintf(stderr, "Couldn't get .rel*.plt"
" data from \"%s\": %s\n",
filename, elf_errmsg(-1));
@@ -472,9 +699,9 @@ do_init_elf(struct ltelf *lte, const char *filename)
filename);
exit(EXIT_FAILURE);
}
-
- debug(1, "%s %zd PLT relocations", filename, lte->relplt_count);
}
+ debug(1, "%s %zd PLT relocations", filename,
+ vect_size(&lte->plt_relocs));
if (soname_offset != 0)
lte->soname = lte->dynstr + soname_offset;
@@ -482,53 +709,76 @@ do_init_elf(struct ltelf *lte, const char *filename)
return 0;
}
-void
-do_close_elf(struct ltelf *lte)
+#ifndef ARCH_HAVE_GET_SYMINFO
+int
+arch_get_sym_info(struct ltelf *lte, const char *filename,
+ size_t sym_index, GElf_Rela *rela, GElf_Sym *sym)
{
- debug(DEBUG_FUNCTION, "do_close_elf()");
- arch_elf_destroy(lte);
- elf_end(lte->elf);
- close(lte->fd);
+ return gelf_getsym(lte->dynsym,
+ ELF64_R_SYM(rela->r_info), sym) != NULL ? 0 : -1;
}
+#endif
int
-elf_get_sym_info(struct ltelf *lte, const char *filename,
- size_t sym_index, GElf_Rela *rela, GElf_Sym *sym)
-{
- int i = sym_index;
- GElf_Rel rel;
- void *ret;
-
- if (lte->relplt->d_type == ELF_T_REL) {
- ret = gelf_getrel(lte->relplt, i, &rel);
- rela->r_offset = rel.r_offset;
- rela->r_info = rel.r_info;
- rela->r_addend = 0;
- } else {
- ret = gelf_getrela(lte->relplt, i, rela);
+default_elf_add_plt_entry(struct process *proc, struct ltelf *lte,
+ const char *a_name, GElf_Rela *rela, size_t ndx,
+ struct library_symbol **ret)
+{
+ char *name = strdup(a_name);
+ if (name == NULL) {
+ fail_message:
+ fprintf(stderr, "Couldn't create symbol for PLT entry: %s\n",
+ strerror(errno));
+ fail:
+ free(name);
+ return -1;
}
- if (ret == NULL
- || ELF64_R_SYM(rela->r_info) >= lte->dynsym_count
- || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela->r_info),
- sym) == NULL) {
- fprintf(stderr,
- "Couldn't get relocation from \"%s\": %s\n",
- filename, elf_errmsg(-1));
- exit(EXIT_FAILURE);
+ GElf_Addr addr = arch_plt_sym_val(lte, ndx, rela);
+
+ struct library_symbol *libsym = malloc(sizeof(*libsym));
+ if (libsym == NULL)
+ goto fail_message;
+
+ /* XXX The double cast should be removed when
+ * arch_addr_t becomes integral type. */
+ arch_addr_t taddr = (arch_addr_t)
+ (uintptr_t)(addr + lte->bias);
+
+ if (library_symbol_init(libsym, taddr, name, 1, LS_TOPLT_EXEC) < 0) {
+ free(libsym);
+ goto fail;
}
+ libsym->next = *ret;
+ *ret = libsym;
return 0;
}
-#ifndef ARCH_HAVE_GET_SYMINFO
int
-arch_get_sym_info(struct ltelf *lte, const char *filename,
- size_t sym_index, GElf_Rela *rela, GElf_Sym *sym)
+elf_add_plt_entry(struct process *proc, struct ltelf *lte,
+ const char *name, GElf_Rela *rela, size_t idx,
+ struct library_symbol **ret)
{
- return elf_get_sym_info(lte, filename, sym_index, rela, sym);
+ enum plt_status plts
+ = arch_elf_add_plt_entry(proc, lte, name, rela, idx, ret);
+
+ if (plts == PLT_DEFAULT)
+ plts = os_elf_add_plt_entry(proc, lte, name, rela, idx, ret);
+
+ switch (plts) {
+ case PLT_DEFAULT:
+ return default_elf_add_plt_entry(proc, lte, name,
+ rela, idx, ret);
+ case PLT_FAIL:
+ return -1;
+ case PLT_OK:
+ return 0;
+ }
+
+ assert(! "Invalid return from X_elf_add_plt_entry!");
+ abort();
}
-#endif
static void
mark_chain_latent(struct library_symbol *libsym)
@@ -539,53 +789,87 @@ mark_chain_latent(struct library_symbol *libsym)
}
}
+static void
+filter_symbol_chain(struct filter *filter,
+ struct library_symbol **libsymp, struct library *lib)
+{
+ assert(libsymp != NULL);
+ struct library_symbol **ptr = libsymp;
+ while (*ptr != NULL) {
+ if (filter_matches_symbol(filter, (*ptr)->name, lib)) {
+ ptr = &(*ptr)->next;
+ } else {
+ struct library_symbol *sym = *ptr;
+ *ptr = (*ptr)->next;
+ library_symbol_destroy(sym);
+ free(sym);
+ }
+ }
+}
+
static int
-populate_plt(struct Process *proc, const char *filename,
- struct ltelf *lte, struct library *lib,
- int latent_plts)
+populate_plt(struct process *proc, const char *filename,
+ struct ltelf *lte, struct library *lib)
{
+ const bool latent_plts = options.export_filter != NULL;
+ const size_t count = vect_size(&lte->plt_relocs);
+
size_t i;
- for (i = 0; i < lte->relplt_count; ++i) {
- GElf_Rela rela;
+ for (i = 0; i < count; ++i) {
+ GElf_Rela *rela = VECT_ELEMENT(&lte->plt_relocs, GElf_Rela, i);
GElf_Sym sym;
- if (arch_get_sym_info(lte, filename, i, &rela, &sym) < 0)
+ switch (arch_get_sym_info(lte, filename, i, rela, &sym)) {
+ default:
+ fprintf(stderr,
+ "Couldn't get relocation for symbol #%zd"
+ " from \"%s\": %s\n",
+ i, filename, elf_errmsg(-1));
+ /* Fall through. */
+ case 1:
continue; /* Skip this entry. */
+ case 0:
+ break;
+ }
char const *name = lte->dynstr + sym.st_name;
-
- /* If the symbol wasn't matched, reject it, unless we
- * need to keep latent PLT breakpoints for tracing
- * exports. */
int matched = filter_matches_symbol(options.plt_filter,
name, lib);
- if (!matched && !latent_plts)
- continue;
struct library_symbol *libsym = NULL;
- switch (arch_elf_add_plt_entry(proc, lte, name,
- &rela, i, &libsym)) {
- case plt_default:
- if (default_elf_add_plt_entry(proc, lte, name,
- &rela, i, &libsym) < 0)
- /* fall-through */
- case plt_fail:
- return -1;
- /* fall-through */
- case plt_ok:
- if (libsym != NULL) {
- /* If we are adding those symbols just
- * for tracing exports, mark them all
- * latent. */
- if (!matched)
- mark_chain_latent(libsym);
- library_add_symbol(lib, libsym);
- }
+ if (elf_add_plt_entry(proc, lte, name, rela, i, &libsym) < 0)
+ return -1;
+
+ /* If we didn't match the PLT entry, filter the chain
+ * to only include the matching symbols (but include
+ * all if we are adding latent symbols) to allow
+ * backends to override the PLT symbol's name. */
+
+ if (! matched && ! latent_plts)
+ filter_symbol_chain(options.plt_filter, &libsym, lib);
+
+ if (libsym != NULL) {
+ /* If we are adding those symbols just for
+ * tracing exports, mark them all latent. */
+ if (! matched && latent_plts)
+ mark_chain_latent(libsym);
+ library_add_symbol(lib, libsym);
}
}
return 0;
}
+void
+delete_symbol_chain(struct library_symbol *libsym)
+{
+ while (libsym != NULL) {
+ struct library_symbol *tmp = libsym->next;
+ library_symbol_destroy(libsym);
+ free(libsym);
+ libsym = tmp;
+ }
+}
+
/* When -x rules result in request to trace several aliases, we only
* want to add such symbol once. The only way that those symbols
* differ in is their name, e.g. in glibc you have __GI___libc_free,
@@ -614,21 +898,17 @@ symbol_with_address(struct library_symbol *sym, void *addrptr)
}
static int
-populate_this_symtab(struct Process *proc, const char *filename,
+populate_this_symtab(struct process *proc, const char *filename,
struct ltelf *lte, struct library *lib,
- Elf_Data *symtab, const char *strtab, size_t size,
- struct library_exported_name **names)
+ Elf_Data *symtab, const char *strtab, size_t count,
+ struct library_exported_names *names,
+ bool only_exported_names)
{
- /* If a valid NAMES is passed, we pass in *NAMES a list of
- * symbol names that this library exports. */
- if (names != NULL)
- *names = NULL;
-
/* Using sorted array would be arguably better, but this
* should be well enough for the number of symbols that we
* typically deal with. */
size_t num_symbols = 0;
- struct unique_symbol *symbols = malloc(sizeof(*symbols) * size);
+ struct unique_symbol *symbols = malloc(sizeof(*symbols) * count);
if (symbols == NULL) {
fprintf(stderr, "couldn't insert symbols for -x: %s\n",
strerror(errno));
@@ -639,28 +919,26 @@ populate_this_symtab(struct Process *proc, const char *filename,
size_t i;
for (i = 1; i < lte->ehdr.e_shnum; ++i) {
Elf_Scn *scn = elf_getscn(lte->elf, i);
- if (scn == NULL)
- continue;
GElf_Shdr shdr;
- if (gelf_getshdr(scn, &shdr) == NULL)
- continue;
- secflags[i] = shdr.sh_flags;
+ if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
+ secflags[i] = 0;
+ else
+ secflags[i] = shdr.sh_flags;
}
- for (i = 0; i < size; ++i) {
+ for (i = 0; i < count; ++i) {
GElf_Sym sym;
if (gelf_getsym(symtab, i, &sym) == NULL) {
- fail:
fprintf(stderr,
"couldn't get symbol #%zd from %s: %s\n",
i, filename, elf_errmsg(-1));
continue;
}
- /* XXX support IFUNC as well. */
- if (GELF_ST_TYPE(sym.st_info) != STT_FUNC
- || sym.st_value == 0
- || sym.st_shndx == STN_UNDEF)
+ if (sym.st_value == 0 || sym.st_shndx == STN_UNDEF
+ /* Also ignore any special values besides direct
+ * section references. */
+ || sym.st_shndx >= lte->ehdr.e_shnum)
continue;
/* Find symbol name and snip version. */
@@ -674,24 +952,23 @@ populate_this_symtab(struct Process *proc, const char *filename,
name[len] = 0;
/* If we are interested in exports, store this name. */
- char *name_copy = NULL;
if (names != NULL) {
- struct library_exported_name *export = NULL;
- name_copy = strdup(name);
-
- if (name_copy == NULL
- || (export = malloc(sizeof(*export))) == NULL) {
- free(name_copy);
+ char *name_copy = strdup(name);
+ if (name_copy == NULL ||
+ library_exported_names_push(names,
+ sym.st_value,
+ name_copy, 1) != 0)
+ {
fprintf(stderr, "Couldn't store symbol %s. "
"Tracing may be incomplete.\n", name);
- } else {
- export->name = name_copy;
- export->own_name = 1;
- export->next = *names;
- *names = export;
}
}
+ /* If we're only dealing with the exported names list, there's
+ * nothing left to do with this symbol */
+ if (only_exported_names)
+ continue;
+
/* If the symbol is not matched, skip it. We already
* stored it to export list above. */
if (!filter_matches_symbol(options.static_filter, name, lib))
@@ -714,45 +991,94 @@ populate_this_symtab(struct Process *proc, const char *filename,
continue;
}
- char *full_name;
- int own_full_name = 1;
- if (name_copy == NULL) {
- full_name = strdup(name);
- if (full_name == NULL)
- goto fail;
- } else {
- full_name = name_copy;
- own_full_name = 0;
+ char *full_name = strdup(name);
+ if (full_name == NULL) {
+ fprintf(stderr, "couldn't copy name of %s@%s: %s\n",
+ name, lib->soname, strerror(errno));
+ continue;
}
- /* Look whether we already have a symbol for this
- * address. If not, add this one. */
- struct unique_symbol key = { naddr, NULL };
- struct unique_symbol *unique
- = lsearch(&key, symbols, &num_symbols,
- sizeof(*symbols), &unique_symbol_cmp);
-
- if (unique->libsym == NULL) {
- struct library_symbol *libsym = malloc(sizeof(*libsym));
- if (libsym == NULL
- || library_symbol_init(libsym, naddr,
- full_name, own_full_name,
+ struct library_symbol *libsym = NULL;
+ enum plt_status plts
+ = arch_elf_add_func_entry(proc, lte, &sym,
+ naddr, full_name, &libsym);
+ if (plts == PLT_DEFAULT)
+ plts = os_elf_add_func_entry(proc, lte, &sym,
+ naddr, full_name, &libsym);
+
+ switch (plts) {
+ case PLT_DEFAULT:;
+ /* Put the default symbol to the chain. */
+ struct library_symbol *tmp = malloc(sizeof *tmp);
+ if (tmp == NULL
+ || library_symbol_init(tmp, naddr, full_name, 1,
LS_TOPLT_NONE) < 0) {
- --num_symbols;
- goto fail;
+ free(tmp);
+
+ /* Either add the whole bunch, or none
+ * of it. Note that for PLT_FAIL we
+ * don't do this--it's the callee's
+ * job to clean up after itself before
+ * it bails out. */
+ delete_symbol_chain(libsym);
+ libsym = NULL;
+
+ case PLT_FAIL:
+ fprintf(stderr, "Couldn't add symbol %s@%s "
+ "for tracing.\n", name, lib->soname);
+
+ break;
}
- unique->libsym = libsym;
- unique->addr = naddr;
- } else if (strlen(full_name) < strlen(unique->libsym->name)) {
- library_symbol_set_name(unique->libsym,
- full_name, own_full_name);
+ full_name = NULL;
+ tmp->next = libsym;
+ libsym = tmp;
+ break;
- } else if (own_full_name) {
- free(full_name);
+ case PLT_OK:
+ break;
+ }
+
+ free(full_name);
+
+ struct library_symbol *tmp;
+ for (tmp = libsym; tmp != NULL; ) {
+ /* Look whether we already have a symbol for
+ * this address. If not, add this one. If
+ * yes, look if we should pick the new symbol
+ * name. */
+
+ struct unique_symbol key = { tmp->enter_addr, NULL };
+ struct unique_symbol *unique
+ = lsearch(&key, symbols, &num_symbols,
+ sizeof *symbols, &unique_symbol_cmp);
+
+ if (unique->libsym == NULL) {
+ unique->libsym = tmp;
+ unique->addr = tmp->enter_addr;
+ tmp = tmp->next;
+ unique->libsym->next = NULL;
+ } else {
+ if (strlen(tmp->name)
+ < strlen(unique->libsym->name)) {
+ library_symbol_set_name
+ (unique->libsym, tmp->name, 1);
+ tmp->name = NULL;
+ }
+ struct library_symbol *next = tmp->next;
+ library_symbol_destroy(tmp);
+ free(tmp);
+ tmp = next;
+ }
}
}
+ /* If we're only dealing with the exported names list, there's nothing
+ * left to do */
+ if (only_exported_names)
+ return 0;
+
+
/* Now we do the union of this set of unique symbols with
* what's already in the library. */
for (i = 0; i < num_symbols; ++i) {
@@ -777,7 +1103,7 @@ populate_this_symtab(struct Process *proc, const char *filename,
}
static int
-populate_symtab(struct Process *proc, const char *filename,
+populate_symtab(struct process *proc, const char *filename,
struct ltelf *lte, struct library *lib,
int symtabs, int exports)
{
@@ -785,28 +1111,28 @@ populate_symtab(struct Process *proc, const char *filename,
if (symtabs && lte->symtab != NULL && lte->strtab != NULL
&& (status = populate_this_symtab(proc, filename, lte, lib,
lte->symtab, lte->strtab,
- lte->symtab_count, NULL)) < 0)
+ lte->symtab_count, NULL,
+ false)) < 0)
return status;
/* Check whether we want to trace symbols implemented by this
* library (-l). */
- struct library_exported_name **names = NULL;
- if (exports) {
- debug(DEBUG_FUNCTION, "-l matches %s", lib->soname);
- names = &lib->exported_names;
- }
+ struct library_exported_names *names = &lib->exported_names;
+ lib->should_activate_latent = exports != 0;
+ bool only_exported_names = symtabs == 0 && exports == 0;
return populate_this_symtab(proc, filename, lte, lib,
lte->dynsym, lte->dynstr,
- lte->dynsym_count, names);
+ lte->dynsym_count, names,
+ only_exported_names);
}
static int
-read_module(struct library *lib, struct Process *proc,
+read_module(struct library *lib, struct process *proc,
const char *filename, GElf_Addr bias, int main)
{
- struct ltelf lte = {};
- if (open_elf(&lte, filename) < 0)
+ struct ltelf lte;
+ if (ltelf_init(&lte, filename) < 0)
return -1;
/* XXX When we abstract ABI into a module, this should instead
@@ -814,8 +1140,8 @@ read_module(struct library *lib, struct Process *proc,
*
* proc->abi = arch_get_abi(lte.ehdr);
*
- * The code in open_elf needs to be replaced by this logic.
- * Be warned that libltrace.c calls open_elf as well to
+ * The code in ltelf_init needs to be replaced by this logic.
+ * Be warned that libltrace.c calls ltelf_init as well to
* determine whether ABI is supported. This is to get
* reasonable error messages when trying to run 64-bit binary
* with 32-bit ltrace. It is desirable to preserve this. */
@@ -830,6 +1156,8 @@ read_module(struct library *lib, struct Process *proc,
if (process_get_entry(proc, &entry, NULL) < 0) {
fprintf(stderr, "Couldn't find entry of PIE %s\n",
filename);
+ fail:
+ ltelf_destroy(&lte);
return -1;
}
/* XXX The double cast should be removed when
@@ -854,26 +1182,25 @@ read_module(struct library *lib, struct Process *proc,
fprintf(stderr,
"Couldn't determine base address of %s\n",
filename);
- return -1;
+ goto fail;
}
}
- if (do_init_elf(&lte, filename) < 0)
- return -1;
+ if (ltelf_read_elf(&lte, filename) < 0)
+ goto fail;
if (arch_elf_init(&lte, lib) < 0) {
fprintf(stderr, "Backend initialization failed.\n");
- return -1;
+ goto fail;
}
- int status = 0;
if (lib == NULL)
goto fail;
/* Note that we set soname and pathname as soon as they are
* allocated, so in case of further errors, this get released
- * when LIB is release, which should happen in the caller when
- * we return error. */
+ * when LIB is released, which should happen in the caller
+ * when we return error. */
if (lib->pathname == NULL) {
char *pathname = strdup(filename);
@@ -888,8 +1215,10 @@ read_module(struct library *lib, struct Process *proc,
goto fail;
library_set_soname(lib, soname, 1);
} else {
- const char *soname = rindex(lib->pathname, '/') + 1;
- if (soname == NULL)
+ const char *soname = rindex(lib->pathname, '/');
+ if (soname != NULL)
+ soname += 1;
+ else
soname = lib->pathname;
library_set_soname(lib, soname, 0);
}
@@ -908,41 +1237,34 @@ read_module(struct library *lib, struct Process *proc,
* arch_addr_t becomes integral type. */
lib->dyn_addr = (arch_addr_t)(uintptr_t)lte.dyn_addr;
- /* There are two reasons that we need to inspect symbol tables
- * or populate PLT entries. Either the user requested
- * corresponding tracing features (respectively -x and -e), or
- * they requested tracing exported symbols (-l).
+ /* There are several reasons that we need to inspect symbol tables or
+ * populate PLT entries. The user may have requested corresponding
+ * tracing features (respectively -x and -e), or they requested tracing
+ * exported symbols (-l). We also do this to resolve symbol aliases
*
- * In the latter case we need to keep even those PLT slots
- * that are not requested by -e (but we keep them latent). We
- * also need to inspect .dynsym to find what exports this
- * library provide, to turn on existing latent PLT
- * entries. */
+ * In the case of -l, we need to keep even those PLT slots that are not
+ * requested by -e (but we keep them latent). We also need to inspect
+ * .dynsym to find what exports this library provide, to turn on
+ * existing latent PLT entries. */
int plts = filter_matches_library(options.plt_filter, lib);
if ((plts || options.export_filter != NULL)
- && populate_plt(proc, filename, &lte, lib,
- options.export_filter != NULL) < 0)
+ && populate_plt(proc, filename, &lte, lib) < 0)
goto fail;
int exports = filter_matches_library(options.export_filter, lib);
int symtabs = filter_matches_library(options.static_filter, lib);
- if ((symtabs || exports)
- && populate_symtab(proc, filename, &lte, lib,
- symtabs, exports) < 0)
+ if (populate_symtab(proc, filename, &lte, lib,
+ symtabs, exports) < 0)
goto fail;
-done:
- do_close_elf(&lte);
- return status;
-
-fail:
- status = -1;
- goto done;
+ arch_elf_destroy(&lte);
+ ltelf_destroy(&lte);
+ return 0;
}
int
-ltelf_read_library(struct library *lib, struct Process *proc,
+ltelf_read_library(struct library *lib, struct process *proc,
const char *filename, GElf_Addr bias)
{
return read_module(lib, proc, filename, bias, 0);
@@ -950,12 +1272,13 @@ ltelf_read_library(struct library *lib, struct Process *proc,
struct library *
-ltelf_read_main_binary(struct Process *proc, const char *path)
+ltelf_read_main_binary(struct process *proc, const char *path)
{
struct library *lib = malloc(sizeof(*lib));
- if (lib == NULL)
+ if (lib == NULL || library_init(lib, LT_LIBTYPE_MAIN) < 0) {
+ free(lib);
return NULL;
- library_init(lib, LT_LIBTYPE_MAIN);
+ }
library_set_pathname(lib, path, 0);
/* There is a race between running the process and reading its
@@ -965,14 +1288,13 @@ ltelf_read_main_binary(struct Process *proc, const char *path)
* that. Presumably we could read the DSOs from the process
* memory image, but that's not currently done. */
char *fname = pid2name(proc->pid);
- if (fname == NULL)
- return NULL;
- if (read_module(lib, proc, fname, 0, 1) < 0) {
+ if (fname == NULL
+ || read_module(lib, proc, fname, 0, 1) < 0) {
library_destroy(lib);
free(lib);
- return NULL;
+ lib = NULL;
}
- free(fname);
+ free(fname);
return lib;
}