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
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  /* 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 (open (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
119int
120internal_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
135int
136dwfl_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
144      && mod->dwfl->user_core != NULL
145      && mod->dwfl->user_core->executable_for_core != NULL)
146    {
147      /* When dwfl_core_file_report was called with a non-NULL executable file
148	 name this callback will replace the Dwfl_Module main.name with the
149	 recorded executable file when MOD was identified as main executable
150	 (which then triggers opening and reporting of the executable).  */
151      const char *executable = mod->dwfl->user_core->executable_for_core;
152      int fd = open (executable, O_RDONLY);
153      if (fd >= 0)
154	{
155	  *file_name = strdup (executable);
156	  if (*file_name != NULL)
157	    return fd;
158	  else
159	    close (fd);
160	}
161    }
162  int fd = __libdwfl_open_mod_by_build_id (mod, false, file_name);
163  if (fd >= 0)
164    {
165      Dwfl_Error error = __libdw_open_file (&fd, elfp, true, false);
166      if (error != DWFL_E_NOERROR)
167	__libdwfl_seterrno (error);
168      else if (__libdwfl_find_build_id (mod, false, *elfp) == 2)
169	{
170	  /* This is a backdoor signal to short-circuit the ID refresh.  */
171	  mod->main.valid = true;
172	  return fd;
173	}
174      else
175	{
176	  /* This file does not contain the ID it should!  */
177	  elf_end (*elfp);
178	  *elfp = NULL;
179	  close (fd);
180	  fd = -1;
181	}
182      free (*file_name);
183      *file_name = NULL;
184    }
185  else if (errno == 0 && mod->build_id_len > 0)
186    /* Setting this with no file yet loaded is a marker that
187       the build ID is authoritative even if we also know a
188       putative *FILE_NAME.  */
189    mod->main.valid = true;
190
191  return fd;
192}
193INTDEF (dwfl_build_id_find_elf)
194