public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
       [not found] <CGME20170616153942eucas1p1945271f893265484bbb3991a368bcd92@eucas1p1.samsung.com>
@ 2017-06-16 15:39 ` Denis Khalikov
  2017-06-18 14:09   ` Matthias Klose
  2017-06-28 23:59   ` Ian Lance Taylor via gcc-patches
  0 siblings, 2 replies; 28+ messages in thread
From: Denis Khalikov @ 2017-06-16 15:39 UTC (permalink / raw)
  To: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 141 bytes --]

Hello everyone,

This is a patch for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77631

Can some one please review attached patch.

Thanks.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: PR-sanitizer-77631.patch --]
[-- Type: text/x-patch; name="PR-sanitizer-77631.patch", Size: 39792 bytes --]

From ae74cf3d632b06a91a986e32e3a6c16223767b24 Mon Sep 17 00:00:00 2001
From: Denis Khalikov <d.khalikov@partner.samsung.com>
Date: Fri, 16 Jun 2017 12:13:13 +0300
Subject: [PATCH] PR sanitizer/77631

	* Makefile.in: Regenerated.
	* configure.ac: Add searching for limits.h, sys/param.h
	* config.h.in: Regenerated.
	* configure: Regenerated.
	* elf.c (enum type_of_file): New enum.
	(enum type_of_elf): 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.
	(process_elf_header): New function. Verify elf header.
	(elf_get_section_by_name): New function. Get section by name.
	(backtrace_readlink): New function. Get type of file from filename.
	(resolve_realname): New function. Resolve real name if file is link.
	(backtrace_resolve_realname): New function. Resolve real name for any
	file type.
	(search_for_debugfile): New function. Search for debug file in known
	paths.
	(open_debugfile_by_gnulink): New function. Open debug file with
	gnulink.
	(hex): New function. Convert to hex.
	(get_build_id_name): New function. Generate build-id name.
	(open_debugfile_by_build_id): New function. Open debug file with
	build-id.
	(backtrace_open_debugfile): New function. Open debug file.
	(elf_add): Move code which reads elf header to elf_header_is_valid.
	(phdr_callback): Call backtrace_open_debugfile function for shared
	library.
	* fileline.c (fileline_initialize): Call backtrace_open_debugfile for
	executable.
	* internal.h: Updated.
---
 libbacktrace/ChangeLog    |  37 ++
 libbacktrace/Makefile.in  |   2 +-
 libbacktrace/config.h.in  |   6 +
 libbacktrace/configure    |  26 ++
 libbacktrace/configure.ac |   4 +
 libbacktrace/elf.c        | 949 +++++++++++++++++++++++++++++++++++++++++-----
 libbacktrace/fileline.c   |  13 +-
 libbacktrace/internal.h   |   8 +
 8 files changed, 958 insertions(+), 87 deletions(-)

diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog
index 096ceb6..4bd97f3 100644
--- a/libbacktrace/ChangeLog
+++ b/libbacktrace/ChangeLog
@@ -1,3 +1,40 @@
+2017-06-16  Denis Khalikov  <d.khalikov@partner.samsung.com>
+
+	PR sanitizer/77631
+	* Makefile.in: Regenerated.
+	* configure.ac: Add searching for limits.h, sys/param.h
+	* config.h.in: Regenerated.
+	* configure: Regenerated.
+	* elf.c (enum type_of_file): New enum.
+	(enum type_of_elf): 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.
+	(process_elf_header): New function. Verify elf header.
+	(elf_get_section_by_name): New function. Get section by name.
+	(backtrace_readlink): New function. Get type of file from filename.
+	(resolve_realname): New function. Resolve real name if file is link.
+	(backtrace_resolve_realname): New function. Resolve real name for any
+	file type.
+	(search_for_debugfile): New function. Search for debug file in known
+	paths.
+	(open_debugfile_by_gnulink): New function. Open debug file with
+	gnulink.
+	(hex): New function. Convert to hex.
+	(get_build_id_name): New function. Generate build-id name.
+	(open_debugfile_by_build_id): New function. Open debug file with
+	build-id.
+	(backtrace_open_debugfile): New function. Open debug file.
+	(elf_add): Move code which reads elf header to elf_header_is_valid.
+	(phdr_callback): Call backtrace_open_debugfile function for shared
+	library.
+	* fileline.c (fileline_initialize): Call backtrace_open_debugfile for
+	executable.
+	* internal.h: Updated.
+
 2017-05-02  Release Manager
 
 	* GCC 7.1.0 released.
diff --git a/libbacktrace/Makefile.in b/libbacktrace/Makefile.in
index de74b5d..e604b6c 100644
--- a/libbacktrace/Makefile.in
+++ b/libbacktrace/Makefile.in
@@ -16,7 +16,7 @@
 @SET_MAKE@
 
 # Makefile.am -- Backtrace Makefile.
-# Copyright (C) 2012-2016 Free Software Foundation, Inc.
+# Copyright (C) 2012-2017 Free Software Foundation, Inc.
 
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
diff --git a/libbacktrace/config.h.in b/libbacktrace/config.h.in
index 87cb805..edbd9af 100644
--- a/libbacktrace/config.h.in
+++ b/libbacktrace/config.h.in
@@ -28,6 +28,9 @@
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
+/* Define 1 if <limits.h> is available. */
+#undef HAVE_LIMITS_H
+
 /* Define to 1 if you have the <link.h> header file. */
 #undef HAVE_LINK_H
 
@@ -52,6 +55,9 @@
 /* Define to 1 if you have the <sys/mman.h> header file. */
 #undef HAVE_SYS_MMAN_H
 
+/* Define 1 if <sys/param.h> is available. */
+#undef HAVE_SYS_PARAM_H
+
 /* Define to 1 if you have the <sys/stat.h> header file. */
 #undef HAVE_SYS_STAT_H
 
diff --git a/libbacktrace/configure b/libbacktrace/configure
index ee90bc6..edd50b0 100755
--- a/libbacktrace/configure
+++ b/libbacktrace/configure
@@ -12350,6 +12350,32 @@ if test "$ALLOC_FILE" = "alloc.lo"; then
 fi
 
 
+for ac_header in limits.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default"
+if test "x$ac_cv_header_limits_h" = x""yes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIMITS_H 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in sys/param.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_param_h" = x""yes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_SYS_PARAM_H 1
+_ACEOF
+
+fi
+
+done
+
+
 # Check for dl_iterate_phdr.
 for ac_header in link.h
 do :
diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac
index f9cad21..01ac2a1 100644
--- a/libbacktrace/configure.ac
+++ b/libbacktrace/configure.ac
@@ -301,6 +301,10 @@ if test "$ALLOC_FILE" = "alloc.lo"; then
 fi
 AC_SUBST(BACKTRACE_USES_MALLOC)
 
+AC_CHECK_HEADERS(limits.h)
+
+AC_CHECK_HEADERS(sys/param.h)
+
 # Check for dl_iterate_phdr.
 AC_CHECK_HEADERS(link.h)
 if test "$ac_cv_header_link_h" = "no"; then
diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c
index 89ed42b..32f6871 100644
--- a/libbacktrace/elf.c
+++ b/libbacktrace/elf.c
@@ -35,6 +35,9 @@ POSSIBILITY OF SUCH DAMAGE.  */
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
 
 #ifdef HAVE_DL_ITERATE_PHDR
 #include <link.h>
@@ -42,6 +45,32 @@ POSSIBILITY OF SUCH DAMAGE.  */
 
 #include "backtrace.h"
 #include "internal.h"
+#include "filenames.h"
+
+/* The following is from pathmax.h.  */
+/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
+   PATH_MAX but might cause redefinition warnings when sys/param.h is
+   later included (as on MORE/BSD 4.3).  */
+#if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
+# include <limits.h>
+#endif
+
+#ifndef _POSIX_PATH_MAX
+# define _POSIX_PATH_MAX 255
+#endif
+
+/* Don't include sys/param.h if it already has been.  */
+#if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
+# include <sys/param.h>
+#endif
+
+#if !defined PATH_MAX && defined MAXPATHLEN
+# define PATH_MAX MAXPATHLEN
+#endif
+
+#ifndef PATH_MAX
+# define PATH_MAX _POSIX_PATH_MAX
+#endif
 
 #ifndef HAVE_DL_ITERATE_PHDR
 
@@ -283,6 +312,821 @@ struct elf_syminfo_data
   size_t count;
 };
 
+/* Information to read ELF note section.  */
+
+typedef struct
+{
+  unsigned char namesz[4];
+  unsigned char descsz[4];
+  unsigned char type[4];
+  unsigned char name[1];
+} Elf_External_Note;
+
+/* Information about type of the file.  */
+
+enum type_of_file
+{
+  LINK = 1,
+  REGULAR = 2
+};
+
+/* Information about debug paths.  */
+
+enum debug_path
+{
+  CURRENT,
+  CURRENT_DEBUG,
+  USR_LIB_DEBUG,
+  USR_LIB_DEBUG_PATH_TO_EXE,
+  DEBUG_PATH_MAX
+};
+
+/* Type of the ELF file.  */
+
+enum type_of_elf
+{
+  DYN = -1,
+  INVALID = 0,
+  EXEC = 1
+};
+
+/* Paths to debug file.  */
+
+static const char *const debug_file_path[DEBUG_PATH_MAX]
+  = {"", ".debug/", "/usr/lib/debug/", "/usr/lib/debug"};
+
+/* Cast from void pointer to uint32_t.  */
+
+static uint32_t
+getl32 (void *p)
+{
+  char *addr = (char *) p;
+  uint32_t v = 0;
+  v = *((uint32_t *) addr);
+  return v;
+}
+
+/* Function that produce a crc32 value for input buffer.  */
+
+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;
+}
+
+/* Generate crc32 sum from the file.  */
+
+static unsigned long
+get_crc32 (int descriptor)
+{
+  unsigned char buffer[8 * 1024];
+  memset (buffer, 0, 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;
+}
+
+/* Get length of the path.  */
+
+static int
+pathlen (const char *buffer)
+{
+  int len;
+
+  len = strlen (buffer);
+  while (len > 1 && !IS_DIR_SEPARATOR (buffer[len - 1]))
+    --len;
+  return len > 1 ? len : -1;
+}
+
+/* Verify crc32 sum.  */
+
+static int
+check_sum (int descriptor, char *debug_link, int offset, int section_size)
+{
+  unsigned long crc32_debug_link;
+  unsigned long crc32_debug_file;
+  offset += 1;
+  offset = (offset + 3) & ~3;
+  if (offset >= section_size)
+    return 0;
+  crc32_debug_link = getl32 (debug_link + offset);
+  crc32_debug_file = get_crc32 (descriptor);
+  return crc32_debug_link == crc32_debug_file;
+}
+
+/* Process elf header. Verify magic number, version, etc, of the ELF
+   header. Return 1 if the file type is EXE, -1 if type of the file
+   is DYN and 0 on the fail.  */
+
+static int
+process_elf_header (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;
+
+  if (!backtrace_get_view (state, descriptor, 0, sizeof *ehdr_out,
+			   error_callback, data, &ehdr_view))
+    goto fail;
+
+  memcpy (ehdr_out, ehdr_view.data, sizeof *ehdr_out);
+
+  backtrace_release_view (state, &ehdr_view, error_callback, data);
+
+  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_out->e_ident[EI_VERSION] != EV_CURRENT)
+    {
+      error_callback (data, "executable file is unrecognized ELF version", 0);
+      goto fail;
+    }
+
+#if BACKTRACE_ELF_SIZE == 32
+#define BACKTRACE_ELFCLASS ELFCLASS32
+#else
+#define BACKTRACE_ELFCLASS ELFCLASS64
+#endif
+
+  if (ehdr_out->e_ident[EI_CLASS] != BACKTRACE_ELFCLASS)
+    {
+      error_callback (data, "executable file is unexpected ELF class", 0);
+      goto fail;
+    }
+
+  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;
+    }
+
+  /* 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_out->e_type == ET_DYN)
+    return -1;
+
+  *shoff_out = ehdr_out->e_shoff;
+  *shnum_out = ehdr_out->e_shnum;
+  *shstrndx_out = ehdr_out->e_shstrndx;
+
+  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_out,
+			      sizeof (b_elf_shdr), error_callback,
+			      data, &shdr_view))
+        goto fail;
+
+      shdr = (const b_elf_shdr *) shdr_view.data;
+
+      if (*shnum_out == 0)
+	*shnum_out = shdr->sh_size;
+
+      if (*shstrndx_out == SHN_XINDEX)
+	{
+	  *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
+	     correctly.  All large section indexes were offset by
+	     0x100.  There is more information at
+	     http://sourceware.org/bugzilla/show_bug.cgi?id-5900 .
+	     Fortunately these object files are easy to detect, as the
+	     GNU binutils always put the section header string table
+	     near the end of the list of sections.  Thus if the
+	     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_out >= *shnum_out
+	      && *shstrndx_out >= SHN_LORESERVE + 0x100)
+	    *shstrndx_out -= 0x100;
+	}
+      backtrace_release_view (state, &shdr_view, error_callback, data);
+    }
+  return 1;
+ fail:
+  return 0;
+}
+
+
+/* Get content of the specifying section.  */
+
+static unsigned char *
+elf_get_section_by_name (struct backtrace_state *state, int descriptor,
+			 backtrace_error_callback error_callback, void *data,
+			 int *section_data_len_out, const char *section_name)
+{
+  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 section_view;
+  int names_view_valid;
+  const char *names;
+  unsigned int i;
+  int section_view_valid;
+  unsigned char *section_data;
+
+  section_view_valid = 0;
+  shdrs_view_valid = 0;
+  names_view_valid = 0;
+  section_data = NULL;
+
+  if (!process_elf_header (state, descriptor, error_callback, data, 0,
+			    &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;
+
+  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, section_name) == 0)
+	{
+	  if (backtrace_get_view (state, descriptor, shdr->sh_offset,
+				  shdr->sh_size, error_callback, data,
+				  &section_view))
+	    {
+	      section_view_valid = 1;
+	      section_data
+		= backtrace_alloc (state, shdr->sh_size, error_callback, data);
+	      if (section_data == NULL)
+		goto exit;
+	      memcpy (section_data, section_view.data, shdr->sh_size);
+	      *section_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 (section_view_valid)
+    backtrace_release_view (state, &section_view, error_callback, data);
+  return section_data;
+}
+
+
+/* Verify type of the file.  */
+
+static int
+backtrace_readlink (const char *filename,
+		    backtrace_error_callback error_callback, void *data)
+{
+  struct stat link_stat;
+  int file_type;
+  mode_t mode;
+
+  memset (&link_stat, 0, sizeof (struct stat));
+
+  if (lstat (filename, &link_stat) == -1)
+    {
+      if (errno != ENOENT)
+	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;
+}
+
+/* Resolve full name of the link. In this case we can't use realpath function
+   because it could be undefined on some platfroms, also it allocates memory
+   by malloc, which we can't use.  */
+
+static int
+resolve_realname (const char *filename, char *buffer,
+		  struct backtrace_state *state,
+		  backtrace_error_callback error_callback, void *data)
+{
+  char *temp_buffer;
+  int file_type;
+  int filename_len;
+  int temp_filename_len;
+  int valid_temp_buffer;
+  int path_len;
+
+  valid_temp_buffer = 0;
+  filename_len = -1;
+  file_type = LINK;
+
+  /* Allocate memory for sizeof(PATH_MAX) + 1 bytes because at this time
+     we don't know how long path could be.  */
+  temp_buffer = backtrace_alloc (state, PATH_MAX + 1, error_callback, data);
+  if (temp_buffer == NULL)
+    return -1;
+
+  valid_temp_buffer = 1;
+
+  memset (temp_buffer, 0, PATH_MAX + 1);
+  memcpy (temp_buffer, filename, strlen (filename));
+
+  while (file_type == LINK)
+    {
+      filename_len = readlink (temp_buffer, buffer, PATH_MAX);
+      if (filename_len < 1)
+	goto exit;
+
+      temp_filename_len = strlen (buffer);
+
+      /* Full path.  */
+      if (buffer[0] == '/')
+	{
+	  memset (temp_buffer, 0, PATH_MAX);
+	  memcpy (temp_buffer, buffer, temp_filename_len);
+	}
+      else
+	{
+	  /* Relative path.  */
+	  path_len = pathlen (temp_buffer);
+	  if (path_len < 1)
+	    goto exit;
+
+	  memcpy (temp_buffer + path_len, buffer, filename_len);
+	  temp_buffer[path_len + filename_len] = '\0';
+	}
+
+      file_type = backtrace_readlink (temp_buffer, error_callback, data);
+      memset (buffer, 0, filename_len);
+    }
+
+  if (file_type != REGULAR)
+    {
+      filename_len = -1;
+      goto exit;
+    }
+
+  filename_len = strlen (temp_buffer);
+  memcpy (buffer, temp_buffer, filename_len);
+
+ exit:
+  if (valid_temp_buffer)
+    backtrace_free (state, temp_buffer, PATH_MAX + 1, error_callback, data);
+  return filename_len;
+}
+
+/* Resolve realname of the filename. This function verifies filename.
+   If filename is name of the file it populates realname buffer.
+   If filename is link, it calls resolve_realname function.  */
+
+static char *
+backtrace_resolve_realname (const char *filename, int *filename_len,
+			    struct backtrace_state *state,
+			    backtrace_error_callback error_callback, void *data)
+{
+  int file_type;
+  char *realname;
+
+  *filename_len = -1;
+
+  realname = backtrace_alloc (state, PATH_MAX + 1, error_callback, data);
+  if (realname == NULL)
+    goto exit;
+
+  file_type = backtrace_readlink (filename, error_callback, data);
+
+  if (file_type == LINK)
+    {
+      /* Read the actual filename.  */
+      *filename_len
+	= resolve_realname (filename, realname, state, error_callback, data);
+      if (*filename_len < 0)
+	goto exit;
+    }
+  else if (file_type == REGULAR)
+    {
+      *filename_len = strlen (filename);
+      if (*filename_len > PATH_MAX)
+	goto exit;
+
+      memcpy (realname, filename, *filename_len);
+    }
+
+ exit:
+  return realname;
+}
+
+/* Search for debug file into specifying directorires.  */
+
+static int
+search_for_debugfile (char *realname, char *debug_filename,
+		      backtrace_error_callback error_callback, void *data,
+		      struct backtrace_state *state)
+{
+  int debug_filename_len;
+  int pass;
+  int debug_path_len;
+  int debug_does_not_exist;
+  int debug_descriptor;
+  int path_len;
+  char *buffer;
+  int buffer_len;
+  int valid_buffer;
+
+  debug_descriptor = -1;
+  valid_buffer = 0;
+
+  path_len = pathlen (realname);
+  debug_filename_len = strlen ((const char *) debug_filename);
+
+  if (debug_filename_len < 1)
+    goto exit;
+
+  buffer_len = path_len + strlen (debug_file_path[USR_LIB_DEBUG])
+	       + debug_filename_len + 1;
+
+  buffer = backtrace_alloc (state, buffer_len, error_callback, data);
+
+  if (buffer == NULL)
+    goto exit;
+  memset (buffer, 0, buffer_len);
+  memcpy (buffer, realname, path_len);
+
+  valid_buffer = 1;
+  for (pass = 0; pass < DEBUG_PATH_MAX; ++pass)
+    {
+      switch (pass)
+	{
+	case CURRENT:
+	  {
+	    memcpy (buffer + path_len, debug_filename, 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_filename,
+		    debug_filename_len);
+	    break;
+	  }
+	case USR_LIB_DEBUG:
+	  {
+	    debug_path_len = strlen (debug_file_path[USR_LIB_DEBUG]);
+	    memset (buffer, 0, buffer_len);
+	    memcpy (buffer, debug_file_path[USR_LIB_DEBUG], debug_path_len);
+	    memcpy (buffer + debug_path_len, debug_filename,
+		    debug_filename_len);
+	    break;
+	  }
+	case USR_LIB_DEBUG_PATH_TO_EXE:
+	  {
+	    debug_path_len
+	      = strlen (debug_file_path[USR_LIB_DEBUG_PATH_TO_EXE]);
+	    memset (buffer, 0, buffer_len);
+	    memcpy (buffer, debug_file_path[USR_LIB_DEBUG_PATH_TO_EXE],
+		    debug_path_len);
+	    memcpy (buffer + debug_path_len, realname, path_len);
+	    memcpy (buffer + debug_path_len + path_len, debug_filename,
+		    debug_filename_len);
+	    break;
+	  }
+	default:
+	  goto exit;
+	}
+
+      debug_descriptor
+	= backtrace_open (buffer, error_callback, data, &debug_does_not_exist);
+
+      if (debug_descriptor > 0)
+	break;
+    }
+ exit:
+  if (valid_buffer)
+    backtrace_free (state, buffer, buffer_len, error_callback, data);
+  return debug_descriptor;
+}
+
+/* Open debug file by gnulink.  */
+
+static int
+open_debugfile_by_gnulink (char *realname, unsigned char *section_data,
+			   int section_data_len, struct backtrace_state *state,
+			   backtrace_error_callback error_callback, void *data)
+{
+  int debug_descriptor;
+
+  debug_descriptor = search_for_debugfile (realname, (char *) section_data,
+					   error_callback, data, state);
+  if (debug_descriptor < 0)
+    goto exit;
+
+  /* Check the crc32 checksum if it not the same return -1.  */
+
+  if (!check_sum (debug_descriptor, (char *) section_data,
+		  strlen ((char *) section_data), section_data_len))
+    debug_descriptor = -1;
+
+ exit:
+  return debug_descriptor;
+}
+
+/* Convert char to hex */
+
+static char
+hex (char ch)
+{
+  return ch > 9 ? ('a' + (ch - 10)) : ('0' + ch);
+}
+
+/* Get build-id name.  */
+
+static char *
+get_build_id_name (unsigned char *section_data, int *len,
+		   struct backtrace_state *state,
+		   backtrace_error_callback error_callback, void *data)
+{
+  Elf_External_Note *build_id_section;
+  char *build_id_name;
+  char *temp;
+  const char *debug_postfix;
+  const char *debug_prefix;
+  size_t debug_postfix_len;
+  size_t debug_prefix_len;
+  size_t name_size;
+  int offset;
+  unsigned char *hash_start;
+  unsigned long hash_size;
+  unsigned long identifier;
+
+  debug_postfix_len = 6;
+  debug_prefix_len = 10;
+  debug_postfix = ".debug";
+  debug_prefix = ".build-id/";
+  *len = 0;
+
+  build_id_section = (Elf_External_Note *) section_data;
+  hash_size = getl32 (build_id_section->descsz);
+  identifier = getl32 (build_id_section->type);
+  name_size = getl32 (build_id_section->namesz);
+
+  if (identifier != NT_GNU_BUILD_ID || hash_size == 0 || name_size != 4
+      || strncmp ((char *) build_id_section->name, "GNU", 3) != 0)
+    return NULL;
+
+  offset = 16;
+  hash_start = section_data + offset;
+  *len = hash_size * 2 + debug_postfix_len + debug_prefix_len + 1;
+  build_id_name = backtrace_alloc (state, *len, error_callback, data);
+
+  memset (build_id_name, 0, *len);
+  memcpy (build_id_name, debug_prefix, debug_prefix_len);
+  temp = build_id_name + debug_prefix_len;
+
+  *temp++ = hex ((*hash_start & 0xF0) >> 4);
+  *temp++ = hex (*hash_start & 0x0F);
+  ++hash_start;
+  --hash_size;
+
+  memcpy (temp, "/", 1);
+  ++temp;
+
+  while (hash_size--)
+    {
+      *temp++ = hex ((*hash_start & 0xF0) >> 4);
+      *temp++ = hex (*hash_start & 0x0F);
+      ++hash_start;
+    }
+
+  memcpy (temp, debug_postfix, debug_postfix_len);
+  return build_id_name;
+}
+
+/* Open file by build-id.  */
+
+static int
+open_debugfile_by_build_id (char *realname, unsigned char *section_data,
+			    struct backtrace_state *state,
+			    backtrace_error_callback error_callback, void *data)
+
+{
+  char *build_id_name;
+  int debug_descriptor;
+  int build_id_name_len;
+  int valid_build_id_name;
+
+  debug_descriptor = -1;
+  valid_build_id_name = 0;
+
+  build_id_name = get_build_id_name (section_data, &build_id_name_len, state,
+				     error_callback, data);
+
+  if (build_id_name == NULL || build_id_name_len <= 0)
+    goto exit;
+
+  valid_build_id_name = 1;
+
+  debug_descriptor = search_for_debugfile (realname, build_id_name,
+					   error_callback, data, state);
+
+ exit:
+  if (valid_build_id_name)
+    backtrace_free (state, build_id_name, build_id_name_len, error_callback,
+		    data);
+  return debug_descriptor;
+}
+
+/* Open debug file.  */
+
+int
+backtrace_open_debugfile (int descriptor, const char *filename,
+			  backtrace_error_callback error_callback, void *data,
+			  struct backtrace_state *state)
+{
+  int debug_descriptor;
+  unsigned char *gnulink_section_data;
+  unsigned char *build_id_section_data;
+  size_t valid_descriptor;
+  size_t valid_gnulink_section_data;
+  size_t valid_build_id_section_data;
+  size_t valid_realname;
+  int build_id_section_data_len;
+  int gnu_link_section_data_len;
+  char *realname;
+  int filename_len;
+
+  valid_realname = 0;
+  valid_descriptor = 0;
+  valid_gnulink_section_data = 0;
+  valid_build_id_section_data = 0;
+  build_id_section_data_len = 0;
+  gnu_link_section_data_len = 0;
+  debug_descriptor = -1;
+
+  realname = backtrace_resolve_realname (filename, &filename_len, state,
+					 error_callback, data);
+
+  if (realname == NULL || filename_len < 0)
+    goto exit;
+
+  valid_realname = 1;
+
+  /* Check if build-id section does exist.  */
+  build_id_section_data
+    = elf_get_section_by_name (state, descriptor, error_callback, data,
+			       &build_id_section_data_len,
+			       ".note.gnu.build-id");
+
+  if (build_id_section_data != NULL && build_id_section_data_len > 0)
+    {
+      valid_build_id_section_data = 1;
+      debug_descriptor
+	= open_debugfile_by_build_id (realname, build_id_section_data, state,
+				      error_callback, data);
+    }
+
+  if (debug_descriptor < 0)
+    {
+      gnulink_section_data
+	= elf_get_section_by_name (state, descriptor, error_callback, data,
+				   &gnu_link_section_data_len,
+				   ".gnu_debuglink");
+
+      if (gnulink_section_data != NULL && gnu_link_section_data_len > 0)
+	{
+	  valid_gnulink_section_data = 1;
+	  debug_descriptor
+	    = open_debugfile_by_gnulink (realname, gnulink_section_data,
+					 gnu_link_section_data_len, state,
+					 error_callback, data);
+	}
+    }
+
+  if (debug_descriptor >= 0)
+    valid_descriptor = 1;
+
+ exit:
+  if (valid_descriptor)
+    backtrace_close (descriptor, error_callback, data);
+  if (valid_gnulink_section_data)
+    backtrace_free (state, gnulink_section_data, gnu_link_section_data_len,
+		    error_callback, data);
+  if (valid_build_id_section_data)
+    backtrace_free (state, build_id_section_data, build_id_section_data_len,
+		    error_callback, data);
+  if (valid_realname)
+    backtrace_free (state, realname, PATH_MAX + 1, error_callback, data);
+  return debug_descriptor;
+}
+
 /* A dummy callback function used when we can't find any debug info.  */
 
 static int
@@ -521,7 +1365,6 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
 	 backtrace_error_callback error_callback, void *data,
 	 fileline *fileline_fn, int *found_sym, int *found_dwarf, int exe)
 {
-  struct backtrace_view ehdr_view;
   b_elf_ehdr ehdr;
   off_t shoff;
   unsigned int shnum;
@@ -547,6 +1390,7 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
   off_t max_offset;
   struct backtrace_view debug_view;
   int debug_view_valid;
+  enum type_of_elf elf_type;
 
   *found_sym = 0;
   *found_dwarf = 0;
@@ -557,94 +1401,20 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
   strtab_view_valid = 0;
   debug_view_valid = 0;
 
-  if (!backtrace_get_view (state, descriptor, 0, sizeof ehdr, error_callback,
-			   data, &ehdr_view))
-    goto fail;
-
-  memcpy (&ehdr, ehdr_view.data, sizeof ehdr);
-
-  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)
-    {
-      error_callback (data, "executable file is not ELF", 0);
-      goto fail;
-    }
-  if (ehdr.e_ident[EI_VERSION] != EV_CURRENT)
+  elf_type = process_elf_header (state, descriptor, error_callback, data, exe,
+				 &shoff, &shnum, &shstrndx, &ehdr);
+  switch (elf_type)
     {
-      error_callback (data, "executable file is unrecognized ELF version", 0);
+    /* Binary compiled with PIE option.  */
+    case DYN:
+      return -1;
+    case EXEC:
+      break;
+    /* Header is invalid.  */
+    case INVALID:
       goto fail;
     }
 
-#if BACKTRACE_ELF_SIZE == 32
-#define BACKTRACE_ELFCLASS ELFCLASS32
-#else
-#define BACKTRACE_ELFCLASS ELFCLASS64
-#endif
-
-  if (ehdr.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)
-    {
-      error_callback (data, "executable file has unknown endianness", 0);
-      goto fail;
-    }
-
-  /* If the executable is ET_DYN, it is either a PIE, or we are running
-     directly a shared library with .interp.  We need to wait for
-     dl_iterate_phdr in that case to determine the actual base_address.  */
-  if (exe && ehdr.e_type == ET_DYN)
-    return -1;
-
-  shoff = ehdr.e_shoff;
-  shnum = ehdr.e_shnum;
-  shstrndx = ehdr.e_shstrndx;
-
-  if ((shnum == 0 || shstrndx == SHN_XINDEX)
-      && shoff != 0)
-    {
-      struct backtrace_view shdr_view;
-      const b_elf_shdr *shdr;
-
-      if (!backtrace_get_view (state, descriptor, shoff, 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 (shstrndx == SHN_XINDEX)
-	{
-	  shstrndx = shdr->sh_link;
-
-	  /* Versions of the GNU binutils between 2.12 and 2.18 did
-	     not handle objects with more than SHN_LORESERVE sections
-	     correctly.  All large section indexes were offset by
-	     0x100.  There is more information at
-	     http://sourceware.org/bugzilla/show_bug.cgi?id-5900 .
-	     Fortunately these object files are easy to detect, as the
-	     GNU binutils always put the section header string table
-	     near the end of the list of sections.  Thus if the
-	     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;
-	}
-
-      backtrace_release_view (state, &shdr_view, error_callback, data);
-    }
-
   /* To translate PC to file/line when using DWARF, we need to find
      the .debug_info and .debug_line sections.  */
 
@@ -877,6 +1647,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 debug_descriptor;
 
   /* 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
@@ -895,11 +1666,19 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED,
 	  backtrace_close (pd->exe_descriptor, pd->error_callback, pd->data);
 	  pd->exe_descriptor = -1;
 	}
+      debug_descriptor = -1;
 
       descriptor = backtrace_open (info->dlpi_name, pd->error_callback,
 				   pd->data, &does_not_exist);
+
       if (descriptor < 0)
 	return 0;
+
+      debug_descriptor
+	= backtrace_open_debugfile (descriptor, info->dlpi_name,
+				    pd->error_callback, pd->data, pd->state);
+      if (debug_descriptor >= 0)
+	descriptor = debug_descriptor;
     }
 
   if (elf_add (pd->state, descriptor, info->dlpi_addr, pd->error_callback,
diff --git a/libbacktrace/fileline.c b/libbacktrace/fileline.c
index 0fd350a..1a5cfda 100644
--- a/libbacktrace/fileline.c
+++ b/libbacktrace/fileline.c
@@ -57,6 +57,7 @@ fileline_initialize (struct backtrace_state *state,
   int pass;
   int called_error_callback;
   int descriptor;
+  int debug_descriptor;
 
   if (!state->threaded)
     failed = state->fileline_initialization_failed;
@@ -79,6 +80,7 @@ fileline_initialize (struct backtrace_state *state,
   /* We have not initialized the information.  Do it now.  */
 
   descriptor = -1;
+  debug_descriptor = -1;
   called_error_callback = 0;
   for (pass = 0; pass < 4; ++pass)
     {
@@ -108,13 +110,22 @@ fileline_initialize (struct backtrace_state *state,
 
       descriptor = backtrace_open (filename, error_callback, data,
 				   &does_not_exist);
+
       if (descriptor < 0 && !does_not_exist)
 	{
 	  called_error_callback = 1;
 	  break;
 	}
+
       if (descriptor >= 0)
-	break;
+	{
+	  debug_descriptor
+	    = backtrace_open_debugfile (descriptor, filename, error_callback,
+					data, state);
+	  if (debug_descriptor >= 0)
+	    descriptor = debug_descriptor;
+	  break;
+	}
     }
 
   if (descriptor < 0)
diff --git a/libbacktrace/internal.h b/libbacktrace/internal.h
index 89b7bf7..dc4b5f8 100644
--- a/libbacktrace/internal.h
+++ b/libbacktrace/internal.h
@@ -176,6 +176,14 @@ struct backtrace_view
   size_t len;
 };
 
+/* Open debug file whose 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 (int descriptor, const char *filename,
+				     backtrace_error_callback, void *data,
+				     struct backtrace_state *state);
+
 /* 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,
-- 
1.9.1


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-06-16 15:39 ` [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace Denis Khalikov
@ 2017-06-18 14:09   ` Matthias Klose
  2017-06-19  8:48     ` Denis Khalikov
  2017-06-28 23:59   ` Ian Lance Taylor via gcc-patches
  1 sibling, 1 reply; 28+ messages in thread
From: Matthias Klose @ 2017-06-18 14:09 UTC (permalink / raw)
  To: Denis Khalikov, GCC Patches

On 16.06.2017 17:39, Denis Khalikov wrote:
> Hello everyone,
> 
> This is a patch for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77631
> 
> Can some one please review attached patch.

not a full review, but it looks like the system debug files based on build-id's
are not found.  In newer distro releases you find these at

$ ls /usr/lib/debug/.build-id/
00/ 0d/ 1a/ 25/ 2e/ 3a/ 45/ 4d/ 57/ 64/ 6d/ 77/ 82/ 8b/ 94/ a0/ a9/ b7/ c1/ cd/
d6/ e0/ eb/ f6/
01/ 0e/ 1b/ 26/ 2f/ 3b/ 46/ 4e/ 58/ 65/ 6e/ 78/ 83/ 8c/ 95/ a1/ ac/ b9/ c4/ ce/
d7/ e1/ ec/ f7/
02/ 11/ 1c/ 27/ 30/ 3d/ 47/ 50/ 59/ 66/ 6f/ 79/ 84/ 8e/ 96/ a2/ ae/ ba/ c5/ d0/
d8/ e3/ ee/ f9/
03/ 15/ 1d/ 28/ 32/ 3e/ 48/ 51/ 5b/ 67/ 70/ 7a/ 85/ 8f/ 99/ a3/ b0/ bb/ c6/ d1/
d9/ e4/ ef/ fb/
05/ 16/ 1e/ 29/ 35/ 41/ 49/ 52/ 5c/ 68/ 72/ 7b/ 87/ 90/ 9a/ a4/ b1/ bc/ c8/ d2/
db/ e5/ f1/ fc/
08/ 17/ 1f/ 2a/ 36/ 42/ 4a/ 53/ 5e/ 69/ 73/ 7c/ 88/ 91/ 9b/ a5/ b2/ be/ c9/ d3/
dc/ e6/ f2/ fd/
09/ 18/ 20/ 2b/ 37/ 43/ 4b/ 54/ 60/ 6a/ 75/ 7f/ 89/ 92/ 9c/ a6/ b4/ bf/ cb/ d4/
dd/ e7/ f3/ fe/
0a/ 19/ 24/ 2d/ 39/ 44/ 4c/ 55/ 61/ 6b/ 76/ 80/ 8a/ 93/ 9f/ a7/ b5/ c0/ cc/ d5/
de/ e8/ f5/ ff/

the first two bytes of the crc make the sub dir name, the debug file name has
these first two bytes omitted.

$ ls /usr/lib/debug/.build-id/0d
c55467dc9eb81a00c7715a790844e7cf035345.debug

objdump/objcopy in binutils 2.28 has these lookups properly working.

Matthias

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-06-18 14:09   ` Matthias Klose
@ 2017-06-19  8:48     ` Denis Khalikov
  0 siblings, 0 replies; 28+ messages in thread
From: Denis Khalikov @ 2017-06-19  8:48 UTC (permalink / raw)
  To: Matthias Klose, GCC Patches

Hello Matthias,
thanks for review.

As far as I understood that build-id should look like this:

https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html

"For the “build ID” method, GDB looks in the .build-id subdirectory of 
each one of the global debug directories for a file named 
nn/nnnnnnnn.debug, where nn are the first 2 hex characters of the build 
ID bit string, and nnnnnnnn are the rest of the bit string. (Real build 
ID strings are 32 or more hex characters, not 10.)"

I also check gdb internals, for PR binutils/20876, which you provided.


1926   build_id = get_build_id (abfd);
1927   if (build_id == NULL)
1928     return NULL;
1929
1930   /* Compute the debug pathname corresponding to the build-id.  */
1931   name = bfd_malloc (strlen (".build-id/") + build_id->size * 2 + 2 
+ strlen (".debug"));
1932   if (name == NULL)
1933     {
1934       bfd_set_error (bfd_error_no_memory);
1935       return NULL;
1936     }
1937   n = name;
1938   d = build_id->data;
1939   s = build_id->size;
1940
1941   n += sprintf (n, ".build-id/");
1942   n += sprintf (n, "%02x", (unsigned) *d++); s--;
1943   n += sprintf (n, "/");
1944   while (s--)
1945     n += sprintf (n, "%02x", (unsigned) *d++);
1946   n += sprintf (n, ".debug");
1947


In my patch I do the same, in case we can't use printf functions family, 
because we can't use malloc.

  972   debug_postfix = ".debug";
  973   debug_prefix = ".build-id/";
  ...
  990   memset (build_id_name, 0, *len);
  991   memcpy (build_id_name, debug_prefix, debug_prefix_len);
  992   temp = build_id_name + debug_prefix_len;
  993
  994   *temp++ = hex ((*hash_start & 0xF0) >> 4);
  995   *temp++ = hex (*hash_start & 0x0F);
  996   ++hash_start;
  997   --hash_size;
  998
  999   memcpy (temp, "/", 1);
1000   ++temp;
1001
1002   while (hash_size--)
1003     {
1004       *temp++ = hex ((*hash_start & 0xF0) >> 4);
1005       *temp++ = hex (*hash_start & 0x0F);
1006       ++hash_start;
1007     }

In this case if we have binary with build id:

0x0123456789abcdef0123456789abcdef01234567

For example we can use linker option to specify build-id manually:

-Wl,--build-id=0x0123456789abcdef0123456789abcdef01234567

I expect to find .build-id link to debug file at least at path

/usr/lib/debug/.build-id/01/23456789abcdef0123456789abcdef01234567.debug

May be I'am missing something ?

I also can provide 3 more tests for this patch, but it will
increase patch size in about 1,5 times.

Thanks.

On 06/18/2017 05:08 PM, Matthias Klose wrote:
> On 16.06.2017 17:39, Denis Khalikov wrote:
>> Hello everyone,
>>
>> This is a patch for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77631
>>
>> Can some one please review attached patch.
>
> not a full review, but it looks like the system debug files based on build-id's
> are not found.  In newer distro releases you find these at
>
> $ ls /usr/lib/debug/.build-id/
> 00/ 0d/ 1a/ 25/ 2e/ 3a/ 45/ 4d/ 57/ 64/ 6d/ 77/ 82/ 8b/ 94/ a0/ a9/ b7/ c1/ cd/
> d6/ e0/ eb/ f6/
> 01/ 0e/ 1b/ 26/ 2f/ 3b/ 46/ 4e/ 58/ 65/ 6e/ 78/ 83/ 8c/ 95/ a1/ ac/ b9/ c4/ ce/
> d7/ e1/ ec/ f7/
> 02/ 11/ 1c/ 27/ 30/ 3d/ 47/ 50/ 59/ 66/ 6f/ 79/ 84/ 8e/ 96/ a2/ ae/ ba/ c5/ d0/
> d8/ e3/ ee/ f9/
> 03/ 15/ 1d/ 28/ 32/ 3e/ 48/ 51/ 5b/ 67/ 70/ 7a/ 85/ 8f/ 99/ a3/ b0/ bb/ c6/ d1/
> d9/ e4/ ef/ fb/
> 05/ 16/ 1e/ 29/ 35/ 41/ 49/ 52/ 5c/ 68/ 72/ 7b/ 87/ 90/ 9a/ a4/ b1/ bc/ c8/ d2/
> db/ e5/ f1/ fc/
> 08/ 17/ 1f/ 2a/ 36/ 42/ 4a/ 53/ 5e/ 69/ 73/ 7c/ 88/ 91/ 9b/ a5/ b2/ be/ c9/ d3/
> dc/ e6/ f2/ fd/
> 09/ 18/ 20/ 2b/ 37/ 43/ 4b/ 54/ 60/ 6a/ 75/ 7f/ 89/ 92/ 9c/ a6/ b4/ bf/ cb/ d4/
> dd/ e7/ f3/ fe/
> 0a/ 19/ 24/ 2d/ 39/ 44/ 4c/ 55/ 61/ 6b/ 76/ 80/ 8a/ 93/ 9f/ a7/ b5/ c0/ cc/ d5/
> de/ e8/ f5/ ff/
>
> the first two bytes of the crc make the sub dir name, the debug file name has
> these first two bytes omitted.
>
> $ ls /usr/lib/debug/.build-id/0d
> c55467dc9eb81a00c7715a790844e7cf035345.debug
>
> objdump/objcopy in binutils 2.28 has these lookups properly working.
>
> Matthias
>
>
>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-06-16 15:39 ` [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace Denis Khalikov
  2017-06-18 14:09   ` Matthias Klose
@ 2017-06-28 23:59   ` Ian Lance Taylor via gcc-patches
  2017-07-01 21:38     ` Denis Khalikov
  2017-07-29 20:42     ` Denis Khalikov
  1 sibling, 2 replies; 28+ messages in thread
From: Ian Lance Taylor via gcc-patches @ 2017-06-28 23:59 UTC (permalink / raw)
  To: Denis Khalikov; +Cc: GCC Patches

On Fri, Jun 16, 2017 at 8:39 AM, Denis Khalikov
<d.khalikov@partner.samsung.com> wrote:
> Hello everyone,
>
> This is a patch for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77631
>
> Can some one please review attached patch.

Sorry to take so long about this.  It's a lot to look at.



> diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog
> index 096ceb6..4bd97f3 100644
> --- a/libbacktrace/ChangeLog
> +++ b/libbacktrace/ChangeLog

It's best if you don't include the ChangeLog as part of the patch.
The patch never applies cleanly anyhow.  Just put the ChangeLog entry
in the e-mail or as a separate attachment.  Actually I guess you did
that part, just leave ChangeLog out of the patch.  Thanks.

> +AC_CHECK_HEADERS(limits.h)
> +
> +AC_CHECK_HEADERS(sys/param.h)

May as well put these in a single AC_CHECK_HEADERS.  But actually it
looks like you only want these to determine PATH_MAX, and I don't
think it's worth it.  Just use 4096.

> +  LINK = 1,
> +  REGULAR = 2

These names, and also DYN, INVALID, and EXEC, are a little too short
and too likely to conflict with some sloppy system header file.  The
enums in this code all use a prefix for each element; do that here
too.

> +/* Cast from void pointer to uint32_t.  */
> +
> +static uint32_t
> +getl32 (void *p)
> +{
> +  char *addr = (char *) p;
> +  uint32_t v = 0;
> +  v = *((uint32_t *) addr);
> +  return v;
> +}

The comment here doesn't look right; this isn't a cast.  And the
argument should really be char*.  But more importantly, the name
suggests that you want a little-endian read, but this won't fetch a
little-endian value on a big-endian system.  Either do a proper
little-endian fetch byte by byte, as the DWARF code already does, or
clarify the function name.  I don't know what you actually need.

> +/* Function that produce a crc32 value for input buffer.  */

Let's move the CRC32 stuff into a different file.  Add a comment
explaining how the table was generated.

> +  static const unsigned long crc32_table[256]

Seems like this should be uint32_t.  In any case not `unsigned long`,
which is 64 bits.  In general the CRC code seems to use `unsigned
long` where I would expect `uint32_t`.

> +  unsigned char buffer[8 * 1024];

This code is called from signal handlers and on threads; this array is
too long to put on the stack.  Instead of looping and calling read
like this, use backtrace_get_view.

> +/* Get length of the path.  */
> +
> +static int
> +pathlen (const char *buffer)

This function doesn't seem to get the length of the path, it seems to
get the length of the base name.

> +  if (offset >= section_size)
> +    return 0;
> +  crc32_debug_link = getl32 (debug_link + offset);

Seems like you should compare offset + 4 to section_size to avoid
running off the end.

> +      error_callback (data, "executable file is not ELF", 0);

This and other error messages no longer seem correct, as this function
is now used for files other than the executable file.

It doesn't seem like elf_get_section_by_name should call
process_elf_header, it seems like that will do unnecessary extra work.
For that matter elf_get_section_by_name shouldn't read the section
headers and names each time, we should only do that once.

> +static int
> +backtrace_readlink

This function should return type_of_file, and the enum should have an
error value.

+      if (buffer[0] == '/')

Use IS_ABSOLUTE_PATH.

> +  debug_descriptor
> +    = backtrace_open_debugfile (descriptor, filename, error_callback,
> + data, state);

If you're going to call this from fileline.c, then the function needs
to be defined in pecoff.c also.  But calling it in fileline.c doesn't
seem right; why doesn't backtrace_initialize call it?

Ian

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-06-28 23:59   ` Ian Lance Taylor via gcc-patches
@ 2017-07-01 21:38     ` Denis Khalikov
  2017-07-29 20:42     ` Denis Khalikov
  1 sibling, 0 replies; 28+ messages in thread
From: Denis Khalikov @ 2017-07-01 21:38 UTC (permalink / raw)
  To: Ian Lance Taylor; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 4043 bytes --]

Hello Ian,
thanks for review, I've fixed issues and updated the patch.
Can you please take a look.

Thanks.

On 06/29/2017 02:59 AM, Ian Lance Taylor wrote:
> On Fri, Jun 16, 2017 at 8:39 AM, Denis Khalikov
> <d.khalikov@partner.samsung.com> wrote:
>> Hello everyone,
>>
>> This is a patch for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77631
>>
>> Can some one please review attached patch.
>
> Sorry to take so long about this.  It's a lot to look at.
>
>
>
>> diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog
>> index 096ceb6..4bd97f3 100644
>> --- a/libbacktrace/ChangeLog
>> +++ b/libbacktrace/ChangeLog
>
> It's best if you don't include the ChangeLog as part of the patch.
> The patch never applies cleanly anyhow.  Just put the ChangeLog entry
> in the e-mail or as a separate attachment.  Actually I guess you did
> that part, just leave ChangeLog out of the patch.  Thanks.
>
>> +AC_CHECK_HEADERS(limits.h)
>> +
>> +AC_CHECK_HEADERS(sys/param.h)
>
> May as well put these in a single AC_CHECK_HEADERS.  But actually it
> looks like you only want these to determine PATH_MAX, and I don't
> think it's worth it.  Just use 4096.
>
>> +  LINK = 1,
>> +  REGULAR = 2
>
> These names, and also DYN, INVALID, and EXEC, are a little too short
> and too likely to conflict with some sloppy system header file.  The
> enums in this code all use a prefix for each element; do that here
> too.
>
>> +/* Cast from void pointer to uint32_t.  */
>> +
>> +static uint32_t
>> +getl32 (void *p)
>> +{
>> +  char *addr = (char *) p;
>> +  uint32_t v = 0;
>> +  v = *((uint32_t *) addr);
>> +  return v;
>> +}
>
> The comment here doesn't look right; this isn't a cast.  And the
> argument should really be char*.  But more importantly, the name
> suggests that you want a little-endian read, but this won't fetch a
> little-endian value on a big-endian system.  Either do a proper
> little-endian fetch byte by byte, as the DWARF code already does, or
> clarify the function name.  I don't know what you actually need.
>
>> +/* Function that produce a crc32 value for input buffer.  */
>
> Let's move the CRC32 stuff into a different file.  Add a comment
> explaining how the table was generated.
>
>> +  static const unsigned long crc32_table[256]
>
> Seems like this should be uint32_t.  In any case not `unsigned long`,
> which is 64 bits.  In general the CRC code seems to use `unsigned
> long` where I would expect `uint32_t`.
>
>> +  unsigned char buffer[8 * 1024];
>
> This code is called from signal handlers and on threads; this array is
> too long to put on the stack.  Instead of looping and calling read
> like this, use backtrace_get_view.
>
>> +/* Get length of the path.  */
>> +
>> +static int
>> +pathlen (const char *buffer)
>
> This function doesn't seem to get the length of the path, it seems to
> get the length of the base name.
>
>> +  if (offset >= section_size)
>> +    return 0;
>> +  crc32_debug_link = getl32 (debug_link + offset);
>
> Seems like you should compare offset + 4 to section_size to avoid
> running off the end.
>
>> +      error_callback (data, "executable file is not ELF", 0);
>
> This and other error messages no longer seem correct, as this function
> is now used for files other than the executable file.
>
> It doesn't seem like elf_get_section_by_name should call
> process_elf_header, it seems like that will do unnecessary extra work.
> For that matter elf_get_section_by_name shouldn't read the section
> headers and names each time, we should only do that once.
>
>> +static int
>> +backtrace_readlink
>
> This function should return type_of_file, and the enum should have an
> error value.
>
> +      if (buffer[0] == '/')
>
> Use IS_ABSOLUTE_PATH.
>
>> +  debug_descriptor
>> +    = backtrace_open_debugfile (descriptor, filename, error_callback,
>> + data, state);
>
> If you're going to call this from fileline.c, then the function needs
> to be defined in pecoff.c also.  But calling it in fileline.c doesn't
> seem right; why doesn't backtrace_initialize call it?
>
> Ian
>
>
>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-PR-sanitizer-77631.patch --]
[-- Type: text/x-patch; name="0001-PR-sanitizer-77631.patch", Size: 40467 bytes --]

From 053f4fab56ed45dec17c1d4c66868678c817acf9 Mon Sep 17 00:00:00 2001
From: Denis Khalikov <d.khalikov@partner.samsung.com>
Date: Sat, 1 Jul 2017 23:37:34 +0300
Subject: [PATCH] PR sanitizer/77631

    * elf.c (enum type_of_file): New enum.
    (enum type_of_elf): New enum.
    (enum debug_path): New enum.
    (get_uint32): New function.
    (get_crc32): New function.
    (base_name_len): New function.
    (check_sum): New function. Verify sum.
    (process_elf_header): New function. Process elf header.
    (elf_get_section_by_name): New function. Get section by name.
    (backtrace_readlink): New function. Get type of file from filename.
    (resolve_realname): New function. Resolve real name if file is link.
    (backtrace_resolve_realname): New function. Resolve real name for any
    file type.
    (search_for_debugfile): New function. Search for debug file in known
    paths.
    (open_debugfile_by_gnulink): New function. Open debug file with
    gnulink.
    (hex): New function. Convert to hex.
    (get_build_id_name): New function. Generate build-id name.
    (open_debugfile_by_build_id): New function. Open debug file with
    build-id.
    (backtrace_open_debugfile): New function. Open debug file.
    (elf_add): Move code which reads elf header, headers section and names
    section to process_elf_header.
    Call backtrace_open_debugfile_file for executable.
    (phdr_callback): Call backtrace_open_debugfile function for shared
    library.
    * crc32.c: New file.
    (gnu_debuglink_crc32): New function. Generate crc32 sum.
---
 libbacktrace/crc32.c | 107 ++++++
 libbacktrace/elf.c   | 995 +++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 1000 insertions(+), 102 deletions(-)
 create mode 100644 libbacktrace/crc32.c

diff --git a/libbacktrace/crc32.c b/libbacktrace/crc32.c
new file mode 100644
index 0000000..18d8119
--- /dev/null
+++ b/libbacktrace/crc32.c
@@ -0,0 +1,107 @@
+/* crc32.c -- compute the CRC-32
+
+   The CRC-32 defined in IEEE 802.3 using the polynomial:
+
+   x32 + x26 + x23 + x22 + x16 + x12 + x11  + x10 + x8 + x7 + x5 + x4
+   + x2 + x + 1
+
+   Hexademical representation of the polynomial over GF(2): 0xedb88320
+
+   The table was gerated by algorithm from zlib.(zlib/crc32.c)
+   The algorithm is described below.
+
+   * Copyright (C) 1995-2006, 2010, 2011, 2012, 2016 Mark Adler
+   * For conditions of distribution and use, see copyright notice in zlib.h
+
+   Polynomials over GF(2) are represented in binary, one bit per coefficient,
+   with the lowest powers in the most significant bit.  Then adding polynomials
+   is just exclusive-or, and multiplying a polynomial by x is a right shift by
+   one.
+
+   This calculation is done using the shift-register method of multiplying and
+   taking the remainder.  The register is initialized to zero, and for each
+   incoming bit, x^32 is added mod p to the register if the bit is a one (where
+   x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
+   x (which is shifting right by one and adding x^32 mod p if the bit shifted
+   out is a one).  We start with the highest power (least significant bit) of
+   q and repeat for all eight bits of q.
+
+   unsigned long crc_table[256];
+   void make_crc_table()
+   {
+    unsigned long c;
+    int n, k;
+    unsigned long poly;
+    static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+    poly = 0;
+    for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++)
+        poly |= (unsigned long)1 << (31 - p[n]);
+
+    for (n = 0; n < 256; n++) {
+       c = (unsigned long)n;
+       for (k = 0; k < 8; k++)
+           c = c & 1 ? poly ^ (c >> 1) : c >> 1;
+       crc_table[n] = c;
+     }
+   }
+*/
+
+static const uint32_t 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};
+
+static uint32_t
+gnu_debuglink_crc32 (uint32_t crc, const unsigned char *buf, size_t len)
+{
+  const unsigned char *end;
+  end = buf + len;
+  crc = ~crc;
+  while (buf < end)
+    {
+      crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
+      ++buf;
+    }
+  return ~crc;
+}
diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c
index 89ed42b..c3c5586 100644
--- a/libbacktrace/elf.c
+++ b/libbacktrace/elf.c
@@ -35,6 +35,9 @@ POSSIBILITY OF SUCH DAMAGE.  */
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
 
 #ifdef HAVE_DL_ITERATE_PHDR
 #include <link.h>
@@ -42,6 +45,16 @@ POSSIBILITY OF SUCH DAMAGE.  */
 
 #include "backtrace.h"
 #include "internal.h"
+#include "filenames.h"
+/* In case some other libraries build libbacktrace from source
+   (for example: libsanitizer do that way), we should manually update
+   each Makefile for each library to avoid situation with undefined
+   references after adding new file into libbacktrace sources.
+   Another way is to include crc32.c file into preproccessing stage.  */
+#include "crc32.c"
+
+#undef PATH_MAX
+#define PATH_MAX 4096
 
 #ifndef HAVE_DL_ITERATE_PHDR
 
@@ -283,6 +296,855 @@ struct elf_syminfo_data
   size_t count;
 };
 
+/* Information to read ELF note section.  */
+
+typedef struct
+{
+  unsigned char namesz[4];
+  unsigned char descsz[4];
+  unsigned char type[4];
+  unsigned char name[1];
+} Elf_External_Note;
+
+/* Information about type of the file.  */
+
+enum type_of_file
+{
+  FILE_TYPE_INVALID = -1,
+  FILE_TYPE_LINK = 1,
+  FILE_TYPE_REGULAR = 2
+};
+
+/* Information about debug paths.  */
+
+enum debug_path
+{
+  DEBUG_PATH_CURRENT,
+  DEBUG_PATH_CURRENT_DEBUG,
+  DEBUG_PATH_USR_LIB_DEBUG,
+  DEBUG_PATH_USR_LIB_DEBUG_PATH_TO_EXE,
+  DEBUG_PATH_MAX
+};
+
+/* Type of the ELF file.  */
+
+enum type_of_elf
+{
+  ELF_TYPE_DYN = -1,
+  ELF_TYPE_INVALID = 0,
+  ELF_TYPE_EXEC = 1
+};
+
+/* Paths to debug file.  */
+
+static const char *const debug_file_path[DEBUG_PATH_MAX]
+  = {"", ".debug/", "/usr/lib/debug/", "/usr/lib/debug"};
+
+/* Get a uint32 from the buffer.  */
+
+static uint32_t
+get_uint32 (const unsigned char *p, int is_bigendian)
+{
+  if (is_bigendian)
+    return (((uint32_t) p[0] << 24) | ((uint32_t) p[1] << 16)
+	    | ((uint32_t) p[2] << 8) | (uint32_t) p[3]);
+  else
+    return (((uint32_t) p[3] << 24) | ((uint32_t) p[2] << 16)
+	    | ((uint32_t) p[1] << 8) | (uint32_t) p[0]);
+}
+
+/* Generate crc32 sum from the file.  */
+
+static uint32_t
+get_crc32 (struct backtrace_state *state, int descriptor,
+	   backtrace_error_callback error_callback, void *data)
+{
+  uint32_t file_crc;
+  off_t offset;
+  unsigned int buffer_count;
+  unsigned int buffer_len;
+  const unsigned char *buffer;
+  unsigned int pass;
+  unsigned int out_of_buffer_len;
+  struct stat file_stat;
+  struct backtrace_view file_view;
+
+  buffer_len = 8 * 1024;
+  offset = 0;
+  file_crc = 0;
+  out_of_buffer_len = 0;
+
+  memset (&file_stat, 0, sizeof (struct stat));
+
+  if (fstat (descriptor, &file_stat) == -1)
+    {
+      if (errno != ENOENT)
+	error_callback (data, "fstat", errno);
+      return 0;
+    }
+
+  if (!backtrace_get_view (state, descriptor, offset, file_stat.st_size,
+			   error_callback, data, &file_view))
+    return 0;
+
+  buffer = (const unsigned char *) file_view.data;
+  buffer_count = file_stat.st_size / buffer_len;
+  out_of_buffer_len = file_stat.st_size % buffer_len;
+
+  for (pass = 0; pass < buffer_count; ++pass)
+    {
+      file_crc = gnu_debuglink_crc32 (file_crc, buffer + offset, buffer_len);
+      offset += buffer_len;
+    }
+
+  if (out_of_buffer_len > 0)
+    file_crc
+      = gnu_debuglink_crc32 (file_crc, buffer + offset, out_of_buffer_len);
+
+  backtrace_release_view (state, &file_view, error_callback, data);
+  return file_crc;
+}
+
+/* Get length of the base name.  */
+
+static int
+base_name_len (const char *buffer)
+{
+  int len;
+
+  len = strlen (buffer);
+  while (len > 1 && !IS_DIR_SEPARATOR (buffer[len - 1]))
+    --len;
+  return len > 1 ? len : -1;
+}
+
+/* Verify crc32 sum.  */
+
+static int
+check_sum (struct backtrace_state *state, int descriptor,
+	   backtrace_error_callback error_callback, void *data,
+	   const unsigned char *debug_link, unsigned int offset,
+	   unsigned int section_size, int is_bigendian)
+{
+  unsigned long crc32_debug_link;
+  unsigned long crc32_debug_file;
+  offset += 1;
+  offset = (offset + 3) & ~3;
+  if (offset + 4 > section_size)
+    return 0;
+  crc32_debug_link
+    = get_uint32 (debug_link + offset, is_bigendian);
+  crc32_debug_file = get_crc32 (state, descriptor, error_callback, data);
+  return crc32_debug_link == crc32_debug_file;
+}
+
+/* Process elf header. Verify magic number, version, etc, of the ELF
+   header. Populate ehdr_out, names_view_out and shdrs_view_out, caller
+   should realese view of the names_view_out and shdrs_view_out. Return
+   ELF_TYPE_EXEC if the file type is EXE, ELF_TYPE_DYN if type of the
+   file is DYN and ELF_TYPE_INVALID on the fail.  */
+
+static enum type_of_elf
+process_elf_header (struct backtrace_state *state, int descriptor,
+		    backtrace_error_callback error_callback, void *data,
+		    int exe, b_elf_ehdr *ehdr_out,
+		    struct backtrace_view *shdrs_view_out,
+		    struct backtrace_view *names_view_out)
+{
+  struct backtrace_view ehdr_view;
+  const b_elf_shdr *shstrhdr;
+  const b_elf_shdr *shdrs;
+  unsigned int shstrndx;
+  unsigned int shnum;
+  off_t shoff;
+  int shdrs_view_valid;
+
+  shdrs_view_valid = 0;
+
+  if (!backtrace_get_view (state, descriptor, 0, sizeof *ehdr_out,
+			   error_callback, data, &ehdr_view))
+    goto fail;
+
+  memcpy (ehdr_out, ehdr_view.data, sizeof *ehdr_out);
+
+  backtrace_release_view (state, &ehdr_view, error_callback, data);
+
+  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, "processed file is not ELF", 0);
+      goto fail;
+    }
+  if (ehdr_out->e_ident[EI_VERSION] != EV_CURRENT)
+    {
+      error_callback (data, "processed file is unrecognized ELF version", 0);
+      goto fail;
+    }
+
+#if BACKTRACE_ELF_SIZE == 32
+#define BACKTRACE_ELFCLASS ELFCLASS32
+#else
+#define BACKTRACE_ELFCLASS ELFCLASS64
+#endif
+
+  if (ehdr_out->e_ident[EI_CLASS] != BACKTRACE_ELFCLASS)
+    {
+      error_callback (data, "processed file is unexpected ELF class", 0);
+      goto fail;
+    }
+
+  if (ehdr_out->e_ident[EI_DATA] != ELFDATA2LSB
+      && ehdr_out->e_ident[EI_DATA] != ELFDATA2MSB)
+    {
+      error_callback (data, "processed file has unknown endianness", 0);
+      goto fail;
+    }
+
+  /* If the executable is ET_DYN, it is either a PIE, or we are running
+     directly a shared library with .interp.  We need to wait for
+     dl_iterate_phdr in that case to determine the actual base_address.  */
+  if (exe && ehdr_out->e_type == ET_DYN)
+    return ELF_TYPE_DYN;
+
+  shoff = ehdr_out->e_shoff;
+  shnum = ehdr_out->e_shnum;
+  shstrndx = ehdr_out->e_shstrndx;
+
+  if ((shnum == 0 || shstrndx == SHN_XINDEX) && shoff != 0)
+    {
+      struct backtrace_view shdr_view;
+      const b_elf_shdr *shdr;
+
+      if (!backtrace_get_view (state, descriptor, shoff, sizeof (b_elf_shdr),
+			       error_callback, data, &shdr_view))
+	goto fail;
+
+      shdr = (const b_elf_shdr *) shdr_view.data;
+
+      if (shnum == 0)
+	shnum = shdr->sh_size;
+
+      if (shstrndx == SHN_XINDEX)
+	{
+	  shstrndx = shdr->sh_link;
+
+	  /* Versions of the GNU binutils between 2.12 and 2.18 did
+	     not handle objects with more than SHN_LORESERVE sections
+	     correctly.  All large section indexes were offset by
+	     0x100.  There is more information at
+	     http://sourceware.org/bugzilla/show_bug.cgi?id-5900 .
+	     Fortunately these object files are easy to detect, as the
+	     GNU binutils always put the section header string table
+	     near the end of the list of sections.  Thus if the
+	     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;
+	}
+      backtrace_release_view (state, &shdr_view, error_callback, data);
+    }
+
+  /* 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_out))
+    goto fail;
+
+  shdrs_view_valid = 1;
+
+  shdrs = (const b_elf_shdr *) shdrs_view_out->data;
+  shstrhdr = &shdrs[shstrndx - 1];
+
+  /* Read the section names.  */
+
+  if (!backtrace_get_view (state, descriptor, shstrhdr->sh_offset,
+			   shstrhdr->sh_size, error_callback, data,
+			   names_view_out))
+    goto fail;
+
+  ehdr_out->e_shoff = shoff;
+  ehdr_out->e_shnum = shnum;
+  ehdr_out->e_shstrndx = shstrndx;
+
+  return ELF_TYPE_EXEC;
+
+ fail:
+  /* In case fail happens with backtrace_get_view for names section.  */
+  if (shdrs_view_valid)
+    backtrace_release_view (state, shdrs_view_out, error_callback, data);
+  return ELF_TYPE_INVALID;
+}
+
+/* Get content of the specifying section.  */
+
+static unsigned char *
+elf_get_section_by_name (struct backtrace_state *state, int descriptor,
+			 backtrace_error_callback error_callback, void *data,
+			 unsigned int *section_data_len_out, const char *section_name,
+			 b_elf_ehdr *ehdr, struct backtrace_view *shdrs_view,
+			 struct backtrace_view *names_view)
+{
+  const b_elf_shdr *shdrs;
+  const b_elf_shdr *shstrhdr;
+  size_t shstr_size;
+  struct backtrace_view section_view;
+  const char *names;
+  unsigned int i;
+  unsigned int shnum;
+  unsigned int shstrndx;
+  int section_view_valid;
+  unsigned char *section_data;
+
+  section_view_valid = 0;
+  section_data = NULL;
+
+  shnum = ehdr->e_shnum;
+  shstrndx = ehdr->e_shstrndx;
+  shdrs = (const b_elf_shdr *) shdrs_view->data;
+  shstrhdr = &shdrs[shstrndx - 1];
+  shstr_size = shstrhdr->sh_size;
+
+  names = (const char *) names_view->data;
+
+  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, section_name) == 0)
+	{
+	  if (backtrace_get_view (state, descriptor, shdr->sh_offset,
+				  shdr->sh_size, error_callback, data,
+				  &section_view))
+	    {
+	      section_view_valid = 1;
+	      section_data
+		= backtrace_alloc (state, shdr->sh_size, error_callback, data);
+	      if (section_data == NULL)
+		goto exit;
+	      memcpy (section_data, section_view.data, shdr->sh_size);
+	      *section_data_len_out = shdr->sh_size;
+	    }
+	  break;
+	}
+    }
+
+ exit:
+  if (section_view_valid)
+    backtrace_release_view (state, &section_view, error_callback, data);
+  return section_data;
+}
+
+/* Verify type of the file.  */
+
+static enum type_of_file
+backtrace_readlink (const char *filename,
+		    backtrace_error_callback error_callback, void *data)
+{
+  struct stat link_stat;
+  enum type_of_file file_type;
+  mode_t mode;
+
+  memset (&link_stat, 0, sizeof (struct stat));
+
+  if (lstat (filename, &link_stat) == -1)
+    {
+      if (errno != ENOENT)
+	error_callback (data, filename, errno);
+      file_type = FILE_TYPE_INVALID;
+    }
+
+  mode = link_stat.st_mode & S_IFMT;
+
+  switch (mode)
+    {
+    case S_IFLNK:
+      file_type = FILE_TYPE_LINK;
+      break;
+    case S_IFREG:
+      file_type = FILE_TYPE_REGULAR;
+      break;
+    default:
+      file_type = FILE_TYPE_INVALID;
+    }
+  return file_type;
+}
+
+/* Resolve full name of the link. In this case we can't use realpath function
+   because it could be undefined on some platfroms, also it allocates memory
+   by malloc, which we can't use.  */
+
+static int
+resolve_realname (const char *filename, char *buffer,
+		  struct backtrace_state *state,
+		  backtrace_error_callback error_callback, void *data)
+{
+  char *temp_buffer;
+  enum type_of_file file_type;
+  int filename_len;
+  int temp_filename_len;
+  int valid_temp_buffer;
+  int base_len;
+
+  valid_temp_buffer = 0;
+  filename_len = -1;
+  file_type = FILE_TYPE_LINK;
+
+  /* Allocate memory for sizeof(PATH_MAX) + 1 bytes because at this time
+     we don't know how long path could be.  */
+  temp_buffer = backtrace_alloc (state, PATH_MAX + 1, error_callback, data);
+  if (temp_buffer == NULL)
+    return -1;
+
+  valid_temp_buffer = 1;
+
+  memset (temp_buffer, 0, PATH_MAX + 1);
+  memcpy (temp_buffer, filename, strlen (filename));
+
+  while (file_type == FILE_TYPE_LINK)
+    {
+      filename_len = readlink (temp_buffer, buffer, PATH_MAX);
+      if (filename_len < 1)
+	goto exit;
+
+      temp_filename_len = strlen (buffer);
+
+      /* Full path.  */
+      if (IS_ABSOLUTE_PATH (buffer))
+	{
+	  memset (temp_buffer, 0, PATH_MAX);
+	  memcpy (temp_buffer, buffer, temp_filename_len);
+	}
+      else
+	{
+	  /* Relative path.  */
+	  base_len = base_name_len (temp_buffer);
+	  if (base_len < 1 || (base_len + filename_len > PATH_MAX))
+	    {
+	      filename_len = -1;
+	      goto exit;
+	    }
+	  memcpy (temp_buffer + base_len, buffer, filename_len);
+	  temp_buffer[base_len + filename_len] = '\0';
+	}
+
+      file_type = backtrace_readlink (temp_buffer, error_callback, data);
+      memset (buffer, 0, filename_len);
+    }
+
+  if (file_type != FILE_TYPE_REGULAR)
+    {
+      filename_len = -1;
+      goto exit;
+    }
+
+  filename_len = strlen (temp_buffer);
+  memcpy (buffer, temp_buffer, filename_len);
+
+ exit:
+  if (valid_temp_buffer)
+    backtrace_free (state, temp_buffer, PATH_MAX + 1, error_callback, data);
+  return filename_len;
+}
+
+/* Resolve realname of the filename. This function verifies filename.
+   If filename is name of the file it populates realname buffer.
+   If filename is link, it calls resolve_realname function.  */
+
+static int
+backtrace_resolve_realname (const char *filename, char *realname,
+			    struct backtrace_state *state,
+			    backtrace_error_callback error_callback, void *data)
+{
+  enum type_of_file file_type;
+  int filename_len;
+
+  filename_len = -1;
+
+  file_type = backtrace_readlink (filename, error_callback, data);
+
+  if (file_type == FILE_TYPE_LINK)
+    {
+      /* Read the actual filename.  */
+      filename_len
+	= resolve_realname (filename, realname, state, error_callback, data);
+      if (filename_len <= 0)
+	return 0;
+    }
+  else if (file_type == FILE_TYPE_REGULAR)
+    {
+      filename_len = strlen (filename);
+      if (filename_len > PATH_MAX)
+	return 0;
+      memcpy (realname, filename, filename_len);
+    }
+  /* FILE_TYPE_INVALID.  */
+  else
+    return 0;
+
+  return 1;
+}
+
+/* Search for debug file into specifying directorires.  */
+
+static int
+search_for_debugfile (char *realname, char *debug_filename,
+		      backtrace_error_callback error_callback, void *data,
+		      struct backtrace_state *state)
+{
+  int debug_filename_len;
+  int pass;
+  int debug_path_len;
+  int debug_does_not_exist;
+  int debug_descriptor;
+  int base_len;
+  char *buffer;
+  int buffer_len;
+  int valid_buffer;
+
+  debug_descriptor = -1;
+  valid_buffer = 0;
+
+  base_len = base_name_len (realname);
+  if (base_len < 1)
+    goto exit;
+
+  debug_filename_len = strlen ((const char *) debug_filename);
+
+  if (debug_filename_len < 1)
+    goto exit;
+
+  buffer_len = base_len + strlen (debug_file_path[DEBUG_PATH_USR_LIB_DEBUG])
+	       + debug_filename_len + 1;
+
+  buffer = backtrace_alloc (state, buffer_len, error_callback, data);
+
+  if (buffer == NULL)
+    goto exit;
+  memset (buffer, 0, buffer_len);
+  memcpy (buffer, realname, base_len);
+
+  valid_buffer = 1;
+  for (pass = 0; pass < DEBUG_PATH_MAX; ++pass)
+    {
+      switch (pass)
+	{
+	case DEBUG_PATH_CURRENT:
+	  {
+	    memcpy (buffer + base_len, debug_filename, debug_filename_len);
+	    break;
+	  }
+	case DEBUG_PATH_CURRENT_DEBUG:
+	  {
+	    debug_path_len = strlen (debug_file_path[DEBUG_PATH_CURRENT_DEBUG]);
+	    memcpy (buffer + base_len,
+		    debug_file_path[DEBUG_PATH_CURRENT_DEBUG], debug_path_len);
+	    memcpy (buffer + base_len + debug_path_len, debug_filename,
+		    debug_filename_len);
+	    break;
+	  }
+	case DEBUG_PATH_USR_LIB_DEBUG:
+	  {
+	    debug_path_len = strlen (debug_file_path[DEBUG_PATH_USR_LIB_DEBUG]);
+	    memset (buffer, 0, buffer_len);
+	    memcpy (buffer, debug_file_path[DEBUG_PATH_USR_LIB_DEBUG],
+		    debug_path_len);
+	    memcpy (buffer + debug_path_len, debug_filename,
+		    debug_filename_len);
+	    break;
+	  }
+	case DEBUG_PATH_USR_LIB_DEBUG_PATH_TO_EXE:
+	  {
+	    debug_path_len
+	      = strlen (debug_file_path[DEBUG_PATH_USR_LIB_DEBUG_PATH_TO_EXE]);
+	    memset (buffer, 0, buffer_len);
+	    memcpy (buffer,
+		    debug_file_path[DEBUG_PATH_USR_LIB_DEBUG_PATH_TO_EXE],
+		    debug_path_len);
+	    memcpy (buffer + debug_path_len, realname, base_len);
+	    memcpy (buffer + debug_path_len + base_len, debug_filename,
+		    debug_filename_len);
+	    break;
+	  }
+	default:
+	  goto exit;
+	}
+
+      debug_descriptor
+	= backtrace_open (buffer, error_callback, data, &debug_does_not_exist);
+
+      if (debug_descriptor > 0)
+	break;
+    }
+ exit:
+  if (valid_buffer)
+    backtrace_free (state, buffer, buffer_len, error_callback, data);
+  return debug_descriptor;
+}
+
+/* Open debug file by gnulink.  */
+
+static int
+open_debugfile_by_gnulink (char *realname, unsigned char *section_data,
+			   unsigned int section_data_len, int is_bigendian,
+			   struct backtrace_state *state,
+			   backtrace_error_callback error_callback, void *data)
+{
+  int debug_descriptor;
+
+  debug_descriptor = search_for_debugfile (realname, (char *) section_data,
+					   error_callback, data, state);
+  if (debug_descriptor < 0)
+    goto exit;
+
+  /* Check the crc32 checksum if it not the same return -1.  */
+
+  if (!check_sum (state, debug_descriptor, error_callback, data,
+		  (const unsigned char *) section_data,
+		  strlen ((char *) section_data), section_data_len,
+		  is_bigendian))
+    {
+      /* If crc32 sums are different, just close the descriptor
+	 associated with debuginfo file.  */
+      backtrace_close (debug_descriptor, error_callback, data);
+      debug_descriptor = -1;
+    }
+
+ exit:
+  return debug_descriptor;
+}
+
+/* Convert char to hex */
+
+static char
+hex (char ch)
+{
+  return ch > 9 ? ('a' + (ch - 10)) : ('0' + ch);
+}
+
+/* Get build-id name.  */
+
+static char *
+get_build_id_name (unsigned char *section_data, unsigned int *len,
+		   int is_bigendian, struct backtrace_state *state,
+		   backtrace_error_callback error_callback, void *data)
+{
+  Elf_External_Note *build_id_section;
+  char *build_id_name;
+  char *temp;
+  const char *debug_postfix;
+  const char *debug_prefix;
+  size_t debug_postfix_len;
+  size_t debug_prefix_len;
+  size_t name_size;
+  size_t offset;
+  unsigned char *hash_start;
+  uint32_t hash_size;
+  uint32_t identifier;
+
+  debug_postfix_len = 6;
+  debug_prefix_len = 10;
+  debug_postfix = ".debug";
+  debug_prefix = ".build-id/";
+  *len = 0;
+
+  build_id_section = (Elf_External_Note *) section_data;
+  hash_size = get_uint32 (build_id_section->descsz, is_bigendian);
+  identifier = get_uint32 (build_id_section->type, is_bigendian);
+  name_size = get_uint32 (build_id_section->namesz, is_bigendian);
+
+  if (identifier != NT_GNU_BUILD_ID || hash_size == 0 || name_size != 4
+      || strncmp ((char *) build_id_section->name, "GNU", 3) != 0)
+    return NULL;
+
+  offset = 16;
+  hash_start = section_data + offset;
+  *len = hash_size * 2 + debug_postfix_len + debug_prefix_len + 1;
+  build_id_name = backtrace_alloc (state, *len, error_callback, data);
+
+  if (build_id_name == NULL)
+    {
+      *len = 0;
+      return NULL;
+    }
+
+  memset (build_id_name, 0, *len);
+  memcpy (build_id_name, debug_prefix, debug_prefix_len);
+  temp = build_id_name + debug_prefix_len;
+
+  *temp++ = hex ((*hash_start & 0xF0) >> 4);
+  *temp++ = hex (*hash_start & 0x0F);
+  ++hash_start;
+  --hash_size;
+
+  memcpy (temp, "/", 1);
+  ++temp;
+
+  while (hash_size--)
+    {
+      *temp++ = hex ((*hash_start & 0xF0) >> 4);
+      *temp++ = hex (*hash_start & 0x0F);
+      ++hash_start;
+    }
+
+  memcpy (temp, debug_postfix, debug_postfix_len);
+  return build_id_name;
+}
+
+/* Open file by build-id.  */
+
+static int
+open_debugfile_by_build_id (char *realname, unsigned char *section_data,
+			    int is_bigendian, struct backtrace_state *state,
+			    backtrace_error_callback error_callback, void *data)
+
+{
+  char *build_id_name;
+  int debug_descriptor;
+  unsigned int build_id_name_len;
+  size_t valid_build_id_name;
+
+  debug_descriptor = -1;
+  valid_build_id_name = 0;
+
+  build_id_name = get_build_id_name (section_data, &build_id_name_len,
+				     is_bigendian, state, error_callback, data);
+
+  if (build_id_name == NULL || build_id_name_len == 0)
+    goto exit;
+
+  valid_build_id_name = 1;
+
+  debug_descriptor = search_for_debugfile (realname, build_id_name,
+					   error_callback, data, state);
+
+ exit:
+  if (valid_build_id_name)
+    backtrace_free (state, build_id_name, build_id_name_len, error_callback,
+		    data);
+  return debug_descriptor;
+}
+
+/* Open debug file.  */
+
+static int
+backtrace_open_debugfile (int descriptor, const char *filename,
+			  backtrace_error_callback error_callback, void *data,
+			  struct backtrace_state *state)
+{
+  int debug_descriptor;
+  unsigned char *gnulink_section_data;
+  unsigned char *build_id_section_data;
+  size_t valid_descriptor;
+  size_t valid_gnulink_section_data;
+  size_t valid_build_id_section_data;
+  size_t valid_realname;
+  unsigned int build_id_section_data_len;
+  unsigned int gnu_link_section_data_len;
+  char *realname;
+  b_elf_ehdr ehdr;
+  struct backtrace_view shdrs_view;
+  struct backtrace_view names_view;
+  size_t valid_elf_header;
+
+  valid_elf_header = 0;
+  valid_realname = 0;
+  valid_descriptor = 0;
+  valid_gnulink_section_data = 0;
+  valid_build_id_section_data = 0;
+  build_id_section_data_len = 0;
+  gnu_link_section_data_len = 0;
+  debug_descriptor = -1;
+
+  if (!process_elf_header (state, descriptor, error_callback, data, 0, &ehdr,
+			   &shdrs_view, &names_view))
+    goto exit;
+
+  valid_elf_header = 1;
+
+  realname = backtrace_alloc (state, PATH_MAX + 1, error_callback, data);
+
+  if (realname == NULL)
+    goto exit;
+
+  /* Indicates that we successfully allocated memory.  */
+  valid_realname = 1;
+  memset (realname, 0, PATH_MAX + 1);
+
+  /* Populate the buffer with realname.  */
+  if (!backtrace_resolve_realname (filename, realname, state, error_callback,
+				   data))
+    goto exit;
+
+  /* Check if build-id section does exist.  */
+  build_id_section_data
+    = elf_get_section_by_name (state, descriptor, error_callback, data,
+			       &build_id_section_data_len, ".note.gnu.build-id",
+			       &ehdr, &shdrs_view, &names_view);
+
+  if (build_id_section_data != NULL && build_id_section_data_len > 0)
+    {
+      valid_build_id_section_data = 1;
+      debug_descriptor
+	= open_debugfile_by_build_id (realname, build_id_section_data,
+				      ehdr.e_ident[EI_DATA] == ELFDATA2MSB,
+				      state, error_callback, data);
+    }
+
+  if (debug_descriptor < 0)
+    {
+      gnulink_section_data
+	= elf_get_section_by_name (state, descriptor, error_callback, data,
+				   &gnu_link_section_data_len, ".gnu_debuglink",
+				   &ehdr, &shdrs_view, &names_view);
+
+      if (gnulink_section_data != NULL && gnu_link_section_data_len > 0)
+	{
+	  valid_gnulink_section_data = 1;
+	  debug_descriptor
+	    = open_debugfile_by_gnulink (realname, gnulink_section_data,
+					 gnu_link_section_data_len,
+					 ehdr.e_ident[EI_DATA] == ELFDATA2MSB,
+					 state, error_callback, data);
+	}
+    }
+
+  if (debug_descriptor >= 0)
+    valid_descriptor = 1;
+
+ exit:
+  if (valid_descriptor)
+    backtrace_close (descriptor, error_callback, data);
+  if (valid_gnulink_section_data)
+    backtrace_free (state, gnulink_section_data, gnu_link_section_data_len,
+		    error_callback, data);
+  if (valid_build_id_section_data)
+    backtrace_free (state, build_id_section_data, build_id_section_data_len,
+		    error_callback, data);
+  if (valid_realname)
+    backtrace_free (state, realname, PATH_MAX + 1, error_callback, data);
+  if (valid_elf_header)
+    {
+      backtrace_release_view (state, &names_view, error_callback, data);
+      backtrace_release_view (state, &shdrs_view, error_callback, data);
+    }
+  return debug_descriptor;
+}
+
 /* A dummy callback function used when we can't find any debug info.  */
 
 static int
@@ -521,9 +1383,7 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
 	 backtrace_error_callback error_callback, void *data,
 	 fileline *fileline_fn, int *found_sym, int *found_dwarf, int exe)
 {
-  struct backtrace_view ehdr_view;
   b_elf_ehdr ehdr;
-  off_t shoff;
   unsigned int shnum;
   unsigned int shstrndx;
   struct backtrace_view shdrs_view;
@@ -531,7 +1391,6 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
   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;
@@ -547,6 +1406,7 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
   off_t max_offset;
   struct backtrace_view debug_view;
   int debug_view_valid;
+  enum type_of_elf elf_type;
 
   *found_sym = 0;
   *found_dwarf = 0;
@@ -557,116 +1417,32 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
   strtab_view_valid = 0;
   debug_view_valid = 0;
 
-  if (!backtrace_get_view (state, descriptor, 0, sizeof ehdr, error_callback,
-			   data, &ehdr_view))
-    goto fail;
-
-  memcpy (&ehdr, ehdr_view.data, sizeof ehdr);
-
-  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)
-    {
-      error_callback (data, "executable file is not ELF", 0);
-      goto fail;
-    }
-  if (ehdr.e_ident[EI_VERSION] != EV_CURRENT)
-    {
-      error_callback (data, "executable file is unrecognized ELF version", 0);
-      goto fail;
-    }
-
-#if BACKTRACE_ELF_SIZE == 32
-#define BACKTRACE_ELFCLASS ELFCLASS32
-#else
-#define BACKTRACE_ELFCLASS ELFCLASS64
-#endif
-
-  if (ehdr.e_ident[EI_CLASS] != BACKTRACE_ELFCLASS)
+  elf_type = process_elf_header (state, descriptor, error_callback, data, exe,
+				 &ehdr, &shdrs_view, &names_view);
+  switch (elf_type)
     {
-      error_callback (data, "executable file is unexpected ELF class", 0);
+    /* Binary compiled with PIE option.  */
+    case ELF_TYPE_DYN:
+      return -1;
+    case ELF_TYPE_EXEC:
+      break;
+    /* Header is invalid.  */
+    case ELF_TYPE_INVALID:
       goto fail;
     }
 
-  if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB
-      && ehdr.e_ident[EI_DATA] != ELFDATA2MSB)
-    {
-      error_callback (data, "executable file has unknown endianness", 0);
-      goto fail;
-    }
-
-  /* If the executable is ET_DYN, it is either a PIE, or we are running
-     directly a shared library with .interp.  We need to wait for
-     dl_iterate_phdr in that case to determine the actual base_address.  */
-  if (exe && ehdr.e_type == ET_DYN)
-    return -1;
-
-  shoff = ehdr.e_shoff;
-  shnum = ehdr.e_shnum;
-  shstrndx = ehdr.e_shstrndx;
-
-  if ((shnum == 0 || shstrndx == SHN_XINDEX)
-      && shoff != 0)
-    {
-      struct backtrace_view shdr_view;
-      const b_elf_shdr *shdr;
-
-      if (!backtrace_get_view (state, descriptor, shoff, 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 (shstrndx == SHN_XINDEX)
-	{
-	  shstrndx = shdr->sh_link;
-
-	  /* Versions of the GNU binutils between 2.12 and 2.18 did
-	     not handle objects with more than SHN_LORESERVE sections
-	     correctly.  All large section indexes were offset by
-	     0x100.  There is more information at
-	     http://sourceware.org/bugzilla/show_bug.cgi?id-5900 .
-	     Fortunately these object files are easy to detect, as the
-	     GNU binutils always put the section header string table
-	     near the end of the list of sections.  Thus if the
-	     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;
-	}
-
-      backtrace_release_view (state, &shdr_view, error_callback, data);
-    }
+  shdrs_view_valid = 1;
+  names_view_valid = 1;
 
   /* To translate PC to file/line when using DWARF, we need to find
      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))
-    goto fail;
-  shdrs_view_valid = 1;
   shdrs = (const b_elf_shdr *) shdrs_view.data;
 
-  /* Read the section names.  */
-
+  shnum = ehdr.e_shnum;
+  shstrndx = ehdr.e_shstrndx;
   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 fail;
-  names_view_valid = 1;
   names = (const char *) names_view.data;
 
   symtab_shndx = 0;
@@ -846,7 +1622,7 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
     backtrace_release_view (state, &debug_view, error_callback, data);
   if (descriptor != -1)
     backtrace_close (descriptor, error_callback, data);
-  return 0;
+   return 0;
 }
 
 /* Data passed to phdr_callback.  */
@@ -877,6 +1653,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 debug_descriptor;
 
   /* 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
@@ -895,11 +1672,19 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED,
 	  backtrace_close (pd->exe_descriptor, pd->error_callback, pd->data);
 	  pd->exe_descriptor = -1;
 	}
+      debug_descriptor = -1;
 
       descriptor = backtrace_open (info->dlpi_name, pd->error_callback,
 				   pd->data, &does_not_exist);
+
       if (descriptor < 0)
 	return 0;
+
+      debug_descriptor
+	= backtrace_open_debugfile (descriptor, info->dlpi_name,
+				    pd->error_callback, pd->data, pd->state);
+      if (debug_descriptor >= 0)
+	descriptor = debug_descriptor;
     }
 
   if (elf_add (pd->state, descriptor, info->dlpi_addr, pd->error_callback,
@@ -929,6 +1714,12 @@ backtrace_initialize (struct backtrace_state *state, int descriptor,
   int found_dwarf;
   fileline elf_fileline_fn = elf_nodebug;
   struct phdr_data pd;
+  int debug_descriptor;
+
+  debug_descriptor = backtrace_open_debugfile (descriptor, state->filename,
+					       error_callback, data, state);
+  if (debug_descriptor >= 0)
+    descriptor = debug_descriptor;
 
   ret = elf_add (state, descriptor, 0, error_callback, data, &elf_fileline_fn,
 		 &found_sym, &found_dwarf, 1);
-- 
1.9.1


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-06-28 23:59   ` Ian Lance Taylor via gcc-patches
  2017-07-01 21:38     ` Denis Khalikov
@ 2017-07-29 20:42     ` Denis Khalikov
  2017-09-10 21:11       ` Ian Lance Taylor via gcc-patches
  1 sibling, 1 reply; 28+ messages in thread
From: Denis Khalikov @ 2017-07-29 20:42 UTC (permalink / raw)
  To: Ian Lance Taylor; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 4021 bytes --]

On 06/29/2017 02:59 AM, Ian Lance Taylor wrote:
> On Fri, Jun 16, 2017 at 8:39 AM, Denis Khalikov
> <d.khalikov@partner.samsung.com> wrote:
>> Hello everyone,
>>
>> This is a patch for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77631
>>
>> Can some one please review attached patch.
>
> Sorry to take so long about this.  It's a lot to look at.
>
>
>
>> diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog
>> index 096ceb6..4bd97f3 100644
>> --- a/libbacktrace/ChangeLog
>> +++ b/libbacktrace/ChangeLog
>
> It's best if you don't include the ChangeLog as part of the patch.
> The patch never applies cleanly anyhow.  Just put the ChangeLog entry
> in the e-mail or as a separate attachment.  Actually I guess you did
> that part, just leave ChangeLog out of the patch.  Thanks.
>
>> +AC_CHECK_HEADERS(limits.h)
>> +
>> +AC_CHECK_HEADERS(sys/param.h)
>
> May as well put these in a single AC_CHECK_HEADERS.  But actually it
> looks like you only want these to determine PATH_MAX, and I don't
> think it's worth it.  Just use 4096.
>
>> +  LINK = 1,
>> +  REGULAR = 2
>
> These names, and also DYN, INVALID, and EXEC, are a little too short
> and too likely to conflict with some sloppy system header file.  The
> enums in this code all use a prefix for each element; do that here
> too.
>
>> +/* Cast from void pointer to uint32_t.  */
>> +
>> +static uint32_t
>> +getl32 (void *p)
>> +{
>> +  char *addr = (char *) p;
>> +  uint32_t v = 0;
>> +  v = *((uint32_t *) addr);
>> +  return v;
>> +}
>
> The comment here doesn't look right; this isn't a cast.  And the
> argument should really be char*.  But more importantly, the name
> suggests that you want a little-endian read, but this won't fetch a
> little-endian value on a big-endian system.  Either do a proper
> little-endian fetch byte by byte, as the DWARF code already does, or
> clarify the function name.  I don't know what you actually need.
>
>> +/* Function that produce a crc32 value for input buffer.  */
>
> Let's move the CRC32 stuff into a different file.  Add a comment
> explaining how the table was generated.
>
>> +  static const unsigned long crc32_table[256]
>
> Seems like this should be uint32_t.  In any case not `unsigned long`,
> which is 64 bits.  In general the CRC code seems to use `unsigned
> long` where I would expect `uint32_t`.
>
>> +  unsigned char buffer[8 * 1024];
>
> This code is called from signal handlers and on threads; this array is
> too long to put on the stack.  Instead of looping and calling read
> like this, use backtrace_get_view.
>
>> +/* Get length of the path.  */
>> +
>> +static int
>> +pathlen (const char *buffer)
>
> This function doesn't seem to get the length of the path, it seems to
> get the length of the base name.
>
>> +  if (offset >= section_size)
>> +    return 0;
>> +  crc32_debug_link = getl32 (debug_link + offset);
>
> Seems like you should compare offset + 4 to section_size to avoid
> running off the end.
>
>> +      error_callback (data, "executable file is not ELF", 0);
>
> This and other error messages no longer seem correct, as this function
> is now used for files other than the executable file.
>
> It doesn't seem like elf_get_section_by_name should call
> process_elf_header, it seems like that will do unnecessary extra work.
> For that matter elf_get_section_by_name shouldn't read the section
> headers and names each time, we should only do that once.
>
>> +static int
>> +backtrace_readlink
>
> This function should return type_of_file, and the enum should have an
> error value.
>
> +      if (buffer[0] == '/')
>
> Use IS_ABSOLUTE_PATH.
>
>> +  debug_descriptor
>> +    = backtrace_open_debugfile (descriptor, filename, error_callback,
>> + data, state);
>
> If you're going to call this from fileline.c, then the function needs
> to be defined in pecoff.c also.  But calling it in fileline.c doesn't
> seem right; why doesn't backtrace_initialize call it?
>
> Ian
>

Hello Ian,
thanks for review.
I've updated the patch, can you please take a look.
Thanks.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: PR-sanitizer-77631.patch --]
[-- Type: text/x-patch; name="PR-sanitizer-77631.patch", Size: 41951 bytes --]

From cbecf0d858c50aa00e5445b93fdd968e0778d225 Mon Sep 17 00:00:00 2001
From: Denis Khalikov <d.khalikov@partner.samsung.com>
Date: Sat, 29 Jul 2017 22:14:48 +0300
Subject: [PATCH] PR sanitizer/77631

    * elf.c (enum type_of_file): New enum.
    (enum type_of_elf): New enum.
    (enum debug_path): New enum.
    (get_uint32): New function.
    (get_crc32): New function.
    (base_name_len): New function.
    (check_sum): New function. Verify sum.
    (process_elf_header): New function. Process elf header.
    (elf_get_section_by_name): New function. Get section by name.
    (backtrace_readlink): New function. Get type of file from filename.
    (resolve_realname): New function. Resolve real name if file is link.
    (backtrace_resolve_realname): New function. Resolve real name for any
    file type.
    (search_for_debugfile): New function. Search for debug file in known
    paths.
    (open_debugfile_by_gnulink): New function. Open debug file with
    gnulink.
    (hex): New function. Convert to hex.
    (get_build_id_name): New function. Generate build-id name.
    (open_debugfile_by_build_id): New function. Open debug file with
    build-id.
    (backtrace_open_debugfile): New function. Open debug file.
    (get_exec_filename): New function. Get pathname of the executable.
    (elf_add): Move code which reads elf header, headers section and names
    section to process_elf_header.
    Call backtrace_open_debugfile_file for executable.
    (phdr_callback): Call backtrace_open_debugfile function for shared
    library.
    * crc32.c: New file.
    (gnu_debuglink_crc32): New function. Generate crc32 sum.
---
 libbacktrace/crc32.c |  107 +++++
 libbacktrace/elf.c   | 1061 +++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 1067 insertions(+), 101 deletions(-)
 create mode 100644 libbacktrace/crc32.c

diff --git a/libbacktrace/crc32.c b/libbacktrace/crc32.c
new file mode 100644
index 0000000..f38a422
--- /dev/null
+++ b/libbacktrace/crc32.c
@@ -0,0 +1,107 @@
+/* crc32.c -- compute the CRC-32
+
+   The CRC-32 defined in IEEE 802.3 using the polynomial:
+
+   x32 + x26 + x23 + x22 + x16 + x12 + x11  + x10 + x8 + x7 + x5 + x4
+   + x2 + x + 1
+
+   Hexademical representation of the polynomial over GF(2): 0xedb88320
+
+   The table was generated by algorithm from zlib.(zlib/crc32.c)
+   The algorithm is described below.
+
+   * Copyright (C) 1995-2006, 2010, 2011, 2012, 2016 Mark Adler
+   * For conditions of distribution and use, see copyright notice in zlib.h
+
+   Polynomials over GF(2) are represented in binary, one bit per coefficient,
+   with the lowest powers in the most significant bit.  Then adding polynomials
+   is just exclusive-or, and multiplying a polynomial by x is a right shift by
+   one.
+
+   This calculation is done using the shift-register method of multiplying and
+   taking the remainder.  The register is initialized to zero, and for each
+   incoming bit, x^32 is added mod p to the register if the bit is a one (where
+   x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
+   x (which is shifting right by one and adding x^32 mod p if the bit shifted
+   out is a one).  We start with the highest power (least significant bit) of
+   q and repeat for all eight bits of q.
+
+   unsigned long crc_table[256];
+   void make_crc_table()
+   {
+    unsigned long c;
+    int n, k;
+    unsigned long poly;
+    static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+    poly = 0;
+    for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++)
+        poly |= (unsigned long)1 << (31 - p[n]);
+
+    for (n = 0; n < 256; n++) {
+       c = (unsigned long)n;
+       for (k = 0; k < 8; k++)
+           c = c & 1 ? poly ^ (c >> 1) : c >> 1;
+       crc_table[n] = c;
+     }
+   }
+*/
+
+static const uint32_t 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};
+
+static uint32_t
+gnu_debuglink_crc32 (uint32_t crc, const unsigned char *buf, size_t len)
+{
+  const unsigned char *end;
+  end = buf + len;
+  crc = ~crc;
+  while (buf < end)
+    {
+      crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
+      ++buf;
+    }
+  return ~crc;
+}
diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c
index 1471007..89d95a4 100644
--- a/libbacktrace/elf.c
+++ b/libbacktrace/elf.c
@@ -35,6 +35,9 @@ POSSIBILITY OF SUCH DAMAGE.  */
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
 
 #ifdef HAVE_DL_ITERATE_PHDR
 #include <link.h>
@@ -42,6 +45,24 @@ POSSIBILITY OF SUCH DAMAGE.  */
 
 #include "backtrace.h"
 #include "internal.h"
+#include "filenames.h"
+/* In case some other libraries build libbacktrace from source
+   (for example: libsanitizer does that way), we should manually update
+   each Makefile for each library to avoid situation with undefined
+   references after adding new file into libbacktrace sources.
+   Another way is to include crc32.c file into preproccessing stage.  */
+#include "crc32.c"
+
+#undef PATH_MAX
+#define PATH_MAX 4096
+
+#ifndef DIR_SEPARATOR
+#define DIR_SEPARATOR '/'
+#endif
+
+#ifndef HAVE_GETEXECNAME
+#define getexecname() NULL
+#endif
 
 #ifndef HAVE_DL_ITERATE_PHDR
 
@@ -283,6 +304,853 @@ struct elf_syminfo_data
   size_t count;
 };
 
+/* Information to read ELF note section.  */
+
+typedef struct
+{
+  unsigned char namesz[4];
+  unsigned char descsz[4];
+  unsigned char type[4];
+  unsigned char name[1];
+} Elf_External_Note;
+
+/* Information about type of the file.  */
+
+enum type_of_file
+{
+  FILE_TYPE_INVALID = -1,
+  FILE_TYPE_LINK = 1,
+  FILE_TYPE_REGULAR = 2
+};
+
+/* Information about debug paths.  */
+
+enum debug_path
+{
+  DEBUG_PATH_CURRENT,
+  DEBUG_PATH_CURRENT_DEBUG,
+  DEBUG_PATH_USR_LIB_DEBUG,
+  DEBUG_PATH_USR_LIB_DEBUG_PATH_TO_EXE,
+  DEBUG_PATH_MAX
+};
+
+/* Type of the ELF file.  */
+
+enum type_of_elf
+{
+  ELF_TYPE_DYN = -1,
+  ELF_TYPE_INVALID = 0,
+  ELF_TYPE_EXEC = 1
+};
+
+/* Paths to debug file.  */
+
+static const char *const debug_file_path[DEBUG_PATH_MAX]
+  = {"", ".debug/", "/usr/lib/debug/", "/usr/lib/debug"};
+
+/* Get a uint32 from the buffer.  */
+
+static uint32_t
+get_uint32 (const unsigned char *p, int is_bigendian)
+{
+  if (is_bigendian)
+    return (((uint32_t) p[0] << 24) | ((uint32_t) p[1] << 16)
+	    | ((uint32_t) p[2] << 8) | (uint32_t) p[3]);
+  else
+    return (((uint32_t) p[3] << 24) | ((uint32_t) p[2] << 16)
+	    | ((uint32_t) p[1] << 8) | (uint32_t) p[0]);
+}
+
+/* Generate crc32 sum from the file.  */
+
+static uint32_t
+get_crc32 (struct backtrace_state *state, int descriptor,
+	   backtrace_error_callback error_callback, void *data)
+{
+  uint32_t file_crc;
+  off_t offset;
+  size_t buffer_count;
+  size_t buffer_len;
+  size_t pass;
+  size_t out_of_buffer_len;
+  const unsigned char *buffer;
+  struct stat file_stat;
+  struct backtrace_view file_view;
+
+  buffer_len = 8 * 1024;
+  offset = 0;
+  file_crc = 0;
+  out_of_buffer_len = 0;
+
+  memset (&file_stat, 0, sizeof (struct stat));
+
+  if (fstat (descriptor, &file_stat) == -1)
+    {
+      if (errno != ENOENT)
+	error_callback (data, "fstat", errno);
+      return 0;
+    }
+
+  if (!backtrace_get_view (state, descriptor, offset, file_stat.st_size,
+			   error_callback, data, &file_view))
+    return 0;
+
+  buffer = (const unsigned char *) file_view.data;
+  buffer_count = file_stat.st_size / buffer_len;
+  out_of_buffer_len = file_stat.st_size % buffer_len;
+
+  for (pass = 0; pass < buffer_count; ++pass)
+    {
+      file_crc = gnu_debuglink_crc32 (file_crc, buffer + offset, buffer_len);
+      offset += buffer_len;
+    }
+
+  if (out_of_buffer_len)
+    file_crc
+      = gnu_debuglink_crc32 (file_crc, buffer + offset, out_of_buffer_len);
+
+  backtrace_release_view (state, &file_view, error_callback, data);
+  return file_crc;
+}
+
+/* Get length of the base name.  */
+
+static size_t
+base_name_len (const char *buffer)
+{
+  size_t len;
+
+  len = strlen (buffer);
+  while (len > 1 && !IS_DIR_SEPARATOR (buffer[len - 1]))
+    --len;
+  return len > 1 ? len : 0;
+}
+
+/* Verify crc32 sum.  */
+
+static unsigned int
+check_sum (struct backtrace_state *state, int descriptor,
+	   backtrace_error_callback error_callback, void *data,
+	   const unsigned char *debug_link, size_t offset, size_t section_size,
+	   int is_bigendian)
+{
+  uint32_t crc32_debug_link;
+  uint32_t crc32_debug_file;
+  offset += 1;
+  offset = (offset + 3) & ~3;
+  if (offset + 4 > section_size)
+    return 0;
+  crc32_debug_link = get_uint32 (debug_link + offset, is_bigendian);
+  crc32_debug_file = get_crc32 (state, descriptor, error_callback, data);
+  return crc32_debug_link == crc32_debug_file;
+}
+
+/* Process elf header. Verify magic number, version, etc, of the ELF
+   header. Populate ehdr_out, names_view_out and shdrs_view_out. Caller
+   should release view of the names_view_out and shdrs_view_out. Return
+   ELF_TYPE_EXEC if the file type is EXE, ELF_TYPE_DYN if type of the
+   file is DYN and ELF_TYPE_INVALID on the fail.  */
+
+static enum type_of_elf
+process_elf_header (struct backtrace_state *state, int descriptor,
+		    backtrace_error_callback error_callback, void *data,
+		    int exe, b_elf_ehdr *ehdr_out,
+		    struct backtrace_view *shdrs_view_out,
+		    struct backtrace_view *names_view_out)
+{
+  struct backtrace_view ehdr_view;
+  const b_elf_shdr *shstrhdr;
+  const b_elf_shdr *shdrs;
+  unsigned int shstrndx;
+  unsigned int shnum;
+  off_t shoff;
+  int shdrs_view_valid;
+
+  shdrs_view_valid = 0;
+
+  if (!backtrace_get_view (state, descriptor, 0, sizeof *ehdr_out,
+			   error_callback, data, &ehdr_view))
+    goto fail;
+
+  memcpy (ehdr_out, ehdr_view.data, sizeof *ehdr_out);
+
+  backtrace_release_view (state, &ehdr_view, error_callback, data);
+
+  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, "processed file is not ELF", 0);
+      goto fail;
+    }
+  if (ehdr_out->e_ident[EI_VERSION] != EV_CURRENT)
+    {
+      error_callback (data, "processed file is unrecognized ELF version", 0);
+      goto fail;
+    }
+
+#if BACKTRACE_ELF_SIZE == 32
+#define BACKTRACE_ELFCLASS ELFCLASS32
+#else
+#define BACKTRACE_ELFCLASS ELFCLASS64
+#endif
+
+  if (ehdr_out->e_ident[EI_CLASS] != BACKTRACE_ELFCLASS)
+    {
+      error_callback (data, "processed file is unexpected ELF class", 0);
+      goto fail;
+    }
+
+  if (ehdr_out->e_ident[EI_DATA] != ELFDATA2LSB
+      && ehdr_out->e_ident[EI_DATA] != ELFDATA2MSB)
+    {
+      error_callback (data, "processed file has unknown endianness", 0);
+      goto fail;
+    }
+
+  /* If the executable is ET_DYN, it is either a PIE, or we are running
+     directly a shared library with .interp.  We need to wait for
+     dl_iterate_phdr in that case to determine the actual base_address.  */
+  if (exe && ehdr_out->e_type == ET_DYN)
+    return ELF_TYPE_DYN;
+
+  shoff = ehdr_out->e_shoff;
+  shnum = ehdr_out->e_shnum;
+  shstrndx = ehdr_out->e_shstrndx;
+
+  if ((shnum == 0 || shstrndx == SHN_XINDEX) && shoff != 0)
+    {
+      struct backtrace_view shdr_view;
+      const b_elf_shdr *shdr;
+
+      if (!backtrace_get_view (state, descriptor, shoff, sizeof (b_elf_shdr),
+			       error_callback, data, &shdr_view))
+	goto fail;
+
+      shdr = (const b_elf_shdr *) shdr_view.data;
+
+      if (shnum == 0)
+	shnum = shdr->sh_size;
+
+      if (shstrndx == SHN_XINDEX)
+	{
+	  shstrndx = shdr->sh_link;
+
+	  /* Versions of the GNU binutils between 2.12 and 2.18 did
+	     not handle objects with more than SHN_LORESERVE sections
+	     correctly.  All large section indexes were offset by
+	     0x100.  There is more information at
+	     http://sourceware.org/bugzilla/show_bug.cgi?id-5900 .
+	     Fortunately these object files are easy to detect, as the
+	     GNU binutils always put the section header string table
+	     near the end of the list of sections.  Thus if the
+	     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;
+	}
+      backtrace_release_view (state, &shdr_view, error_callback, data);
+    }
+
+  /* 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_out))
+    goto fail;
+
+  shdrs_view_valid = 1;
+
+  shdrs = (const b_elf_shdr *) shdrs_view_out->data;
+  shstrhdr = &shdrs[shstrndx - 1];
+
+  /* Read the section names.  */
+
+  if (!backtrace_get_view (state, descriptor, shstrhdr->sh_offset,
+			   shstrhdr->sh_size, error_callback, data,
+			   names_view_out))
+    goto fail;
+
+  ehdr_out->e_shoff = shoff;
+  ehdr_out->e_shnum = shnum;
+  ehdr_out->e_shstrndx = shstrndx;
+
+  return ELF_TYPE_EXEC;
+
+ fail:
+  /* In case fail happens with backtrace_get_view for names section.  */
+  if (shdrs_view_valid)
+    backtrace_release_view (state, shdrs_view_out, error_callback, data);
+  return ELF_TYPE_INVALID;
+}
+
+/* Get content of the specifying section.  */
+
+static unsigned char *
+elf_get_section_by_name (struct backtrace_state *state, int descriptor,
+			 backtrace_error_callback error_callback, void *data,
+			 size_t *section_data_len_out, const char *section_name,
+			 b_elf_ehdr *ehdr, struct backtrace_view *shdrs_view,
+			 struct backtrace_view *names_view)
+{
+  const b_elf_shdr *shdrs;
+  const b_elf_shdr *shstrhdr;
+  size_t shstr_size;
+  struct backtrace_view section_view;
+  const char *names;
+  unsigned int i;
+  unsigned int shnum;
+  unsigned int shstrndx;
+  int section_view_valid;
+  unsigned char *section_data;
+
+  section_view_valid = 0;
+  section_data = NULL;
+
+  shnum = ehdr->e_shnum;
+  shstrndx = ehdr->e_shstrndx;
+  shdrs = (const b_elf_shdr *) shdrs_view->data;
+  shstrhdr = &shdrs[shstrndx - 1];
+  shstr_size = shstrhdr->sh_size;
+
+  names = (const char *) names_view->data;
+
+  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, section_name) == 0)
+	{
+	  if (backtrace_get_view (state, descriptor, shdr->sh_offset,
+				  shdr->sh_size, error_callback, data,
+				  &section_view))
+	    {
+	      section_view_valid = 1;
+	      section_data
+		= backtrace_alloc (state, shdr->sh_size, error_callback, data);
+	      if (section_data == NULL)
+		goto exit;
+	      memcpy (section_data, section_view.data, shdr->sh_size);
+	      *section_data_len_out = shdr->sh_size;
+	    }
+	  break;
+	}
+    }
+
+ exit:
+  if (section_view_valid)
+    backtrace_release_view (state, &section_view, error_callback, data);
+  return section_data;
+}
+
+/* Verify type of the file.  */
+
+static enum type_of_file
+backtrace_readlink (const char *filename,
+		    backtrace_error_callback error_callback, void *data)
+{
+  struct stat link_stat;
+  enum type_of_file file_type;
+  mode_t mode;
+
+  memset (&link_stat, 0, sizeof (struct stat));
+
+  if (lstat (filename, &link_stat) == -1)
+    {
+      if (errno != ENOENT)
+	error_callback (data, filename, errno);
+      file_type = FILE_TYPE_INVALID;
+    }
+
+  mode = link_stat.st_mode & S_IFMT;
+
+  switch (mode)
+    {
+    case S_IFLNK:
+      file_type = FILE_TYPE_LINK;
+      break;
+    case S_IFREG:
+      file_type = FILE_TYPE_REGULAR;
+      break;
+    default:
+      file_type = FILE_TYPE_INVALID;
+    }
+  return file_type;
+}
+
+/* Resolve full name of the link. In this case we can't use realpath function
+   because it could be undefined on some platfroms, also it allocates memory
+   by malloc, which we can't use.  */
+
+static ssize_t
+resolve_realname (const char *filename, char *buffer,
+		  struct backtrace_state *state,
+		  backtrace_error_callback error_callback, void *data)
+{
+  char *temp_buffer;
+  enum type_of_file file_type;
+  ssize_t filename_len;
+  size_t temp_filename_len;
+  size_t base_len;
+  int valid_temp_buffer;
+
+  valid_temp_buffer = 0;
+  filename_len = -1;
+  file_type = FILE_TYPE_LINK;
+
+  /* Allocate memory for sizeof(PATH_MAX) + 1 bytes because at this time
+     we don't know how long path could be.  */
+  temp_buffer = backtrace_alloc (state, PATH_MAX + 1, error_callback, data);
+  if (temp_buffer == NULL)
+    return -1;
+
+  valid_temp_buffer = 1;
+
+  memset (temp_buffer, 0, PATH_MAX + 1);
+  memcpy (temp_buffer, filename, strlen (filename));
+
+  while (file_type == FILE_TYPE_LINK)
+    {
+      filename_len = readlink (temp_buffer, buffer, PATH_MAX);
+      if (filename_len < 1)
+	goto exit;
+
+      temp_filename_len = strlen (buffer);
+
+      /* Full path.  */
+      if (IS_ABSOLUTE_PATH (buffer))
+	{
+	  memset (temp_buffer, 0, PATH_MAX);
+	  memcpy (temp_buffer, buffer, temp_filename_len);
+	}
+      else
+	{
+	  /* Relative path.  */
+	  base_len = base_name_len (temp_buffer);
+	  if (!base_len || (base_len + filename_len > PATH_MAX))
+	    {
+	      filename_len = -1;
+	      goto exit;
+	    }
+	  memcpy (temp_buffer + base_len, buffer, filename_len);
+	  temp_buffer[base_len + filename_len] = '\0';
+	}
+
+      file_type = backtrace_readlink (temp_buffer, error_callback, data);
+      memset (buffer, 0, filename_len);
+    }
+
+  if (file_type != FILE_TYPE_REGULAR)
+    {
+      filename_len = -1;
+      goto exit;
+    }
+
+  filename_len = strlen (temp_buffer);
+  memcpy (buffer, temp_buffer, filename_len);
+
+ exit:
+  if (valid_temp_buffer)
+    backtrace_free (state, temp_buffer, PATH_MAX + 1, error_callback, data);
+  return filename_len;
+}
+
+/* Resolve realname of the filename. This function verifies filename.
+   If filename is name of the file it populates realname buffer.
+   If filename is link, it calls resolve_realname function.  */
+
+static unsigned int
+backtrace_resolve_realname (const char *filename, char *realname,
+			    struct backtrace_state *state,
+			    backtrace_error_callback error_callback, void *data)
+{
+  enum type_of_file file_type;
+  ssize_t filename_len;
+
+  filename_len = -1;
+
+  file_type = backtrace_readlink (filename, error_callback, data);
+
+  if (file_type == FILE_TYPE_LINK)
+    {
+      /* Read the actual filename.  */
+      filename_len
+	= resolve_realname (filename, realname, state, error_callback, data);
+      if (filename_len < 1)
+	return 0;
+    }
+  else if (file_type == FILE_TYPE_REGULAR)
+    {
+      filename_len = strlen (filename);
+      if (filename_len > PATH_MAX)
+	return 0;
+      memcpy (realname, filename, filename_len);
+    }
+  /* FILE_TYPE_INVALID.  */
+  else
+    return 0;
+
+  return 1;
+}
+
+/* Search for debug file into specifying directorires.  */
+
+static int
+search_for_debugfile (char *realname, char *debug_filename,
+		      backtrace_error_callback error_callback, void *data,
+		      struct backtrace_state *state)
+{
+  size_t debug_filename_len;
+  size_t pass;
+  size_t debug_path_len;
+  size_t buffer_len;
+  size_t base_len;
+  int debug_does_not_exist;
+  int debug_descriptor;
+  int valid_buffer;
+  char *buffer;
+
+  debug_descriptor = -1;
+  valid_buffer = 0;
+
+  base_len = base_name_len (realname);
+
+  if (!base_len)
+    return debug_descriptor;
+
+  debug_filename_len = strlen ((const char *) debug_filename);
+
+  if (!debug_filename_len)
+    return debug_descriptor;
+
+  buffer_len = base_len + strlen (debug_file_path[DEBUG_PATH_USR_LIB_DEBUG])
+	       + debug_filename_len + 1;
+
+  buffer = backtrace_alloc (state, buffer_len, error_callback, data);
+
+  if (buffer == NULL)
+    return debug_descriptor;
+
+  valid_buffer = 1;
+
+  memset (buffer, 0, buffer_len);
+  memcpy (buffer, realname, base_len);
+
+  for (pass = 0; pass < DEBUG_PATH_MAX; ++pass)
+    {
+      switch (pass)
+	{
+	case DEBUG_PATH_CURRENT:
+	  {
+	    memcpy (buffer + base_len, debug_filename, debug_filename_len);
+	    break;
+	  }
+	case DEBUG_PATH_CURRENT_DEBUG:
+	  {
+	    debug_path_len = strlen (debug_file_path[DEBUG_PATH_CURRENT_DEBUG]);
+	    memcpy (buffer + base_len,
+		    debug_file_path[DEBUG_PATH_CURRENT_DEBUG], debug_path_len);
+	    memcpy (buffer + base_len + debug_path_len, debug_filename,
+		    debug_filename_len);
+	    break;
+	  }
+	case DEBUG_PATH_USR_LIB_DEBUG:
+	  {
+	    debug_path_len = strlen (debug_file_path[DEBUG_PATH_USR_LIB_DEBUG]);
+	    memset (buffer, 0, buffer_len);
+	    memcpy (buffer, debug_file_path[DEBUG_PATH_USR_LIB_DEBUG],
+		    debug_path_len);
+	    memcpy (buffer + debug_path_len, debug_filename,
+		    debug_filename_len);
+	    break;
+	  }
+	case DEBUG_PATH_USR_LIB_DEBUG_PATH_TO_EXE:
+	  {
+	    debug_path_len
+	      = strlen (debug_file_path[DEBUG_PATH_USR_LIB_DEBUG_PATH_TO_EXE]);
+	    memset (buffer, 0, buffer_len);
+	    memcpy (buffer,
+		    debug_file_path[DEBUG_PATH_USR_LIB_DEBUG_PATH_TO_EXE],
+		    debug_path_len);
+	    memcpy (buffer + debug_path_len, realname, base_len);
+	    memcpy (buffer + debug_path_len + base_len, debug_filename,
+		    debug_filename_len);
+	    break;
+	  }
+	default:
+	  goto exit;
+	}
+
+      debug_descriptor
+	= backtrace_open (buffer, error_callback, data, &debug_does_not_exist);
+
+      if (debug_descriptor >= 0)
+	break;
+    }
+
+ exit:
+  if (valid_buffer)
+    backtrace_free (state, buffer, buffer_len, error_callback, data);
+  return debug_descriptor;
+}
+
+/* Open debug file by gnulink.  */
+
+static int
+open_debugfile_by_gnulink (char *realname, unsigned char *section_data,
+			   size_t section_data_len, int is_bigendian,
+			   struct backtrace_state *state,
+			   backtrace_error_callback error_callback, void *data)
+{
+  int debug_descriptor;
+
+  debug_descriptor = search_for_debugfile (realname, (char *) section_data,
+					   error_callback, data, state);
+  if (debug_descriptor < 0)
+    return -1;
+
+  /* Check the crc32 checksum if it is not the same return -1.  */
+
+  if (!check_sum (state, debug_descriptor, error_callback, data,
+		  (const unsigned char *) section_data,
+		  strlen ((char *) section_data), section_data_len,
+		  is_bigendian))
+    {
+      /* If crc32 sums are different, just close the descriptor
+	 associated with debuginfo file.  */
+      backtrace_close (debug_descriptor, error_callback, data);
+      debug_descriptor = -1;
+    }
+
+  return debug_descriptor;
+}
+
+/* Convert char to hex */
+
+static char
+hex (char ch)
+{
+  return ch > 9 ? ('a' + (ch - 10)) : ('0' + ch);
+}
+
+/* Get build-id name.  */
+
+static char *
+get_build_id_name (unsigned char *section_data, size_t *len,
+		   int is_bigendian, struct backtrace_state *state,
+		   backtrace_error_callback error_callback, void *data)
+{
+  Elf_External_Note *build_id_section;
+  char *build_id_name;
+  char *temp;
+  const char *debug_postfix;
+  const char *debug_prefix;
+  size_t debug_postfix_len;
+  size_t debug_prefix_len;
+  size_t name_size;
+  size_t offset;
+  unsigned char *hash_start;
+  uint32_t hash_size;
+  uint32_t identifier;
+
+  debug_postfix_len = 6;
+  debug_prefix_len = 10;
+  debug_postfix = ".debug";
+  debug_prefix = ".build-id/";
+
+  build_id_section = (Elf_External_Note *) section_data;
+  hash_size = get_uint32 (build_id_section->descsz, is_bigendian);
+  identifier = get_uint32 (build_id_section->type, is_bigendian);
+  name_size = get_uint32 (build_id_section->namesz, is_bigendian);
+
+  if (identifier != NT_GNU_BUILD_ID || hash_size == 0 || name_size != 4
+      || strncmp ((char *) build_id_section->name, "GNU", 3) != 0)
+    return NULL;
+
+  offset = 16;
+  hash_start = section_data + offset;
+  *len = hash_size * 2 + debug_postfix_len + debug_prefix_len + 1;
+  build_id_name = backtrace_alloc (state, *len, error_callback, data);
+
+  if (build_id_name == NULL)
+    {
+      *len = 0;
+      return NULL;
+    }
+
+  memset (build_id_name, 0, *len);
+  memcpy (build_id_name, debug_prefix, debug_prefix_len);
+  temp = build_id_name + debug_prefix_len;
+
+  *temp++ = hex ((*hash_start & 0xF0) >> 4);
+  *temp++ = hex (*hash_start & 0x0F);
+  ++hash_start;
+  --hash_size;
+  *temp++ = DIR_SEPARATOR;
+
+  while (hash_size--)
+    {
+      *temp++ = hex ((*hash_start & 0xF0) >> 4);
+      *temp++ = hex (*hash_start & 0x0F);
+      ++hash_start;
+    }
+
+  memcpy (temp, debug_postfix, debug_postfix_len);
+  return build_id_name;
+}
+
+/* Open file by build-id.  */
+
+static int
+open_debugfile_by_build_id (char *realname, unsigned char *section_data,
+			    int is_bigendian, struct backtrace_state *state,
+			    backtrace_error_callback error_callback, void *data)
+
+{
+  char *build_id_name;
+  int debug_descriptor;
+  size_t build_id_name_len;
+  int valid_build_id_name;
+
+  debug_descriptor = -1;
+  valid_build_id_name = 0;
+  build_id_name_len = 0;
+
+  build_id_name = get_build_id_name (section_data, &build_id_name_len,
+				     is_bigendian, state, error_callback, data);
+
+  if (!build_id_name_len)
+    return -1;
+
+  valid_build_id_name = 1;
+
+  debug_descriptor = search_for_debugfile (realname, build_id_name,
+					   error_callback, data, state);
+
+  if (valid_build_id_name)
+    backtrace_free (state, build_id_name, build_id_name_len, error_callback,
+		    data);
+  return debug_descriptor;
+}
+
+/* Open debug file.  */
+
+static int
+backtrace_open_debugfile (int descriptor, const char *filename,
+			  backtrace_error_callback error_callback, void *data,
+			  struct backtrace_state *state)
+{
+  int debug_descriptor;
+  unsigned char *gnulink_section_data;
+  unsigned char *build_id_section_data;
+  int valid_descriptor;
+  int valid_gnulink_section_data;
+  int valid_build_id_section_data;
+  int valid_realname;
+  int valid_elf_header;
+  size_t build_id_section_data_len;
+  size_t gnu_link_section_data_len;
+  char *realname;
+  b_elf_ehdr ehdr;
+  struct backtrace_view shdrs_view;
+  struct backtrace_view names_view;
+
+  valid_elf_header = 0;
+  valid_realname = 0;
+  valid_descriptor = 0;
+  valid_gnulink_section_data = 0;
+  valid_build_id_section_data = 0;
+  build_id_section_data_len = 0;
+  gnu_link_section_data_len = 0;
+  debug_descriptor = -1;
+
+  if (!process_elf_header (state, descriptor, error_callback, data, 0, &ehdr,
+			   &shdrs_view, &names_view))
+    return -1;
+
+  valid_elf_header = 1;
+
+  realname = backtrace_alloc (state, PATH_MAX + 1, error_callback, data);
+
+  if (realname == NULL)
+    goto exit;
+
+  /* Indicates that we successfully allocated memory.  */
+  valid_realname = 1;
+
+  /* Populate the buffer with realname.  */
+  if (!backtrace_resolve_realname (filename, realname, state, error_callback,
+				   data))
+    goto exit;
+
+  /* Check if build-id section does exist.  */
+  build_id_section_data
+    = elf_get_section_by_name (state, descriptor, error_callback, data,
+			       &build_id_section_data_len, ".note.gnu.build-id",
+			       &ehdr, &shdrs_view, &names_view);
+
+  if (build_id_section_data_len)
+    {
+      valid_build_id_section_data = 1;
+      debug_descriptor
+	= open_debugfile_by_build_id (realname, build_id_section_data,
+				      ehdr.e_ident[EI_DATA] == ELFDATA2MSB,
+				      state, error_callback, data);
+    }
+
+  if (debug_descriptor < 0)
+    {
+      gnulink_section_data
+	= elf_get_section_by_name (state, descriptor, error_callback, data,
+				   &gnu_link_section_data_len, ".gnu_debuglink",
+				   &ehdr, &shdrs_view, &names_view);
+
+      if (gnu_link_section_data_len)
+	{
+	  valid_gnulink_section_data = 1;
+	  debug_descriptor
+	    = open_debugfile_by_gnulink (realname, gnulink_section_data,
+					 gnu_link_section_data_len,
+					 ehdr.e_ident[EI_DATA] == ELFDATA2MSB,
+					 state, error_callback, data);
+	}
+    }
+
+  if (debug_descriptor >= 0)
+    valid_descriptor = 1;
+
+ exit:
+  if (valid_descriptor)
+    backtrace_close (descriptor, error_callback, data);
+  if (valid_gnulink_section_data)
+    backtrace_free (state, gnulink_section_data, gnu_link_section_data_len,
+		    error_callback, data);
+  if (valid_build_id_section_data)
+    backtrace_free (state, build_id_section_data, build_id_section_data_len,
+		    error_callback, data);
+  if (valid_realname)
+    backtrace_free (state, realname, PATH_MAX + 1, error_callback, data);
+  if (valid_elf_header)
+    {
+      backtrace_release_view (state, &names_view, error_callback, data);
+      backtrace_release_view (state, &shdrs_view, error_callback, data);
+    }
+  return debug_descriptor;
+}
+
 /* A dummy callback function used when we can't find any debug info.  */
 
 static int
@@ -521,9 +1389,7 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
 	 backtrace_error_callback error_callback, void *data,
 	 fileline *fileline_fn, int *found_sym, int *found_dwarf, int exe)
 {
-  struct backtrace_view ehdr_view;
   b_elf_ehdr ehdr;
-  off_t shoff;
   unsigned int shnum;
   unsigned int shstrndx;
   struct backtrace_view shdrs_view;
@@ -531,7 +1397,6 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
   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;
@@ -547,6 +1412,7 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
   off_t max_offset;
   struct backtrace_view debug_view;
   int debug_view_valid;
+  enum type_of_elf elf_type;
 
   *found_sym = 0;
   *found_dwarf = 0;
@@ -557,116 +1423,32 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
   strtab_view_valid = 0;
   debug_view_valid = 0;
 
-  if (!backtrace_get_view (state, descriptor, 0, sizeof ehdr, error_callback,
-			   data, &ehdr_view))
-    goto fail;
-
-  memcpy (&ehdr, ehdr_view.data, sizeof ehdr);
-
-  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)
-    {
-      error_callback (data, "executable file is not ELF", 0);
-      goto fail;
-    }
-  if (ehdr.e_ident[EI_VERSION] != EV_CURRENT)
-    {
-      error_callback (data, "executable file is unrecognized ELF version", 0);
-      goto fail;
-    }
-
-#if BACKTRACE_ELF_SIZE == 32
-#define BACKTRACE_ELFCLASS ELFCLASS32
-#else
-#define BACKTRACE_ELFCLASS ELFCLASS64
-#endif
-
-  if (ehdr.e_ident[EI_CLASS] != BACKTRACE_ELFCLASS)
+  elf_type = process_elf_header (state, descriptor, error_callback, data, exe,
+				 &ehdr, &shdrs_view, &names_view);
+  switch (elf_type)
     {
-      error_callback (data, "executable file is unexpected ELF class", 0);
+    /* Binary compiled with PIE option.  */
+    case ELF_TYPE_DYN:
+      return -1;
+    case ELF_TYPE_EXEC:
+      break;
+    /* Header is invalid.  */
+    case ELF_TYPE_INVALID:
       goto fail;
     }
 
-  if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB
-      && ehdr.e_ident[EI_DATA] != ELFDATA2MSB)
-    {
-      error_callback (data, "executable file has unknown endianness", 0);
-      goto fail;
-    }
-
-  /* If the executable is ET_DYN, it is either a PIE, or we are running
-     directly a shared library with .interp.  We need to wait for
-     dl_iterate_phdr in that case to determine the actual base_address.  */
-  if (exe && ehdr.e_type == ET_DYN)
-    return -1;
-
-  shoff = ehdr.e_shoff;
-  shnum = ehdr.e_shnum;
-  shstrndx = ehdr.e_shstrndx;
-
-  if ((shnum == 0 || shstrndx == SHN_XINDEX)
-      && shoff != 0)
-    {
-      struct backtrace_view shdr_view;
-      const b_elf_shdr *shdr;
-
-      if (!backtrace_get_view (state, descriptor, shoff, 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 (shstrndx == SHN_XINDEX)
-	{
-	  shstrndx = shdr->sh_link;
-
-	  /* Versions of the GNU binutils between 2.12 and 2.18 did
-	     not handle objects with more than SHN_LORESERVE sections
-	     correctly.  All large section indexes were offset by
-	     0x100.  There is more information at
-	     http://sourceware.org/bugzilla/show_bug.cgi?id-5900 .
-	     Fortunately these object files are easy to detect, as the
-	     GNU binutils always put the section header string table
-	     near the end of the list of sections.  Thus if the
-	     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;
-	}
-
-      backtrace_release_view (state, &shdr_view, error_callback, data);
-    }
+  shdrs_view_valid = 1;
+  names_view_valid = 1;
 
   /* To translate PC to file/line when using DWARF, we need to find
      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))
-    goto fail;
-  shdrs_view_valid = 1;
   shdrs = (const b_elf_shdr *) shdrs_view.data;
 
-  /* Read the section names.  */
-
+  shnum = ehdr.e_shnum;
+  shstrndx = ehdr.e_shstrndx;
   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 fail;
-  names_view_valid = 1;
   names = (const char *) names_view.data;
 
   symtab_shndx = 0;
@@ -877,6 +1659,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 debug_descriptor;
 
   /* 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
@@ -895,11 +1678,19 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED,
 	  backtrace_close (pd->exe_descriptor, pd->error_callback, pd->data);
 	  pd->exe_descriptor = -1;
 	}
+      debug_descriptor = -1;
 
       descriptor = backtrace_open (info->dlpi_name, pd->error_callback,
 				   pd->data, &does_not_exist);
+
       if (descriptor < 0)
 	return 0;
+
+      debug_descriptor
+	= backtrace_open_debugfile (descriptor, info->dlpi_name,
+				    pd->error_callback, pd->data, pd->state);
+      if (debug_descriptor >= 0)
+	descriptor = debug_descriptor;
     }
 
   if (elf_add (pd->state, descriptor, info->dlpi_addr, pd->error_callback,
@@ -915,6 +1706,57 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED,
   return 0;
 }
 
+/* On succcess returns pathname of the executable. On fail returns NULL.  */
+static const char *
+get_exec_filename (struct backtrace_state *state,
+		   backtrace_error_callback error_callback, void *data)
+{
+  int descriptor;
+  const char *filename;
+  unsigned int pass;
+
+  descriptor = -1;
+  filename = NULL;
+
+  for (pass = 0; pass < 4; ++pass)
+    {
+      int does_not_exist;
+
+      switch (pass)
+	{
+	case 0:
+	  filename = state->filename;
+	  break;
+	case 1:
+	  filename = getexecname ();
+	  break;
+	case 2:
+	  filename = "/proc/self/exe";
+	  break;
+	case 3:
+	  filename = "/proc/curproc/file";
+	  break;
+	default:
+	  filename = NULL;
+	  break;
+	}
+
+      if (filename == NULL)
+	continue;
+
+      descriptor
+	= backtrace_open (filename, error_callback, data, &does_not_exist);
+
+      if (descriptor >= 0)
+	break;
+    }
+
+  if (descriptor >= 0)
+    backtrace_close (descriptor, error_callback, data);
+
+  return filename;
+}
+
 /* 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.  */
@@ -929,6 +1771,23 @@ backtrace_initialize (struct backtrace_state *state, int descriptor,
   int found_dwarf;
   fileline elf_fileline_fn = elf_nodebug;
   struct phdr_data pd;
+  int debug_descriptor;
+  const char *filename;
+
+  debug_descriptor = -1;
+
+  /* In case state->filename is not initialized, we should resolve the
+     executable name, because that name is needed to find the path to
+     the file with debug info. So to avoid changing API for
+     backtrace_initalize we can do it with some overhead by iterating
+     through all possible names and trying to open the file.  */
+  filename = get_exec_filename (state, error_callback, data);
+
+  if (filename != NULL)
+    debug_descriptor = backtrace_open_debugfile (descriptor, filename,
+						 error_callback, data, state);
+  if (debug_descriptor >= 0)
+    descriptor = debug_descriptor;
 
   ret = elf_add (state, descriptor, 0, error_callback, data, &elf_fileline_fn,
 		 &found_sym, &found_dwarf, 1);
-- 
1.9.1


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-07-29 20:42     ` Denis Khalikov
@ 2017-09-10 21:11       ` Ian Lance Taylor via gcc-patches
  2017-09-11 10:06         ` Denis Khalikov
  2017-09-20 21:09         ` Ian Lance Taylor via gcc-patches
  0 siblings, 2 replies; 28+ messages in thread
From: Ian Lance Taylor via gcc-patches @ 2017-09-10 21:11 UTC (permalink / raw)
  To: Denis Khalikov; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 770 bytes --]

On Sat, Jul 29, 2017 at 1:42 PM, Denis Khalikov
<d.khalikov@partner.samsung.com> wrote:
>
> Hello Ian,
> thanks for review.
> I've updated the patch, can you please take a look.

Apologies again for the length of time it took to reply.  I've had a
hard time understanding the patch.  It's quite likely that I don't
understand how it works, but it seems to pass the same file descriptor
to process_elf_header twice.  It seems to look for debug files with
the buildid in places where they will not be found.  It seems to work
out the file name a second time, even though the file name must
already be known.  I eventually just wrote my own implementation.

Could you try this patch and see if it works for your cases?  The
patch is against current mainline.  Thanks.

Ian

[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 32458 bytes --]

Index: Makefile.am
===================================================================
--- Makefile.am	(revision 251948)
+++ Makefile.am	(working copy)
@@ -122,6 +122,16 @@ ttest_LDADD = libbacktrace.la
 
 endif HAVE_PTHREAD
 
+if HAVE_OBJCOPY_DEBUGLINK
+
+TESTS += dtest
+
+dtest: btest
+	$(OBJCOPY) --only-keep-debug btest btest.debug
+	$(OBJCOPY) --strip-debug --add-gnu-debuglink=btest.debug btest dtest
+
+endif HAVE_OBJCOPY_DEBUGLINK
+
 endif NATIVE
 
 # We can't use automake's automatic dependency tracking, because it
Index: Makefile.in
===================================================================
--- Makefile.in	(revision 251948)
+++ Makefile.in	(working copy)
@@ -86,6 +86,7 @@ target_triplet = @target@
 check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2)
 @NATIVE_TRUE@am__append_1 = btest stest edtest
 @HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am__append_2 = ttest
+@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_3 = dtest
 subdir = .
 DIST_COMMON = README ChangeLog $(srcdir)/Makefile.in \
 	$(srcdir)/Makefile.am $(top_srcdir)/configure \
@@ -217,6 +218,7 @@ MAKEINFO = @MAKEINFO@
 MKDIR_P = @MKDIR_P@
 NM = @NM@
 NMEDIT = @NMEDIT@
+OBJCOPY = @OBJCOPY@
 OBJDUMP = @OBJDUMP@
 OBJEXT = @OBJEXT@
 OTOOL = @OTOOL@
@@ -343,7 +345,7 @@ libbacktrace_la_LIBADD = \
 	$(ALLOC_FILE)
 
 libbacktrace_la_DEPENDENCIES = $(libbacktrace_la_LIBADD)
-TESTS = $(check_PROGRAMS)
+TESTS = $(check_PROGRAMS) $(am__append_3)
 @NATIVE_TRUE@btest_SOURCES = btest.c testlib.c
 @NATIVE_TRUE@btest_CFLAGS = $(AM_CFLAGS) -g -O
 @NATIVE_TRUE@btest_LDADD = libbacktrace.la
@@ -799,6 +801,10 @@ uninstall-am:
 @NATIVE_TRUE@	cat $(srcdir)/edtest2.c > tmp-edtest2_build.c
 @NATIVE_TRUE@	$(SHELL) $(srcdir)/../move-if-change tmp-edtest2_build.c edtest2_build.c
 @NATIVE_TRUE@	echo timestamp > $@
+
+@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@dtest: btest
+@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@	$(OBJCOPY) --only-keep-debug btest btest.debug
+@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@	$(OBJCOPY) --strip-debug --add-gnu-debuglink=btest.debug btest dtest
 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
Index: configure
===================================================================
--- configure	(revision 251948)
+++ configure	(working copy)
@@ -604,6 +604,9 @@ LTLIBOBJS
 LIBOBJS
 NATIVE_FALSE
 NATIVE_TRUE
+HAVE_OBJCOPY_DEBUGLINK_FALSE
+HAVE_OBJCOPY_DEBUGLINK_TRUE
+OBJCOPY
 HAVE_PTHREAD_FALSE
 HAVE_PTHREAD_TRUE
 PTHREAD_CFLAGS
@@ -746,7 +749,8 @@ CFLAGS
 LDFLAGS
 LIBS
 CPPFLAGS
-CPP'
+CPP
+OBJCOPY'
 
 
 # Initialize some variables set by options.
@@ -1396,6 +1400,7 @@ Some influential environment variables:
   CPPFLAGS    C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
               you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
+  OBJCOPY     location of objcopy
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -11136,7 +11141,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11139 "configure"
+#line 11144 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11242,7 +11247,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11245 "configure"
+#line 11250 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12761,6 +12766,69 @@ 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
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether objcopy supports debuglink" >&5
+$as_echo_n "checking whether objcopy supports debuglink... " >&6; }
+if test "${libbacktrace_cv_objcopy_debuglink+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "${with_target_subdir}"; then
+  libbacktrace_cv_objcopy_debuglink=no
+elif ${OBJCOPY} --add-gnu-debuglink=x /bin/ls /tmp/ls$$; then
+  rm -f /tmp/ls$$
+  libbacktrace_cv_objcopy_debuglink=yes
+else
+  libbacktrace_cv_objcopy_debuglink=no
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libbacktrace_cv_objcopy_debuglink" >&5
+$as_echo "$libbacktrace_cv_objcopy_debuglink" >&6; }
+ if test "$libbacktrace_cv_objcopy_debuglink" = yes; then
+  HAVE_OBJCOPY_DEBUGLINK_TRUE=
+  HAVE_OBJCOPY_DEBUGLINK_FALSE='#'
+else
+  HAVE_OBJCOPY_DEBUGLINK_TRUE='#'
+  HAVE_OBJCOPY_DEBUGLINK_FALSE=
+fi
+
+
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether tests can run" >&5
 $as_echo_n "checking whether tests can run... " >&6; }
 if test "${libbacktrace_cv_sys_native+set}" = set; then :
@@ -12927,6 +12995,10 @@ if test -z "${HAVE_PTHREAD_TRUE}" && tes
   as_fn_error "conditional \"HAVE_PTHREAD\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${HAVE_OBJCOPY_DEBUGLINK_TRUE}" && test -z "${HAVE_OBJCOPY_DEBUGLINK_FALSE}"; then
+  as_fn_error "conditional \"HAVE_OBJCOPY_DEBUGLINK\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${NATIVE_TRUE}" && test -z "${NATIVE_FALSE}"; then
   as_fn_error "conditional \"NATIVE\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
Index: configure.ac
===================================================================
--- configure.ac	(revision 251948)
+++ configure.ac	(working copy)
@@ -404,6 +404,20 @@ AC_SUBST(PTHREAD_CFLAGS)
 
 AM_CONDITIONAL(HAVE_PTHREAD, test "$libgo_cv_lib_pthread" = yes)
 
+AC_ARG_VAR(OBJCOPY, [location of objcopy])
+AC_CHECK_PROG(OBJCOPY, objcopy, objcopy,)
+AC_CACHE_CHECK([whether objcopy supports debuglink],
+[libbacktrace_cv_objcopy_debuglink],
+[if test -n "${with_target_subdir}"; then
+  libbacktrace_cv_objcopy_debuglink=no
+elif ${OBJCOPY} --add-gnu-debuglink=x /bin/ls /tmp/ls$$; then
+  rm -f /tmp/ls$$
+  libbacktrace_cv_objcopy_debuglink=yes
+else
+  libbacktrace_cv_objcopy_debuglink=no
+fi])
+AM_CONDITIONAL(HAVE_OBJCOPY_DEBUGLINK, test "$libbacktrace_cv_objcopy_debuglink" = yes)
+
 AC_CACHE_CHECK([whether tests can run],
   [libbacktrace_cv_sys_native],
   [AC_RUN_IFELSE([AC_LANG_PROGRAM([], [return 0;])],
Index: elf.c
===================================================================
--- elf.c	(revision 251948)
+++ elf.c	(working copy)
@@ -32,9 +32,12 @@ POSSIBILITY OF SUCH DAMAGE.  */
 
 #include "config.h"
 
+#include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
 #ifdef HAVE_DL_ITERATE_PHDR
 #include <link.h>
@@ -43,6 +46,35 @@ POSSIBILITY OF SUCH DAMAGE.  */
 #include "backtrace.h"
 #include "internal.h"
 
+#ifndef S_ISLNK
+ #ifndef S_IFLNK
+  #define S_IFLNK 0120000
+ #endif
+ #ifndef S_IFMT
+  #define S_IFMT 0170000
+ #endif
+ #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+
+#if !defined(HAVE_DECL_STRNLEN) || !HAVE_DECL_STRNLEN
+
+/* If strnlen is not declared, provide our own version.  */
+
+static size_t
+xstrnlen (const char *s, size_t maxlen)
+{
+  size_t i;
+
+  for (i = 0; i < maxlen; ++i)
+    if (s[i] == '\0')
+      break;
+  return i;
+}
+
+#define strnlen xstrnlen
+
+#endif
+
 #ifndef HAVE_DL_ITERATE_PHDR
 
 /* Dummy version of dl_iterate_phdr for systems that don't have it.  */
@@ -105,6 +137,7 @@ dl_iterate_phdr (int (*callback) (struct
 #undef SHT_DYNSYM
 #undef STT_OBJECT
 #undef STT_FUNC
+#undef NT_GNU_BUILD_ID
 
 /* Basic types.  */
 
@@ -224,6 +257,16 @@ typedef struct
 #define STT_OBJECT 1
 #define STT_FUNC 2
 
+typedef struct
+{
+  uint32_t namesz;
+  uint32_t descsz;
+  uint32_t type;
+  char name[1];
+} b_elf_note;
+
+#define NT_GNU_BUILD_ID 3
+
 /* An index of ELF sections we care about.  */
 
 enum debug_section
@@ -283,6 +326,102 @@ struct elf_syminfo_data
   size_t count;
 };
 
+/* Compute the CRC-32 of BUF/LEN.  This uses the CRC used for
+   .gnu_debuglink files.  */
+
+static uint32_t
+elf_crc32 (uint32_t crc, const unsigned char *buf, size_t len)
+{
+  static const uint32_t 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;
+  for (end = buf + len; buf < end; ++ buf)
+    crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
+  return ~crc;
+}
+
+/* Return the CRC-32 of the entire file open at DESCRIPTOR.  */
+
+static uint32_t
+elf_crc32_file (struct backtrace_state *state, int descriptor,
+		backtrace_error_callback error_callback, void *data)
+{
+  struct stat st;
+  struct backtrace_view file_view;
+  uint32_t ret;
+
+  if (fstat (descriptor, &st) < 0)
+    {
+      error_callback (data, "fstat", errno);
+      return 0;
+    }
+
+  if (!backtrace_get_view (state, descriptor, 0, st.st_size, error_callback,
+			   data, &file_view))
+    return 0;
+
+  ret = elf_crc32 (0, (const unsigned char *) file_view.data, st.st_size);
+
+  backtrace_release_view (state, &file_view, error_callback, data);
+
+  return ret;
+}
+
 /* A dummy callback function used when we can't find any debug info.  */
 
 static int
@@ -510,6 +649,293 @@ elf_syminfo (struct backtrace_state *sta
     callback (data, addr, sym->name, sym->address, sym->size);
 }
 
+/* Return whether FILENAME is a symlink.  */
+
+static int
+elf_is_symlink (const char *filename)
+{
+  struct stat st;
+
+  if (lstat (filename, &st) < 0)
+    return 0;
+  return S_ISLNK (st.st_mode);
+}
+
+/* Return the results of reading the symlink FILENAME in a buffer
+   allocated by backtrace_alloc.  Return the length of the buffer in
+   *LEN.  */
+
+static char *
+elf_readlink (struct backtrace_state *state, const char *filename,
+	      backtrace_error_callback error_callback, void *data,
+	      size_t *plen)
+{
+  size_t len;
+  char *buf;
+
+  len = 128;
+  while (1)
+    {
+      ssize_t rl;
+
+      buf = backtrace_alloc (state, len, error_callback, data);
+      if (buf == NULL)
+	return NULL;
+      rl = readlink (filename, buf, len);
+      if (rl < 0)
+	{
+	  backtrace_free (state, buf, len, error_callback, data);
+	  return NULL;
+	}
+      if ((size_t) rl < len - 1)
+	{
+	  buf[rl] = '\0';
+	  *plen = len;
+	  return buf;
+	}
+      backtrace_free (state, buf, len, error_callback, data);
+      len *= 2;
+    }
+}
+
+/* Open a separate debug info file, using the build ID to find it.
+   Returns an open file descriptor, or -1.
+
+   The GDB manual says that the only place gdb looks for a debug file
+   when the build ID is known is in /usr/lib/debug/.build-id.  */
+
+static int
+elf_open_debugfile_by_buildid (struct backtrace_state *state,
+			       const char *buildid_data, size_t buildid_size,
+			       backtrace_error_callback error_callback,
+			       void *data)
+{
+  const char * const prefix = "/usr/lib/debug/.build-id/";
+  const size_t prefix_len = strlen (prefix);
+  const char * const suffix = ".debug";
+  const size_t suffix_len = strlen (suffix);
+  size_t len;
+  char *bd_filename;
+  char *t;
+  size_t i;
+  int ret;
+  int does_not_exist;
+
+  len = prefix_len + buildid_size * 2 + suffix_len + 2;
+  bd_filename = backtrace_alloc (state, len, error_callback, data);
+  if (bd_filename == NULL)
+    return -1;
+
+  t = bd_filename;
+  memcpy (t, prefix, prefix_len);
+  t += prefix_len;
+  for (i = 0; i < buildid_size; i++)
+    {
+      unsigned char b;
+      unsigned char nib;
+
+      b = (unsigned char) buildid_data[i];
+      nib = (b & 0xf0) >> 4;
+      *t++ = nib < 10 ? '0' + nib : 'a' + nib - 10;
+      nib = b & 0x0f;
+      *t++ = nib < 10 ? '0' + nib : 'a' + nib - 10;
+      if (i == 0)
+	*t++ = '/';
+    }
+  memcpy (t, suffix, suffix_len);
+  t[suffix_len] = '\0';
+
+  ret = backtrace_open (bd_filename, error_callback, data, &does_not_exist);
+
+  backtrace_free (state, bd_filename, len, error_callback, data);
+
+  /* gdb checks that the debuginfo file has the same build ID note.
+     That seems kind of pointless to me--why would it have the right
+     name but not the right build ID?--so skipping the check.  */
+
+  return ret;
+}
+
+/* Try to open a file whose name is PREFIX (length PREFIX_LEN)
+   concatenated with PREFIX2 (length PREFIX2_LEN) concatenated with
+   DEBUGLINK_NAME.  Returns an open file descriptor, or -1.  */
+
+static int
+elf_try_debugfile (struct backtrace_state *state, const char *prefix,
+		   size_t prefix_len, const char *prefix2, size_t prefix2_len,
+		   const char *debuglink_name,
+		   backtrace_error_callback error_callback, void *data)
+{
+  size_t debuglink_len;
+  size_t try_len;
+  char *try;
+  int does_not_exist;
+  int ret;
+
+  debuglink_len = strlen (debuglink_name);
+  try_len = prefix_len + prefix2_len + debuglink_len + 1;
+  try = backtrace_alloc (state, try_len, error_callback, data);
+  if (try == NULL)
+    return -1;
+
+  memcpy (try, prefix, prefix_len);
+  memcpy (try + prefix_len, prefix2, prefix2_len);
+  memcpy (try + prefix_len + prefix2_len, debuglink_name, debuglink_len);
+  try[prefix_len + prefix2_len + debuglink_len] = '\0';
+
+  ret = backtrace_open (try, error_callback, data, &does_not_exist);
+
+  backtrace_free (state, try, try_len, error_callback, data);
+
+  return ret;
+}
+
+/* Find a separate debug info file, using the debuglink section data
+   to find it.  Returns an open file descriptor, or -1.  */
+
+static int
+elf_find_debugfile_by_debuglink (struct backtrace_state *state,
+				 const char *filename,
+				 const char *debuglink_name,
+				 backtrace_error_callback error_callback,
+				 void *data)
+{
+  int ret;
+  char *alc;
+  size_t alc_len;
+  const char *slash;
+  int ddescriptor;
+  const char *prefix;
+  size_t prefix_len;
+
+  /* Resolve symlinks in FILENAME.  Since FILENAME is fairly likely to
+     be /proc/self/exe, symlinks are common.  We don't try to resolve
+     the whole path name, just the base name.  */
+  ret = -1;
+  alc = NULL;
+  alc_len = 0;
+  while (elf_is_symlink (filename))
+    {
+      char *new_buf;
+      size_t new_len;
+
+      new_buf = elf_readlink (state, filename, error_callback, data, &new_len);
+      if (new_buf == NULL)
+	break;
+
+      if (new_buf[0] == '/')
+	filename = new_buf;
+      else
+	{
+	  slash = strrchr (filename, '/');
+	  if (slash == NULL)
+	    filename = new_buf;
+	  else
+	    {
+	      size_t clen;
+	      char *c;
+
+	      slash++;
+	      clen = slash - filename + strlen (new_buf) + 1;
+	      c = backtrace_alloc (state, clen, error_callback, data);
+	      if (c == NULL)
+		goto done;
+
+	      memcpy (c, filename, slash - filename);
+	      memcpy (c + (slash - filename), new_buf, strlen (new_buf));
+	      c[slash - filename + strlen (new_buf)] = '\0';
+	      backtrace_free (state, new_buf, new_len, error_callback, data);
+	      filename = c;
+	      new_buf = c;
+	      new_len = clen;
+	    }
+	}
+
+      if (alc != NULL)
+	backtrace_free (state, alc, alc_len, error_callback, data);
+      alc = new_buf;
+      alc_len = new_len;
+    }
+
+  /* Look for DEBUGLINK_NAME in the same directory as FILENAME.  */
+
+  slash = strrchr (filename, '/');
+  if (slash == NULL)
+    {
+      prefix = "";
+      prefix_len = 0;
+    }
+  else
+    {
+      slash++;
+      prefix = filename;
+      prefix_len = slash - filename;
+    }
+
+  ddescriptor = elf_try_debugfile (state, prefix, prefix_len, "", 0,
+				   debuglink_name, error_callback, data);
+  if (ddescriptor >= 0)
+    {
+      ret = ddescriptor;
+      goto done;
+    }
+
+  /* Look for DEBUGLINK_NAME in a .debug subdirectory of FILENAME.  */
+
+  ddescriptor = elf_try_debugfile (state, prefix, prefix_len, ".debug/",
+				   strlen (".debug/"), debuglink_name,
+				   error_callback, data);
+  if (ddescriptor >= 0)
+    {
+      ret = ddescriptor;
+      goto done;
+    }
+
+  /* Look for DEBUGLINK_NAME in /usr/lib/debug.  */
+
+  ddescriptor = elf_try_debugfile (state, "/usr/lib/debug/",
+				   strlen ("/usr/lib/debug/"), prefix,
+				   prefix_len, debuglink_name,
+				   error_callback, data);
+  if (ddescriptor >= 0)
+    ret = ddescriptor;
+
+ done:
+  if (alc != NULL && alc_len > 0)
+    backtrace_free (state, alc, alc_len, error_callback, data);
+  return ret;
+}
+
+/* Open a separate debug info file, using the debuglink section data
+   to find it.  Returns an open file descriptor, or -1.  */
+
+static int
+elf_open_debugfile_by_debuglink (struct backtrace_state *state,
+				 const char *filename,
+				 const char *debuglink_name,
+				 uint32_t debuglink_crc,
+				 backtrace_error_callback error_callback,
+				 void *data)
+{
+  int ddescriptor;
+  uint32_t got_crc;
+
+  ddescriptor = elf_find_debugfile_by_debuglink (state, filename,
+						 debuglink_name,
+						 error_callback, data);
+  if (ddescriptor < 0)
+    return -1;
+
+  got_crc = elf_crc32_file (state, ddescriptor, error_callback, data);
+  if (got_crc != debuglink_crc)
+    {
+      backtrace_close (ddescriptor, error_callback, data);
+      return -1;
+    }
+
+  return ddescriptor;
+}
+
 /* 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
@@ -517,9 +943,10 @@ elf_syminfo (struct backtrace_state *sta
    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)
+elf_add (struct backtrace_state *state, const char *filename, int descriptor,
+	 uintptr_t base_address, backtrace_error_callback error_callback,
+	 void *data, fileline *fileline_fn, int *found_sym, int *found_dwarf,
+	 int exe, int debuginfo)
 {
   struct backtrace_view ehdr_view;
   b_elf_ehdr ehdr;
@@ -543,6 +970,14 @@ elf_add (struct backtrace_state *state,
   int symtab_view_valid;
   struct backtrace_view strtab_view;
   int strtab_view_valid;
+  struct backtrace_view buildid_view;
+  int buildid_view_valid;
+  const char *buildid_data;
+  uint32_t buildid_size;
+  struct backtrace_view debuglink_view;
+  int debuglink_view_valid;
+  const char *debuglink_name;
+  uint32_t debuglink_crc;
   off_t min_offset;
   off_t max_offset;
   struct backtrace_view debug_view;
@@ -555,6 +990,12 @@ elf_add (struct backtrace_state *state,
   names_view_valid = 0;
   symtab_view_valid = 0;
   strtab_view_valid = 0;
+  buildid_view_valid = 0;
+  buildid_data = NULL;
+  buildid_size = 0;
+  debuglink_view_valid = 0;
+  debuglink_name = NULL;
+  debuglink_crc = 0;
   debug_view_valid = 0;
 
   if (!backtrace_get_view (state, descriptor, 0, sizeof ehdr, error_callback,
@@ -707,11 +1148,62 @@ elf_add (struct backtrace_state *state,
 	      break;
 	    }
 	}
+
+
+      /* Read the build ID if present.  This could check for any
+	 SHT_NOTE section with the right note name and type, but gdb
+	 looks for a specific section name.  */
+      if (!debuginfo
+	  && !buildid_view_valid
+	  && strcmp (name, ".note.gnu.build-id") == 0)
+	{
+	  const b_elf_note *note;
+
+	  if (!backtrace_get_view (state, descriptor, shdr->sh_offset,
+				   shdr->sh_size, error_callback, data,
+				   &buildid_view))
+	    goto fail;
+
+	  buildid_view_valid = 1;
+	  note = (const b_elf_note *) buildid_view.data;
+	  if (note->type == NT_GNU_BUILD_ID
+	      && note->namesz == 4
+	      && strncmp (note->name, "GNU", 4) == 0
+	      && shdr->sh_size < 12 + ((note->namesz + 3) & ~ 3) + note->descsz)
+	    {
+	      buildid_data = &note->name[0] + ((note->namesz + 3) & ~ 3);
+	      buildid_size = note->descsz;
+	    }
+	}
+
+      /* Read the debuglink file if present.  */
+      if (!debuginfo
+	  && !debuglink_view_valid
+	  && strcmp (name, ".gnu_debuglink") == 0)
+	{
+	  const char *debuglink_data;
+	  size_t crc_offset;
+
+	  if (!backtrace_get_view (state, descriptor, shdr->sh_offset,
+				   shdr->sh_size, error_callback, data,
+				   &debuglink_view))
+	    goto fail;
+
+	  debuglink_view_valid = 1;
+	  debuglink_data = (const char *) debuglink_view.data;
+	  crc_offset = strnlen (debuglink_data, shdr->sh_size);
+	  crc_offset = (crc_offset + 3) & ~3;
+	  if (crc_offset + 4 <= shdr->sh_size)
+	    {
+	      debuglink_name = debuglink_data;
+	      debuglink_crc = *(const uint32_t*)(debuglink_data + crc_offset);
+	    }
+	}
     }
 
   if (symtab_shndx == 0)
     symtab_shndx = dynsym_shndx;
-  if (symtab_shndx != 0)
+  if (symtab_shndx != 0 && !debuginfo)
     {
       const b_elf_shdr *symtab_shdr;
       unsigned int strtab_shndx;
@@ -757,6 +1249,7 @@ elf_add (struct backtrace_state *state,
       /* We no longer need the symbol table, but we hold on to the
 	 string table permanently.  */
       backtrace_release_view (state, &symtab_view, error_callback, data);
+      symtab_view_valid = 0;
 
       *found_sym = 1;
 
@@ -770,6 +1263,53 @@ elf_add (struct backtrace_state *state,
   backtrace_release_view (state, &names_view, error_callback, data);
   names_view_valid = 0;
 
+  /* If the debug info is in a separate file, read that one instead.  */
+
+  if (buildid_data != NULL)
+    {
+      int d;
+
+      d = elf_open_debugfile_by_buildid (state, buildid_data, buildid_size,
+					 error_callback, data);
+      if (d >= 0)
+	{
+	  backtrace_release_view (state, &buildid_view, error_callback, data);
+	  if (debuglink_view_valid)
+	    backtrace_release_view (state, &debuglink_view, error_callback,
+				    data);
+	  return elf_add (state, NULL, d, base_address, error_callback, data,
+			  fileline_fn, found_sym, found_dwarf, 0, 1);
+	}
+    }
+
+  if (buildid_view_valid)
+    {
+      backtrace_release_view (state, &buildid_view, error_callback, data);
+      buildid_view_valid = 0;
+    }
+
+  if (debuglink_name != NULL)
+    {
+      int d;
+
+      d = elf_open_debugfile_by_debuglink (state, filename, debuglink_name,
+					   debuglink_crc, error_callback,
+					   data);
+      if (d >= 0)
+	{
+	  backtrace_release_view (state, &debuglink_view, error_callback,
+				  data);
+	  return elf_add (state, NULL, d, base_address, error_callback, data,
+			  fileline_fn, found_sym, found_dwarf, 0, 1);
+	}
+    }
+
+  if (debuglink_view_valid)
+    {
+      backtrace_release_view (state, &debuglink_view, error_callback, data);
+      debuglink_view_valid = 0;
+    }
+
   /* Read all the debug sections in a single view, since they are
      probably adjacent in the file.  We never release this view.  */
 
@@ -842,6 +1382,10 @@ elf_add (struct backtrace_state *state,
     backtrace_release_view (state, &symtab_view, error_callback, data);
   if (strtab_view_valid)
     backtrace_release_view (state, &strtab_view, error_callback, data);
+  if (debuglink_view_valid)
+    backtrace_release_view (state, &debuglink_view, error_callback, data);
+  if (buildid_view_valid)
+    backtrace_release_view (state, &buildid_view, error_callback, data);
   if (debug_view_valid)
     backtrace_release_view (state, &debug_view, error_callback, data);
   if (descriptor != -1)
@@ -859,6 +1403,7 @@ struct phdr_data
   fileline *fileline_fn;
   int *found_sym;
   int *found_dwarf;
+  const char *exe_filename;
   int exe_descriptor;
 };
 
@@ -873,6 +1418,7 @@ phdr_callback (struct dl_phdr_info *info
 	       void *pdata)
 {
   struct phdr_data *pd = (struct phdr_data *) pdata;
+  const char *filename;
   int descriptor;
   int does_not_exist;
   fileline elf_fileline_fn;
@@ -885,6 +1431,7 @@ phdr_callback (struct dl_phdr_info *info
     {
       if (pd->exe_descriptor == -1)
 	return 0;
+      filename = pd->exe_filename;
       descriptor = pd->exe_descriptor;
       pd->exe_descriptor = -1;
     }
@@ -896,14 +1443,16 @@ phdr_callback (struct dl_phdr_info *info
 	  pd->exe_descriptor = -1;
 	}
 
+      filename = info->dlpi_name;
       descriptor = backtrace_open (info->dlpi_name, pd->error_callback,
 				   pd->data, &does_not_exist);
       if (descriptor < 0)
 	return 0;
     }
 
-  if (elf_add (pd->state, descriptor, info->dlpi_addr, pd->error_callback,
-	       pd->data, &elf_fileline_fn, pd->found_sym, &found_dwarf, 0))
+  if (elf_add (pd->state, filename, descriptor, info->dlpi_addr,
+	       pd->error_callback, pd->data, &elf_fileline_fn, pd->found_sym,
+	       &found_dwarf, 0, 0))
     {
       if (found_dwarf)
 	{
@@ -920,8 +1469,8 @@ phdr_callback (struct dl_phdr_info *info
    sections.  */
 
 int
-backtrace_initialize (struct backtrace_state *state, int descriptor,
-		      backtrace_error_callback error_callback,
+backtrace_initialize (struct backtrace_state *state, const char *filename,
+		      int descriptor, backtrace_error_callback error_callback,
 		      void *data, fileline *fileline_fn)
 {
   int ret;
@@ -930,8 +1479,8 @@ backtrace_initialize (struct backtrace_s
   fileline elf_fileline_fn = elf_nodebug;
   struct phdr_data pd;
 
-  ret = elf_add (state, descriptor, 0, error_callback, data, &elf_fileline_fn,
-		 &found_sym, &found_dwarf, 1);
+  ret = elf_add (state, filename, descriptor, 0, error_callback, data,
+		 &elf_fileline_fn, &found_sym, &found_dwarf, 1, 0);
   if (!ret)
     return 0;
 
Index: fileline.c
===================================================================
--- fileline.c	(revision 251948)
+++ fileline.c	(working copy)
@@ -58,6 +58,7 @@ fileline_initialize (struct backtrace_st
   int pass;
   int called_error_callback;
   int descriptor;
+  const char *filename;
   char buf[64];
 
   if (!state->threaded)
@@ -84,7 +85,6 @@ fileline_initialize (struct backtrace_st
   called_error_callback = 0;
   for (pass = 0; pass < 5; ++pass)
     {
-      const char *filename;
       int does_not_exist;
 
       switch (pass)
@@ -140,8 +140,8 @@ fileline_initialize (struct backtrace_st
 
   if (!failed)
     {
-      if (!backtrace_initialize (state, descriptor, error_callback, data,
-				 &fileline_fn))
+      if (!backtrace_initialize (state, filename, descriptor, error_callback,
+				 data, &fileline_fn))
 	failed = 1;
     }
 
Index: internal.h
===================================================================
--- internal.h	(revision 251948)
+++ internal.h	(working copy)
@@ -268,6 +268,7 @@ extern int backtrace_vector_release (str
    appropriate one.  */
 
 extern int backtrace_initialize (struct backtrace_state *state,
+				 const char *filename,
 				 int descriptor,
 				 backtrace_error_callback error_callback,
 				 void *data,
Index: pecoff.c
===================================================================
--- pecoff.c	(revision 251948)
+++ pecoff.c	(working copy)
@@ -890,7 +890,8 @@ coff_add (struct backtrace_state *state,
    sections.  */
 
 int
-backtrace_initialize (struct backtrace_state *state, int descriptor,
+backtrace_initialize (struct backtrace_state *state,
+		      const char *filename ATTRIBUTE_UNUSED, int descriptor,
 		      backtrace_error_callback error_callback,
 		      void *data, fileline *fileline_fn)
 {
Index: unknown.c
===================================================================
--- unknown.c	(revision 251948)
+++ unknown.c	(working copy)
@@ -54,6 +54,7 @@ unknown_fileline (struct backtrace_state
 
 int
 backtrace_initialize (struct backtrace_state *state ATTRIBUTE_UNUSED,
+		      const char *filename ATTRIBUTE_UNUSED,
 		      int descriptor ATTRIBUTE_UNUSED,
 		      backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
 		      void *data ATTRIBUTE_UNUSED, fileline *fileline_fn)
Index: xcoff.c
===================================================================
--- xcoff.c	(revision 251948)
+++ xcoff.c	(working copy)
@@ -1434,7 +1434,8 @@ xcoff_add_shared_libs (struct backtrace_
    Returns 1 on success, 0 on failure.  */
 
 int
-backtrace_initialize (struct backtrace_state *state, int descriptor,
+backtrace_initialize (struct backtrace_state *state,
+		      const char *filename ATTRIBUTE_UNUSED, int descriptor,
 		      backtrace_error_callback error_callback,
 		      void *data, fileline *fileline_fn)
 {

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-09-10 21:11       ` Ian Lance Taylor via gcc-patches
@ 2017-09-11 10:06         ` Denis Khalikov
  2017-09-12 14:06           ` Ian Lance Taylor via gcc-patches
  2017-09-20 21:09         ` Ian Lance Taylor via gcc-patches
  1 sibling, 1 reply; 28+ messages in thread
From: Denis Khalikov @ 2017-09-11 10:06 UTC (permalink / raw)
  To: Ian Lance Taylor; +Cc: GCC Patches

Thanks for answer.
I understood all points which you mentioned, but can't
find last one
 > It seems to work
 > out the file name a second time, even though the file name must
 > already be known.

Can you please show me where I've missed that, if you have a time for that.

Anyway, your patch works for me.
Thanks.

On 09/11/2017 12:11 AM, Ian Lance Taylor wrote:
> On Sat, Jul 29, 2017 at 1:42 PM, Denis Khalikov
> <d.khalikov@partner.samsung.com> wrote:
>>
>> Hello Ian,
>> thanks for review.
>> I've updated the patch, can you please take a look.
>
> Apologies again for the length of time it took to reply.  I've had a
> hard time understanding the patch.  It's quite likely that I don't
> understand how it works, but it seems to pass the same file descriptor
> to process_elf_header twice.  It seems to look for debug files with
> the buildid in places where they will not be found.  It seems to work
> out the file name a second time, even though the file name must
> already be known.  I eventually just wrote my own implementation.
>
> Could you try this patch and see if it works for your cases?  The
> patch is against current mainline.  Thanks.
>
> Ian
>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-09-11 10:06         ` Denis Khalikov
@ 2017-09-12 14:06           ` Ian Lance Taylor via gcc-patches
  2017-09-20  8:54             ` Maxim Ostapenko
  0 siblings, 1 reply; 28+ messages in thread
From: Ian Lance Taylor via gcc-patches @ 2017-09-12 14:06 UTC (permalink / raw)
  To: Denis Khalikov; +Cc: GCC Patches

On Mon, Sep 11, 2017 at 3:06 AM, Denis Khalikov
<d.khalikov@partner.samsung.com> wrote:
> Thanks for answer.
> I understood all points which you mentioned, but can't
> find last one
>> It seems to work
>> out the file name a second time, even though the file name must
>> already be known.
>
> Can you please show me where I've missed that, if you have a time for that.

It's quite possible that I misunderstand the patch.  I was looking at
the call to a copy of get_exec_filename in fileline_initialize.

> Anyway, your patch works for me.

Thanks for testing.  I will commit it later, probably today.

Ian


> On 09/11/2017 12:11 AM, Ian Lance Taylor wrote:
>>
>> On Sat, Jul 29, 2017 at 1:42 PM, Denis Khalikov
>> <d.khalikov@partner.samsung.com> wrote:
>>>
>>>
>>> Hello Ian,
>>> thanks for review.
>>> I've updated the patch, can you please take a look.
>>
>>
>> Apologies again for the length of time it took to reply.  I've had a
>> hard time understanding the patch.  It's quite likely that I don't
>> understand how it works, but it seems to pass the same file descriptor
>> to process_elf_header twice.  It seems to look for debug files with
>> the buildid in places where they will not be found.  It seems to work
>> out the file name a second time, even though the file name must
>> already be known.  I eventually just wrote my own implementation.
>>
>> Could you try this patch and see if it works for your cases?  The
>> patch is against current mainline.  Thanks.
>>
>> Ian
>>
>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-09-12 14:06           ` Ian Lance Taylor via gcc-patches
@ 2017-09-20  8:54             ` Maxim Ostapenko
  2017-09-20 12:33               ` Ian Lance Taylor via gcc-patches
  0 siblings, 1 reply; 28+ messages in thread
From: Maxim Ostapenko @ 2017-09-20  8:54 UTC (permalink / raw)
  To: Ian Lance Taylor, Denis Khalikov; +Cc: GCC Patches

Hi Ian,

On 12/09/17 17:05, Ian Lance Taylor via gcc-patches wrote:
> On Mon, Sep 11, 2017 at 3:06 AM, Denis Khalikov
> <d.khalikov@partner.samsung.com> wrote:
>> Thanks for answer.
>> I understood all points which you mentioned, but can't
>> find last one
>>> It seems to work
>>> out the file name a second time, even though the file name must
>>> already be known.
>> Can you please show me where I've missed that, if you have a time for that.
> It's quite possible that I misunderstand the patch.  I was looking at
> the call to a copy of get_exec_filename in fileline_initialize.
>
>> Anyway, your patch works for me.
> Thanks for testing.  I will commit it later, probably today.

As far as I can see, this patch wasn't committed to trunk so far. Did 
you meet some issues with current implementation? Do we need to perform 
more testing here?

Thanks,
-Maxim

>
> Ian
>
>
>> On 09/11/2017 12:11 AM, Ian Lance Taylor wrote:
>>> On Sat, Jul 29, 2017 at 1:42 PM, Denis Khalikov
>>> <d.khalikov@partner.samsung.com> wrote:
>>>>
>>>> Hello Ian,
>>>> thanks for review.
>>>> I've updated the patch, can you please take a look.
>>>
>>> Apologies again for the length of time it took to reply.  I've had a
>>> hard time understanding the patch.  It's quite likely that I don't
>>> understand how it works, but it seems to pass the same file descriptor
>>> to process_elf_header twice.  It seems to look for debug files with
>>> the buildid in places where they will not be found.  It seems to work
>>> out the file name a second time, even though the file name must
>>> already be known.  I eventually just wrote my own implementation.
>>>
>>> Could you try this patch and see if it works for your cases?  The
>>> patch is against current mainline.  Thanks.
>>>
>>> Ian
>>>
>
>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-09-20  8:54             ` Maxim Ostapenko
@ 2017-09-20 12:33               ` Ian Lance Taylor via gcc-patches
  0 siblings, 0 replies; 28+ messages in thread
From: Ian Lance Taylor via gcc-patches @ 2017-09-20 12:33 UTC (permalink / raw)
  To: Maxim Ostapenko; +Cc: Denis Khalikov, GCC Patches

On Wed, Sep 20, 2017 at 1:53 AM, Maxim Ostapenko
<m.ostapenko@samsung.com> wrote:
> Hi Ian,
>
> On 12/09/17 17:05, Ian Lance Taylor via gcc-patches wrote:
>>
>> On Mon, Sep 11, 2017 at 3:06 AM, Denis Khalikov
>> <d.khalikov@partner.samsung.com> wrote:
>>>
>>> Thanks for answer.
>>> I understood all points which you mentioned, but can't
>>> find last one
>>>>
>>>> It seems to work
>>>> out the file name a second time, even though the file name must
>>>> already be known.
>>>
>>> Can you please show me where I've missed that, if you have a time for
>>> that.
>>
>> It's quite possible that I misunderstand the patch.  I was looking at
>> the call to a copy of get_exec_filename in fileline_initialize.
>>
>>> Anyway, your patch works for me.
>>
>> Thanks for testing.  I will commit it later, probably today.
>
>
> As far as I can see, this patch wasn't committed to trunk so far. Did you
> meet some issues with current implementation? Do we need to perform more
> testing here?

No, I've just been busy.  Will get to it soon.

Ian

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-09-10 21:11       ` Ian Lance Taylor via gcc-patches
  2017-09-11 10:06         ` Denis Khalikov
@ 2017-09-20 21:09         ` Ian Lance Taylor via gcc-patches
  2017-10-02 11:12           ` Martin Liška
  1 sibling, 1 reply; 28+ messages in thread
From: Ian Lance Taylor via gcc-patches @ 2017-09-20 21:09 UTC (permalink / raw)
  To: Denis Khalikov; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 2338 bytes --]

On Sun, Sep 10, 2017 at 2:11 PM, Ian Lance Taylor <iant@google.com> wrote:
> On Sat, Jul 29, 2017 at 1:42 PM, Denis Khalikov
> <d.khalikov@partner.samsung.com> wrote:
>>
>> Hello Ian,
>> thanks for review.
>> I've updated the patch, can you please take a look.
>
> Apologies again for the length of time it took to reply.  I've had a
> hard time understanding the patch.  It's quite likely that I don't
> understand how it works, but it seems to pass the same file descriptor
> to process_elf_header twice.  It seems to look for debug files with
> the buildid in places where they will not be found.  It seems to work
> out the file name a second time, even though the file name must
> already be known.  I eventually just wrote my own implementation.
>
> Could you try this patch and see if it works for your cases?  The
> patch is against current mainline.  Thanks.

I have committed this patch as appended.  Bootstrapped and tested on
x86_64-pc-linux-gnu.

Ian

2017-09-20  Ian Lance Taylor  <iant@golang.org>
    Denis Khalikov  <d.khalikov@partner.samsung.com>

PR sanitizer/77631
Support for external debug info.
* elf.c: Include <errno.h>, <sys/stat.h>, <unistd.h>.
(S_ISLNK): Define if not defined.
(xstrnlen): Define if strnlen is not available.
(b_elf_note): Define type.
(NT_GNU_BUILD_ID): Define macro.
(elf_crc32, elf_crc32_file): New static functions.
(elf_is_symlink, elf_readlink): New static functions.
(elf_open_debugfile_by_buildid): New static function.
(elf_try_debugfile): New static function.
(elf_find_debugfile_by_debuglink): New static function.
(elf_open_debugfile_by_debuglink): New static function.
(elf_add): Add filename and debuginfo parameters.  Adjust all
callers.  Look for external debug info notes, and try to fetch
debug info from external file.
(struct phdr_data): Add exe_filename field.
(phdr_callback): Pass filename to elf_add.
(backtrace_initialize): Add filename parameter.
* internal.h (backtrace_initialize): Add filename parameter.
* fileline.c (fileline_initialize): Pass filename to
backtrace_initialize.
* pecoff.c (fileline_initialize): Add unused filename parameter.
* unknown.c (fileline_initialize): Likewise.
* xcoff.c (fileline_initialize): Likewise.
* configure.ac: Check for objcopy --add-gnu-debuglink.
* Makefile.am (dtest): New test target.
* configure, Makefile.in: Rebuild.

[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 26539 bytes --]

Index: Makefile.am
===================================================================
--- Makefile.am	(revision 253025)
+++ Makefile.am	(working copy)
@@ -122,6 +122,16 @@ ttest_LDADD = libbacktrace.la
 
 endif HAVE_PTHREAD
 
+if HAVE_OBJCOPY_DEBUGLINK
+
+TESTS += dtest
+
+dtest: btest
+	$(OBJCOPY) --only-keep-debug btest btest.debug
+	$(OBJCOPY) --strip-debug --add-gnu-debuglink=btest.debug btest dtest
+
+endif HAVE_OBJCOPY_DEBUGLINK
+
 endif NATIVE
 
 # We can't use automake's automatic dependency tracking, because it
Index: configure.ac
===================================================================
--- configure.ac	(revision 253025)
+++ configure.ac	(working copy)
@@ -404,6 +404,20 @@ AC_SUBST(PTHREAD_CFLAGS)
 
 AM_CONDITIONAL(HAVE_PTHREAD, test "$libgo_cv_lib_pthread" = yes)
 
+AC_ARG_VAR(OBJCOPY, [location of objcopy])
+AC_CHECK_PROG(OBJCOPY, objcopy, objcopy,)
+AC_CACHE_CHECK([whether objcopy supports debuglink],
+[libbacktrace_cv_objcopy_debuglink],
+[if test -n "${with_target_subdir}"; then
+  libbacktrace_cv_objcopy_debuglink=no
+elif ${OBJCOPY} --add-gnu-debuglink=x /bin/ls /tmp/ls$$; then
+  rm -f /tmp/ls$$
+  libbacktrace_cv_objcopy_debuglink=yes
+else
+  libbacktrace_cv_objcopy_debuglink=no
+fi])
+AM_CONDITIONAL(HAVE_OBJCOPY_DEBUGLINK, test "$libbacktrace_cv_objcopy_debuglink" = yes)
+
 AC_CACHE_CHECK([whether tests can run],
   [libbacktrace_cv_sys_native],
   [AC_RUN_IFELSE([AC_LANG_PROGRAM([], [return 0;])],
Index: elf.c
===================================================================
--- elf.c	(revision 253025)
+++ elf.c	(working copy)
@@ -32,9 +32,12 @@ POSSIBILITY OF SUCH DAMAGE.  */
 
 #include "config.h"
 
+#include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
 #ifdef HAVE_DL_ITERATE_PHDR
 #include <link.h>
@@ -43,6 +46,35 @@ POSSIBILITY OF SUCH DAMAGE.  */
 #include "backtrace.h"
 #include "internal.h"
 
+#ifndef S_ISLNK
+ #ifndef S_IFLNK
+  #define S_IFLNK 0120000
+ #endif
+ #ifndef S_IFMT
+  #define S_IFMT 0170000
+ #endif
+ #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+
+#if !defined(HAVE_DECL_STRNLEN) || !HAVE_DECL_STRNLEN
+
+/* If strnlen is not declared, provide our own version.  */
+
+static size_t
+xstrnlen (const char *s, size_t maxlen)
+{
+  size_t i;
+
+  for (i = 0; i < maxlen; ++i)
+    if (s[i] == '\0')
+      break;
+  return i;
+}
+
+#define strnlen xstrnlen
+
+#endif
+
 #ifndef HAVE_DL_ITERATE_PHDR
 
 /* Dummy version of dl_iterate_phdr for systems that don't have it.  */
@@ -105,6 +137,7 @@ dl_iterate_phdr (int (*callback) (struct
 #undef SHT_DYNSYM
 #undef STT_OBJECT
 #undef STT_FUNC
+#undef NT_GNU_BUILD_ID
 
 /* Basic types.  */
 
@@ -224,6 +257,16 @@ typedef struct
 #define STT_OBJECT 1
 #define STT_FUNC 2
 
+typedef struct
+{
+  uint32_t namesz;
+  uint32_t descsz;
+  uint32_t type;
+  char name[1];
+} b_elf_note;
+
+#define NT_GNU_BUILD_ID 3
+
 /* An index of ELF sections we care about.  */
 
 enum debug_section
@@ -283,6 +326,102 @@ struct elf_syminfo_data
   size_t count;
 };
 
+/* Compute the CRC-32 of BUF/LEN.  This uses the CRC used for
+   .gnu_debuglink files.  */
+
+static uint32_t
+elf_crc32 (uint32_t crc, const unsigned char *buf, size_t len)
+{
+  static const uint32_t 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;
+  for (end = buf + len; buf < end; ++ buf)
+    crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
+  return ~crc;
+}
+
+/* Return the CRC-32 of the entire file open at DESCRIPTOR.  */
+
+static uint32_t
+elf_crc32_file (struct backtrace_state *state, int descriptor,
+		backtrace_error_callback error_callback, void *data)
+{
+  struct stat st;
+  struct backtrace_view file_view;
+  uint32_t ret;
+
+  if (fstat (descriptor, &st) < 0)
+    {
+      error_callback (data, "fstat", errno);
+      return 0;
+    }
+
+  if (!backtrace_get_view (state, descriptor, 0, st.st_size, error_callback,
+			   data, &file_view))
+    return 0;
+
+  ret = elf_crc32 (0, (const unsigned char *) file_view.data, st.st_size);
+
+  backtrace_release_view (state, &file_view, error_callback, data);
+
+  return ret;
+}
+
 /* A dummy callback function used when we can't find any debug info.  */
 
 static int
@@ -510,6 +649,293 @@ elf_syminfo (struct backtrace_state *sta
     callback (data, addr, sym->name, sym->address, sym->size);
 }
 
+/* Return whether FILENAME is a symlink.  */
+
+static int
+elf_is_symlink (const char *filename)
+{
+  struct stat st;
+
+  if (lstat (filename, &st) < 0)
+    return 0;
+  return S_ISLNK (st.st_mode);
+}
+
+/* Return the results of reading the symlink FILENAME in a buffer
+   allocated by backtrace_alloc.  Return the length of the buffer in
+   *LEN.  */
+
+static char *
+elf_readlink (struct backtrace_state *state, const char *filename,
+	      backtrace_error_callback error_callback, void *data,
+	      size_t *plen)
+{
+  size_t len;
+  char *buf;
+
+  len = 128;
+  while (1)
+    {
+      ssize_t rl;
+
+      buf = backtrace_alloc (state, len, error_callback, data);
+      if (buf == NULL)
+	return NULL;
+      rl = readlink (filename, buf, len);
+      if (rl < 0)
+	{
+	  backtrace_free (state, buf, len, error_callback, data);
+	  return NULL;
+	}
+      if ((size_t) rl < len - 1)
+	{
+	  buf[rl] = '\0';
+	  *plen = len;
+	  return buf;
+	}
+      backtrace_free (state, buf, len, error_callback, data);
+      len *= 2;
+    }
+}
+
+/* Open a separate debug info file, using the build ID to find it.
+   Returns an open file descriptor, or -1.
+
+   The GDB manual says that the only place gdb looks for a debug file
+   when the build ID is known is in /usr/lib/debug/.build-id.  */
+
+static int
+elf_open_debugfile_by_buildid (struct backtrace_state *state,
+			       const char *buildid_data, size_t buildid_size,
+			       backtrace_error_callback error_callback,
+			       void *data)
+{
+  const char * const prefix = "/usr/lib/debug/.build-id/";
+  const size_t prefix_len = strlen (prefix);
+  const char * const suffix = ".debug";
+  const size_t suffix_len = strlen (suffix);
+  size_t len;
+  char *bd_filename;
+  char *t;
+  size_t i;
+  int ret;
+  int does_not_exist;
+
+  len = prefix_len + buildid_size * 2 + suffix_len + 2;
+  bd_filename = backtrace_alloc (state, len, error_callback, data);
+  if (bd_filename == NULL)
+    return -1;
+
+  t = bd_filename;
+  memcpy (t, prefix, prefix_len);
+  t += prefix_len;
+  for (i = 0; i < buildid_size; i++)
+    {
+      unsigned char b;
+      unsigned char nib;
+
+      b = (unsigned char) buildid_data[i];
+      nib = (b & 0xf0) >> 4;
+      *t++ = nib < 10 ? '0' + nib : 'a' + nib - 10;
+      nib = b & 0x0f;
+      *t++ = nib < 10 ? '0' + nib : 'a' + nib - 10;
+      if (i == 0)
+	*t++ = '/';
+    }
+  memcpy (t, suffix, suffix_len);
+  t[suffix_len] = '\0';
+
+  ret = backtrace_open (bd_filename, error_callback, data, &does_not_exist);
+
+  backtrace_free (state, bd_filename, len, error_callback, data);
+
+  /* gdb checks that the debuginfo file has the same build ID note.
+     That seems kind of pointless to me--why would it have the right
+     name but not the right build ID?--so skipping the check.  */
+
+  return ret;
+}
+
+/* Try to open a file whose name is PREFIX (length PREFIX_LEN)
+   concatenated with PREFIX2 (length PREFIX2_LEN) concatenated with
+   DEBUGLINK_NAME.  Returns an open file descriptor, or -1.  */
+
+static int
+elf_try_debugfile (struct backtrace_state *state, const char *prefix,
+		   size_t prefix_len, const char *prefix2, size_t prefix2_len,
+		   const char *debuglink_name,
+		   backtrace_error_callback error_callback, void *data)
+{
+  size_t debuglink_len;
+  size_t try_len;
+  char *try;
+  int does_not_exist;
+  int ret;
+
+  debuglink_len = strlen (debuglink_name);
+  try_len = prefix_len + prefix2_len + debuglink_len + 1;
+  try = backtrace_alloc (state, try_len, error_callback, data);
+  if (try == NULL)
+    return -1;
+
+  memcpy (try, prefix, prefix_len);
+  memcpy (try + prefix_len, prefix2, prefix2_len);
+  memcpy (try + prefix_len + prefix2_len, debuglink_name, debuglink_len);
+  try[prefix_len + prefix2_len + debuglink_len] = '\0';
+
+  ret = backtrace_open (try, error_callback, data, &does_not_exist);
+
+  backtrace_free (state, try, try_len, error_callback, data);
+
+  return ret;
+}
+
+/* Find a separate debug info file, using the debuglink section data
+   to find it.  Returns an open file descriptor, or -1.  */
+
+static int
+elf_find_debugfile_by_debuglink (struct backtrace_state *state,
+				 const char *filename,
+				 const char *debuglink_name,
+				 backtrace_error_callback error_callback,
+				 void *data)
+{
+  int ret;
+  char *alc;
+  size_t alc_len;
+  const char *slash;
+  int ddescriptor;
+  const char *prefix;
+  size_t prefix_len;
+
+  /* Resolve symlinks in FILENAME.  Since FILENAME is fairly likely to
+     be /proc/self/exe, symlinks are common.  We don't try to resolve
+     the whole path name, just the base name.  */
+  ret = -1;
+  alc = NULL;
+  alc_len = 0;
+  while (elf_is_symlink (filename))
+    {
+      char *new_buf;
+      size_t new_len;
+
+      new_buf = elf_readlink (state, filename, error_callback, data, &new_len);
+      if (new_buf == NULL)
+	break;
+
+      if (new_buf[0] == '/')
+	filename = new_buf;
+      else
+	{
+	  slash = strrchr (filename, '/');
+	  if (slash == NULL)
+	    filename = new_buf;
+	  else
+	    {
+	      size_t clen;
+	      char *c;
+
+	      slash++;
+	      clen = slash - filename + strlen (new_buf) + 1;
+	      c = backtrace_alloc (state, clen, error_callback, data);
+	      if (c == NULL)
+		goto done;
+
+	      memcpy (c, filename, slash - filename);
+	      memcpy (c + (slash - filename), new_buf, strlen (new_buf));
+	      c[slash - filename + strlen (new_buf)] = '\0';
+	      backtrace_free (state, new_buf, new_len, error_callback, data);
+	      filename = c;
+	      new_buf = c;
+	      new_len = clen;
+	    }
+	}
+
+      if (alc != NULL)
+	backtrace_free (state, alc, alc_len, error_callback, data);
+      alc = new_buf;
+      alc_len = new_len;
+    }
+
+  /* Look for DEBUGLINK_NAME in the same directory as FILENAME.  */
+
+  slash = strrchr (filename, '/');
+  if (slash == NULL)
+    {
+      prefix = "";
+      prefix_len = 0;
+    }
+  else
+    {
+      slash++;
+      prefix = filename;
+      prefix_len = slash - filename;
+    }
+
+  ddescriptor = elf_try_debugfile (state, prefix, prefix_len, "", 0,
+				   debuglink_name, error_callback, data);
+  if (ddescriptor >= 0)
+    {
+      ret = ddescriptor;
+      goto done;
+    }
+
+  /* Look for DEBUGLINK_NAME in a .debug subdirectory of FILENAME.  */
+
+  ddescriptor = elf_try_debugfile (state, prefix, prefix_len, ".debug/",
+				   strlen (".debug/"), debuglink_name,
+				   error_callback, data);
+  if (ddescriptor >= 0)
+    {
+      ret = ddescriptor;
+      goto done;
+    }
+
+  /* Look for DEBUGLINK_NAME in /usr/lib/debug.  */
+
+  ddescriptor = elf_try_debugfile (state, "/usr/lib/debug/",
+				   strlen ("/usr/lib/debug/"), prefix,
+				   prefix_len, debuglink_name,
+				   error_callback, data);
+  if (ddescriptor >= 0)
+    ret = ddescriptor;
+
+ done:
+  if (alc != NULL && alc_len > 0)
+    backtrace_free (state, alc, alc_len, error_callback, data);
+  return ret;
+}
+
+/* Open a separate debug info file, using the debuglink section data
+   to find it.  Returns an open file descriptor, or -1.  */
+
+static int
+elf_open_debugfile_by_debuglink (struct backtrace_state *state,
+				 const char *filename,
+				 const char *debuglink_name,
+				 uint32_t debuglink_crc,
+				 backtrace_error_callback error_callback,
+				 void *data)
+{
+  int ddescriptor;
+  uint32_t got_crc;
+
+  ddescriptor = elf_find_debugfile_by_debuglink (state, filename,
+						 debuglink_name,
+						 error_callback, data);
+  if (ddescriptor < 0)
+    return -1;
+
+  got_crc = elf_crc32_file (state, ddescriptor, error_callback, data);
+  if (got_crc != debuglink_crc)
+    {
+      backtrace_close (ddescriptor, error_callback, data);
+      return -1;
+    }
+
+  return ddescriptor;
+}
+
 /* 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
@@ -517,9 +943,10 @@ elf_syminfo (struct backtrace_state *sta
    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)
+elf_add (struct backtrace_state *state, const char *filename, int descriptor,
+	 uintptr_t base_address, backtrace_error_callback error_callback,
+	 void *data, fileline *fileline_fn, int *found_sym, int *found_dwarf,
+	 int exe, int debuginfo)
 {
   struct backtrace_view ehdr_view;
   b_elf_ehdr ehdr;
@@ -543,6 +970,14 @@ elf_add (struct backtrace_state *state,
   int symtab_view_valid;
   struct backtrace_view strtab_view;
   int strtab_view_valid;
+  struct backtrace_view buildid_view;
+  int buildid_view_valid;
+  const char *buildid_data;
+  uint32_t buildid_size;
+  struct backtrace_view debuglink_view;
+  int debuglink_view_valid;
+  const char *debuglink_name;
+  uint32_t debuglink_crc;
   off_t min_offset;
   off_t max_offset;
   struct backtrace_view debug_view;
@@ -555,6 +990,12 @@ elf_add (struct backtrace_state *state,
   names_view_valid = 0;
   symtab_view_valid = 0;
   strtab_view_valid = 0;
+  buildid_view_valid = 0;
+  buildid_data = NULL;
+  buildid_size = 0;
+  debuglink_view_valid = 0;
+  debuglink_name = NULL;
+  debuglink_crc = 0;
   debug_view_valid = 0;
 
   if (!backtrace_get_view (state, descriptor, 0, sizeof ehdr, error_callback,
@@ -707,11 +1148,61 @@ elf_add (struct backtrace_state *state,
 	      break;
 	    }
 	}
+
+      /* Read the build ID if present.  This could check for any
+	 SHT_NOTE section with the right note name and type, but gdb
+	 looks for a specific section name.  */
+      if (!debuginfo
+	  && !buildid_view_valid
+	  && strcmp (name, ".note.gnu.build-id") == 0)
+	{
+	  const b_elf_note *note;
+
+	  if (!backtrace_get_view (state, descriptor, shdr->sh_offset,
+				   shdr->sh_size, error_callback, data,
+				   &buildid_view))
+	    goto fail;
+
+	  buildid_view_valid = 1;
+	  note = (const b_elf_note *) buildid_view.data;
+	  if (note->type == NT_GNU_BUILD_ID
+	      && note->namesz == 4
+	      && strncmp (note->name, "GNU", 4) == 0
+	      && shdr->sh_size < 12 + ((note->namesz + 3) & ~ 3) + note->descsz)
+	    {
+	      buildid_data = &note->name[0] + ((note->namesz + 3) & ~ 3);
+	      buildid_size = note->descsz;
+	    }
+	}
+
+      /* Read the debuglink file if present.  */
+      if (!debuginfo
+	  && !debuglink_view_valid
+	  && strcmp (name, ".gnu_debuglink") == 0)
+	{
+	  const char *debuglink_data;
+	  size_t crc_offset;
+
+	  if (!backtrace_get_view (state, descriptor, shdr->sh_offset,
+				   shdr->sh_size, error_callback, data,
+				   &debuglink_view))
+	    goto fail;
+
+	  debuglink_view_valid = 1;
+	  debuglink_data = (const char *) debuglink_view.data;
+	  crc_offset = strnlen (debuglink_data, shdr->sh_size);
+	  crc_offset = (crc_offset + 3) & ~3;
+	  if (crc_offset + 4 <= shdr->sh_size)
+	    {
+	      debuglink_name = debuglink_data;
+	      debuglink_crc = *(const uint32_t*)(debuglink_data + crc_offset);
+	    }
+	}
     }
 
   if (symtab_shndx == 0)
     symtab_shndx = dynsym_shndx;
-  if (symtab_shndx != 0)
+  if (symtab_shndx != 0 && !debuginfo)
     {
       const b_elf_shdr *symtab_shdr;
       unsigned int strtab_shndx;
@@ -757,6 +1248,7 @@ elf_add (struct backtrace_state *state,
       /* We no longer need the symbol table, but we hold on to the
 	 string table permanently.  */
       backtrace_release_view (state, &symtab_view, error_callback, data);
+      symtab_view_valid = 0;
 
       *found_sym = 1;
 
@@ -770,6 +1262,53 @@ elf_add (struct backtrace_state *state,
   backtrace_release_view (state, &names_view, error_callback, data);
   names_view_valid = 0;
 
+  /* If the debug info is in a separate file, read that one instead.  */
+
+  if (buildid_data != NULL)
+    {
+      int d;
+
+      d = elf_open_debugfile_by_buildid (state, buildid_data, buildid_size,
+					 error_callback, data);
+      if (d >= 0)
+	{
+	  backtrace_release_view (state, &buildid_view, error_callback, data);
+	  if (debuglink_view_valid)
+	    backtrace_release_view (state, &debuglink_view, error_callback,
+				    data);
+	  return elf_add (state, NULL, d, base_address, error_callback, data,
+			  fileline_fn, found_sym, found_dwarf, 0, 1);
+	}
+    }
+
+  if (buildid_view_valid)
+    {
+      backtrace_release_view (state, &buildid_view, error_callback, data);
+      buildid_view_valid = 0;
+    }
+
+  if (debuglink_name != NULL)
+    {
+      int d;
+
+      d = elf_open_debugfile_by_debuglink (state, filename, debuglink_name,
+					   debuglink_crc, error_callback,
+					   data);
+      if (d >= 0)
+	{
+	  backtrace_release_view (state, &debuglink_view, error_callback,
+				  data);
+	  return elf_add (state, NULL, d, base_address, error_callback, data,
+			  fileline_fn, found_sym, found_dwarf, 0, 1);
+	}
+    }
+
+  if (debuglink_view_valid)
+    {
+      backtrace_release_view (state, &debuglink_view, error_callback, data);
+      debuglink_view_valid = 0;
+    }
+
   /* Read all the debug sections in a single view, since they are
      probably adjacent in the file.  We never release this view.  */
 
@@ -842,6 +1381,10 @@ elf_add (struct backtrace_state *state,
     backtrace_release_view (state, &symtab_view, error_callback, data);
   if (strtab_view_valid)
     backtrace_release_view (state, &strtab_view, error_callback, data);
+  if (debuglink_view_valid)
+    backtrace_release_view (state, &debuglink_view, error_callback, data);
+  if (buildid_view_valid)
+    backtrace_release_view (state, &buildid_view, error_callback, data);
   if (debug_view_valid)
     backtrace_release_view (state, &debug_view, error_callback, data);
   if (descriptor != -1)
@@ -859,6 +1402,7 @@ struct phdr_data
   fileline *fileline_fn;
   int *found_sym;
   int *found_dwarf;
+  const char *exe_filename;
   int exe_descriptor;
 };
 
@@ -873,6 +1417,7 @@ phdr_callback (struct dl_phdr_info *info
 	       void *pdata)
 {
   struct phdr_data *pd = (struct phdr_data *) pdata;
+  const char *filename;
   int descriptor;
   int does_not_exist;
   fileline elf_fileline_fn;
@@ -885,6 +1430,7 @@ phdr_callback (struct dl_phdr_info *info
     {
       if (pd->exe_descriptor == -1)
 	return 0;
+      filename = pd->exe_filename;
       descriptor = pd->exe_descriptor;
       pd->exe_descriptor = -1;
     }
@@ -896,14 +1442,16 @@ phdr_callback (struct dl_phdr_info *info
 	  pd->exe_descriptor = -1;
 	}
 
+      filename = info->dlpi_name;
       descriptor = backtrace_open (info->dlpi_name, pd->error_callback,
 				   pd->data, &does_not_exist);
       if (descriptor < 0)
 	return 0;
     }
 
-  if (elf_add (pd->state, descriptor, info->dlpi_addr, pd->error_callback,
-	       pd->data, &elf_fileline_fn, pd->found_sym, &found_dwarf, 0))
+  if (elf_add (pd->state, filename, descriptor, info->dlpi_addr,
+	       pd->error_callback, pd->data, &elf_fileline_fn, pd->found_sym,
+	       &found_dwarf, 0, 0))
     {
       if (found_dwarf)
 	{
@@ -920,8 +1468,8 @@ phdr_callback (struct dl_phdr_info *info
    sections.  */
 
 int
-backtrace_initialize (struct backtrace_state *state, int descriptor,
-		      backtrace_error_callback error_callback,
+backtrace_initialize (struct backtrace_state *state, const char *filename,
+		      int descriptor, backtrace_error_callback error_callback,
 		      void *data, fileline *fileline_fn)
 {
   int ret;
@@ -930,8 +1478,8 @@ backtrace_initialize (struct backtrace_s
   fileline elf_fileline_fn = elf_nodebug;
   struct phdr_data pd;
 
-  ret = elf_add (state, descriptor, 0, error_callback, data, &elf_fileline_fn,
-		 &found_sym, &found_dwarf, 1);
+  ret = elf_add (state, filename, descriptor, 0, error_callback, data,
+		 &elf_fileline_fn, &found_sym, &found_dwarf, 1, 0);
   if (!ret)
     return 0;
 
Index: fileline.c
===================================================================
--- fileline.c	(revision 253025)
+++ fileline.c	(working copy)
@@ -58,6 +58,7 @@ fileline_initialize (struct backtrace_st
   int pass;
   int called_error_callback;
   int descriptor;
+  const char *filename;
   char buf[64];
 
   if (!state->threaded)
@@ -84,7 +85,6 @@ fileline_initialize (struct backtrace_st
   called_error_callback = 0;
   for (pass = 0; pass < 5; ++pass)
     {
-      const char *filename;
       int does_not_exist;
 
       switch (pass)
@@ -140,8 +140,8 @@ fileline_initialize (struct backtrace_st
 
   if (!failed)
     {
-      if (!backtrace_initialize (state, descriptor, error_callback, data,
-				 &fileline_fn))
+      if (!backtrace_initialize (state, filename, descriptor, error_callback,
+				 data, &fileline_fn))
 	failed = 1;
     }
 
Index: internal.h
===================================================================
--- internal.h	(revision 253025)
+++ internal.h	(working copy)
@@ -268,6 +268,7 @@ extern int backtrace_vector_release (str
    appropriate one.  */
 
 extern int backtrace_initialize (struct backtrace_state *state,
+				 const char *filename,
 				 int descriptor,
 				 backtrace_error_callback error_callback,
 				 void *data,
Index: pecoff.c
===================================================================
--- pecoff.c	(revision 253025)
+++ pecoff.c	(working copy)
@@ -890,7 +890,8 @@ coff_add (struct backtrace_state *state,
    sections.  */
 
 int
-backtrace_initialize (struct backtrace_state *state, int descriptor,
+backtrace_initialize (struct backtrace_state *state,
+		      const char *filename ATTRIBUTE_UNUSED, int descriptor,
 		      backtrace_error_callback error_callback,
 		      void *data, fileline *fileline_fn)
 {
Index: unknown.c
===================================================================
--- unknown.c	(revision 253025)
+++ unknown.c	(working copy)
@@ -54,6 +54,7 @@ unknown_fileline (struct backtrace_state
 
 int
 backtrace_initialize (struct backtrace_state *state ATTRIBUTE_UNUSED,
+		      const char *filename ATTRIBUTE_UNUSED,
 		      int descriptor ATTRIBUTE_UNUSED,
 		      backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
 		      void *data ATTRIBUTE_UNUSED, fileline *fileline_fn)
Index: xcoff.c
===================================================================
--- xcoff.c	(revision 253025)
+++ xcoff.c	(working copy)
@@ -1434,7 +1434,8 @@ xcoff_add_shared_libs (struct backtrace_
    Returns 1 on success, 0 on failure.  */
 
 int
-backtrace_initialize (struct backtrace_state *state, int descriptor,
+backtrace_initialize (struct backtrace_state *state,
+		      const char *filename ATTRIBUTE_UNUSED, int descriptor,
 		      backtrace_error_callback error_callback,
 		      void *data, fileline *fileline_fn)
 {

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-09-20 21:09         ` Ian Lance Taylor via gcc-patches
@ 2017-10-02 11:12           ` Martin Liška
  2017-10-02 11:32             ` Jakub Jelinek
  0 siblings, 1 reply; 28+ messages in thread
From: Martin Liška @ 2017-10-02 11:12 UTC (permalink / raw)
  To: Ian Lance Taylor, Denis Khalikov; +Cc: GCC Patches

Hi.

Currently I see with --with-build-config=bootstrap-ubsan:

/home/marxin/BIG/buildbot/slave/gcc-master-bootstrap-ubsan/build/builddir/prev-x86_64-pc-linux-gnu/libsanitizer/ubsan/.libs/libubsan.a(elf.o): In function `backtrace_uncompress_zdebug':
/home/marxin/BIG/buildbot/slave/gcc-master-bootstrap-ubsan/build/builddir/x86_64-pc-linux-gnu/libsanitizer/libbacktrace/../../.././../libsanitizer/libbacktrace/../../libbacktrace/elf.c:2489: multiple definition of `backtrace_uncompress_zdebug'
../libbacktrace/.libs/libbacktrace.a(elf.o):/home/marxin/BIG/buildbot/slave/gcc-master-bootstrap-ubsan/build/builddir/libbacktrace/.././../libbacktrace/elf.c:2489: first defined here
collect2: error: ld returned 1 exit status
make[3]: *** [Makefile:2904: gcov] Error 1
make[3]: *** Waiting for unfinished jobs....

Thanks,
Martin

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-10-02 11:12           ` Martin Liška
@ 2017-10-02 11:32             ` Jakub Jelinek
  2017-10-02 14:01               ` Jakub Jelinek
  0 siblings, 1 reply; 28+ messages in thread
From: Jakub Jelinek @ 2017-10-02 11:32 UTC (permalink / raw)
  To: Martin Liška; +Cc: Ian Lance Taylor, Denis Khalikov, GCC Patches

On Mon, Oct 02, 2017 at 01:12:24PM +0200, Martin Liška wrote:
> Hi.
> 
> Currently I see with --with-build-config=bootstrap-ubsan:
> 
> /home/marxin/BIG/buildbot/slave/gcc-master-bootstrap-ubsan/build/builddir/prev-x86_64-pc-linux-gnu/libsanitizer/ubsan/.libs/libubsan.a(elf.o): In function `backtrace_uncompress_zdebug':
> /home/marxin/BIG/buildbot/slave/gcc-master-bootstrap-ubsan/build/builddir/x86_64-pc-linux-gnu/libsanitizer/libbacktrace/../../.././../libsanitizer/libbacktrace/../../libbacktrace/elf.c:2489: multiple definition of `backtrace_uncompress_zdebug'
> ../libbacktrace/.libs/libbacktrace.a(elf.o):/home/marxin/BIG/buildbot/slave/gcc-master-bootstrap-ubsan/build/builddir/libbacktrace/.././../libbacktrace/elf.c:2489: first defined here
> collect2: error: ld returned 1 exit status
> make[3]: *** [Makefile:2904: gcov] Error 1
> make[3]: *** Waiting for unfinished jobs....

I think this should fix it, I'm going to bootstrap/regtest this now (though
not --with-build-config=bootstrap-ubsan).

2017-10-02  Jakub Jelinek  <jakub@redhat.com>

	* libbacktrace/backtrace-rename.h (backtrace_uncompress_zdebug):
	Define.

--- libsanitizer/libbacktrace/backtrace-rename.h.jj	2014-09-25 15:01:25.000000000 +0200
+++ libsanitizer/libbacktrace/backtrace-rename.h	2017-10-02 13:30:23.096271411 +0200
@@ -11,6 +11,7 @@
 #define backtrace_qsort __asan_backtrace_qsort
 #define backtrace_release_view __asan_backtrace_release_view
 #define backtrace_syminfo __asan_backtrace_syminfo
+#define backtrace_uncompress_zdebug __asan_backtrace_uncompress_zdebug
 #define backtrace_vector_finish __asan_backtrace_vector_finish
 #define backtrace_vector_grow __asan_backtrace_vector_grow
 #define backtrace_vector_release __asan_backtrace_vector_release


	Jakub

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-10-02 11:32             ` Jakub Jelinek
@ 2017-10-02 14:01               ` Jakub Jelinek
  0 siblings, 0 replies; 28+ messages in thread
From: Jakub Jelinek @ 2017-10-02 14:01 UTC (permalink / raw)
  To: Martin Liška; +Cc: Ian Lance Taylor, Denis Khalikov, GCC Patches

On Mon, Oct 02, 2017 at 01:32:01PM +0200, Jakub Jelinek wrote:
> On Mon, Oct 02, 2017 at 01:12:24PM +0200, Martin Liška wrote:
> > Hi.
> > 
> > Currently I see with --with-build-config=bootstrap-ubsan:
> > 
> > /home/marxin/BIG/buildbot/slave/gcc-master-bootstrap-ubsan/build/builddir/prev-x86_64-pc-linux-gnu/libsanitizer/ubsan/.libs/libubsan.a(elf.o): In function `backtrace_uncompress_zdebug':
> > /home/marxin/BIG/buildbot/slave/gcc-master-bootstrap-ubsan/build/builddir/x86_64-pc-linux-gnu/libsanitizer/libbacktrace/../../.././../libsanitizer/libbacktrace/../../libbacktrace/elf.c:2489: multiple definition of `backtrace_uncompress_zdebug'
> > ../libbacktrace/.libs/libbacktrace.a(elf.o):/home/marxin/BIG/buildbot/slave/gcc-master-bootstrap-ubsan/build/builddir/libbacktrace/.././../libbacktrace/elf.c:2489: first defined here
> > collect2: error: ld returned 1 exit status
> > make[3]: *** [Makefile:2904: gcov] Error 1
> > make[3]: *** Waiting for unfinished jobs....
> 
> I think this should fix it, I'm going to bootstrap/regtest this now (though
> not --with-build-config=bootstrap-ubsan).
> 
> 2017-10-02  Jakub Jelinek  <jakub@redhat.com>
> 
> 	* libbacktrace/backtrace-rename.h (backtrace_uncompress_zdebug):
> 	Define.

Committed as obvious after bootstrap/regtest on x86_64-linux and i686-linux.
> 
> --- libsanitizer/libbacktrace/backtrace-rename.h.jj	2014-09-25 15:01:25.000000000 +0200
> +++ libsanitizer/libbacktrace/backtrace-rename.h	2017-10-02 13:30:23.096271411 +0200
> @@ -11,6 +11,7 @@
>  #define backtrace_qsort __asan_backtrace_qsort
>  #define backtrace_release_view __asan_backtrace_release_view
>  #define backtrace_syminfo __asan_backtrace_syminfo
> +#define backtrace_uncompress_zdebug __asan_backtrace_uncompress_zdebug
>  #define backtrace_vector_finish __asan_backtrace_vector_finish
>  #define backtrace_vector_grow __asan_backtrace_vector_grow
>  #define backtrace_vector_release __asan_backtrace_vector_release

	Jakub

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-04-12 22:08       ` Jeff Law
@ 2017-05-18 14:23         ` Denis Khalikov
  0 siblings, 0 replies; 28+ messages in thread
From: Denis Khalikov @ 2017-05-18 14:23 UTC (permalink / raw)
  To: Jeff Law, Ian Lance Taylor, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 279 bytes --]

Hello everyone,
i've updated the patch recently for this issue:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77631

Can someone please review it.

Thanks.


On 04/13/2017 01:07 AM, Jeff Law wrote:
> Given this doesn't look like a regression, I'm going to punt to gcc-8.
>
> jeff

[-- Attachment #2: PR-sanitizer-77631.patch --]
[-- Type: text/x-patch, Size: 39377 bytes --]

From: Denis Khalikov <d.khalikov@partner.samsung.com>
Date: Thu, 18 May 2017 16:04:17 +0300
Subject: [PATCH] 	PR sanitizer/77631

	* Makefile.am: Regenerated.
	* Makefile.in: Regenerated.
	* configure.ac: Add searching for limits.h and sys/param.h
	* config.h.in: Regenerated.
	* configure: Regenerated.
	* elf.c (enum type_of_file): New enum.
	* elf.c (enum_type_of_elf): 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.
	(process_elf_header): New function. Verify elf header.
	(elf_get_section_by_name): New function. Get section by name.
	(backtrace_readlink): New function. Get type of file from filename.
	(resolve_realname): New function. Resolve real name if file is link.
	(backtrace_resolve_realname): New function. Resolve real name for any
	file type.
	(search_for_debugfile): New function. Search for debug file in known
	paths.
	(open_debugfile_by_gnulink): New function. Open debug file with
	gnulink.
	(hex): New function. Convert to hex.
	(get_build_id_name): New function. Generate build-id name.
	(open_debugfile_by_build_id): New function. Open debug file with
	build-id.
	(backtrace_open_debugfile): New function. Open debug file.
	(elf_add): Move code which reads elf header to elf_header_is_valid.
	(phdr_callback): Call backtrace_open_debugfile function for shared
	library.
	* fileline.c (fileline_initialize): Call backtrace_open_debugfile for
	executable.
	* internal.h: Updated.
---
 libbacktrace/ChangeLog    |  38 ++
 libbacktrace/Makefile.in  |   2 +-
 libbacktrace/config.h.in  |   6 +
 libbacktrace/configure    |  18 +
 libbacktrace/configure.ac |   6 +
 libbacktrace/elf.c        | 977 +++++++++++++++++++++++++++++++++++++++++-----
 libbacktrace/fileline.c   |  11 +-
 libbacktrace/internal.h   |   5 +
 8 files changed, 971 insertions(+), 92 deletions(-)

diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog
index 25cd921..33bbf63 100644
--- a/libbacktrace/ChangeLog
+++ b/libbacktrace/ChangeLog
@@ -1,3 +1,41 @@
+2017-05-18  Denis Khalikov  <d.khalikov@partner.samsung.com>
+
+	PR sanitizer/77631
+	* Makefile.am: Regenerated.
+	* Makefile.in: Regenerated.
+	* configure.ac: Add searching for limits.h and sys/param.h
+	* config.h.in: Regenerated.
+	* configure: Regenerated.
+	* elf.c (enum type_of_file): New enum.
+	* elf.c (enum_type_of_elf): 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.
+	(process_elf_header): New function. Verify elf header.
+	(elf_get_section_by_name): New function. Get section by name.
+	(backtrace_readlink): New function. Get type of file from filename.
+	(resolve_realname): New function. Resolve real name if file is link.
+	(backtrace_resolve_realname): New function. Resolve real name for any
+	file type.
+	(search_for_debugfile): New function. Search for debug file in known
+	paths.
+	(open_debugfile_by_gnulink): New function. Open debug file with
+	gnulink.
+	(hex): New function. Convert to hex.
+	(get_build_id_name): New function. Generate build-id name.
+	(open_debugfile_by_build_id): New function. Open debug file with
+	build-id.
+	(backtrace_open_debugfile): New function. Open debug file.
+	(elf_add): Move code which reads elf header to elf_header_is_valid.
+	(phdr_callback): Call backtrace_open_debugfile function for shared
+	library.
+	* fileline.c (fileline_initialize): Call backtrace_open_debugfile for
+	executable.
+	* internal.h: Updated.
+
 2017-03-08  Sam Thursfield  <sam.thursfield@codethink.co.uk>
 
 	* btest.c (test5): Replace #ifdef guard with 'unused' attribute
diff --git a/libbacktrace/Makefile.in b/libbacktrace/Makefile.in
index de74b5d..e604b6c 100644
--- a/libbacktrace/Makefile.in
+++ b/libbacktrace/Makefile.in
@@ -16,7 +16,7 @@
 @SET_MAKE@
 
 # Makefile.am -- Backtrace Makefile.
-# Copyright (C) 2012-2016 Free Software Foundation, Inc.
+# Copyright (C) 2012-2017 Free Software Foundation, Inc.
 
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
diff --git a/libbacktrace/config.h.in b/libbacktrace/config.h.in
index 87cb805..edbd9af 100644
--- a/libbacktrace/config.h.in
+++ b/libbacktrace/config.h.in
@@ -28,6 +28,9 @@
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
+/* Define 1 if <limits.h> is available. */
+#undef HAVE_LIMITS_H
+
 /* Define to 1 if you have the <link.h> header file. */
 #undef HAVE_LINK_H
 
@@ -52,6 +55,9 @@
 /* Define to 1 if you have the <sys/mman.h> header file. */
 #undef HAVE_SYS_MMAN_H
 
+/* Define 1 if <sys/param.h> is available. */
+#undef HAVE_SYS_PARAM_H
+
 /* Define to 1 if you have the <sys/stat.h> header file. */
 #undef HAVE_SYS_STAT_H
 
diff --git a/libbacktrace/configure b/libbacktrace/configure
index ee90bc6..fe78772 100755
--- a/libbacktrace/configure
+++ b/libbacktrace/configure
@@ -12350,6 +12350,24 @@ if test "$ALLOC_FILE" = "alloc.lo"; then
 fi
 
 
+ac_fn_c_check_header_mongrel "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default"
+if test "x$ac_cv_header_limits_h" = x""yes; then :
+
+$as_echo "#define HAVE_LIMITS_H 1" >>confdefs.h
+
+fi
+
+
+
+ac_fn_c_check_header_mongrel "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_param_h" = x""yes; then :
+
+$as_echo "#define HAVE_SYS_PARAM_H 1" >>confdefs.h
+
+fi
+
+
+
 # Check for dl_iterate_phdr.
 for ac_header in link.h
 do :
diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac
index f9cad21..0d029bf 100644
--- a/libbacktrace/configure.ac
+++ b/libbacktrace/configure.ac
@@ -301,6 +301,12 @@ if test "$ALLOC_FILE" = "alloc.lo"; then
 fi
 AC_SUBST(BACKTRACE_USES_MALLOC)
 
+AC_CHECK_HEADER(limits.h,
+AC_DEFINE(HAVE_LIMITS_H, 1,[Define 1 if <limits.h> is available.]))
+
+AC_CHECK_HEADER(sys/param.h,
+AC_DEFINE(HAVE_SYS_PARAM_H, 1,[Define 1 if <sys/param.h> is available.]))
+
 # Check for dl_iterate_phdr.
 AC_CHECK_HEADERS(link.h)
 if test "$ac_cv_header_link_h" = "no"; then
diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c
index 89ed42b..bbf55ce 100644
--- a/libbacktrace/elf.c
+++ b/libbacktrace/elf.c
@@ -35,6 +35,9 @@ POSSIBILITY OF SUCH DAMAGE.  */
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
 
 #ifdef HAVE_DL_ITERATE_PHDR
 #include <link.h>
@@ -42,6 +45,34 @@ POSSIBILITY OF SUCH DAMAGE.  */
 
 #include "backtrace.h"
 #include "internal.h"
+#include "filenames.h"
+
+
+/* The following is from pathmax.h.  */
+/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
+   PATH_MAX but might cause redefinition warnings when sys/param.h is
+   later included (as on MORE/BSD 4.3).  */
+#if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
+# include <limits.h>
+#endif
+
+#ifndef _POSIX_PATH_MAX
+# define _POSIX_PATH_MAX 255
+#endif
+
+/* Don't include sys/param.h if it already has been.  */
+#if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
+# include <sys/param.h>
+#endif
+
+#if !defined PATH_MAX && defined MAXPATHLEN
+# define PATH_MAX MAXPATHLEN
+#endif
+
+#ifndef PATH_MAX
+# define PATH_MAX _POSIX_PATH_MAX
+#endif
+
 
 #ifndef HAVE_DL_ITERATE_PHDR
 
@@ -283,6 +314,843 @@ struct elf_syminfo_data
   size_t count;
 };
 
+/* Information to read ELF note section */
+
+typedef struct
+{
+  unsigned char namesz[4];
+  unsigned char descsz[4];
+  unsigned char type[4];
+  unsigned char name[1];
+} Elf_External_Note;
+
+/* Type of the file */
+
+enum type_of_file
+{
+  LINK = 1,
+  REGULAR = 2
+};
+
+/* Information about debug paths */
+
+enum debug_path
+{
+  CURRENT,
+  CURRENT_DEBUG,
+  USR_LIB_DEBUG,
+  USR_LIB_DEBUG_PATH_TO_EXE,
+  DEBUG_PATH_MAX
+};
+
+/* Type of the elf file  */
+
+enum type_of_elf
+{
+  DYN = -1,
+  INVALID = 0,
+  EXEC = 1
+};
+
+/* Paths to debug file */
+
+static const char *const debug_file_path[DEBUG_PATH_MAX]
+  = {"", ".debug/", "/usr/lib/debug/", "/usr/lib/debug"};
+
+
+/* Cast from void pointer to uint32_t */
+
+static uint32_t
+getl32 (void *p)
+{
+  char *addr = (char *) p;
+  uint32_t v = 0;
+  v = *((uint32_t *) addr);
+  return v;
+}
+
+/* Function that produce crc32 value for input buffer */
+
+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;
+}
+
+/* Generate crc32 sum from the file */
+
+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;
+}
+
+/* Get length of the path */
+
+static int
+pathlen (const char *buffer)
+{
+  int len;
+  int full_filename_len;
+
+  len = full_filename_len = strlen (buffer);
+  while (len > 1 && !IS_DIR_SEPARATOR (buffer[len - 1]))
+    --len;
+  return len > 1 ? len : -1;
+}
+
+/* Verify crc32 sum */
+
+static int
+check_sum (int descriptor, char *debug_link, int offset, 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;
+}
+
+/* Process elf header. Verify magic number, version, etc, of the ELF header,
+   return 1 if the file type is EXE, -1 if type of the file is DYN
+   and 0 on the fail. In case we call this function when we want to read elf
+   section, we shouldn't care about type of the elf file, last parameter of
+   this function "int read_section" determines this situation. In other case,
+   when we want to read debuginfo from the elf file, we should know
+   the base_address, which we can't get for this moment if it's a PIE,
+   because we do not know to which address rtld has loaded this binary.
+   We will get this address later, from dl_phdr_info. */
+static int
+process_elf_header (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,
+		    int read_section)
+{
+
+  struct backtrace_view ehdr_view;
+
+  if (!backtrace_get_view (state, descriptor, 0, sizeof *ehdr_out,
+			   error_callback, data, &ehdr_view))
+    goto fail;
+
+  memcpy (ehdr_out, ehdr_view.data, sizeof *ehdr_out);
+
+  backtrace_release_view (state, &ehdr_view, error_callback, data);
+
+  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_out->e_ident[EI_VERSION] != EV_CURRENT)
+    {
+      error_callback (data, "executable file is unrecognized ELF version", 0);
+      goto fail;
+    }
+
+#if BACKTRACE_ELF_SIZE == 32
+#define BACKTRACE_ELFCLASS ELFCLASS32
+#else
+#define BACKTRACE_ELFCLASS ELFCLASS64
+#endif
+
+  if (ehdr_out->e_ident[EI_CLASS] != BACKTRACE_ELFCLASS)
+    {
+      error_callback (data, "executable file is unexpected ELF class", 0);
+      goto fail;
+    }
+
+  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;
+    }
+
+  /* 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_out->e_type == ET_DYN && !read_section)
+    return -1;
+
+  *shoff_out = ehdr_out->e_shoff;
+  *shnum_out = ehdr_out->e_shnum;
+  *shstrndx_out = ehdr_out->e_shstrndx;
+
+  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_out, sizeof shdr,
+			       error_callback, data, &shdr_view))
+	goto fail;
+
+      shdr = (const b_elf_shdr *) shdr_view.data;
+
+      if (*shnum_out == 0)
+	*shnum_out = shdr->sh_size;
+
+      if (*shstrndx_out == SHN_XINDEX)
+	{
+	  *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
+	     correctly.  All large section indexes were offset by
+	     0x100.  There is more information at
+	     http://sourceware.org/bugzilla/show_bug.cgi?id-5900 .
+	     Fortunately these object files are easy to detect, as the
+	     GNU binutils always put the section header string table
+	     near the end of the list of sections.  Thus if the
+	     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_out >= *shnum_out
+	      && *shstrndx_out >= SHN_LORESERVE + 0x100)
+	    *shstrndx_out -= 0x100;
+	}
+      backtrace_release_view (state, &shdr_view, error_callback, data);
+    }
+  return 1;
+fail:
+  return 0;
+}
+
+
+/* Get content of the specifying section */
+
+static unsigned char *
+elf_get_section_by_name (struct backtrace_state *state, int descriptor,
+			 backtrace_error_callback error_callback, void *data,
+			 int exe, int *section_data_len_out,
+			 const char *section_name)
+{
+  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 section_view;
+  int names_view_valid;
+  const char *names;
+  unsigned int i;
+  int section_view_valid;
+  unsigned char *section_data;
+
+  section_view_valid = 0;
+  shdrs_view_valid = 0;
+  names_view_valid = 0;
+  section_data = NULL;
+
+  if (!process_elf_header (state, descriptor, error_callback, data, exe,
+			    &shoff, &shnum, &shstrndx, &ehdr, 1))
+    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, section_name) == 0)
+	{
+	  if (backtrace_get_view (state, descriptor, shdr->sh_offset,
+				  shdr->sh_size, error_callback, data,
+				  &section_view))
+	    {
+
+	      section_view_valid = 1;
+	      section_data
+		= backtrace_alloc (state, shdr->sh_size, error_callback, data);
+	      if (section_data == NULL)
+		goto exit;
+	      memcpy (section_data, section_view.data, shdr->sh_size);
+	      *section_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 (section_view_valid)
+    backtrace_release_view (state, &section_view, error_callback, data);
+  return section_data;
+}
+
+
+/* Verify type of the file */
+
+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;
+}
+
+/* Resolve full name of the link. In this case we can't use realpath function
+   because it could be undefined on some platfroms, also it allocates memory
+   by malloc, which we can't use. */
+
+static int
+resolve_realname (const char *filename, char *buffer, int *does_not_exist,
+		  struct backtrace_state *state,
+		  backtrace_error_callback error_callback, void *data)
+{
+  char *temp_buffer;
+  int file_type;
+  int filename_len;
+  int temp_filename_len;
+  int valid_temp_buffer;
+  int path_len;
+
+  valid_temp_buffer = 0;
+  filename_len = -1;
+  file_type = LINK;
+
+  /* allocate memory for sizeof(PATH_MAX) + 1 bytes because at this time
+     we don't know how long path could be */
+  temp_buffer = backtrace_alloc (state, PATH_MAX + 1, error_callback, data);
+  if (temp_buffer == NULL)
+    return -1;
+
+  valid_temp_buffer = 1;
+
+  memset (temp_buffer, 0, PATH_MAX + 1);
+  memcpy (temp_buffer, filename, strlen (filename));
+
+  while (file_type == LINK)
+    {
+      filename_len = readlink (temp_buffer, buffer, PATH_MAX);
+      if (filename_len < 1)
+	goto exit;
+
+      temp_filename_len = strlen (buffer);
+
+      /* full path */
+      if (buffer[0] == '/')
+	{
+	  memset (temp_buffer, 0, PATH_MAX);
+	  memcpy (temp_buffer, buffer, temp_filename_len);
+	}
+      else
+	{
+	  /* relative path */
+	  path_len = pathlen (temp_buffer);
+	  if (path_len < 1)
+	    goto exit;
+
+	  memcpy (temp_buffer + path_len, buffer, filename_len);
+	  temp_buffer[path_len + filename_len] = '\0';
+	}
+
+      file_type = backtrace_readlink (temp_buffer, error_callback,
+				      does_not_exist, data);
+      memset (buffer, 0, filename_len);
+    }
+
+  if (file_type != REGULAR)
+    {
+      filename_len = -1;
+      goto exit;
+    }
+
+  filename_len = strlen (temp_buffer);
+  memcpy (buffer, temp_buffer, filename_len);
+
+exit:
+  if (valid_temp_buffer)
+    backtrace_free (state, temp_buffer, PATH_MAX + 1, error_callback, data);
+  return filename_len;
+}
+
+/* Resolve realname of the filename. This function verifies filename.
+   If filename is name of the file it populate realname buffer.
+   If filename is link, it calls resolve_realname function. */
+
+static char *
+backtrace_resolve_realname (const char *filename, int *filename_len,
+			    struct backtrace_state *state,
+			    backtrace_error_callback error_callback, void *data,
+			    int *does_not_exist)
+{
+  int file_type;
+  char *realname;
+
+  *filename_len = -1;
+
+  realname = backtrace_alloc (state, PATH_MAX + 1, error_callback, data);
+  if (realname == NULL)
+    goto exit;
+
+  file_type
+    = backtrace_readlink (filename, error_callback, does_not_exist, data);
+
+  if (file_type == LINK)
+    {
+      /* read the actual filename */
+      *filename_len = resolve_realname (filename, realname, does_not_exist,
+					state, error_callback, data);
+      if (*filename_len < 0)
+	goto exit;
+    }
+  else if (file_type == REGULAR)
+    {
+      *filename_len = strlen (filename);
+      if (*filename_len > PATH_MAX)
+	goto exit;
+
+      memcpy (realname, filename, *filename_len);
+    }
+
+exit:
+  return realname;
+}
+
+/* Search for debug file into specifying directorires */
+
+static int
+search_for_debugfile (char *realname, char *debug_filename,
+		      backtrace_error_callback error_callback, void *data,
+		      struct backtrace_state *state)
+{
+  int debug_filename_len;
+  int pass;
+  int debug_path_len;
+  int debug_does_not_exist;
+  int debug_descriptor;
+  int path_len;
+  char *buffer;
+  int buffer_len;
+  int valid_buffer;
+
+  debug_descriptor = -1;
+  valid_buffer = 0;
+
+  path_len = pathlen (realname);
+  debug_filename_len = strlen ((const char *) debug_filename);
+
+  if (debug_filename_len < 1)
+    goto exit;
+
+  buffer_len = path_len + strlen (debug_file_path[USR_LIB_DEBUG])
+	       + debug_filename_len + 1;
+
+  buffer = backtrace_alloc (state, buffer_len, error_callback, data);
+
+  if (buffer == NULL)
+    goto exit;
+  memset (buffer, 0, buffer_len);
+  memcpy (buffer, realname, path_len);
+
+  valid_buffer = 1;
+  for (pass = 0; pass < DEBUG_PATH_MAX; ++pass)
+    {
+      switch (pass)
+	{
+	case CURRENT:
+	  {
+	    memcpy (buffer + path_len, debug_filename, 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_filename,
+		    debug_filename_len);
+	    break;
+	  }
+	case USR_LIB_DEBUG:
+	  {
+	    debug_path_len = strlen (debug_file_path[USR_LIB_DEBUG]);
+	    memset (buffer, 0, buffer_len);
+	    memcpy (buffer, debug_file_path[USR_LIB_DEBUG], debug_path_len);
+	    memcpy (buffer + debug_path_len, debug_filename,
+		    debug_filename_len);
+	    break;
+	  }
+	case USR_LIB_DEBUG_PATH_TO_EXE:
+	  {
+	    debug_path_len
+	      = strlen (debug_file_path[USR_LIB_DEBUG_PATH_TO_EXE]);
+	    memset (buffer, 0, buffer_len);
+	    memcpy (buffer, debug_file_path[USR_LIB_DEBUG_PATH_TO_EXE],
+		    debug_path_len);
+	    memcpy (buffer + debug_path_len, realname, path_len);
+	    memcpy (buffer + debug_path_len + path_len, debug_filename,
+		    debug_filename_len);
+	    break;
+	  }
+	default:
+	  goto exit;
+	}
+
+      debug_descriptor
+	= backtrace_open (buffer, error_callback, data, &debug_does_not_exist);
+
+      if (debug_descriptor > 0)
+	break;
+    }
+exit:
+  if (valid_buffer)
+    backtrace_free (state, buffer, buffer_len, error_callback, data);
+  return debug_descriptor;
+}
+
+/* Open debug file by gnulink */
+
+static int
+open_debugfile_by_gnulink (char *realname, unsigned char *section_data,
+			   int section_data_len, struct backtrace_state *state,
+			   backtrace_error_callback error_callback, void *data)
+{
+  int debug_descriptor;
+
+  debug_descriptor = search_for_debugfile (realname, (char *) section_data,
+					   error_callback, data, state);
+  if (debug_descriptor < 0)
+    goto exit;
+
+  /* check the crc32 checksum if it not the same return -1 */
+
+  if (!check_sum (debug_descriptor, (char *) section_data,
+		  strlen ((char *) section_data), section_data_len))
+    debug_descriptor = -1;
+
+exit:
+  return debug_descriptor;
+}
+
+/* Convert char to hex */
+
+static char
+hex (char ch)
+{
+  return ch > 9 ? ('a' + (ch - 10)) : ('0' + ch);
+}
+
+/* Get build-id name  */
+
+static char *
+get_build_id_name (unsigned char *section_data, int *len,
+		   struct backtrace_state *state,
+		   backtrace_error_callback error_callback, void *data)
+{
+  Elf_External_Note *build_id_section;
+  char *build_id_name;
+  char *temp;
+  const char *debug_postfix;
+  const char *debug_prefix;
+  size_t debug_postfix_len;
+  size_t debug_prefix_len;
+  size_t name_size;
+  int offset;
+  unsigned char *hash_start;
+  unsigned long hash_size;
+  unsigned long identifier;
+
+  debug_postfix_len = 6;
+  debug_prefix_len = 10;
+  debug_postfix = ".debug";
+  debug_prefix = ".build-id/";
+  *len = 0;
+
+  build_id_section = (Elf_External_Note *) section_data;
+  hash_size = getl32 (build_id_section->descsz);
+  identifier = getl32 (build_id_section->type);
+  name_size = getl32 (build_id_section->namesz);
+
+  if (identifier != NT_GNU_BUILD_ID || hash_size == 0 || name_size != 4
+      || strncmp ((char *) build_id_section->name, "GNU", 3) != 0)
+    return NULL;
+
+  offset = 16;
+  hash_start = section_data + offset;
+  *len = hash_size * 2 + debug_postfix_len + debug_prefix_len + 1;
+  build_id_name = backtrace_alloc (state, *len, error_callback, data);
+
+  memset (build_id_name, 0, *len);
+  memcpy (build_id_name, debug_prefix, debug_prefix_len);
+  temp = build_id_name + debug_prefix_len;
+
+  *temp++ = hex ((*hash_start & 0xF0) >> 4);
+  *temp++ = hex (*hash_start & 0x0F);
+  ++hash_start;
+  --hash_size;
+
+  memcpy (temp, "/", 1);
+  ++temp;
+
+  while (hash_size--)
+    {
+      *temp++ = hex ((*hash_start & 0xF0) >> 4);
+      *temp++ = hex (*hash_start & 0x0F);
+      ++hash_start;
+    }
+
+  memcpy (temp, debug_postfix, debug_postfix_len);
+  return build_id_name;
+}
+
+/* Open file by build-id */
+
+static int
+open_debugfile_by_build_id (char *realname, unsigned char *section_data,
+			    struct backtrace_state *state,
+			    backtrace_error_callback error_callback, void *data)
+
+{
+  char *build_id_name;
+  int debug_descriptor;
+  int build_id_name_len;
+  int valid_build_id_name;
+
+  debug_descriptor = -1;
+  valid_build_id_name = 0;
+
+  build_id_name = get_build_id_name (section_data, &build_id_name_len, state,
+				     error_callback, data);
+
+  if (build_id_name == NULL || build_id_name_len <= 0)
+    goto exit;
+
+  valid_build_id_name = 1;
+
+  debug_descriptor = search_for_debugfile (realname, build_id_name,
+					   error_callback, data, state);
+
+exit:
+  if (valid_build_id_name)
+    backtrace_free (state, build_id_name, build_id_name_len, error_callback,
+		    data);
+  return debug_descriptor;
+}
+
+/* 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)
+{
+  int descriptor;
+  int debug_descriptor;
+  unsigned char *gnulink_section_data;
+  unsigned char *build_id_section_data;
+  size_t valid_descriptor;
+  size_t valid_gnulink_section_data;
+  size_t valid_build_id_section_data;
+  size_t valid_realname;
+  int build_id_section_data_len;
+  int gnu_link_section_data_len;
+  char *realname;
+  int filename_len;
+
+  valid_realname = 0;
+  valid_descriptor = 0;
+  valid_gnulink_section_data = 0;
+  valid_build_id_section_data = 0;
+  build_id_section_data_len = 0;
+  gnu_link_section_data_len = 0;
+  debug_descriptor = -1;
+
+  descriptor = backtrace_open (filename, error_callback, data, does_not_exist);
+
+  if (descriptor < 0)
+    goto exit;
+
+  valid_descriptor = 1;
+
+  realname = backtrace_resolve_realname (filename, &filename_len, state,
+					 error_callback, data, does_not_exist);
+
+  if (realname == NULL || filename_len < 0)
+    goto exit;
+
+  valid_realname = 1;
+
+  /* check if build-id  section does exist */
+  build_id_section_data
+    = elf_get_section_by_name (state, descriptor, error_callback, data, exe,
+			       &build_id_section_data_len,
+			       ".note.gnu.build-id");
+
+  if (build_id_section_data != NULL && build_id_section_data_len > 0)
+    {
+      valid_build_id_section_data = 1;
+      debug_descriptor
+	= open_debugfile_by_build_id (realname, build_id_section_data, state,
+				      error_callback, data);
+    }
+
+  if (debug_descriptor < 0)
+    {
+      gnulink_section_data
+	= elf_get_section_by_name (state, descriptor, error_callback, data, exe,
+				   &gnu_link_section_data_len,
+				   ".gnu_debuglink");
+
+      if (gnulink_section_data != NULL && gnu_link_section_data_len > 0)
+	{
+	  valid_gnulink_section_data = 1;
+	  debug_descriptor
+	    = open_debugfile_by_gnulink (realname, gnulink_section_data,
+					 gnu_link_section_data_len, state,
+					 error_callback, data);
+	}
+    }
+
+exit:
+  if (valid_descriptor)
+    backtrace_close (descriptor, error_callback, data);
+  if (valid_gnulink_section_data)
+    backtrace_free (state, gnulink_section_data, gnu_link_section_data_len,
+		    error_callback, data);
+  if (valid_build_id_section_data)
+    backtrace_free (state, build_id_section_data, build_id_section_data_len,
+		    error_callback, data);
+  if (valid_realname)
+    backtrace_free (state, realname, PATH_MAX + 1, error_callback, data);
+  return debug_descriptor;
+}
+
 /* A dummy callback function used when we can't find any debug info.  */
 
 static int
@@ -521,7 +1389,6 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
 	 backtrace_error_callback error_callback, void *data,
 	 fileline *fileline_fn, int *found_sym, int *found_dwarf, int exe)
 {
-  struct backtrace_view ehdr_view;
   b_elf_ehdr ehdr;
   off_t shoff;
   unsigned int shnum;
@@ -547,6 +1414,7 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
   off_t max_offset;
   struct backtrace_view debug_view;
   int debug_view_valid;
+  enum type_of_elf elf_type;
 
   *found_sym = 0;
   *found_dwarf = 0;
@@ -557,102 +1425,28 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
   strtab_view_valid = 0;
   debug_view_valid = 0;
 
-  if (!backtrace_get_view (state, descriptor, 0, sizeof ehdr, error_callback,
-			   data, &ehdr_view))
-    goto fail;
-
-  memcpy (&ehdr, ehdr_view.data, sizeof ehdr);
-
-  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)
-    {
-      error_callback (data, "executable file is not ELF", 0);
-      goto fail;
-    }
-  if (ehdr.e_ident[EI_VERSION] != EV_CURRENT)
-    {
-      error_callback (data, "executable file is unrecognized ELF version", 0);
-      goto fail;
-    }
-
-#if BACKTRACE_ELF_SIZE == 32
-#define BACKTRACE_ELFCLASS ELFCLASS32
-#else
-#define BACKTRACE_ELFCLASS ELFCLASS64
-#endif
-
-  if (ehdr.e_ident[EI_CLASS] != BACKTRACE_ELFCLASS)
+  elf_type = process_elf_header (state, descriptor, error_callback, data, exe,
+				 &shoff, &shnum, &shstrndx, &ehdr, 0);
+  switch (elf_type)
     {
-      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)
-    {
-      error_callback (data, "executable file has unknown endianness", 0);
+    /* Binary compiled with PIE option */
+    case DYN:
+      return -1;
+    case EXEC:
+      break;
+    /* Header is invlalid */
+    case INVALID:
       goto fail;
     }
 
-  /* If the executable is ET_DYN, it is either a PIE, or we are running
-     directly a shared library with .interp.  We need to wait for
-     dl_iterate_phdr in that case to determine the actual base_address.  */
-  if (exe && ehdr.e_type == ET_DYN)
-    return -1;
-
-  shoff = ehdr.e_shoff;
-  shnum = ehdr.e_shnum;
-  shstrndx = ehdr.e_shstrndx;
-
-  if ((shnum == 0 || shstrndx == SHN_XINDEX)
-      && shoff != 0)
-    {
-      struct backtrace_view shdr_view;
-      const b_elf_shdr *shdr;
-
-      if (!backtrace_get_view (state, descriptor, shoff, 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 (shstrndx == SHN_XINDEX)
-	{
-	  shstrndx = shdr->sh_link;
-
-	  /* Versions of the GNU binutils between 2.12 and 2.18 did
-	     not handle objects with more than SHN_LORESERVE sections
-	     correctly.  All large section indexes were offset by
-	     0x100.  There is more information at
-	     http://sourceware.org/bugzilla/show_bug.cgi?id-5900 .
-	     Fortunately these object files are easy to detect, as the
-	     GNU binutils always put the section header string table
-	     near the end of the list of sections.  Thus if the
-	     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;
-	}
-
-      backtrace_release_view (state, &shdr_view, error_callback, data);
-    }
-
   /* To translate PC to file/line when using DWARF, we need to find
      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 +1671,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 +1691,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/internal.h b/libbacktrace/internal.h
index 89b7bf7..c8e5fac 100644
--- a/libbacktrace/internal.h
+++ b/libbacktrace/internal.h
@@ -176,6 +176,11 @@ struct backtrace_view
   size_t len;
 };
 
+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,
-- 
1.9.1


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-03-22 15:29     ` Denis Khalikov
@ 2017-04-12 22:08       ` Jeff Law
  2017-05-18 14:23         ` Denis Khalikov
  0 siblings, 1 reply; 28+ messages in thread
From: Jeff Law @ 2017-04-12 22:08 UTC (permalink / raw)
  To: Denis Khalikov, Ian Lance Taylor, gcc-patches

On 03/22/2017 09:28 AM, Denis Khalikov wrote:
> Hello everyone,
> I've fixed some issues and implemented functionality
> to search debug file by build-id.
> Can someone please review my patch.
Given this doesn't look like a regression, I'm going to punt to gcc-8.

jeff

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-03-14 13:49   ` Ian Lance Taylor via gcc-patches
  2017-03-14 14:30     ` Denis Khalikov
@ 2017-03-22 15:29     ` Denis Khalikov
  2017-04-12 22:08       ` Jeff Law
  1 sibling, 1 reply; 28+ messages in thread
From: Denis Khalikov @ 2017-03-22 15:29 UTC (permalink / raw)
  To: Ian Lance Taylor, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 6122 bytes --]

Hello everyone,
I've fixed some issues and implemented functionality
to search debug file by build-id.
Can someone please review my patch.

 > As far as I know all the debuglink code is ELF-specific.  I would do
 > it all in elf.c.  While reading the sections of the executable, look
 > for a debuglink section, and use it if present.  Keep the readlink
 > code in posix.c, I suppose.  Apologies if this doesn't make sense.

I've moved backtrace_open_debugfile() function into elf.c.
This function checks debug sections (build-id/debuglink),
and if one of them exist - the function starts to search and open debug 
files.

Also I'll be very appreciated if someone give me advise about issues:
1.
| Skimming over the patch I noticed you duplicate libiberties xcrc32
| functionality.

To verify that debug file is valid, we should
verify crc32 sum, I took that algorithm from gdb, and
put it in the libbacktrace sources. However
this functionality is implemented by libiberty.
In case if libbactrace is being built as a static library,
I can't just link libbacktrace against static libbiberty.
Libtool can do it with objects which has "la" suffix,
but in time when libbacktrace build process happens
libiberty.la already removed.
Should i extract *.o files from the libiberty archive
and create libbacktrace archive with crc32.o from
libiberty or just keep this code in libbacktrace ?


2.
 > > +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;
 >
 > |As far as I know "separate" is not a special thing in automake.  We
 > | should always run (and clean) this test if the necessary objcopy
 > | option is available.
 >
In the attached patch I overrode default target check-TESTS
to use objcopy and strip, before test suite will run.
But the solution to move all test suite code from Makefile.in to 
Makefile.am seems not really good.
I searched for default target, which can be run before test suite logic, 
but can't find it for Makefile.am.
Should I keep current solution, or we have better alternative?

Thanks.


On 03/14/2017 04:49 PM, Ian Lance Taylor wrote:
> On Mon, Mar 13, 2017 at 10:16 AM, Denis Khalikov
> <d.khalikov@partner.samsung.com> wrote:
>> 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
>
>> +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;
>
> As far as I know "separate" is not a special thing in automake.  We
> should always run (and clean) this test if the necessary objcopy
> option is available.
>
>> +glinktest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h
>
> Missing '$'  in "$(INCDIR)".
>
>> +/* Return 1 if header is valid and -1 on fail */
>
> This comment does not explain what the function actually does.
>
>> +/* Return the pointer to char array with data from .gnudebuglink section inside.  */
>
> Line too long, we use 80 column lines.
>
>> +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)
>
> This should be static.  I see that you are calling it elsewhere, but
> it doesn't make sense to call an "elf" function outside of elf.c.
> This library is used on non-ELF systems.
>
>> +  /* Look for for the .gnu_debuglink section  */
>
> Period at end of sentence.
>
>>    /* 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.  */
>
> Why change the indentation like this?  I think the original was correct.
>
>> -      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);
>
> This seems like unnecessary work.  Shouldn't we only try to open the
> debug file if we find a .gnu_debuglink section?
>
>> +/*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. */
>
> No leading '*' on subsequent lines of multi-line comments, see existing code.
>
> I'm not sure I see the point of glinktest.c.  Why don't we just use btest.c?
>
>> +#define MAX_PATH_LEN 4096 /*  from linux/limits.h   */
>
> This library works on systems other than GNU/Linux.  We should
> dynamically allocate the buffer.
>
>> +  while (len > 1 && !IS_DIR_SEPARATOR (*(buffer + (len - 1))))
>
> while (len > 1 && !IS_DIR_SEPARATOR(buffer[len-1]))
>
> or just call basename.  I'm not sure why you bother with the count
> variable; doesn't full_filename_len - count exactly == len?
>
>> +  sign_byte = 0x00;
>> +  count = 0;
>> +  while (*(buffer + count) != sign_byte && size > count)
>> +    ++count;
>> +  return count;
>
> This looks like `return strnlen(buffer, size)`.
>
> Ian
>
>
>

[-- Attachment #2: PR_sanitizer_77631.patch --]
[-- Type: text/x-patch, Size: 64352 bytes --]

commit 7eeee7478019bdb6c843cccd566cb4d0176ec47e
Author: Denis Khalikov <d.khalikov@partner.samsung.com>
Date:   Tue Mar 14 13:45:11 2017 +0300

    	PR sanitizer/77631
    
    	* Makefile.am: Update to support test for gnu link
    	* Makefile.in: Regenerated.
    	* configure.ac: Add searching for limits.h and sys/param.h
    	* config.h.in: Regenerated.
    	* configure: Regenerated.
    	* elf.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.
    	(elf_header_is_valid): New function. Verify elf header.
    	(elf_get_section_by_name): New function. Get section by name.
    	(backtrace_readlink): New function. Get type of file from filename.
    	(resolve_realname): New function. Resolve real name if file is link.
    	(backtrace_resolve_realname): New function. Resolve real name for any
    	file type.
    	(search_for_debugfile): New function. Search for debug file in known
    	paths.
    	(open_debugfile_by_gnulink): New function. Open debug file with
    	gnulink.
    	(hex): New function. Convert to hex.
    	(get_build_id_name): New function. Generate build-id name.
    	(open_debugfile_by_build_id): New function. Open debug file with
    	build-id.
    	(backtrace_open_debugfile): New function. Open debug file.
    	(elf_add): Move code which reads elf header to elf_header_is_valid.
    	(phdr_callback): Call backtrace_open_debugfile function for shared
    	library.
    	* fileline.c (fileline_initialize): Call backtrace_open_debugfile for
    	executable.
    	* internal.h: Updated.

diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog
index 25cd921..154be2f 100644
--- a/libbacktrace/ChangeLog
+++ b/libbacktrace/ChangeLog
@@ -1,3 +1,40 @@
+2017-03-14  Denis Khalikov  <d.khalikov@partner.samsung.com>
+
+	PR sanitizer/77631
+	* Makefile.am: Update to support test for gnu link
+	* Makefile.in: Regenerated.
+	* configure.ac: Add searching for limits.h and sys/param.h
+	* config.h.in: Regenerated.
+	* configure: Regenerated.
+	* elf.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.
+	(elf_header_is_valid): New function. Verify elf header.
+	(elf_get_section_by_name): New function. Get section by name.
+	(backtrace_readlink): New function. Get type of file from filename.
+	(resolve_realname): New function. Resolve real name if file is link.
+	(backtrace_resolve_realname): New function. Resolve real name for any
+	file type.
+	(search_for_debugfile): New function. Search for debug file in known
+	paths.
+	(open_debugfile_by_gnulink): New function. Open debug file with
+	gnulink.
+	(hex): New function. Convert to hex.
+	(get_build_id_name): New function. Generate build-id name.
+	(open_debugfile_by_build_id): New function. Open debug file with
+	build-id.
+	(backtrace_open_debugfile): New function. Open debug file.
+	(elf_add): Move code which reads elf header to elf_header_is_valid.
+	(phdr_callback): Call backtrace_open_debugfile function for shared
+	library.
+	* fileline.c (fileline_initialize): Call backtrace_open_debugfile for
+	executable.
+	* internal.h: Updated.
+
 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..548d5c7 100644
--- a/libbacktrace/Makefile.am
+++ b/libbacktrace/Makefile.am
@@ -100,8 +100,150 @@ stest_LDADD = libbacktrace.la
 
 check_PROGRAMS += stest
 
-endif NATIVE
+glinktest_SOURCES = btest.c
+glinktest_CFLAGS = $(AM_CFLAGS) -g -O
+glinktest_LDADD = libbacktrace.la
+
+check_PROGRAMS += glinktest
+
+bidtest_SOURCES = btest.c
+bidtest_CFLAGS = $(AM_CFLAGS) -g -O -Wl,--build-id=0x0123456789abcdef0123456789abcdef01234567
+bidtest_LDADD = libbacktrace.la
+
+check_PROGRAMS += bidtest
 
+check-TESTS: $(TESTS)
+	@failed=0; all=0; xfail=0; xpass=0; skip=0; \
+	srcdir=$(srcdir); export srcdir; \
+	list=' $(TESTS) '; \
+	$(am__tty_colors); \
+	if ! test -e glinktest.debug; then \
+	  $(OBJCOPY) --only-keep-debug glinktest glinktest.debug; \
+	  $(STRIP) glinktest; \
+	  $(OBJCOPY) --add-gnu-debuglink=glinktest.debug glinktest; \
+	fi; \
+	if ! test -e .build-id; then \
+	  mkdir .build-id; \
+	  mkdir .build-id/01/; \
+	  mkdir temp; \
+	  $(OBJCOPY) --only-keep-debug bidtest bidtest.debug; \
+	  $(STRIP) bidtest; \
+	  touch temp; \
+	  mv bidtest.debug temp;\
+	  ln -s ../../temp/bidtest.debug .build-id/01/23456789abcdef0123456789abcdef01234567.debug; \
+	fi; \
+	if test -n "$$list"; then \
+	  for tst in $$list; do \
+	    if test -f ./$$tst; then dir=./; \
+	    elif test -f $$tst; then dir=; \
+	    else dir="$(srcdir)/"; fi; \
+	    if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+	      all=`expr $$all + 1`; \
+	      case " $(XFAIL_TESTS) " in \
+	      *[\ \	]$$tst[\ \	]*) \
+		xpass=`expr $$xpass + 1`; \
+		failed=`expr $$failed + 1`; \
+		col=$$red; res=XPASS; \
+	      ;; \
+	      *) \
+		col=$$grn; res=PASS; \
+	      ;; \
+	      esac; \
+	    elif test $$? -ne 77; then \
+	      all=`expr $$all + 1`; \
+	      case " $(XFAIL_TESTS) " in \
+	      *[\ \	]$$tst[\ \	]*) \
+		xfail=`expr $$xfail + 1`; \
+		col=$$lgn; res=XFAIL; \
+	      ;; \
+	      *) \
+		failed=`expr $$failed + 1`; \
+		col=$$red; res=FAIL; \
+	      ;; \
+	      esac; \
+	    else \
+	      skip=`expr $$skip + 1`; \
+	      col=$$blu; res=SKIP; \
+	    fi; \
+	    echo "$${col}$$res$${std}: $$tst"; \
+	  done; \
+	  if test "$$all" -eq 1; then \
+	    tests="test"; \
+	    All=""; \
+	  else \
+	    tests="tests"; \
+	    All="All "; \
+	  fi; \
+	  if test "$$failed" -eq 0; then \
+	    if test "$$xfail" -eq 0; then \
+	      banner="$$All$$all $$tests passed"; \
+	    else \
+	      if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+	      banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+	    fi; \
+	  else \
+	    if test "$$xpass" -eq 0; then \
+	      banner="$$failed of $$all $$tests failed"; \
+	    else \
+	      if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+	      banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+	    fi; \
+	  fi; \
+	  dashes="$$banner"; \
+	  skipped=""; \
+	  if test "$$skip" -ne 0; then \
+	    if test "$$skip" -eq 1; then \
+	      skipped="($$skip test was not run)"; \
+	    else \
+	      skipped="($$skip tests were not run)"; \
+	    fi; \
+	    test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+	      dashes="$$skipped"; \
+	  fi; \
+	  report=""; \
+	  if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+	    report="Please report to $(PACKAGE_BUGREPORT)"; \
+	    test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+	      dashes="$$report"; \
+	  fi; \
+	  dashes=`echo "$$dashes" | sed s/./=/g`; \
+	  if test "$$failed" -eq 0; then \
+	    col="$$grn"; \
+	  else \
+	    col="$$red"; \
+	  fi; \
+	  echo "$${col}$$dashes$${std}"; \
+	  echo "$${col}$$banner$${std}"; \
+	  test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+	  test -z "$$report" || echo "$${col}$$report$${std}"; \
+	  echo "$${col}$$dashes$${std}"; \
+	  test "$$failed" -eq 0; \
+	else :; fi
+
+
+clean-checkPROGRAMS:
+	@if test -e glinktest.debug; then \
+	 echo "rm -f glinktest.debug"; \
+	 rm -f glinktest.debug; \
+	fi; \
+	if test -d temp; then \
+	 echo "rm -rf temp"; \
+	 rm -rf temp; \
+	fi; \
+	if test -d .build-id; then \
+	  echo "rm -rf .build-id"; \
+	  rm -rf .build-id; \
+	fi; \
+	list='$(check_PROGRAMS)'; \
+	test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list;
+	
+endif NATIVE
 # We can't use automake's automatic dependency tracking, because it
 # breaks when using bootstrap-lean.  Automatic dependency tracking
 # with GCC bootstrap will cause some of the objects to depend on
diff --git a/libbacktrace/Makefile.in b/libbacktrace/Makefile.in
index de74b5d..a5b8fe5 100644
--- a/libbacktrace/Makefile.in
+++ b/libbacktrace/Makefile.in
@@ -16,7 +16,7 @@
 @SET_MAKE@
 
 # Makefile.am -- Backtrace Makefile.
-# Copyright (C) 2012-2016 Free Software Foundation, Inc.
+# Copyright (C) 2012-2017 Free Software Foundation, Inc.
 
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -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 bidtest
 subdir = .
 DIST_COMMON = README ChangeLog $(srcdir)/Makefile.in \
 	$(srcdir)/Makefile.am $(top_srcdir)/configure \
@@ -113,13 +113,26 @@ 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) bidtest$(EXEEXT)
+@NATIVE_TRUE@am_bidtest_OBJECTS = bidtest-btest.$(OBJEXT)
+bidtest_OBJECTS = $(am_bidtest_OBJECTS)
+@NATIVE_TRUE@bidtest_DEPENDENCIES = libbacktrace.la
+bidtest_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+	--mode=link $(CCLD) $(bidtest_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
 @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-btest.$(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 +149,8 @@ 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)
+	$(bidtest_SOURCES) $(btest_SOURCES) $(glinktest_SOURCES) \
+	$(stest_SOURCES)
 MULTISRCTOP = 
 MULTIBUILDTOP = 
 MULTIDIRS = 
@@ -200,6 +214,7 @@ MAKEINFO = @MAKEINFO@
 MKDIR_P = @MKDIR_P@
 NM = @NM@
 NMEDIT = @NMEDIT@
+OBJCOPY = @OBJCOPY@
 OBJDUMP = @OBJDUMP@
 OBJEXT = @OBJEXT@
 OTOOL = @OTOOL@
@@ -330,6 +345,12 @@ 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 = btest.c
+@NATIVE_TRUE@glinktest_CFLAGS = $(AM_CFLAGS) -g -O
+@NATIVE_TRUE@glinktest_LDADD = libbacktrace.la
+@NATIVE_TRUE@bidtest_SOURCES = btest.c
+@NATIVE_TRUE@bidtest_CFLAGS = $(AM_CFLAGS) -g -O -Wl,--build-id=0x0123456789abcdef0123456789abcdef01234567
+@NATIVE_TRUE@bidtest_LDADD = libbacktrace.la
 
 # We can't use automake's automatic dependency tracking, because it
 # breaks when using bootstrap-lean.  Automatic dependency tracking
@@ -411,17 +432,23 @@ clean-noinstLTLIBRARIES:
 libbacktrace.la: $(libbacktrace_la_OBJECTS) $(libbacktrace_la_DEPENDENCIES) $(EXTRA_libbacktrace_la_DEPENDENCIES) 
 	$(LINK)  $(libbacktrace_la_OBJECTS) $(libbacktrace_la_LIBADD) $(LIBS)
 
-clean-checkPROGRAMS:
-	@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
-	echo " rm -f" $$list; \
-	rm -f $$list || exit $$?; \
-	test -n "$(EXEEXT)" || exit 0; \
-	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
-	echo " rm -f" $$list; \
-	rm -f $$list
+@NATIVE_FALSE@clean-checkPROGRAMS:
+@NATIVE_FALSE@	@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+@NATIVE_FALSE@	echo " rm -f" $$list; \
+@NATIVE_FALSE@	rm -f $$list || exit $$?; \
+@NATIVE_FALSE@	test -n "$(EXEEXT)" || exit 0; \
+@NATIVE_FALSE@	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+@NATIVE_FALSE@	echo " rm -f" $$list; \
+@NATIVE_FALSE@	rm -f $$list
+bidtest$(EXEEXT): $(bidtest_OBJECTS) $(bidtest_DEPENDENCIES) $(EXTRA_bidtest_DEPENDENCIES) 
+	@rm -f bidtest$(EXEEXT)
+	$(bidtest_LINK) $(bidtest_OBJECTS) $(bidtest_LDADD) $(LIBS)
 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)
@@ -441,12 +468,24 @@ distclean-compile:
 .c.lo:
 	$(LTCOMPILE) -c -o $@ $<
 
+bidtest-btest.o: btest.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bidtest_CFLAGS) $(CFLAGS) -c -o bidtest-btest.o `test -f 'btest.c' || echo '$(srcdir)/'`btest.c
+
+bidtest-btest.obj: btest.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(bidtest_CFLAGS) $(CFLAGS) -c -o bidtest-btest.obj `if test -f 'btest.c'; then $(CYGPATH_W) 'btest.c'; else $(CYGPATH_W) '$(srcdir)/btest.c'; fi`
+
 btest-btest.o: btest.c
 	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(btest_CFLAGS) $(CFLAGS) -c -o btest-btest.o `test -f 'btest.c' || echo '$(srcdir)/'`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-btest.o: btest.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(glinktest_CFLAGS) $(CFLAGS) -c -o glinktest-btest.o `test -f 'btest.c' || echo '$(srcdir)/'`btest.c
+
+glinktest-btest.obj: btest.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(glinktest_CFLAGS) $(CFLAGS) -c -o glinktest-btest.obj `if test -f 'btest.c'; then $(CYGPATH_W) 'btest.c'; else $(CYGPATH_W) '$(srcdir)/btest.c'; fi`
+
 mostlyclean-libtool:
 	-rm -f *.lo
 
@@ -525,98 +564,98 @@ GTAGS:
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
-check-TESTS: $(TESTS)
-	@failed=0; all=0; xfail=0; xpass=0; skip=0; \
-	srcdir=$(srcdir); export srcdir; \
-	list=' $(TESTS) '; \
-	$(am__tty_colors); \
-	if test -n "$$list"; then \
-	  for tst in $$list; do \
-	    if test -f ./$$tst; then dir=./; \
-	    elif test -f $$tst; then dir=; \
-	    else dir="$(srcdir)/"; fi; \
-	    if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
-	      all=`expr $$all + 1`; \
-	      case " $(XFAIL_TESTS) " in \
-	      *[\ \	]$$tst[\ \	]*) \
-		xpass=`expr $$xpass + 1`; \
-		failed=`expr $$failed + 1`; \
-		col=$$red; res=XPASS; \
-	      ;; \
-	      *) \
-		col=$$grn; res=PASS; \
-	      ;; \
-	      esac; \
-	    elif test $$? -ne 77; then \
-	      all=`expr $$all + 1`; \
-	      case " $(XFAIL_TESTS) " in \
-	      *[\ \	]$$tst[\ \	]*) \
-		xfail=`expr $$xfail + 1`; \
-		col=$$lgn; res=XFAIL; \
-	      ;; \
-	      *) \
-		failed=`expr $$failed + 1`; \
-		col=$$red; res=FAIL; \
-	      ;; \
-	      esac; \
-	    else \
-	      skip=`expr $$skip + 1`; \
-	      col=$$blu; res=SKIP; \
-	    fi; \
-	    echo "$${col}$$res$${std}: $$tst"; \
-	  done; \
-	  if test "$$all" -eq 1; then \
-	    tests="test"; \
-	    All=""; \
-	  else \
-	    tests="tests"; \
-	    All="All "; \
-	  fi; \
-	  if test "$$failed" -eq 0; then \
-	    if test "$$xfail" -eq 0; then \
-	      banner="$$All$$all $$tests passed"; \
-	    else \
-	      if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
-	      banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
-	    fi; \
-	  else \
-	    if test "$$xpass" -eq 0; then \
-	      banner="$$failed of $$all $$tests failed"; \
-	    else \
-	      if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
-	      banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
-	    fi; \
-	  fi; \
-	  dashes="$$banner"; \
-	  skipped=""; \
-	  if test "$$skip" -ne 0; then \
-	    if test "$$skip" -eq 1; then \
-	      skipped="($$skip test was not run)"; \
-	    else \
-	      skipped="($$skip tests were not run)"; \
-	    fi; \
-	    test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
-	      dashes="$$skipped"; \
-	  fi; \
-	  report=""; \
-	  if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
-	    report="Please report to $(PACKAGE_BUGREPORT)"; \
-	    test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
-	      dashes="$$report"; \
-	  fi; \
-	  dashes=`echo "$$dashes" | sed s/./=/g`; \
-	  if test "$$failed" -eq 0; then \
-	    col="$$grn"; \
-	  else \
-	    col="$$red"; \
-	  fi; \
-	  echo "$${col}$$dashes$${std}"; \
-	  echo "$${col}$$banner$${std}"; \
-	  test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
-	  test -z "$$report" || echo "$${col}$$report$${std}"; \
-	  echo "$${col}$$dashes$${std}"; \
-	  test "$$failed" -eq 0; \
-	else :; fi
+@NATIVE_FALSE@check-TESTS: $(TESTS)
+@NATIVE_FALSE@	@failed=0; all=0; xfail=0; xpass=0; skip=0; \
+@NATIVE_FALSE@	srcdir=$(srcdir); export srcdir; \
+@NATIVE_FALSE@	list=' $(TESTS) '; \
+@NATIVE_FALSE@	$(am__tty_colors); \
+@NATIVE_FALSE@	if test -n "$$list"; then \
+@NATIVE_FALSE@	  for tst in $$list; do \
+@NATIVE_FALSE@	    if test -f ./$$tst; then dir=./; \
+@NATIVE_FALSE@	    elif test -f $$tst; then dir=; \
+@NATIVE_FALSE@	    else dir="$(srcdir)/"; fi; \
+@NATIVE_FALSE@	    if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+@NATIVE_FALSE@	      all=`expr $$all + 1`; \
+@NATIVE_FALSE@	      case " $(XFAIL_TESTS) " in \
+@NATIVE_FALSE@	      *[\ \	]$$tst[\ \	]*) \
+@NATIVE_FALSE@		xpass=`expr $$xpass + 1`; \
+@NATIVE_FALSE@		failed=`expr $$failed + 1`; \
+@NATIVE_FALSE@		col=$$red; res=XPASS; \
+@NATIVE_FALSE@	      ;; \
+@NATIVE_FALSE@	      *) \
+@NATIVE_FALSE@		col=$$grn; res=PASS; \
+@NATIVE_FALSE@	      ;; \
+@NATIVE_FALSE@	      esac; \
+@NATIVE_FALSE@	    elif test $$? -ne 77; then \
+@NATIVE_FALSE@	      all=`expr $$all + 1`; \
+@NATIVE_FALSE@	      case " $(XFAIL_TESTS) " in \
+@NATIVE_FALSE@	      *[\ \	]$$tst[\ \	]*) \
+@NATIVE_FALSE@		xfail=`expr $$xfail + 1`; \
+@NATIVE_FALSE@		col=$$lgn; res=XFAIL; \
+@NATIVE_FALSE@	      ;; \
+@NATIVE_FALSE@	      *) \
+@NATIVE_FALSE@		failed=`expr $$failed + 1`; \
+@NATIVE_FALSE@		col=$$red; res=FAIL; \
+@NATIVE_FALSE@	      ;; \
+@NATIVE_FALSE@	      esac; \
+@NATIVE_FALSE@	    else \
+@NATIVE_FALSE@	      skip=`expr $$skip + 1`; \
+@NATIVE_FALSE@	      col=$$blu; res=SKIP; \
+@NATIVE_FALSE@	    fi; \
+@NATIVE_FALSE@	    echo "$${col}$$res$${std}: $$tst"; \
+@NATIVE_FALSE@	  done; \
+@NATIVE_FALSE@	  if test "$$all" -eq 1; then \
+@NATIVE_FALSE@	    tests="test"; \
+@NATIVE_FALSE@	    All=""; \
+@NATIVE_FALSE@	  else \
+@NATIVE_FALSE@	    tests="tests"; \
+@NATIVE_FALSE@	    All="All "; \
+@NATIVE_FALSE@	  fi; \
+@NATIVE_FALSE@	  if test "$$failed" -eq 0; then \
+@NATIVE_FALSE@	    if test "$$xfail" -eq 0; then \
+@NATIVE_FALSE@	      banner="$$All$$all $$tests passed"; \
+@NATIVE_FALSE@	    else \
+@NATIVE_FALSE@	      if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+@NATIVE_FALSE@	      banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+@NATIVE_FALSE@	    fi; \
+@NATIVE_FALSE@	  else \
+@NATIVE_FALSE@	    if test "$$xpass" -eq 0; then \
+@NATIVE_FALSE@	      banner="$$failed of $$all $$tests failed"; \
+@NATIVE_FALSE@	    else \
+@NATIVE_FALSE@	      if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+@NATIVE_FALSE@	      banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+@NATIVE_FALSE@	    fi; \
+@NATIVE_FALSE@	  fi; \
+@NATIVE_FALSE@	  dashes="$$banner"; \
+@NATIVE_FALSE@	  skipped=""; \
+@NATIVE_FALSE@	  if test "$$skip" -ne 0; then \
+@NATIVE_FALSE@	    if test "$$skip" -eq 1; then \
+@NATIVE_FALSE@	      skipped="($$skip test was not run)"; \
+@NATIVE_FALSE@	    else \
+@NATIVE_FALSE@	      skipped="($$skip tests were not run)"; \
+@NATIVE_FALSE@	    fi; \
+@NATIVE_FALSE@	    test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+@NATIVE_FALSE@	      dashes="$$skipped"; \
+@NATIVE_FALSE@	  fi; \
+@NATIVE_FALSE@	  report=""; \
+@NATIVE_FALSE@	  if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+@NATIVE_FALSE@	    report="Please report to $(PACKAGE_BUGREPORT)"; \
+@NATIVE_FALSE@	    test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+@NATIVE_FALSE@	      dashes="$$report"; \
+@NATIVE_FALSE@	  fi; \
+@NATIVE_FALSE@	  dashes=`echo "$$dashes" | sed s/./=/g`; \
+@NATIVE_FALSE@	  if test "$$failed" -eq 0; then \
+@NATIVE_FALSE@	    col="$$grn"; \
+@NATIVE_FALSE@	  else \
+@NATIVE_FALSE@	    col="$$red"; \
+@NATIVE_FALSE@	  fi; \
+@NATIVE_FALSE@	  echo "$${col}$$dashes$${std}"; \
+@NATIVE_FALSE@	  echo "$${col}$$banner$${std}"; \
+@NATIVE_FALSE@	  test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+@NATIVE_FALSE@	  test -z "$$report" || echo "$${col}$$report$${std}"; \
+@NATIVE_FALSE@	  echo "$${col}$$dashes$${std}"; \
+@NATIVE_FALSE@	  test "$$failed" -eq 0; \
+@NATIVE_FALSE@	else :; fi
 check-am: all-am
 	$(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
 	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
@@ -745,6 +784,136 @@ uninstall-am:
 	mostlyclean-multi pdf pdf-am ps ps-am tags uninstall \
 	uninstall-am
 
+
+@NATIVE_TRUE@check-TESTS: $(TESTS)
+@NATIVE_TRUE@	@failed=0; all=0; xfail=0; xpass=0; skip=0; \
+@NATIVE_TRUE@	srcdir=$(srcdir); export srcdir; \
+@NATIVE_TRUE@	list=' $(TESTS) '; \
+@NATIVE_TRUE@	$(am__tty_colors); \
+@NATIVE_TRUE@	if ! test -e glinktest.debug; then \
+@NATIVE_TRUE@	  $(OBJCOPY) --only-keep-debug glinktest glinktest.debug; \
+@NATIVE_TRUE@	  $(STRIP) glinktest; \
+@NATIVE_TRUE@	  $(OBJCOPY) --add-gnu-debuglink=glinktest.debug glinktest; \
+@NATIVE_TRUE@	fi; \
+@NATIVE_TRUE@	if ! test -e .build-id; then \
+@NATIVE_TRUE@	  mkdir .build-id; \
+@NATIVE_TRUE@	  mkdir .build-id/01/; \
+@NATIVE_TRUE@	  mkdir temp; \
+@NATIVE_TRUE@	  $(OBJCOPY) --only-keep-debug bidtest bidtest.debug; \
+@NATIVE_TRUE@	  $(STRIP) bidtest; \
+@NATIVE_TRUE@	  touch temp; \
+@NATIVE_TRUE@	  mv bidtest.debug temp;\
+@NATIVE_TRUE@	  ln -s ../../temp/bidtest.debug .build-id/01/23456789abcdef0123456789abcdef01234567.debug; \
+@NATIVE_TRUE@	fi; \
+@NATIVE_TRUE@	if test -n "$$list"; then \
+@NATIVE_TRUE@	  for tst in $$list; do \
+@NATIVE_TRUE@	    if test -f ./$$tst; then dir=./; \
+@NATIVE_TRUE@	    elif test -f $$tst; then dir=; \
+@NATIVE_TRUE@	    else dir="$(srcdir)/"; fi; \
+@NATIVE_TRUE@	    if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+@NATIVE_TRUE@	      all=`expr $$all + 1`; \
+@NATIVE_TRUE@	      case " $(XFAIL_TESTS) " in \
+@NATIVE_TRUE@	      *[\ \	]$$tst[\ \	]*) \
+@NATIVE_TRUE@		xpass=`expr $$xpass + 1`; \
+@NATIVE_TRUE@		failed=`expr $$failed + 1`; \
+@NATIVE_TRUE@		col=$$red; res=XPASS; \
+@NATIVE_TRUE@	      ;; \
+@NATIVE_TRUE@	      *) \
+@NATIVE_TRUE@		col=$$grn; res=PASS; \
+@NATIVE_TRUE@	      ;; \
+@NATIVE_TRUE@	      esac; \
+@NATIVE_TRUE@	    elif test $$? -ne 77; then \
+@NATIVE_TRUE@	      all=`expr $$all + 1`; \
+@NATIVE_TRUE@	      case " $(XFAIL_TESTS) " in \
+@NATIVE_TRUE@	      *[\ \	]$$tst[\ \	]*) \
+@NATIVE_TRUE@		xfail=`expr $$xfail + 1`; \
+@NATIVE_TRUE@		col=$$lgn; res=XFAIL; \
+@NATIVE_TRUE@	      ;; \
+@NATIVE_TRUE@	      *) \
+@NATIVE_TRUE@		failed=`expr $$failed + 1`; \
+@NATIVE_TRUE@		col=$$red; res=FAIL; \
+@NATIVE_TRUE@	      ;; \
+@NATIVE_TRUE@	      esac; \
+@NATIVE_TRUE@	    else \
+@NATIVE_TRUE@	      skip=`expr $$skip + 1`; \
+@NATIVE_TRUE@	      col=$$blu; res=SKIP; \
+@NATIVE_TRUE@	    fi; \
+@NATIVE_TRUE@	    echo "$${col}$$res$${std}: $$tst"; \
+@NATIVE_TRUE@	  done; \
+@NATIVE_TRUE@	  if test "$$all" -eq 1; then \
+@NATIVE_TRUE@	    tests="test"; \
+@NATIVE_TRUE@	    All=""; \
+@NATIVE_TRUE@	  else \
+@NATIVE_TRUE@	    tests="tests"; \
+@NATIVE_TRUE@	    All="All "; \
+@NATIVE_TRUE@	  fi; \
+@NATIVE_TRUE@	  if test "$$failed" -eq 0; then \
+@NATIVE_TRUE@	    if test "$$xfail" -eq 0; then \
+@NATIVE_TRUE@	      banner="$$All$$all $$tests passed"; \
+@NATIVE_TRUE@	    else \
+@NATIVE_TRUE@	      if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+@NATIVE_TRUE@	      banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+@NATIVE_TRUE@	    fi; \
+@NATIVE_TRUE@	  else \
+@NATIVE_TRUE@	    if test "$$xpass" -eq 0; then \
+@NATIVE_TRUE@	      banner="$$failed of $$all $$tests failed"; \
+@NATIVE_TRUE@	    else \
+@NATIVE_TRUE@	      if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+@NATIVE_TRUE@	      banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+@NATIVE_TRUE@	    fi; \
+@NATIVE_TRUE@	  fi; \
+@NATIVE_TRUE@	  dashes="$$banner"; \
+@NATIVE_TRUE@	  skipped=""; \
+@NATIVE_TRUE@	  if test "$$skip" -ne 0; then \
+@NATIVE_TRUE@	    if test "$$skip" -eq 1; then \
+@NATIVE_TRUE@	      skipped="($$skip test was not run)"; \
+@NATIVE_TRUE@	    else \
+@NATIVE_TRUE@	      skipped="($$skip tests were not run)"; \
+@NATIVE_TRUE@	    fi; \
+@NATIVE_TRUE@	    test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+@NATIVE_TRUE@	      dashes="$$skipped"; \
+@NATIVE_TRUE@	  fi; \
+@NATIVE_TRUE@	  report=""; \
+@NATIVE_TRUE@	  if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+@NATIVE_TRUE@	    report="Please report to $(PACKAGE_BUGREPORT)"; \
+@NATIVE_TRUE@	    test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+@NATIVE_TRUE@	      dashes="$$report"; \
+@NATIVE_TRUE@	  fi; \
+@NATIVE_TRUE@	  dashes=`echo "$$dashes" | sed s/./=/g`; \
+@NATIVE_TRUE@	  if test "$$failed" -eq 0; then \
+@NATIVE_TRUE@	    col="$$grn"; \
+@NATIVE_TRUE@	  else \
+@NATIVE_TRUE@	    col="$$red"; \
+@NATIVE_TRUE@	  fi; \
+@NATIVE_TRUE@	  echo "$${col}$$dashes$${std}"; \
+@NATIVE_TRUE@	  echo "$${col}$$banner$${std}"; \
+@NATIVE_TRUE@	  test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+@NATIVE_TRUE@	  test -z "$$report" || echo "$${col}$$report$${std}"; \
+@NATIVE_TRUE@	  echo "$${col}$$dashes$${std}"; \
+@NATIVE_TRUE@	  test "$$failed" -eq 0; \
+@NATIVE_TRUE@	else :; fi
+
+@NATIVE_TRUE@clean-checkPROGRAMS:
+@NATIVE_TRUE@	@if test -e glinktest.debug; then \
+@NATIVE_TRUE@	 echo "rm -f glinktest.debug"; \
+@NATIVE_TRUE@	 rm -f glinktest.debug; \
+@NATIVE_TRUE@	fi; \
+@NATIVE_TRUE@	if test -d temp; then \
+@NATIVE_TRUE@	 echo "rm -rf temp"; \
+@NATIVE_TRUE@	 rm -rf temp; \
+@NATIVE_TRUE@	fi; \
+@NATIVE_TRUE@	if test -d .build-id; then \
+@NATIVE_TRUE@	  echo "rm -rf .build-id"; \
+@NATIVE_TRUE@	  rm -rf .build-id; \
+@NATIVE_TRUE@	fi; \
+@NATIVE_TRUE@	list='$(check_PROGRAMS)'; \
+@NATIVE_TRUE@	test -n "$$list" || exit 0; \
+@NATIVE_TRUE@	echo " rm -f" $$list; \
+@NATIVE_TRUE@	rm -f $$list || exit $$?; \
+@NATIVE_TRUE@	test -n "$(EXEEXT)" || exit 0; \
+@NATIVE_TRUE@	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+@NATIVE_TRUE@	echo " rm -f" $$list; \
+@NATIVE_TRUE@	rm -f $$list;
 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
diff --git a/libbacktrace/config.h.in b/libbacktrace/config.h.in
index 87cb805..edbd9af 100644
--- a/libbacktrace/config.h.in
+++ b/libbacktrace/config.h.in
@@ -28,6 +28,9 @@
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
+/* Define 1 if <limits.h> is available. */
+#undef HAVE_LIMITS_H
+
 /* Define to 1 if you have the <link.h> header file. */
 #undef HAVE_LINK_H
 
@@ -52,6 +55,9 @@
 /* Define to 1 if you have the <sys/mman.h> header file. */
 #undef HAVE_SYS_MMAN_H
 
+/* Define 1 if <sys/param.h> is available. */
+#undef HAVE_SYS_PARAM_H
+
 /* Define to 1 if you have the <sys/stat.h> header file. */
 #undef HAVE_SYS_STAT_H
 
diff --git a/libbacktrace/configure b/libbacktrace/configure
index ee90bc6..562cede 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
@@ -12350,6 +12389,24 @@ if test "$ALLOC_FILE" = "alloc.lo"; then
 fi
 
 
+ac_fn_c_check_header_mongrel "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default"
+if test "x$ac_cv_header_limits_h" = x""yes; then :
+
+$as_echo "#define HAVE_LIMITS_H 1" >>confdefs.h
+
+fi
+
+
+
+ac_fn_c_check_header_mongrel "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_param_h" = x""yes; then :
+
+$as_echo "#define HAVE_SYS_PARAM_H 1" >>confdefs.h
+
+fi
+
+
+
 # Check for dl_iterate_phdr.
 for ac_header in link.h
 do :
diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac
index f9cad21..67cce41 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]) ;;
@@ -301,6 +303,12 @@ if test "$ALLOC_FILE" = "alloc.lo"; then
 fi
 AC_SUBST(BACKTRACE_USES_MALLOC)
 
+AC_CHECK_HEADER(limits.h,
+AC_DEFINE(HAVE_LIMITS_H, 1,[Define 1 if <limits.h> is available.]))
+
+AC_CHECK_HEADER(sys/param.h,
+AC_DEFINE(HAVE_SYS_PARAM_H, 1,[Define 1 if <sys/param.h> is available.]))
+
 # Check for dl_iterate_phdr.
 AC_CHECK_HEADERS(link.h)
 if test "$ac_cv_header_link_h" = "no"; then
diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c
index 89ed42b..3f33d51 100644
--- a/libbacktrace/elf.c
+++ b/libbacktrace/elf.c
@@ -35,6 +35,9 @@ POSSIBILITY OF SUCH DAMAGE.  */
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
 
 #ifdef HAVE_DL_ITERATE_PHDR
 #include <link.h>
@@ -42,6 +45,34 @@ POSSIBILITY OF SUCH DAMAGE.  */
 
 #include "backtrace.h"
 #include "internal.h"
+#include "filenames.h"
+
+
+/* The following is from pathmax.h.  */
+/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
+   PATH_MAX but might cause redefinition warnings when sys/param.h is
+   later included (as on MORE/BSD 4.3).  */
+#if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
+# include <limits.h>
+#endif
+
+#ifndef _POSIX_PATH_MAX
+# define _POSIX_PATH_MAX 255
+#endif
+
+/* Don't include sys/param.h if it already has been.  */
+#if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
+# include <sys/param.h>
+#endif
+
+#if !defined PATH_MAX && defined MAXPATHLEN
+# define PATH_MAX MAXPATHLEN
+#endif
+
+#ifndef PATH_MAX
+# define PATH_MAX _POSIX_PATH_MAX
+#endif
+
 
 #ifndef HAVE_DL_ITERATE_PHDR
 
@@ -283,6 +314,827 @@ struct elf_syminfo_data
   size_t count;
 };
 
+/* Information to read ELF note section */
+
+typedef struct
+{
+  unsigned char namesz[4];
+  unsigned char descsz[4];
+  unsigned char type[4];
+  unsigned char name[1];
+} Elf_External_Note;
+
+/* Information about type of the file */
+
+enum type_of_file
+{
+  LINK = 1,
+  REGULAR = 2
+};
+
+/* Information about debug paths */
+
+enum debug_path
+{
+  CURRENT,
+  CURRENT_DEBUG,
+  USR_LIB_DEBUG,
+  USR_LIB_DEBUG_PATH_TO_EXE,
+  DEBUG_PATH_MAX
+};
+
+/* Paths to debug file */
+
+static const char *const debug_file_path[DEBUG_PATH_MAX]
+  = {"", ".debug/", "/usr/lib/debug/", "/usr/lib/debug"};
+
+
+/* Cast from void pointer to uint32_t */
+
+static uint32_t
+getl32 (void *p)
+{
+  char *addr = (char *) p;
+  uint32_t v = 0;
+  v = *((uint32_t *) addr);
+  return v;
+}
+
+/* Function that produce crc32 value for input buffer */
+
+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;
+}
+
+/* Generate crc32 sum from the file */
+
+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;
+}
+
+/* Get length of the path */
+
+static int
+pathlen (const char *buffer)
+{
+  int len;
+  int full_filename_len;
+
+  len = full_filename_len = strlen (buffer);
+  while (len > 1 && !IS_DIR_SEPARATOR (buffer[len - 1]))
+    --len;
+  return len > 1 ? len : -1;
+}
+
+/* Verify crc32 sum */
+
+static int
+check_sum (int descriptor, char *debug_link, int offset, 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;
+}
+
+/* Verify magic number, version, etc, of the ELF header,
+   return 1 if header is valid and -1 on fail */
+
+static int
+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;
+
+  if (!backtrace_get_view (state, descriptor, 0, sizeof *ehdr_out,
+			   error_callback, data, &ehdr_view))
+    goto fail;
+
+  memcpy (ehdr_out, ehdr_view.data, sizeof *ehdr_out);
+
+  backtrace_release_view (state, &ehdr_view, error_callback, data);
+
+  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_out->e_ident[EI_VERSION] != EV_CURRENT)
+    {
+      error_callback (data, "executable file is unrecognized ELF version", 0);
+      goto fail;
+    }
+
+#if BACKTRACE_ELF_SIZE == 32
+#define BACKTRACE_ELFCLASS ELFCLASS32
+#else
+#define BACKTRACE_ELFCLASS ELFCLASS64
+#endif
+
+  if (ehdr_out->e_ident[EI_CLASS] != BACKTRACE_ELFCLASS)
+    {
+      error_callback (data, "executable file is unexpected ELF class", 0);
+      goto fail;
+    }
+
+  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;
+    }
+
+  /* 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_out->e_type == ET_DYN)
+    return -1;
+
+  *shoff_out = ehdr_out->e_shoff;
+  *shnum_out = ehdr_out->e_shnum;
+  *shstrndx_out = ehdr_out->e_shstrndx;
+
+  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_out, sizeof shdr,
+			       error_callback, data, &shdr_view))
+	goto fail;
+
+      shdr = (const b_elf_shdr *) shdr_view.data;
+
+      if (*shnum_out == 0)
+	*shnum_out = shdr->sh_size;
+
+      if (*shstrndx_out == SHN_XINDEX)
+	{
+	  *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
+	     correctly.  All large section indexes were offset by
+	     0x100.  There is more information at
+	     http://sourceware.org/bugzilla/show_bug.cgi?id-5900 .
+	     Fortunately these object files are easy to detect, as the
+	     GNU binutils always put the section header string table
+	     near the end of the list of sections.  Thus if the
+	     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_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;
+}
+
+
+/* Get content of the specifying section */
+
+static unsigned char *
+elf_get_section_by_name (struct backtrace_state *state, int descriptor,
+			 backtrace_error_callback error_callback, void *data,
+			 int exe, int *section_data_len_out,
+			 const char *section_name)
+{
+  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 section_view;
+  int names_view_valid;
+  const char *names;
+  unsigned int i;
+  int section_view_valid;
+  unsigned char *section_data;
+
+  section_view_valid = 0;
+  shdrs_view_valid = 0;
+  names_view_valid = 0;
+  section_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, section_name) == 0)
+	{
+	  if (backtrace_get_view (state, descriptor, shdr->sh_offset,
+				  shdr->sh_size, error_callback, data,
+				  &section_view))
+	    {
+
+	      section_view_valid = 1;
+	      section_data
+		= backtrace_alloc (state, shdr->sh_size, error_callback, data);
+	      if (section_data == NULL)
+		goto exit;
+	      memcpy (section_data, section_view.data, shdr->sh_size);
+	      *section_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 (section_view_valid)
+    backtrace_release_view (state, &section_view, error_callback, data);
+  return section_data;
+}
+
+
+/* Verify type of the file */
+
+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;
+}
+
+/* Resolve full name of the link. In this case we can't use realpath function
+   because it could be undefined on some platfroms, also it allocates memory
+   by malloc, which we can't use. */
+
+static int
+resolve_realname (const char *filename, char *buffer, int *does_not_exist,
+		  struct backtrace_state *state,
+		  backtrace_error_callback error_callback, void *data)
+{
+  char *temp_buffer;
+  int file_type;
+  int filename_len;
+  int temp_filename_len;
+  int valid_temp_buffer;
+  int path_len;
+
+  valid_temp_buffer = 0;
+  filename_len = -1;
+  file_type = LINK;
+
+  /* allocate memory for sizeof(PATH_MAX) + 1 bytes because at this time
+     we don't know how long path could be */
+  temp_buffer = backtrace_alloc (state, PATH_MAX + 1, error_callback, data);
+  if (temp_buffer == NULL)
+    return -1;
+
+  valid_temp_buffer = 1;
+
+  memset (temp_buffer, 0, PATH_MAX + 1);
+  memcpy (temp_buffer, filename, strlen (filename));
+
+  while (file_type == LINK)
+    {
+      filename_len = readlink (temp_buffer, buffer, PATH_MAX);
+      if (filename_len < 1)
+	goto exit;
+
+      temp_filename_len = strlen (buffer);
+
+      /* full path */
+      if (buffer[0] == '/')
+	{
+	  memset (temp_buffer, 0, PATH_MAX);
+	  memcpy (temp_buffer, buffer, temp_filename_len);
+	}
+      else
+	{
+	  /* relative path */
+	  path_len = pathlen (temp_buffer);
+	  if (path_len < 1)
+	    goto exit;
+
+	  memcpy (temp_buffer + path_len, buffer, filename_len);
+	  temp_buffer[path_len + filename_len] = '\0';
+	}
+
+      file_type = backtrace_readlink (temp_buffer, error_callback,
+				      does_not_exist, data);
+      memset (buffer, 0, filename_len);
+    }
+
+  if (file_type != REGULAR)
+    {
+      filename_len = -1;
+      goto exit;
+    }
+
+  filename_len = strlen (temp_buffer);
+  memcpy (buffer, temp_buffer, filename_len);
+
+exit:
+  if (valid_temp_buffer)
+    backtrace_free (state, temp_buffer, PATH_MAX + 1, error_callback, data);
+  return filename_len;
+}
+
+/* Resolve realname of the filename. This function verifies filename.
+   If filename is name of the file it populate realname buffer.
+   If filename is link, it calls resolve_realname function. */
+
+static char *
+backtrace_resolve_realname (const char *filename, int *filename_len,
+			    struct backtrace_state *state,
+			    backtrace_error_callback error_callback, void *data,
+			    int *does_not_exist)
+{
+  int file_type;
+  char *realname;
+
+  *filename_len = -1;
+
+  realname = backtrace_alloc (state, PATH_MAX + 1, error_callback, data);
+  if (realname == NULL)
+    goto exit;
+
+  file_type
+    = backtrace_readlink (filename, error_callback, does_not_exist, data);
+
+  if (file_type == LINK)
+    {
+      /* read the actual filename */
+      *filename_len = resolve_realname (filename, realname, does_not_exist,
+					state, error_callback, data);
+      if (*filename_len < 0)
+	goto exit;
+    }
+  else if (file_type == REGULAR)
+    {
+      *filename_len = strlen (filename);
+      if (*filename_len > PATH_MAX)
+	goto exit;
+
+      memcpy (realname, filename, *filename_len);
+    }
+
+exit:
+  return realname;
+}
+
+/* Search for debug file into specifying directorires */
+
+static int
+search_for_debugfile (char *realname, char *debug_filename,
+		      backtrace_error_callback error_callback, void *data,
+		      struct backtrace_state *state)
+{
+  int debug_filename_len;
+  int pass;
+  int debug_path_len;
+  int debug_does_not_exist;
+  int debug_descriptor;
+  int path_len;
+  char *buffer;
+  int buffer_len;
+  int valid_buffer;
+
+  debug_descriptor = -1;
+  valid_buffer = 0;
+
+  path_len = pathlen (realname);
+  debug_filename_len = strlen ((const char *) debug_filename);
+
+  if (debug_filename_len < 1)
+    goto exit;
+
+  buffer_len = path_len + strlen (debug_file_path[USR_LIB_DEBUG])
+	       + debug_filename_len + 1;
+
+  buffer = backtrace_alloc (state, buffer_len, error_callback, data);
+
+  if (buffer == NULL)
+    goto exit;
+  memset (buffer, 0, buffer_len);
+  memcpy (buffer, realname, path_len);
+
+  valid_buffer = 1;
+  for (pass = 0; pass < DEBUG_PATH_MAX; ++pass)
+    {
+      switch (pass)
+	{
+	case CURRENT:
+	  {
+	    memcpy (buffer + path_len, debug_filename, 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_filename,
+		    debug_filename_len);
+	    break;
+	  }
+	case USR_LIB_DEBUG:
+	  {
+	    debug_path_len = strlen (debug_file_path[USR_LIB_DEBUG]);
+	    memset (buffer, 0, buffer_len);
+	    memcpy (buffer, debug_file_path[USR_LIB_DEBUG], debug_path_len);
+	    memcpy (buffer + debug_path_len, debug_filename,
+		    debug_filename_len);
+	    break;
+	  }
+	case USR_LIB_DEBUG_PATH_TO_EXE:
+	  {
+	    debug_path_len
+	      = strlen (debug_file_path[USR_LIB_DEBUG_PATH_TO_EXE]);
+	    memset (buffer, 0, buffer_len);
+	    memcpy (buffer, debug_file_path[USR_LIB_DEBUG_PATH_TO_EXE],
+		    debug_path_len);
+	    memcpy (buffer + debug_path_len, realname, path_len);
+	    memcpy (buffer + debug_path_len + path_len, debug_filename,
+		    debug_filename_len);
+	    break;
+	  }
+	default:
+	  goto exit;
+	}
+
+      debug_descriptor
+	= backtrace_open (buffer, error_callback, data, &debug_does_not_exist);
+
+      if (debug_descriptor > 0)
+	break;
+    }
+exit:
+  if (valid_buffer)
+    backtrace_free (state, buffer, buffer_len, error_callback, data);
+  return debug_descriptor;
+}
+
+/* Open debug file by gnulink */
+
+static int
+open_debugfile_by_gnulink (char *realname, unsigned char *section_data,
+			   int section_data_len, struct backtrace_state *state,
+			   backtrace_error_callback error_callback, void *data)
+{
+  int debug_descriptor;
+
+  debug_descriptor = search_for_debugfile (realname, (char *) section_data,
+					   error_callback, data, state);
+  if (debug_descriptor < 0)
+    goto exit;
+
+  /* check the crc32 checksum if it not the same return -1 */
+
+  if (!check_sum (debug_descriptor, (char *) section_data,
+		  strlen ((char *) section_data), section_data_len))
+    debug_descriptor = -1;
+
+exit:
+  return debug_descriptor;
+}
+
+/* Convert char to hex */
+
+static char
+hex (char ch)
+{
+  return ch > 9 ? ('a' + (ch - 10)) : ('0' + ch);
+}
+
+/* Get build-id name  */
+
+static char *
+get_build_id_name (unsigned char *section_data, int *len,
+		   struct backtrace_state *state,
+		   backtrace_error_callback error_callback, void *data)
+{
+  Elf_External_Note *build_id_section;
+  char *build_id_name;
+  char *temp;
+  const char *debug_postfix;
+  const char *debug_prefix;
+  size_t debug_postfix_len;
+  size_t debug_prefix_len;
+  size_t name_size;
+  int offset;
+  unsigned char *hash_start;
+  unsigned long hash_size;
+  unsigned long identifier;
+
+  debug_postfix_len = 6;
+  debug_prefix_len = 10;
+  debug_postfix = ".debug";
+  debug_prefix = ".build-id/";
+  *len = 0;
+
+  build_id_section = (Elf_External_Note *) section_data;
+  hash_size = getl32 (build_id_section->descsz);
+  identifier = getl32 (build_id_section->type);
+  name_size = getl32 (build_id_section->namesz);
+
+  if (identifier != NT_GNU_BUILD_ID || hash_size == 0 || name_size != 4
+      || strncmp ((char *) build_id_section->name, "GNU", 3) != 0)
+    return NULL;
+
+  offset = 16;
+  hash_start = section_data + offset;
+  *len = hash_size * 2 + debug_postfix_len + debug_prefix_len + 1;
+  build_id_name = backtrace_alloc (state, *len, error_callback, data);
+
+  memset (build_id_name, 0, *len);
+  memcpy (build_id_name, debug_prefix, debug_prefix_len);
+  temp = build_id_name + debug_prefix_len;
+
+  *temp++ = hex ((*hash_start & 0xF0) >> 4);
+  *temp++ = hex (*hash_start & 0x0F);
+  ++hash_start;
+  --hash_size;
+
+  memcpy (temp, "/", 1);
+  ++temp;
+
+  while (hash_size--)
+    {
+      *temp++ = hex ((*hash_start & 0xF0) >> 4);
+      *temp++ = hex (*hash_start & 0x0F);
+      ++hash_start;
+    }
+
+  memcpy (temp, debug_postfix, debug_postfix_len);
+  return build_id_name;
+}
+
+/* Open file by build-id */
+
+static int
+open_debugfile_by_build_id (char *realname, unsigned char *section_data,
+			    struct backtrace_state *state,
+			    backtrace_error_callback error_callback, void *data)
+
+{
+  char *build_id_name;
+  int debug_descriptor;
+  int build_id_name_len;
+  int valid_build_id_name;
+
+  debug_descriptor = -1;
+  valid_build_id_name = 0;
+
+  build_id_name = get_build_id_name (section_data, &build_id_name_len, state,
+				     error_callback, data);
+
+  if (build_id_name == NULL || build_id_name_len <= 0)
+    goto exit;
+
+  valid_build_id_name = 1;
+
+  debug_descriptor = search_for_debugfile (realname, build_id_name,
+					   error_callback, data, state);
+
+exit:
+  if (valid_build_id_name)
+    backtrace_free (state, build_id_name, build_id_name_len, error_callback,
+		    data);
+  return debug_descriptor;
+}
+
+/* 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)
+{
+  int descriptor;
+  int debug_descriptor;
+  unsigned char *gnulink_section_data;
+  unsigned char *build_id_section_data;
+  size_t valid_descriptor;
+  size_t valid_gnulink_section_data;
+  size_t valid_build_id_section_data;
+  size_t valid_realname;
+  int build_id_section_data_len;
+  int gnu_link_section_data_len;
+  char *realname;
+  int filename_len;
+
+  valid_realname = 0;
+  valid_descriptor = 0;
+  valid_gnulink_section_data = 0;
+  valid_build_id_section_data = 0;
+  build_id_section_data_len = 0;
+  gnu_link_section_data_len = 0;
+  debug_descriptor = -1;
+
+  descriptor = backtrace_open (filename, error_callback, data, does_not_exist);
+
+  if (descriptor < 0)
+    goto exit;
+
+  valid_descriptor = 1;
+
+  realname = backtrace_resolve_realname (filename, &filename_len, state,
+					 error_callback, data, does_not_exist);
+
+  if (realname == NULL || filename_len < 0)
+    goto exit;
+
+  valid_realname = 1;
+
+  /* check if build-id  section does exist */
+  build_id_section_data
+    = elf_get_section_by_name (state, descriptor, error_callback, data, exe,
+			       &build_id_section_data_len,
+			       ".note.gnu.build-id");
+
+  if (build_id_section_data != NULL && build_id_section_data_len > 0)
+    {
+      valid_build_id_section_data = 1;
+      debug_descriptor
+	= open_debugfile_by_build_id (realname, build_id_section_data, state,
+				      error_callback, data);
+    }
+
+  if (debug_descriptor < 0)
+    {
+      gnulink_section_data
+	= elf_get_section_by_name (state, descriptor, error_callback, data, exe,
+				   &gnu_link_section_data_len,
+				   ".gnu_debuglink");
+
+      if (gnulink_section_data != NULL && gnu_link_section_data_len > 0)
+	{
+	  valid_gnulink_section_data = 1;
+	  debug_descriptor
+	    = open_debugfile_by_gnulink (realname, gnulink_section_data,
+					 gnu_link_section_data_len, state,
+					 error_callback, data);
+	}
+    }
+
+exit:
+  if (valid_descriptor)
+    backtrace_close (descriptor, error_callback, data);
+  if (valid_gnulink_section_data)
+    backtrace_free (state, gnulink_section_data, gnu_link_section_data_len,
+		    error_callback, data);
+  if (valid_build_id_section_data)
+    backtrace_free (state, build_id_section_data, build_id_section_data_len,
+		    error_callback, data);
+  if (valid_realname)
+    backtrace_free (state, realname, PATH_MAX + 1, error_callback, data);
+  return debug_descriptor;
+}
+
 /* A dummy callback function used when we can't find any debug info.  */
 
 static int
@@ -521,7 +1373,6 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
 	 backtrace_error_callback error_callback, void *data,
 	 fileline *fileline_fn, int *found_sym, int *found_dwarf, int exe)
 {
-  struct backtrace_view ehdr_view;
   b_elf_ehdr ehdr;
   off_t shoff;
   unsigned int shnum;
@@ -557,102 +1408,18 @@ elf_add (struct backtrace_state *state, int descriptor, uintptr_t base_address,
   strtab_view_valid = 0;
   debug_view_valid = 0;
 
-  if (!backtrace_get_view (state, descriptor, 0, sizeof ehdr, error_callback,
-			   data, &ehdr_view))
+  if (!elf_header_is_valid (state, descriptor, error_callback, data, exe, &shoff,
+			 &shnum, &shstrndx, &ehdr))
     goto fail;
 
-  memcpy (&ehdr, ehdr_view.data, sizeof ehdr);
-
-  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)
-    {
-      error_callback (data, "executable file is not ELF", 0);
-      goto fail;
-    }
-  if (ehdr.e_ident[EI_VERSION] != EV_CURRENT)
-    {
-      error_callback (data, "executable file is unrecognized ELF version", 0);
-      goto fail;
-    }
-
-#if BACKTRACE_ELF_SIZE == 32
-#define BACKTRACE_ELFCLASS ELFCLASS32
-#else
-#define BACKTRACE_ELFCLASS ELFCLASS64
-#endif
-
-  if (ehdr.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)
-    {
-      error_callback (data, "executable file has unknown endianness", 0);
-      goto fail;
-    }
-
-  /* If the executable is ET_DYN, it is either a PIE, or we are running
-     directly a shared library with .interp.  We need to wait for
-     dl_iterate_phdr in that case to determine the actual base_address.  */
-  if (exe && ehdr.e_type == ET_DYN)
-    return -1;
-
-  shoff = ehdr.e_shoff;
-  shnum = ehdr.e_shnum;
-  shstrndx = ehdr.e_shstrndx;
-
-  if ((shnum == 0 || shstrndx == SHN_XINDEX)
-      && shoff != 0)
-    {
-      struct backtrace_view shdr_view;
-      const b_elf_shdr *shdr;
-
-      if (!backtrace_get_view (state, descriptor, shoff, 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 (shstrndx == SHN_XINDEX)
-	{
-	  shstrndx = shdr->sh_link;
-
-	  /* Versions of the GNU binutils between 2.12 and 2.18 did
-	     not handle objects with more than SHN_LORESERVE sections
-	     correctly.  All large section indexes were offset by
-	     0x100.  There is more information at
-	     http://sourceware.org/bugzilla/show_bug.cgi?id-5900 .
-	     Fortunately these object files are easy to detect, as the
-	     GNU binutils always put the section header string table
-	     near the end of the list of sections.  Thus if the
-	     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;
-	}
-
-      backtrace_release_view (state, &shdr_view, error_callback, data);
-    }
-
   /* To translate PC to file/line when using DWARF, we need to find
      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 +1644,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 +1664,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/internal.h b/libbacktrace/internal.h
index 89b7bf7..80a75ed 100644
--- a/libbacktrace/internal.h
+++ b/libbacktrace/internal.h
@@ -176,6 +176,15 @@ struct backtrace_view
   size_t len;
 };
 
+/* 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,

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-03-14 16:26       ` Ian Lance Taylor via gcc-patches
@ 2017-03-14 17:44         ` Denis Khalikov
  0 siblings, 0 replies; 28+ messages in thread
From: Denis Khalikov @ 2017-03-14 17:44 UTC (permalink / raw)
  To: Ian Lance Taylor; +Cc: gcc-patches

Thanks for answer, i got it now.
I will also delete all readlink code. Looks like is no reason
to use it instead just one call of realpath(char*,char*). Binutils
using realpath in the same cases.

On 03/14/2017 07:26 PM, Ian Lance Taylor wrote:
> On Tue, Mar 14, 2017 at 7:30 AM, Denis Khalikov
> <d.khalikov@partner.samsung.com> wrote:
>> Thanks for review, got all of my mistakes, except one.
>>
>>
>>> -      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);
>>
>> This seems like unnecessary work.  Shouldn't we only try to open the
>> debug file if we find a .gnu_debuglink section?
>>
>> Should i move code below
>>
>>   /* check if debug section does exist */
>>  debug_link_data
>>     = elf_gnu_debuglink_section (state, descriptor, error_callbackdata, exe,
>> &debug_link_data_len);
>>
>> from backtrace_open_debugfile .
>
> As far as I know all the debuglink code is ELF-specific.  I would do
> it all in elf.c.  While reading the sections of the executable, look
> for a debuglink section, and use it if present.  Keep the readlink
> code in posix.c, I suppose.  Apologies if this doesn't make sense.
>
> Ian
>
>
>
>> On 03/14/2017 04:49 PM, Ian Lance Taylor wrote:
>>>
>>> On Mon, Mar 13, 2017 at 10:16 AM, Denis Khalikov
>>> <d.khalikov@partner.samsung.com> wrote:
>>>>
>>>> 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
>>>
>>>
>>>> +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;
>>>
>>>
>>> As far as I know "separate" is not a special thing in automake.  We
>>> should always run (and clean) this test if the necessary objcopy
>>> option is available.
>>>
>>>> +glinktest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h
>>>
>>>
>>> Missing '$'  in "$(INCDIR)".
>>>
>>>> +/* Return 1 if header is valid and -1 on fail */
>>>
>>>
>>> This comment does not explain what the function actually does.
>>>
>>>> +/* Return the pointer to char array with data from .gnudebuglink section
>>>> inside.  */
>>>
>>>
>>> Line too long, we use 80 column lines.
>>>
>>>> +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)
>>>
>>>
>>> This should be static.  I see that you are calling it elsewhere, but
>>> it doesn't make sense to call an "elf" function outside of elf.c.
>>> This library is used on non-ELF systems.
>>>
>>>> +  /* Look for for the .gnu_debuglink section  */
>>>
>>>
>>> Period at end of sentence.
>>>
>>>>    /* 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.  */
>>>
>>>
>>> Why change the indentation like this?  I think the original was correct.
>>>
>>>> -      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);
>>>
>>>
>>> This seems like unnecessary work.  Shouldn't we only try to open the
>>> debug file if we find a .gnu_debuglink section?
>>>
>>>> +/*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. */
>>>
>>>
>>> No leading '*' on subsequent lines of multi-line comments, see existing
>>> code.
>>>
>>> I'm not sure I see the point of glinktest.c.  Why don't we just use
>>> btest.c?
>>>
>>>> +#define MAX_PATH_LEN 4096 /*  from linux/limits.h   */
>>>
>>>
>>> This library works on systems other than GNU/Linux.  We should
>>> dynamically allocate the buffer.
>>>
>>>> +  while (len > 1 && !IS_DIR_SEPARATOR (*(buffer + (len - 1))))
>>>
>>>
>>> while (len > 1 && !IS_DIR_SEPARATOR(buffer[len-1]))
>>>
>>> or just call basename.  I'm not sure why you bother with the count
>>> variable; doesn't full_filename_len - count exactly == len?
>>>
>>>> +  sign_byte = 0x00;
>>>> +  count = 0;
>>>> +  while (*(buffer + count) != sign_byte && size > count)
>>>> +    ++count;
>>>> +  return count;
>>>
>>>
>>> This looks like `return strnlen(buffer, size)`.
>>>
>>> Ian
>>>
>>>
>>>
>>
>
>
>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-03-14 14:30     ` Denis Khalikov
@ 2017-03-14 16:26       ` Ian Lance Taylor via gcc-patches
  2017-03-14 17:44         ` Denis Khalikov
  0 siblings, 1 reply; 28+ messages in thread
From: Ian Lance Taylor via gcc-patches @ 2017-03-14 16:26 UTC (permalink / raw)
  To: Denis Khalikov; +Cc: gcc-patches

On Tue, Mar 14, 2017 at 7:30 AM, Denis Khalikov
<d.khalikov@partner.samsung.com> wrote:
> Thanks for review, got all of my mistakes, except one.
>
>
>> -      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);
>
> This seems like unnecessary work.  Shouldn't we only try to open the
> debug file if we find a .gnu_debuglink section?
>
> Should i move code below
>
>   /* check if debug section does exist */
>  debug_link_data
>     = elf_gnu_debuglink_section (state, descriptor, error_callbackdata, exe,
> &debug_link_data_len);
>
> from backtrace_open_debugfile .

As far as I know all the debuglink code is ELF-specific.  I would do
it all in elf.c.  While reading the sections of the executable, look
for a debuglink section, and use it if present.  Keep the readlink
code in posix.c, I suppose.  Apologies if this doesn't make sense.

Ian



> On 03/14/2017 04:49 PM, Ian Lance Taylor wrote:
>>
>> On Mon, Mar 13, 2017 at 10:16 AM, Denis Khalikov
>> <d.khalikov@partner.samsung.com> wrote:
>>>
>>> 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
>>
>>
>>> +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;
>>
>>
>> As far as I know "separate" is not a special thing in automake.  We
>> should always run (and clean) this test if the necessary objcopy
>> option is available.
>>
>>> +glinktest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h
>>
>>
>> Missing '$'  in "$(INCDIR)".
>>
>>> +/* Return 1 if header is valid and -1 on fail */
>>
>>
>> This comment does not explain what the function actually does.
>>
>>> +/* Return the pointer to char array with data from .gnudebuglink section
>>> inside.  */
>>
>>
>> Line too long, we use 80 column lines.
>>
>>> +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)
>>
>>
>> This should be static.  I see that you are calling it elsewhere, but
>> it doesn't make sense to call an "elf" function outside of elf.c.
>> This library is used on non-ELF systems.
>>
>>> +  /* Look for for the .gnu_debuglink section  */
>>
>>
>> Period at end of sentence.
>>
>>>    /* 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.  */
>>
>>
>> Why change the indentation like this?  I think the original was correct.
>>
>>> -      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);
>>
>>
>> This seems like unnecessary work.  Shouldn't we only try to open the
>> debug file if we find a .gnu_debuglink section?
>>
>>> +/*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. */
>>
>>
>> No leading '*' on subsequent lines of multi-line comments, see existing
>> code.
>>
>> I'm not sure I see the point of glinktest.c.  Why don't we just use
>> btest.c?
>>
>>> +#define MAX_PATH_LEN 4096 /*  from linux/limits.h   */
>>
>>
>> This library works on systems other than GNU/Linux.  We should
>> dynamically allocate the buffer.
>>
>>> +  while (len > 1 && !IS_DIR_SEPARATOR (*(buffer + (len - 1))))
>>
>>
>> while (len > 1 && !IS_DIR_SEPARATOR(buffer[len-1]))
>>
>> or just call basename.  I'm not sure why you bother with the count
>> variable; doesn't full_filename_len - count exactly == len?
>>
>>> +  sign_byte = 0x00;
>>> +  count = 0;
>>> +  while (*(buffer + count) != sign_byte && size > count)
>>> +    ++count;
>>> +  return count;
>>
>>
>> This looks like `return strnlen(buffer, size)`.
>>
>> Ian
>>
>>
>>
>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-03-14 13:49   ` Ian Lance Taylor via gcc-patches
@ 2017-03-14 14:30     ` Denis Khalikov
  2017-03-14 16:26       ` Ian Lance Taylor via gcc-patches
  2017-03-22 15:29     ` Denis Khalikov
  1 sibling, 1 reply; 28+ messages in thread
From: Denis Khalikov @ 2017-03-14 14:30 UTC (permalink / raw)
  To: Ian Lance Taylor; +Cc: gcc-patches

Thanks for review, got all of my mistakes, except one.


 > -      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);

This seems like unnecessary work.  Shouldn't we only try to open the
debug file if we find a .gnu_debuglink section?

Should i move code below

   /* check if debug section does exist */
  debug_link_data
     = elf_gnu_debuglink_section (state, descriptor, error_callbackdata, 
exe, &debug_link_data_len);

from backtrace_open_debugfile .

On 03/14/2017 04:49 PM, Ian Lance Taylor wrote:
> On Mon, Mar 13, 2017 at 10:16 AM, Denis Khalikov
> <d.khalikov@partner.samsung.com> wrote:
>> 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
>
>> +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;
>
> As far as I know "separate" is not a special thing in automake.  We
> should always run (and clean) this test if the necessary objcopy
> option is available.
>
>> +glinktest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h
>
> Missing '$'  in "$(INCDIR)".
>
>> +/* Return 1 if header is valid and -1 on fail */
>
> This comment does not explain what the function actually does.
>
>> +/* Return the pointer to char array with data from .gnudebuglink section inside.  */
>
> Line too long, we use 80 column lines.
>
>> +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)
>
> This should be static.  I see that you are calling it elsewhere, but
> it doesn't make sense to call an "elf" function outside of elf.c.
> This library is used on non-ELF systems.
>
>> +  /* Look for for the .gnu_debuglink section  */
>
> Period at end of sentence.
>
>>    /* 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.  */
>
> Why change the indentation like this?  I think the original was correct.
>
>> -      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);
>
> This seems like unnecessary work.  Shouldn't we only try to open the
> debug file if we find a .gnu_debuglink section?
>
>> +/*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. */
>
> No leading '*' on subsequent lines of multi-line comments, see existing code.
>
> I'm not sure I see the point of glinktest.c.  Why don't we just use btest.c?
>
>> +#define MAX_PATH_LEN 4096 /*  from linux/limits.h   */
>
> This library works on systems other than GNU/Linux.  We should
> dynamically allocate the buffer.
>
>> +  while (len > 1 && !IS_DIR_SEPARATOR (*(buffer + (len - 1))))
>
> while (len > 1 && !IS_DIR_SEPARATOR(buffer[len-1]))
>
> or just call basename.  I'm not sure why you bother with the count
> variable; doesn't full_filename_len - count exactly == len?
>
>> +  sign_byte = 0x00;
>> +  count = 0;
>> +  while (*(buffer + count) != sign_byte && size > count)
>> +    ++count;
>> +  return count;
>
> This looks like `return strnlen(buffer, size)`.
>
> Ian
>
>
>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-03-13 17:16 ` Denis Khalikov
  2017-03-14  8:27   ` Richard Biener
@ 2017-03-14 13:49   ` Ian Lance Taylor via gcc-patches
  2017-03-14 14:30     ` Denis Khalikov
  2017-03-22 15:29     ` Denis Khalikov
  1 sibling, 2 replies; 28+ messages in thread
From: Ian Lance Taylor via gcc-patches @ 2017-03-14 13:49 UTC (permalink / raw)
  To: Denis Khalikov; +Cc: gcc-patches

On Mon, Mar 13, 2017 at 10:16 AM, Denis Khalikov
<d.khalikov@partner.samsung.com> wrote:
> 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

> +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;

As far as I know "separate" is not a special thing in automake.  We
should always run (and clean) this test if the necessary objcopy
option is available.

> +glinktest.lo: (INCDIR)/filenames.h backtrace.h backtrace-supported.h

Missing '$'  in "$(INCDIR)".

> +/* Return 1 if header is valid and -1 on fail */

This comment does not explain what the function actually does.

> +/* Return the pointer to char array with data from .gnudebuglink section inside.  */

Line too long, we use 80 column lines.

> +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)

This should be static.  I see that you are calling it elsewhere, but
it doesn't make sense to call an "elf" function outside of elf.c.
This library is used on non-ELF systems.

> +  /* Look for for the .gnu_debuglink section  */

Period at end of sentence.

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

Why change the indentation like this?  I think the original was correct.

> -      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);

This seems like unnecessary work.  Shouldn't we only try to open the
debug file if we find a .gnu_debuglink section?

> +/*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. */

No leading '*' on subsequent lines of multi-line comments, see existing code.

I'm not sure I see the point of glinktest.c.  Why don't we just use btest.c?

> +#define MAX_PATH_LEN 4096 /*  from linux/limits.h   */

This library works on systems other than GNU/Linux.  We should
dynamically allocate the buffer.

> +  while (len > 1 && !IS_DIR_SEPARATOR (*(buffer + (len - 1))))

while (len > 1 && !IS_DIR_SEPARATOR(buffer[len-1]))

or just call basename.  I'm not sure why you bother with the count
variable; doesn't full_filename_len - count exactly == len?

> +  sign_byte = 0x00;
> +  count = 0;
> +  while (*(buffer + count) != sign_byte && size > count)
> +    ++count;
> +  return count;

This looks like `return strnlen(buffer, size)`.

Ian

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-03-14 13:21       ` Ian Lance Taylor via gcc-patches
@ 2017-03-14 13:46         ` Denis Khalikov
  0 siblings, 0 replies; 28+ messages in thread
From: Denis Khalikov @ 2017-03-14 13:46 UTC (permalink / raw)
  To: Ian Lance Taylor; +Cc: Richard Biener, GCC Patches, Matthias Klose

Ok, thanks,
i will change patch to use crc32 from libiberty and
also implement searching for debuginfo with build id.
As it was implemented to binutils PR binutils/20876.

On 03/14/2017 04:21 PM, Ian Lance Taylor wrote:
> On Tue, Mar 14, 2017 at 3:20 AM, Denis Khalikov
> <d.khalikov@partner.samsung.com> wrote:
>> Thanks for review,
>>
>>> Skimming over the patch I noticed you duplicate libiberties xcrc32
>>> functionality.
>>
>> should i take care about standalone libbacktrace ?
>> https://github.com/ianlancetaylor/libbacktrace
>
> No, don't worry about it.
>
> Ian
>
>
>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-03-14 10:21     ` Denis Khalikov
@ 2017-03-14 13:21       ` Ian Lance Taylor via gcc-patches
  2017-03-14 13:46         ` Denis Khalikov
  0 siblings, 1 reply; 28+ messages in thread
From: Ian Lance Taylor via gcc-patches @ 2017-03-14 13:21 UTC (permalink / raw)
  To: Denis Khalikov; +Cc: Richard Biener, GCC Patches

On Tue, Mar 14, 2017 at 3:20 AM, Denis Khalikov
<d.khalikov@partner.samsung.com> wrote:
> Thanks for review,
>
>> Skimming over the patch I noticed you duplicate libiberties xcrc32
>> functionality.
>
> should i take care about standalone libbacktrace ?
> https://github.com/ianlancetaylor/libbacktrace

No, don't worry about it.

Ian

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-03-14  8:27   ` Richard Biener
  2017-03-14  9:22     ` Matthias Klose
@ 2017-03-14 10:21     ` Denis Khalikov
  2017-03-14 13:21       ` Ian Lance Taylor via gcc-patches
  1 sibling, 1 reply; 28+ messages in thread
From: Denis Khalikov @ 2017-03-14 10:21 UTC (permalink / raw)
  To: Richard Biener, Ian Lance Taylor; +Cc: GCC Patches

Thanks for review,

 > Skimming over the patch I noticed you duplicate libiberties xcrc32
 > functionality.

should i take care about standalone libbacktrace ?
https://github.com/ianlancetaylor/libbacktrace

 > Also the additions to posix.c probably belong to dwarf.c and elf.c 
(the feature
 > is dwarf + elf specific but proper abstraction / #ifdefing should
 > ensure compiling
 > also succeeds for non-dwarf / non-elf platforms).

thanks, i will move code to elf.c since it's about reading section from 
elf format.


On 03/14/2017 11:27 AM, Richard Biener wrote:
> On Mon, Mar 13, 2017 at 6:16 PM, Denis Khalikov
> <d.khalikov@partner.samsung.com> wrote:
>> Hello everyone, i have a patch for this issue.
>
> Great!
>
>> 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
>
> Skimming over the patch I noticed you duplicate libiberties xcrc32
> functionality.
>
> Also the additions to posix.c probably belong to dwarf.c and elf.c (the feature
> is dwarf + elf specific but proper abstraction / #ifdefing should
> ensure compiling
> also succeeds for non-dwarf / non-elf platforms).
>
> Leaving actual review to the maintainer (CCed).
>
> Thanks,
> Richard.
>
>
>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-03-14  8:27   ` Richard Biener
@ 2017-03-14  9:22     ` Matthias Klose
  2017-03-14 10:21     ` Denis Khalikov
  1 sibling, 0 replies; 28+ messages in thread
From: Matthias Klose @ 2017-03-14  9:22 UTC (permalink / raw)
  To: Richard Biener, Denis Khalikov, Ian Lance Taylor; +Cc: GCC Patches

On 14.03.2017 09:27, Richard Biener wrote:
> On Mon, Mar 13, 2017 at 6:16 PM, Denis Khalikov
> <d.khalikov@partner.samsung.com> wrote:
>> Hello everyone, i have a patch for this issue.
> 
> Great!
> 
>> 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

These days using the build-id for separate debug info seems to be the new way of
doing that, so adding support for the build-id method would be useful. See PR
binutils/20876 for how binutils is doing that.

Matthias

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
  2017-03-13 17:16 ` Denis Khalikov
@ 2017-03-14  8:27   ` Richard Biener
  2017-03-14  9:22     ` Matthias Klose
  2017-03-14 10:21     ` Denis Khalikov
  2017-03-14 13:49   ` Ian Lance Taylor via gcc-patches
  1 sibling, 2 replies; 28+ messages in thread
From: Richard Biener @ 2017-03-14  8:27 UTC (permalink / raw)
  To: Denis Khalikov, Ian Lance Taylor; +Cc: GCC Patches

On Mon, Mar 13, 2017 at 6:16 PM, Denis Khalikov
<d.khalikov@partner.samsung.com> wrote:
> Hello everyone, i have a patch for this issue.

Great!

> 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

Skimming over the patch I noticed you duplicate libiberties xcrc32
functionality.

Also the additions to posix.c probably belong to dwarf.c and elf.c (the feature
is dwarf + elf specific but proper abstraction / #ifdefing should
ensure compiling
also succeeds for non-dwarf / non-elf platforms).

Leaving actual review to the maintainer (CCed).

Thanks,
Richard.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace
       [not found] <CGME20170313171637eucas1p2d70c4ed7cbd6d088c8c58dc76e1ef722@eucas1p2.samsung.com>
@ 2017-03-13 17:16 ` Denis Khalikov
  2017-03-14  8:27   ` Richard Biener
  2017-03-14 13:49   ` Ian Lance Taylor via gcc-patches
  0 siblings, 2 replies; 28+ messages in thread
From: Denis Khalikov @ 2017-03-13 17:16 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 510 bytes --]

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


[-- Attachment #2: PR_sanitizer_77631 --]
[-- Type: text/plain, Size: 45694 bytes --]

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;
+}
+

^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2017-10-02 14:01 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <CGME20170616153942eucas1p1945271f893265484bbb3991a368bcd92@eucas1p1.samsung.com>
2017-06-16 15:39 ` [PATCH][PR sanitizer/77631] Support separate debug info in libbacktrace Denis Khalikov
2017-06-18 14:09   ` Matthias Klose
2017-06-19  8:48     ` Denis Khalikov
2017-06-28 23:59   ` Ian Lance Taylor via gcc-patches
2017-07-01 21:38     ` Denis Khalikov
2017-07-29 20:42     ` Denis Khalikov
2017-09-10 21:11       ` Ian Lance Taylor via gcc-patches
2017-09-11 10:06         ` Denis Khalikov
2017-09-12 14:06           ` Ian Lance Taylor via gcc-patches
2017-09-20  8:54             ` Maxim Ostapenko
2017-09-20 12:33               ` Ian Lance Taylor via gcc-patches
2017-09-20 21:09         ` Ian Lance Taylor via gcc-patches
2017-10-02 11:12           ` Martin Liška
2017-10-02 11:32             ` Jakub Jelinek
2017-10-02 14:01               ` Jakub Jelinek
     [not found] <CGME20170313171637eucas1p2d70c4ed7cbd6d088c8c58dc76e1ef722@eucas1p2.samsung.com>
2017-03-13 17:16 ` Denis Khalikov
2017-03-14  8:27   ` Richard Biener
2017-03-14  9:22     ` Matthias Klose
2017-03-14 10:21     ` Denis Khalikov
2017-03-14 13:21       ` Ian Lance Taylor via gcc-patches
2017-03-14 13:46         ` Denis Khalikov
2017-03-14 13:49   ` Ian Lance Taylor via gcc-patches
2017-03-14 14:30     ` Denis Khalikov
2017-03-14 16:26       ` Ian Lance Taylor via gcc-patches
2017-03-14 17:44         ` Denis Khalikov
2017-03-22 15:29     ` Denis Khalikov
2017-04-12 22:08       ` Jeff Law
2017-05-18 14:23         ` Denis Khalikov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).