Coverage Report

Created: 2025-07-18 06:08

/src/elfutils/libdwfl/dwfl_report_elf.c
Line
Count
Source (jump to first uncovered line)
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
9.41k
#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
21.4k
{
51
21.4k
  GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
52
21.4k
  if (ehdr == NULL)
53
0
    {
54
864
    elf_error:
55
864
      __libdwfl_seterrno (DWFL_E_LIBELF);
56
864
      return false;
57
0
    }
58
59
21.4k
  GElf_Addr vaddr = 0;
60
21.4k
  GElf_Addr address_sync = 0;
61
21.4k
  GElf_Addr start = 0, end = 0, bias = 0;
62
21.4k
  switch (ehdr->e_type)
63
21.4k
    {
64
4.70k
    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.70k
      start = end = base = (base + REL_MIN_ALIGN - 1) & -REL_MIN_ALIGN;
70
71
4.70k
      bool first = true;
72
4.70k
      Elf_Scn *scn = NULL;
73
48.0k
      while ((scn = elf_nextscn (elf, scn)) != NULL)
74
121k
  {
75
121k
    GElf_Shdr shdr_mem;
76
121k
    GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
77
121k
    if (unlikely (shdr == NULL))
78
0
      goto elf_error;
79
80
121k
    if (shdr->sh_flags & SHF_ALLOC)
81
55.0k
      {
82
55.0k
        const GElf_Xword align = shdr->sh_addralign ?: 1;
83
55.0k
        const GElf_Addr next = (end + align - 1) & -align;
84
898
        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
53.9k
      || (bias == 0 && end > start && end != next))
89
3.40k
    {
90
3.40k
      shdr->sh_addr = next;
91
3.40k
      if (end == base)
92
        /* This is the first section assigned a location.
93
           Use its aligned address as the module's base.  */
94
591
        start = base = shdr->sh_addr;
95
2.81k
      else if (unlikely (base & (align - 1)))
96
566
        {
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
566
          start = end = base = (base + align - 1) & -align;
104
566
          Elf_Scn *prev_scn = NULL;
105
566
          do
106
123k
      {
107
123k
        prev_scn = elf_nextscn (elf, prev_scn);
108
123k
        GElf_Shdr prev_shdr_mem;
109
123k
        GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
110
123k
                     &prev_shdr_mem);
111
123k
        if (unlikely (prev_shdr == NULL))
112
0
          goto elf_error;
113
123k
        if (prev_shdr->sh_flags & SHF_ALLOC)
114
36.3k
          {
115
36.3k
            const GElf_Xword prev_align
116
36.3k
        = prev_shdr->sh_addralign ?: 1;
117
118
36.3k
            prev_shdr->sh_addr
119
12.5k
        = (end + prev_align - 1) & -prev_align;
120
12.5k
            end = prev_shdr->sh_addr + prev_shdr->sh_size;
121
122
12.5k
            if (unlikely (! gelf_update_shdr (prev_scn,
123
12.5k
                prev_shdr)))
124
59
        goto elf_error;
125
12.5k
          }
126
123k
      }
127
99.9k
          while (prev_scn != scn);
128
18.4E
          continue;
129
566
        }
130
131
2.84k
      end = shdr->sh_addr + shdr->sh_size;
132
2.84k
      if (likely (shdr->sh_addr != 0)
133
2.84k
          && unlikely (! gelf_update_shdr (scn, shdr)))
134
23
        goto elf_error;
135
2.84k
    }
136
18.4E
        else
137
18.4E
    {
138
      /* The address is already assigned.  Just track it.  */
139
18.4E
      if (first || end < shdr->sh_addr + shdr->sh_size)
140
1.93k
        end = shdr->sh_addr + shdr->sh_size;
141
18.4E
      if (first || bias > shdr->sh_addr)
142
        /* This is the lowest address in the module.  */
143
1.29k
        bias = shdr->sh_addr;
144
145
18.4E
      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
50.5k
        base = (((base + align - 1) & -align)
150
50.5k
          + (bias & (align - 1)));
151
18.4E
    }
152
153
309
        first = false;
154
309
      }
155
121k
  }
156
157
18.4E
      if (bias != 0)
158
995
  {
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
995
    start = base;
163
995
    end = end - bias + start;
164
995
    bias = start - bias;
165
995
  }
166
18.4E
      break;
167
168
      /* Everything else has to have program headers.  */
169
170
256
    case ET_EXEC:
