Commit e561a992 by Ian Lance Taylor Committed by Ian Lance Taylor

Add support for tracing through shared libraries.

	* configure.ac: Check for link.h and dl_iterate_phdr.
	* elf.c: #include <link.h> if system has dl_iterate_phdr.  #undef
	ELF macros before #defining them.
	(dl_phdr_info, dl_iterate_phdr): Define if system does not have
	dl_iterate_phdr.
	(struct elf_syminfo_data): Add next field.
	(elf_initialize_syminfo): Initialize next field.
	(elf_add_syminfo_data): New static function.
	(elf_add): New static function, broken out of
	backtrace_initialize.  Call backtrace_dwarf_add instead of
	backtrace_dwarf_initialize.
	(struct phdr_data): Define.
	(phdr_callback): New static function.
	(backtrace_initialize): Call elf_add.
	* dwarf.c (struct dwarf_data): Add next and base_address fields.
	(add_unit_addr): Add base_address parameter.  Change all callers.
	(add_unit_ranges, build_address_map): Likewise.
	(add_line): Add ddata parameter.  Change all callers.
	(read_line_program, add_function_range): Likewise.
	(dwarf_lookup_pc): New static function, broken out of
	dwarf_fileline.
	(dwarf_fileline): Call dwarf_lookup_pc.
	(build_dwarf_data): New static function.
	(backtrace_dwarf_add): New function.
	(backtrace_dwarf_initialize): Remove.
	* internal.h (backtrace_dwarf_initialize): Don't declare.
	(backtrace_dwarf_add): Declare.
	* configure, config.h.in: Rebuild.

