Coverage Report

Created: 2026-09-06 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/elfutils/libdwfl/offline.c
Line
Count
Source
1
/* Recover relocatibility for addresses computed from debug information.
2
   Copyright (C) 2005-2009, 2012 Red Hat, Inc.
3
   Copyright (C) 2022 Mark J. Wielaard <mark@klomp.org>
4
   Copyright (C) 2022 Google LLC
5
   This file is part of elfutils.
6
7
   This file is free software; you can redistribute it and/or modify
8
   it under the terms of either
9
10
     * the GNU Lesser General Public License as published by the Free
11
       Software Foundation; either version 3 of the License, or (at
12
       your option) any later version
13
14
   or
15
16
     * the GNU General Public License as published by the Free
17
       Software Foundation; either version 2 of the License, or (at
18
       your option) any later version
19
20
   or both in parallel, as here.
21
22
   elfutils is distributed in the hope that it will be useful, but
23
   WITHOUT ANY WARRANTY; without even the implied warranty of
24
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25
   General Public License for more details.
26
27
   You should have received copies of the GNU General Public License and
28
   the GNU Lesser General Public License along with this program.  If
29
   not, see <http://www.gnu.org/licenses/>.  */
30
31
#ifdef HAVE_CONFIG_H
32
# include <config.h>
33
#endif
34
35
#include "libdwflP.h"
36
#include <fcntl.h>
37
38
/* Since dwfl_report_elf lays out the sections already, this will only be
39
   called when the section headers of the debuginfo file are being
40
   consulted instead, or for the section placed at 0.  With binutils
41
   strip-to-debug, the symbol table is in the debuginfo file and relocation
42
   looks there.  */
43
int
44
dwfl_offline_section_address (Dwfl_Module *mod,
45
            void **userdata __attribute__ ((unused)),
46
            const char *modname __attribute__ ((unused)),
47
            Dwarf_Addr base __attribute__ ((unused)),
48
            const char *secname __attribute__ ((unused)),
49
            Elf32_Word shndx,
50
            const GElf_Shdr *shdr __attribute__ ((unused)),
51
            Dwarf_Addr *addr)
52
0
{
53
0
  if (mod->e_type != ET_REL
54
0
      || shdr->sh_addr != 0
55
0
      || !(shdr->sh_flags & SHF_ALLOC)
56
0
      || shndx == 0)
57
0
    return -1;
58
59
0
  if (mod->debug.elf == NULL)
60
    /* We are only here because sh_addr is zero even though layout is complete.
61
       The first section in the first file under -e is placed at 0.  */
62
0
    return 0;
63
64
  /* The section numbers might not match between the two files.
65
     The best we can rely on is the order of SHF_ALLOC sections.  */
66
67
0
  Elf_Scn *ourscn = elf_getscn (mod->debug.elf, shndx);
68
0
  Elf_Scn *scn = NULL;
69
0
  uint_fast32_t skip_alloc = 0;
70
0
  while ((scn = elf_nextscn (mod->debug.elf, scn)) != ourscn)
71
0
    {
72
0
      assert (scn != NULL);
73
0
      GElf_Shdr shdr_mem;
74
0
      GElf_Shdr *sh = gelf_getshdr (scn, &shdr_mem);
75
0
      if (unlikely (sh == NULL))
76
0
  return -1;
77
0
      if (sh->sh_flags & SHF_ALLOC)
78
0
  ++skip_alloc;
79
0
    }
80
81
0
  scn = NULL;
82
0
  while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
83
0
    {
84
0
      GElf_Shdr shdr_mem;
85
0
      GElf_Shdr *main_shdr = gelf_getshdr (scn, &shdr_mem);
86
0
      if (unlikely (main_shdr == NULL))
87
0
  return -1;
88
0
      if ((main_shdr->sh_flags & SHF_ALLOC) && skip_alloc-- == 0)
89
0
  {
90
0
    assert (main_shdr->sh_flags == shdr->sh_flags);
91
0
    *addr = main_shdr->sh_addr;
92
0
    return 0;
93
0
  }
94
0
    }
95
96
  /* This should never happen.  */
97
0
  return -1;
98
0
}
99
INTDEF (dwfl_offline_section_address)
100
101
/* Forward declarations.  */
102
static Dwfl_Module *process_elf (Dwfl *dwfl, const char *name,
103
         const char *file_name, int fd, Elf *elf);
104
static Dwfl_Module *process_archive (Dwfl *dwfl, const char *name,
105
             const char *file_name, int fd, Elf *elf,
106
             int (*predicate) (const char *module,
107
                   const char *file));
108
109
/* Report one module for an ELF file, or many for an archive.
110
   Always consumes ELF and FD.  */
111
static Dwfl_Module *
112
process_file (Dwfl *dwfl, const char *name, const char *file_name, int fd,
113
        Elf *elf, int (*predicate) (const char *module,
114
            const char *file))
