1da0c48c4Sopenharmony_ci/* FDE reading. 2da0c48c4Sopenharmony_ci Copyright (C) 2009-2010, 2014, 2015 Red Hat, Inc. 3da0c48c4Sopenharmony_ci This file is part of elfutils. 4da0c48c4Sopenharmony_ci 5da0c48c4Sopenharmony_ci This file is free software; you can redistribute it and/or modify 6da0c48c4Sopenharmony_ci it under the terms of either 7da0c48c4Sopenharmony_ci 8da0c48c4Sopenharmony_ci * the GNU Lesser General Public License as published by the Free 9da0c48c4Sopenharmony_ci Software Foundation; either version 3 of the License, or (at 10da0c48c4Sopenharmony_ci your option) any later version 11da0c48c4Sopenharmony_ci 12da0c48c4Sopenharmony_ci or 13da0c48c4Sopenharmony_ci 14da0c48c4Sopenharmony_ci * the GNU General Public License as published by the Free 15da0c48c4Sopenharmony_ci Software Foundation; either version 2 of the License, or (at 16da0c48c4Sopenharmony_ci your option) any later version 17da0c48c4Sopenharmony_ci 18da0c48c4Sopenharmony_ci or both in parallel, as here. 19da0c48c4Sopenharmony_ci 20da0c48c4Sopenharmony_ci elfutils is distributed in the hope that it will be useful, but 21da0c48c4Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 22da0c48c4Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23da0c48c4Sopenharmony_ci General Public License for more details. 24da0c48c4Sopenharmony_ci 25da0c48c4Sopenharmony_ci You should have received copies of the GNU General Public License and 26da0c48c4Sopenharmony_ci the GNU Lesser General Public License along with this program. If 27da0c48c4Sopenharmony_ci not, see <http://www.gnu.org/licenses/>. */ 28da0c48c4Sopenharmony_ci 29da0c48c4Sopenharmony_ci#ifdef HAVE_CONFIG_H 30da0c48c4Sopenharmony_ci# include <config.h> 31da0c48c4Sopenharmony_ci#endif 32da0c48c4Sopenharmony_ci 33da0c48c4Sopenharmony_ci#include "cfi.h" 34da0c48c4Sopenharmony_ci#include <search.h> 35da0c48c4Sopenharmony_ci#include <stdlib.h> 36da0c48c4Sopenharmony_ci 37da0c48c4Sopenharmony_ci#include "encoded-value.h" 38da0c48c4Sopenharmony_ci 39da0c48c4Sopenharmony_cistatic int 40da0c48c4Sopenharmony_cicompare_fde (const void *a, const void *b) 41da0c48c4Sopenharmony_ci{ 42da0c48c4Sopenharmony_ci const struct dwarf_fde *fde1 = a; 43da0c48c4Sopenharmony_ci const struct dwarf_fde *fde2 = b; 44da0c48c4Sopenharmony_ci 45da0c48c4Sopenharmony_ci /* Find out which of the two arguments is the search value. 46da0c48c4Sopenharmony_ci It has end offset 0. */ 47da0c48c4Sopenharmony_ci if (fde1->end == 0) 48da0c48c4Sopenharmony_ci { 49da0c48c4Sopenharmony_ci if (fde1->start < fde2->start) 50da0c48c4Sopenharmony_ci return -1; 51da0c48c4Sopenharmony_ci if (fde1->start >= fde2->end) 52da0c48c4Sopenharmony_ci return 1; 53da0c48c4Sopenharmony_ci } 54da0c48c4Sopenharmony_ci else 55da0c48c4Sopenharmony_ci { 56da0c48c4Sopenharmony_ci if (fde2->start < fde1->start) 57da0c48c4Sopenharmony_ci return 1; 58da0c48c4Sopenharmony_ci if (fde2->start >= fde1->end) 59da0c48c4Sopenharmony_ci return -1; 60da0c48c4Sopenharmony_ci } 61da0c48c4Sopenharmony_ci 62da0c48c4Sopenharmony_ci return 0; 63da0c48c4Sopenharmony_ci} 64da0c48c4Sopenharmony_ci 65da0c48c4Sopenharmony_cistatic struct dwarf_fde * 66da0c48c4Sopenharmony_ciintern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry) 67da0c48c4Sopenharmony_ci{ 68da0c48c4Sopenharmony_ci /* Look up the new entry's CIE. */ 69da0c48c4Sopenharmony_ci struct dwarf_cie *cie = __libdw_find_cie (cache, entry->CIE_pointer); 70da0c48c4Sopenharmony_ci if (cie == NULL) 71da0c48c4Sopenharmony_ci return (void *) -1l; 72da0c48c4Sopenharmony_ci 73da0c48c4Sopenharmony_ci struct dwarf_fde *fde = malloc (sizeof (struct dwarf_fde)); 74da0c48c4Sopenharmony_ci if (fde == NULL) 75da0c48c4Sopenharmony_ci { 76da0c48c4Sopenharmony_ci __libdw_seterrno (DWARF_E_NOMEM); 77da0c48c4Sopenharmony_ci return NULL; 78da0c48c4Sopenharmony_ci } 79da0c48c4Sopenharmony_ci 80da0c48c4Sopenharmony_ci fde->instructions = entry->start; 81da0c48c4Sopenharmony_ci fde->instructions_end = entry->end; 82da0c48c4Sopenharmony_ci if (unlikely (read_encoded_value (cache, cie->fde_encoding, 83da0c48c4Sopenharmony_ci &fde->instructions, &fde->start)) 84da0c48c4Sopenharmony_ci || unlikely (read_encoded_value (cache, cie->fde_encoding & 0x0f, 85da0c48c4Sopenharmony_ci &fde->instructions, &fde->end))) 86da0c48c4Sopenharmony_ci { 87da0c48c4Sopenharmony_ci free (fde); 88da0c48c4Sopenharmony_ci __libdw_seterrno (DWARF_E_INVALID_DWARF); 89da0c48c4Sopenharmony_ci return NULL; 90da0c48c4Sopenharmony_ci } 91da0c48c4Sopenharmony_ci fde->end += fde->start; 92da0c48c4Sopenharmony_ci 93da0c48c4Sopenharmony_ci /* Make sure the fde actually covers a real code range. */ 94da0c48c4Sopenharmony_ci if (fde->start >= fde->end) 95da0c48c4Sopenharmony_ci { 96da0c48c4Sopenharmony_ci free (fde); 97da0c48c4Sopenharmony_ci return (void *) -1; 98da0c48c4Sopenharmony_ci } 99da0c48c4Sopenharmony_ci 100da0c48c4Sopenharmony_ci fde->cie = cie; 101da0c48c4Sopenharmony_ci 102da0c48c4Sopenharmony_ci if (cie->sized_augmentation_data) 103da0c48c4Sopenharmony_ci { 104da0c48c4Sopenharmony_ci /* The CIE augmentation says the FDE has a DW_FORM_block 105da0c48c4Sopenharmony_ci before its actual instruction stream. */ 106da0c48c4Sopenharmony_ci Dwarf_Word len; 107da0c48c4Sopenharmony_ci get_uleb128 (len, fde->instructions, fde->instructions_end); 108da0c48c4Sopenharmony_ci if ((Dwarf_Word) (fde->instructions_end - fde->instructions) < len) 109da0c48c4Sopenharmony_ci { 110da0c48c4Sopenharmony_ci free (fde); 111da0c48c4Sopenharmony_ci __libdw_seterrno (DWARF_E_INVALID_DWARF); 112da0c48c4Sopenharmony_ci return NULL; 113da0c48c4Sopenharmony_ci } 114da0c48c4Sopenharmony_ci fde->instructions += len; 115da0c48c4Sopenharmony_ci } 116da0c48c4Sopenharmony_ci else 117da0c48c4Sopenharmony_ci /* We had to understand all of the CIE augmentation string. 118da0c48c4Sopenharmony_ci We've recorded the number of data bytes in FDEs. */ 119da0c48c4Sopenharmony_ci fde->instructions += cie->fde_augmentation_data_size; 120da0c48c4Sopenharmony_ci 121da0c48c4Sopenharmony_ci /* Add the new entry to the search tree. */ 122da0c48c4Sopenharmony_ci struct dwarf_fde **tres = tsearch (fde, &cache->fde_tree, &compare_fde); 123da0c48c4Sopenharmony_ci if (tres == NULL) 124da0c48c4Sopenharmony_ci { 125da0c48c4Sopenharmony_ci free (fde); 126da0c48c4Sopenharmony_ci __libdw_seterrno (DWARF_E_NOMEM); 127da0c48c4Sopenharmony_ci return NULL; 128da0c48c4Sopenharmony_ci } 129da0c48c4Sopenharmony_ci else if (*tres != fde) 130da0c48c4Sopenharmony_ci { 131da0c48c4Sopenharmony_ci /* There is already an FDE in the cache that covers the same 132da0c48c4Sopenharmony_ci address range. That is odd. Ignore this FDE. And just use 133da0c48c4Sopenharmony_ci the one in the cache for consistency. */ 134da0c48c4Sopenharmony_ci free (fde); 135da0c48c4Sopenharmony_ci return *tres; 136da0c48c4Sopenharmony_ci } 137da0c48c4Sopenharmony_ci 138da0c48c4Sopenharmony_ci return fde; 139da0c48c4Sopenharmony_ci} 140da0c48c4Sopenharmony_ci 141da0c48c4Sopenharmony_cistruct dwarf_fde * 142da0c48c4Sopenharmony_ciinternal_function 143da0c48c4Sopenharmony_ci__libdw_fde_by_offset (Dwarf_CFI *cache, Dwarf_Off offset) 144da0c48c4Sopenharmony_ci{ 145da0c48c4Sopenharmony_ci Dwarf_CFI_Entry entry; 146da0c48c4Sopenharmony_ci Dwarf_Off next_offset; 147da0c48c4Sopenharmony_ci int result = INTUSE(dwarf_next_cfi) (cache->e_ident, 148da0c48c4Sopenharmony_ci &cache->data->d, CFI_IS_EH (cache), 149da0c48c4Sopenharmony_ci offset, &next_offset, &entry); 150da0c48c4Sopenharmony_ci if (result != 0) 151da0c48c4Sopenharmony_ci { 152da0c48c4Sopenharmony_ci if (result > 0) 153da0c48c4Sopenharmony_ci invalid: 154da0c48c4Sopenharmony_ci __libdw_seterrno (DWARF_E_INVALID_DWARF); 155da0c48c4Sopenharmony_ci return NULL; 156da0c48c4Sopenharmony_ci } 157da0c48c4Sopenharmony_ci 158da0c48c4Sopenharmony_ci if (unlikely (dwarf_cfi_cie_p (&entry))) 159da0c48c4Sopenharmony_ci goto invalid; 160da0c48c4Sopenharmony_ci 161da0c48c4Sopenharmony_ci /* We have a new FDE to consider. */ 162da0c48c4Sopenharmony_ci struct dwarf_fde *fde = intern_fde (cache, &entry.fde); 163da0c48c4Sopenharmony_ci if (fde == (void *) -1l || fde == NULL) 164da0c48c4Sopenharmony_ci return NULL; 165da0c48c4Sopenharmony_ci 166da0c48c4Sopenharmony_ci /* If this happened to be what we would have read next, notice it. */ 167da0c48c4Sopenharmony_ci if (cache->next_offset == offset) 168da0c48c4Sopenharmony_ci cache->next_offset = next_offset; 169da0c48c4Sopenharmony_ci 170da0c48c4Sopenharmony_ci return fde; 171da0c48c4Sopenharmony_ci} 172da0c48c4Sopenharmony_ci 173da0c48c4Sopenharmony_ci/* Use a binary search table in .eh_frame_hdr format, yield an FDE offset. */ 174da0c48c4Sopenharmony_cistatic Dwarf_Off 175da0c48c4Sopenharmony_cibinary_search_fde (Dwarf_CFI *cache, Dwarf_Addr address) 176da0c48c4Sopenharmony_ci{ 177da0c48c4Sopenharmony_ci const size_t size = 2 * encoded_value_size (&cache->data->d, cache->e_ident, 178da0c48c4Sopenharmony_ci cache->search_table_encoding, 179da0c48c4Sopenharmony_ci NULL); 180da0c48c4Sopenharmony_ci if (unlikely (size == 0)) 181da0c48c4Sopenharmony_ci return (Dwarf_Off) -1l; 182da0c48c4Sopenharmony_ci 183da0c48c4Sopenharmony_ci /* Dummy used by read_encoded_value. */ 184da0c48c4Sopenharmony_ci Elf_Data_Scn dummy_cfi_hdr_data = 185da0c48c4Sopenharmony_ci { 186da0c48c4Sopenharmony_ci .d = { .d_buf = (void *) cache->search_table, 187da0c48c4Sopenharmony_ci .d_size = cache->search_table_len } 188da0c48c4Sopenharmony_ci }; 189da0c48c4Sopenharmony_ci 190da0c48c4Sopenharmony_ci Dwarf_CFI dummy_cfi = 191da0c48c4Sopenharmony_ci { 192da0c48c4Sopenharmony_ci .e_ident = cache->e_ident, 193da0c48c4Sopenharmony_ci .datarel = cache->search_table_vaddr, 194da0c48c4Sopenharmony_ci .frame_vaddr = cache->search_table_vaddr, 195da0c48c4Sopenharmony_ci .data = &dummy_cfi_hdr_data 196da0c48c4Sopenharmony_ci }; 197da0c48c4Sopenharmony_ci 198da0c48c4Sopenharmony_ci size_t l = 0, u = cache->search_table_entries; 199da0c48c4Sopenharmony_ci while (l < u) 200da0c48c4Sopenharmony_ci { 201da0c48c4Sopenharmony_ci size_t idx = (l + u) / 2; 202da0c48c4Sopenharmony_ci 203da0c48c4Sopenharmony_ci /* Max idx * size is checked against search_table len when 204da0c48c4Sopenharmony_ci loading eh_frame_hdr. */ 205da0c48c4Sopenharmony_ci const uint8_t *p = &cache->search_table[idx * size]; 206da0c48c4Sopenharmony_ci Dwarf_Addr start; 207da0c48c4Sopenharmony_ci if (unlikely (read_encoded_value (&dummy_cfi, 208da0c48c4Sopenharmony_ci cache->search_table_encoding, &p, 209da0c48c4Sopenharmony_ci &start))) 210da0c48c4Sopenharmony_ci break; 211da0c48c4Sopenharmony_ci if (address < start) 212da0c48c4Sopenharmony_ci u = idx; 213da0c48c4Sopenharmony_ci else 214da0c48c4Sopenharmony_ci { 215da0c48c4Sopenharmony_ci l = idx + 1; 216da0c48c4Sopenharmony_ci 217da0c48c4Sopenharmony_ci Dwarf_Addr fde; 218da0c48c4Sopenharmony_ci if (unlikely (read_encoded_value (&dummy_cfi, 219da0c48c4Sopenharmony_ci cache->search_table_encoding, &p, 220da0c48c4Sopenharmony_ci &fde))) 221da0c48c4Sopenharmony_ci break; 222da0c48c4Sopenharmony_ci 223da0c48c4Sopenharmony_ci /* If this is the last entry, its upper bound is assumed to be 224da0c48c4Sopenharmony_ci the end of the module. 225da0c48c4Sopenharmony_ci XXX really should be end of containing PT_LOAD segment */ 226da0c48c4Sopenharmony_ci if (l < cache->search_table_entries) 227da0c48c4Sopenharmony_ci { 228da0c48c4Sopenharmony_ci /* Look at the start address in the following entry. */ 229da0c48c4Sopenharmony_ci Dwarf_Addr end; 230da0c48c4Sopenharmony_ci if (unlikely (read_encoded_value 231da0c48c4Sopenharmony_ci (&dummy_cfi, cache->search_table_encoding, &p, 232da0c48c4Sopenharmony_ci &end))) 233da0c48c4Sopenharmony_ci break; 234da0c48c4Sopenharmony_ci if (address >= end) 235da0c48c4Sopenharmony_ci continue; 236da0c48c4Sopenharmony_ci } 237da0c48c4Sopenharmony_ci 238da0c48c4Sopenharmony_ci return fde - cache->frame_vaddr; 239da0c48c4Sopenharmony_ci } 240da0c48c4Sopenharmony_ci } 241da0c48c4Sopenharmony_ci 242da0c48c4Sopenharmony_ci return (Dwarf_Off) -1l; 243da0c48c4Sopenharmony_ci} 244da0c48c4Sopenharmony_ci 245da0c48c4Sopenharmony_cistruct dwarf_fde * 246da0c48c4Sopenharmony_ciinternal_function 247da0c48c4Sopenharmony_ci__libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address) 248da0c48c4Sopenharmony_ci{ 249da0c48c4Sopenharmony_ci /* Look for a cached FDE covering this address. */ 250da0c48c4Sopenharmony_ci 251da0c48c4Sopenharmony_ci const struct dwarf_fde fde_key = { .start = address, .end = 0 }; 252da0c48c4Sopenharmony_ci struct dwarf_fde **found = tfind (&fde_key, &cache->fde_tree, &compare_fde); 253da0c48c4Sopenharmony_ci if (found != NULL) 254da0c48c4Sopenharmony_ci return *found; 255da0c48c4Sopenharmony_ci 256da0c48c4Sopenharmony_ci /* Use .eh_frame_hdr binary search table if possible. */ 257da0c48c4Sopenharmony_ci if (cache->search_table != NULL) 258da0c48c4Sopenharmony_ci { 259da0c48c4Sopenharmony_ci Dwarf_Off offset = binary_search_fde (cache, address); 260da0c48c4Sopenharmony_ci if (offset == (Dwarf_Off) -1l) 261da0c48c4Sopenharmony_ci goto no_match; 262da0c48c4Sopenharmony_ci struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset); 263da0c48c4Sopenharmony_ci if (likely (fde != NULL)) 264da0c48c4Sopenharmony_ci { 265da0c48c4Sopenharmony_ci /* Sanity check the address range. */ 266da0c48c4Sopenharmony_ci if (unlikely (address < fde->start)) 267da0c48c4Sopenharmony_ci { 268da0c48c4Sopenharmony_ci __libdw_seterrno (DWARF_E_INVALID_DWARF); 269da0c48c4Sopenharmony_ci return NULL; 270da0c48c4Sopenharmony_ci } 271da0c48c4Sopenharmony_ci /* .eh_frame_hdr does not indicate length covered by FDE. */ 272da0c48c4Sopenharmony_ci if (unlikely (address >= fde->end)) 273da0c48c4Sopenharmony_ci goto no_match; 274da0c48c4Sopenharmony_ci } 275da0c48c4Sopenharmony_ci return fde; 276da0c48c4Sopenharmony_ci } 277da0c48c4Sopenharmony_ci 278da0c48c4Sopenharmony_ci /* It's not there. Read more CFI entries until we find it. */ 279da0c48c4Sopenharmony_ci while (1) 280da0c48c4Sopenharmony_ci { 281da0c48c4Sopenharmony_ci Dwarf_Off last_offset = cache->next_offset; 282da0c48c4Sopenharmony_ci Dwarf_CFI_Entry entry; 283da0c48c4Sopenharmony_ci int result = INTUSE(dwarf_next_cfi) (cache->e_ident, 284da0c48c4Sopenharmony_ci &cache->data->d, CFI_IS_EH (cache), 285da0c48c4Sopenharmony_ci last_offset, &cache->next_offset, 286da0c48c4Sopenharmony_ci &entry); 287da0c48c4Sopenharmony_ci if (result > 0) 288da0c48c4Sopenharmony_ci break; 289da0c48c4Sopenharmony_ci if (result < 0) 290da0c48c4Sopenharmony_ci { 291da0c48c4Sopenharmony_ci if (cache->next_offset == last_offset) 292da0c48c4Sopenharmony_ci /* We couldn't progress past the bogus FDE. */ 293da0c48c4Sopenharmony_ci break; 294da0c48c4Sopenharmony_ci /* Skip the loser and look at the next entry. */ 295da0c48c4Sopenharmony_ci continue; 296da0c48c4Sopenharmony_ci } 297da0c48c4Sopenharmony_ci 298da0c48c4Sopenharmony_ci if (dwarf_cfi_cie_p (&entry)) 299da0c48c4Sopenharmony_ci { 300da0c48c4Sopenharmony_ci /* This is a CIE, not an FDE. We eagerly intern these 301da0c48c4Sopenharmony_ci because the next FDE will usually refer to this CIE. */ 302da0c48c4Sopenharmony_ci __libdw_intern_cie (cache, last_offset, &entry.cie); 303da0c48c4Sopenharmony_ci continue; 304da0c48c4Sopenharmony_ci } 305da0c48c4Sopenharmony_ci 306da0c48c4Sopenharmony_ci /* We have a new FDE to consider. */ 307da0c48c4Sopenharmony_ci struct dwarf_fde *fde = intern_fde (cache, &entry.fde); 308da0c48c4Sopenharmony_ci 309da0c48c4Sopenharmony_ci if (fde == (void *) -1l) /* Bad FDE, but we can keep looking. */ 310da0c48c4Sopenharmony_ci continue; 311da0c48c4Sopenharmony_ci 312da0c48c4Sopenharmony_ci if (fde == NULL) /* Bad data. */ 313da0c48c4Sopenharmony_ci return NULL; 314da0c48c4Sopenharmony_ci 315da0c48c4Sopenharmony_ci /* Is this the one we're looking for? */ 316da0c48c4Sopenharmony_ci if (fde->start <= address && fde->end > address) 317da0c48c4Sopenharmony_ci return fde; 318da0c48c4Sopenharmony_ci } 319da0c48c4Sopenharmony_ci 320da0c48c4Sopenharmony_ci no_match: 321da0c48c4Sopenharmony_ci /* We found no FDE covering this address. */ 322da0c48c4Sopenharmony_ci __libdw_seterrno (DWARF_E_NO_MATCH); 323da0c48c4Sopenharmony_ci return NULL; 324da0c48c4Sopenharmony_ci} 325