171
367
    case ET_CORE:
172
      /* An assigned base address is meaningless for these.  */
173
367
      base = 0;
174
367
      add_p_vaddr = true;
175
367
      FALLTHROUGH;
176
376
    case ET_DYN:
177
16.7k
    default:;
178
16.7k
      size_t phnum;
179
16.7k
      if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
180
595
  goto elf_error;
181
4.36M
      for (size_t i = 0; i < phnum; ++i)
182
4.34M
  {
183
4.34M
    GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
184
4.34M
    if (unlikely (ph == NULL))
185
187
      goto elf_error;
186
4.34M
    if (ph->p_type == PT_LOAD)
187
2.15k
      {
188
2.15k
        vaddr = ph->p_vaddr & -ph->p_align;
189
2.15k
        address_sync = ph->p_vaddr + ph->p_memsz;
190
2.15k
        break;
191
2.15k
      }
192
4.34M
  }
193
15.9k
      if (add_p_vaddr)
194
15.9k
  {
195
15.9k
    start = base + vaddr;
196
15.9k
    bias = base;
197
15.9k
  }
198
0
      else
199
0
  {
200
0
    start = base;
201
0
    bias = base - vaddr;
202
0
  }
203
204
5.45M
      for (size_t i = phnum; i-- > 0;)
205
5.44M
  {
206
5.44M
    GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
207
5.44M
    if (unlikely (ph == NULL))
208
0
      goto elf_error;
209
5.44M
    if (ph->p_type == PT_LOAD
210
5.44M
        && ph->p_vaddr + ph->p_memsz > 0)
211
2.14k
      {
212
2.14k
        end = bias + (ph->p_vaddr + ph->p_memsz);
213
2.14k
        break;
214
2.14k
      }
215
5.44M
  }
216
217
15.9k
      if (end == 0 && sanity)
218
0
  {
219
0
    __libdwfl_seterrno (DWFL_E_NO_PHDR);
220
0
    return false;
221
0
  }
222
15.9k
      break;
223
21.4k
    }
224
20.5k
  if (vaddrp)
225
20.5k
    *vaddrp = vaddr;
226
20.5k
  if (address_syncp)
227
20.5k
    *address_syncp = address_sync;
228
20.5k
  if (startp)
229
20.5k
    *startp = start;
230
20.5k
  if (endp)
231
20.5k
    *endp = end;
232
20.5k
  if (biasp)
233
20.5k
    *biasp = bias;
234
20.5k
  if (e_typep)
235
20.5k
    *e_typep = ehdr->e_type;
236
20.5k
  return true;
237
21.4k
}
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
21.4k
{
245
21.4k
  GElf_Addr vaddr, address_sync, start, end, bias;
246
21.4k
  GElf_Half e_type;
247
21.4k
  if (! __libdwfl_elf_address_range (elf, base, add_p_vaddr, sanity, &vaddr,
248
21.4k
             &address_sync, &start, &end, &bias,
249
21.4k
             &e_type))
250
864
    return NULL;
251
20.5k
  Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name, start, end);
252
20.5k
  if (m != NULL)
253
20.5k
    {
254
20.5k
      if (m->main.name == NULL)
255
19.9k
  {
256
19.9k
    m->main.name = strdup (file_name);
257
19.9k
    m->main.fd = fd;
258
19.9k
  }
259
666
      else if ((fd >= 0 && m->main.fd != fd)
260
666
         || strcmp (m->main.name, file_name))
261
10
  {
262
56
  overlap:
263
56
    m->gc = true;
264
56
    __libdwfl_seterrno (DWFL_E_OVERLAP);
265
56
    return NULL;
266
10
  }
267
268
      /* Preinstall the open ELF handle for the module.  */
269
20.5k
      if (m->main.elf == NULL)
270
19.9k
  {
271
19.9k
    m->main.elf = elf;
272
19.9k
    m->main.vaddr = vaddr;
273
19.9k
    m->main.address_sync = address_sync;
274
19.9k
    m->main_bias = bias;
275
19.9k
    m->e_type = e_type;
276
19.9k
  }
277
656
      else
278
656
  {
279
656
    if (m->main_bias != bias
280
656
        || m->main.vaddr != vaddr || m->main.address_sync != address_sync)
281
46
      goto overlap;
282
610
    elf_end (m->main.elf);
283
610
    m->main.elf = elf;
284
610
  }
285
20.5k
    }
286
20.5k
  return m;
287
20.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