Imported Upstream version 0.155
[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 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 {
39   /* If *FILE_NAME was primed into the module, leave it there
40      as the fallback when we have nothing to offer.  */
41   errno = 0;
42   if (mod->build_id_len <= 0)
43     return -1;
44
45   const size_t id_len = mod->build_id_len;
46   const uint8_t *id = mod->build_id_bits;
47
48   /* Search debuginfo_path directories' .build-id/ subdirectories.  */
49
50   char id_name[sizeof "/.build-id/" + 1 + id_len * 2 + sizeof ".debug" - 1];
51   strcpy (id_name, "/.build-id/");
52   int n = snprintf (&id_name[sizeof "/.build-id/" - 1],
53                     4, "%02" PRIx8 "/", (uint8_t) id[0]);
54   assert (n == 3);
55   for (size_t i = 1; i < id_len; ++i)
56     {
57       n = snprintf (&id_name[sizeof "/.build-id/" - 1 + 3 + (i - 1) * 2],
58                     3, "%02" PRIx8, (uint8_t) id[i]);
59       assert (n == 2);
60     }
61   if (debug)
62     strcpy (&id_name[sizeof "/.build-id/" - 1 + 3 + (id_len - 1) * 2],
63             ".debug");
64
65   const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
66   char *path = strdupa ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
67                         ?: DEFAULT_DEBUGINFO_PATH);
68
69   int fd = -1;
70   char *dir;
71   while (fd < 0 && (dir = strsep (&path, ":")) != NULL)
72     {
73       if (dir[0] == '+' || dir[0] == '-')
74         ++dir;
75
76       /* Only absolute directory names are useful to us.  */
77       if (dir[0] != '/')
78         continue;
79
80       size_t dirlen = strlen (dir);
81       char *name = malloc (dirlen + sizeof id_name);
82       if (unlikely (name == NULL))
83         break;
84       memcpy (mempcpy (name, dir, dirlen), id_name, sizeof id_name);
85
86       fd = TEMP_FAILURE_RETRY (open64 (name, O_RDONLY));
87       if (fd >= 0)
88         {
89           if (*file_name != NULL)
90             free (*file_name);
91           *file_name = canonicalize_file_name (name);
92           if (*file_name == NULL)
93             {
94               *file_name = name;
95               name = NULL;
96             }
97         }
98       free (name);
99     }
100
101   /* If we simply found nothing, clear errno.  If we had some other error
102      with the file, report that.  Possibly this should treat other errors
103      like ENOENT too.  But ignoring all errors could mask some that should
104      be reported.  */
105   if (fd < 0 && errno == ENOENT)
106     errno = 0;
107
108   return fd;
109 }
110
111 int
112 dwfl_build_id_find_elf (Dwfl_Module *mod,
113                         void **userdata __attribute__ ((unused)),
114                         const char *modname __attribute__ ((unused)),
115                         Dwarf_Addr base __attribute__ ((unused)),
116                         char **file_name, Elf **elfp)
117 {
118   *elfp = NULL;
119   int fd = __libdwfl_open_by_build_id (mod, false, file_name);
120   if (fd >= 0)
121     {
122       Dwfl_Error error = __libdw_open_file (&fd, elfp, true, false);
123       if (error != DWFL_E_NOERROR)
124         __libdwfl_seterrno (error);
125       else if (__libdwfl_find_build_id (mod, false, *elfp) == 2)
126         {
127           /* This is a backdoor signal to short-circuit the ID refresh.  */
128           mod->main.valid = true;
129           return fd;
130         }
131       else
132         {
133           /* This file does not contain the ID it should!  */
134           elf_end (*elfp);
135           *elfp = NULL;
136           close (fd);
137           fd = -1;
138         }
139       free (*file_name);
140       *file_name = NULL;
141     }
142   else if (errno == 0 && mod->build_id_len > 0)
143     /* Setting this with no file yet loaded is a marker that
144        the build ID is authoritative even if we also know a
145        putative *FILE_NAME.  */
146     mod->main.valid = true;
147
148   return fd;
149 }
150 INTDEF (dwfl_build_id_find_elf)