Coverage Report

Created: 2026-09-01 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/elfutils/libdwfl/dwfl_report_elf.c
Line
Count
Source
1
/* Report a module to libdwfl based on ELF program headers.
2
   Copyright (C) 2005-2010 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 <fcntl.h>
35
36
/* We start every ET_REL module at a moderately aligned boundary.
37
   This keeps the low addresses easy to read compared to a layout
38
   starting at 0 (as when using -e).  It also makes it unlikely
39
   that a middle section will have a larger alignment and require
40
   rejiggering (see below).  */
41
5.50k
#define REL_MIN_ALIGN ((GElf_Xword) 0x100)
42
43
bool
44
internal_function
45
__libdwfl_elf_address_range (Elf *elf, GElf_Addr base, bool add_p_vaddr,
46
           bool sanity, GElf_Addr *vaddrp,
47
           GElf_Addr *address_syncp, GElf_Addr *startp,
48
           GElf_Addr *endp, GElf_Addr *biasp,
49
           GElf_Half *e_typep)
50
14.5k
{
51
14.5k
  GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
52
14.5k
  if (ehdr == NULL)
53
0
    {
54
837
    elf_error:
55
837
      __libdwfl_seterrno (DWFL_E_LIBELF);
56
837
      return false;
57
0
    }
58
59
14.5k
  GElf_Addr vaddr = 0;
60
14.5k
  GElf_Addr address_sync = 0;
61
14.5k
  GElf_Addr start = 0, end = 0, bias = 0;
62
14.5k
  switch (ehdr->e_type)
63
14.5k
    {
64
2.75k
    case ET_REL:
65
      /* For a relocatable object, we do an arbitrary section layout.
66
   By updating the section header in place, we leave the layout
67
   information to be found by relocation.  */
68
69
2.75k
      start = end = base = (base + REL_MIN_ALIGN - 1) & -REL_MIN_ALIGN;
70
71
2.75k
      bool first = true;
72
2.75k
      Elf_Scn *scn = NULL;
73
27.5k
      while ((scn = elf_nextscn (elf, scn)) != NULL)
74
24.8k
  {
75
24.8k
    GElf_Shdr shdr_mem;
76
24.8k
    GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
77
24.8k
    if (unlikely (shdr == NULL))
78
0
      goto elf_error;
79
80
24.8k
    if (shdr->sh_flags & SHF_ALLOC)
81
5.85k
      {
82
5.85k
        const GElf_Xword align = shdr->sh_addralign ?: 1;
83
5.85k
        const GElf_Addr next = (end + align - 1) & -align;
84
5.85k
        if (shdr->sh_addr == 0
85
      /* Once we've started doing layout we have to do it all,
86
         unless we just laid out the first section at 0 when
87
         it already was at 0.  */
88
5.11k
      || (bias == 0 && end > start && end != next))
89
1.51k
    {
90
1.51k
      shdr->sh_addr = next;
91
1.51k
      if (end == base)
92
        /* This is the first section assigned a location.
93
           Use its aligned address as the module's base.  */
94
363
        start = base = shdr->sh_addr;
95
1.14k
      else if (unlikely (base & (align - 1)))
96
279
        {
97
          /* If BASE has less than the maximum alignment of
98
       any section, we eat more than the optimal amount
99
       of padding and so make the module's apparent
100
       size come out larger than it would when placed
101
       at zero.  So reset the layout with a better base.  */
102
103
279
          start = end = base = (base + align - 1) & -align;
104
279
          Elf_Scn *prev_scn = NULL;
105
279
          do
106
1.87k
      {
107
1.87k
        prev_scn = elf_nextscn (elf, prev_scn);
108
1.87k
        GElf_Shdr prev_shdr_mem;
109
1.87k
        GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
110
1.87k
                     &prev_shdr_mem);
111
1.87k
        if (unlikely (prev_shdr == NULL))
112
0
          goto elf_error;
113
1.87k
        if (prev_shdr->sh_flags & SHF_ALLOC)
114
1.10k
          {
115
1.10k
            const GElf_Xword prev_align
116
1.10k
        = prev_shdr->sh_addralign ?: 1;
117
118
1.10k
            prev_shdr->sh_addr
119
1.10k
        = (end + prev_align - 1) & -prev_align;
120
1.10k
            end = prev_shdr->sh_addr + prev_shdr->sh_size;
121
122
1.10k
            if (unlikely (! gelf_update_shdr (prev_scn,
123
1.10k
                prev_shdr)))
124
25
        goto elf_error;
125
1.10k
          }
126
1.87k
      }
127
1.84k
          while (prev_scn != scn);
128
254
          continue;
129
279
        }
130
131
1.23k
      end = shdr->sh_addr + shdr->sh_size;
132
1.23k
      if (likely (shdr->sh_addr != 0)
133
1.08k
          && unlikely (! gelf_update_shdr (scn, shdr)))
134
21
        goto elf_error;
135
1.23k
    }
136
4.34k
        else
137
4.34k
    {
138
      /* The address is already assigned.  Just track it.  */
139
4.34k
      if (first || end < shdr->sh_addr + shdr->sh_size)
140
1.43k
        end = shdr->sh_addr + shdr->sh_size;
141
4.34k
      if (first || bias > shdr->sh_addr)
142
        /* This is the lowest address in the module.  */
143
1.25k
        bias = shdr->sh_addr;
144
145
4.34k
      if ((shdr->sh_addr - bias + base) & (align - 1))
146
        /* This section winds up misaligned using BASE.
147
           Adjust BASE upwards to make it congruent to
148
           the lowest section address in the file modulo ALIGN.  */
149
2.82k
        base = (((base + align - 1) & -align)
150
2.82k
          + (bias & (align - 1)));
151
4.34k
    }
152
153
5.55k
        first = false;
154
5.55k
      }
155
24.8k
  }
156
157
2.70k
      if (bias != 0)
158
921
  {
159
    /* The section headers had nonzero sh_addr values.  The layout
160
       was already done.  We've just collected the total span.
161
       Now just compute the bias from the requested base.  */
162
921
    start = base;
163
921
    end = end - bias + start;
164
921
    bias = start - bias;
165
921
  }
166
2.70k
      break;
167
168
      /* Everything else has to have program headers.  */
169
170
51
    case ET_EXEC:
171
145
    case ET_CORE:
172
      /* An assigned base address is meaningless for these.  */
173
145
      base = 0;
174
145
      add_p_vaddr = true;
175
145
      FALLTHROUGH;
176
154
    case ET_DYN:
177
11.8k
    default:;
178
11.8k
      size_t phnum;
179
11.8k
      if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
180
634
  goto elf_error;
181
4.63M
      for (size_t i = 0; i < phnum; ++i)
182
4.62M
  {
183
4.62M
    GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
184
4.62M
    if (unlikely (ph == NULL))
185
157
      goto elf_error;
186
4.62M
    if (ph->p_type == PT_LOAD)
187
1.12k
      {
188
1.12k
        vaddr = ph->p_vaddr & -ph->p_align;
189
1.12k
        address_sync = ph->p_vaddr + ph->p_memsz;
190
1.12k
        break;
191
1.12k
      }
192
4.62M
  }
193
11.0k
      if (add_p_vaddr)
194
11.0k
  {
195
11.0k
    start = base + vaddr;
196
11.0k
    bias = base;
197
11.0k
  }
198
0
      else
199
0
  {
200
0
    start = base;
201
0
    bias = base - vaddr;
202
0
  }
203
204
5.57M
      for (size_t i = phnum; i-- > 0;)
205
5.56M
  {
206
5.56M
    GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
207
5.56M
    if (unlikely (ph == NULL))
208
0
      goto elf_error;
209
5.56M
    if (ph->p_type == PT_LOAD
210
1.32k
        && ph->p_vaddr + ph->p_memsz > 0)
211
1.10k
      {
212
1.10k
        end = bias + (ph->p_vaddr + ph->p_memsz);
213
1.10k
        break;
214
1.10k
      }
215
5.56M
  }
216
217
11.0k
      if (end == 0 && sanity)
218
0
  {
219
0
    __libdwfl_seterrno (DWFL_E_NO_PHDR);
220
0
    return false;
221
0
  }
222
11.0k
      break;
223
14.5k
    }
224
13.7k
  if (vaddrp)
225
13.7k
    *vaddrp = vaddr;
226
13.7k
  if (address_syncp)
227
13.7k
    *address_syncp = address_sync;
228
13.7k
  if (startp)
229
13.7k
    *startp = start;
230
13.7k
  if (endp)
231
13.7k
    *endp = end;
232
13.7k
  if (biasp)
233
13.7k
    *biasp = bias;
234
13.7k
  if (e_typep)
235
13.7k
    *e_typep = ehdr->e_type;
236
13.7k
  return true;
237
14.5k
}
238
239
Dwfl_Module *
240
internal_function
241
__libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
242
          int fd, Elf *elf, GElf_Addr base, bool add_p_vaddr,
243
          bool sanity)
244
14.5k
{
245
14.5k
  GElf_Addr vaddr, address_sync, start, end, bias;
246
14.5k
  GElf_Half e_type;
247
14.5k
  if (! __libdwfl_elf_address_range (elf, base, add_p_vaddr, sanity, &vaddr,
248
14.5k
             &address_sync, &start, &end, &bias,
249
14.5k
             &e_type))
250
837
    return NULL;
251
13.7k
  Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name, start, end);
252
13.7k
  if (m != NULL)
253
13.7k
    {
254
13.7k
      if (m->main.name == NULL)
255
13.7k
  {
256
13.7k
    m->main.name = strdup (file_name);
257
13.7k
    m->main.fd = fd;
258
13.7k
  }
259
8
      else if ((fd >= 0 && m->main.fd != fd)
260
0
         || strcmp (m->main.name, file_name))
261
8
  {
262
8
  overlap:
263
8
    m->gc = true;
264
8
    __libdwfl_seterrno (DWFL_E_OVERLAP);
265
8
    return NULL;
266
8
  }
267
268
      /* Preinstall the open ELF handle for the module.  */
269
13.7k
      if (m->main.elf == NULL)
270
13.7k
  {
271
13.7k
    m->main.elf = elf;
272
13.7k
    m->main.vaddr = vaddr;
273
13.7k
    m->main.address_sync = address_sync;
274
13.7k
    m->main_bias = bias;
275
13.7k
    m->e_type = e_type;
276
13.7k
  }
277
0
      else
278
0
  {
279
0
    elf_end (elf);
280
0
    if (m->main_bias != bias
281
0
        || m->main.vaddr != vaddr || m->main.address_sync != address_sync)
282
0
      goto overlap;
283
0
  }
284
13.7k
    }
