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