unwind-dw2-fde-glibc.c revision 90075
190075Sobrien/* Copyright (C) 2001, 2002 Free Software Foundation, Inc. 290075Sobrien Contributed by Jakub Jelinek <jakub@redhat.com>. 390075Sobrien 490075Sobrien This file is part of GNU CC. 590075Sobrien 690075Sobrien GNU CC 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 1190075Sobrien GNU CC 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 1790075Sobrien along with GNU CC; 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 3290075Sobrien#include "auto-host.h" /* For HAVE_LD_EH_FRAME_HDR. */ 3390075Sobrien#include "tconfig.h" 3490075Sobrien#ifndef inhibit_libc 3590075Sobrien#include <stddef.h> 3690075Sobrien#include <stdlib.h> 3790075Sobrien#include <link.h> 3890075Sobrien#endif 3990075Sobrien#include "tsystem.h" 4090075Sobrien#include "dwarf2.h" 4190075Sobrien#include "unwind.h" 4290075Sobrien#define NO_BASE_OF_ENCODED_VALUE 4390075Sobrien#include "unwind-pe.h" 4490075Sobrien#include "unwind-dw2-fde.h" 4590075Sobrien#include "gthr.h" 4690075Sobrien 4790075Sobrien#if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \ 4890075Sobrien && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \ 4990075Sobrien || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG))) 5090075Sobrien 5190075Sobrienstatic fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases); 5290075Sobrien 5390075Sobrien#define _Unwind_Find_FDE _Unwind_Find_registered_FDE 5490075Sobrien#include "unwind-dw2-fde.c" 5590075Sobrien#undef _Unwind_Find_FDE 5690075Sobrien 5790075Sobrien#ifndef PT_GNU_EH_FRAME 5890075Sobrien#define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550) 5990075Sobrien#endif 6090075Sobrien 6190075Sobrienstruct unw_eh_callback_data 6290075Sobrien{ 6390075Sobrien _Unwind_Ptr pc; 6490075Sobrien void *tbase; 6590075Sobrien void *dbase; 6690075Sobrien void *func; 6790075Sobrien fde *ret; 6890075Sobrien}; 6990075Sobrien 7090075Sobrienstruct unw_eh_frame_hdr 7190075Sobrien{ 7290075Sobrien unsigned char version; 7390075Sobrien unsigned char eh_frame_ptr_enc; 7490075Sobrien unsigned char fde_count_enc; 7590075Sobrien unsigned char table_enc; 7690075Sobrien}; 7790075Sobrien 7890075Sobrien/* Like base_of_encoded_value, but take the base from a struct object 7990075Sobrien instead of an _Unwind_Context. */ 8090075Sobrien 8190075Sobrienstatic _Unwind_Ptr 8290075Sobrienbase_from_cb_data (unsigned char encoding, struct unw_eh_callback_data *data) 8390075Sobrien{ 8490075Sobrien if (encoding == DW_EH_PE_omit) 8590075Sobrien return 0; 8690075Sobrien 8790075Sobrien switch (encoding & 0x70) 8890075Sobrien { 8990075Sobrien case DW_EH_PE_absptr: 9090075Sobrien case DW_EH_PE_pcrel: 9190075Sobrien case DW_EH_PE_aligned: 9290075Sobrien return 0; 9390075Sobrien 9490075Sobrien case DW_EH_PE_textrel: 9590075Sobrien return (_Unwind_Ptr) data->tbase; 9690075Sobrien case DW_EH_PE_datarel: 9790075Sobrien return (_Unwind_Ptr) data->dbase; 9890075Sobrien } 9990075Sobrien abort (); 10090075Sobrien} 10190075Sobrien 10290075Sobrienstatic int 10390075Sobrien_Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr) 10490075Sobrien{ 10590075Sobrien struct unw_eh_callback_data *data = (struct unw_eh_callback_data *) ptr; 10690075Sobrien const ElfW(Phdr) *phdr, *p_eh_frame_hdr, *p_dynamic; 10790075Sobrien long n, match; 10890075Sobrien _Unwind_Ptr load_base; 10990075Sobrien const unsigned char *p; 11090075Sobrien const struct unw_eh_frame_hdr *hdr; 11190075Sobrien _Unwind_Ptr eh_frame; 11290075Sobrien struct object ob; 11390075Sobrien 11490075Sobrien /* Make sure struct dl_phdr_info is at least as big as we need. */ 11590075Sobrien if (size < offsetof (struct dl_phdr_info, dlpi_phnum) 11690075Sobrien + sizeof (info->dlpi_phnum)) 11790075Sobrien return -1; 11890075Sobrien 11990075Sobrien match = 0; 12090075Sobrien phdr = info->dlpi_phdr; 12190075Sobrien load_base = info->dlpi_addr; 12290075Sobrien p_eh_frame_hdr = NULL; 12390075Sobrien p_dynamic = NULL; 12490075Sobrien 12590075Sobrien /* See if PC falls into one of the loaded segments. Find the eh_frame 12690075Sobrien segment at the same time. */ 12790075Sobrien for (n = info->dlpi_phnum; --n >= 0; phdr++) 12890075Sobrien { 12990075Sobrien if (phdr->p_type == PT_LOAD) 13090075Sobrien { 13190075Sobrien _Unwind_Ptr vaddr = phdr->p_vaddr + load_base; 13290075Sobrien if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz) 13390075Sobrien match = 1; 13490075Sobrien } 13590075Sobrien else if (phdr->p_type == PT_GNU_EH_FRAME) 13690075Sobrien p_eh_frame_hdr = phdr; 13790075Sobrien else if (phdr->p_type == PT_DYNAMIC) 13890075Sobrien p_dynamic = phdr; 13990075Sobrien } 14090075Sobrien if (!match || !p_eh_frame_hdr) 14190075Sobrien return 0; 14290075Sobrien 14390075Sobrien /* Read .eh_frame_hdr header. */ 14490075Sobrien hdr = (const struct unw_eh_frame_hdr *) 14590075Sobrien (p_eh_frame_hdr->p_vaddr + load_base); 14690075Sobrien if (hdr->version != 1) 14790075Sobrien return 1; 14890075Sobrien 14990075Sobrien#ifdef CRT_GET_RFIB_DATA 15090075Sobrien# ifdef __i386__ 15190075Sobrien data->dbase = NULL; 15290075Sobrien if (p_dynamic) 15390075Sobrien { 15490075Sobrien /* For dynamicly linked executables and shared libraries, 15590075Sobrien DT_PLTGOT is the gp value for that object. */ 15690075Sobrien ElfW(Dyn) *dyn = (ElfW(Dyn) *) (p_dynamic->p_vaddr + load_base); 15790075Sobrien for (; dyn->d_tag != DT_NULL ; dyn++) 15890075Sobrien if (dyn->d_tag == DT_PLTGOT) 15990075Sobrien { 16090075Sobrien /* On IA-32, _DYNAMIC is writable and GLIBC has relocated it. */ 16190075Sobrien data->dbase = (void *) dyn->d_un.d_ptr; 16290075Sobrien break; 16390075Sobrien } 16490075Sobrien } 16590075Sobrien# else 16690075Sobrien# error What is DW_EH_PE_datarel base on this platform? 16790075Sobrien# endif 16890075Sobrien#endif 16990075Sobrien#ifdef CRT_GET_RFIB_TEXT 17090075Sobrien# error What is DW_EH_PE_textrel base on this platform? 17190075Sobrien#endif 17290075Sobrien 17390075Sobrien p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc, 17490075Sobrien base_from_cb_data (hdr->eh_frame_ptr_enc, 17590075Sobrien data), 17690075Sobrien (const unsigned char *) (hdr + 1), 17790075Sobrien &eh_frame); 17890075Sobrien 17990075Sobrien /* We require here specific table encoding to speed things up. 18090075Sobrien Also, DW_EH_PE_datarel here means using PT_GNU_EH_FRAME start 18190075Sobrien as base, not the processor specific DW_EH_PE_datarel. */ 18290075Sobrien if (hdr->fde_count_enc != DW_EH_PE_omit 18390075Sobrien && hdr->table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4)) 18490075Sobrien { 18590075Sobrien _Unwind_Ptr fde_count; 18690075Sobrien 18790075Sobrien p = read_encoded_value_with_base (hdr->fde_count_enc, 18890075Sobrien base_from_cb_data (hdr->fde_count_enc, 18990075Sobrien data), 19090075Sobrien p, &fde_count); 19190075Sobrien /* Shouldn't happen. */ 19290075Sobrien if (fde_count == 0) 19390075Sobrien return 1; 19490075Sobrien if ((((_Unwind_Ptr) p) & 3) == 0) 19590075Sobrien { 19690075Sobrien struct fde_table { 19790075Sobrien signed initial_loc __attribute__ ((mode (SI))); 19890075Sobrien signed fde __attribute__ ((mode (SI))); 19990075Sobrien }; 20090075Sobrien const struct fde_table *table = (const struct fde_table *) p; 20190075Sobrien size_t lo, hi, mid; 20290075Sobrien _Unwind_Ptr data_base = (_Unwind_Ptr) hdr; 20390075Sobrien fde *f; 20490075Sobrien unsigned int f_enc, f_enc_size; 20590075Sobrien _Unwind_Ptr range; 20690075Sobrien 20790075Sobrien mid = fde_count - 1; 20890075Sobrien if (data->pc < table[0].initial_loc + data_base) 20990075Sobrien return 1; 21090075Sobrien else if (data->pc < table[mid].initial_loc + data_base) 21190075Sobrien { 21290075Sobrien lo = 0; 21390075Sobrien hi = mid; 21490075Sobrien 21590075Sobrien while (lo < hi) 21690075Sobrien { 21790075Sobrien mid = (lo + hi) / 2; 21890075Sobrien if (data->pc < table[mid].initial_loc + data_base) 21990075Sobrien hi = mid; 22090075Sobrien else if (data->pc >= table[mid + 1].initial_loc + data_base) 22190075Sobrien lo = mid + 1; 22290075Sobrien else 22390075Sobrien break; 22490075Sobrien } 22590075Sobrien 22690075Sobrien if (lo >= hi) 22790075Sobrien __gxx_abort (); 22890075Sobrien } 22990075Sobrien 23090075Sobrien f = (fde *) (table[mid].fde + data_base); 23190075Sobrien f_enc = get_fde_encoding (f); 23290075Sobrien f_enc_size = size_of_encoded_value (f_enc); 23390075Sobrien read_encoded_value_with_base (f_enc & 0x0f, 0, 23490075Sobrien &f->pc_begin[f_enc_size], &range); 23590075Sobrien if (data->pc < table[mid].initial_loc + data_base + range) 23690075Sobrien data->ret = f; 23790075Sobrien data->func = (void *) (table[mid].initial_loc + data_base); 23890075Sobrien return 1; 23990075Sobrien } 24090075Sobrien } 24190075Sobrien 24290075Sobrien /* We have no sorted search table, so need to go the slow way. 24390075Sobrien As soon as GLIBC will provide API so to notify that a library has been 24490075Sobrien removed, we could cache this (and thus use search_object). */ 24590075Sobrien ob.pc_begin = NULL; 24690075Sobrien ob.tbase = data->tbase; 24790075Sobrien ob.dbase = data->dbase; 24890075Sobrien ob.u.single = (fde *) eh_frame; 24990075Sobrien ob.s.i = 0; 25090075Sobrien ob.s.b.mixed_encoding = 1; /* Need to assume worst case. */ 25190075Sobrien data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc); 25290075Sobrien if (data->ret != NULL) 25390075Sobrien { 25490075Sobrien unsigned int encoding = get_fde_encoding (data->ret); 25590075Sobrien read_encoded_value_with_base (encoding, 25690075Sobrien base_from_cb_data (encoding, data), 25790075Sobrien data->ret->pc_begin, 25890075Sobrien (_Unwind_Ptr *)&data->func); 25990075Sobrien } 26090075Sobrien return 1; 26190075Sobrien} 26290075Sobrien 26390075Sobrienfde * 26490075Sobrien_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases) 26590075Sobrien{ 26690075Sobrien struct unw_eh_callback_data data; 26790075Sobrien fde *ret; 26890075Sobrien 26990075Sobrien ret = _Unwind_Find_registered_FDE (pc, bases); 27090075Sobrien if (ret != NULL) 27190075Sobrien return ret; 27290075Sobrien 27390075Sobrien data.pc = (_Unwind_Ptr) pc; 27490075Sobrien data.tbase = NULL; 27590075Sobrien data.dbase = NULL; 27690075Sobrien data.func = NULL; 27790075Sobrien data.ret = NULL; 27890075Sobrien 27990075Sobrien if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0) 28090075Sobrien return NULL; 28190075Sobrien 28290075Sobrien if (data.ret) 28390075Sobrien { 28490075Sobrien bases->tbase = data.tbase; 28590075Sobrien bases->dbase = data.dbase; 28690075Sobrien bases->func = data.func; 28790075Sobrien } 28890075Sobrien return data.ret; 28990075Sobrien} 29090075Sobrien 29190075Sobrien#else 29290075Sobrien/* Prevent multiple include of header files. */ 29390075Sobrien#define _Unwind_Find_FDE _Unwind_Find_FDE 29490075Sobrien#include "unwind-dw2-fde.c" 29590075Sobrien#endif 296