commit 6147ee1a9aeeb748563a8998033f2ce195460162 Author: Denis Khalikov Date: Mon Mar 13 18:55:36 2017 +0300 PR sanitizer/77631 * Makefile.am: Update to support glinktest. * Makefile.in: Regenerated. * configure.ac: Add AC_CHECK_PROG for objcopy. * configure: Regenerated. * elf.c (elf_header_is_valid): New function. Verify elf header. (elf_add): Move code which reads elf header to elf_header_is_valid. (elf_gnu_debuglink_section): New function. Read debuglink section from elf. (phdr_callback): Call backtrace_open_debugfile function for shared library. * fileline.c (fileline_initialize): Call backtrace_open_debugfile function for executable. * glinktest.c: New test. * internal.h (MAX_PATH_LEN): Defined new variable. * posix.c (enum type_of_file): New enum. (enum debug_path): New enum. (getl32): New function. (gnu_debuglink_crc32): New function. Generate crc32 sum. (get_crc32): New function. (pathlen): New function. (check_sum): New function. Verify sum. (get_debug_filename_len): New function. (backtrace_readlink): New function. (backtrace_open_debugfile): New function. diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog index 25cd921..cec5d3f 100644 --- a/libbacktrace/ChangeLog +++ b/libbacktrace/ChangeLog @@ -1,3 +1,30 @@ +2017-03-13 Denis Khalikov + + PR sanitizer/77631 + * Makefile.am: Update to support glinktest. + * Makefile.in: Regenerated. + * configure: Add script to find objcopy in the system. + * elf.c (elf_header_is_valid): New function. Verify elf header. + (elf_add): Move code which reads elf header to elf_header_is_valid. + (elf_gnu_debuglink_section): New function. Read debuglink section from + elf. + (phdr_callback): Call backtrace_open_debugfile function for shared + library. + * fileline.c (fileline_initialize): Call backtrace_open_debugfile + function for executable. + * glinktest.c: New test. + * internal.h (MAX_PATH_LEN): Defined new variable. + * posix.c (enum type_of_file): New enum. + (enum debug_path): New enum. + (getl32): New function. + (gnu_debuglink_crc32): New function. Generate crc32 sum. + (get_crc32): New function. + (pathlen): New function. + (check_sum): New function. Verify sum. + (get_debug_filename_len): New function. + (backtrace_readlink): New function. + (backtrace_open_debugfile): New function. + 2017-03-08 Sam Thursfield * btest.c (test5): Replace #ifdef guard with 'unused' attribute diff --git a/libbacktrace/Makefile.am b/libbacktrace/Makefile.am index 344dad5..17460a5 100644 --- a/libbacktrace/Makefile.am +++ b/libbacktrace/Makefile.am @@ -81,6 +81,17 @@ libbacktrace_la_LIBADD = \ libbacktrace_la_DEPENDENCIES = $(libbacktrace_la_LIBADD) +clean_separate:glinktest.debug + rm -f glinktest.debug + +separate: glinktest + if test -n "$(OBJCOPY)" && test -n "$(STRIP)"; \ + then \ + $(OBJCOPY) --only-keep-debug glinktest glinktest.debug;\ + $(STRIP) glinktest;\ + $(OBJCOPY) --add-gnu-debuglink=glinktest.debug glinktest;\ + fi; + # Testsuite. check_PROGRAMS = @@ -100,6 +111,12 @@ stest_LDADD = libbacktrace.la check_PROGRAMS += stest +glinktest_SOURCES = glinktest.c +glinktest_CFLAGS = $(AM_CFLAGS) -g -O +glinktest_LDADD = libbacktrace.la + +check_PROGRAMS += glinktest + endif NATIVE # We can't use automake's automatic dependency tracking, because it @@ -134,3 +151,4 @@ sort.lo: config.h backtrace.h internal.h stest.lo: config.h backtrace.h internal.h state.lo: config.h backtrace.h backtrace-supported.h internal.h unknown.lo: config.h backtrace.h internal.h +glinktest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h diff --git a/libbacktrace/Makefile.in b/libbacktrace/Makefile.in index de74b5d..354ecc7 100644 --- a/libbacktrace/Makefile.in +++ b/libbacktrace/Makefile.in @@ -84,7 +84,7 @@ build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ check_PROGRAMS = $(am__EXEEXT_1) -@NATIVE_TRUE@am__append_1 = btest stest +@NATIVE_TRUE@am__append_1 = btest stest glinktest subdir = . DIST_COMMON = README ChangeLog $(srcdir)/Makefile.in \ $(srcdir)/Makefile.am $(top_srcdir)/configure \ @@ -113,13 +113,20 @@ am__DEPENDENCIES_1 = am_libbacktrace_la_OBJECTS = atomic.lo dwarf.lo fileline.lo posix.lo \ print.lo sort.lo state.lo libbacktrace_la_OBJECTS = $(am_libbacktrace_la_OBJECTS) -@NATIVE_TRUE@am__EXEEXT_1 = btest$(EXEEXT) stest$(EXEEXT) +@NATIVE_TRUE@am__EXEEXT_1 = btest$(EXEEXT) stest$(EXEEXT) \ +@NATIVE_TRUE@ glinktest$(EXEEXT) @NATIVE_TRUE@am_btest_OBJECTS = btest-btest.$(OBJEXT) btest_OBJECTS = $(am_btest_OBJECTS) @NATIVE_TRUE@btest_DEPENDENCIES = libbacktrace.la btest_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CCLD) $(btest_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ +@NATIVE_TRUE@am_glinktest_OBJECTS = glinktest-glinktest.$(OBJEXT) +glinktest_OBJECTS = $(am_glinktest_OBJECTS) +@NATIVE_TRUE@glinktest_DEPENDENCIES = libbacktrace.la +glinktest_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(glinktest_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ @NATIVE_TRUE@am_stest_OBJECTS = stest.$(OBJEXT) stest_OBJECTS = $(am_stest_OBJECTS) @NATIVE_TRUE@stest_DEPENDENCIES = libbacktrace.la @@ -136,7 +143,7 @@ LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ SOURCES = $(libbacktrace_la_SOURCES) $(EXTRA_libbacktrace_la_SOURCES) \ - $(btest_SOURCES) $(stest_SOURCES) + $(btest_SOURCES) $(glinktest_SOURCES) $(stest_SOURCES) MULTISRCTOP = MULTIBUILDTOP = MULTIDIRS = @@ -200,6 +207,7 @@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ +OBJCOPY = @OBJCOPY@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ @@ -330,6 +338,9 @@ TESTS = $(check_PROGRAMS) @NATIVE_TRUE@btest_LDADD = libbacktrace.la @NATIVE_TRUE@stest_SOURCES = stest.c @NATIVE_TRUE@stest_LDADD = libbacktrace.la +@NATIVE_TRUE@glinktest_SOURCES = glinktest.c +@NATIVE_TRUE@glinktest_CFLAGS = $(AM_CFLAGS) -g -O +@NATIVE_TRUE@glinktest_LDADD = libbacktrace.la # We can't use automake's automatic dependency tracking, because it # breaks when using bootstrap-lean. Automatic dependency tracking @@ -422,6 +433,9 @@ clean-checkPROGRAMS: btest$(EXEEXT): $(btest_OBJECTS) $(btest_DEPENDENCIES) $(EXTRA_btest_DEPENDENCIES) @rm -f btest$(EXEEXT) $(btest_LINK) $(btest_OBJECTS) $(btest_LDADD) $(LIBS) +glinktest$(EXEEXT): $(glinktest_OBJECTS) $(glinktest_DEPENDENCIES) $(EXTRA_glinktest_DEPENDENCIES) + @rm -f glinktest$(EXEEXT) + $(glinktest_LINK) $(glinktest_OBJECTS) $(glinktest_LDADD) $(LIBS) stest$(EXEEXT): $(stest_OBJECTS) $(stest_DEPENDENCIES) $(EXTRA_stest_DEPENDENCIES) @rm -f stest$(EXEEXT) $(LINK) $(stest_OBJECTS) $(stest_LDADD) $(LIBS) @@ -447,6 +461,12 @@ btest-btest.o: btest.c btest-btest.obj: btest.c $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(btest_CFLAGS) $(CFLAGS) -c -o btest-btest.obj `if test -f 'btest.c'; then $(CYGPATH_W) 'btest.c'; else $(CYGPATH_W) '$(srcdir)/btest.c'; fi` +glinktest-glinktest.o: glinktest.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(glinktest_CFLAGS) $(CFLAGS) -c -o glinktest-glinktest.o `test -f 'glinktest.c' || echo '$(srcdir)/'`glinktest.c + +glinktest-glinktest.obj: glinktest.c + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(glinktest_CFLAGS) $(CFLAGS) -c -o glinktest-glinktest.obj `if test -f 'glinktest.c'; then $(CYGPATH_W) 'glinktest.c'; else $(CYGPATH_W) '$(srcdir)/glinktest.c'; fi` + mostlyclean-libtool: -rm -f *.lo @@ -745,6 +765,17 @@ uninstall-am: mostlyclean-multi pdf pdf-am ps ps-am tags uninstall \ uninstall-am + +clean_separate:glinktest.debug + rm -f glinktest.debug + +separate: glinktest + if test -n "$(OBJCOPY)" && test -n "$(STRIP)"; \ + then \ + $(OBJCOPY) --only-keep-debug glinktest glinktest.debug;\ + $(STRIP) glinktest;\ + $(OBJCOPY) --add-gnu-debuglink=glinktest.debug glinktest;\ + fi; alloc.lo: config.h backtrace.h internal.h backtrace.lo: config.h backtrace.h internal.h btest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h @@ -764,6 +795,7 @@ sort.lo: config.h backtrace.h internal.h stest.lo: config.h backtrace.h internal.h state.lo: config.h backtrace.h backtrace-supported.h internal.h unknown.lo: config.h backtrace.h internal.h +glinktest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/libbacktrace/configure b/libbacktrace/configure index ee90bc6..dda8b09 100755 --- a/libbacktrace/configure +++ b/libbacktrace/configure @@ -630,6 +630,7 @@ LD FGREP SED LIBTOOL +OBJCOPY RANLIB MAINT MAINTAINER_MODE_FALSE @@ -5011,6 +5012,44 @@ else fi +# Extract the first word of "objcopy", so it can be a program name with args. +set dummy objcopy; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_OBJCOPY+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OBJCOPY"; then + ac_cv_prog_OBJCOPY="$OBJCOPY" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_OBJCOPY="objcopy" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OBJCOPY=$ac_cv_prog_OBJCOPY +if test -n "$OBJCOPY"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJCOPY" >&5 +$as_echo "$OBJCOPY" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. @@ -11131,7 +11170,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11134 "configure" +#line 11173 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11237,7 +11276,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11240 "configure" +#line 11279 "configure" #include "confdefs.h" #if HAVE_DLFCN_H diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac index f9cad21..9e1e4c4 100644 --- a/libbacktrace/configure.ac +++ b/libbacktrace/configure.ac @@ -74,6 +74,8 @@ AC_SUBST(CFLAGS) AC_PROG_RANLIB +AC_CHECK_PROG(OBJCOPY, objcopy, objcopy) + AC_PROG_AWK case "$AWK" in "") AC_MSG_ERROR([can't build without awk]) ;; diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c index 89ed42b..697a8c9 100644 --- a/libbacktrace/elf.c +++ b/libbacktrace/elf.c @@ -510,70 +510,34 @@ elf_syminfo (struct backtrace_state *state, uintptr_t addr, callback (data, addr, sym->name, sym->address, sym->size); } -/* 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. */ +/* Return 1 if header is valid and -1 on fail */ 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) +elf_header_is_valid (struct backtrace_state *state, int descriptor, + backtrace_error_callback error_callback, void *data, int exe, + off_t *shoff_out, unsigned int *shnum_out, + unsigned int *shstrndx_out, b_elf_ehdr *ehdr_out) { - struct backtrace_view ehdr_view; - b_elf_ehdr ehdr; - off_t shoff; - unsigned int shnum; - unsigned int shstrndx; - struct backtrace_view shdrs_view; - int shdrs_view_valid; - const b_elf_shdr *shdrs; - const b_elf_shdr *shstrhdr; - size_t shstr_size; - off_t shstr_off; - struct backtrace_view names_view; - int names_view_valid; - const char *names; - unsigned int symtab_shndx; - unsigned int dynsym_shndx; - unsigned int i; - struct debug_section_info sections[DEBUG_MAX]; - struct backtrace_view symtab_view; - int symtab_view_valid; - struct backtrace_view strtab_view; - int strtab_view_valid; - off_t min_offset; - off_t max_offset; - 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; - strtab_view_valid = 0; - debug_view_valid = 0; + struct backtrace_view ehdr_view; - if (!backtrace_get_view (state, descriptor, 0, sizeof ehdr, error_callback, - data, &ehdr_view)) + if (!backtrace_get_view (state, descriptor, 0, sizeof *ehdr_out, + error_callback, data, &ehdr_view)) goto fail; - memcpy (&ehdr, ehdr_view.data, sizeof ehdr); + memcpy (ehdr_out, ehdr_view.data, sizeof *ehdr_out); backtrace_release_view (state, &ehdr_view, error_callback, data); - if (ehdr.e_ident[EI_MAG0] != ELFMAG0 - || ehdr.e_ident[EI_MAG1] != ELFMAG1 - || ehdr.e_ident[EI_MAG2] != ELFMAG2 - || ehdr.e_ident[EI_MAG3] != ELFMAG3) + if (ehdr_out->e_ident[EI_MAG0] != ELFMAG0 + || ehdr_out->e_ident[EI_MAG1] != ELFMAG1 + || ehdr_out->e_ident[EI_MAG2] != ELFMAG2 + || ehdr_out->e_ident[EI_MAG3] != ELFMAG3) { error_callback (data, "executable file is not ELF", 0); goto fail; } - if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) + if (ehdr_out->e_ident[EI_VERSION] != EV_CURRENT) { error_callback (data, "executable file is unrecognized ELF version", 0); goto fail; @@ -585,14 +549,14 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, #define BACKTRACE_ELFCLASS ELFCLASS64 #endif - if (ehdr.e_ident[EI_CLASS] != BACKTRACE_ELFCLASS) + if (ehdr_out->e_ident[EI_CLASS] != BACKTRACE_ELFCLASS) { error_callback (data, "executable file is unexpected ELF class", 0); goto fail; } - if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB - && ehdr.e_ident[EI_DATA] != ELFDATA2MSB) + if (ehdr_out->e_ident[EI_DATA] != ELFDATA2LSB + && ehdr_out->e_ident[EI_DATA] != ELFDATA2MSB) { error_callback (data, "executable file has unknown endianness", 0); goto fail; @@ -601,31 +565,30 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, /* 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) + if (exe && ehdr_out->e_type == ET_DYN) return -1; - shoff = ehdr.e_shoff; - shnum = ehdr.e_shnum; - shstrndx = ehdr.e_shstrndx; + *shoff_out = ehdr_out->e_shoff; + *shnum_out = ehdr_out->e_shnum; + *shstrndx_out = ehdr_out->e_shstrndx; - if ((shnum == 0 || shstrndx == SHN_XINDEX) - && shoff != 0) + if ((*shnum_out == 0 || *shstrndx_out == SHN_XINDEX) && *shoff_out != 0) { struct backtrace_view shdr_view; const b_elf_shdr *shdr; - if (!backtrace_get_view (state, descriptor, shoff, sizeof shdr, + if (!backtrace_get_view (state, descriptor, *shoff_out, sizeof shdr, error_callback, data, &shdr_view)) goto fail; shdr = (const b_elf_shdr *) shdr_view.data; - if (shnum == 0) - shnum = shdr->sh_size; + if (*shnum_out == 0) + *shnum_out = shdr->sh_size; - if (shstrndx == SHN_XINDEX) + if (*shstrndx_out == SHN_XINDEX) { - shstrndx = shdr->sh_link; + *shstrndx_out = shdr->sh_link; /* Versions of the GNU binutils between 2.12 and 2.18 did not handle objects with more than SHN_LORESERVE sections @@ -638,21 +601,173 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address, section header string table index is larger than the number of sections, then we know we have to subtract 0x100 to get the real section index. */ - if (shstrndx >= shnum && shstrndx >= SHN_LORESERVE + 0x100) - shstrndx -= 0x100; + if (*shstrndx_out >= *shnum_out + && *shstrndx_out >= SHN_LORESERVE + 0x100) + *shstrndx_out -= 0x100; } - backtrace_release_view (state, &shdr_view, error_callback, data); } + return 1; +fail: + return -1; +} + +/* Return the pointer to char array with data from .gnudebuglink section inside. */ + +unsigned char * +elf_gnu_debuglink_section (struct backtrace_state *state, int descriptor, + backtrace_error_callback error_callback, void *data, + int exe, int *gnulink_data_len_out) +{ + b_elf_ehdr ehdr; + off_t shoff; + unsigned int shnum; + unsigned int shstrndx; + struct backtrace_view shdrs_view; + int shdrs_view_valid; + 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 gnulink_view; + int names_view_valid; + const char *names; + unsigned int i; + int gnulink_view_valid; + unsigned char *gnulink_data; + + gnulink_view_valid = 0; + shdrs_view_valid = 0; + names_view_valid = 0; + gnulink_data = NULL; + + if (!elf_header_is_valid (state, descriptor, error_callback, data, exe, &shoff, + &shnum, &shstrndx, &ehdr)) + goto exit; + + if (!backtrace_get_view (state, descriptor, shoff + sizeof (b_elf_shdr), + (shnum - 1) * sizeof (b_elf_shdr), error_callback, + data, &shdrs_view)) + goto exit; + + shdrs_view_valid = 1; + shdrs = (const b_elf_shdr *) shdrs_view.data; + + /* Read the section names. */ + + shstrhdr = &shdrs[shstrndx - 1]; + shstr_size = shstrhdr->sh_size; + shstr_off = shstrhdr->sh_offset; + + if (!backtrace_get_view (state, descriptor, shstr_off, shstr_size, + error_callback, data, &names_view)) + goto exit; + + names_view_valid = 1; + names = (const char *) names_view.data; + + /* Look for for the .gnu_debuglink section */ + for (i = 1; i < shnum; ++i) + { + const b_elf_shdr *shdr; + unsigned int sh_name; + const char *name; + shdr = &shdrs[i - 1]; + sh_name = shdr->sh_name; + if (sh_name >= shstr_size) + { + error_callback (data, "ELF section name out of range", 0); + goto exit; + } + name = names + sh_name; + if (strcmp (name, ".gnu_debuglink") == 0) + { + if (backtrace_get_view (state, descriptor, shdr->sh_offset, + shdr->sh_size, error_callback, data, + &gnulink_view)) + { + + gnulink_view_valid = 1; + gnulink_data + = backtrace_alloc (state, shdr->sh_size, error_callback, data); + if (gnulink_data == NULL) + goto exit; + memcpy (gnulink_data, gnulink_view.data, shdr->sh_size); + *gnulink_data_len_out = shdr->sh_size; + } + break; + } + } + +exit: + if (shdrs_view_valid) + backtrace_release_view (state, &shdrs_view, error_callback, data); + if (names_view_valid) + backtrace_release_view (state, &names_view, error_callback, data); + if (gnulink_view_valid) + backtrace_release_view (state, &gnulink_view, error_callback, data); + return gnulink_data; +} + +/* 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, int exe) +{ + b_elf_ehdr ehdr; + off_t shoff; + unsigned int shnum; + unsigned int shstrndx; + struct backtrace_view shdrs_view; + int shdrs_view_valid; + const b_elf_shdr *shdrs; + const b_elf_shdr *shstrhdr; + size_t shstr_size; + off_t shstr_off; + struct backtrace_view names_view; + int names_view_valid; + const char *names; + unsigned int symtab_shndx; + unsigned int dynsym_shndx; + unsigned int i; + struct debug_section_info sections[DEBUG_MAX]; + struct backtrace_view symtab_view; + int symtab_view_valid; + struct backtrace_view strtab_view; + int strtab_view_valid; + off_t min_offset; + off_t max_offset; + 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; + strtab_view_valid = 0; + debug_view_valid = 0; + + if (!elf_header_is_valid (state, descriptor, error_callback, data, exe, &shoff, + &shnum, &shstrndx, &ehdr)) + goto fail; /* To translate PC to file/line when using DWARF, we need to find - the .debug_info and .debug_line sections. */ + the .debug_info and .debug_line sections. */ /* Read the section headers, skipping the first one. */ if (!backtrace_get_view (state, descriptor, shoff + sizeof (b_elf_shdr), - (shnum - 1) * sizeof (b_elf_shdr), - error_callback, data, &shdrs_view)) + (shnum - 1) * sizeof (b_elf_shdr), error_callback, + data, &shdrs_view)) goto fail; shdrs_view_valid = 1; shdrs = (const b_elf_shdr *) shdrs_view.data; @@ -877,6 +992,7 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, int does_not_exist; fileline elf_fileline_fn; int found_dwarf; + int debugfile_does_not_exist; /* 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 @@ -896,8 +1012,12 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, pd->exe_descriptor = -1; } - descriptor = backtrace_open (info->dlpi_name, pd->error_callback, - pd->data, &does_not_exist); + descriptor + = backtrace_open_debugfile (info->dlpi_name, pd->error_callback, pd->data, + &debugfile_does_not_exist, pd->state, 0); + if (descriptor < 0) + descriptor = backtrace_open (info->dlpi_name, pd->error_callback, + pd->data, &does_not_exist); if (descriptor < 0) return 0; } diff --git a/libbacktrace/fileline.c b/libbacktrace/fileline.c index 0fd350a..5699a8e 100644 --- a/libbacktrace/fileline.c +++ b/libbacktrace/fileline.c @@ -84,6 +84,7 @@ fileline_initialize (struct backtrace_state *state, { const char *filename; int does_not_exist; + int debugfile_does_not_exist; switch (pass) { @@ -106,8 +107,14 @@ fileline_initialize (struct backtrace_state *state, if (filename == NULL) continue; - descriptor = backtrace_open (filename, error_callback, data, - &does_not_exist); + descriptor + = backtrace_open_debugfile (filename, error_callback, data, + &debugfile_does_not_exist, state, 1); + + if (descriptor < 0) + descriptor + = backtrace_open (filename, error_callback, data, &does_not_exist); + if (descriptor < 0 && !does_not_exist) { called_error_callback = 1; diff --git a/libbacktrace/glinktest.c b/libbacktrace/glinktest.c new file mode 100644 index 0000000..1a86b43 --- /dev/null +++ b/libbacktrace/glinktest.c @@ -0,0 +1,365 @@ +/* glinktest.c -- Test for libbacktrace library + Copyright (C) 2012-2017 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Google. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + (1) Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + (2) Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + (3) The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. */ + +/* This program tests the externally visible interfaces of the + libbacktrace library. */ + +#include +#include +#include +#include + +#include "backtrace.h" +#include "backtrace-supported.h" +#ifndef GCC_VERSION +#define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) +#endif + +#if (GCC_VERSION < 2007) +#define __attribute__(x) +#endif + +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#endif + +/* Used to collect backtrace info. */ + +/*Just a simple test copied from btest.c, but in this case we don't have debug + * info in the executable and test should verify that we can read debug info + * from separate file. See Makefile check-TESTS target. */ + +struct info +{ + char *filename; + int lineno; + char *function; +}; + +/* Passed to backtrace callback function. */ + +struct bdata +{ + struct info *all; + size_t index; + size_t max; + int failed; +}; + +/* Passed to backtrace_simple callback function. */ + +struct sdata +{ + uintptr_t *addrs; + size_t index; + size_t max; + int failed; +}; + +/* Passed to backtrace_syminfo callback function. */ + +struct symdata +{ + const char *name; + uintptr_t val, size; + int failed; +}; + +/* The backtrace state. */ + +static void *state; + +/* The number of failures. */ + +static int failures; + +/* The backtrace callback function. */ + +static int +callback_one (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED, const char *filename, + int lineno, const char *function) +{ + struct bdata *data = (struct bdata *) vdata; + struct info *p; + + if (data->index >= data->max) + { + fprintf (stderr, "callback_one: callback called too many times\n"); + data->failed = 1; + return 1; + } + + p = &data->all[data->index]; + if (filename == NULL) + p->filename = NULL; + else + { + p->filename = strdup (filename); + assert (p->filename != NULL); + } + p->lineno = lineno; + if (function == NULL) + p->function = NULL; + else + { + p->function = strdup (function); + assert (p->function != NULL); + } + ++data->index; + + return 0; +} + +/* An error callback passed to backtrace. */ + +static void +error_callback_one (void *vdata, const char *msg, int errnum) +{ + struct bdata *data = (struct bdata *) vdata; + + fprintf (stderr, "%s", msg); + if (errnum > 0) + fprintf (stderr, ": %s", strerror (errnum)); + fprintf (stderr, "\n"); + data->failed = 1; +} + +/* The backtrace_simple callback function. */ + +static int +callback_two (void *vdata, uintptr_t pc) +{ + struct sdata *data = (struct sdata *) vdata; + + if (data->index >= data->max) + { + fprintf (stderr, "callback_two: callback called too many times\n"); + data->failed = 1; + return 1; + } + + data->addrs[data->index] = pc; + ++data->index; + + return 0; +} + +/* An error callback passed to backtrace_simple. */ + +static void +error_callback_two (void *vdata, const char *msg, int errnum) +{ + struct sdata *data = (struct sdata *) vdata; + + fprintf (stderr, "%s", msg); + if (errnum > 0) + fprintf (stderr, ": %s", strerror (errnum)); + fprintf (stderr, "\n"); + data->failed = 1; +} + +/* The backtrace_syminfo callback function. */ + +static void +callback_three (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED, const char *symname, + uintptr_t symval, uintptr_t symsize) +{ + struct symdata *data = (struct symdata *) vdata; + + if (symname == NULL) + data->name = NULL; + else + { + data->name = strdup (symname); + assert (data->name != NULL); + } + data->val = symval; + data->size = symsize; +} + +/* The backtrace_syminfo error callback function. */ + +static void +error_callback_three (void *vdata, const char *msg, int errnum) +{ + struct symdata *data = (struct symdata *) vdata; + + fprintf (stderr, "%s", msg); + if (errnum > 0) + fprintf (stderr, ": %s", strerror (errnum)); + fprintf (stderr, "\n"); + data->failed = 1; +} + +static void +error_callback_create (void *data ATTRIBUTE_UNUSED, const char *msg, int errnum) +{ + fprintf (stderr, "%s", msg); + if (errnum > 0) + fprintf (stderr, ": %s", strerror (errnum)); + fprintf (stderr, "\n"); + exit (EXIT_FAILURE); +} + +static int +f23 (void) +{ + uintptr_t addrs[20]; + struct sdata data; + int i; + + data.addrs = &addrs[0]; + data.index = 0; + data.max = 20; + data.failed = 0; + + i = backtrace_simple (state, 0, callback_two, error_callback_two, &data); + + if (i != 0) + { + fprintf (stderr, "test3: unexpected return value %d\n", i); + data.failed = 1; + } + + if (!data.failed) + { + struct info all[20]; + struct bdata bdata; + int j; + + bdata.all = &all[0]; + bdata.index = 0; + bdata.max = 20; + bdata.failed = 0; + + for (j = 0; j < 3; ++j) + { + i = backtrace_pcinfo (state, addrs[j], callback_one, + error_callback_one, &bdata); + if (i != 0) + { + fprintf (stderr, ("test3: unexpected return value " + "from backtrace_pcinfo %d\n"), + i); + bdata.failed = 1; + } + if (!bdata.failed && bdata.index != (size_t) (j + 1)) + { + fprintf (stderr, ("wrong number of calls from backtrace_pcinfo " + "got %u expected %d\n"), + (unsigned int) bdata.index, j + 1); + bdata.failed = 1; + } + } + + if (bdata.failed) + data.failed = 1; + + for (j = 0; j < 1; ++j) + { + struct symdata symdata; + + symdata.name = NULL; + symdata.val = 0; + symdata.size = 0; + symdata.failed = 0; + + i = backtrace_syminfo (state, addrs[j], callback_three, + error_callback_three, &symdata); + if (i == 0) + { + fprintf (stderr, ("test3: [%d]: unexpected return value " + "from backtrace_syminfo %d\n"), + j, i); + symdata.failed = 1; + } + + if (!symdata.failed) + { + const char *expected; + + switch (j) + { + case 0: + expected = "f23"; + break; + default: + assert (0); + } + + if (symdata.name == NULL) + { + fprintf (stderr, "test3: [%d]: NULL syminfo name\n", j); + symdata.failed = 1; + } + /* Use strncmp, not strcmp, because GCC might create a + clone. */ + else if (strncmp (symdata.name, expected, strlen (expected)) != 0) + { + fprintf (stderr, ("test3: [%d]: unexpected syminfo name " + "got %s expected %s\n"), + j, symdata.name, expected); + symdata.failed = 1; + } + } + + if (symdata.failed) + data.failed = 1; + } + } + + printf ("%s: backtrace_simple noinline\n", data.failed ? "FAIL" : "PASS"); + + if (data.failed) + ++failures; + + return failures; +} + +static void +test3 (void) +{ + f23 (); +} + +int +main (int argc ATTRIBUTE_UNUSED, char **argv) +{ + + state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS, + error_callback_create, NULL); +#if BACKTRACE_SUPPORTED + test3 (); +#endif + + return 0; +} diff --git a/libbacktrace/internal.h b/libbacktrace/internal.h index 89b7bf7..48c8218 100644 --- a/libbacktrace/internal.h +++ b/libbacktrace/internal.h @@ -108,6 +108,8 @@ extern void backtrace_atomic_store_int (int *, int); #endif /* !defined (HAVE_SYNC_FUNCTIONS) */ #endif /* !defined (HAVE_ATOMIC_FUNCTIONS) */ +#define MAX_PATH_LEN 4096 /* from linux/limits.h */ + /* The type of the function that collects file/line information. This is like backtrace_pcinfo. */ @@ -176,6 +178,26 @@ struct backtrace_view size_t len; }; +/* Read the .gnu_debuglink section from elf file and return pointer to + * unsigned char with data from that section. If success caller should free the + * memory */ + +extern unsigned char *elf_gnu_debuglink_section (struct backtrace_state *state, + int descriptor, + backtrace_error_callback error_callback, + void *data, int exe,int * + gnulink_data_len_out); + + +/* Open debug file which name is placed in gnu_debuglink section. + Check the crc32 sum and search file with debug data. On success returns + descriptor of that file on fail -1 */ + +extern int backtrace_open_debugfile (const char *filename, + backtrace_error_callback, void *data, + int *does_not_exist, + struct backtrace_state *state, int exe); + /* Create a view of SIZE bytes from DESCRIPTOR at OFFSET. Store the result in *VIEW. Returns 1 on success, 0 on error. */ extern int backtrace_get_view (struct backtrace_state *state, int descriptor, diff --git a/libbacktrace/posix.c b/libbacktrace/posix.c index 5e5571f..f0443ab 100644 --- a/libbacktrace/posix.c +++ b/libbacktrace/posix.c @@ -37,9 +37,10 @@ POSSIBILITY OF SUCH DAMAGE. */ #include #include #include - +#include #include "backtrace.h" #include "internal.h" +#include "filenames.h" #ifndef O_BINARY #define O_BINARY 0 @@ -53,6 +54,23 @@ POSSIBILITY OF SUCH DAMAGE. */ #define FD_CLOEXEC 1 #endif +enum type_of_file +{ + LINK = 1, + REGULAR = 2 +}; + +enum debug_path +{ + CURRENT, + CURRENT_DEBUG, + USR_LIB_DEBUG, + DEBUG_PATH_MAX +}; + +static const char *const debug_file_path[DEBUG_PATH_MAX] + = {"", ".debug/", "/usr/lib/debug"}; + /* Open a file for reading. */ int @@ -98,3 +116,343 @@ backtrace_close (int descriptor, backtrace_error_callback error_callback, } return 1; } + +static unsigned long +getl32 (void *p) +{ + char *addr = (char *) p; + unsigned long v = 0; + v = *((unsigned long *) addr); + return v; +} + +/* Function that produce crc32 value */ + +static unsigned long +gnu_debuglink_crc32 (unsigned long crc, const unsigned char *buf, size_t len) +{ + static const unsigned long crc32_table[256] + = {0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; + const unsigned char *end; + + crc = ~crc & 0xffffffff; + for (end = buf + len; buf < end; ++buf) + crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); + return ~crc & 0xffffffff; +} + +static unsigned long +get_crc32 (int descriptor) +{ + static unsigned char buffer[8 * 1024]; + unsigned long file_crc = 0; + ssize_t count; + + while ((count = read (descriptor, buffer, sizeof (buffer))) > 0) + file_crc = gnu_debuglink_crc32 (file_crc, buffer, count); + + return file_crc; +} + +static int +pathlen (const char *buffer) +{ + int len; + int count; + int full_filename_len; + + count = 0; + len = full_filename_len = strlen (buffer); + while (len > 1 && !IS_DIR_SEPARATOR (*(buffer + (len - 1)))) + { + ++count; + --len; + } + return len > 1 ? (full_filename_len - count) : -1; +} + +static int +check_sum (int descriptor, unsigned char *debug_link, unsigned int offset, + unsigned int section_size) +{ + unsigned long crc32_debug_link; + unsigned long crc32_debug_file; + offset += 1; + offset = (offset + 3) & ~3; + if (offset >= section_size) + return -1; + crc32_debug_link = getl32 (debug_link + offset); + crc32_debug_file = get_crc32 (descriptor); + return crc32_debug_link == crc32_debug_file; +} + +static int +get_debug_filename_len (unsigned char *buffer, size_t size) +{ + size_t count; + unsigned char sign_byte; + + sign_byte = 0x00; + count = 0; + while (*(buffer + count) != sign_byte && size > count) + ++count; + return count; +} + +static int +backtrace_readlink (const char *filename, + backtrace_error_callback error_callback, void *data, + int *does_not_exist) +{ + struct stat link_stat; + int file_type; + mode_t mode; + + memset (&link_stat, 0, sizeof (struct stat)); + + if (lstat (filename, &link_stat) == -1) + { + if (does_not_exist != NULL && errno == ENOENT) + *does_not_exist = 1; + else + error_callback (data, filename, errno); + file_type = -1; + } + + mode = link_stat.st_mode & S_IFMT; + + switch (mode) + { + case S_IFLNK: + file_type = LINK; + break; + case S_IFREG: + file_type = REGULAR; + break; + default: + file_type = -1; + } + return file_type; +} + +/* Open debug file */ + +int +backtrace_open_debugfile (const char *filename, + backtrace_error_callback error_callback, void *data, + int *does_not_exist, struct backtrace_state *state, + int exe) +{ + ssize_t filename_len; + char *buffer; + int descriptor; + int debug_descriptor; + int file_type; + unsigned char *debug_link_data; + size_t valid_debug_link_data; + size_t valid_buffer; + size_t valid_descriptor; + size_t valid_path_buffer; + int debug_filename_len; + int debug_link_data_len; + int path_len; + int debug_does_not_exist; + char *path_buffer; + int pass; + int debug_path_len; + + debug_descriptor = -1; + valid_path_buffer = 0; + valid_buffer = 0; + valid_debug_link_data = 0; + debug_link_data_len = 0; + file_type = -1; + + descriptor = backtrace_open (filename, error_callback, data, does_not_exist); + + if (descriptor < 0) + goto exit; + + valid_descriptor = 1; + + /* check if debug section does exist */ + debug_link_data + = elf_gnu_debuglink_section (state, descriptor, error_callback, data, exe, + &debug_link_data_len); + + /* if does not exist, nothing we can do, just go to exit */ + if (debug_link_data == NULL || debug_link_data_len <= 0) + goto exit; + + valid_debug_link_data = 1; + + /* For most files under the /proc directory, stat() does not + return the file size in the st_size field from struct stat; + instead the field is returned with the value 0. + That means we can not use st_size from struct stat to determine the + length of the actual filename and should + use some max path length for the system to allocate the memory. + For this time i defined MAX_PATH_LEN in the internal.h header. */ + + buffer = backtrace_alloc(state, MAX_PATH_LEN + 1, error_callback, data); + + if (buffer == NULL) + goto exit; + + valid_buffer = 1; + + memset (buffer, 0, MAX_PATH_LEN + 1); + + if (exe) + { + file_type + = backtrace_readlink (filename, error_callback, does_not_exist, data); + + if (file_type == LINK) + { + /* read the actual filename */ + filename_len = readlink (filename, buffer, MAX_PATH_LEN); + if (filename_len < 0) + { + error_callback (data, "lstat", errno); + goto exit; + } + } + else if (file_type == REGULAR) + { + filename_len = strlen (filename); + memcpy (buffer, filename, filename_len); + } + else + /* other file formats are not supported */ + goto exit; + } + else + { + filename_len = strlen (filename); + memcpy (buffer, filename, filename_len); + } + + path_len = pathlen (buffer); + + if (path_len < 1) + goto exit; + + /* allocate memory for path, we need it because should search in + /usr/lib/debug/path/to/executable */ + + path_buffer = backtrace_alloc (state, path_len + 1, error_callback, data); + if (path_buffer == NULL) + goto exit; + + valid_path_buffer = 1; + + memset (path_buffer, 0, path_len + 1); + memcpy (path_buffer, buffer, path_len); + + debug_filename_len + = get_debug_filename_len (debug_link_data, debug_link_data_len); + + if (debug_filename_len < 1) + goto exit; + + for (pass = 0; pass < DEBUG_PATH_MAX; ++pass) + { + switch (pass) + { + case CURRENT: + { + memcpy (buffer + path_len, debug_link_data, debug_filename_len); + break; + } + case CURRENT_DEBUG: + { + debug_path_len = strlen (debug_file_path[CURRENT_DEBUG]); + memcpy (buffer + path_len, debug_file_path[CURRENT_DEBUG], + debug_path_len); + memcpy (buffer + path_len + debug_path_len, debug_link_data, + debug_filename_len); + break; + } + case USR_LIB_DEBUG: + { + debug_path_len = strlen (debug_file_path[USR_LIB_DEBUG]); + memset (buffer, 0, MAX_PATH_LEN + 1); + memcpy (buffer, debug_file_path[USR_LIB_DEBUG], debug_path_len); + memcpy (buffer + debug_path_len, path_buffer, path_len); + memcpy (buffer + debug_path_len + path_len, debug_link_data, + debug_filename_len); + break; + } + default: + goto exit; + } + + debug_descriptor + = backtrace_open (buffer, error_callback, data, &debug_does_not_exist); + + if (debug_descriptor > 0) + break; + } + + /* check the crc32 checksum if it not the same return -1 */ + + if (!check_sum (debug_descriptor, debug_link_data, debug_filename_len, + debug_link_data_len)) + debug_descriptor = -1; + +exit: + if (valid_debug_link_data) + backtrace_free (state, debug_link_data, debug_link_data_len, error_callback, + data); + if (valid_buffer) + backtrace_free (state, buffer, MAX_PATH_LEN + 1, error_callback, data); + if (valid_path_buffer) + backtrace_free (state, path_buffer, path_len + 1, error_callback, data); + if (valid_descriptor) + backtrace_close (descriptor, error_callback, data); + return debug_descriptor; +} +