Handle symbol aliases
diff --git a/ltrace-elf.c b/ltrace-elf.c
index b15c31c..9285fee 100644
--- a/ltrace-elf.c
+++ b/ltrace-elf.c
@@ -1,16 +1,17 @@
#include "config.h"
+#include <assert.h>
#include <endian.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <gelf.h>
#include <inttypes.h>
+#include <search.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <assert.h>
#include "common.h"
#include "proc.h"
@@ -507,11 +508,41 @@
return 0;
}
+/* 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,
+ * __cfree, __free, __libc_free, cfree and free all defined on the
+ * same address. So instead we keep this unique symbol struct for
+ * each address, and replace name in libsym with a shorter variant if
+ * we find it. */
+struct unique_symbol {
+ target_address_t addr;
+ struct library_symbol *libsym;
+};
+
+static int
+unique_symbol_cmp(const void *key, const void *val)
+{
+ const struct unique_symbol *sym_key = key;
+ const struct unique_symbol *sym_val = val;
+ return sym_key->addr != sym_val->addr;
+}
+
static int
populate_this_symtab(struct Process *proc, const char *filename,
struct ltelf *lte, struct library *lib,
Elf_Data *symtab, const char *strtab, size_t size)
{
+ /* 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);
+ if (symbols == NULL) {
+ error(0, errno, "couldn't insert symbols for -x");
+ return -1;
+ }
+
size_t lib_len = strlen(lib->soname);
size_t i;
for (i = 0; i < size; ++i) {
@@ -555,13 +586,39 @@
goto fail;
sprintf(full_name, "%s@%s", name, lib->soname);
- struct library_symbol *libsym = malloc(sizeof(*libsym));
- if (libsym == NULL)
- goto fail;
+ /* 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);
- library_symbol_init(libsym, naddr, full_name, 1, LS_TOPLT_NONE);
- library_add_symbol(lib, libsym);
+ if (unique->libsym == NULL) {
+ struct library_symbol *libsym = malloc(sizeof(*libsym));
+ if (libsym == NULL) {
+ --num_symbols;
+ goto fail;
+ }
+ library_symbol_init(libsym, naddr, full_name,
+ 1, LS_TOPLT_NONE);
+ unique->libsym = libsym;
+ unique->addr = naddr;
+
+ } else if (strlen(full_name) < strlen(unique->libsym->name)) {
+ library_symbol_set_name(unique->libsym, full_name, 1);
+
+ } else {
+ free(full_name);
+ }
}
+
+ for (i = 0; i < num_symbols; ++i) {
+ assert(symbols[i].libsym != NULL);
+ library_add_symbol(lib, symbols[i].libsym);
+ }
+
+ free(symbols);
+
return 0;
}