1/* Standard find_debuginfo callback for libdwfl. 2 Copyright (C) 2005-2010, 2014, 2015 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 <stdio.h> 31#include <fcntl.h> 32#include <unistd.h> 33#include <sys/stat.h> 34#include "system.h" 35 36 37/* Try to open [DIR/][SUBDIR/]DEBUGLINK, return file descriptor or -1. 38 On success, *DEBUGINFO_FILE_NAME has the malloc'd name of the open file. */ 39static int 40try_open (const struct stat *main_stat, 41 const char *dir, const char *subdir, const char *debuglink, 42 char **debuginfo_file_name) 43{ 44 char *fname; 45 if (dir == NULL && subdir == NULL) 46 { 47 fname = strdup (debuglink); 48 if (unlikely (fname == NULL)) 49 return -1; 50 } 51 else if ((subdir == NULL ? asprintf (&fname, "%s/%s", dir, debuglink) 52 : dir == NULL ? asprintf (&fname, "%s/%s", subdir, debuglink) 53 : asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink)) < 0) 54 return -1; 55 56 struct stat st; 57 int fd = TEMP_FAILURE_RETRY (open (fname, O_RDONLY)); 58 if (fd < 0) 59 free (fname); 60 else if (fstat (fd, &st) == 0 61 && st.st_ino == main_stat->st_ino 62 && st.st_dev == main_stat->st_dev) 63 { 64 /* This is the main file by another name. Don't look at it again. */ 65 free (fname); 66 close (fd); 67 errno = ENOENT; 68 fd = -1; 69 } 70 else 71 *debuginfo_file_name = fname; 72 73 return fd; 74} 75 76/* Return true iff the FD's contents CRC matches DEBUGLINK_CRC. */ 77static inline bool 78check_crc (int fd, GElf_Word debuglink_crc) 79{ 80 uint32_t file_crc; 81 return (__libdwfl_crc32_file (fd, &file_crc) == 0 82 && file_crc == debuglink_crc); 83} 84 85static bool 86validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc) 87{ 88 /* For alt debug files always check the build-id from the Dwarf and alt. */ 89 if (mod->dw != NULL) 90 { 91 bool valid = false; 92 const void *build_id; 93 const char *altname; 94 ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw, 95 &altname, 96 &build_id); 97 if (build_id_len > 0) 98 { 99 /* We need to open an Elf handle on the file so we can check its 100 build ID note for validation. Backdoor the handle into the 101 module data structure since we had to open it early anyway. */ 102 Dwfl_Error error = __libdw_open_file (&fd, &mod->alt_elf, 103 false, false); 104 if (error != DWFL_E_NOERROR) 105 __libdwfl_seterrno (error); 106 else 107 { 108 const void *alt_build_id; 109 ssize_t alt_len = INTUSE(dwelf_elf_gnu_build_id) (mod->alt_elf, 110 &alt_build_id); 111 if (alt_len > 0 && alt_len == build_id_len 112 && memcmp (build_id, alt_build_id, alt_len) == 0) 113 valid = true; 114 else 115 { 116 /* A mismatch! */ 117 elf_end (mod->alt_elf); 118 mod->alt_elf = NULL; 119 close (fd); 120 fd = -1; 121 } 122 } 123 } 124 return valid; 125 } 126 127 /* If we have a build ID, check only that. */ 128 if (mod->build_id_len > 0) 129 { 130 /* We need to open an Elf handle on the file so we can check its 131 build ID note for validation. Backdoor the handle into the 132 module data structure since we had to open it early anyway. */ 133 134 mod->debug.valid = false; 135 Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false); 136 if (error != DWFL_E_NOERROR) 137 __libdwfl_seterrno (error); 138 else if (likely (__libdwfl_find_build_id (mod, false, 139 mod->debug.elf) == 2)) 140 /* Also backdoor the gratuitous flag. */ 141 mod->debug.valid = true; 142 else 143 { 144 /* A mismatch! */ 145 elf_end (mod->debug.elf); 146 mod->debug.elf = NULL; 147 close (fd); 148 fd = -1; 149 } 150 151 return mod->debug.valid; 152 } 153 154 return !check || check_crc (fd, debuglink_crc); 155} 156 157static int 158find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name, 159 const char *debuglink_file, GElf_Word debuglink_crc, 160 char **debuginfo_file_name) 161{ 162 bool cancheck = debuglink_crc != (GElf_Word) 0; 163 164 const char *file_basename = file_name == NULL ? NULL : basename (file_name); 165 char *localname = NULL; 166 if (debuglink_file == NULL) 167 { 168 /* For a alt debug multi file we need a name, for a separate debug 169 name we may be able to fall back on file_basename.debug. */ 170 if (file_basename == NULL || mod->dw != NULL) 171 { 172 errno = 0; 173 return -1; 174 } 175 176 size_t len = strlen (file_basename); 177 localname = malloc (len + sizeof ".debug"); 178 if (unlikely (localname == NULL)) 179 return -1; 180 memcpy (localname, file_basename, len); 181 memcpy (&localname[len], ".debug", sizeof ".debug"); 182 debuglink_file = localname; 183 cancheck = false; 184 } 185 186 /* Look for a file named DEBUGLINK_FILE in the directories 187 indicated by the debug directory path setting. */ 188 189 const Dwfl_Callbacks *const cb = mod->dwfl->callbacks; 190 char *localpath = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL) 191 ?: DEFAULT_DEBUGINFO_PATH); 192 if (unlikely (localpath == NULL)) 193 { 194 free (localname); 195 return -1; 196 } 197 198 /* A leading - or + in the whole path sets whether to check file CRCs. */ 199 bool defcheck = true; 200 char *path = localpath; 201 if (path[0] == '-' || path[0] == '+') 202 { 203 defcheck = path[0] == '+'; 204 ++path; 205 } 206 207 /* XXX dev/ino should be cached in struct dwfl_file. */ 208 struct stat main_stat; 209 if (unlikely ((mod->main.fd != -1 ? fstat (mod->main.fd, &main_stat) 210 : file_name != NULL ? stat (file_name, &main_stat) 211 : -1) < 0)) 212 { 213 main_stat.st_dev = 0; 214 main_stat.st_ino = 0; 215 } 216 217 char *file_dirname = (file_basename == file_name ? NULL 218 : strndup (file_name, file_basename - 1 - file_name)); 219 if (file_basename != file_name && file_dirname == NULL) 220 { 221 free (localpath); 222 free (localname); 223 return -1; 224 } 225 char *p; 226 while ((p = strsep (&path, ":")) != NULL) 227 { 228 /* A leading - or + says whether to check file CRCs for this element. */ 229 bool check = defcheck; 230 if (*p == '+' || *p == '-') 231 check = *p++ == '+'; 232 check = check && cancheck; 233 234 const char *dir, *subdir, *file; 235 switch (p[0]) 236 { 237 case '\0': 238 /* An empty entry says to try the main file's directory. */ 239 dir = file_dirname; 240 subdir = NULL; 241 file = debuglink_file; 242 break; 243 case '/': 244 /* An absolute path says to look there for a subdirectory 245 named by the main file's absolute directory. This cannot 246 be applied to a relative file name. For alt debug files 247 it means to look for the basename file in that dir or the 248 .dwz subdir (see below). */ 249 if (mod->dw == NULL 250 && (file_dirname == NULL || file_dirname[0] != '/')) 251 continue; 252 dir = p; 253 if (mod->dw == NULL) 254 { 255 subdir = file_dirname; 256 /* We want to explore all sub-subdirs. Chop off one slash 257 at a time. */ 258 explore_dir: 259 subdir = strchr (subdir, '/'); 260 if (subdir != NULL) 261 subdir = subdir + 1; 262 if (subdir && *subdir == 0) 263 continue; 264 file = debuglink_file; 265 } 266 else 267 { 268 subdir = NULL; 269 file = basename (debuglink_file); 270 } 271 break; 272 default: 273 /* A relative path says to try a subdirectory of that name 274 in the main file's directory. */ 275 dir = file_dirname; 276 subdir = p; 277 file = debuglink_file; 278 break; 279 } 280 281 char *fname = NULL; 282 int fd = try_open (&main_stat, dir, subdir, file, &fname); 283 if (fd < 0) 284 switch (errno) 285 { 286 case ENOENT: 287 case ENOTDIR: 288 /* If we are looking for the alt file also try the .dwz subdir. 289 But only if this is the empty or absolute path. */ 290 if (mod->dw != NULL && (p[0] == '\0' || p[0] == '/')) 291 { 292 fd = try_open (&main_stat, dir, ".dwz", 293 basename (file), &fname); 294 if (fd < 0) 295 { 296 if (errno != ENOENT && errno != ENOTDIR) 297 goto fail_free; 298 else 299 continue; 300 } 301 break; 302 } 303 /* If possible try again with a sub-subdir. */ 304 if (mod->dw == NULL && subdir) 305 goto explore_dir; 306 continue; 307 default: 308 goto fail_free; 309 } 310 if (validate (mod, fd, check, debuglink_crc)) 311 { 312 free (localpath); 313 free (localname); 314 free (file_dirname); 315 *debuginfo_file_name = fname; 316 return fd; 317 } 318 free (fname); 319 close (fd); 320 } 321 322 /* No dice. */ 323 errno = 0; 324fail_free: 325 free (localpath); 326 free (localname); 327 free (file_dirname); 328 return -1; 329} 330 331int 332dwfl_standard_find_debuginfo (Dwfl_Module *mod, 333 void **userdata __attribute__ ((unused)), 334 const char *modname __attribute__ ((unused)), 335 GElf_Addr base __attribute__ ((unused)), 336 const char *file_name, 337 const char *debuglink_file, 338 GElf_Word debuglink_crc, 339 char **debuginfo_file_name) 340{ 341 /* First try by build ID if we have one. If that succeeds or fails 342 other than just by finding nothing, that's all we do. */ 343 const unsigned char *bits; 344 GElf_Addr vaddr; 345 if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0) 346 { 347 /* Dropping most arguments means we cannot rely on them in 348 dwfl_build_id_find_debuginfo. But leave it that way since 349 some user code out there also does this, so we'll have to 350 handle it anyway. */ 351 int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod, 352 NULL, NULL, 0, 353 NULL, NULL, 0, 354 debuginfo_file_name); 355 356 /* Did the build_id callback find something or report an error? 357 Then we are done. Otherwise fallback on path based search. */ 358 if (fd >= 0 359 || (mod->dw == NULL && mod->debug.elf != NULL) 360 || (mod->dw != NULL && mod->alt_elf != NULL) 361 || errno != 0) 362 return fd; 363 } 364 365 /* Failing that, search the path by name. */ 366 int fd = find_debuginfo_in_path (mod, file_name, 367 debuglink_file, debuglink_crc, 368 debuginfo_file_name); 369 370 if (fd < 0 && errno == 0 && file_name != NULL) 371 { 372 /* If FILE_NAME is a symlink, the debug file might be associated 373 with the symlink target name instead. */ 374 375 char *canon = canonicalize_file_name (file_name); 376 if (canon != NULL && strcmp (file_name, canon)) 377 fd = find_debuginfo_in_path (mod, canon, 378 debuglink_file, debuglink_crc, 379 debuginfo_file_name); 380 free (canon); 381 } 382 383 return fd; 384} 385INTDEF (dwfl_standard_find_debuginfo) 386