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