Coverage Report

Created: 2026-02-26 06:47

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
8.12k
#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
20.3k
{
51
20.3k
  GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
52
20.3k
  if (ehdr == NULL)
53
0
    {
54
832
    elf_error:
55
832
      __libdwfl_seterrno (DWFL_E_LIBELF);
56
832
      return false;
57
0
    }
58
59
20.3k
  GElf_Addr vaddr = 0;
60
20.3k
  GElf_Addr address_sync = 0;
61
20.3k
  GElf_Addr start = 0, end = 0, bias = 0;
62
20.3k
  switch (ehdr->e_type)
63
20.3k
    {
64
4.06k
    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
4.06k
      start = end = base = (base + REL_MIN_ALIGN - 1) & -REL_MIN_ALIGN;
70
71
4.06k
      bool first = true;
72
4.06k
      Elf_Scn *scn = NULL;
73
24.2k
      while ((scn = elf_nextscn (elf, scn)) != NULL)
74
20.3k
  {
75
20.3k
    GElf_Shdr shdr_mem;
76
20.3k
    GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
77
20.3k
    if (unlikely (shdr == NULL))
78
0
      goto elf_error;
79
80
20.3k
    if (shdr->sh_flags & SHF_ALLOC)
81
12.1k
      {
82
12.1k
        const GElf_Xword align = shdr->sh_addralign ?: 1;
83
12.1k
        const GElf_Addr next = (end + align - 1) & -align;
84
12.1k
        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
10.9k
      || (bias == 0 && end > start && end != next))
89
2.52k
    {
90
2.52k
      shdr->sh_addr = next;
91
2.52k
      if (end == base)
92
        /* This is the first section assigned a location.
93
           Use its aligned address as the module's base.  */
94
633
        start = base = shdr->sh_addr;
95
1.88k
      else if (unlikely (base & (align - 1)))
96
604
        {
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
604
          start = end = base = (base + align - 1) & -align;
104
604
          Elf_Scn *prev_scn = NULL;
105
604
          do
106
169k
      {
107
169k
        prev_scn = elf_nextscn (elf, prev_scn);
108
169k
        GElf_Shdr prev_shdr_mem;
109
169k
        GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
110
169k
                     &prev_shdr_mem);
111
169k
        if (unlikely (prev_shdr == NULL))
112
0
          goto elf_error;
113
169k
        if (prev_shdr->sh_flags & SHF_ALLOC)
114
66.0k
          {
115
66.0k
            const GElf_Xword prev_align
116
66.0k
        = prev_shdr->sh_addralign ?: 1;
117
118
66.0k
            prev_shdr->sh_addr
119
66.0k
        = (end + prev_align - 1) & -prev_align;
120
66.0k
            end = prev_shdr->sh_addr + prev_shdr->sh_size;
121
122
66.0k
            if (unlikely (! gelf_update_shdr (prev_scn,
123
66.0k
                prev_shdr)))
124
40
        goto elf_error;
125
66.0k
          }
126
169k
      }
127
169k
          while (prev_scn != scn);
128
564
          continue;
129
604
        }
130
131
1.91k
      end = shdr->sh_addr + shdr->sh_size;
132
1.91k
      if (likely (shdr->sh_addr != 0)
133
1.62k
          && unlikely (! gelf_update_shdr (scn, shdr)))
134
65
        goto elf_error;
135
1.91k
    }
136
9.60k
        else
137
9.60k
    {
138
      /* The address is already assigned.  Just track it.  */
139
9.60k
      if (first || end < shdr->sh_addr + shdr->sh_size)
140
1.75k
        end = shdr->sh_addr + shdr->sh_size;
141
9.60k
      if (first || bias > shdr->sh_addr)
142
        /* This is the lowest address in the module.  */
143
1.24k
        bias = shdr->sh_addr;
144
145
9.60k
      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
8.26k
        base = (((base + align - 1) & -align)
150
8.26k
          + (bias & (align - 1)));
151
9.60k
    }
152
153
11.4k
        first = false;
154
11.4k
      }
155
20.3k
  }
156
157
3.95k
      if (bias != 0)
158
980
  {
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
980
    start = base;
163
980
    end = end - bias + start;
164
980
    bias = start - bias;
165
980
  }
166
3.95k
      break;
167
168
      /* Everything else has to have program headers.  */
169
170
449
    case ET_EXEC:
171
586
    case ET_CORE:
172
      /* An assigned base address is meaningless for these.  */
173
586
      base = 0;
174
586
      add_p_vaddr = true;
175
586
      FALLTHROUGH;
176
600
    case ET_DYN:
177
16.3k
    default:;
178
16.3k
      size_t phnum;
179
16.3k
      if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
180
548
  goto elf_error;
181
5.51M
      for (size_t i = 0; i < phnum; ++i)
182
5.50M
  {
183
5.50M
    GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
184
5.50M
    if (unlikely (ph == NULL))
185
179
      goto elf_error;
186
5.50M
    if (ph->p_type == PT_LOAD)
187
1.88k
      {
188
1.88k
        vaddr = ph->p_vaddr & -ph->p_align;
189
1.88k
        address_sync = ph->p_vaddr + ph->p_memsz;
190
1.88k
        break;
191
1.88k
      }
192
5.50M
  }
193
15.5k
      if (add_p_vaddr)
194
15.5k
  {
195
15.5k
    start = base + vaddr;
196
15.5k
    bias = base;
197
15.5k
  }
198
0
      else
199
0
  {
200
0
    start = base;
201
0
    bias = base - vaddr;
202
0
  }
203
204
6.53M
      for (size_t i = phnum; i-- > 0;)
205
6.52M
  {
206
6.52M
    GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
207
6.52M
    if (unlikely (ph == NULL))
208
0
      goto elf_error;
209
6.52M
    if (ph->p_type == PT_LOAD
210
2.10k
        && ph->p_vaddr + ph->p_memsz > 0)
211
1.87k
      {
212
1.87k
        end = bias + (ph->p_vaddr + ph->p_memsz);
213
1.87k
        break;
214
1.87k
      }
215
6.52M
  }
216
217
15.5k
      if (end == 0 && sanity)
218
0
  {
219
0
    __libdwfl_seterrno (DWFL_E_NO_PHDR);
220
0
    return false;
221
0
  }
222
15.5k
      break;
223
20.3k
    }
224
19.5k
  if (vaddrp)
225
19.5k
    *vaddrp = vaddr;
226
19.5k
  if (address_syncp)
227
19.5k
    *address_syncp = address_sync;
228
19.5k
  if (startp)
229
19.5k
    *startp = start;
230
19.5k
  if (endp)
231
19.5k
    *endp = end;
232
19.5k
  if (biasp)
233
19.5k
    *biasp = bias;
234
19.5k
  if (e_typep)
235
19.5k
    *e_typep = ehdr->e_type;
236
19.5k
  return true;
237
20.3k
}
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
20.3k
{
245
20.3k
  GElf_Addr vaddr, address_sync, start, end, bias;
246
20.3k
  GElf_Half e_type;
247
20.3k
  if (! __libdwfl_elf_address_range (elf, base, add_p_vaddr, sanity, &vaddr,
248
20.3k
             &address_sync, &start, &end, &bias,
249
20.3k
             &e_type))
250
832
    return NULL;
251
19.5k
  Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name, start, end);
252
19.5k
  if (m != NULL)
253
19.5k
    {
254
19.5k
      if (m->main.name == NULL)
255
17.9k
  {
256
17.9k
    m->main.name = strdup (file_name);
257
17.9k
    m->main.fd = fd;
258
17.9k
  }
259
1.63k
      else if ((fd >= 0 && m->main.fd != fd)
260
1.62k
         || strcmp (m->main.name, file_name))
261
10
  {
262
86
  overlap:
263
86
    m->gc = true;
264
86
    __libdwfl_seterrno (DWFL_E_OVERLAP);
265
86
    return NULL;
266
10
  }
267
268
      /* Preinstall the open ELF handle for the module.  */
269
19.5k
      if (m->main.elf == NULL)
270
17.9k
  {
271
17.9k
    m->main.elf = elf;
272
17.9k
    m->main.vaddr = vaddr;
273
17.9k
    m->main.address_sync = address_sync;
274
17.9k
    m->main_bias = bias;
275
17.9k
    m->e_type = e_type;
276
17.9k
  }
277
1.62k
      else
278
1.62k
  {
279
1.62k
    if (m->main_bias != bias
280
1.54k
        || m->main.vaddr != vaddr || m->main.address_sync != address_sync)
281
76
      goto overlap;
282
1.54k
    elf_end (m->main.elf);
283
1.54k
    m->main.elf = elf;
284
1.54k
  }
285
19.5k
    }
286
19.4k
  return m;
287
19.5k
}
288
289
NEW_VERSION (dwfl_report_elf, ELFUTILS_0.156)
290
Dwfl_Module *
291
dwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd,
292
     GElf_Addr base, bool add_p_vaddr)
293
0
{
294
0
  bool closefd = false;
295
0
  if (fd < 0)
296
0
    {
297
0
      closefd = true;
298
0
      fd = open (file_name, O_RDONLY);
299
0
      if (fd < 0)
300
0
  {
301
0
    __libdwfl_seterrno (DWFL_E_ERRNO);
302
0
    return NULL;
303
0
  }
304
0
    }
305
306
0
  Elf *elf;
307
0
  Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, false);
308
0
  if (error != DWFL_E_NOERROR)
309
0
    {
310
0
      __libdwfl_seterrno (error);
311
0
      return NULL;
312
0
    }
313
314
0
  Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name,
315
0
             fd, elf, base, add_p_vaddr, true);
316
0
  if (mod == NULL)
317
0
    {
318
0
      elf_end (elf);
319
0
      if (closefd)
320
0
  close (fd);
321
0
    }
322
323
0
  return mod;
324
0
}
325
NEW_INTDEF (dwfl_report_elf)
326
327
#ifdef SYMBOL_VERSIONING
328
Dwfl_Module *
329
  _compat_without_add_p_vaddr_dwfl_report_elf (Dwfl *dwfl, const char *name,
330
                 const char *file_name, int fd,
331
                 GElf_Addr base);
332
COMPAT_VERSION_NEWPROTO (dwfl_report_elf, ELFUTILS_0.122, without_add_p_vaddr)
333
334
Dwfl_Module *
335
_compat_without_add_p_vaddr_dwfl_report_elf (Dwfl *dwfl, const char *name,
336
               const char *file_name, int fd,
337
               GElf_Addr base)
338
{
339
  return dwfl_report_elf (dwfl, name, file_name, fd, base, true);
340
}
341
#endif