unwind-dw2-fde-glibc.c revision 132718
1279377Simp/* Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. 2279377Simp Contributed by Jakub Jelinek <jakub@redhat.com>. 3279377Simp 4279377Simp This file is part of GCC. 5279377Simp 6279377Simp GCC is free software; you can redistribute it and/or modify 7279377Simp it under the terms of the GNU General Public License as published by 8279377Simp the Free Software Foundation; either version 2, or (at your option) 9279377Simp any later version. 10279377Simp 11279377Simp GCC is distributed in the hope that it will be useful, 12279377Simp but WITHOUT ANY WARRANTY; without even the implied warranty of 13279377Simp MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14279377Simp GNU General Public License for more details. 15279377Simp 16279377Simp You should have received a copy of the GNU General Public License 17279377Simp along with GCC; see the file COPYING. If not, write to 18279377Simp the Free Software Foundation, 59 Temple Place - Suite 330, 19279377Simp Boston, MA 02111-1307, USA. */ 20279377Simp 21279377Simp/* As a special exception, if you link this library with other files, 22279377Simp some of which are compiled with GCC, to produce an executable, 23279377Simp this library does not by itself cause the resulting executable 24279377Simp to be covered by the GNU General Public License. 25279377Simp This exception does not however invalidate any other reasons why 26279377Simp the executable file might be covered by the GNU General Public License. */ 27279377Simp 28279377Simp/* Locate the FDE entry for a given address, using PT_GNU_EH_FRAME ELF 29279377Simp segment and dl_iterate_phdr to avoid register/deregister calls at 30279377Simp DSO load/unload. */ 31279377Simp 32279377Simp#ifndef _GNU_SOURCE 33279377Simp#define _GNU_SOURCE 1 34279377Simp#endif 35279377Simp 36279377Simp#include "auto-host.h" /* For HAVE_LD_EH_FRAME_HDR. */ 37279377Simp#include "tconfig.h" 38279377Simp#include "tsystem.h" 39279377Simp#ifndef inhibit_libc 40279377Simp#include <link.h> 41279377Simp#endif 42279377Simp#include "coretypes.h" 43279377Simp#include "tm.h" 44279377Simp#include "dwarf2.h" 45279377Simp#include "unwind.h" 46279377Simp#define NO_BASE_OF_ENCODED_VALUE 47279377Simp#include "unwind-pe.h" 48279377Simp#include "unwind-dw2-fde.h" 49279377Simp#include "gthr.h" 50279377Simp 51279377Simp#if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \ 52279377Simp && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \ 53279377Simp || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG))) 54279377Simp 55279377Simpstatic const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases); 56279377Simp 57279377Simp#define _Unwind_Find_FDE _Unwind_Find_registered_FDE 58279377Simp#include "unwind-dw2-fde.c" 59279377Simp#undef _Unwind_Find_FDE 60279377Simp 61279377Simp#ifndef PT_GNU_EH_FRAME 62279377Simp#define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550) 63279377Simp#endif 64279377Simp 65279377Simpstruct unw_eh_callback_data 66279377Simp{ 67279377Simp _Unwind_Ptr pc; 68279377Simp void *tbase; 69279377Simp void *dbase; 70279377Simp void *func; 71279377Simp const fde *ret; 72279377Simp}; 73279377Simp 74279377Simpstruct unw_eh_frame_hdr 75279377Simp{ 76279377Simp unsigned char version; 77279377Simp unsigned char eh_frame_ptr_enc; 78279377Simp unsigned char fde_count_enc; 79279377Simp unsigned char table_enc; 80279377Simp}; 81279377Simp 82279377Simp/* Like base_of_encoded_value, but take the base from a struct 83279377Simp unw_eh_callback_data instead of an _Unwind_Context. */ 84279377Simp 85279377Simpstatic _Unwind_Ptr 86279377Simpbase_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data) 87279377Simp{ 88279377Simp if (encoding == DW_EH_PE_omit) 89279377Simp return 0; 90279377Simp 91279377Simp switch (encoding & 0x70) 92279377Simp { 93279377Simp case DW_EH_PE_absptr: 94279377Simp case DW_EH_PE_pcrel: 95279377Simp case DW_EH_PE_aligned: 96279377Simp return 0; 97279377Simp 98279377Simp case DW_EH_PE_textrel: 99279377Simp return (_Unwind_Ptr) data->tbase; 100279377Simp case DW_EH_PE_datarel: 101279377Simp return (_Unwind_Ptr) data->dbase; 102279377Simp } 103279377Simp abort (); 104279377Simp} 105279377Simp 106279377Simpstatic int 107279377Simp_Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr) 108279377Simp{ 109279377Simp struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr; 110279377Simp const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic; 111279377Simp long n, match; 112279377Simp _Unwind_Ptr load_base; 113279377Simp const unsigned char *p; 114279377Simp const struct unw_eh_frame_hdr *hdr; 115279377Simp _Unwind_Ptr eh_frame; 116279377Simp struct object ob; 117279377Simp 118279377Simp /* Make sure struct dl_phdr_info is at least as big as we need. */ 119279377Simp if (size < offsetof (struct dl_phdr_info, dlpi_phnum) 120279377Simp + sizeof (info->dlpi_phnum)) 121279377Simp return -1; 122279377Simp 123279377Simp match = 0; 124279377Simp phdr = info->dlpi_phdr; 125279377Simp load_base = info->dlpi_addr; 126279377Simp p_eh_frame_hdr = NULL; 127279377Simp p_dynamic = NULL; 128279377Simp 129279377Simp /* See if PC falls into one of the loaded segments. Find the eh_frame 130279377Simp segment at the same time. */ 131279377Simp for (n = info->dlpi_phnum; --n >= 0; phdr++) 132279377Simp { 133279377Simp if (phdr->p_type == PT_LOAD) 134279377Simp { 135279377Simp _Unwind_Ptr vaddr = phdr->p_vaddr + load_base; 136279377Simp if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz) 137279377Simp match = 1; 138279377Simp } 139279377Simp else if (phdr->p_type == PT_GNU_EH_FRAME) 140279377Simp p_eh_frame_hdr = phdr; 141279377Simp else if (phdr->p_type == PT_DYNAMIC) 142279377Simp p_dynamic = phdr; 143279377Simp } 144279377Simp if (!match || !p_eh_frame_hdr) 145279377Simp return 0; 146279377Simp 147279377Simp /* Read .eh_frame_hdr header. */ 148279377Simp hdr = (const struct unw_eh_frame_hdr *) 149279377Simp (p_eh_frame_hdr->p_vaddr + load_base); 150279377Simp if (hdr->version != 1) 151279377Simp return 1; 152279377Simp 153279377Simp#ifdef CRT_GET_RFIB_DATA 154279377Simp# ifdef __i386__ 155279377Simp data->dbase = NULL; 156279377Simp if (p_dynamic) 157279377Simp { 158279377Simp /* For dynamically linked executables and shared libraries, 159279377Simp DT_PLTGOT is the gp value for that object. */ 160279377Simp ElfW(Dyn) *dyn = (ElfW(Dyn) *) (p_dynamic->p_vaddr + load_base); 161279377Simp for (; dyn->d_tag != DT_NULL ; dyn++) 162279377Simp if (dyn->d_tag == DT_PLTGOT) 163279377Simp { 164279377Simp /* On IA-32, _DYNAMIC is writable and GLIBC has relocated it. */ 165279377Simp data->dbase = (void *) dyn->d_un.d_ptr; 166279377Simp break; 167279377Simp } 168279377Simp } 169279377Simp# else 170279377Simp# error What is DW_EH_PE_datarel base on this platform? 171279377Simp# endif 172279377Simp#endif 173279377Simp 174279377Simp p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc, 175279377Simp base_from_cb_data (hdr->eh_frame_ptr_enc, 176279377Simp data), 177279377Simp (const unsigned char *) (hdr + 1), 178279377Simp &eh_frame); 179279377Simp 180279377Simp /* We require here specific table encoding to speed things up. 181279377Simp Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start 182279377Simp as base, not the processor specific DW_EH_PE_datarel. */ 183279377Simp if (hdr->fde_count_enc != DW_EH_PE_omit 184279377Simp && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4)) 185279377Simp { 186279377Simp _Unwind_Ptr fde_count; 187279377Simp 188279377Simp p = read_encoded_value_with_base (hdr->fde_count_enc, 189279377Simp base_from_cb_data (hdr->fde_count_enc, 190279377Simp data), 191279377Simp p, &fde_count); 192279377Simp /* Shouldn't happen. */ 193279377Simp if (fde_count == 0) 194279377Simp return 1; 195279377Simp if ((((_Unwind_Ptr) p) & 3) == 0) 196279377Simp { 197279377Simp struct fde_table { 198279377Simp signed initial_loc __attribute__ ((mode (SI))); 199279377Simp signed fde __attribute__ ((mode (SI))); 200279377Simp }; 201279377Simp const struct fde_table *table = (const struct fde_table *) p; 202279377Simp size_t lo, hi, mid; 203279377Simp _Unwind_Ptr data_base = (_Unwind_Ptr) hdr; 204279377Simp fde *f; 205279377Simp unsigned int f_enc, f_enc_size; 206279377Simp _Unwind_Ptr range; 207279377Simp 208279377Simp mid = fde_count - 1; 209279377Simp if (data->pc < table[0].initial_loc + data_base) 210279377Simp return 1; 211279377Simp else if (data->pc < table[mid].initial_loc + data_base) 212279377Simp { 213279377Simp lo = 0; 214279377Simp hi = mid; 215279377Simp 216279377Simp while (lo < hi) 217279377Simp { 218279377Simp mid = (lo + hi) / 2; 219279377Simp if (data->pc < table[mid].initial_loc + data_base) 220279377Simp hi = mid; 221279377Simp else if (data->pc >= table[mid + 1].initial_loc + data_base) 222279377Simp lo = mid + 1; 223279377Simp else 224279377Simp break; 225279377Simp } 226279377Simp 227279377Simp if (lo >= hi) 228279377Simp __gxx_abort (); 229279377Simp } 230279377Simp 231279377Simp f = (fde *) (table[mid].fde + data_base); 232279377Simp f_enc = get_fde_encoding (f); 233279377Simp f_enc_size = size_of_encoded_value (f_enc); 234279377Simp read_encoded_value_with_base (f_enc & 0x0f, 0, 235279377Simp &f->pc_begin[f_enc_size], &range); 236279377Simp if (data->pc < table[mid].initial_loc + data_base + range) 237279377Simp data->ret = f; 238279377Simp data->func = (void *) (table[mid].initial_loc + data_base); 239279377Simp return 1; 240279377Simp } 241279377Simp } 242279377Simp 243279377Simp /* We have no sorted search table, so need to go the slow way. 244279377Simp As soon as GLIBC will provide API so to notify that a library has been 245279377Simp removed, we could cache this (and thus use search_object). */ 246279377Simp ob.pc_begin = NULL; 247279377Simp ob.tbase = data->tbase; 248279377Simp ob.dbase = data->dbase; 249279377Simp ob.u.single = (fde *) eh_frame; 250279377Simp ob.s.i = 0; 251279377Simp ob.s.b.mixed_encoding = 1; /* Need to assume worst case. */ 252279377Simp data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc); 253279377Simp if (data->ret != NULL) 254279377Simp { 255279377Simp unsigned int encoding = get_fde_encoding (data->ret); 256279377Simp read_encoded_value_with_base (encoding, 257279377Simp base_from_cb_data (encoding, data), 258279377Simp data->ret->pc_begin, 259279377Simp (_Unwind_Ptr *)&data->func); 260279377Simp } 261279377Simp return 1; 262279377Simp} 263279377Simp 264279377Simpconst fde * 265279377Simp_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases) 266279377Simp{ 267279377Simp struct unw_eh_callback_data data; 268279377Simp const fde *ret; 269279377Simp 270279377Simp ret = _Unwind_Find_registered_FDE (pc, bases); 271279377Simp if (ret != NULL) 272279377Simp return ret; 273279377Simp 274279377Simp data.pc = (_Unwind_Ptr) pc; 275279377Simp data.tbase = NULL; 276279377Simp data.dbase = NULL; 277279377Simp data.func = NULL; 278279377Simp data.ret = NULL; 279279377Simp 280279377Simp if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0) 281279377Simp return NULL; 282279377Simp 283279377Simp if (data.ret) 284279377Simp { 285279377Simp bases->tbase = data.tbase; 286279377Simp bases->dbase = data.dbase; 287279377Simp bases->func = data.func; 288279377Simp } 289279377Simp return data.ret; 290279377Simp} 291279377Simp 292279377Simp#else 293279377Simp/* Prevent multiple include of header files. */ 294279377Simp#define _Unwind_Find_FDE _Unwind_Find_FDE 295279377Simp#include "unwind-dw2-fde.c" 296279377Simp#endif 297279377Simp