From-SVN: r192267
parent 1a61077e
2012-10-09 Ian Lance Taylor <iant@google.com>
Add support for tracing through shared libraries.
* configure.ac: Check for link.h and dl_iterate_phdr.
* elf.c: #include <link.h> if system has dl_iterate_phdr. #undef
ELF macros before #defining them.
(dl_phdr_info, dl_iterate_phdr): Define if system does not have
dl_iterate_phdr.
(struct elf_syminfo_data): Add next field.
(elf_initialize_syminfo): Initialize next field.
(elf_add_syminfo_data): New static function.
(elf_add): New static function, broken out of
backtrace_initialize. Call backtrace_dwarf_add instead of
backtrace_dwarf_initialize.
(struct phdr_data): Define.
(phdr_callback): New static function.
(backtrace_initialize): Call elf_add.
* dwarf.c (struct dwarf_data): Add next and base_address fields.
(add_unit_addr): Add base_address parameter. Change all callers.
(add_unit_ranges, build_address_map): Likewise.
(add_line): Add ddata parameter. Change all callers.
(read_line_program, add_function_range): Likewise.
(dwarf_lookup_pc): New static function, broken out of
dwarf_fileline.
(dwarf_fileline): Call dwarf_lookup_pc.
(build_dwarf_data): New static function.
(backtrace_dwarf_add): New function.
(backtrace_dwarf_initialize): Remove.
* internal.h (backtrace_dwarf_initialize): Don't declare.
(backtrace_dwarf_add): Declare.
* configure, config.h.in: Rebuild.
2012-10-04 Gerald Pfeifer <gerald@pfeifer.com> 2012-10-04 Gerald Pfeifer <gerald@pfeifer.com>
* btest.c (f23): Avoid uninitialized variable warning. * btest.c (f23): Avoid uninitialized variable warning.
2012-10-04 Ian Lance Taylor <iant@google.com> 2012-10-04 Ian Lance Taylor <iant@google.com>
* dwarf.c: If the system header files do not declare strnlen, * dwarf.c: If the system header files do not declare strnlen,
......
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
/* Define to 1 if you have the <dlfcn.h> header file. */ /* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H #undef HAVE_DLFCN_H
/* Define if dl_iterate_phdr is available. */
#undef HAVE_DL_ITERATE_PHDR
/* Define to 1 if you have the fcntl function */ /* Define to 1 if you have the fcntl function */
#undef HAVE_FCNTL #undef HAVE_FCNTL
...@@ -19,6 +22,9 @@ ...@@ -19,6 +22,9 @@
/* Define to 1 if you have the <inttypes.h> header file. */ /* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H #undef HAVE_INTTYPES_H
/* Define to 1 if you have the <link.h> header file. */
#undef HAVE_LINK_H
/* Define to 1 if you have the <memory.h> header file. */ /* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H #undef HAVE_MEMORY_H
......
...@@ -12182,6 +12182,53 @@ if test "$ALLOC_FILE" = "alloc.lo"; then ...@@ -12182,6 +12182,53 @@ if test "$ALLOC_FILE" = "alloc.lo"; then
fi fi
# Check for dl_iterate_phdr.
for ac_header in link.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "link.h" "ac_cv_header_link_h" "$ac_includes_default"
if test "x$ac_cv_header_link_h" = x""yes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LINK_H 1
_ACEOF
fi
done
if test "$ac_cv_header_link_h" = "no"; then
have_dl_iterate_phdr=no
else
if test -n "${with_target_subdir}"; then
# When built as a GCC target library, we can't do a link test.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <link.h>
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "dl_iterate_phdr" >/dev/null 2>&1; then :
have_dl_iterate_phdr=yes
else
have_dl_iterate_phdr=no
fi
rm -f conftest*
else
ac_fn_c_check_func "$LINENO" "dl_iterate_phdr" "ac_cv_func_dl_iterate_phdr"
if test "x$ac_cv_func_dl_iterate_phdr" = x""yes; then :
have_dl_iterate_phdr=yes
else
have_dl_iterate_phdr=no
fi
fi
fi
if test "$have_dl_iterate_phdr" = "yes"; then
$as_echo "#define HAVE_DL_ITERATE_PHDR 1" >>confdefs.h
fi
# Check for the fcntl function. # Check for the fcntl function.
if test -n "${with_target_subdir}"; then if test -n "${with_target_subdir}"; then
case "${host}" in case "${host}" in
......
...@@ -226,6 +226,24 @@ if test "$ALLOC_FILE" = "alloc.lo"; then ...@@ -226,6 +226,24 @@ if test "$ALLOC_FILE" = "alloc.lo"; then
fi fi
AC_SUBST(BACKTRACE_USES_MALLOC) AC_SUBST(BACKTRACE_USES_MALLOC)
# Check for dl_iterate_phdr.
AC_CHECK_HEADERS(link.h)
if test "$ac_cv_header_link_h" = "no"; then
have_dl_iterate_phdr=no
else
if test -n "${with_target_subdir}"; then
# When built as a GCC target library, we can't do a link test.
AC_EGREP_HEADER([dl_iterate_phdr], [link.h], [have_dl_iterate_phdr=yes],
[have_dl_iterate_phdr=no])
else
AC_CHECK_FUNC([dl_iterate_phdr], [have_dl_iterate_phdr=yes],
[have_dl_iterate_phdr=no])
fi
fi
if test "$have_dl_iterate_phdr" = "yes"; then
AC_DEFINE(HAVE_DL_ITERATE_PHDR, 1, [Define if dl_iterate_phdr is available.])
fi
# Check for the fcntl function. # Check for the fcntl function.
if test -n "${with_target_subdir}"; then if test -n "${with_target_subdir}"; then
case "${host}" in case "${host}" in
......
...@@ -36,9 +36,36 @@ POSSIBILITY OF SUCH DAMAGE. */ ...@@ -36,9 +36,36 @@ POSSIBILITY OF SUCH DAMAGE. */
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#ifdef HAVE_DL_ITERATE_PHDR
#include <link.h>
#endif
#include "backtrace.h" #include "backtrace.h"
#include "internal.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 /* 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, ELF. We could make this code test and support either possibility,
but there is no point. This code only works for the currently but there is no point. This code only works for the currently
...@@ -49,6 +76,33 @@ POSSIBILITY OF SUCH DAMAGE. */ ...@@ -49,6 +76,33 @@ POSSIBILITY OF SUCH DAMAGE. */
#error "Unknown BACKTRACE_ELF_SIZE" #error "Unknown BACKTRACE_ELF_SIZE"
#endif #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 SHN_LORESERVE
#undef SHN_XINDEX
#undef SHT_SYMTAB
#undef SHT_STRTAB
#undef SHT_DYNSYM
#undef STT_FUNC
/* Basic types. */ /* Basic types. */
typedef uint16_t Elf_Half; typedef uint16_t Elf_Half;
...@@ -214,6 +268,8 @@ struct elf_symbol ...@@ -214,6 +268,8 @@ struct elf_symbol
struct elf_syminfo_data struct elf_syminfo_data
{ {
/* Symbols for the next module. */
struct elf_syminfo_data *next;
/* The ELF symbols, sorted by address. */ /* The ELF symbols, sorted by address. */
struct elf_symbol *symbols; struct elf_symbol *symbols;
/* The number of symbols. */ /* The number of symbols. */
...@@ -337,12 +393,58 @@ elf_initialize_syminfo (struct backtrace_state *state, ...@@ -337,12 +393,58 @@ elf_initialize_syminfo (struct backtrace_state *state,
qsort (elf_symbols, elf_symbol_count, sizeof (struct elf_symbol), qsort (elf_symbols, elf_symbol_count, sizeof (struct elf_symbol),
elf_symbol_compare); elf_symbol_compare);
sdata->next = NULL;
sdata->symbols = elf_symbols; sdata->symbols = elf_symbols;
sdata->count = elf_symbol_count; sdata->count = elf_symbol_count;
return 1; return 1;
} }
/* Add EDATA to the list in STATE. */
static void
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 **) &state->syminfo_data;
*pp != NULL;
pp = &(*pp)->next)
;
*pp = edata;
}
else
{
while (1)
{
struct elf_syminfo_data **pp;
pp = (struct elf_syminfo_data **) &state->syminfo_data;
while (1)
{
struct elf_syminfo_data *p;
/* Atomic load. */
p = *pp;
while (!__sync_bool_compare_and_swap (pp, p, p))
p = *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 a PC. */ /* Return the symbol name and value for a PC. */
static void static void
...@@ -364,14 +466,12 @@ elf_syminfo (struct backtrace_state *state, uintptr_t pc, ...@@ -364,14 +466,12 @@ elf_syminfo (struct backtrace_state *state, uintptr_t pc,
callback (data, pc, sym->name, sym->address); callback (data, pc, sym->name, sym->address);
} }
/* Initialize the backtrace data we need from an ELF executable. At /* Add the backtrace data for one ELF file. */
the ELF level, all we need to do is find the debug info
sections. */
int static int
backtrace_initialize (struct backtrace_state *state, int descriptor, elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
backtrace_error_callback error_callback, backtrace_error_callback error_callback, void *data,
void *data, fileline *fileline_fn) fileline *fileline_fn, int *found_sym, int *found_dwarf)
{ {
struct backtrace_view ehdr_view; struct backtrace_view ehdr_view;
Elf_Ehdr ehdr; Elf_Ehdr ehdr;
...@@ -400,6 +500,9 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, ...@@ -400,6 +500,9 @@ backtrace_initialize (struct backtrace_state *state, int descriptor,
struct backtrace_view debug_view; struct backtrace_view debug_view;
int debug_view_valid; int debug_view_valid;
*found_sym = 0;
*found_dwarf = 0;
shdrs_view_valid = 0; shdrs_view_valid = 0;
names_view_valid = 0; names_view_valid = 0;
symtab_view_valid = 0; symtab_view_valid = 0;
...@@ -516,6 +619,8 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, ...@@ -516,6 +619,8 @@ backtrace_initialize (struct backtrace_state *state, int descriptor,
dynsym_shndx = 0; dynsym_shndx = 0;
memset (sections, 0, sizeof sections); memset (sections, 0, sizeof sections);
/* Look for the symbol table. */
for (i = 1; i < shnum; ++i) for (i = 1; i < shnum; ++i)
{ {
const Elf_Shdr *shdr; const Elf_Shdr *shdr;
...@@ -552,12 +657,7 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, ...@@ -552,12 +657,7 @@ backtrace_initialize (struct backtrace_state *state, int descriptor,
if (symtab_shndx == 0) if (symtab_shndx == 0)
symtab_shndx = dynsym_shndx; symtab_shndx = dynsym_shndx;
if (symtab_shndx == 0) if (symtab_shndx != 0)
{
state->syminfo_fn = elf_nosyms;
state->syminfo_data = NULL;
}
else
{ {
const Elf_Shdr *symtab_shdr; const Elf_Shdr *symtab_shdr;
unsigned int strtab_shndx; unsigned int strtab_shndx;
...@@ -604,8 +704,9 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, ...@@ -604,8 +704,9 @@ backtrace_initialize (struct backtrace_state *state, int descriptor,
string table permanently. */ string table permanently. */
backtrace_release_view (state, &symtab_view, error_callback, data); backtrace_release_view (state, &symtab_view, error_callback, data);
state->syminfo_fn = elf_syminfo; *found_sym = 1;
state->syminfo_data = sdata;
elf_add_syminfo_data (state, sdata);
} }
/* FIXME: Need to handle compressed debug sections. */ /* FIXME: Need to handle compressed debug sections. */
...@@ -635,7 +736,6 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, ...@@ -635,7 +736,6 @@ backtrace_initialize (struct backtrace_state *state, int descriptor,
if (!backtrace_close (descriptor, error_callback, data)) if (!backtrace_close (descriptor, error_callback, data))
goto fail; goto fail;
*fileline_fn = elf_nodebug; *fileline_fn = elf_nodebug;
state->fileline_data = NULL;
return 1; return 1;
} }
...@@ -654,21 +754,23 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, ...@@ -654,21 +754,23 @@ backtrace_initialize (struct backtrace_state *state, int descriptor,
sections[i].data = ((const unsigned char *) debug_view.data sections[i].data = ((const unsigned char *) debug_view.data
+ (sections[i].offset - min_offset)); + (sections[i].offset - min_offset));
if (!backtrace_dwarf_initialize (state, if (!backtrace_dwarf_add (state, base_address,
sections[DEBUG_INFO].data, sections[DEBUG_INFO].data,
sections[DEBUG_INFO].size, sections[DEBUG_INFO].size,
sections[DEBUG_LINE].data, sections[DEBUG_LINE].data,
sections[DEBUG_LINE].size, sections[DEBUG_LINE].size,
sections[DEBUG_ABBREV].data, sections[DEBUG_ABBREV].data,
sections[DEBUG_ABBREV].size, sections[DEBUG_ABBREV].size,
sections[DEBUG_RANGES].data, sections[DEBUG_RANGES].data,
sections[DEBUG_RANGES].size, sections[DEBUG_RANGES].size,
sections[DEBUG_STR].data, sections[DEBUG_STR].data,
sections[DEBUG_STR].size, sections[DEBUG_STR].size,
ehdr.e_ident[EI_DATA] == ELFDATA2MSB, ehdr.e_ident[EI_DATA] == ELFDATA2MSB,
error_callback, data, fileline_fn)) error_callback, data, fileline_fn))
goto fail; goto fail;
*found_dwarf = 1;
return 1; return 1;
fail: fail:
...@@ -686,3 +788,115 @@ backtrace_initialize (struct backtrace_state *state, int descriptor, ...@@ -686,3 +788,115 @@ backtrace_initialize (struct backtrace_state *state, int descriptor,
backtrace_close (descriptor, error_callback, data); backtrace_close (descriptor, error_callback, data);
return 0; 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;
};
/* 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;
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;
descriptor = backtrace_open (info->dlpi_name, pd->error_callback, pd->data);
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))
{
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 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))
return 0;
pd.state = state;
pd.error_callback = error_callback;
pd.data = data;
pd.fileline_fn = fileline_fn;
pd.found_sym = &found_sym;
pd.found_dwarf = &found_dwarf;
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;
}
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);
}
if (!state->threaded)
{
if (state->fileline_fn == NULL || state->fileline_fn == elf_nodebug)
*fileline_fn = elf_fileline_fn;
}
else
{
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;
if (current_fn == NULL || current_fn == elf_nodebug)
*fileline_fn = elf_fileline_fn;
}
return 1;
}
...@@ -215,21 +215,22 @@ extern int backtrace_initialize (struct backtrace_state *state, ...@@ -215,21 +215,22 @@ extern int backtrace_initialize (struct backtrace_state *state,
void *data, void *data,
fileline *fileline_fn); fileline *fileline_fn);
/* Prepare to read file/line information from DWARF debug data. */ /* Add file/line information for a DWARF module. */
extern int backtrace_dwarf_initialize (struct backtrace_state *state, extern int backtrace_dwarf_add (struct backtrace_state *state,
const unsigned char* dwarf_info, uintptr_t base_address,
size_t dwarf_info_size, const unsigned char* dwarf_info,
const unsigned char *dwarf_line, size_t dwarf_info_size,
size_t dwarf_line_size, const unsigned char *dwarf_line,
const unsigned char *dwarf_abbrev, size_t dwarf_line_size,
size_t dwarf_abbrev_size, const unsigned char *dwarf_abbrev,
const unsigned char *dwarf_ranges, size_t dwarf_abbrev_size,
size_t dwarf_range_size, const unsigned char *dwarf_ranges,
const unsigned char *dwarf_str, size_t dwarf_range_size,
size_t dwarf_str_size, const unsigned char *dwarf_str,
int is_bigendian, size_t dwarf_str_size,
backtrace_error_callback error_callback, int is_bigendian,
void *data, fileline *fileline_fn); backtrace_error_callback error_callback,
void *data, fileline *fileline_fn);
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment