unwind-dw2-fde-glibc.c revision 146895
1132718Skan/* Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. 290075Sobrien Contributed by Jakub Jelinek <jakub@redhat.com>. 390075Sobrien 4132718Skan This file is part of GCC. 590075Sobrien 6132718Skan GCC is free software; you can redistribute it and/or modify 790075Sobrien it under the terms of the GNU General Public License as published by 890075Sobrien the Free Software Foundation; either version 2, or (at your option) 990075Sobrien any later version. 1090075Sobrien 11132718Skan GCC is distributed in the hope that it will be useful, 1290075Sobrien but WITHOUT ANY WARRANTY; without even the implied warranty of 1390075Sobrien MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1490075Sobrien GNU General Public License for more details. 1590075Sobrien 1690075Sobrien You should have received a copy of the GNU General Public License 17132718Skan along with GCC; see the file COPYING. If not, write to 1890075Sobrien the Free Software Foundation, 59 Temple Place - Suite 330, 1990075Sobrien Boston, MA 02111-1307, USA. */ 2090075Sobrien 2190075Sobrien/* As a special exception, if you link this library with other files, 2290075Sobrien some of which are compiled with GCC, to produce an executable, 2390075Sobrien this library does not by itself cause the resulting executable 2490075Sobrien to be covered by the GNU General Public License. 2590075Sobrien This exception does not however invalidate any other reasons why 2690075Sobrien the executable file might be covered by the GNU General Public License. */ 2790075Sobrien 2890075Sobrien/* Locate the FDE entry for a given address, using PT_GNU_EH_FRAME ELF 2990075Sobrien segment and dl_iterate_phdr to avoid register/deregister calls at 3090075Sobrien DSO load/unload. */ 3190075Sobrien 3296263Sobrien#ifndef _GNU_SOURCE 3396263Sobrien#define _GNU_SOURCE 1 3496263Sobrien#endif 3596263Sobrien 3690075Sobrien#include "auto-host.h" /* For HAVE_LD_EH_FRAME_HDR. */ 3790075Sobrien#include "tconfig.h" 38132718Skan#include "tsystem.h" 3990075Sobrien#ifndef inhibit_libc 4090075Sobrien#include <link.h> 4190075Sobrien#endif 42132718Skan#include "coretypes.h" 43132718Skan#include "tm.h" 4490075Sobrien#include "dwarf2.h" 4590075Sobrien#include "unwind.h" 4690075Sobrien#define NO_BASE_OF_ENCODED_VALUE 4790075Sobrien#include "unwind-pe.h" 4890075Sobrien#include "unwind-dw2-fde.h" 49146895Skan#include "unwind-compat.h" 5090075Sobrien#include "gthr.h" 5190075Sobrien 5290075Sobrien#if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \ 5390075Sobrien && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \ 5490075Sobrien || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG))) 5590075Sobrien 56132718Skanstatic const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases); 5790075Sobrien 5890075Sobrien#define _Unwind_Find_FDE _Unwind_Find_registered_FDE 5990075Sobrien#include "unwind-dw2-fde.c" 6090075Sobrien#undef _Unwind_Find_FDE 6190075Sobrien 6290075Sobrien#ifndef PT_GNU_EH_FRAME 6390075Sobrien#define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550) 6490075Sobrien#endif 6590075Sobrien 6690075Sobrienstruct unw_eh_callback_data 6790075Sobrien{ 6890075Sobrien _Unwind_Ptr pc; 6990075Sobrien void *tbase; 7090075Sobrien void *dbase; 7190075Sobrien void *func; 72132718Skan const fde *ret; 7390075Sobrien}; 7490075Sobrien 7590075Sobrienstruct unw_eh_frame_hdr 7690075Sobrien{ 7790075Sobrien unsigned char version; 7890075Sobrien unsigned char eh_frame_ptr_enc; 7990075Sobrien unsigned char fde_count_enc; 8090075Sobrien unsigned char table_enc; 8190075Sobrien}; 8290075Sobrien 83117395Skan/* Like base_of_encoded_value, but take the base from a struct 84117395Skan unw_eh_callback_data instead of an _Unwind_Context. */ 85117395Skan 8690075Sobrienstatic _Unwind_Ptr 8790075Sobrienbase_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data) 8890075Sobrien{ 8990075Sobrien if (encoding == DW_EH_PE_omit) 9090075Sobrien return 0; 9190075Sobrien 9290075Sobrien switch (encoding & 0x70) 9390075Sobrien { 9490075Sobrien case DW_EH_PE_absptr: 9590075Sobrien case DW_EH_PE_pcrel: 9690075Sobrien case DW_EH_PE_aligned: 9790075Sobrien return 0; 98117395Skan 9990075Sobrien case DW_EH_PE_textrel: 10090075Sobrien return (_Unwind_Ptr) data->tbase; 10190075Sobrien case DW_EH_PE_datarel: 10290075Sobrien return (_Unwind_Ptr) data->dbase; 10390075Sobrien } 10490075Sobrien abort (); 10590075Sobrien} 10690075Sobrien 10790075Sobrienstatic int 10890075Sobrien_Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr) 10990075Sobrien{ 11090075Sobrien struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr; 11190075Sobrien const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic; 11290075Sobrien long n, match; 11390075Sobrien _Unwind_Ptr load_base; 11490075Sobrien const unsigned char *p; 11590075Sobrien const struct unw_eh_frame_hdr *hdr; 11690075Sobrien _Unwind_Ptr eh_frame; 11790075Sobrien struct object ob; 11890075Sobrien 11990075Sobrien /* Make sure struct dl_phdr_info is at least as big as we need. */ 12090075Sobrien if (size < offsetof (struct dl_phdr_info, dlpi_phnum) 12190075Sobrien + sizeof (info->dlpi_phnum)) 12290075Sobrien return -1; 12390075Sobrien 12490075Sobrien match = 0; 12590075Sobrien phdr = info->dlpi_phdr; 12690075Sobrien load_base = info->dlpi_addr; 12790075Sobrien p_eh_frame_hdr = NULL; 12890075Sobrien p_dynamic = NULL; 12990075Sobrien 13090075Sobrien /* See if PC falls into one of the loaded segments. Find the eh_frame 13190075Sobrien segment at the same time. */ 13290075Sobrien for (n = info->dlpi_phnum; --n >= 0; phdr++) 13390075Sobrien { 13490075Sobrien if (phdr->p_type == PT_LOAD) 13590075Sobrien { 13690075Sobrien _Unwind_Ptr vaddr = phdr->p_vaddr + load_base; 13790075Sobrien if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz) 13890075Sobrien match = 1; 13990075Sobrien } 14090075Sobrien else if (phdr->p_type == PT_GNU_EH_FRAME) 14190075Sobrien p_eh_frame_hdr = phdr; 14290075Sobrien else if (phdr->p_type == PT_DYNAMIC) 14390075Sobrien p_dynamic = phdr; 14490075Sobrien } 14590075Sobrien if (!match || !p_eh_frame_hdr) 14690075Sobrien return 0; 14790075Sobrien 14890075Sobrien /* Read .eh_frame_hdr header. */ 14990075Sobrien hdr = (const struct unw_eh_frame_hdr *) 15090075Sobrien (p_eh_frame_hdr->p_vaddr + load_base); 15190075Sobrien if (hdr->version != 1) 15290075Sobrien return 1; 15390075Sobrien 15490075Sobrien#ifdef CRT_GET_RFIB_DATA 15590075Sobrien# ifdef __i386__ 15690075Sobrien data->dbase = NULL; 15790075Sobrien if (p_dynamic) 15890075Sobrien { 159132718Skan /* For dynamically linked executables and shared libraries, 16090075Sobrien DT_PLTGOT is the gp value for that object. */ 16190075Sobrien ElfW(Dyn) *dyn = (ElfW(Dyn) *) (p_dynamic->p_vaddr + load_base); 16290075Sobrien for (; dyn->d_tag != DT_NULL ; dyn++) 16390075Sobrien if (dyn->d_tag == DT_PLTGOT) 16490075Sobrien { 16590075Sobrien /* On IA-32, _DYNAMIC is writable and GLIBC has relocated it. */ 16690075Sobrien data->dbase = (void *) dyn->d_un.d_ptr; 16790075Sobrien break; 16890075Sobrien } 16990075Sobrien } 17090075Sobrien# else 17190075Sobrien# error What is DW_EH_PE_datarel base on this platform? 17290075Sobrien# endif 17390075Sobrien#endif 17490075Sobrien 17590075Sobrien p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc, 17690075Sobrien base_from_cb_data (hdr->eh_frame_ptr_enc, 17790075Sobrien data), 17890075Sobrien (const unsigned char *) (hdr + 1), 17990075Sobrien &eh_frame); 18090075Sobrien 18190075Sobrien /* We require here specific table encoding to speed things up. 18290075Sobrien Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start 18390075Sobrien as base, not the processor specific DW_EH_PE_datarel. */ 18490075Sobrien if (hdr->fde_count_enc != DW_EH_PE_omit 18590075Sobrien && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4)) 18690075Sobrien { 18790075Sobrien _Unwind_Ptr fde_count; 18890075Sobrien 18990075Sobrien p = read_encoded_value_with_base (hdr->fde_count_enc, 19090075Sobrien base_from_cb_data (hdr->fde_count_enc, 19190075Sobrien data), 19290075Sobrien p, &fde_count); 19390075Sobrien /* Shouldn't happen. */ 19490075Sobrien if (fde_count == 0) 19590075Sobrien return 1; 19690075Sobrien if ((((_Unwind_Ptr) p) & 3) == 0) 19790075Sobrien { 19890075Sobrien struct fde_table { 19990075Sobrien signed initial_loc __attribute__ ((mode (SI))); 20090075Sobrien signed fde __attribute__ ((mode (SI))); 20190075Sobrien }; 20290075Sobrien const struct fde_table *table = (const struct fde_table *) p; 20390075Sobrien size_t lo, hi, mid; 20490075Sobrien _Unwind_Ptr data_base = (_Unwind_Ptr) hdr; 20590075Sobrien fde *f; 20690075Sobrien unsigned int f_enc, f_enc_size; 20790075Sobrien _Unwind_Ptr range; 20890075Sobrien 20990075Sobrien mid = fde_count - 1; 21090075Sobrien if (data->pc < table[0].initial_loc + data_base) 21190075Sobrien return 1; 21290075Sobrien else if (data->pc < table[mid].initial_loc + data_base) 21390075Sobrien { 21490075Sobrien lo = 0; 21590075Sobrien hi = mid; 21690075Sobrien 21790075Sobrien while (lo < hi) 21890075Sobrien { 21990075Sobrien mid = (lo + hi) / 2; 22090075Sobrien if (data->pc < table[mid].initial_loc + data_base) 22190075Sobrien hi = mid; 22290075Sobrien else if (data->pc >= table[mid + 1].initial_loc + data_base) 22390075Sobrien lo = mid + 1; 22490075Sobrien else 22590075Sobrien break; 22690075Sobrien } 22790075Sobrien 22890075Sobrien if (lo >= hi) 22990075Sobrien __gxx_abort (); 23090075Sobrien } 23190075Sobrien 23290075Sobrien f = (fde *) (table[mid].fde + data_base); 23390075Sobrien f_enc = get_fde_encoding (f); 23490075Sobrien f_enc_size = size_of_encoded_value (f_enc); 23590075Sobrien read_encoded_value_with_base (f_enc & 0x0f, 0, 23690075Sobrien &f->pc_begin[f_enc_size], &range); 23790075Sobrien if (data->pc < table[mid].initial_loc + data_base + range) 23890075Sobrien data->ret = f; 23990075Sobrien data->func = (void *) (table[mid].initial_loc + data_base); 24090075Sobrien return 1; 24190075Sobrien } 24290075Sobrien } 24390075Sobrien 24490075Sobrien /* We have no sorted search table, so need to go the slow way. 24590075Sobrien As soon as GLIBC will provide API so to notify that a library has been 24690075Sobrien removed, we could cache this (and thus use search_object). */ 24790075Sobrien ob.pc_begin = NULL; 24890075Sobrien ob.tbase = data->tbase; 24990075Sobrien ob.dbase = data->dbase; 25090075Sobrien ob.u.single = (fde *) eh_frame; 25190075Sobrien ob.s.i = 0; 25290075Sobrien ob.s.b.mixed_encoding = 1; /* Need to assume worst case. */ 25390075Sobrien data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc); 25490075Sobrien if (data->ret != NULL) 25590075Sobrien { 25690075Sobrien unsigned int encoding = get_fde_encoding (data->ret); 25790075Sobrien read_encoded_value_with_base (encoding, 25890075Sobrien base_from_cb_data (encoding, data), 25990075Sobrien data->ret->pc_begin, 26090075Sobrien (_Unwind_Ptr *)&data->func); 26190075Sobrien } 26290075Sobrien return 1; 26390075Sobrien} 26490075Sobrien 265132718Skanconst fde * 26690075Sobrien_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases) 26790075Sobrien{ 26890075Sobrien struct unw_eh_callback_data data; 269132718Skan const fde *ret; 27090075Sobrien 27190075Sobrien ret = _Unwind_Find_registered_FDE (pc, bases); 27290075Sobrien if (ret != NULL) 27390075Sobrien return ret; 27490075Sobrien 27590075Sobrien data.pc = (_Unwind_Ptr) pc; 27690075Sobrien data.tbase = NULL; 27790075Sobrien data.dbase = NULL; 27890075Sobrien data.func = NULL; 27990075Sobrien data.ret = NULL; 28090075Sobrien 28190075Sobrien if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0) 28290075Sobrien return NULL; 28390075Sobrien 28490075Sobrien if (data.ret) 28590075Sobrien { 28690075Sobrien bases->tbase = data.tbase; 28790075Sobrien bases->dbase = data.dbase; 28890075Sobrien bases->func = data.func; 28990075Sobrien } 29090075Sobrien return data.ret; 29190075Sobrien} 29290075Sobrien 29390075Sobrien#else 29490075Sobrien/* Prevent multiple include of header files. */ 29590075Sobrien#define _Unwind_Find_FDE _Unwind_Find_FDE 29690075Sobrien#include "unwind-dw2-fde.c" 29790075Sobrien#endif 298146895Skan 299146895Skan#if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS) 300146895Skanalias (_Unwind_Find_FDE); 301146895Skan#endif 302