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