From 456776256c8fa3912ba7205bb0f730fd47a6f756 Mon Sep 17 00:00:00 2001 From: Derek Bruening Date: Fri, 21 Feb 2020 19:36:27 -0500 Subject: [PATCH 1/3] i#4123: Look for symbols in special build-id subdir Adds to drsyms reading of the build id and searching in /usr/lib/debug/.build-id for a separate debuginfo file, which is the modern scheme for installed libraries. Tested on internal symbols like __GI___libc_malloc on a local machine. It is not easy to make an automated test for this without setting up a chroot or something which does not seem worth the effort. Fixes #4123 --- ext/drsyms/drsyms_elf.c | 54 ++++++++++++++++++++++++++++++++- ext/drsyms/drsyms_macho.c | 7 +++++ ext/drsyms/drsyms_obj.h | 5 ++- ext/drsyms/drsyms_unix_common.c | 20 +++++++++--- 4 files changed, 80 insertions(+), 6 deletions(-) diff --git a/ext/drsyms/drsyms_elf.c b/ext/drsyms/drsyms_elf.c index 029f08d9bf5..8e134af00db 100644 --- a/ext/drsyms/drsyms_elf.c +++ b/ext/drsyms/drsyms_elf.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2011-2015 Google, Inc. All rights reserved. + * Copyright (c) 2011-2020 Google, Inc. All rights reserved. * **********************************************************/ /* @@ -126,6 +126,8 @@ typedef struct _elf_info_t { byte *map_base; ptr_uint_t load_base; drsym_debug_kind_t debug_kind; +#define MAX_BUILD_ID_LENGTH 128 + char build_id[MAX_BUILD_ID_LENGTH]; } elf_info_t; /* Looks for a section with real data, not just a section with a header */ @@ -166,6 +168,47 @@ find_elf_section_by_name(Elf *elf, const char *match_name) return NULL; } +/* Reads the build id into mod->build_id. */ +static void +read_build_id(Elf *elf, elf_info_t *mod) +{ + Elf_Scn *scn; + for (scn = elf_getscn(elf, 0); scn != NULL; scn = elf_nextscn(elf, scn)) { + Elf_Shdr *section_header = elf_getshdr(scn); + if (section_header == NULL || section_header->sh_type != SHT_NOTE) + continue; + Elf_Data *data = elf_getdata(scn, NULL); + Elf_Note *note = (Elf_Note *)data->d_buf; + if (note->n_type == NT_GNU_BUILD_ID) { + /* Following the note are the name and value. */ + byte *src = ((byte *)(note + 1)) + note->n_namesz; + size_t size = note->n_descsz; + if ((byte *)data->d_buf + data->d_size < src + note->n_descsz) { + NOTIFY_ELF("note data is shorter than specified length"); + size = (byte *)data->d_buf + data->d_size - src; + } + char *dst = mod->build_id; + for (int i = 0; i < size; i++) { + if (dst + 3 >= mod->build_id + MAX_BUILD_ID_LENGTH) { + NOTIFY_ELF("build id is too long"); + NULL_TERMINATE_BUFFER(mod->build_id); + return; + } + unsigned int val = (unsigned int)*src; + int len = dr_snprintf(dst, 3, "%02x", val); + if (len < 0) { + NOTIFY_ELF("malformed build id"); + mod->build_id[0] = '\0'; + return; + } + dst += len; + src++; + } + return; + } + } +} + /* Iterates the program headers for an ELF object and returns the minimum * segment load address. For executables this is generally a well-known * address. For PIC shared libraries this is usually 0. For DR clients this is @@ -260,6 +303,8 @@ drsym_obj_mod_init_pre(byte *map_base, size_t map_size) mod->debug_kind |= DRSYM_LINE_NUMS | DRSYM_DWARF_LINE; } + read_build_id(mod->elf, mod); + return (void *)mod; } @@ -431,6 +476,13 @@ drsym_obj_addrsearch_symtab(void *mod_in, size_t modoffs, uint *idx OUT) return DRSYM_ERROR_SYMBOL_NOT_FOUND; } +const char * +drsym_obj_build_id(void *mod_in) +{ + elf_info_t *mod = (elf_info_t *)mod_in; + return mod->build_id; +} + /****************************************************************************** * Linux-specific helpers */ diff --git a/ext/drsyms/drsyms_macho.c b/ext/drsyms/drsyms_macho.c index 8ebb0176b8f..3444492c4b7 100644 --- a/ext/drsyms/drsyms_macho.c +++ b/ext/drsyms/drsyms_macho.c @@ -645,3 +645,10 @@ drsym_obj_debug_path(void) { return "/usr/lib/debug"; } + +const char * +drsym_obj_build_id(void *mod_in) +{ + /* NYI. Are build id-based dirs used on Mac? */ + return nullptr; +} diff --git a/ext/drsyms/drsyms_obj.h b/ext/drsyms/drsyms_obj.h index ccffef6b7e4..81f27d7bbe7 100644 --- a/ext/drsyms/drsyms_obj.h +++ b/ext/drsyms/drsyms_obj.h @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2011-2014 Google, Inc. All rights reserved. + * Copyright (c) 2011-2020 Google, Inc. All rights reserved. * **********************************************************/ /* @@ -101,6 +101,9 @@ drsym_obj_same_file(const char *path1, const char *path2); const char * drsym_obj_debug_path(void); +const char * +drsym_obj_build_id(void *mod_in); + /*************************************************************************** * DWARF */ diff --git a/ext/drsyms/drsyms_unix_common.c b/ext/drsyms/drsyms_unix_common.c index 7d0c31a9016..75c2fd6fc58 100644 --- a/ext/drsyms/drsyms_unix_common.c +++ b/ext/drsyms/drsyms_unix_common.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2011-2014 Google, Inc. All rights reserved. + * Copyright (c) 2011-2020 Google, Inc. All rights reserved. * **********************************************************/ /* @@ -253,7 +253,19 @@ follow_debuglink(const char *modpath, dbg_module_t *mod, const char *debuglink, if (last_slash != NULL) *last_slash = '\0'; - /* 1. Check $mod_dir/$debuglink */ + /* 1. Check /usr/lib/debug/.build-id/xx/$debuglink */ + const char *build_id = drsym_obj_build_id(mod->obj_info); + NOTIFY("%s: build id is %s\n", __FUNCTION__, build_id == NULL ? "" : build_id); + if (build_id != NULL && build_id[0] != '\0') { + dr_snprintf(debug_modpath, MAXIMUM_PATH, "%s/.build-id/%c%c/%s", + drsym_obj_debug_path(), build_id[0], build_id[1], debuglink); + debug_modpath[MAXIMUM_PATH - 1] = '\0'; + NOTIFY("%s: looking for %s\n", __FUNCTION__, debug_modpath); + if (dr_file_exists(debug_modpath)) + return true; + } + + /* 2. Check $mod_dir/$debuglink */ dr_snprintf(debug_modpath, MAXIMUM_PATH, "%s/%s", mod_dir, debuglink); debug_modpath[MAXIMUM_PATH - 1] = '\0'; NOTIFY("%s: looking for %s\n", __FUNCTION__, debug_modpath); @@ -265,14 +277,14 @@ follow_debuglink(const char *modpath, dbg_module_t *mod, const char *debuglink, if (dr_file_exists(debug_modpath) && !drsym_obj_same_file(modpath, debug_modpath)) return true; - /* 2. Check $mod_dir/.debug/$debuglink */ + /* 3. Check $mod_dir/.debug/$debuglink */ dr_snprintf(debug_modpath, MAXIMUM_PATH, "%s/.debug/%s", mod_dir, debuglink); debug_modpath[MAXIMUM_PATH - 1] = '\0'; NOTIFY("%s: looking for %s\n", __FUNCTION__, debug_modpath); if (dr_file_exists(debug_modpath)) return true; - /* 3. Check /usr/lib/debug/$mod_dir/$debuglink */ + /* 4. Check /usr/lib/debug/$mod_dir/$debuglink */ dr_snprintf(debug_modpath, MAXIMUM_PATH, "%s/%s/%s", drsym_obj_debug_path(), mod_dir, debuglink); debug_modpath[MAXIMUM_PATH - 1] = '\0'; From b6f497fa1b98289988b099a035e4df456c1b1bd5 Mon Sep 17 00:00:00 2001 From: Derek Bruening Date: Mon, 24 Feb 2020 18:01:36 -0500 Subject: [PATCH 2/3] Add comments per reviewer request --- ext/drsyms/drsyms_elf.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ext/drsyms/drsyms_elf.c b/ext/drsyms/drsyms_elf.c index 8e134af00db..5b08182c906 100644 --- a/ext/drsyms/drsyms_elf.c +++ b/ext/drsyms/drsyms_elf.c @@ -189,9 +189,14 @@ read_build_id(Elf *elf, elf_info_t *mod) } char *dst = mod->build_id; for (int i = 0; i < size; i++) { - if (dst + 3 >= mod->build_id + MAX_BUILD_ID_LENGTH) { + /* We're writing 3 chars at a time (2 digits + newline). */ + if (dst + 3 > mod->build_id + MAX_BUILD_ID_LENGTH) { NOTIFY_ELF("build id is too long"); - NULL_TERMINATE_BUFFER(mod->build_id); + /* It is already null-terminated from the prior write. Return + * the truncated id. It will likely still work for buildid-dir + * purposes where we only need the 1st 2 chars, and the rest + * come from the debuglink name. + */ return; } unsigned int val = (unsigned int)*src; From b6939d6aa9c3381cf98de7537769860892b8bc4a Mon Sep 17 00:00:00 2001 From: Derek Bruening Date: Mon, 24 Feb 2020 18:04:48 -0500 Subject: [PATCH 3/3] Fix Mac and Windows build issues --- ext/drsyms/drsyms_macho.c | 2 +- ext/drsyms/drsyms_pecoff.c | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ext/drsyms/drsyms_macho.c b/ext/drsyms/drsyms_macho.c index 3444492c4b7..ee14c98bdc5 100644 --- a/ext/drsyms/drsyms_macho.c +++ b/ext/drsyms/drsyms_macho.c @@ -650,5 +650,5 @@ const char * drsym_obj_build_id(void *mod_in) { /* NYI. Are build id-based dirs used on Mac? */ - return nullptr; + return NULL; } diff --git a/ext/drsyms/drsyms_pecoff.c b/ext/drsyms/drsyms_pecoff.c index ab8a4d2197b..acd40e3cbe0 100644 --- a/ext/drsyms/drsyms_pecoff.c +++ b/ext/drsyms/drsyms_pecoff.c @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2012-2017 Google, Inc. All rights reserved. + * Copyright (c) 2012-2020 Google, Inc. All rights reserved. * **********************************************************/ /* @@ -664,3 +664,10 @@ drsym_obj_debug_path(void) /* XXX: also search mingw debug path */ return "c:\\cygwin\\lib\\debug"; } + +const char * +drsym_obj_build_id(void *mod_in) +{ + /* NYI. Are build id-based dirs used on cygwin? */ + return NULL; +}