/* elf.c -- Get debug data from an ELF file for backtraces.
- Copyright (C) 2012 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
#include <string.h>
#include <sys/types.h>
+#ifdef HAVE_DL_ITERATE_PHDR
+#include <link.h>
+#endif
+
#include "backtrace.h"
#include "internal.h"
+#ifndef HAVE_DL_ITERATE_PHDR
+
+/* Dummy version of dl_iterate_phdr for systems that don't have it. */
+
+#define dl_phdr_info x_dl_phdr_info
+#define dl_iterate_phdr x_dl_iterate_phdr
+
+struct dl_phdr_info
+{
+ uintptr_t dlpi_addr;
+ const char *dlpi_name;
+};
+
+static int
+dl_iterate_phdr (int (*callback) (struct dl_phdr_info *,
+ size_t, void *) ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+#endif /* ! defined (HAVE_DL_ITERATE_PHDR) */
+
/* The configure script must tell us whether we are 32-bit or 64-bit
ELF. We could make this code test and support either possibility,
but there is no point. This code only works for the currently
#error "Unknown BACKTRACE_ELF_SIZE"
#endif
+/* <link.h> might #include <elf.h> which might define our constants
+ with slightly different values. Undefine them to be safe. */
+
+#undef EI_NIDENT
+#undef EI_MAG0
+#undef EI_MAG1
+#undef EI_MAG2
+#undef EI_MAG3
+#undef EI_CLASS
+#undef EI_DATA
+#undef EI_VERSION
+#undef ELF_MAG0
+#undef ELF_MAG1
+#undef ELF_MAG2
+#undef ELF_MAG3
+#undef ELFCLASS32
+#undef ELFCLASS64
+#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. */
-typedef uint16_t Elf_Half;
-typedef uint32_t Elf_Word;
-typedef int32_t Elf_Sword;
+typedef uint16_t b_elf_half; /* Elf_Half. */
+typedef uint32_t b_elf_word; /* Elf_Word. */
+typedef int32_t b_elf_sword; /* Elf_Sword. */
#if BACKTRACE_ELF_SIZE == 32
-typedef uint32_t Elf_Addr;
-typedef uint32_t Elf_Off;
+typedef uint32_t b_elf_addr; /* Elf_Addr. */
+typedef uint32_t b_elf_off; /* Elf_Off. */
-typedef uint32_t Elf_WXword;
+typedef uint32_t b_elf_wxword; /* 32-bit Elf_Word, 64-bit ELF_Xword. */
#else
-typedef uint64_t Elf_Addr;
-typedef uint64_t Elf_Off;
-typedef uint64_t Elf_Xword;
-typedef int64_t Elf_Sxword;
+typedef uint64_t b_elf_addr; /* Elf_Addr. */
+typedef uint64_t b_elf_off; /* Elf_Off. */
+typedef uint64_t b_elf_xword; /* Elf_Xword. */
+typedef int64_t b_elf_sxword; /* Elf_Sxword. */
-typedef uint64_t Elf_WXword;
+typedef uint64_t b_elf_wxword; /* 32-bit Elf_Word, 64-bit ELF_Xword. */
#endif
typedef struct {
unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */
- Elf_Half e_type; /* Identifies object file type */
- Elf_Half e_machine; /* Specifies required architecture */
- Elf_Word e_version; /* Identifies object file version */
- Elf_Addr e_entry; /* Entry point virtual address */
- Elf_Off e_phoff; /* Program header table file offset */
- Elf_Off e_shoff; /* Section header table file offset */
- Elf_Word e_flags; /* Processor-specific flags */
- Elf_Half e_ehsize; /* ELF header size in bytes */
- Elf_Half e_phentsize; /* Program header table entry size */
- Elf_Half e_phnum; /* Program header table entry count */
- Elf_Half e_shentsize; /* Section header table entry size */
- Elf_Half e_shnum; /* Section header table entry count */
- Elf_Half e_shstrndx; /* Section header string table index */
-} Elf_Ehdr;
+ b_elf_half e_type; /* Identifies object file type */
+ b_elf_half e_machine; /* Specifies required architecture */
+ b_elf_word e_version; /* Identifies object file version */
+ b_elf_addr e_entry; /* Entry point virtual address */
+ b_elf_off e_phoff; /* Program header table file offset */
+ b_elf_off e_shoff; /* Section header table file offset */
+ b_elf_word e_flags; /* Processor-specific flags */
+ b_elf_half e_ehsize; /* ELF header size in bytes */
+ b_elf_half e_phentsize; /* Program header table entry size */
+ b_elf_half e_phnum; /* Program header table entry count */
+ b_elf_half e_shentsize; /* Section header table entry size */
+ b_elf_half e_shnum; /* Section header table entry count */
+ b_elf_half e_shstrndx; /* Section header string table index */
+} b_elf_ehdr; /* Elf_Ehdr. */
#define EI_MAG0 0
#define EI_MAG1 1
#define EV_CURRENT 1
-typedef struct {
- Elf_Word sh_name; /* Section name, index in string tbl */
- Elf_Word sh_type; /* Type of section */
- Elf_WXword sh_flags; /* Miscellaneous section attributes */
- Elf_Addr sh_addr; /* Section virtual addr at execution */
- Elf_Off sh_offset; /* Section file offset */
- Elf_WXword sh_size; /* Size of section in bytes */
- Elf_Word sh_link; /* Index of another section */
- Elf_Word sh_info; /* Additional section information */
- Elf_WXword sh_addralign; /* Section alignment */
- Elf_WXword sh_entsize; /* Entry size if section holds table */
-} Elf_Shdr;
+#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_flags; /* Miscellaneous section attributes */
+ b_elf_addr sh_addr; /* Section virtual addr at execution */
+ b_elf_off sh_offset; /* Section file offset */
+ b_elf_wxword sh_size; /* Size of section in bytes */
+ b_elf_word sh_link; /* Index of another section */
+ b_elf_word sh_info; /* Additional section information */
+ b_elf_wxword sh_addralign; /* Section alignment */
+ 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 */
typedef struct
{
- Elf_Word st_name; /* Symbol name, index in string tbl */
- Elf_Addr st_value; /* Symbol value */
- Elf_Word st_size; /* Symbol size */
+ b_elf_word st_name; /* Symbol name, index in string tbl */
+ b_elf_addr st_value; /* Symbol value */
+ b_elf_word st_size; /* Symbol size */
unsigned char st_info; /* Symbol binding and type */
unsigned char st_other; /* Visibility and other data */
- Elf_Half st_shndx; /* Symbol section index */
-} Elf_Sym;
+ b_elf_half st_shndx; /* Symbol section index */
+} b_elf_sym; /* Elf_Sym. */
#else /* BACKTRACE_ELF_SIZE != 32 */
typedef struct
{
- Elf_Word st_name; /* Symbol name, index in string tbl */
+ b_elf_word st_name; /* Symbol name, index in string tbl */
unsigned char st_info; /* Symbol binding and type */
unsigned char st_other; /* Visibility and other data */
- Elf_Half st_shndx; /* Symbol section index */
- Elf_Addr st_value; /* Symbol value */
- Elf_Xword st_size; /* Symbol size */
-} Elf_Sym;
+ b_elf_half st_shndx; /* Symbol section index */
+ b_elf_addr st_value; /* Symbol value */
+ b_elf_xword st_size; /* Symbol size */
+} b_elf_sym; /* Elf_Sym. */
#endif /* BACKTRACE_ELF_SIZE != 32 */
+#define STT_OBJECT 1
#define STT_FUNC 2
/* An index of ELF sections we care about. */
struct elf_syminfo_data
{
+ /* Symbols for the next module. */
+ struct elf_syminfo_data *next;
/* The ELF symbols, sorted by address. */
struct elf_symbol *symbols;
/* The number of symbols. */
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,
void *data, struct elf_syminfo_data *sdata)
{
size_t sym_count;
- const Elf_Sym *sym;
+ const b_elf_sym *sym;
size_t elf_symbol_count;
size_t elf_symbol_size;
struct elf_symbol *elf_symbols;
size_t i;
unsigned int j;
- sym_count = symtab_size / sizeof (Elf_Sym);
+ sym_count = symtab_size / sizeof (b_elf_sym);
/* We only care about function symbols. Count them. */
- sym = (const Elf_Sym *) symtab_data;
+ sym = (const b_elf_sym *) symtab_data;
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;
}
if (elf_symbols == NULL)
return 0;
- sym = (const Elf_Sym *) symtab_data;
+ sym = (const b_elf_sym *) symtab_data;
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;
sdata->count = elf_symbol_count;
return 1;
}
-/* Return the symbol name and value for a PC. */
+/* Add EDATA to the list in STATE. */
static void
-elf_syminfo (struct backtrace_state *state, uintptr_t pc,
+elf_add_syminfo_data (struct backtrace_state *state,
+ struct elf_syminfo_data *edata)
+{
+ if (!state->threaded)
+ {
+ struct elf_syminfo_data **pp;
+
+ for (pp = (struct elf_syminfo_data **) (void *) &state->syminfo_data;
+ *pp != NULL;
+ pp = &(*pp)->next)
+ ;
+ *pp = edata;
+ }
+ else
+ {
+ while (1)
+ {
+ struct elf_syminfo_data **pp;
+
+ pp = (struct elf_syminfo_data **) (void *) &state->syminfo_data;
+
+ while (1)
+ {
+ struct elf_syminfo_data *p;
+
+ p = backtrace_atomic_load_pointer (pp);
+
+ if (p == NULL)
+ break;
+
+ pp = &p->next;
+ }
+
+ if (__sync_bool_compare_and_swap (pp, NULL, edata))
+ break;
+ }
+ }
+}
+
+/* Return the symbol name and value for an ADDR. */
+
+static void
+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);
}
-/* Initialize the backtrace data we need from an ELF executable. At
- the ELF level, all we need to do is find the debug info
- sections. */
+/* 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. */
-int
-backtrace_initialize (struct backtrace_state *state, int descriptor,
- backtrace_error_callback error_callback,
- void *data, fileline *fileline_fn)
+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, int exe)
{
struct backtrace_view ehdr_view;
- Elf_Ehdr ehdr;
+ b_elf_ehdr ehdr;
off_t shoff;
unsigned int shnum;
unsigned int shstrndx;
struct backtrace_view shdrs_view;
int shdrs_view_valid;
- const Elf_Shdr *shdrs;
- const Elf_Shdr *shstrhdr;
+ const b_elf_shdr *shdrs;
+ const b_elf_shdr *shstrhdr;
size_t shstr_size;
off_t shstr_off;
struct backtrace_view names_view;
struct backtrace_view debug_view;
int debug_view_valid;
+ *found_sym = 0;
+ *found_dwarf = 0;
+
shdrs_view_valid = 0;
names_view_valid = 0;
symtab_view_valid = 0;
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;
&& shoff != 0)
{
struct backtrace_view shdr_view;
- const Elf_Shdr *shdr;
+ const b_elf_shdr *shdr;
if (!backtrace_get_view (state, descriptor, shoff, sizeof shdr,
error_callback, data, &shdr_view))
goto fail;
- shdr = (const Elf_Shdr *) shdr_view.data;
+ shdr = (const b_elf_shdr *) shdr_view.data;
if (shnum == 0)
shnum = shdr->sh_size;
/* Read the section headers, skipping the first one. */
- if (!backtrace_get_view (state, descriptor, shoff + sizeof (Elf_Shdr),
- (shnum - 1) * sizeof (Elf_Shdr),
+ if (!backtrace_get_view (state, descriptor, shoff + sizeof (b_elf_shdr),
+ (shnum - 1) * sizeof (b_elf_shdr),
error_callback, data, &shdrs_view))
goto fail;
shdrs_view_valid = 1;
- shdrs = (const Elf_Shdr *) shdrs_view.data;
+ shdrs = (const b_elf_shdr *) shdrs_view.data;
/* Read the section names. */
dynsym_shndx = 0;
memset (sections, 0, sizeof sections);
+
+ /* Look for the symbol table. */
for (i = 1; i < shnum; ++i)
{
- const Elf_Shdr *shdr;
+ const b_elf_shdr *shdr;
unsigned int sh_name;
const char *name;
int j;
if (symtab_shndx == 0)
symtab_shndx = dynsym_shndx;
- if (symtab_shndx == 0)
- {
- state->syminfo_fn = elf_nosyms;
- state->syminfo_data = NULL;
- }
- else
+ if (symtab_shndx != 0)
{
- const Elf_Shdr *symtab_shdr;
+ const b_elf_shdr *symtab_shdr;
unsigned int strtab_shndx;
- const Elf_Shdr *strtab_shdr;
+ const b_elf_shdr *strtab_shdr;
struct elf_syminfo_data *sdata;
symtab_shdr = &shdrs[symtab_shndx - 1];
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))
string table permanently. */
backtrace_release_view (state, &symtab_view, error_callback, data);
- state->syminfo_fn = elf_syminfo;
- state->syminfo_data = sdata;
+ *found_sym = 1;
+
+ elf_add_syminfo_data (state, sdata);
}
/* FIXME: Need to handle compressed debug sections. */
{
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;
if (!backtrace_close (descriptor, error_callback, data))
goto fail;
*fileline_fn = elf_nodebug;
- state->fileline_data = NULL;
return 1;
}
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 (!backtrace_dwarf_initialize (state,
- sections[DEBUG_INFO].data,
- sections[DEBUG_INFO].size,
- sections[DEBUG_LINE].data,
- sections[DEBUG_LINE].size,
- sections[DEBUG_ABBREV].data,
- sections[DEBUG_ABBREV].size,
- sections[DEBUG_RANGES].data,
- sections[DEBUG_RANGES].size,
- sections[DEBUG_STR].data,
- sections[DEBUG_STR].size,
- ehdr.e_ident[EI_DATA] == ELFDATA2MSB,
- error_callback, data, fileline_fn))
+ {
+ 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,
+ sections[DEBUG_INFO].size,
+ sections[DEBUG_LINE].data,
+ sections[DEBUG_LINE].size,
+ sections[DEBUG_ABBREV].data,
+ sections[DEBUG_ABBREV].size,
+ sections[DEBUG_RANGES].data,
+ sections[DEBUG_RANGES].size,
+ sections[DEBUG_STR].data,
+ sections[DEBUG_STR].size,
+ ehdr.e_ident[EI_DATA] == ELFDATA2MSB,
+ error_callback, data, fileline_fn))
goto fail;
+ *found_dwarf = 1;
+
return 1;
fail:
backtrace_close (descriptor, error_callback, data);
return 0;
}
+
+/* Data passed to phdr_callback. */
+
+struct phdr_data
+{
+ struct backtrace_state *state;
+ backtrace_error_callback error_callback;
+ void *data;
+ fileline *fileline_fn;
+ int *found_sym;
+ int *found_dwarf;
+ int exe_descriptor;
+};
+
+/* Callback passed to dl_iterate_phdr. Load debug info from shared
+ libraries. */
+
+static int
+phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED,
+ void *pdata)
+{
+ struct phdr_data *pd = (struct phdr_data *) pdata;
+ int descriptor;
+ int does_not_exist;
+ fileline elf_fileline_fn;
+ int found_dwarf;
+
+ /* 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;
+ }
+
+ if (elf_add (pd->state, descriptor, info->dlpi_addr, pd->error_callback,
+ pd->data, &elf_fileline_fn, pd->found_sym, &found_dwarf, 0))
+ {
+ if (found_dwarf)
+ {
+ *pd->found_dwarf = 1;
+ *pd->fileline_fn = elf_fileline_fn;
+ }
+ }
+
+ return 0;
+}
+
+/* Initialize the backtrace data we need from an ELF executable. At
+ the ELF level, all we need to do is find the debug info
+ sections. */
+
+int
+backtrace_initialize (struct backtrace_state *state, int descriptor,
+ backtrace_error_callback error_callback,
+ void *data, fileline *fileline_fn)
+{
+ int ret;
+ int found_sym;
+ int found_dwarf;
+ fileline elf_fileline_fn;
+ struct phdr_data pd;
+
+ 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 = &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);
+
+ if (!state->threaded)
+ {
+ if (found_sym)
+ state->syminfo_fn = elf_syminfo;
+ else if (state->syminfo_fn == NULL)
+ state->syminfo_fn = elf_nosyms;
+ }
+ else
+ {
+ if (found_sym)
+ backtrace_atomic_store_pointer (&state->syminfo_fn, elf_syminfo);
+ else
+ __sync_bool_compare_and_swap (&state->syminfo_fn, NULL, elf_nosyms);
+ }
+
+ if (!state->threaded)
+ {
+ if (state->fileline_fn == NULL || state->fileline_fn == elf_nodebug)
+ *fileline_fn = elf_fileline_fn;
+ }
+ else
+ {
+ fileline current_fn;
+
+ current_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
+ if (current_fn == NULL || current_fn == elf_nodebug)
+ *fileline_fn = elf_fileline_fn;
+ }
+
+ return 1;
+}