Coverage Report

Created: 2025-02-03 06:24

/src/elfutils/libdw/dwarf_getcfi_elf.c
Line
Count
Source (jump to first uncovered line)
1
/* Get CFI from ELF file's exception-handling info.
2
   Copyright (C) 2009-2010, 2014, 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 <stdlib.h>
34
#include <string.h>
35
#include <assert.h>
36
37
#include "libdwP.h"
38
#include "cfi.h"
39
#include "encoded-value.h"
40
#include <dwarf.h>
41
42
43
static Dwarf_CFI *
44
allocate_cfi (Elf *elf, const GElf_Ehdr *ehdr, GElf_Addr vaddr)
45
0
{
46
0
  Dwarf_CFI *cfi = calloc (1, sizeof *cfi);
47
0
  if (cfi == NULL)
48
0
    {
49
0
      __libdw_seterrno (DWARF_E_NOMEM);
50
0
      return NULL;
51
0
    }
52
53
0
  cfi->e_ident = (unsigned char *) elf_getident (elf, NULL);
54
0
  if (cfi->e_ident == NULL)
55
0
    {
56
0
      free (cfi);
57
0
      __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
58
0
      return NULL;
59
0
    }
60
61
0
  cfi->e_machine = ehdr->e_machine;
62
63
0
  if ((BYTE_ORDER == LITTLE_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2MSB)
64
0
      || (BYTE_ORDER == BIG_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2LSB))
65
0
    cfi->other_byte_order = true;
66
67
0
  cfi->frame_vaddr = vaddr;
68
0
  cfi->textrel = 0;   /* XXX ? */
69
0
  cfi->datarel = 0;   /* XXX ? */
70
71
0
  return cfi;
72
0
}
73
74
static const uint8_t *
75
parse_eh_frame_hdr (const uint8_t *hdr, size_t hdr_size, GElf_Addr hdr_vaddr,
76
        const GElf_Ehdr *ehdr, GElf_Addr *eh_frame_vaddr,
77
        size_t *table_entries, uint8_t *table_encoding)
78
0
{
79
0
  const uint8_t *h = hdr;
80
81
0
  if (hdr_size < 4 || *h++ != 1)   /* version */
82
0
    return (void *) -1l;
83
84
0
  uint8_t eh_frame_ptr_encoding = *h++;
85
0
  uint8_t fde_count_encoding = *h++;
86
0
  uint8_t fde_table_encoding = *h++;
87
88
0
  if (eh_frame_ptr_encoding == DW_EH_PE_omit)
89
0
    return (void *) -1l;
90
91
  /* Dummy used by read_encoded_value.  */
92
0
  Elf_Data_Scn dummy_cfi_hdr_data =
93
0
    {
94
0
      .d = { .d_buf = (void *) hdr, .d_size = hdr_size }
95
0
    };
96
0
  Dwarf_CFI dummy_cfi =
97
0
    {
98
0
      .e_ident = ehdr->e_ident,
99
0
      .datarel = hdr_vaddr,
100
0
      .frame_vaddr = hdr_vaddr,
101
0
      .data = &dummy_cfi_hdr_data,
102
0
    };
103
104
0
  if (unlikely (read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h,
105
0
            eh_frame_vaddr)))
106
0
    return (void *) -1l;
107
108
0
  if (fde_count_encoding != DW_EH_PE_omit)
109
0
    {
110
0
      Dwarf_Word fde_count;
111
0
      if (unlikely (read_encoded_value (&dummy_cfi, fde_count_encoding, &h,
112
0
          &fde_count)))
113
0
  return (void *) -1l;
114
0
      if (fde_count != 0 && (size_t) fde_count == fde_count
115
0
    && fde_table_encoding != DW_EH_PE_omit
116
0
    && (fde_table_encoding &~ DW_EH_PE_signed) != DW_EH_PE_uleb128)
117
0
  {
118
0
    *table_entries = fde_count;
119
0
    *table_encoding = fde_table_encoding;
120
0
    return h;
121
0
  }
122
0
    }
123
124
0
  return NULL;
125
0
}
126
127
static Dwarf_CFI *
128
getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr)
129
0
{
130
0
  Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz,
131
0
           ELF_T_BYTE);
