1/* Get CFI from ELF file's exception-handling info. 2 Copyright (C) 2009-2010 Red Hat, Inc. 3 This file is part of Red Hat elfutils. 4 5 Red Hat elfutils is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by the 7 Free Software Foundation; version 2 of the License. 8 9 Red Hat elfutils is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 General Public License for more details. 13 14 You should have received a copy of the GNU General Public License along 15 with Red Hat elfutils; if not, write to the Free Software Foundation, 16 Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. 17 18 In addition, as a special exception, Red Hat, Inc. gives You the 19 additional right to link the code of Red Hat elfutils with code licensed 20 under any Open Source Initiative certified open source license 21 (http://www.opensource.org/licenses/index.php) which requires the 22 distribution of source code with any binary distribution and to 23 distribute linked combinations of the two. Non-GPL Code permitted under 24 this exception must only link to the code of Red Hat elfutils through 25 those well defined interfaces identified in the file named EXCEPTION 26 found in the source code files (the "Approved Interfaces"). The files 27 of Non-GPL Code may instantiate templates or use macros or inline 28 functions from the Approved Interfaces without causing the resulting 29 work to be covered by the GNU General Public License. Only Red Hat, 30 Inc. may make changes or additions to the list of Approved Interfaces. 31 Red Hat's grant of this exception is conditioned upon your not adding 32 any new exceptions. If you wish to add a new Approved Interface or 33 exception, please contact Red Hat. You must obey the GNU General Public 34 License in all respects for all of the Red Hat elfutils code and other 35 code used in conjunction with Red Hat elfutils except the Non-GPL Code 36 covered by this exception. If you modify this file, you may extend this 37 exception to your version of the file, but you are not obligated to do 38 so. If you do not wish to provide this exception without modification, 39 you must delete this exception statement from your version and license 40 this file solely under the GPL without exception. 41 42 Red Hat elfutils is an included package of the Open Invention Network. 43 An included package of the Open Invention Network is a package for which 44 Open Invention Network licensees cross-license their patents. No patent 45 license is granted, either expressly or impliedly, by designation as an 46 included package. Should you wish to participate in the Open Invention 47 Network licensing program, please visit www.openinventionnetwork.com 48 <http://www.openinventionnetwork.com>. */ 49 50#ifdef HAVE_CONFIG_H 51# include <config.h> 52#endif 53 54#include <stdlib.h> 55#include <string.h> 56#include <assert.h> 57 58#include "libdwP.h" 59#include "cfi.h" 60#include "encoded-value.h" 61#include <dwarf.h> 62 63 64static Dwarf_CFI * 65allocate_cfi (Elf *elf, GElf_Addr vaddr) 66{ 67 Dwarf_CFI *cfi = calloc (1, sizeof *cfi); 68 if (cfi == NULL) 69 { 70 __libdw_seterrno (DWARF_E_NOMEM); 71 return NULL; 72 } 73 74 cfi->e_ident = (unsigned char *) elf_getident (elf, NULL); 75 if (cfi->e_ident == NULL) 76 { 77 free (cfi); 78 __libdw_seterrno (DWARF_E_GETEHDR_ERROR); 79 return NULL; 80 } 81 82 if ((BYTE_ORDER == LITTLE_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2MSB) 83 || (BYTE_ORDER == BIG_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2LSB)) 84 cfi->other_byte_order = true; 85 86 cfi->frame_vaddr = vaddr; 87 cfi->textrel = 0; /* XXX ? */ 88 cfi->datarel = 0; /* XXX ? */ 89 90 return cfi; 91} 92 93static const uint8_t * 94parse_eh_frame_hdr (const uint8_t *hdr, size_t hdr_size, GElf_Addr hdr_vaddr, 95 const GElf_Ehdr *ehdr, GElf_Addr *eh_frame_vaddr, 96 size_t *table_entries, uint8_t *table_encoding) 97{ 98 const uint8_t *h = hdr; 99 100 if (*h++ != 1) /* version */ 101 return (void *) -1l; 102 103 uint8_t eh_frame_ptr_encoding = *h++; 104 uint8_t fde_count_encoding = *h++; 105 uint8_t fde_table_encoding = *h++; 106 107 if (eh_frame_ptr_encoding == DW_EH_PE_omit) 108 return (void *) -1l; 109 110 /* Dummy used by read_encoded_value. */ 111 Elf_Data_Scn dummy_cfi_hdr_data = 112 { 113 .d = { .d_buf = (void *) hdr, .d_size = hdr_size } 114 }; 115 Dwarf_CFI dummy_cfi = 116 { 117 .e_ident = ehdr->e_ident, 118 .datarel = hdr_vaddr, 119 .frame_vaddr = hdr_vaddr, 120 .data = &dummy_cfi_hdr_data, 121 }; 122 123 if (unlikely (read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h, 124 eh_frame_vaddr))) 125 return (void *) -1l; 126 127 if (fde_count_encoding != DW_EH_PE_omit) 128 { 129 Dwarf_Word fde_count; 130 if (unlikely (read_encoded_value (&dummy_cfi, fde_count_encoding, &h, 131 &fde_count))) 132 return (void *) -1l; 133 if (fde_count != 0 && (size_t) fde_count == fde_count 134 && fde_table_encoding != DW_EH_PE_omit 135 && (fde_table_encoding &~ DW_EH_PE_signed) != DW_EH_PE_uleb128) 136 { 137 *table_entries = fde_count; 138 *table_encoding = fde_table_encoding; 139 return h; 140 } 141 } 142 143 return NULL; 144} 145 146static Dwarf_CFI * 147getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr) 148{ 149 if (unlikely (phdr->p_filesz < 4)) 150 goto invalid; 151 152 Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz, 153 ELF_T_BYTE); 154 if (data == NULL) 155 { 156 invalid_hdr: 157 invalid: 158 /* XXX might be read error or corrupt phdr */ 159 __libdw_seterrno (DWARF_E_INVALID_CFI); 160 return NULL; 161 } 162 163 Dwarf_Addr eh_frame_ptr; 164 size_t search_table_entries; 165 uint8_t search_table_encoding; 166 const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz, 167 phdr->p_vaddr, ehdr, 168 &eh_frame_ptr, 169 &search_table_entries, 170 &search_table_encoding); 171 if (search_table == (void *) -1l) 172 goto invalid_hdr; 173 174 Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset; 175 Dwarf_Word eh_frame_size = 0; 176 177 /* XXX we have no way without section headers to know the size 178 of the .eh_frame data. Calculate the largest it might possibly be. 179 This won't be wasteful if the file is already mmap'd, but if it isn't 180 it might be quite excessive. */ 181 size_t filesize; 182 if (elf_rawfile (elf, &filesize) != NULL) 183 eh_frame_size = filesize - eh_frame_offset; 184 185 data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE); 186 if (data == NULL) 187 { 188 __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */ 189 return NULL; 190 } 191 Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr); 192 if (cfi != NULL) 193 { 194 cfi->data = (Elf_Data_Scn *) data; 195 196 if (search_table != NULL) 197 { 198 cfi->search_table = search_table; 199 cfi->search_table_vaddr = phdr->p_vaddr; 200 cfi->search_table_encoding = search_table_encoding; 201 cfi->search_table_entries = search_table_entries; 202 } 203 } 204 return cfi; 205} 206 207/* Search the phdrs for PT_GNU_EH_FRAME. */ 208static Dwarf_CFI * 209getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr) 210{ 211 size_t phnum; 212 if (unlikely (elf_getphdrnum (elf, &phnum) != 0)) 213 return NULL; 214 215 for (size_t i = 0; i < phnum; ++i) 216 { 217 GElf_Phdr phdr_mem; 218 GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); 219 if (unlikely (phdr == NULL)) 220 return NULL; 221 if (phdr->p_type == PT_GNU_EH_FRAME) 222 return getcfi_gnu_eh_frame (elf, ehdr, phdr); 223 } 224 225 __libdw_seterrno (DWARF_E_NO_DWARF); 226 return NULL; 227} 228 229static Dwarf_CFI * 230getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, 231 Elf_Scn *scn, GElf_Shdr *shdr, 232 Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr) 233{ 234 Elf_Data *data = elf_rawdata (scn, NULL); 235 if (data == NULL) 236 { 237 __libdw_seterrno (DWARF_E_INVALID_ELF); 238 return NULL; 239 } 240 Dwarf_CFI *cfi = allocate_cfi (elf, shdr->sh_addr); 241 if (cfi != NULL) 242 { 243 cfi->data = (Elf_Data_Scn *) data; 244 if (hdr_scn != NULL) 245 { 246 Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL); 247 if (hdr_data != NULL) 248 { 249 GElf_Addr eh_frame_vaddr; 250 cfi->search_table_vaddr = hdr_vaddr; 251 cfi->search_table 252 = parse_eh_frame_hdr (hdr_data->d_buf, hdr_data->d_size, 253 hdr_vaddr, ehdr, &eh_frame_vaddr, 254 &cfi->search_table_entries, 255 &cfi->search_table_encoding); 256 if (cfi->search_table == (void *) -1l) 257 { 258 free (cfi); 259 /* XXX might be read error or corrupt phdr */ 260 __libdw_seterrno (DWARF_E_INVALID_CFI); 261 return NULL; 262 } 263 264 /* Sanity check. */ 265 if (unlikely (eh_frame_vaddr != shdr->sh_addr)) 266 cfi->search_table = NULL; 267 } 268 } 269 } 270 return cfi; 271} 272 273/* Search for the sections named ".eh_frame" and ".eh_frame_hdr". */ 274static Dwarf_CFI * 275getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr) 276{ 277 size_t shstrndx; 278 if (elf_getshdrstrndx (elf, &shstrndx) != 0) 279 { 280 __libdw_seterrno (DWARF_E_GETEHDR_ERROR); 281 return NULL; 282 } 283 284 if (shstrndx != 0) 285 { 286 Elf_Scn *hdr_scn = NULL; 287 GElf_Addr hdr_vaddr = 0; 288 Elf_Scn *scn = NULL; 289 while ((scn = elf_nextscn (elf, scn)) != NULL) 290 { 291 GElf_Shdr shdr_mem; 292 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 293 if (shdr == NULL) 294 continue; 295 const char *name = elf_strptr (elf, shstrndx, shdr->sh_name); 296 if (name == NULL) 297 continue; 298 if (!strcmp (name, ".eh_frame_hdr")) 299 { 300 hdr_scn = scn; 301 hdr_vaddr = shdr->sh_addr; 302 } 303 else if (!strcmp (name, ".eh_frame")) 304 return getcfi_scn_eh_frame (elf, ehdr, scn, shdr, 305 hdr_scn, hdr_vaddr); 306 } 307 } 308 309 return (void *) -1l; 310} 311 312Dwarf_CFI * 313dwarf_getcfi_elf (elf) 314 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