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