[PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace

Denis Khalikov d.khalikov@partner.samsung.com
Mon Mar 13 17:16:00 GMT 2017


Hello everyone, i have a patch for this issue.

List of implemented functionality:

1.Reading .gnu_debuglink section from ELF file:
  a. Reading name of debug info file.
  b. Verifying crc32 sum.

2. Searching for separate debug info file from paths:
  a. /usr/lib/debug/path/to/executable
  b. /path/to/executable
  c. /path/to/executable/.debug

Assumed that debug info file generated by objcopy from binutils.

objcopy --only-keep-debug foo foo.debug
strip -g foo
objcopy --add-gnu-debuglink=foo.debug foo

-------------- next part --------------
commit 6147ee1a9aeeb748563a8998033f2ce195460162
Author: Denis Khalikov <d.khalikov@partner.samsung.com>
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  <d.khalikov@partner.samsung.com>
+
+	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  <sam.thursfield@codethink.co.uk>
 
 	* 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 <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#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 <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
-
+#include <string.h>
 #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;
+}
+


More information about the Gcc-patches mailing list