summaryrefslogtreecommitdiffstats
path: root/libdwfl/dwfl_build_id_find_elf.c
blob: cc6c3f62d276f9f325f4f37ebb60230c970a8cd9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/* Find an ELF file for a module from its build ID.
   Copyright (C) 2007-2010, 2014, 2015 Red Hat, Inc.
   This file is part of elfutils.

   This file is free software; you can redistribute it and/or modify
   it under the terms of either

     * the GNU Lesser General Public License as published by the Free
       Software Foundation; either version 3 of the License, or (at
       your option) any later version

   or

     * the GNU General Public License as published by the Free
       Software Foundation; either version 2 of the License, or (at
       your option) any later version

   or both in parallel, as here.

   elfutils is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received copies of the GNU General Public License and
   the GNU Lesser General Public License along with this program.  If
   not, see <http://www.gnu.org/licenses/>.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include "libdwflP.h"
#include <inttypes.h>
#include <fcntl.h>
#include <unistd.h>
#include "system.h"


int
internal_function
__libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug, char **file_name,
			    const size_t id_len, const uint8_t *id)
{
  /* We don't handle very short or really large build-ids.  We need at
     at least 3 and allow for up to 64 (normally ids are 20 long).  */
#define MIN_BUILD_ID_BYTES 3
#define MAX_BUILD_ID_BYTES 64
  if (id_len < MIN_BUILD_ID_BYTES || id_len > MAX_BUILD_ID_BYTES)
    {
      __libdwfl_seterrno (DWFL_E_WRONG_ID_ELF);
      return -1;
    }

  /* Search debuginfo_path directories' .build-id/ subdirectories.  */

  char id_name[sizeof "/.build-id/" + 1 + MAX_BUILD_ID_BYTES * 2
	       + sizeof ".debug" - 1];
  strcpy (id_name, "/.build-id/");
  int n = snprintf (&id_name[sizeof "/.build-id/" - 1],
		    4, "%02" PRIx8 "/", (uint8_t) id[0]);
  assert (n == 3);
  for (size_t i = 1; i < id_len; ++i)
    {
      n = snprintf (&id_name[sizeof "/.build-id/" - 1 + 3 + (i - 1) * 2],
		    3, "%02" PRIx8, (uint8_t) id[i]);
      assert (n == 2);
    }
  if (debug)
    strcpy (&id_name[sizeof "/.build-id/" - 1 + 3 + (id_len - 1) * 2],
	    ".debug");

  const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
  char *path = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
		       ?: DEFAULT_DEBUGINFO_PATH);
  if (path == NULL)
    return -1;

  int fd = -1;
  char *dir;
  char *paths = path;
  while (fd < 0 && (dir = strsep (&paths, ":")) != NULL)
    {
      if (dir[0] == '+' || dir[0] == '-')
	++dir;

      /* Only absolute directory names are useful to us.  */
      if (dir[0] != '/')
	continue;

      size_t dirlen = strlen (dir);
      char *name = malloc (dirlen + sizeof id_name);
      if (unlikely (name == NULL))
	break;
      memcpy (mempcpy (name, dir, dirlen), id_name, sizeof id_name);

      fd = TEMP_FAILURE_RETRY (open (name, O_RDONLY));
      if (fd >= 0)
	{
	  if (*file_name != NULL)
	    free (*file_name);
	  *file_name = realpath (name, NULL);
	  if (*file_name == NULL)
	    {
	      *file_name = name;
	      name = NULL;
	    }
	}
      free (name);
    }

  free (path);

  /* If we simply found nothing, clear errno.  If we had some other error
     with the file, report that.  Possibly this should treat other errors
     like ENOENT too.  But ignoring all errors could mask some that should
     be reported.  */
  if (fd < 0 && errno == ENOENT)
    errno = 0;

  return fd;
}

int
internal_function
__libdwfl_open_mod_by_build_id (Dwfl_Module *mod, bool debug, char **file_name)
{
  /* If *FILE_NAME was primed into the module, leave it there
     as the fallback when we have nothing to offer.  */
  errno = 0;
  if (mod->build_id_len <= 0)
    return -1;

  const size_t id_len = mod->build_id_len;
  const uint8_t *id = mod->build_id_bits;

  return __libdwfl_open_by_build_id (mod, debug, file_name, id_len, id);
}

int
dwfl_build_id_find_elf (Dwfl_Module *mod,
			void **userdata __attribute__ ((unused)),
			const char *modname __attribute__ ((unused)),
			Dwarf_Addr base __attribute__ ((unused)),
			char **file_name, Elf **elfp)
{
  *elfp = NULL;
  if (mod->is_executable
      && mod->dwfl->user_core != NULL
      && mod->dwfl->user_core->executable_for_core != NULL)
    {
      /* When dwfl_core_file_report was called with a non-NULL executable file
	 name this callback will replace the Dwfl_Module main.name with the
	 recorded executable file when MOD was identified as main executable
	 (which then triggers opening and reporting of the executable).  */
      const char *executable = mod->dwfl->user_core->executable_for_core;
      int fd = open (executable, O_RDONLY);
      if (fd >= 0)
	{
	  *file_name = strdup (executable);
	  if (*file_name != NULL)
	    return fd;
	  else
	    close (fd);
	}
    }
  int fd = __libdwfl_open_mod_by_build_id (mod, false, file_name);
  if (fd >= 0)
    {
      Dwfl_Error error = __libdw_open_file (&fd, elfp, true, false);
      if (error != DWFL_E_NOERROR)
	__libdwfl_seterrno (error);
      else if (__libdwfl_find_build_id (mod, false, *elfp) == 2)
	{
	  /* This is a backdoor signal to short-circuit the ID refresh.  */
	  mod->main.valid = true;
	  return fd;
	}
      else
	{
	  /* This file does not contain the ID it should!  */
	  elf_end (*elfp);
	  *elfp = NULL;
	  close (fd);
	  fd = -1;
	}
      free (*file_name);
      *file_name = NULL;
    }
  else if (errno == 0 && mod->build_id_len > 0)
    /* Setting this with no file yet loaded is a marker that
       the build ID is authoritative even if we also know a
       putative *FILE_NAME.  */
    mod->main.valid = true;

  return fd;
}
INTDEF (dwfl_build_id_find_elf)