Coverage Report

Created: 2025-07-18 06:06

/src/elfutils/libdwfl/derelocate.c
Line
Count
Source (jump to first uncovered line)
1
/* Recover relocatibility for addresses computed from debug information.
2
   Copyright (C) 2005-2010, 2013, 2015 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
35
struct dwfl_relocation
36
{
37
  size_t count;
38
  struct
39
  {
40
    Elf_Scn *scn;
41
    Elf_Scn *relocs;
42
    const char *name;
43
    GElf_Addr start, end;
44
  } refs[0];
45
};
46
47
48
struct secref
49
{
50
  struct secref *next;
51
  Elf_Scn *scn;
52
  Elf_Scn *relocs;
53
  const char *name;
54
  GElf_Addr start, end;
55
};
56
57
static int
58
compare_secrefs (const void *a, const void *b)
59
0
{
60
0
  struct secref *const *p1 = a;
61
0
  struct secref *const *p2 = b;
62
63
  /* No signed difference calculation is correct here, since the
64
     terms are unsigned and could be more than INT64_MAX apart.  */
65
0
  if ((*p1)->start < (*p2)->start)
66
0
    return -1;
67
0
  if ((*p1)->start > (*p2)->start)
68
0
    return 1;
69
70
0
  if ((*p1)->end < (*p2)->end)
71
0
    return -1;
72
0
  if ((*p1)->end > (*p2)->end)
73
0
    return 1;
74
75
  /* Same start/end, then just compare which section came first.  */
76
0
  return elf_ndxscn ((*p1)->scn) - elf_ndxscn ((*p2)->scn);
