* [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, + §ion_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, §ion_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, + §ion_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, §ion_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, + §ion_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, §ion_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 = ¬e->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 = ¬e->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
[parent not found: <CGME20170313171637eucas1p2d70c4ed7cbd6d088c8c58dc76e1ef722@eucas1p2.samsung.com>]
* [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
* 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
* 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-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 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 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-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: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-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 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 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, + §ion_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, §ion_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-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-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, + §ion_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, §ion_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
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).