155500824ad6f327acf3dbb6cb4eb3a189ccccdf
[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 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   /* 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
103 int
104 internal_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
119 int
120 dwfl_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 (modname != NULL && mod->dwfl->executable_for_core != NULL
128       && (strcmp (modname, "[exe]") == 0 || strcmp (modname, "[pie]") == 0))
129     {
130       /* When dwfl_core_file_report was called with a non-NULL executable file
131          name this callback will replace the Dwfl_Module main.name with the
132          recorded executable file when the modname is [exe] or [pie] (which
133          then triggers opening and reporting of the executable).  */
134       int fd = open64 (mod->dwfl->executable_for_core, O_RDONLY);
135       if (fd >= 0)
136         {
137           *file_name = strdup (mod->dwfl->executable_for_core);
138           if (*file_name != NULL)
139             return fd;
140           else
141             close (fd);
142         }
143     }
144   int fd = __libdwfl_open_mod_by_build_id (mod, false, file_name);
145   if (fd >= 0)
146     {
147       Dwfl_Error error = __libdw_open_file (&fd, elfp, true, false);
148       if (error != DWFL_E_NOERROR)
149         __libdwfl_seterrno (error);
150       else if (__libdwfl_find_build_id (mod, false, *elfp) == 2)
151         {
152           /* This is a backdoor signal to short-circuit the ID refresh.  */
153           mod->main.valid = true;
154           return fd;
155         }
156       else
157         {
158           /* This file does not contain the ID it should!  */
159           elf_end (*elfp);
160           *elfp = NULL;
161           close (fd);
162           fd = -1;
163         }
164       free (*file_name);
165       *file_name = NULL;
166     }
167   else if (errno == 0 && mod->build_id_len > 0)
168     /* Setting this with no file yet loaded is a marker that
169        the build ID is authoritative even if we also know a
170        putative *FILE_NAME.  */
171     mod->main.valid = true;
172
173   return fd;
174 }
175 INTDEF (dwfl_build_id_find_elf)