]>
Commit | Line | Data |
---|---|---|
a734f2a3 | 1 | /* Copyright (C) 2000, 2001 Free Software Foundation, Inc. |
fee0225a RH |
2 | Contributed by Richard Henderson <rth@cygnus.com>. |
3 | ||
4 | This file is part of GNU CC. | |
5 | ||
6 | GNU CC is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 2, or (at your option) | |
9 | any later version. | |
10 | ||
11 | GNU CC is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with GNU CC; see the file COPYING. If not, write to | |
18 | the Free Software Foundation, 59 Temple Place - Suite 330, | |
19 | Boston, MA 02111-1307, USA. */ | |
20 | ||
21 | /* As a special exception, if you link this library with other files, | |
22 | some of which are compiled with GCC, to produce an executable, | |
23 | this library does not by itself cause the resulting executable | |
24 | to be covered by the GNU General Public License. | |
25 | This exception does not however invalidate any other reasons why | |
26 | the executable file might be covered by the GNU General Public License. */ | |
27 | ||
28 | /* Locate the FDE entry for a given address, using glibc ld.so routines | |
29 | to avoid register/deregister calls at DSO load/unload. */ | |
30 | ||
a734f2a3 JJ |
31 | #ifndef _GNU_SOURCE |
32 | #define _GNU_SOURCE | |
33 | #endif | |
34 | #include "config.h" | |
35 | #include <stddef.h> | |
fee0225a RH |
36 | #include <stdlib.h> |
37 | #include <link.h> | |
2a1ee410 | 38 | #include "unwind-ia64.h" |
fee0225a | 39 | |
a734f2a3 JJ |
40 | #if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2) \ |
41 | || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && !defined(DT_CONFIG)) | |
42 | # error You need GLIBC 2.2.4 or later on IA-64 Linux | |
43 | #endif | |
fee0225a | 44 | |
a734f2a3 JJ |
45 | struct unw_ia64_callback_data |
46 | { | |
47 | Elf64_Addr pc; | |
48 | unsigned long *segment_base; | |
49 | unsigned long *gp; | |
50 | struct unw_table_entry *ret; | |
51 | }; | |
fee0225a | 52 | |
a734f2a3 JJ |
53 | static int |
54 | _Unwind_IteratePhdrCallback (struct dl_phdr_info *info, size_t size, void *ptr) | |
fee0225a | 55 | { |
a734f2a3 JJ |
56 | struct unw_ia64_callback_data *data = (struct unw_ia64_callback_data *) ptr; |
57 | const Elf64_Phdr *phdr, *p_unwind, *p_dynamic; | |
fee0225a RH |
58 | long n, match; |
59 | Elf64_Addr load_base, seg_base; | |
2a1ee410 | 60 | struct unw_table_entry *f_base, *f; |
cd5c4048 | 61 | size_t lo, hi; |
fee0225a | 62 | |
a734f2a3 JJ |
63 | /* Make sure struct dl_phdr_info is at least as big as we need. */ |
64 | if (size < offsetof (struct dl_phdr_info, dlpi_phnum) | |
65 | + sizeof (info->dlpi_phnum)) | |
66 | return -1; | |
fee0225a RH |
67 | |
68 | match = 0; | |
a734f2a3 JJ |
69 | phdr = info->dlpi_phdr; |
70 | load_base = info->dlpi_addr; | |
fee0225a | 71 | p_unwind = NULL; |
2a1ee410 | 72 | p_dynamic = NULL; |
a734f2a3 | 73 | seg_base = ~(Elf64_Addr) 0; |
fee0225a RH |
74 | |
75 | /* See if PC falls into one of the loaded segments. Find the unwind | |
76 | segment at the same time. */ | |
a734f2a3 | 77 | for (n = info->dlpi_phnum; --n >= 0; phdr++) |
fee0225a RH |
78 | { |
79 | if (phdr->p_type == PT_LOAD) | |
80 | { | |
81 | Elf64_Addr vaddr = phdr->p_vaddr + load_base; | |
a734f2a3 | 82 | if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz) |
fee0225a | 83 | match = 1; |
a734f2a3 JJ |
84 | if (vaddr < seg_base) |
85 | seg_base = vaddr; | |
fee0225a RH |
86 | } |
87 | else if (phdr->p_type == PT_IA_64_UNWIND) | |
88 | p_unwind = phdr; | |
2a1ee410 RH |
89 | else if (phdr->p_type == PT_DYNAMIC) |
90 | p_dynamic = phdr; | |
fee0225a RH |
91 | } |
92 | if (!match || !p_unwind) | |
a734f2a3 | 93 | return 0; |
fee0225a RH |
94 | |
95 | /* Search for the FDE within the unwind segment. */ | |
fee0225a | 96 | |
2a1ee410 | 97 | f_base = (struct unw_table_entry *) (p_unwind->p_vaddr + load_base); |
cd5c4048 | 98 | lo = 0; |
2a1ee410 | 99 | hi = p_unwind->p_memsz / sizeof (struct unw_table_entry); |
cd5c4048 RH |
100 | |
101 | while (lo < hi) | |
102 | { | |
103 | size_t mid = (lo + hi) / 2; | |
cd5c4048 | 104 | |
2a1ee410 | 105 | f = f_base + mid; |
a734f2a3 | 106 | if (data->pc < f->start_offset + seg_base) |
cd5c4048 | 107 | hi = mid; |
a734f2a3 | 108 | else if (data->pc >= f->end_offset + seg_base) |
cd5c4048 RH |
109 | lo = mid + 1; |
110 | else | |
2a1ee410 | 111 | goto found; |
cd5c4048 | 112 | } |
275b60d6 JJ |
113 | /* No need to search for further libraries when we know pc is contained |
114 | in this library. */ | |
115 | return 1; | |
2a1ee410 RH |
116 | |
117 | found: | |
a734f2a3 JJ |
118 | *data->segment_base = seg_base; |
119 | *data->gp = 0; | |
120 | data->ret = f; | |
2a1ee410 RH |
121 | |
122 | if (p_dynamic) | |
123 | { | |
124 | /* For dynamicly linked executables and shared libraries, | |
125 | DT_PLTGOT is the gp value for that object. */ | |
126 | Elf64_Dyn *dyn = (Elf64_Dyn *)(p_dynamic->p_vaddr + load_base); | |
127 | for (; dyn->d_tag != DT_NULL ; dyn++) | |
128 | if (dyn->d_tag == DT_PLTGOT) | |
129 | { | |
a734f2a3 JJ |
130 | /* On IA-64, _DYNAMIC is writable and GLIBC has relocated it. */ |
131 | *data->gp = dyn->d_un.d_ptr; | |
2a1ee410 RH |
132 | break; |
133 | } | |
134 | } | |
135 | else | |
136 | { | |
137 | /* Otherwise this is a static executable with no _DYNAMIC. | |
138 | The gp is constant program-wide. */ | |
139 | register unsigned long gp __asm__("gp"); | |
a734f2a3 | 140 | *data->gp = gp; |
2a1ee410 RH |
141 | } |
142 | ||
a734f2a3 | 143 | return 1; |
fee0225a RH |
144 | } |
145 | ||
2a1ee410 RH |
146 | /* Return a pointer to the unwind table entry for the function |
147 | containing PC. */ | |
148 | ||
149 | struct unw_table_entry * | |
150 | _Unwind_FindTableEntry (void *pc, unsigned long *segment_base, | |
151 | unsigned long *gp) | |
fee0225a | 152 | { |
a734f2a3 | 153 | struct unw_ia64_callback_data data; |
fee0225a | 154 | |
a734f2a3 JJ |
155 | data.pc = (Elf64_Addr) pc; |
156 | data.segment_base = segment_base; | |
157 | data.gp = gp; | |
158 | data.ret = NULL; | |
fee0225a | 159 | |
a734f2a3 JJ |
160 | if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0) |
161 | return NULL; | |
fee0225a | 162 | |
a734f2a3 | 163 | return data.ret; |
fee0225a | 164 | } |