1169689Skan/* Copyright (C) 2001, 2002, 2003, 2004, 2005 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 18169689Skan the Free Software Foundation, 51 Franklin Street, Fifth Floor, 19169689Skan Boston, MA 02110-1301, 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 "tconfig.h" 37132718Skan#include "tsystem.h" 3890075Sobrien#ifndef inhibit_libc 3990075Sobrien#include <link.h> 4090075Sobrien#endif 41132718Skan#include "coretypes.h" 42132718Skan#include "tm.h" 4390075Sobrien#include "dwarf2.h" 4490075Sobrien#include "unwind.h" 4590075Sobrien#define NO_BASE_OF_ENCODED_VALUE 4690075Sobrien#include "unwind-pe.h" 4790075Sobrien#include "unwind-dw2-fde.h" 48146895Skan#include "unwind-compat.h" 4990075Sobrien#include "gthr.h" 5090075Sobrien 5190075Sobrien#if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \ 5290075Sobrien && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \ 5390075Sobrien || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG))) 5490075Sobrien 55169689Skan#ifndef __RELOC_POINTER 56169689Skan# define __RELOC_POINTER(ptr, base) ((ptr) + (base)) 57169689Skan#endif 58169689Skan 59132718Skanstatic const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases); 6090075Sobrien 6190075Sobrien#define _Unwind_Find_FDE _Unwind_Find_registered_FDE 6290075Sobrien#include "unwind-dw2-fde.c" 6390075Sobrien#undef _Unwind_Find_FDE 6490075Sobrien 6590075Sobrien#ifndef PT_GNU_EH_FRAME 6690075Sobrien#define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550) 6790075Sobrien#endif 6890075Sobrien 6990075Sobrienstruct unw_eh_callback_data 7090075Sobrien{ 7190075Sobrien _Unwind_Ptr pc; 7290075Sobrien void *tbase; 7390075Sobrien void *dbase; 7490075Sobrien void *func; 75132718Skan const fde *ret; 76169689Skan int check_cache; 7790075Sobrien}; 7890075Sobrien 7990075Sobrienstruct unw_eh_frame_hdr 8090075Sobrien{ 8190075Sobrien unsigned char version; 8290075Sobrien unsigned char eh_frame_ptr_enc; 8390075Sobrien unsigned char fde_count_enc; 8490075Sobrien unsigned char table_enc; 8590075Sobrien}; 8690075Sobrien 87169689Skan#define FRAME_HDR_CACHE_SIZE 8 88169689Skan 89169689Skanstatic struct frame_hdr_cache_element 90169689Skan{ 91169689Skan _Unwind_Ptr pc_low; 92169689Skan _Unwind_Ptr pc_high; 93169689Skan _Unwind_Ptr load_base; 94169689Skan const ElfW(Phdr) *p_eh_frame_hdr; 95169689Skan const ElfW(Phdr) *p_dynamic; 96169689Skan struct frame_hdr_cache_element *link; 97169689Skan} frame_hdr_cache[FRAME_HDR_CACHE_SIZE]; 98169689Skan 99169689Skanstatic struct frame_hdr_cache_element *frame_hdr_cache_head; 100169689Skan 101117395Skan/* Like base_of_encoded_value, but take the base from a struct 102117395Skan unw_eh_callback_data instead of an _Unwind_Context. */ 103117395Skan 10490075Sobrienstatic _Unwind_Ptr 10590075Sobrienbase_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data) 10690075Sobrien{ 10790075Sobrien if (encoding == DW_EH_PE_omit) 10890075Sobrien return 0; 10990075Sobrien 11090075Sobrien switch (encoding & 0x70) 11190075Sobrien { 11290075Sobrien case DW_EH_PE_absptr: 11390075Sobrien case DW_EH_PE_pcrel: 11490075Sobrien case DW_EH_PE_aligned: 11590075Sobrien return 0; 116117395Skan 11790075Sobrien case DW_EH_PE_textrel: 11890075Sobrien return (_Unwind_Ptr) data->tbase; 11990075Sobrien case DW_EH_PE_datarel: 12090075Sobrien return (_Unwind_Ptr) data->dbase; 121169689Skan default: 122169689Skan gcc_unreachable (); 12390075Sobrien } 12490075Sobrien} 12590075Sobrien 12690075Sobrienstatic int 12790075Sobrien_Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr) 12890075Sobrien{ 12990075Sobrien struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr; 13090075Sobrien const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic; 13190075Sobrien long n, match; 132169689Skan#ifdef __FRV_FDPIC__ 133169689Skan struct elf32_fdpic_loadaddr load_base; 134169689Skan#else 13590075Sobrien _Unwind_Ptr load_base; 136169689Skan#endif 13790075Sobrien const unsigned char *p; 13890075Sobrien const struct unw_eh_frame_hdr *hdr; 13990075Sobrien _Unwind_Ptr eh_frame; 14090075Sobrien struct object ob; 141169689Skan 142169689Skan struct ext_dl_phdr_info 143169689Skan { 144169689Skan ElfW(Addr) dlpi_addr; 145169689Skan const char *dlpi_name; 146169689Skan const ElfW(Phdr) *dlpi_phdr; 147169689Skan ElfW(Half) dlpi_phnum; 148169689Skan unsigned long long int dlpi_adds; 149169689Skan unsigned long long int dlpi_subs; 150169689Skan }; 15190075Sobrien 15290075Sobrien match = 0; 15390075Sobrien phdr = info->dlpi_phdr; 15490075Sobrien load_base = info->dlpi_addr; 15590075Sobrien p_eh_frame_hdr = NULL; 15690075Sobrien p_dynamic = NULL; 15790075Sobrien 158169689Skan struct frame_hdr_cache_element *prev_cache_entry = NULL, 159169689Skan *last_cache_entry = NULL; 160169689Skan 161169689Skan if (data->check_cache && size >= sizeof (struct ext_dl_phdr_info)) 162169689Skan { 163169689Skan static unsigned long long adds = -1ULL, subs; 164169689Skan struct ext_dl_phdr_info *einfo = (struct ext_dl_phdr_info *) info; 165169689Skan 166169689Skan /* We use a least recently used cache replacement policy. Also, 167169689Skan the most recently used cache entries are placed at the head 168169689Skan of the search chain. */ 169169689Skan 170169689Skan if (einfo->dlpi_adds == adds && einfo->dlpi_subs == subs) 171169689Skan { 172169689Skan /* Find data->pc in shared library cache. 173169689Skan Set load_base, p_eh_frame_hdr and p_dynamic 174169689Skan plus match from the cache and goto 175169689Skan "Read .eh_frame_hdr header." below. */ 176169689Skan 177169689Skan struct frame_hdr_cache_element *cache_entry; 178169689Skan 179169689Skan for (cache_entry = frame_hdr_cache_head; 180169689Skan cache_entry; 181169689Skan cache_entry = cache_entry->link) 182169689Skan { 183169689Skan if (data->pc >= cache_entry->pc_low 184169689Skan && data->pc < cache_entry->pc_high) 185169689Skan { 186169689Skan load_base = cache_entry->load_base; 187169689Skan p_eh_frame_hdr = cache_entry->p_eh_frame_hdr; 188169689Skan p_dynamic = cache_entry->p_dynamic; 189169689Skan 190169689Skan /* And move the entry we're using to the head. */ 191169689Skan if (cache_entry != frame_hdr_cache_head) 192169689Skan { 193169689Skan prev_cache_entry->link = cache_entry->link; 194169689Skan cache_entry->link = frame_hdr_cache_head; 195169689Skan frame_hdr_cache_head = cache_entry; 196169689Skan } 197169689Skan goto found; 198169689Skan } 199169689Skan 200169689Skan last_cache_entry = cache_entry; 201169689Skan /* Exit early if we found an unused entry. */ 202169689Skan if ((cache_entry->pc_low | cache_entry->pc_high) == 0) 203169689Skan break; 204169689Skan if (cache_entry->link != NULL) 205169689Skan prev_cache_entry = cache_entry; 206169689Skan } 207169689Skan } 208169689Skan else 209169689Skan { 210169689Skan adds = einfo->dlpi_adds; 211169689Skan subs = einfo->dlpi_subs; 212169689Skan /* Initialize the cache. Create a chain of cache entries, 213169689Skan with the final one terminated by a NULL link. */ 214169689Skan int i; 215169689Skan for (i = 0; i < FRAME_HDR_CACHE_SIZE; i++) 216169689Skan { 217169689Skan frame_hdr_cache[i].pc_low = 0; 218169689Skan frame_hdr_cache[i].pc_high = 0; 219169689Skan frame_hdr_cache[i].link = &frame_hdr_cache[i+1]; 220169689Skan } 221169689Skan frame_hdr_cache[i-1].link = NULL; 222169689Skan frame_hdr_cache_head = &frame_hdr_cache[0]; 223169689Skan data->check_cache = 0; 224169689Skan } 225169689Skan } 226169689Skan 227169689Skan /* Make sure struct dl_phdr_info is at least as big as we need. */ 228169689Skan if (size < offsetof (struct dl_phdr_info, dlpi_phnum) 229169689Skan + sizeof (info->dlpi_phnum)) 230169689Skan return -1; 231169689Skan 232169689Skan _Unwind_Ptr pc_low = 0, pc_high = 0; 233169689Skan 23490075Sobrien /* See if PC falls into one of the loaded segments. Find the eh_frame 23590075Sobrien segment at the same time. */ 23690075Sobrien for (n = info->dlpi_phnum; --n >= 0; phdr++) 23790075Sobrien { 23890075Sobrien if (phdr->p_type == PT_LOAD) 23990075Sobrien { 240169689Skan _Unwind_Ptr vaddr = (_Unwind_Ptr) 241169689Skan __RELOC_POINTER (phdr->p_vaddr, load_base); 24290075Sobrien if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz) 243169689Skan { 244169689Skan match = 1; 245169689Skan pc_low = vaddr; 246169689Skan pc_high = vaddr + phdr->p_memsz; 247169689Skan } 24890075Sobrien } 24990075Sobrien else if (phdr->p_type == PT_GNU_EH_FRAME) 25090075Sobrien p_eh_frame_hdr = phdr; 25190075Sobrien else if (phdr->p_type == PT_DYNAMIC) 25290075Sobrien p_dynamic = phdr; 25390075Sobrien } 254169689Skan 255169689Skan if (!match) 25690075Sobrien return 0; 25790075Sobrien 258169689Skan if (size >= sizeof (struct ext_dl_phdr_info)) 259169689Skan { 260169689Skan /* Move the cache entry we're about to overwrite to the head of 261169689Skan the list. If either last_cache_entry or prev_cache_entry are 262169689Skan NULL, that cache entry is already at the head. */ 263169689Skan if (last_cache_entry != NULL && prev_cache_entry != NULL) 264169689Skan { 265169689Skan prev_cache_entry->link = last_cache_entry->link; 266169689Skan last_cache_entry->link = frame_hdr_cache_head; 267169689Skan frame_hdr_cache_head = last_cache_entry; 268169689Skan } 269169689Skan 270169689Skan frame_hdr_cache_head->load_base = load_base; 271169689Skan frame_hdr_cache_head->p_eh_frame_hdr = p_eh_frame_hdr; 272169689Skan frame_hdr_cache_head->p_dynamic = p_dynamic; 273169689Skan frame_hdr_cache_head->pc_low = pc_low; 274169689Skan frame_hdr_cache_head->pc_high = pc_high; 275169689Skan } 276169689Skan 277169689Skan found: 278169689Skan 279169689Skan if (!p_eh_frame_hdr) 280169689Skan return 0; 281169689Skan 28290075Sobrien /* Read .eh_frame_hdr header. */ 28390075Sobrien hdr = (const struct unw_eh_frame_hdr *) 284169689Skan __RELOC_POINTER (p_eh_frame_hdr->p_vaddr, load_base); 28590075Sobrien if (hdr->version != 1) 28690075Sobrien return 1; 28790075Sobrien 28890075Sobrien#ifdef CRT_GET_RFIB_DATA 28990075Sobrien# ifdef __i386__ 29090075Sobrien data->dbase = NULL; 29190075Sobrien if (p_dynamic) 29290075Sobrien { 293132718Skan /* For dynamically linked executables and shared libraries, 29490075Sobrien DT_PLTGOT is the gp value for that object. */ 295169689Skan ElfW(Dyn) *dyn = (ElfW(Dyn) *) 296169689Skan __RELOC_POINTER (p_dynamic->p_vaddr, load_base); 29790075Sobrien for (; dyn->d_tag != DT_NULL ; dyn++) 29890075Sobrien if (dyn->d_tag == DT_PLTGOT) 29990075Sobrien { 30090075Sobrien /* On IA-32, _DYNAMIC is writable and GLIBC has relocated it. */ 30190075Sobrien data->dbase = (void *) dyn->d_un.d_ptr; 30290075Sobrien break; 30390075Sobrien } 30490075Sobrien } 305169689Skan# elif defined __FRV_FDPIC__ && defined __linux__ 306169689Skan data->dbase = load_base.got_value; 30790075Sobrien# else 30890075Sobrien# error What is DW_EH_PE_datarel base on this platform? 30990075Sobrien# endif 31090075Sobrien#endif 31190075Sobrien 31290075Sobrien p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc, 31390075Sobrien base_from_cb_data (hdr->eh_frame_ptr_enc, 31490075Sobrien data), 31590075Sobrien (const unsigned char *) (hdr + 1), 31690075Sobrien &eh_frame); 31790075Sobrien 31890075Sobrien /* We require here specific table encoding to speed things up. 31990075Sobrien Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start 32090075Sobrien as base, not the processor specific DW_EH_PE_datarel. */ 32190075Sobrien if (hdr->fde_count_enc != DW_EH_PE_omit 32290075Sobrien && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4)) 32390075Sobrien { 32490075Sobrien _Unwind_Ptr fde_count; 32590075Sobrien 32690075Sobrien p = read_encoded_value_with_base (hdr->fde_count_enc, 32790075Sobrien base_from_cb_data (hdr->fde_count_enc, 32890075Sobrien data), 32990075Sobrien p, &fde_count); 33090075Sobrien /* Shouldn't happen. */ 33190075Sobrien if (fde_count == 0) 33290075Sobrien return 1; 33390075Sobrien if ((((_Unwind_Ptr) p) & 3) == 0) 33490075Sobrien { 33590075Sobrien struct fde_table { 33690075Sobrien signed initial_loc __attribute__ ((mode (SI))); 33790075Sobrien signed fde __attribute__ ((mode (SI))); 33890075Sobrien }; 33990075Sobrien const struct fde_table *table = (const struct fde_table *) p; 34090075Sobrien size_t lo, hi, mid; 34190075Sobrien _Unwind_Ptr data_base = (_Unwind_Ptr) hdr; 34290075Sobrien fde *f; 34390075Sobrien unsigned int f_enc, f_enc_size; 34490075Sobrien _Unwind_Ptr range; 34590075Sobrien 34690075Sobrien mid = fde_count - 1; 34790075Sobrien if (data->pc < table[0].initial_loc + data_base) 34890075Sobrien return 1; 34990075Sobrien else if (data->pc < table[mid].initial_loc + data_base) 35090075Sobrien { 35190075Sobrien lo = 0; 35290075Sobrien hi = mid; 35390075Sobrien 35490075Sobrien while (lo < hi) 35590075Sobrien { 35690075Sobrien mid = (lo + hi) / 2; 35790075Sobrien if (data->pc < table[mid].initial_loc + data_base) 35890075Sobrien hi = mid; 35990075Sobrien else if (data->pc >= table[mid + 1].initial_loc + data_base) 36090075Sobrien lo = mid + 1; 36190075Sobrien else 36290075Sobrien break; 36390075Sobrien } 36490075Sobrien 365169689Skan gcc_assert (lo < hi); 36690075Sobrien } 36790075Sobrien 36890075Sobrien f = (fde *) (table[mid].fde + data_base); 36990075Sobrien f_enc = get_fde_encoding (f); 37090075Sobrien f_enc_size = size_of_encoded_value (f_enc); 37190075Sobrien read_encoded_value_with_base (f_enc & 0x0f, 0, 37290075Sobrien &f->pc_begin[f_enc_size], &range); 37390075Sobrien if (data->pc < table[mid].initial_loc + data_base + range) 37490075Sobrien data->ret = f; 37590075Sobrien data->func = (void *) (table[mid].initial_loc + data_base); 37690075Sobrien return 1; 37790075Sobrien } 37890075Sobrien } 37990075Sobrien 38090075Sobrien /* We have no sorted search table, so need to go the slow way. 38190075Sobrien As soon as GLIBC will provide API so to notify that a library has been 38290075Sobrien removed, we could cache this (and thus use search_object). */ 38390075Sobrien ob.pc_begin = NULL; 38490075Sobrien ob.tbase = data->tbase; 38590075Sobrien ob.dbase = data->dbase; 38690075Sobrien ob.u.single = (fde *) eh_frame; 38790075Sobrien ob.s.i = 0; 38890075Sobrien ob.s.b.mixed_encoding = 1; /* Need to assume worst case. */ 38990075Sobrien data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc); 39090075Sobrien if (data->ret != NULL) 39190075Sobrien { 392169689Skan _Unwind_Ptr func; 39390075Sobrien unsigned int encoding = get_fde_encoding (data->ret); 394169689Skan 39590075Sobrien read_encoded_value_with_base (encoding, 39690075Sobrien base_from_cb_data (encoding, data), 397169689Skan data->ret->pc_begin, &func); 398169689Skan data->func = (void *) func; 39990075Sobrien } 40090075Sobrien return 1; 40190075Sobrien} 40290075Sobrien 403132718Skanconst fde * 40490075Sobrien_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases) 40590075Sobrien{ 40690075Sobrien struct unw_eh_callback_data data; 407132718Skan const fde *ret; 40890075Sobrien 40990075Sobrien ret = _Unwind_Find_registered_FDE (pc, bases); 41090075Sobrien if (ret != NULL) 41190075Sobrien return ret; 41290075Sobrien 41390075Sobrien data.pc = (_Unwind_Ptr) pc; 41490075Sobrien data.tbase = NULL; 41590075Sobrien data.dbase = NULL; 41690075Sobrien data.func = NULL; 41790075Sobrien data.ret = NULL; 418169689Skan data.check_cache = 1; 41990075Sobrien 42090075Sobrien if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0) 42190075Sobrien return NULL; 42290075Sobrien 42390075Sobrien if (data.ret) 42490075Sobrien { 42590075Sobrien bases->tbase = data.tbase; 42690075Sobrien bases->dbase = data.dbase; 42790075Sobrien bases->func = data.func; 42890075Sobrien } 42990075Sobrien return data.ret; 43090075Sobrien} 43190075Sobrien 43290075Sobrien#else 43390075Sobrien/* Prevent multiple include of header files. */ 43490075Sobrien#define _Unwind_Find_FDE _Unwind_Find_FDE 43590075Sobrien#include "unwind-dw2-fde.c" 43690075Sobrien#endif 437146895Skan 438146895Skan#if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS) 439146895Skanalias (_Unwind_Find_FDE); 440146895Skan#endif 441