/* elf.c -- Get debug data from an ELF file for backtraces.
- Copyright (C) 2012-2013 Free Software Foundation, Inc.
+ Copyright (C) 2012-2015 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Google.
Redistribution and use in source and binary forms, with or without
#undef ELFDATA2LSB
#undef ELFDATA2MSB
#undef EV_CURRENT
+#undef ET_DYN
#undef SHN_LORESERVE
#undef SHN_XINDEX
+#undef SHN_UNDEF
#undef SHT_SYMTAB
#undef SHT_STRTAB
#undef SHT_DYNSYM
+#undef STT_OBJECT
#undef STT_FUNC
/* Basic types. */
#define EV_CURRENT 1
+#define ET_DYN 3
+
typedef struct {
b_elf_word sh_name; /* Section name, index in string tbl */
b_elf_word sh_type; /* Type of section */
b_elf_wxword sh_entsize; /* Entry size if section holds table */
} b_elf_shdr; /* Elf_Shdr. */
+#define SHN_UNDEF 0x0000 /* Undefined section */
#define SHN_LORESERVE 0xFF00 /* Begin range of reserved indices */
#define SHN_XINDEX 0xFFFF /* Section index is held elsewhere */
#endif /* BACKTRACE_ELF_SIZE != 32 */
+#define STT_OBJECT 1
#define STT_FUNC 2
/* An index of ELF sections we care about. */
static void
elf_nosyms (struct backtrace_state *state ATTRIBUTE_UNUSED,
- uintptr_t pc ATTRIBUTE_UNUSED,
+ uintptr_t addr ATTRIBUTE_UNUSED,
backtrace_syminfo_callback callback ATTRIBUTE_UNUSED,
backtrace_error_callback error_callback, void *data)
{
return 0;
}
-/* Compare a PC against an elf_symbol for bsearch. We allocate one
+/* Compare an ADDR against an elf_symbol for bsearch. We allocate one
extra entry in the array so that this can look safely at the next
entry. */
{
const uintptr_t *key = (const uintptr_t *) vkey;
const struct elf_symbol *entry = (const struct elf_symbol *) ventry;
- uintptr_t pc;
+ uintptr_t addr;
- pc = *key;
- if (pc < entry->address)
+ addr = *key;
+ if (addr < entry->address)
return -1;
- else if (pc >= entry->address + entry->size)
+ else if (addr >= entry->address + entry->size)
return 1;
else
return 0;
static int
elf_initialize_syminfo (struct backtrace_state *state,
+ uintptr_t base_address,
const unsigned char *symtab_data, size_t symtab_size,
const unsigned char *strtab, size_t strtab_size,
backtrace_error_callback error_callback,
elf_symbol_count = 0;
for (i = 0; i < sym_count; ++i, ++sym)
{
- if ((sym->st_info & 0xf) == STT_FUNC)
+ int info;
+
+ info = sym->st_info & 0xf;
+ if ((info == STT_FUNC || info == STT_OBJECT)
+ && sym->st_shndx != SHN_UNDEF)
++elf_symbol_count;
}
j = 0;
for (i = 0; i < sym_count; ++i, ++sym)
{
- if ((sym->st_info & 0xf) != STT_FUNC)
+ int info;
+
+ info = sym->st_info & 0xf;
+ if (info != STT_FUNC && info != STT_OBJECT)
+ continue;
+ if (sym->st_shndx == SHN_UNDEF)
continue;
if (sym->st_name >= strtab_size)
{
return 0;
}
elf_symbols[j].name = (const char *) strtab + sym->st_name;
- elf_symbols[j].address = sym->st_value;
+ elf_symbols[j].address = sym->st_value + base_address;
elf_symbols[j].size = sym->st_size;
++j;
}
- qsort (elf_symbols, elf_symbol_count, sizeof (struct elf_symbol),
- elf_symbol_compare);
+ backtrace_qsort (elf_symbols, elf_symbol_count, sizeof (struct elf_symbol),
+ elf_symbol_compare);
sdata->next = NULL;
sdata->symbols = elf_symbols;
{
struct elf_syminfo_data *p;
- /* Atomic load. */
- p = *pp;
- while (!__sync_bool_compare_and_swap (pp, p, p))
- p = *pp;
+ p = backtrace_atomic_load_pointer (pp);
if (p == NULL)
break;
}
}
-/* Return the symbol name and value for a PC. */
+/* Return the symbol name and value for an ADDR. */
static void
-elf_syminfo (struct backtrace_state *state, uintptr_t pc,
+elf_syminfo (struct backtrace_state *state, uintptr_t addr,
backtrace_syminfo_callback callback,
backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
void *data)
{
struct elf_syminfo_data *edata;
- struct elf_symbol *sym;
+ struct elf_symbol *sym = NULL;
+
+ if (!state->threaded)
+ {
+ for (edata = (struct elf_syminfo_data *) state->syminfo_data;
+ edata != NULL;
+ edata = edata->next)
+ {
+ sym = ((struct elf_symbol *)
+ bsearch (&addr, edata->symbols, edata->count,
+ sizeof (struct elf_symbol), elf_symbol_search));
+ if (sym != NULL)
+ break;
+ }
+ }
+ else
+ {
+ struct elf_syminfo_data **pp;
+
+ pp = (struct elf_syminfo_data **) (void *) &state->syminfo_data;
+ while (1)
+ {
+ edata = backtrace_atomic_load_pointer (pp);
+ if (edata == NULL)
+ break;
+
+ sym = ((struct elf_symbol *)
+ bsearch (&addr, edata->symbols, edata->count,
+ sizeof (struct elf_symbol), elf_symbol_search));
+ if (sym != NULL)
+ break;
+
+ pp = &edata->next;
+ }
+ }
- edata = (struct elf_syminfo_data *) state->syminfo_data;
- sym = ((struct elf_symbol *)
- bsearch (&pc, edata->symbols, edata->count,
- sizeof (struct elf_symbol), elf_symbol_search));
if (sym == NULL)
- callback (data, pc, NULL, 0);
+ callback (data, addr, NULL, 0, 0);
else
- callback (data, pc, sym->name, sym->address);
+ callback (data, addr, sym->name, sym->address, sym->size);
}
-/* Add the backtrace data for one ELF file. */
+/* Add the backtrace data for one ELF file. Returns 1 on success,
+ 0 on failure (in both cases descriptor is closed) or -1 if exe
+ is non-zero and the ELF file is ET_DYN, which tells the caller that
+ elf_add will need to be called on the descriptor again after
+ base_address is determined. */
static int
elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
backtrace_error_callback error_callback, void *data,
- fileline *fileline_fn, int *found_sym, int *found_dwarf)
+ fileline *fileline_fn, int *found_sym, int *found_dwarf, int exe)
{
struct backtrace_view ehdr_view;
b_elf_ehdr ehdr;
goto fail;
}
+ /* If the executable is ET_DYN, it is either a PIE, or we are running
+ directly a shared library with .interp. We need to wait for
+ dl_iterate_phdr in that case to determine the actual base_address. */
+ if (exe && ehdr.e_type == ET_DYN)
+ return -1;
+
shoff = ehdr.e_shoff;
shnum = ehdr.e_shnum;
shstrndx = ehdr.e_shstrndx;
if (sdata == NULL)
goto fail;
- if (!elf_initialize_syminfo (state,
+ if (!elf_initialize_syminfo (state, base_address,
symtab_view.data, symtab_shdr->sh_size,
strtab_view.data, strtab_shdr->sh_size,
error_callback, data, sdata))
{
off_t end;
+ if (sections[i].size == 0)
+ continue;
if (min_offset == 0 || sections[i].offset < min_offset)
min_offset = sections[i].offset;
end = sections[i].offset + sections[i].size;
descriptor = -1;
for (i = 0; i < (int) DEBUG_MAX; ++i)
- sections[i].data = ((const unsigned char *) debug_view.data
- + (sections[i].offset - min_offset));
+ {
+ if (sections[i].size == 0)
+ sections[i].data = NULL;
+ else
+ sections[i].data = ((const unsigned char *) debug_view.data
+ + (sections[i].offset - min_offset));
+ }
if (!backtrace_dwarf_add (state, base_address,
sections[DEBUG_INFO].data,
fileline *fileline_fn;
int *found_sym;
int *found_dwarf;
+ int exe_descriptor;
};
/* Callback passed to dl_iterate_phdr. Load debug info from shared
fileline elf_fileline_fn;
int found_dwarf;
- /* There is not much we can do if we don't have the module name. If
- the base address is 0, this is probably the executable, which we
- already loaded. */
- if (info->dlpi_name == NULL
- || info->dlpi_name[0] == '\0'
- || info->dlpi_addr == 0)
- return 0;
+ /* There is not much we can do if we don't have the module name,
+ unless executable is ET_DYN, where we expect the very first
+ phdr_callback to be for the PIE. */
+ if (info->dlpi_name == NULL || info->dlpi_name[0] == '\0')
+ {
+ if (pd->exe_descriptor == -1)
+ return 0;
+ descriptor = pd->exe_descriptor;
+ pd->exe_descriptor = -1;
+ }
+ else
+ {
+ if (pd->exe_descriptor != -1)
+ {
+ backtrace_close (pd->exe_descriptor, pd->error_callback, pd->data);
+ pd->exe_descriptor = -1;
+ }
- descriptor = backtrace_open (info->dlpi_name, pd->error_callback, pd->data,
- &does_not_exist);
- if (descriptor < 0)
- return 0;
+ descriptor = backtrace_open (info->dlpi_name, pd->error_callback,
+ pd->data, &does_not_exist);
+ if (descriptor < 0)
+ return 0;
+ }
if (elf_add (pd->state, descriptor, info->dlpi_addr, pd->error_callback,
- pd->data, &elf_fileline_fn, pd->found_sym, &found_dwarf))
+ pd->data, &elf_fileline_fn, pd->found_sym, &found_dwarf, 0))
{
if (found_dwarf)
{
backtrace_error_callback error_callback,
void *data, fileline *fileline_fn)
{
+ int ret;
int found_sym;
int found_dwarf;
- syminfo elf_syminfo_fn;
fileline elf_fileline_fn;
struct phdr_data pd;
- if (!elf_add (state, descriptor, 0, error_callback, data, &elf_fileline_fn,
- &found_sym, &found_dwarf))
+ ret = elf_add (state, descriptor, 0, error_callback, data, &elf_fileline_fn,
+ &found_sym, &found_dwarf, 1);
+ if (!ret)
return 0;
pd.state = state;
pd.error_callback = error_callback;
pd.data = data;
- pd.fileline_fn = fileline_fn;
+ pd.fileline_fn = &elf_fileline_fn;
pd.found_sym = &found_sym;
pd.found_dwarf = &found_dwarf;
+ pd.exe_descriptor = ret < 0 ? descriptor : -1;
dl_iterate_phdr (phdr_callback, (void *) &pd);
- elf_syminfo_fn = found_sym ? elf_syminfo : elf_nosyms;
if (!state->threaded)
{
- if (state->syminfo_fn == NULL || found_sym)
- state->syminfo_fn = elf_syminfo_fn;
+ if (found_sym)
+ state->syminfo_fn = elf_syminfo;
+ else if (state->syminfo_fn == NULL)
+ state->syminfo_fn = elf_nosyms;
}
else
{
- __sync_bool_compare_and_swap (&state->syminfo_fn, NULL, elf_syminfo_fn);
if (found_sym)
- __sync_bool_compare_and_swap (&state->syminfo_fn, elf_nosyms,
- elf_syminfo_fn);
+ backtrace_atomic_store_pointer (&state->syminfo_fn, elf_syminfo);
+ else
+ __sync_bool_compare_and_swap (&state->syminfo_fn, NULL, elf_nosyms);
}
if (!state->threaded)
{
fileline current_fn;
- /* Atomic load. */
- current_fn = state->fileline_fn;
- while (!__sync_bool_compare_and_swap (&state->fileline_fn, current_fn,
- current_fn))
- current_fn = state->fileline_fn;
+ current_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
if (current_fn == NULL || current_fn == elf_nodebug)
*fileline_fn = elf_fileline_fn;
}