libdwfl: Make dwfl_linux_proc_attach work even without any Dwfl_Modules.
[platform/upstream/elfutils.git] / libdwfl / dwfl_build_id_find_elf.c
1 /* Find an ELF file for a module from its build ID.
2    Copyright (C) 2007-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 <inttypes.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33
34
35 int
36 internal_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   /* We don't handle very short or really large build-ids.  We need at
41      at least 3 and allow for up to 64 (normally ids are 20 long).  */
42 #define MIN_BUILD_ID_BYTES 3
43 #define MAX_BUILD_ID_BYTES 64
44   if (id_len < MIN_BUILD_ID_BYTES || id_len > MAX_BUILD_ID_BYTES)
45     {
46       __libdwfl_seterrno (DWFL_E_WRONG_ID_ELF);
47       return -1;
48     }
49
50   /* Search debuginfo_path directories' .build-id/ subdirectories.  */
51
52   char id_name[sizeof "/.build-id/" + 1 + MAX_BUILD_ID_BYTES * 2
53                + sizeof ".debug" - 1];
54   strcpy (id_name, "/.build-id/");
55   int n = snprintf (&id_name[sizeof "/.build-id/" - 1],
56                     4, "%02" PRIx8 "/", (uint8_t) id[0]);
57   assert (n == 3);
58   for (size_t i = 1; i < id_len; ++i)
59     {
60       n = snprintf (&id_name[sizeof "/.build-id/" - 1 + 3 + (i - 1) * 2],
61                     3, "%02" PRIx8, (uint8_t) id[i]);
62       assert (n == 2);
63     }
64   if (debug)
65     strcpy (&id_name[sizeof "/.build-id/" - 1 + 3 + (id_len - 1) * 2],
66             ".debug");
67
68   const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
69   char *path = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
70                        ?: DEFAULT_DEBUGINFO_PATH);
71   if (path == NULL)
72     return -1;
73
74   int fd = -1;
75   char *dir;
76   char *paths = path;
77   while (fd < 0 && (dir = strsep (&paths, ":")) != NULL)
78     {
79       if (dir[0] == '+' || dir[0] == '-')
80         ++dir;
81
82       /* Only absolute directory names are useful to us.  */
83       if (dir[0] != '/')
84         continue;
85
86       size_t dirlen = strlen (dir);
87       char *name = malloc (dirlen + sizeof id_name);
88       if (unlikely (name == NULL))
89         break;
90       memcpy (mempcpy (name, dir, dirlen), id_name, sizeof id_name);
91
92       fd = TEMP_FAILURE_RETRY (open64 (name, O_RDONLY));
93       if (fd >= 0)
94         {
95           if (*file_name != NULL)
96             free (*file_name);
97           *file_name = canonicalize_file_name (name);
98           if (*file_name == NULL)
99             {
100               *file_name = name;
101               name = NULL;
102             }
103         }
104       free (name);
105     }
106
107   free (path);
108
109   /* If we simply found nothing, clear errno.  If we had some other error
110      with the file, report that.  Possibly this should treat other errors
111      like ENOENT too.  But ignoring all errors could mask some that should
112      be reported.  */
113   if (fd < 0 && errno == ENOENT)
114     errno = 0;
115
116   return fd;
117 }
118
119 int
120 internal_function
121 __libdwfl_open_mod_by_build_id (Dwfl_Module *mod, bool debug, char **file_name)
122 {
123   /* If *FILE_NAME was primed into the module, leave it there
124      as the fallback when we have nothing to offer.  */
125   errno = 0;
126   if (mod->build_id_len <= 0)
127     return -1;
128
129   const size_t id_len = mod->build_id_len;
130   const uint8_t *id = mod->build_id_bits;
131
132   return __libdwfl_open_by_build_id (mod, debug, file_name, id_len, id);
133 }
134
135 int
136 dwfl_build_id_find_elf (Dwfl_Module *mod,
137                         void **userdata __attribute__ ((unused)),
138                         const char *modname __attribute__ ((unused)),
139                         Dwarf_Addr base __attribute__ ((unused)),
140                         char **file_name, Elf **elfp)
141 {
142   *elfp = NULL;
143   if (mod->is_executable && mod->dwfl->executable_for_core != NULL)
144     {
145       /* When dwfl_core_file_report was called with a non-NULL executable file
146          name this callback will replace the Dwfl_Module main.name with the
147          recorded executable file when MOD was identified as main executable
148          (which then triggers opening and reporting of the executable).  */
149       int fd = open64 (mod->dwfl->executable_for_core, O_RDONLY);
150       if (fd >= 0)
151         {
152           *file_name = strdup (mod->dwfl->executable_for_core);
153           if (*file_name != NULL)
154             return fd;
155           else
156             close (fd);
157         }
158     }
159   int fd = __libdwfl_open_mod_by_build_id (mod, false, file_name);
160   if (fd >= 0)
161     {
162       Dwfl_Error error = __libdw_open_file (&fd, elfp, true, false);
163       if (error != DWFL_E_NOERROR)
164         __libdwfl_seterrno (error);
165       else if (__libdwfl_find_build_id (mod, false, *elfp) == 2)
166         {
167           /* This is a backdoor signal to short-circuit the ID refresh.  */
168           mod->main.valid = true;
169           return fd;
170         }
171       else
172         {
173           /* This file does not contain the ID it should!  */
174           elf_end (*elfp);
175           *elfp = NULL;
176           close (fd);
177           fd = -1;
178         }
179       free (*file_name);
180       *file_name = NULL;
181     }
182   else if (errno == 0 && mod->build_id_len > 0)
183     /* Setting this with no file yet loaded is a marker that
184        the build ID is authoritative even if we also know a
185        putative *FILE_NAME.  */
186     mod->main.valid = true;
187
188   return fd;
189 }
190 INTDEF (dwfl_build_id_find_elf)