1/* Get CFI from ELF file's exception-handling info. 2 Copyright (C) 2009-2010, 2014 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 (*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 if (unlikely (phdr->p_filesz < 4)) 129 goto invalid; 130 131 Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz, 132 ELF_T_BYTE); 133 if (data == NULL) 134 { 135 invalid_hdr: 136 invalid: 137 /* XXX might be read error or corrupt phdr */ 138 __libdw_seterrno (DWARF_E_INVALID_CFI); 139 return NULL; 140 } 141 142 Dwarf_Addr eh_frame_ptr; 143 size_t search_table_entries = 0; 144 uint8_t search_table_encoding = 0; 145 const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz, 146 phdr->p_vaddr, ehdr, 147 &eh_frame_ptr, 148 &search_table_entries, 149 &search_table_encoding); 150 if (search_table == (void *) -1l) 151 goto invalid_hdr; 152 153 Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset; 154 Dwarf_Word eh_frame_size = 0; 155 156 /* XXX we have no way without section headers to know the size 157 of the .eh_frame data. Calculate the largest it might possibly be. 158 This won't be wasteful if the file is already mmap'd, but if it isn't 159 it might be quite excessive. */ 160 size_t filesize; 161 if (elf_rawfile (elf, &filesize) != NULL) 162 eh_frame_size = filesize - eh_frame_offset; 163 164 data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE); 165 if (data == NULL) 166 { 167 __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */ 168 return NULL; 169 } 170 Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr); 171 if (cfi != NULL) 172 { 173 cfi->data = (Elf_Data_Scn *) data; 174 175 if (search_table != NULL) 176 { 177 cfi->search_table = search_table; 178 cfi->search_table_vaddr = phdr->p_vaddr; 179 cfi->search_table_encoding = search_table_encoding; 180 cfi->search_table_entries = search_table_entries; 181 } 182 } 183 return cfi; 184} 185 186/* Search the phdrs for PT_GNU_EH_FRAME. */ 187static Dwarf_CFI * 188getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr) 189{ 190 size_t phnum; 191 if (unlikely (elf_getphdrnum (elf, &phnum) != 0)) 192 return NULL; 193 194 for (size_t i = 0; i < phnum; ++i) 195 { 196 GElf_Phdr phdr_mem; 197 GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); 198 if (unlikely (phdr == NULL)) 199 return NULL; 200 if (phdr->p_type == PT_GNU_EH_FRAME) 201 return getcfi_gnu_eh_frame (elf, ehdr, phdr); 202 } 203 204 __libdw_seterrno (DWARF_E_NO_DWARF); 205 return NULL; 206} 207 208static Dwarf_CFI * 209getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, 210 Elf_Scn *scn, GElf_Shdr *shdr, 211 Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr) 212{ 213 Elf_Data *data = elf_rawdata (scn, NULL); 214 if (data == NULL) 215 { 216 __libdw_seterrno (DWARF_E_INVALID_ELF); 217 return NULL; 218 } 219 Dwarf_CFI *cfi = allocate_cfi (elf, shdr->sh_addr); 220 if (cfi != NULL) 221 { 222 cfi->data = (Elf_Data_Scn *) data; 223 if (hdr_scn != NULL) 224 { 225 Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL); 226 if (hdr_data != NULL) 227 { 228 GElf_Addr eh_frame_vaddr; 229 cfi->search_table_vaddr = hdr_vaddr; 230 cfi->search_table 231 = parse_eh_frame_hdr (hdr_data->d_buf, hdr_data->d_size, 232 hdr_vaddr, ehdr, &eh_frame_vaddr, 233 &cfi->search_table_entries, 234 &cfi->search_table_encoding); 235 if (cfi->search_table == (void *) -1l) 236 { 237 free (cfi); 238 /* XXX might be read error or corrupt phdr */ 239 __libdw_seterrno (DWARF_E_INVALID_CFI); 240 return NULL; 241 } 242 243 /* Sanity check. */ 244 if (unlikely (eh_frame_vaddr != shdr->sh_addr)) 245 cfi->search_table = NULL; 246 } 247 } 248 } 249 return cfi; 250} 251 252/* Search for the sections named ".eh_frame" and ".eh_frame_hdr". */ 253static Dwarf_CFI * 254getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr) 255{ 256 size_t shstrndx; 257 if (elf_getshdrstrndx (elf, &shstrndx) != 0) 258 { 259 __libdw_seterrno (DWARF_E_GETEHDR_ERROR); 260 return NULL; 261 } 262 263 if (shstrndx != 0) 264 { 265 Elf_Scn *hdr_scn = NULL; 266 GElf_Addr hdr_vaddr = 0; 267 Elf_Scn *scn = NULL; 268 while ((scn = elf_nextscn (elf, scn)) != NULL) 269 { 270 GElf_Shdr shdr_mem; 271 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 272 if (shdr == NULL) 273 continue; 274 const char *name = elf_strptr (elf, shstrndx, shdr->sh_name); 275 if (name == NULL) 276 continue; 277 if (!strcmp (name, ".eh_frame_hdr")) 278 { 279 hdr_scn = scn; 280 hdr_vaddr = shdr->sh_addr; 281 } 282 else if (!strcmp (name, ".eh_frame")) 283 { 284 if (shdr->sh_type == SHT_PROGBITS) 285 return getcfi_scn_eh_frame (elf, ehdr, scn, shdr, 286 hdr_scn, hdr_vaddr); 287 else 288 return NULL; 289 } 290 } 291 } 292 293 return (void *) -1l; 294} 295 296Dwarf_CFI * 297dwarf_getcfi_elf (elf) 298 Elf *elf; 299{ 300 if (elf_kind (elf) != ELF_K_ELF) 301 { 302 __libdw_seterrno (DWARF_E_NOELF); 303 return NULL; 304 } 305 306 GElf_Ehdr ehdr_mem; 307 GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); 308 if (unlikely (ehdr == NULL)) 309 { 310 __libdw_seterrno (DWARF_E_INVALID_ELF); 311 return NULL; 312 } 313 314 Dwarf_CFI *result = getcfi_shdr (elf, ehdr); 315 if (result == (void *) -1l) 316 result = getcfi_phdr (elf, ehdr); 317 318 return result; 319} 320INTDEF (dwarf_getcfi_elf) 321