aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPetr Machata <pmachata@redhat.com>2013-11-05 01:46:30 +0100
committerPetr Machata <pmachata@redhat.com>2013-11-05 01:58:08 +0100
commit97d13666cd84589135ba593fa43a800d098026d0 (patch)
treea4ec260f7018f39317526098183468b8263a589c
parentfec0b3b1b8323220255f9ebdcac7db2b689783ef (diff)
downloadltrace-97d13666cd84589135ba593fa43a800d098026d0.tar.gz
Add elf_add_plt_entry as a separate entry point
- That calls into OS and ARCH callbacks as required and has the same interface. This also adds os_elf_add_plt_entry. - arch_elf_add_func_entry was added as well for symmetry.
-rw-r--r--backend.h21
-rw-r--r--ltrace-elf.c199
-rw-r--r--ltrace-elf.h16
3 files changed, 154 insertions, 82 deletions
diff --git a/backend.h b/backend.h
index 5f53d79..dc94d5f 100644
--- a/backend.h
+++ b/backend.h
@@ -319,8 +319,8 @@ enum plt_status {
PLT_DEFAULT,
};
-/* The following callback has to be implemented in backend if arch.h
- * defines ARCH_HAVE_ADD_PLT_ENTRY.
+/* The following callback has to be implemented in OS backend if os.h
+ * defines OS_HAVE_ADD_PLT_ENTRY.
*
* This is called for every PLT relocation R in ELF file LTE, that
* ltrace is about to add to a library constructed in process PROC.
@@ -331,11 +331,18 @@ enum plt_status {
* calling arch_plt_sym_val, and symbol is allocated. If PLT_OK or
* PLT_DEFAULT are returned, the chain of symbols passed back in RET
* is added to library under construction. */
+enum plt_status os_elf_add_plt_entry(struct process *proc, struct ltelf *lte,
+ const char *name, GElf_Rela *rela,
+ size_t i, struct library_symbol **ret);
+
+/* Like os_elf_add_plt_entry, but tied to ARCH_HAVE_ADD_PLT_ENTRY in
+ * arch.h. The arch callback is called first. If it returns
+ * PLT_DEFAULT, the os callback is called next. */
enum plt_status arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte,
const char *name, GElf_Rela *rela,
size_t i, struct library_symbol **ret);
-/* The following callback has to be implemented in backend if arch.h
+/* The following callback has to be implemented in OS backend if os.h
* defines OS_HAVE_ADD_FUNC_ENTRY.
*
* This is called for every symbol in ltrace is about to add to the
@@ -352,6 +359,14 @@ enum plt_status os_elf_add_func_entry(struct process *proc, struct ltelf *lte,
arch_addr_t addr, const char *name,
struct library_symbol **ret);
+/* Like os_elf_add_func_entry, but tied to ARCH_HAVE_ADD_FUNC_ENTRY in
+ * arch.h. The arch callback is called first. If it returns
+ * PLT_DEFAULT, the os callback is called next. */
+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);
+
/* This callback needs to be implemented if arch.h defines
* ARCH_HAVE_DYNLINK_DONE. It is called after the dynamic linker is
* done with the process start-up. */
diff --git a/ltrace-elf.c b/ltrace-elf.c
index 9100c46..92b642b 100644
--- a/ltrace-elf.c
+++ b/ltrace-elf.c
@@ -36,6 +36,7 @@
#include <gelf.h>
#include <inttypes.h>
#include <search.h>
+#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -64,41 +65,15 @@ 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
@@ -126,6 +101,17 @@ os_elf_add_func_entry(struct process *proc, struct ltelf *lte,
}
#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
+
Elf_Data *
elf_loaddata(Elf_Scn *scn, GElf_Shdr *shdr)
{
@@ -698,6 +684,67 @@ arch_get_sym_info(struct ltelf *lte, const char *filename,
}
#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)
+{
+ 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;
+}
+
+int
+elf_add_plt_entry(struct process *proc, struct ltelf *lte,
+ const char *name, GElf_Rela *rela, size_t idx,
+ struct library_symbol **ret)
+{
+ 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();
+}
+
static void
mark_chain_latent(struct library_symbol *libsym)
{
@@ -725,23 +772,13 @@ filter_symbol_chain(struct filter *filter,
}
}
-static 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;
- }
-}
-
static int
populate_plt(struct process *proc, const char *filename,
- struct ltelf *lte, struct library *lib,
- int latent_plts)
+ struct ltelf *lte, struct library *lib)
{
- size_t count = vect_size(&lte->plt_relocs);
+ const bool latent_plts = options.export_filter != NULL;
+ const size_t count = vect_size(&lte->plt_relocs);
+
size_t i;
for (i = 0; i < count; ++i) {
GElf_Rela *rela = VECT_ELEMENT(&lte->plt_relocs, GElf_Rela, i);
@@ -761,45 +798,43 @@ populate_plt(struct process *proc, const char *filename,
}
char const *name = lte->dynstr + sym.st_name;
-
int matched = filter_matches_symbol(options.plt_filter,
name, lib);
struct library_symbol *libsym = NULL;
- switch (arch_elf_add_plt_entry(proc, lte, name,
- rela, i, &libsym)) {
- case PLT_FAIL:
+ if (elf_add_plt_entry(proc, lte, name, rela, i, &libsym) < 0)
return -1;
- case PLT_DEFAULT:
- /* Add default entry to the beginning of LIBSYM. */
- if (default_elf_add_plt_entry(proc, lte, name,
- rela, i, &libsym) < 0)
- return -1;
- /* Fall through. */
- case PLT_OK:
- /* If we didn't match the PLT entry up there,
- * filter the chain to only include the
- * matching symbols (but include all if we are
- * adding latent symbols). This is to allow
- * arch_elf_add_plt_entry 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);
- }
+ /* 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;
}
+static 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,
@@ -934,8 +969,14 @@ populate_this_symtab(struct process *proc, const char *filename,
}
struct library_symbol *libsym = NULL;
- switch (os_elf_add_func_entry(proc, lte, &sym,
- naddr, full_name, &libsym)) {
+ 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);
@@ -986,6 +1027,7 @@ populate_this_symtab(struct process *proc, const char *filename,
unique->libsym = tmp;
unique->addr = tmp->enter_addr;
tmp = tmp->next;
+ unique->libsym->next = NULL;
} else {
if (strlen(tmp->name)
< strlen(unique->libsym->name)) {
@@ -1172,8 +1214,7 @@ read_module(struct library *lib, struct process *proc,
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);
diff --git a/ltrace-elf.h b/ltrace-elf.h
index 57ed87d..ea14512 100644
--- a/ltrace-elf.h
+++ b/ltrace-elf.h
@@ -81,6 +81,22 @@ int ltelf_read_library(struct library *lib, struct process *proc,
* point address is stored to *ENTRYP. */
struct library *ltelf_read_main_binary(struct process *proc, const char *path);
+/* This is called for every PLT relocation R in ELF file LTE, that
+ * ltrace is about to add to a library constructed in process PROC.
+ * The corresponding PLT entry is for symbol called NAME, and it's
+ * I-th relocation in the file. *RET shall be initialized and
+ * symbol(s) corresponding to the given PLT entry will be added to the
+ * front. Returns 0 for success, or a negative value for failures.
+ *
+ * This calls os_elf_add_plt_entry and arch_elf_add_plt_entry in the
+ * background (see backend.h for documentation). The arch callback is
+ * called first. If it returns PLT_DEFAULT, the os callback is called
+ * next. If that returns PLT_DEFAULT, default_elf_add_plt_entry is
+ * called. */
+int elf_add_plt_entry(struct process *proc, struct ltelf *lte,
+ const char *name, GElf_Rela *rela, size_t idx,
+ struct library_symbol **ret);
+
/* Create a default PLT entry. This can be used instead (or in
* addition to) returning PLT_DEFAULT from arch_elf_add_plt_entry.
* RET shall be initialized, the created symbol will be added to the