uwx_utable.c revision 160157
1/*
2Copyright (c) 2003-2006 Hewlett-Packard Development Company, L.P.
3Permission is hereby granted, free of charge, to any person
4obtaining a copy of this software and associated documentation
5files (the "Software"), to deal in the Software without
6restriction, including without limitation the rights to use,
7copy, modify, merge, publish, distribute, sublicense, and/or sell
8copies of the Software, and to permit persons to whom the
9Software is furnished to do so, subject to the following
10conditions:
11
12The above copyright notice and this permission notice shall be
13included in all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22OTHER DEALINGS IN THE SOFTWARE.
23*/
24
25#include "uwx_env.h"
26#include "uwx_utable.h"
27#include "uwx_swap.h"
28#include "uwx_trace.h"
29
30/*
31 *  uwx_utable.c
32 *
33 *  This file contains the routines for searching an unwind table.
34 *  The main entry point, uwx_search_utable(), gets the
35 *  necessary information from the lookup ip callback's result
36 *  vector, determines whether the table is 32-bit or 64-bit,
37 *  then invokes the binary search routine for that format.
38 */
39
40
41/* Forward declarations */
42
43int uwx_search_utable32(
44    struct uwx_env *env,
45    uint32_t ip,
46    uint32_t text_base,
47    uint32_t unwind_start,
48    uint32_t unwind_end,
49    struct uwx_utable_entry *uentry);
50
51int uwx_search_utable64(
52    struct uwx_env *env,
53    uint64_t ip,
54    uint64_t text_base,
55    uint64_t unwind_start,
56    uint64_t unwind_end,
57    struct uwx_utable_entry *uentry);
58
59
60/* uwx_search_utable: Searches an unwind table for IP in current context */
61
62int uwx_search_utable(
63    struct uwx_env *env,
64    uint64_t ip,
65    uint64_t *uvec,
66    struct uwx_utable_entry *uentry)
67{
68    uint64_t text_base;
69    uint64_t unwind_flags;
70    uint64_t unwind_start;
71    uint64_t unwind_end;
72    int keys;
73    int status;
74
75    /* Get unwind table information from the result vector. */
76    /* Make sure all three required values are given. */
77
78    keys = 0;
79    unwind_flags = 0;
80    while (*uvec != 0) {
81	switch ((int)*uvec++) {
82	    case UWX_KEY_TBASE:
83		keys |= 1;
84		env->text_base = text_base = *uvec++;
85		break;
86	    case UWX_KEY_UFLAGS:
87		unwind_flags = *uvec++;
88		break;
89	    case UWX_KEY_USTART:
90		keys |= 2;
91		unwind_start = *uvec++;
92		break;
93	    case UWX_KEY_UEND:
94		keys |= 4;
95		unwind_end = *uvec++;
96		break;
97	    case UWX_KEY_GP:
98		uwx_set_reg(env, UWX_REG_GP, *uvec++);
99		break;
100	    default:
101		return UWX_ERR_BADKEY;
102	}
103    }
104    if (keys != 7)
105	return UWX_ERR_BADKEY;
106
107    /* Copy the unwind flags into the unwind entry. */
108    /* (uwx_decode_uinfo needs to know whether it's 32-bit or 64-bit.) */
109
110    uentry->unwind_flags = unwind_flags;
111
112    /* Call the appropriate binary search routine. */
113
114    if (unwind_flags & UNWIND_TBL_32BIT)
115	status = uwx_search_utable32(env,
116			(uint32_t) ip,
117			(uint32_t) text_base,
118			(uint32_t) unwind_start,
119			(uint32_t) unwind_end,
120			uentry);
121    else
122	status = uwx_search_utable64(env,
123			ip, text_base, unwind_start, unwind_end, uentry);
124
125    return status;
126}
127
128
129/* uwx_search_utable32: Binary search of 32-bit unwind table */
130
131#define COPYIN_UINFO_4(dest, src) \
132    (env->remote? \
133	(*env->copyin)(UWX_COPYIN_UINFO, (dest), (src), \
134						WORDSZ, env->cb_token) : \
135	(*(uint32_t *)(dest) = *(uint32_t *)(src), WORDSZ) )
136
137#define SWIZZLE(x) (((uint64_t)((x) & 0xc0000000) << 31) | (x))
138
139int uwx_search_utable32(
140    struct uwx_env *env,
141    uint32_t ip,
142    uint32_t text_base,
143    uint32_t unwind_start,
144    uint32_t unwind_end,
145    struct uwx_utable_entry *uentry)
146{
147    int status;
148    int lb;
149    int ub;
150    int mid;
151    int len;
152    uint32_t code_start;
153    uint32_t code_end;
154    uint32_t unwind_info;
155
156    /* Since the unwind table uses segment-relative offsets, convert */
157    /* the IP in the current context to a segment-relative offset. */
158
159    ip -= text_base;
160
161    TRACE_T_SEARCH32(ip)
162
163    /* Standard binary search. */
164    /* Might modify this to do interpolation in the future. */
165
166    lb = 0;
167    ub = (unwind_end - unwind_start) / (3 * WORDSZ);
168    while (ub > lb) {
169	mid = (lb + ub) / 2;
170	len = COPYIN_UINFO_4((char *)&code_start, unwind_start+mid*3*WORDSZ);
171	len += COPYIN_UINFO_4((char *)&code_end,
172			    unwind_start+mid*3*WORDSZ+WORDSZ);
173	if (len != 2 * WORDSZ)
174	    return UWX_ERR_COPYIN_UTBL;
175	if (env->byte_swap) {
176	    uwx_swap4(&code_start);
177	    uwx_swap4(&code_end);
178	}
179	TRACE_T_BINSEARCH32(lb, ub, mid, code_start, code_end)
180	if (ip >= code_end)
181	    lb = mid + 1;
182	else if (ip < code_start)
183	    ub = mid;
184	else
185	    break;
186    }
187    if (ub <= lb)
188	return UWX_ERR_NOUENTRY;
189    len = COPYIN_UINFO_4((char *)&unwind_info,
190			    unwind_start+mid*3*WORDSZ+2*WORDSZ);
191    if (len != WORDSZ)
192	return UWX_ERR_COPYIN_UTBL;
193    if (env->byte_swap)
194	uwx_swap4(&unwind_info);
195    uentry->ptr_size = WORDSZ;
196    uentry->code_start = SWIZZLE(text_base + code_start);
197    uentry->code_end = SWIZZLE(text_base + code_end);
198    uentry->unwind_info = SWIZZLE(text_base + unwind_info);
199    return UWX_OK;
200}
201
202
203/* uwx_search_utable64: Binary search of 64-bit unwind table */
204
205#define COPYIN_UINFO_8(dest, src) \
206    (env->remote? \
207      (*env->copyin)(UWX_COPYIN_UINFO, (dest), (src), \
208						DWORDSZ, env->cb_token) : \
209      (*(uint64_t *)(intptr_t)(dest) = *(uint64_t *)(intptr_t)(src), DWORDSZ) )
210
211int uwx_search_utable64(
212    struct uwx_env *env,
213    uint64_t ip,
214    uint64_t text_base,
215    uint64_t unwind_start,
216    uint64_t unwind_end,
217    struct uwx_utable_entry *uentry)
218{
219    int status;
220    int lb;
221    int ub;
222    int mid;
223    int len;
224    uint64_t code_start;
225    uint64_t code_end;
226    uint64_t unwind_info;
227
228    /* Since the unwind table uses segment-relative offsets, convert */
229    /* the IP in the current context to a segment-relative offset. */
230
231    ip -= text_base;
232
233    /* Standard binary search. */
234    /* Might modify this to do interpolation in the future. */
235
236    lb = 0;
237    ub = (unwind_end - unwind_start) / (3 * DWORDSZ);
238    while (ub > lb) {
239	mid = (lb + ub) / 2;
240	len = COPYIN_UINFO_8((char *)&code_start, unwind_start+mid*3*DWORDSZ);
241	len += COPYIN_UINFO_8((char *)&code_end,
242				unwind_start+mid*3*DWORDSZ+DWORDSZ);
243	if (len != 2 * DWORDSZ)
244	    return UWX_ERR_COPYIN_UTBL;
245	if (env->byte_swap) {
246	    uwx_swap8(&code_start);
247	    uwx_swap8(&code_end);
248	}
249	if (ip >= code_end)
250	    lb = mid + 1;
251	else if (ip < code_start)
252	    ub = mid;
253	else
254	    break;
255    }
256    if (ub <= lb)
257	return UWX_ERR_NOUENTRY;
258    len = COPYIN_UINFO_8((char *)&unwind_info,
259			unwind_start+mid*3*DWORDSZ+2*DWORDSZ);
260    if (len != DWORDSZ)
261	return UWX_ERR_COPYIN_UTBL;
262    if (env->byte_swap)
263	uwx_swap8(&unwind_info);
264    uentry->ptr_size = DWORDSZ;
265    uentry->code_start = text_base + code_start;
266    uentry->code_end = text_base + code_end;
267    uentry->unwind_info = text_base + unwind_info;
268    return UWX_OK;
269}
270