115
174k
{
116
174k
  switch (elf_kind (elf))
117
174k
    {
118
0
    default:
119
174
    case ELF_K_NONE:
120
174
      __libdwfl_seterrno (elf == NULL ? DWFL_E_LIBELF : DWFL_E_BADELF);
121
174
      return NULL;
122
123
14.5k
    case ELF_K_ELF:
124
14.5k
      return process_elf (dwfl, name, file_name, fd, elf);
125
126
160k
    case ELF_K_AR:
127
160k
      return process_archive (dwfl, name, file_name, fd, elf, predicate);
128
174k
    }
129
174k
}
130
131
/* Report the open ELF file as a module.  Always consumes ELF and FD.  */
132
static Dwfl_Module *
133
process_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd,
134
       Elf *elf)
135
14.5k
{
136
14.5k
  Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name, fd, elf,
137
14.5k
             dwfl->offline_next_address, true,
138
14.5k
             false);
139
14.5k
  if (mod != NULL)
140
13.7k
    {
141
      /* If this is an ET_EXEC file with fixed addresses, the address range
142
   it consumed may or may not intersect with the arbitrary range we
143
   will use for relocatable modules.  Make sure we always use a free
144
   range for the offline allocations.  If this module did use
145
   offline_next_address, it may have rounded it up for the module's
146
   alignment requirements.  */
147
13.7k
      if ((dwfl->offline_next_address >= mod->low_addr
148
1.71k
     || mod->low_addr - dwfl->offline_next_address < OFFLINE_REDZONE)
149
12.5k
    && dwfl->offline_next_address < mod->high_addr + OFFLINE_REDZONE)
150
2.56k
  dwfl->offline_next_address = mod->high_addr + OFFLINE_REDZONE;
151
152
      /* Don't keep the file descriptor around.  */
153
13.7k
      if (mod->main.fd != -1 && elf_cntl (mod->main.elf, ELF_C_FDREAD) == 0)
154
11.8k
  {
155
    /* Grab the path in case we want to report this file as
156
       Dwarf later.  */
157
11.8k
    mod->elfpath = __libdw_elfpath (mod->main.fd);
158
11.8k
    close (mod->main.fd);
159
11.8k
    mod->main.fd = -1;
160
11.8k
  }
161
13.7k
    }
162
163
14.5k
  return mod;
164
14.5k
}
165
166
/* Always consumes MEMBER.  Returns elf_next result on success.
167
   For errors returns ELF_C_NULL with *MOD set to null.  */
168
static Elf_Cmd
169
process_archive_member (Dwfl *dwfl, const char *name, const char *file_name,
170
      int (*predicate) (const char *module, const char *file),
171
      int fd, Elf *member, Dwfl_Module **mod)
172
166k
{
173
166k
  const Elf_Arhdr *h = elf_getarhdr (member);
174
166k
  if (unlikely (h == NULL))
175
12
    {
176
12
      __libdwfl_seterrno (DWFL_E_LIBELF);
177
12
    fail:
178
12
      elf_end (member);
179
12
      *mod = NULL;
180
12
      return ELF_C_NULL;
181
12
    }
182
183
166k
  if (!strcmp (h->ar_name, "/") || !strcmp (h->ar_name, "//")
184
165k
      || !strcmp (h->ar_name, "/SYM64/"))
185
500
    {
186
500
    skip:;
187
      /* Skip this and go to the next.  */
188
500
      Elf_Cmd result = elf_next (member);
189
500
      elf_end (member);
190
500
      return result;
191
500
    }
192
193
165k
  char *member_name;
194
165k
  if (unlikely (asprintf (&member_name, "%s(%s)", file_name, h->ar_name) < 0))
195
0
    {
196
0
    nomem:
197
0
      __libdwfl_seterrno (DWFL_E_NOMEM);
198
0
      elf_end (member);
199
0
      *mod = NULL;
200
0
      return ELF_C_NULL;
201
0
    }
202
203
165k
  char *module_name = NULL;
204
165k
  if (name == NULL || name[0] == '\0')
205
0
    name = h->ar_name;
206
165k
  else if (unlikely (asprintf (&module_name, "%s:%s", name, h->ar_name) < 0))
207
0
    {
208
0
      free (member_name);
209
0
      goto nomem;
210
0
    }
211
165k
  else
212
165k
    name = module_name;
213
214
165k
  if (predicate != NULL)
215
0
    {
216
      /* Let the predicate decide whether to use this one.  */
217
0
      int want = (*predicate) (name, member_name);
218
0
      if (want <= 0)
219
0
  {
220
0
    free (member_name);
221
0
    free (module_name);
222
0
    if (unlikely (want < 0))
223
0
      {
224
0
        __libdwfl_seterrno (DWFL_E_CB);
225
0
        goto fail;
226
0
      }
227
0
    goto skip;
228
0
  }
229
0
    }
230
231
  /* We let __libdwfl_report_elf cache the fd in mod->main.fd,
232
     though it's the same fd for all the members.
233
     On module teardown we will close it only on the last Elf reference.  */
234
165k
  *mod = process_file (dwfl, name, member_name, fd, member, predicate);
235
165k
  free (member_name);
236
165k
  free (module_name);
237
238
165k
  if (*mod == NULL)
239
146k
    {
240
146k
      elf_end (member);
241
146k
      return ELF_C_NULL;
242
146k
    }
243
244
  /* Advance the archive-reading offset for the next iteration.  */
245
19.4k
  return elf_next (member);
246
165k
}
247
248
/* Report each member of the archive as its own module.  */
249
static Dwfl_Module *
250
process_archive (Dwfl *dwfl, const char *name, const char *file_name, int fd,
251
     Elf *archive,
252
     int (*predicate) (const char *module, const char *file))