132
0
  if (data == NULL || data->d_buf == NULL)
133
0
    {
134
0
    invalid_hdr:
135
      /* XXX might be read error or corrupt phdr */
136
0
      __libdw_seterrno (DWARF_E_INVALID_CFI);
137
0
      return NULL;
138
0
    }
139
140
0
  size_t vsize, dmax;
141
0
  Dwarf_Addr eh_frame_ptr;
142
0
  size_t search_table_entries = 0;
143
0
  uint8_t search_table_encoding = 0;
144
0
  const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz,
145
0
                phdr->p_vaddr, ehdr,
146
0
                &eh_frame_ptr,
147
0
                &search_table_entries,
148
0
                &search_table_encoding);
149
150
  /* Make sure there is enough room for the entries in the table,
151
     each entry consists of 2 encoded values.  */
152
0
  vsize = encoded_value_size (data, ehdr->e_ident, search_table_encoding,
153
0
            NULL);
154
0
  dmax = phdr->p_filesz - (search_table - (const uint8_t *) data->d_buf);
155
0
  if (unlikely (search_table == (void *) -1l
156
0
    || vsize == 0
157
0
    || search_table_entries > (dmax / vsize) / 2))
158
0
    goto invalid_hdr;
159
160
0
  Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset;
161
0
  Dwarf_Word eh_frame_size = 0;
162
163
  /* XXX we have no way without section headers to know the size
164
     of the .eh_frame data.  Calculate the largest it might possibly be.
165
     This won't be wasteful if the file is already mmap'd, but if it isn't
166
     it might be quite excessive.  */
167
0
  size_t filesize;
168
0
  if (elf_rawfile (elf, &filesize) != NULL)
169
0
    eh_frame_size = filesize - eh_frame_offset;
170
171
0
  data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE);
172
0
  if (data == NULL)
173
0
    {
174
0
      __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */
175
0
      return NULL;
176
0
    }
177
0
  Dwarf_CFI *cfi = allocate_cfi (elf, ehdr, eh_frame_ptr);
178
0
  if (cfi != NULL)
179
0
    {
180
0
      cfi->data = (Elf_Data_Scn *) data;
181
182
0
      if (search_table != NULL)
183
0
  {
184
0
    cfi->search_table = search_table;
185
0
    cfi->search_table_len = phdr->p_filesz;
186
0
    cfi->search_table_vaddr = phdr->p_vaddr;
187
0
    cfi->search_table_encoding = search_table_encoding;
188
0
    cfi->search_table_entries = search_table_entries;
189
0
  }
190
0
    }
191
0
  return cfi;
192
0
}
193
194
/* Search the phdrs for PT_GNU_EH_FRAME.  */
195
static Dwarf_CFI *
196
getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
197
0
{
198
0
  size_t phnum;
199
0
  if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
200
0
    return NULL;
201
202
0
  for (size_t i = 0; i < phnum; ++i)
203
0
    {
204
0
      GElf_Phdr phdr_mem;
205
0
      GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
206
0
      if (unlikely (phdr == NULL))
207
0
  return NULL;
208
0
      if (phdr->p_type == PT_GNU_EH_FRAME)
209
0
  return getcfi_gnu_eh_frame (elf, ehdr, phdr);
210
0
    }
211
212
0
  __libdw_seterrno (DWARF_E_NO_DWARF);
213
0
  return NULL;
214
0
}
215
216
static Dwarf_CFI *
217
getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr,
218
         Elf_Scn *scn, GElf_Shdr *shdr,
219
         Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr)