77
0
}
78
79
static int
80
cache_sections (Dwfl_Module *mod)
81
0
{
82
0
  if (likely (mod->reloc_info != NULL))
83
0
    return mod->reloc_info->count;
84
85
0
  struct secref *refs = NULL;
86
0
  size_t nrefs = 0;
87
88
0
  size_t shstrndx;
89
0
  if (unlikely (elf_getshdrstrndx (mod->main.elf, &shstrndx) < 0))
90
0
    {
91
0
    elf_error:
92
0
      __libdwfl_seterrno (DWFL_E_LIBELF);
93
0
      nrefs = -1;
94
0
      goto free_refs;
95
0
    }
96
97
0
  bool check_reloc_sections = false;
98
0
  Elf_Scn *scn = NULL;
99
0
  while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
100
0
    {
101
0
      GElf_Shdr shdr_mem;
102
0
      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
103
0
      if (shdr == NULL)
104
0
  goto elf_error;
105
106
0
      if ((shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr == 0
107
0
    && mod->e_type == ET_REL)
108
0
  {
109
    /* This section might not yet have been looked at.  */
110
0
    if (__libdwfl_relocate_value (mod, mod->main.elf, &shstrndx,
111
0
          elf_ndxscn (scn),
112
0
          &shdr->sh_addr) != DWFL_E_NOERROR)
113
0
      continue;
114
0
    shdr = gelf_getshdr (scn, &shdr_mem);
115
0
    if (unlikely (shdr == NULL))
116
0
      goto elf_error;
117
0
  }
118
119
0
      if (shdr->sh_flags & SHF_ALLOC)
120
0
  {
121
0
    const char *name = elf_strptr (mod->main.elf, shstrndx,
122
0
           shdr->sh_name);
123
0
    if (unlikely (name == NULL))
124
0
      goto elf_error;
125
126
0
    struct secref *newref = malloc (sizeof *newref);
127
0
    if (unlikely (newref == NULL))
128
0
      {
129
0
      nomem:
130
0
        __libdwfl_seterrno (DWFL_E_NOMEM);
131
0
        nrefs = -1;
132
0
        goto free_refs;
133
0
      }
134
135
0
    newref->scn = scn;
136
0
    newref->relocs = NULL;
137
0
    newref->name = name;
138
0
    newref->start = dwfl_adjusted_address (mod, shdr->sh_addr);
139
0
    newref->end = newref->start + shdr->sh_size;
140
0
    newref->next = refs;
141
0
    refs = newref;
142
0
    ++nrefs;
143
0
  }
144
145
0
      if (mod->e_type == ET_REL
146
0
    && shdr->sh_size != 0
147
0
    && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
148
0
    && mod->dwfl->callbacks->section_address != NULL)
149
0
  {
150
0
    if (shdr->sh_info < elf_ndxscn (scn))
151
0
      {
152
        /* We've already looked at the section these relocs apply to.  */
153
0
        Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
154
0
        if (likely (tscn != NULL))
155
0
    for (struct secref *sec = refs; sec != NULL; sec = sec->next)
156
0
      if (sec->scn == tscn)
157
0
        {
158
0
          sec->relocs = scn;
159
0
          break;
160
0
        }
161
0
      }
162
0
    else
163
      /* We'll have to do a second pass.  */
164
0
      check_reloc_sections = true;
165
0
  }
166
0
    }
167
168
0
  mod->reloc_info = malloc (offsetof (struct dwfl_relocation, refs[nrefs]));
169
0
  if (unlikely (mod->reloc_info == NULL))
170
0
    goto nomem;
171
172
0
  struct secref **sortrefs = malloc (nrefs * sizeof sortrefs[0]);
173
0
  if (unlikely (sortrefs == NULL))
174
0
    goto nomem;
175
176
0
  for (size_t i = nrefs; i-- > 0; refs = refs->next)
177
0
    sortrefs[i] = refs;
178
0
  assert (refs == NULL);
179
180
0
  qsort (sortrefs, nrefs, sizeof sortrefs[0], &compare_secrefs);
181
182
0
  mod->reloc_info->count = nrefs;
183
0
  for (size_t i = 0; i < nrefs; ++i)
184
0
    {
185
0
      mod->reloc_info->refs[i].name = sortrefs[i]->name;
186
0
      mod->reloc_info->refs[i].scn = sortrefs[i]->scn;
187
0
      mod->reloc_info->refs[i].relocs = sortrefs[i]->relocs;
188
0
      mod->reloc_info->refs[i].start = sortrefs[i]->start;
189
0
      mod->reloc_info->refs[i].end = sortrefs[i]->end;
190
0
      free (sortrefs[i]);
191
0
    }
192
193
0
  free (sortrefs);
194
195
0
  if (unlikely (check_reloc_sections))
196
0
    {
197
      /* There was a reloc section that preceded its target section.
198
   So we have to scan again now that we have cached all the
199
   possible target sections we care about.  */
200
201
0
      scn = NULL;
202
0
      while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
203
0
  {
204
0
    GElf_Shdr shdr_mem;
205
0
    GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
206
0
    if (shdr == NULL)
207
0
      goto elf_error;
208
209
0
          if (shdr->sh_size != 0
210
0
        && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA))
211
0
      {
212
0
        Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
213
0
        if (likely (tscn != NULL))
214
0
    for (size_t i = 0; i < nrefs; ++i)
215
0
      if (mod->reloc_info->refs[i].scn == tscn)
216
0
        {
217
0
          mod->reloc_info->refs[i].relocs = scn;
218
0
          break;
219
0
        }
220
0
      }
221
0
  }
222
0
    }
223
224
0
free_refs:
225
0
  while (refs != NULL)
226
0
    {
227
0
      struct secref *ref = refs;
228
0
      refs = ref->next;
229
0
      free (ref);
230
0
    }
231
232
0
  return nrefs;
233
0
}
234
235
236
int
237
dwfl_module_relocations (Dwfl_Module *mod)
238
0
{
239
0
  if (mod == NULL)
240
0
    return -1;
241
242
0
  switch (mod->e_type)
243
0
    {
244
0
    case ET_REL:
245
0
      return cache_sections (mod);
246
247
0
    case ET_DYN:
248
0
      return 1;
249
250
0
    case ET_EXEC:
251
0
      assert (mod->main.vaddr == mod->low_addr);
252
0
      break;
253
0
    }
254
255
0
  return 0;
256
0
}
257
258
const char *
259
dwfl_module_relocation_info (Dwfl_Module *mod, unsigned int idx,
260
           Elf32_Word *shndxp)
261
0
{
262
0
  if (mod == NULL)
263
0
    return NULL;
264
265
0
  switch (mod->e_type)
266
0
    {
267
0
    case ET_REL:
268
0
      break;
269
270
0
    case ET_DYN:
271
0
      if (idx != 0)
272
0
  return NULL;
273
0
      if (shndxp)
274
0
  *shndxp = SHN_ABS;
275
0
      return "";
276
277
0
    default:
278
0
      return NULL;
279
0
    }
280
281
0
  if (cache_sections (mod) < 0)
282
0
    return NULL;
283
284
0
  struct dwfl_relocation *sections = mod->reloc_info;
285
286
0
  if (idx >= sections->count)
287
0
    return NULL;
288
289
0
  if (shndxp)
290
0
    *shndxp = elf_ndxscn (sections->refs[idx].scn);
291
292
0
  return sections->refs[idx].name;
293
0
}
294
295
/* Check that MOD is valid and make sure its relocation has been done.  */
296
static bool
297
check_module (Dwfl_Module *mod)
298
0
{
299
0
  if (mod == NULL)
300
0
    return true;
301
302
0
  if (INTUSE(dwfl_module_getsymtab) (mod) < 0)
303
0
    {
304
0
      Dwfl_Error error = dwfl_errno ();
305
0
      if (error != DWFL_E_NO_SYMTAB)
306
0
  {
307
0
    __libdwfl_seterrno (error);
308
0
    return true;
309
0
  }
310
0
    }
311
312
0
  if (mod->dw == NULL)
313
0
    {
314
0
      Dwarf_Addr bias;
315
0
      if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL)
316
0
  {
317
0
    Dwfl_Error error = dwfl_errno ();
318
0
    if (error != DWFL_E_NO_DWARF)
319
0
      {
320
0
        __libdwfl_seterrno (error);
321
0
        return true;
322
0
      }
323
0
  }
324
0
    }
