1132718Skan/* Copyright (C) 2000, 2001, 2003 Free Software Foundation, Inc. 290075Sobrien Contributed by Richard Henderson <rth@cygnus.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 glibc ld.so routines 2990075Sobrien to avoid register/deregister calls at DSO load/unload. */ 3090075Sobrien 3190075Sobrien#ifndef _GNU_SOURCE 32132718Skan#define _GNU_SOURCE 1 3390075Sobrien#endif 3490075Sobrien#include "config.h" 3590075Sobrien#include <stddef.h> 3690075Sobrien#include <stdlib.h> 3790075Sobrien#include <link.h> 3890075Sobrien#include "unwind-ia64.h" 3990075Sobrien 4090075Sobrien#if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2) \ 4190075Sobrien || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && !defined(DT_CONFIG)) 4290075Sobrien# error You need GLIBC 2.2.4 or later on IA-64 Linux 4390075Sobrien#endif 4490075Sobrien 4590075Sobrienstruct unw_ia64_callback_data 4690075Sobrien{ 4790075Sobrien Elf64_Addr pc; 4890075Sobrien unsigned long *segment_base; 4990075Sobrien unsigned long *gp; 5090075Sobrien struct unw_table_entry *ret; 5190075Sobrien}; 5290075Sobrien 5390075Sobrienstatic int 5490075Sobrien_Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr) 5590075Sobrien{ 5690075Sobrien struct unw_ia64_callback_data *data = (struct unw_ia64_callback_data *) ptr; 5790075Sobrien const Elf64_Phdr *phdr, *p_unwind, *p_dynamic; 5890075Sobrien long n, match; 5990075Sobrien Elf64_Addr load_base, seg_base; 6090075Sobrien struct unw_table_entry *f_base, *f; 6190075Sobrien size_t lo, hi; 6290075Sobrien 6390075Sobrien /* Make sure struct dl_phdr_info is at least as big as we need. */ 6490075Sobrien if (size < offsetof (struct dl_phdr_info, dlpi_phnum) 6590075Sobrien + sizeof (info->dlpi_phnum)) 6690075Sobrien return -1; 6790075Sobrien 6890075Sobrien match = 0; 6990075Sobrien phdr = info->dlpi_phdr; 7090075Sobrien load_base = info->dlpi_addr; 7190075Sobrien p_unwind = NULL; 7290075Sobrien p_dynamic = NULL; 7390075Sobrien seg_base = ~(Elf64_Addr) 0; 7490075Sobrien 7590075Sobrien /* See if PC falls into one of the loaded segments. Find the unwind 7690075Sobrien segment at the same time. */ 7790075Sobrien for (n = info->dlpi_phnum; --n >= 0; phdr++) 7890075Sobrien { 7990075Sobrien if (phdr->p_type == PT_LOAD) 8090075Sobrien { 8190075Sobrien Elf64_Addr vaddr = phdr->p_vaddr + load_base; 8290075Sobrien if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz) 8390075Sobrien match = 1; 8490075Sobrien if (vaddr < seg_base) 8590075Sobrien seg_base = vaddr; 8690075Sobrien } 8790075Sobrien else if (phdr->p_type == PT_IA_64_UNWIND) 8890075Sobrien p_unwind = phdr; 8990075Sobrien else if (phdr->p_type == PT_DYNAMIC) 9090075Sobrien p_dynamic = phdr; 9190075Sobrien } 9290075Sobrien if (!match || !p_unwind) 9390075Sobrien return 0; 9490075Sobrien 9590075Sobrien /* Search for the FDE within the unwind segment. */ 9690075Sobrien 9790075Sobrien f_base = (struct unw_table_entry *) (p_unwind->p_vaddr + load_base); 9890075Sobrien lo = 0; 9990075Sobrien hi = p_unwind->p_memsz / sizeof (struct unw_table_entry); 10090075Sobrien 10190075Sobrien while (lo < hi) 10290075Sobrien { 10390075Sobrien size_t mid = (lo + hi) / 2; 10490075Sobrien 10590075Sobrien f = f_base + mid; 10690075Sobrien if (data->pc < f->start_offset + seg_base) 10790075Sobrien hi = mid; 10890075Sobrien else if (data->pc >= f->end_offset + seg_base) 10990075Sobrien lo = mid + 1; 11090075Sobrien else 11190075Sobrien goto found; 11290075Sobrien } 11390075Sobrien /* No need to search for further libraries when we know pc is contained 11490075Sobrien in this library. */ 11590075Sobrien return 1; 11690075Sobrien 11790075Sobrien found: 11890075Sobrien *data->segment_base = seg_base; 11990075Sobrien *data->gp = 0; 12090075Sobrien data->ret = f; 12190075Sobrien 12290075Sobrien if (p_dynamic) 12390075Sobrien { 124132718Skan /* For dynamically linked executables and shared libraries, 12590075Sobrien DT_PLTGOT is the gp value for that object. */ 12690075Sobrien Elf64_Dyn *dyn = (Elf64_Dyn *)(p_dynamic->p_vaddr + load_base); 12790075Sobrien for (; dyn->d_tag != DT_NULL ; dyn++) 12890075Sobrien if (dyn->d_tag == DT_PLTGOT) 12990075Sobrien { 13090075Sobrien /* On IA-64, _DYNAMIC is writable and GLIBC has relocated it. */ 13190075Sobrien *data->gp = dyn->d_un.d_ptr; 13290075Sobrien break; 13390075Sobrien } 13490075Sobrien } 13590075Sobrien else 13690075Sobrien { 13790075Sobrien /* Otherwise this is a static executable with no _DYNAMIC. 13890075Sobrien The gp is constant program-wide. */ 13990075Sobrien register unsigned long gp __asm__("gp"); 14090075Sobrien *data->gp = gp; 14190075Sobrien } 14290075Sobrien 14390075Sobrien return 1; 14490075Sobrien} 14590075Sobrien 14690075Sobrien/* Return a pointer to the unwind table entry for the function 14790075Sobrien containing PC. */ 14890075Sobrien 14990075Sobrienstruct unw_table_entry * 15090075Sobrien_Unwind_FindTableEntry (void *pc, unsigned long *segment_base, 15190075Sobrien unsigned long *gp) 15290075Sobrien{ 15390075Sobrien struct unw_ia64_callback_data data; 15490075Sobrien 15590075Sobrien data.pc = (Elf64_Addr) pc; 15690075Sobrien data.segment_base = segment_base; 15790075Sobrien data.gp = gp; 15890075Sobrien data.ret = NULL; 15990075Sobrien 16090075Sobrien if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0) 16190075Sobrien return NULL; 16290075Sobrien 16390075Sobrien return data.ret; 16490075Sobrien} 165