253
254
160k
{
255
160k
  Dwfl_Module *mod = NULL;
256
  /* elf_begin supports opening archives even with fd == -1 passed.  */
257
160k
  Elf *member = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, archive);
258
160k
  if (unlikely (member == NULL)) /* Empty archive.  */
259
829
    {
260
829
      __libdwfl_seterrno (DWFL_E_BADELF);
261
829
      return NULL;
262
829
    }
263
264
166k
  while (process_archive_member (dwfl, name, file_name, predicate,
265
166k
         fd, member, &mod) != ELF_C_NULL)
266
6.87k
    member = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, archive);
267
268
  /* We can drop the archive Elf handle even if we're still using members
269
     in live modules.  When the last module's elf_end on a member returns
270
     zero, that module will close FD.  If no modules survived the predicate,
271
     we are all done with the file right here.  */
272
159k
  if (mod != NULL    /* If no modules, caller will clean up.  */
273
12.9k
      && elf_end (archive) == 0)
274
12.9k
    close (fd);
275
276
159k
  return mod;
277
160k
}
278
279
Dwfl_Module *
280
internal_function
281
__libdwfl_report_offline (Dwfl *dwfl, const char *name,
282
        const char *file_name, int fd, bool closefd,
283
        int (*predicate) (const char *module,
284
              const char *file))
285
11.3k
{
286
11.3k
  Elf *elf;
287
11.3k
  Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, true);
288
11.3k
  if (error != DWFL_E_NOERROR)
289
2.08k
    {
290
2.08k
      __libdwfl_seterrno (error);
291
2.08k
      return NULL;
292
2.08k
    }
293
9.22k
  Dwfl_Module *mod = process_file (dwfl, name, file_name, fd, elf, predicate);
294
9.22k
  if (mod == NULL)
295
1.92k
    {
296
1.92k
      elf_end (elf);
297
1.92k
      if (closefd)
298
1.92k
  close (fd);
299
1.92k
    }
300
9.22k
  return mod;
301
11.3k
}
302
303
Dwfl_Module *
304
dwfl_report_offline (Dwfl *dwfl, const char *name,
305
         const char *file_name, int fd)
306
11.3k
{
307
11.3k
  if (dwfl == NULL)
308
0
    return NULL;
309
310
11.3k
  bool closefd = false;
311
11.3k
  if (fd < 0)
312
11.3k
    {
313
11.3k
      closefd = true;
314
11.3k
      fd = open (file_name, O_RDONLY);
315
11.3k
      if (fd < 0)
316
0
  {
317
0
    __libdwfl_seterrno (DWFL_E_ERRNO);
318
0
    return NULL;
319
0
  }
320
11.3k
    }
321
322
11.3k
  return __libdwfl_report_offline (dwfl, name, file_name, fd, closefd, NULL);
323
11.3k
}
324
INTDEF (dwfl_report_offline)
325
326
Dwfl_Module *
327
dwfl_report_offline_memory (Dwfl *dwfl, const char *name,
328
          const char *file_name, char *data, size_t size)
329
0
{
330
0
  if (dwfl == NULL)
331
0
    return NULL;
332
333
0
  Elf *elf;
334
0
  Dwfl_Error error = __libdw_open_elf_memory (data, size, &elf, true);
335
0
  if (error != DWFL_E_NOERROR)
336
0
    {
337
0
      __libdwfl_seterrno (error);
338
0
      return NULL;
339
0
    }
340
  /* It is ok to pass fd == -1 here, because libelf uses it as a value for
341
     "no file opened" and supports working with files without fd, thanks to
342
     the existence of the elf_memory function.  */
343
0
  Dwfl_Module *mod = process_file (dwfl, name, file_name, -1, elf, NULL);
344
0
  if (mod == NULL)
345
0
    elf_end (elf);
346
0
  return mod;
347
0
}
348
INTDEF (dwfl_report_offline_memory)