285
13.7k
  return m;
286
13.7k
}
287
288
NEW_VERSION (dwfl_report_elf, ELFUTILS_0.156)
289
Dwfl_Module *
290
dwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd,
291
     GElf_Addr base, bool add_p_vaddr)
292
0
{
293
0
  bool closefd = false;
294
0
  if (fd < 0)
295
0
    {
296
0
      closefd = true;
297
0
      fd = open (file_name, O_RDONLY);
298
0
      if (fd < 0)
299
0
  {
300
0
    __libdwfl_seterrno (DWFL_E_ERRNO);
301
0
    return NULL;
302
0
  }
303
0
    }
304
305
0
  Elf *elf;
306
0
  Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, false);
307
0
  if (error != DWFL_E_NOERROR)
308
0
    {
309
0
      __libdwfl_seterrno (error);
310
0
      return NULL;
311
0
    }
312
313
0
  Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name,
314
0
             fd, elf, base, add_p_vaddr, true);
315
0
  if (mod == NULL)
316
0
    {
317
0
      elf_end (elf);
318
0
      if (closefd)
319
0
  close (fd);
320
0
    }
321
322
0
  return mod;
323
0
}
324
NEW_INTDEF (dwfl_report_elf)
325
326
#ifdef SYMBOL_VERSIONING
327
Dwfl_Module *
328
  _compat_without_add_p_vaddr_dwfl_report_elf (Dwfl *dwfl, const char *name,
329
                 const char *file_name, int fd,
330
                 GElf_Addr base);
331
COMPAT_VERSION_NEWPROTO (dwfl_report_elf, ELFUTILS_0.122, without_add_p_vaddr)
332
333
Dwfl_Module *
334
_compat_without_add_p_vaddr_dwfl_report_elf (Dwfl *dwfl, const char *name,
335
               const char *file_name, int fd,
336
               GElf_Addr base)
337
{
338
  return dwfl_report_elf (dwfl, name, file_name, fd, base, true);
339
}
340
#endif