220
0
{
221
0
  Elf_Data *data = elf_rawdata (scn, NULL);
222
0
  if (data == NULL || data->d_buf == NULL)
223
0
    {
224
0
      __libdw_seterrno (DWARF_E_INVALID_ELF);
225
0
      return NULL;
226
0
    }
227
0
  Dwarf_CFI *cfi = allocate_cfi (elf, ehdr, shdr->sh_addr);
228
0
  if (cfi != NULL)
229
0
    {
230
0
      cfi->data = (Elf_Data_Scn *) data;
231
0
      if (hdr_scn != NULL)
232
0
  {
233
0
    Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL);
234
0
    if (hdr_data != NULL && hdr_data->d_buf != NULL)
235
0
      {
236
0
        size_t vsize, dmax;
237
0
        GElf_Addr eh_frame_vaddr;
238
0
        cfi->search_table_vaddr = hdr_vaddr;
239
0
        cfi->search_table
240
0
    = parse_eh_frame_hdr (hdr_data->d_buf, hdr_data->d_size,
241
0
              hdr_vaddr, ehdr, &eh_frame_vaddr,
242
0
              &cfi->search_table_entries,
243
0
              &cfi->search_table_encoding);
244
0
        cfi->search_table_len = hdr_data->d_size;
245
246
        /* Make sure there is enough room for the entries in the table,
247
     each entry consists of 2 encoded values.  */
248
0
        vsize = encoded_value_size (hdr_data, ehdr->e_ident,
249
0
            cfi->search_table_encoding, NULL);
250
0
        dmax = hdr_data->d_size - (cfi->search_table
251
0
           - (const uint8_t *) hdr_data->d_buf);
252
0
        if (unlikely (cfi->search_table == (void *) -1l
253
0
          || vsize == 0
254
0
          || cfi->search_table_entries > (dmax / vsize) / 2))
255
0
    {
256
0
      free (cfi);
257
      /* XXX might be read error or corrupt phdr */
258
0
      __libdw_seterrno (DWARF_E_INVALID_CFI);
259
0
      return NULL;
260
0
    }
261
262
        /* Sanity check.  */
263
0
        if (unlikely (eh_frame_vaddr != shdr->sh_addr))
264
0
    cfi->search_table = NULL;
265
0
      }
266
0
  }
267
0
    }
268
0
  return cfi;
269
0
}
270
271
/* Search for the sections named ".eh_frame" and ".eh_frame_hdr".  */
272
static Dwarf_CFI *
273
getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr)
274
0
{
275
0
  size_t shstrndx;
276
0
  if (elf_getshdrstrndx (elf, &shstrndx) != 0)
277
0
    {
278
0
      __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
279
0
      return NULL;
280
0
    }
281
282
0
  if (shstrndx != 0)
283
0
    {
284
0
      Elf_Scn *hdr_scn = NULL;
285
0
      GElf_Addr hdr_vaddr = 0;
286
0
      Elf_Scn *scn = NULL;
287
0
      while ((scn = elf_nextscn (elf, scn)) != NULL)
288
0
  {
289
0
    GElf_Shdr shdr_mem;
290
0
    GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
291
0
    if (shdr == NULL)
292
0
      continue;
293
0
    const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
294
0
    if (name == NULL)
295
0
      continue;
296
0
    if (!strcmp (name, ".eh_frame_hdr"))
297
0
      {
298
0
        hdr_scn = scn;
299
0
        hdr_vaddr = shdr->sh_addr;
300
0
      }
301
0
    else if (!strcmp (name, ".eh_frame"))
302
0
      {
303
0
        if (shdr->sh_type != SHT_NOBITS)
304
0
    return getcfi_scn_eh_frame (elf, ehdr, scn, shdr,
305
0
              hdr_scn, hdr_vaddr);
306
0
        else
307
0
    return NULL;
308
0
      }
309
0
  }
310
0
    }
311
312
0
  return (void *) -1l;
313
0
}
314
315
Dwarf_CFI *
316
dwarf_getcfi_elf (Elf *elf)
317
0
{
318
0
  if (elf_kind (elf) != ELF_K_ELF)
319
0
    {
320
0
      __libdw_seterrno (DWARF_E_NOELF);
321
0
      return NULL;
322
0
    }
323
324
0
  GElf_Ehdr ehdr_mem;
325
0
  GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
326
0
  if (unlikely (ehdr == NULL))
327
0
    {
328
0
      __libdw_seterrno (DWARF_E_INVALID_ELF);
329
0
      return NULL;
330
0
    }
331
332
0
  Dwarf_CFI *result = getcfi_shdr (elf, ehdr);
333
0
  if (result == (void *) -1l)
334
0
    result = getcfi_phdr (elf, ehdr);
335
336
0
  return result;
337
0
}
338
INTDEF (dwarf_getcfi_elf)