1 /* Standard find_debuginfo callback for libdwfl.
2 Copyright (C) 2005-2010 Red Hat, Inc.
3 This file is part of elfutils.
5 This file is free software; you can redistribute it and/or modify
6 it under the terms of either
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
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
18 or both in parallel, as here.
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.
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/>. */
37 /* Try to open64 [DIR/][SUBDIR/]DEBUGLINK, return file descriptor or -1.
38 On success, *DEBUGINFO_FILE_NAME has the malloc'd name of the open file. */
40 try_open (const struct stat64 *main_stat,
41 const char *dir, const char *subdir, const char *debuglink,
42 char **debuginfo_file_name)
45 if (dir == NULL && subdir == NULL)
47 fname = strdup (debuglink);
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)
57 int fd = TEMP_FAILURE_RETRY (open64 (fname, O_RDONLY));
60 else if (fstat64 (fd, &st) == 0
61 && st.st_ino == main_stat->st_ino
62 && st.st_dev == main_stat->st_dev)
64 /* This is the main file by another name. Don't look at it again. */
70 *debuginfo_file_name = fname;
75 /* Return true iff the FD's contents CRC matches DEBUGLINK_CRC. */
77 check_crc (int fd, GElf_Word debuglink_crc)
80 return (__libdwfl_crc32_file (fd, &file_crc) == 0
81 && file_crc == debuglink_crc);
85 validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc)
87 /* If we have a build ID, check only that. */
88 if (mod->build_id_len > 0)
90 /* We need to open an Elf handle on the file so we can check its
91 build ID note for validation. Backdoor the handle into the
92 module data structure since we had to open it early anyway. */
94 mod->debug.valid = false;
95 Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false);
96 if (error != DWFL_E_NOERROR)
97 __libdwfl_seterrno (error);
98 else if (likely (__libdwfl_find_build_id (mod, false,
99 mod->debug.elf) == 2))
100 /* Also backdoor the gratuitous flag. */
101 mod->debug.valid = true;
105 elf_end (mod->debug.elf);
106 mod->debug.elf = NULL;
111 return mod->debug.valid;
114 return !check || check_crc (fd, debuglink_crc);
118 find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name,
119 const char *debuglink_file, GElf_Word debuglink_crc,
120 char **debuginfo_file_name)
122 bool cancheck = debuglink_crc != (GElf_Word) 0;
124 const char *file_basename = file_name == NULL ? NULL : basename (file_name);
125 if (debuglink_file == NULL)
127 if (file_basename == NULL)
133 size_t len = strlen (file_basename);
134 char *localname = alloca (len + sizeof ".debug");
135 memcpy (localname, file_basename, len);
136 memcpy (&localname[len], ".debug", sizeof ".debug");
137 debuglink_file = localname;
141 /* Look for a file named DEBUGLINK_FILE in the directories
142 indicated by the debug directory path setting. */
144 const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
145 char *path = strdupa ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
146 ?: DEFAULT_DEBUGINFO_PATH);
148 /* A leading - or + in the whole path sets whether to check file CRCs. */
149 bool defcheck = true;
150 if (path[0] == '-' || path[0] == '+')
152 defcheck = path[0] == '+';
156 /* XXX dev/ino should be cached in struct dwfl_file. */
157 struct stat64 main_stat;
158 if (unlikely ((mod->main.fd != -1 ? fstat64 (mod->main.fd, &main_stat)
159 : file_name != NULL ? stat64 (file_name, &main_stat)
162 main_stat.st_dev = 0;
163 main_stat.st_ino = 0;
166 char *file_dirname = (file_basename == file_name ? NULL
167 : strndupa (file_name, file_basename - 1 - file_name));
169 while ((p = strsep (&path, ":")) != NULL)
171 /* A leading - or + says whether to check file CRCs for this element. */
172 bool check = defcheck;
173 if (*p == '+' || *p == '-')
175 check = check && cancheck;
177 const char *dir, *subdir;
181 /* An empty entry says to try the main file's directory. */
186 /* An absolute path says to look there for a subdirectory
187 named by the main file's absolute directory.
188 This cannot be applied to a relative file name. */
189 if (file_dirname == NULL || file_dirname[0] != '/')
192 subdir = file_dirname + 1;
195 /* A relative path says to try a subdirectory of that name
196 in the main file's directory. */
203 int fd = try_open (&main_stat, dir, subdir, debuglink_file, &fname);
213 if (validate (mod, fd, check, debuglink_crc))
215 *debuginfo_file_name = fname;
228 dwfl_standard_find_debuginfo (Dwfl_Module *mod,
229 void **userdata __attribute__ ((unused)),
230 const char *modname __attribute__ ((unused)),
231 GElf_Addr base __attribute__ ((unused)),
232 const char *file_name,
233 const char *debuglink_file,
234 GElf_Word debuglink_crc,
235 char **debuginfo_file_name)
237 /* First try by build ID if we have one. If that succeeds or fails
238 other than just by finding nothing, that's all we do. */
239 const unsigned char *bits;
241 if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0)
243 int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
246 debuginfo_file_name);
247 if (fd >= 0 || mod->debug.elf != NULL || errno != 0)
251 /* Failing that, search the path by name. */
252 int fd = find_debuginfo_in_path (mod, file_name,
253 debuglink_file, debuglink_crc,
254 debuginfo_file_name);
256 if (fd < 0 && errno == 0)
258 /* If FILE_NAME is a symlink, the debug file might be associated
259 with the symlink target name instead. */
261 char *canon = canonicalize_file_name (file_name);
262 if (canon != NULL && strcmp (file_name, canon))
263 fd = find_debuginfo_in_path (mod, canon,
264 debuglink_file, debuglink_crc,
265 debuginfo_file_name);
271 INTDEF (dwfl_standard_find_debuginfo)