Coverage Report

Created: 2025-12-27 06:24

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