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
This commit is contained in:
Ian Lance Taylor 2012-10-09 18:20:45 +00:00 committed by Ian Lance Taylor
parent 1a61077e0e
commit e561a9920c
7 changed files with 578 additions and 105 deletions

View File

@ -1,3 +1,35 @@
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.

View File

@ -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 @@
/* 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

View File

@ -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

View File

@ -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

View File

@ -333,6 +333,10 @@ struct unit_addrs_vector
struct dwarf_data struct dwarf_data
{ {
/* The data for the next file we know about. */
struct dwarf_data *next;
/* The base address for this file. */
uintptr_t base_address;
/* A sorted list of address ranges. */ /* A sorted list of address ranges. */
struct unit_addrs *addrs; struct unit_addrs *addrs;
/* Number of address ranges in list. */ /* Number of address ranges in list. */
@ -831,12 +835,18 @@ function_addrs_search (const void *vkey, const void *ventry)
success, 0 on failure. */ success, 0 on failure. */
static int static int
add_unit_addr (struct backtrace_state *state, struct unit_addrs addrs, add_unit_addr (struct backtrace_state *state, uintptr_t base_address,
struct unit_addrs addrs,
backtrace_error_callback error_callback, void *data, backtrace_error_callback error_callback, void *data,
struct unit_addrs_vector *vec) struct unit_addrs_vector *vec)
{ {
struct unit_addrs *p; struct unit_addrs *p;
/* Add in the base address of the module here, so that we can look
up the PC directly. */
addrs.low += base_address;
addrs.high += base_address;
/* Try to merge with the last entry. */ /* Try to merge with the last entry. */
if (vec->count > 0) if (vec->count > 0)
{ {
@ -1156,9 +1166,10 @@ lookup_abbrev (struct abbrevs *abbrevs, uint64_t code,
1 on success, 0 on failure. */ 1 on success, 0 on failure. */
static int static int
add_unit_ranges (struct backtrace_state *state, struct unit *u, add_unit_ranges (struct backtrace_state *state, uintptr_t base_address,
uint64_t ranges, uint64_t base, int is_bigendian, struct unit *u, uint64_t ranges, uint64_t base,
const unsigned char *dwarf_ranges, size_t dwarf_ranges_size, int is_bigendian, const unsigned char *dwarf_ranges,
size_t dwarf_ranges_size,
backtrace_error_callback error_callback, void *data, backtrace_error_callback error_callback, void *data,
struct unit_addrs_vector *addrs) struct unit_addrs_vector *addrs)
{ {
@ -1202,7 +1213,8 @@ add_unit_ranges (struct backtrace_state *state, struct unit *u,
a.low = low + base; a.low = low + base;
a.high = high + base; a.high = high + base;
a.u = u; a.u = u;
if (!add_unit_addr (state, a, error_callback, data, addrs)) if (!add_unit_addr (state, base_address, a, error_callback, data,
addrs))
return 0; return 0;
} }
} }
@ -1218,7 +1230,7 @@ add_unit_ranges (struct backtrace_state *state, struct unit *u,
on success, 0 on failure. */ on success, 0 on failure. */
static int static int
build_address_map (struct backtrace_state *state, build_address_map (struct backtrace_state *state, uintptr_t base_address,
const unsigned char *dwarf_info, size_t dwarf_info_size, const unsigned char *dwarf_info, size_t dwarf_info_size,
const unsigned char *dwarf_abbrev, size_t dwarf_abbrev_size, const unsigned char *dwarf_abbrev, size_t dwarf_abbrev_size,
const unsigned char *dwarf_ranges, size_t dwarf_ranges_size, const unsigned char *dwarf_ranges, size_t dwarf_ranges_size,
@ -1417,9 +1429,10 @@ build_address_map (struct backtrace_state *state,
if (have_ranges) if (have_ranges)
{ {
if (!add_unit_ranges (state, u, ranges, lowpc, is_bigendian, if (!add_unit_ranges (state, base_address, u, ranges, lowpc,
dwarf_ranges, dwarf_ranges_size, is_bigendian, dwarf_ranges,
error_callback, data, addrs)) dwarf_ranges_size, error_callback, data,
addrs))
{ {
free_abbrevs (state, &u->abbrevs, error_callback, data); free_abbrevs (state, &u->abbrevs, error_callback, data);
backtrace_free (state, u, sizeof *u, error_callback, data); backtrace_free (state, u, sizeof *u, error_callback, data);
@ -1434,7 +1447,8 @@ build_address_map (struct backtrace_state *state,
a.high = highpc; a.high = highpc;
a.u = u; a.u = u;
if (!add_unit_addr (state, a, error_callback, data, addrs)) if (!add_unit_addr (state, base_address, a, error_callback, data,
addrs))
{ {
free_abbrevs (state, &u->abbrevs, error_callback, data); free_abbrevs (state, &u->abbrevs, error_callback, data);
backtrace_free (state, u, sizeof *u, error_callback, data); backtrace_free (state, u, sizeof *u, error_callback, data);
@ -1463,8 +1477,9 @@ build_address_map (struct backtrace_state *state,
building. Returns 1 on success, 0 on failure. */ building. Returns 1 on success, 0 on failure. */
static int static int
add_line (struct backtrace_state *state, uintptr_t pc, const char *filename, add_line (struct backtrace_state *state, struct dwarf_data *ddata,
int lineno, backtrace_error_callback error_callback, void *data, uintptr_t pc, const char *filename, int lineno,
backtrace_error_callback error_callback, void *data,
struct line_vector *vec) struct line_vector *vec)
{ {
struct line *ln; struct line *ln;
@ -1484,7 +1499,10 @@ add_line (struct backtrace_state *state, uintptr_t pc, const char *filename,
if (ln == NULL) if (ln == NULL)
return 0; return 0;
ln->pc = pc; /* Add in the base address here, so that we can look up the PC
directly. */
ln->pc = pc + ddata->base_address;
ln->filename = filename; ln->filename = filename;
ln->lineno = lineno; ln->lineno = lineno;
@ -1672,9 +1690,9 @@ read_line_header (struct backtrace_state *state, struct unit *u,
success, 0 on failure. */ success, 0 on failure. */
static int static int
read_line_program (struct backtrace_state *state, struct unit *u, read_line_program (struct backtrace_state *state, struct dwarf_data *ddata,
const struct line_header *hdr, struct dwarf_buf *line_buf, struct unit *u, const struct line_header *hdr,
struct line_vector *vec) struct dwarf_buf *line_buf, struct line_vector *vec)
{ {
uint64_t address; uint64_t address;
unsigned int op_index; unsigned int op_index;
@ -1706,8 +1724,8 @@ read_line_program (struct backtrace_state *state, struct unit *u,
/ hdr->max_ops_per_insn); / hdr->max_ops_per_insn);
op_index = (op_index + advance) % hdr->max_ops_per_insn; op_index = (op_index + advance) % hdr->max_ops_per_insn;
lineno += hdr->line_base + (int) (op % hdr->line_range); lineno += hdr->line_base + (int) (op % hdr->line_range);
add_line (state, address, filename, lineno, line_buf->error_callback, add_line (state, ddata, address, filename, lineno,
line_buf->data, vec); line_buf->error_callback, line_buf->data, vec);
} }
else if (op == DW_LNS_extended_op) else if (op == DW_LNS_extended_op)
{ {
@ -1795,7 +1813,7 @@ read_line_program (struct backtrace_state *state, struct unit *u,
switch (op) switch (op)
{ {
case DW_LNS_copy: case DW_LNS_copy:
add_line (state, address, filename, lineno, add_line (state, ddata, address, filename, lineno,
line_buf->error_callback, line_buf->data, vec); line_buf->error_callback, line_buf->data, vec);
break; break;
case DW_LNS_advance_pc: case DW_LNS_advance_pc:
@ -1923,7 +1941,7 @@ read_line_info (struct backtrace_state *state, struct dwarf_data *ddata,
if (!read_line_header (state, u, is_dwarf64, &line_buf, hdr)) if (!read_line_header (state, u, is_dwarf64, &line_buf, hdr))
goto fail; goto fail;
if (!read_line_program (state, u, hdr, &line_buf, &vec)) if (!read_line_program (state, ddata, u, hdr, &line_buf, &vec))
goto fail; goto fail;
if (line_buf.reported_underflow) if (line_buf.reported_underflow)
@ -2076,13 +2094,18 @@ read_referenced_name (struct dwarf_data *ddata, struct unit *u,
success, 0 on error. */ success, 0 on error. */
static int static int
add_function_range (struct backtrace_state *state, struct function *function, add_function_range (struct backtrace_state *state, struct dwarf_data *ddata,
uint64_t lowpc, uint64_t highpc, struct function *function, uint64_t lowpc, uint64_t highpc,
backtrace_error_callback error_callback, backtrace_error_callback error_callback,
void *data, struct function_vector *vec) void *data, struct function_vector *vec)
{ {
struct function_addrs *p; struct function_addrs *p;
/* Add in the base address here, so that we can look up the PC
directly. */
lowpc += ddata->base_address;
highpc += ddata->base_address;
if (vec->count > 0) if (vec->count > 0)
{ {
p = (struct function_addrs *) vec->vec.base + vec->count - 1; p = (struct function_addrs *) vec->vec.base + vec->count - 1;
@ -2153,8 +2176,8 @@ add_function_ranges (struct backtrace_state *state, struct dwarf_data *ddata,
base = high; base = high;
else else
{ {
if (!add_function_range (state, function, low + base, high + base, if (!add_function_range (state, ddata, function, low + base,
error_callback, data, vec)) high + base, error_callback, data, vec))
return 0; return 0;
} }
} }
@ -2364,7 +2387,7 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata,
{ {
if (highpc_is_relative) if (highpc_is_relative)
highpc += lowpc; highpc += lowpc;
if (!add_function_range (state, function, lowpc, highpc, if (!add_function_range (state, ddata, function, lowpc, highpc,
error_callback, data, vec)) error_callback, data, vec))
return 0; return 0;
} }
@ -2522,15 +2545,17 @@ report_inlined_functions (uintptr_t pc, struct function *function,
return 0; return 0;
} }
/* Return the file/line information for a PC using the DWARF mapping /* Look for a PC in the DWARF mapping for one module. On success,
we built earlier. */ call CALLBACK and return whatever it returns. On error, call
ERROR_CALLBACK and return 0. Sets *FOUND to 1 if the PC is found,
0 if not. */
static int static int
dwarf_fileline (struct backtrace_state *state, uintptr_t pc, dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
backtrace_full_callback callback, uintptr_t pc, backtrace_full_callback callback,
backtrace_error_callback error_callback, void *data) backtrace_error_callback error_callback, void *data,
int *found)
{ {
struct dwarf_data *ddata;
struct unit_addrs *entry; struct unit_addrs *entry;
struct unit *u; struct unit *u;
int new_data; int new_data;
@ -2542,14 +2567,17 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc,
int lineno; int lineno;
int ret; int ret;
ddata = (struct dwarf_data *) state->fileline_data; *found = 1;
/* Find an address range that includes PC. */ /* Find an address range that includes PC. */
entry = bsearch (&pc, ddata->addrs, ddata->addrs_count, entry = bsearch (&pc, ddata->addrs, ddata->addrs_count,
sizeof (struct unit_addrs), unit_addrs_search); sizeof (struct unit_addrs), unit_addrs_search);
if (entry == NULL) if (entry == NULL)
return callback (data, pc, NULL, 0, NULL); {
*found = 0;
return 0;
}
/* If there are multiple ranges that contain PC, use the last one, /* If there are multiple ranges that contain PC, use the last one,
in order to produce predictable results. If we assume that all in order to produce predictable results. If we assume that all
@ -2656,7 +2684,8 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc,
try again to see if there is a better compilation unit for try again to see if there is a better compilation unit for
this PC. */ this PC. */
if (new_data) if (new_data)
dwarf_fileline (state, pc, callback, error_callback, data); return dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
data, found);
return callback (data, pc, NULL, 0, NULL); return callback (data, pc, NULL, 0, NULL);
} }
@ -2705,39 +2734,93 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc,
return callback (data, pc, filename, lineno, function->name); return callback (data, pc, filename, lineno, function->name);
} }
/* Build our data structures from the .debug_info and .debug_line
sections. Set *FILELINE_FN and *FILELINE_DATA. Return 1 on
success, 0 on failure. */
int /* Return the file/line information for a PC using the DWARF mapping
backtrace_dwarf_initialize (struct backtrace_state *state, we built earlier. */
const unsigned char *dwarf_info,
size_t dwarf_info_size, static int
const unsigned char *dwarf_line, dwarf_fileline (struct backtrace_state *state, uintptr_t pc,
size_t dwarf_line_size, backtrace_full_callback callback,
const unsigned char *dwarf_abbrev, backtrace_error_callback error_callback, void *data)
size_t dwarf_abbrev_size, {
const unsigned char *dwarf_ranges, struct dwarf_data *ddata;
size_t dwarf_ranges_size, int found;
const unsigned char *dwarf_str, int ret;
size_t dwarf_str_size,
int is_bigendian, if (!state->threaded)
backtrace_error_callback error_callback, {
void *data, fileline *fileline_fn) for (ddata = (struct dwarf_data *) state->fileline_data;
ddata != NULL;
ddata = ddata->next)
{
ret = dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
data, &found);
if (ret != 0 || found)
return ret;
}
}
else
{
struct dwarf_data **pp;
pp = (struct dwarf_data **) &state->fileline_data;
while (1)
{
ddata = *pp;
/* Atomic load. */
while (!__sync_bool_compare_and_swap (pp, ddata, ddata))
ddata = *pp;
if (ddata == NULL)
break;
ret = dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
data, &found);
if (ret != 0 || found)
return ret;
pp = &ddata->next;
}
}
/* FIXME: See if any libraries have been dlopen'ed. */
return callback (data, pc, NULL, 0, NULL);
}
/* Initialize our data structures from the DWARF debug info for a
file. Return NULL on failure. */
static struct dwarf_data *
build_dwarf_data (struct backtrace_state *state,
uintptr_t base_address,
const unsigned char *dwarf_info,
size_t dwarf_info_size,
const unsigned char *dwarf_line,
size_t dwarf_line_size,
const unsigned char *dwarf_abbrev,
size_t dwarf_abbrev_size,
const unsigned char *dwarf_ranges,
size_t dwarf_ranges_size,
const unsigned char *dwarf_str,
size_t dwarf_str_size,
int is_bigendian,
backtrace_error_callback error_callback,
void *data)
{ {
struct unit_addrs_vector addrs_vec; struct unit_addrs_vector addrs_vec;
struct unit_addrs *addrs; struct unit_addrs *addrs;
size_t addrs_count; size_t addrs_count;
struct dwarf_data *fdata; struct dwarf_data *fdata;
if (!build_address_map (state, dwarf_info, dwarf_info_size, dwarf_abbrev, if (!build_address_map (state, base_address, dwarf_info, dwarf_info_size,
dwarf_abbrev_size, dwarf_ranges, dwarf_ranges_size, dwarf_abbrev, dwarf_abbrev_size, dwarf_ranges,
dwarf_str, dwarf_str_size, is_bigendian, dwarf_ranges_size, dwarf_str, dwarf_str_size,
error_callback, data, &addrs_vec)) is_bigendian, error_callback, data, &addrs_vec))
return 0; return NULL;
if (!backtrace_vector_release (state, &addrs_vec.vec, error_callback, data)) if (!backtrace_vector_release (state, &addrs_vec.vec, error_callback, data))
return 0; return NULL;
addrs = (struct unit_addrs *) addrs_vec.vec.base; addrs = (struct unit_addrs *) addrs_vec.vec.base;
addrs_count = addrs_vec.count; addrs_count = addrs_vec.count;
qsort (addrs, addrs_count, sizeof (struct unit_addrs), unit_addrs_compare); qsort (addrs, addrs_count, sizeof (struct unit_addrs), unit_addrs_compare);
@ -2746,8 +2829,10 @@ backtrace_dwarf_initialize (struct backtrace_state *state,
backtrace_alloc (state, sizeof (struct dwarf_data), backtrace_alloc (state, sizeof (struct dwarf_data),
error_callback, data)); error_callback, data));
if (fdata == NULL) if (fdata == NULL)
return 0; return NULL;
fdata->next = NULL;
fdata->base_address = base_address;
fdata->addrs = addrs; fdata->addrs = addrs;
fdata->addrs_count = addrs_count; fdata->addrs_count = addrs_count;
fdata->dwarf_info = dwarf_info; fdata->dwarf_info = dwarf_info;
@ -2761,7 +2846,77 @@ backtrace_dwarf_initialize (struct backtrace_state *state,
fdata->is_bigendian = is_bigendian; fdata->is_bigendian = is_bigendian;
memset (&fdata->fvec, 0, sizeof fdata->fvec); memset (&fdata->fvec, 0, sizeof fdata->fvec);
state->fileline_data = fdata; return fdata;
}
/* Build our data structures from the DWARF sections for a module.
Set FILELINE_FN and STATE->FILELINE_DATA. Return 1 on success, 0
on failure. */
int
backtrace_dwarf_add (struct backtrace_state *state,
uintptr_t base_address,
const unsigned char *dwarf_info,
size_t dwarf_info_size,
const unsigned char *dwarf_line,
size_t dwarf_line_size,
const unsigned char *dwarf_abbrev,
size_t dwarf_abbrev_size,
const unsigned char *dwarf_ranges,
size_t dwarf_ranges_size,
const unsigned char *dwarf_str,
size_t dwarf_str_size,
int is_bigendian,
backtrace_error_callback error_callback,
void *data, fileline *fileline_fn)
{
struct dwarf_data *fdata;
fdata = build_dwarf_data (state, base_address, dwarf_info, dwarf_info_size,
dwarf_line, dwarf_line_size, dwarf_abbrev,
dwarf_abbrev_size, dwarf_ranges, dwarf_ranges_size,
dwarf_str, dwarf_str_size, is_bigendian,
error_callback, data);
if (fdata == NULL)
return 0;
if (!state->threaded)
{
struct dwarf_data **pp;
for (pp = (struct dwarf_data **) &state->fileline_data;
*pp != NULL;
pp = &(*pp)->next)
;
*pp = fdata;
}
else
{
while (1)
{
struct dwarf_data **pp;
pp = (struct dwarf_data **) &state->fileline_data;
while (1)
{
struct dwarf_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, fdata))
break;
}
}
*fileline_fn = dwarf_fileline; *fileline_fn = dwarf_fileline;

View File

@ -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. */
#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
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,
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,
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,
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,
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,
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,
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,
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,
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,
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;
}

View File

@ -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