325
326
0
  return false;
327
0
}
328
329
/* Find the index in MOD->reloc_info.refs containing *ADDR.  */
330
static int
331
find_section (Dwfl_Module *mod, Dwarf_Addr *addr)
332
0
{
333
0
  if (cache_sections (mod) < 0)
334
0
    return -1;
335
336
0
  struct dwfl_relocation *sections = mod->reloc_info;
337
338
  /* The sections are sorted by address, so we can use binary search.  */
339
0
  size_t l = 0, u = sections->count;
340
0
  while (l < u)
341
0
    {
342
0
      size_t idx = (l + u) / 2;
343
0
      if (*addr < sections->refs[idx].start)
344
0
  u = idx;
345
0
      else if (*addr > sections->refs[idx].end)
346
0
  l = idx + 1;
347
0
      else
348
0
  {
349
    /* Consider the limit of a section to be inside it, unless it's
350
       inside the next one.  A section limit address can appear in
351
       line records.  */
352
0
    if (*addr == sections->refs[idx].end
353
0
        && idx + 1 < sections->count
354
0
        && *addr == sections->refs[idx + 1].start)
355
0
      ++idx;
356
357
0
    *addr -= sections->refs[idx].start;
358
0
    return idx;
359
0
  }
360
0
    }
361
362
0
  __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_NO_MATCH));
363
0
  return -1;
364
0
}
365
366
size_t
367
internal_function
368
__libdwfl_find_section_ndx (Dwfl_Module *mod, Dwarf_Addr *addr)
369
0
{
370
0
  int idx = find_section (mod, addr);
371
0
  if (unlikely (idx == -1))
372
0
    return SHN_UNDEF;
373
374
0
  return elf_ndxscn (mod->reloc_info->refs[idx].scn);
375
0
}
376
377
int
378
dwfl_module_relocate_address (Dwfl_Module *mod, Dwarf_Addr *addr)
379
0
{
380
0
  if (unlikely (check_module (mod)))
381
0
    return -1;
382
383
0
  switch (mod->e_type)
384
0
    {
385
0
    case ET_REL:
386
0
      return find_section (mod, addr);
387
388
0
    case ET_DYN:
389
      /* All relative to first and only relocation base: module start.  */
390
0
      *addr -= mod->low_addr;
391
0
      break;
392
393
0
    default:
394
      /* Already absolute, dwfl_module_relocations returned zero.  We
395
   shouldn't really have been called, but it's a harmless no-op.  */
396
0
      break;
397
0
    }
398
399
0
  return 0;
400
0
}
401
INTDEF (dwfl_module_relocate_address)
402
403
Elf_Scn *
404
dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address,
405
           Dwarf_Addr *bias)
406
0
{
407
0
  if (check_module (mod))
408
0
    return NULL;
409
410
0
  int idx = find_section (mod, address);
411
0
  if (idx < 0)
412
0
    return NULL;
413
414
0
  if (mod->reloc_info->refs[idx].relocs != NULL)
415
0
    {
416
0
      assert (mod->e_type == ET_REL);
417
418
0
      Elf_Scn *tscn = mod->reloc_info->refs[idx].scn;
419
0
      Elf_Scn *relocscn = mod->reloc_info->refs[idx].relocs;
420
0
      Dwfl_Error result = __libdwfl_relocate_section (mod, mod->main.elf,
421
0
                  relocscn, tscn, true);
422
0
      if (likely (result == DWFL_E_NOERROR))
423
0
  mod->reloc_info->refs[idx].relocs = NULL;
424
0
      else
425
0
  {
426
0
    __libdwfl_seterrno (result);
427
0
    return NULL;
428
0
  }
429
0
    }
430
431
0
  *bias = dwfl_adjusted_address (mod, 0);
432
0
  return mod->reloc_info->refs[idx].scn;
433
0
}
434
INTDEF (dwfl_module_address_section)