1/* Find an ELF file for a module from its build ID. 2 Copyright (C) 2007-2010, 2014 Red Hat, Inc. 3 This file is part of elfutils. 4 5 This file is free software; you can redistribute it and/or modify 6 it under the terms of either 7 8 * the GNU Lesser General Public License as published by the Free 9 Software Foundation; either version 3 of the License, or (at 10 your option) any later version 11 12 or 13 14 * the GNU General Public License as published by the Free 15 Software Foundation; either version 2 of the License, or (at 16 your option) any later version 17 18 or both in parallel, as here. 19 20 elfutils is distributed in the hope that it will be useful, but 21 WITHOUT ANY WARRANTY; without even the implied warranty of 22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23 General Public License for more details. 24 25 You should have received copies of the GNU General Public License and 26 the GNU Lesser General Public License along with this program. If 27 not, see <http://www.gnu.org/licenses/>. */ 28 29#include "libdwflP.h" 30#include <inttypes.h> 31#include <fcntl.h> 32#include <unistd.h> 33 34 35int 36internal_function 37__libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug, char **file_name, 38 const size_t id_len, const uint8_t *id) 39{ 40 /* Search debuginfo_path directories' .build-id/ subdirectories. */ 41 42 char id_name[sizeof "/.build-id/" + 1 + id_len * 2 + sizeof ".debug" - 1]; 43 strcpy (id_name, "/.build-id/"); 44 int n = snprintf (&id_name[sizeof "/.build-id/" - 1], 45 4, "%02" PRIx8 "/", (uint8_t) id[0]); 46 assert (n == 3); 47 for (size_t i = 1; i < id_len; ++i) 48 { 49 n = snprintf (&id_name[sizeof "/.build-id/" - 1 + 3 + (i - 1) * 2], 50 3, "%02" PRIx8, (uint8_t) id[i]); 51 assert (n == 2); 52 } 53 if (debug) 54 strcpy (&id_name[sizeof "/.build-id/" - 1 + 3 + (id_len - 1) * 2], 55 ".debug"); 56 57 const Dwfl_Callbacks *const cb = mod->dwfl->callbacks; 58 char *path = strdupa ((cb->debuginfo_path ? *cb->debuginfo_path : NULL) 59 ?: DEFAULT_DEBUGINFO_PATH); 60 61 int fd = -1; 62 char *dir; 63 while (fd < 0 && (dir = strsep (&path, ":")) != NULL) 64 { 65 if (dir[0] == '+' || dir[0] == '-') 66 ++dir; 67 68 /* Only absolute directory names are useful to us. */ 69 if (dir[0] != '/') 70 continue; 71 72 size_t dirlen = strlen (dir); 73 char *name = malloc (dirlen + sizeof id_name); 74 if (unlikely (name == NULL)) 75 break; 76 memcpy (mempcpy (name, dir, dirlen), id_name, sizeof id_name); 77 78 fd = TEMP_FAILURE_RETRY (open64 (name, O_RDONLY)); 79 if (fd >= 0) 80 { 81 if (*file_name != NULL) 82 free (*file_name); 83 *file_name = canonicalize_file_name (name); 84 if (*file_name == NULL) 85 { 86 *file_name = name; 87 name = NULL; 88 } 89 } 90 free (name); 91 } 92 93 /* If we simply found nothing, clear errno. If we had some other error 94 with the file, report that. Possibly this should treat other errors 95 like ENOENT too. But ignoring all errors could mask some that should 96 be reported. */ 97 if (fd < 0 && errno == ENOENT) 98 errno = 0; 99 100 return fd; 101} 102 103int 104internal_function 105__libdwfl_open_mod_by_build_id (Dwfl_Module *mod, bool debug, char **file_name) 106{ 107 /* If *FILE_NAME was primed into the module, leave it there 108 as the fallback when we have nothing to offer. */ 109 errno = 0; 110 if (mod->build_id_len <= 0) 111 return -1; 112 113 const size_t id_len = mod->build_id_len; 114 const uint8_t *id = mod->build_id_bits; 115 116 return __libdwfl_open_by_build_id (mod, debug, file_name, id_len, id); 117} 118 119int 120dwfl_build_id_find_elf (Dwfl_Module *mod, 121 void **userdata __attribute__ ((unused)), 122 const char *modname __attribute__ ((unused)), 123 Dwarf_Addr base __attribute__ ((unused)), 124 char **file_name, Elf **elfp) 125{ 126 *elfp = NULL; 127 if (mod->is_executable && mod->dwfl->executable_for_core != NULL) 128 { 129 /* When dwfl_core_file_report was called with a non-NULL executable file 130 name this callback will replace the Dwfl_Module main.name with the 131 recorded executable file when MOD was identified as main executable 132 (which then triggers opening and reporting of the executable). */ 133 int fd = open64 (mod->dwfl->executable_for_core, O_RDONLY); 134 if (fd >= 0) 135 { 136 *file_name = strdup (mod->dwfl->executable_for_core); 137 if (*file_name != NULL) 138 return fd; 139 else 140 close (fd); 141 } 142 } 143 int fd = __libdwfl_open_mod_by_build_id (mod, false, file_name); 144 if (fd >= 0) 145 { 146 Dwfl_Error error = __libdw_open_file (&fd, elfp, true, false); 147 if (error != DWFL_E_NOERROR) 148 __libdwfl_seterrno (error); 149 else if (__libdwfl_find_build_id (mod, false, *elfp) == 2) 150 { 151 /* This is a backdoor signal to short-circuit the ID refresh. */ 152 mod->main.valid = true; 153 return fd; 154 } 155 else 156 { 157 /* This file does not contain the ID it should! */ 158 elf_end (*elfp); 159 *elfp = NULL; 160 close (fd); 161 fd = -1; 162 } 163 free (*file_name); 164 *file_name = NULL; 165 } 166 else if (errno == 0 && mod->build_id_len > 0) 167 /* Setting this with no file yet loaded is a marker that 168 the build ID is authoritative even if we also know a 169 putative *FILE_NAME. */ 170 mod->main.valid = true; 171 172 return fd; 173} 174INTDEF (dwfl_build_id_find_elf) 175