xref: /illumos-gate/usr/src/lib/libc/amd64/unwind/unwind.c (revision 0173c38a73f34277e0c97a19fedfd25d81ba8380)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 /*
31  * UNWIND - Unwind library
32  */
33 
34 /*
35  * ===================== stack walk ====================
36  *
37  * Stack walk-back starts with the user code at the top of the stack
38  * calling a language specific support routine which calls the generic
39  * unwind code. The unwind code captures
40  * information which can be used to partially build an _Unwind_Context
41  * for the user code containing:
42  *
43  *    callee saves registers <current values>
44  *    PC
45  *    %rbp
46  *    %rsp
47  *
48  * Using that pc location the unwind info for the function is found.
49  * Then the CFA operations encoded in the unwind info are interepreted to get
50  *
51  *    callee saves registers <values on entry>
52  *    the return address
53  *    cannonical frame address
54  *
55  * completing the context for the user function (See
56  * _Unw_Rollback_Registers()) .
57  *
58  * The values computed above are equivalent to the info which would have been
59  * captured from the caller and are used to initialize the callers context
60  * (see _Unw_Propagate_Registers()) which can be completed.
61  *
62  * Using the same two-step procedure
63  * context records for each frame down the stack may be constructed
64  * in turn.  The ABI defined interface to _Unwind_Context provides
65  * access to
66  *
67  *    callee saves registers <current values>
68  *    current PC
69  *    frame pointer
70  *
71  * and allows changing
72  *
73  *    PC
74  *    values of integer argument registers
75  *
76  * (changed values take effect if context is "installed" - think
77  * setcontext(2))
78  *
79  */
80 
81 /*
82  *
83  * |                              |
84  * | local storage for start()    | <FP == 0>
85  * |                              |
86  * --------------------------------.
87  * |                              |
88  * |     ..........               |
89  * |                              | <-  CFA for bar()
90  * --------------------------------.
91  * |                              |
92  * | local storage for bar()      |
93  * |                              | <-  SP for bar(), CFA for foo()
94  * ................................
95  * |  pc for bar()                |
96  * --------------------------------
97  * |                              |
98  * | local storage for foo()      |
99  * |                              | <-  SP for foo(), CFA for ex_throw()
100  * ................................
101  * | pc for foo() - PC3           |
102  * ................................
103  * | saved RBP from foo() - BP3   | <-  FP for ex_throw() == FP2
104  * --------------------------------
105  * |                              |
106  * | local storage for ex_throw() |
107  * |                              | <- SP for ex_throw(), CFA for Unw()
108  * ................................
109  * | pc for ex_throw() - PC2      |
110  * ................................
111  * | saved RBP from ex_throw()    | <- FP for Unw() == FP1
112  * --------------------------------
113  * |                              |
114  * | local storage for Unw()      |
115  * |                              | <- SP for Unw() == SP1
116  *
117  * We know that Unw() and ex_throw save and have an FP
118  *
119  */
120 
121 #ifdef _LIBCRUN_
122 #define	_Unwind_DeleteException  _SUNW_Unwind_DeleteException
123 #define	_Unwind_ForcedUnwind  _SUNW_Unwind_ForcedUnwind
124 #define	_Unwind_GetCFA  _SUNW_Unwind_GetCFA
125 #define	_Unwind_GetGR  _SUNW_Unwind_GetGR
126 #define	_Unwind_GetIP  _SUNW_Unwind_GetIP
127 #define	_Unwind_GetLanguageSpecificData _SUNW_Unwind_GetLanguageSpecificData
128 #define	_Unwind_GetRegionStart  _SUNW_Unwind_GetRegionStart
129 #define	_Unwind_RaiseException  _SUNW_Unwind_RaiseException
130 #define	_Unwind_Resume  _SUNW_Unwind_Resume
131 #define	_Unwind_SetGR  _SUNW_Unwind_SetGR
132 #define	_Unwind_SetIP  _SUNW_Unwind_SetIP
133 #else
134 #pragma weak _Unwind_DeleteException = _SUNW_Unwind_DeleteException
135 #pragma weak _Unwind_ForcedUnwind = _SUNW_Unwind_ForcedUnwind
136 #pragma weak _Unwind_GetCFA = _SUNW_Unwind_GetCFA
137 #pragma weak _Unwind_GetGR = _SUNW_Unwind_GetGR
138 #pragma weak _Unwind_GetIP = _SUNW_Unwind_GetIP
139 #pragma weak _Unwind_GetLanguageSpecificData = \
140 			_SUNW_Unwind_GetLanguageSpecificData
141 #pragma weak _Unwind_GetRegionStart = _SUNW_Unwind_GetRegionStart
142 #pragma weak _Unwind_RaiseException = _SUNW_Unwind_RaiseException
143 #pragma weak _Unwind_Resume = _SUNW_Unwind_Resume
144 #pragma weak _Unwind_SetGR = _SUNW_Unwind_SetGR
145 #pragma weak _Unwind_SetIP = _SUNW_Unwind_SetIP
146 
147 #include "synonyms.h"
148 #endif
149 
150 #include <string.h>
151 #include "stack_unwind.h"
152 #include "reg_num.h"
153 #include "unwind_context.h"
154 
155 const _Unwind_Action _UA_SEARCH_PHASE = 1;
156 const _Unwind_Action _UA_CLEANUP_PHASE = 2;
157 const _Unwind_Action _UA_HANDLER_FRAME = 4;
158 const _Unwind_Action _UA_FORCE_UNWIND = 8;
159 
160 void _Unw_capture_regs(uint64_t *regs);
161 void _Unw_jmp(uint64_t pc, uint64_t *regs);
162 
163 static void
164 copy_ctx(struct _Unwind_Context *ctx1, struct _Unwind_Context *ctx2)
165 {
166 	if (ctx1 != ctx2) {
167 		(void) memcpy(ctx2, ctx1, sizeof (*ctx2));
168 	}
169 }
170 
171 static _Unwind_Personality_Fn
172 ctx_who(struct _Unwind_Context *ctx)
173 {
174 	return (ctx->pfn);
175 }
176 
177 /* ARGSUSED */
178 _Unwind_Reason_Code
179 _Unw_very_boring_personality(int version, int actions, uint64_t exclass,
180 	struct _Unwind_Exception *exception_object,
181 	struct _Unwind_Context *ctx)
182 {
183 	_Unwind_Reason_Code res = _URC_CONTINUE_UNWIND;
184 	uint64_t fp;
185 
186 	fp =  _Unwind_GetCFA(ctx);
187 	if (fp == 0 || _Unwind_GetIP(ctx) == 0) {
188 		return (_URC_END_OF_STACK);
189 	}
190 	return (res);
191 }
192 
193 /*
194  * The only static variables in this code - changed by debugging hook below
195  */
196 static int using_ehf = 1;
197 static uintptr_t def_per_fcn = (uintptr_t)&_Unw_very_boring_personality;
198 
199 void
200 _SUNW_Unw_set_defaults(int use, uintptr_t def_per)
201 {
202 	using_ehf = use;
203 	def_per_fcn = def_per;
204 }
205 
206 static void
207 complete_context(struct _Unwind_Context *ctx)
208 {
209 	struct eh_frame_fields sf;
210 	struct eh_frame_fields *sfp = 0;
211 
212 	ctx->pfn = (_Unwind_Personality_Fn)def_per_fcn;
213 	ctx->lsda = 0;
214 	ctx->func = 0;
215 	ctx->range = 0;
216 	ctx->fde = 0;
217 	if (using_ehf && (0 != _Unw_EhfhLookup(ctx))) {
218 		sfp = _Unw_Decode_FDE(&sf, ctx);
219 	}
220 	(void) _Unw_Rollback_Registers(sfp, ctx);
221 }
222 
223 /*
224  * input: FP1 (or FP2 if from _Unwind_Resume (from_landing_pad))
225  *
226  * FP2 = FP1[0];
227  * BP3 = FP2[0];
228  * PC3 = FP2[1];
229  * SP3 = FP2 + 16;
230  *
231  * output: PC3, SP3, and BP3
232  *
233  * remaining callee saves registers are also captured in context
234  */
235 static void
236 finish_capture(struct _Unwind_Context *ctx, int from_landing_pad)
237 {
238 	uint64_t fp1 = ctx->current_regs[FP_RBP];
239 	uint64_t fp2 = from_landing_pad ? fp1 : ((uint64_t *)fp1)[0];
240 
241 	ctx->pc = ((uint64_t *)fp2)[1];
242 	ctx->current_regs[SP_RSP] = fp2 + 16;
243 	ctx->current_regs[FP_RBP] = ((uint64_t *)fp2)[0];
244 	complete_context(ctx);
245 }
246 
247 static int
248 down_one(struct _Unwind_Context *old_ctx, struct _Unwind_Context *new_ctx)
249 {
250 	uint64_t old_cfa = old_ctx->cfa;
251 	uint64_t old_pc = old_ctx->pc;
252 	uint64_t new_cfa;
253 
254 	if (old_cfa == 0 || old_pc == 0) {
255 		new_ctx->pc = 0;
256 		new_ctx->cfa = 0;
257 		new_ctx->ra = 0;
258 		return (1);
259 	}
260 	if (old_ctx->ra == 0) {
261 		new_ctx->pc = 0;
262 		new_ctx->cfa = 0;
263 		new_ctx->ra = 0;
264 		return (0);
265 	}
266 	/* now shift ----------------------------- */
267 	_Unw_Propagate_Registers(old_ctx, new_ctx);
268 	complete_context(new_ctx);
269 	new_cfa = new_ctx->cfa;
270 	if ((new_cfa < old_cfa) || (new_cfa & 7)) {
271 		new_ctx->pc = 0;
272 		new_ctx->cfa = 0;
273 		new_ctx->ra = 0;
274 	}
275 	return (0);
276 }
277 
278 static void
279 jmp_ctx(struct _Unwind_Context *ctx)
280 {
281 	_Unw_jmp(ctx->pc, ctx->current_regs);
282 }
283 
284 /*
285  * Here starts the real work - the entry points from either a language
286  * runtime or directly from user code.
287  *
288  * The two ..._Body functions are intended as private interfaces for
289  * Sun code as well so should remain accessible.
290  */
291 _Unwind_Reason_Code
292 _Unwind_RaiseException_Body(struct _Unwind_Exception *exception_object,
293 	struct _Unwind_Context *entry_ctx, int phase)
294 {
295 	struct _Unwind_Context context;
296 	struct _Unwind_Context *ctx = &context;
297 	_Unwind_Reason_Code res;
298 
299 	if (phase & _UA_SEARCH_PHASE) {
300 		finish_capture(entry_ctx, 0);
301 		copy_ctx(entry_ctx, ctx);
302 
303 		for (;;) {
304 			res = (*ctx_who(ctx))(1, phase,
305 			    exception_object->exception_class,
306 			    exception_object, ctx);
307 			if (res != _URC_CONTINUE_UNWIND)
308 				break;
309 			if (down_one(ctx, ctx))
310 				return (_URC_FATAL_PHASE1_ERROR);
311 		}
312 		switch (res) {
313 		case _URC_HANDLER_FOUND:
314 			exception_object->private_2 = _Unwind_GetCFA(ctx);
315 			break;
316 		default:
317 			return (res);
318 			break;
319 		}
320 	} else {
321 		finish_capture(entry_ctx, 1);
322 		if (down_one(entry_ctx, entry_ctx))
323 			return (_URC_FATAL_PHASE2_ERROR);
324 	}
325 
326 	phase = _UA_CLEANUP_PHASE;
327 	copy_ctx(entry_ctx, ctx);
328 
329 	for (;;) {
330 		if (exception_object->private_2 == _Unwind_GetCFA(ctx)) {
331 			phase |= _UA_HANDLER_FRAME;
332 		}
333 		res = (*ctx_who(ctx))(1, phase,
334 		    exception_object->exception_class,
335 		    exception_object, ctx);
336 		if ((phase & _UA_HANDLER_FRAME) && res != _URC_INSTALL_CONTEXT)
337 			return (_URC_FATAL_PHASE2_ERROR);
338 		if (res != _URC_CONTINUE_UNWIND)
339 			break;
340 		if (down_one(ctx, ctx))
341 			return (_URC_FATAL_PHASE2_ERROR);
342 	}
343 	switch (res) {
344 	case _URC_INSTALL_CONTEXT:
345 		exception_object->private_1 = 0;
346 		jmp_ctx(ctx); /* does not return */
347 		break;
348 	default:
349 		break;
350 	}
351 	return (res);
352 }
353 
354 _Unwind_Reason_Code
355 _Unwind_RaiseException(struct _Unwind_Exception *exception_object)
356 {
357 	struct _Unwind_Context entry_context;
358 	struct _Unwind_Context *entry_ctx = &entry_context;
359 
360 	_Unw_capture_regs(entry_ctx->current_regs);
361 
362 	return (_Unwind_RaiseException_Body(exception_object, entry_ctx,
363 	    _UA_SEARCH_PHASE));
364 }
365 
366 _Unwind_Reason_Code
367 _Unwind_ForcedUnwind_Body(struct _Unwind_Exception *exception_object,
368 	_Unwind_Stop_Fn stop, void *stop_parameter,
369 	struct _Unwind_Context *ctx, int resume)
370 {
371 	_Unwind_Reason_Code res;
372 	int phase = _UA_CLEANUP_PHASE | _UA_FORCE_UNWIND;
373 
374 	int again;
375 	int doper;
376 
377 	finish_capture(ctx, resume);
378 	if (resume && down_one(ctx, ctx))
379 	    return (_URC_FATAL_PHASE2_ERROR);
380 
381 	do {
382 		again = 0;
383 		doper = 0;
384 		res = (*stop)(1, phase,
385 		    exception_object->exception_class,
386 		    exception_object, ctx, stop_parameter);
387 		switch (res) {
388 		case _URC_CONTINUE_UNWIND:
389 			/* keep going - don't call personality */
390 			again = 1;
391 			break;
392 		case _URC_NO_REASON:
393 			/* keep going - do call personality */
394 			again = 1;
395 			doper = 1;
396 			break;
397 		case _URC_NORMAL_STOP:  /* done */
398 			break;
399 		case _URC_INSTALL_CONTEXT:  /* resume execution */
400 			break;
401 		default:		/* failure */
402 			break;
403 		}
404 		if (doper) {
405 			res = (*ctx_who(ctx))(1, phase,
406 			    exception_object->exception_class,
407 			    exception_object, ctx);
408 		}
409 		switch (res) {
410 		case _URC_INSTALL_CONTEXT:
411 			exception_object->private_1 = (uint64_t)stop;
412 			exception_object->private_2 = (uint64_t)stop_parameter;
413 			jmp_ctx(ctx); /* does not return */
414 			break;
415 		case _URC_CONTINUE_UNWIND:
416 		case _URC_NO_REASON:
417 			break;
418 		case _URC_END_OF_STACK:
419 			ctx->cfa = ctx->ra = ctx->pc = 0;
420 			res = (*stop)(1, phase,
421 					exception_object->exception_class,
422 					exception_object, ctx, stop_parameter);
423 			return (_URC_END_OF_STACK);
424 			break;
425 		default:
426 			again = 0;
427 			break;
428 		}
429 		if (again) {
430 			if (down_one(ctx, ctx)) {
431 				return (_URC_FATAL_PHASE2_ERROR);
432 			}
433 		}
434 	} while (again);
435 
436 	return (res);
437 }
438 
439 _Unwind_Reason_Code
440 _Unwind_ForcedUnwind(struct _Unwind_Exception *exception_object,
441 	_Unwind_Stop_Fn stop, void *stop_parameter)
442 {
443 	struct _Unwind_Context context;
444 	struct _Unwind_Context *ctx = &context;
445 
446 	_Unw_capture_regs(ctx->current_regs);
447 
448 	return (_Unwind_ForcedUnwind_Body(exception_object, stop,
449 	    stop_parameter, ctx, 0));
450 }
451 
452 void
453 _Unwind_Resume(struct _Unwind_Exception *exception_object)
454 {
455 
456 	struct _Unwind_Context context;
457 	struct _Unwind_Context *ctx = &context;
458 
459 	_Unw_capture_regs(ctx->current_regs);
460 
461 	if (exception_object->private_1)
462 		(void) _Unwind_ForcedUnwind_Body(exception_object,
463 		    (_Unwind_Stop_Fn)exception_object->private_1,
464 		    (void *)exception_object->private_2,
465 		    ctx, 1);
466 	else
467 		(void) _Unwind_RaiseException_Body(exception_object, ctx,
468 		    _UA_CLEANUP_PHASE);
469 }
470 
471 /* Calls destructor function for exception object */
472 void
473 _Unwind_DeleteException(struct _Unwind_Exception *exception_object)
474 {
475 	if (exception_object->exception_cleanup != 0)
476 		(*(exception_object->exception_cleanup))(_URC_NO_REASON,
477 		    exception_object);
478 }
479 
480 
481 /*
482  * stack frame context accessors defined in ABI
483  * (despite all the dire text in the ABI these are reliable Get/Set routines)
484  * Note: RA is handled as GR value
485  */
486 uint64_t
487 _Unwind_GetGR(struct _Unwind_Context *context, int index)
488 {
489 	uint64_t res = 0;
490 	if (index <= EIR_R15) {
491 		res = context->current_regs[index];
492 	} else if (index == RET_ADD) {
493 		res = context->ra;
494 	}
495 	return (res);
496 }
497 
498 
499 void
500 _Unwind_SetGR(struct _Unwind_Context *context, int index,
501 uint64_t new_value)
502 {
503 	if (index <= EIR_R15) {
504 		context->current_regs[index] = new_value;
505 	} else if (index == RET_ADD) {
506 		context->ra = new_value;
507 	}
508 }
509 
510 
511 uint64_t
512 _Unwind_GetIP(struct _Unwind_Context *context)
513 {
514 	return (context->pc);
515 }
516 
517 void
518 _Unwind_SetIP(struct _Unwind_Context *context, uint64_t new_value)
519 {
520 	context->pc = new_value;
521 }
522 
523 
524 void *
525 _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context)
526 {
527 	return (context->lsda);
528 }
529 
530 
531 uint64_t
532 _Unwind_GetRegionStart(struct _Unwind_Context *context)
533 {
534 	return (context->func);
535 }
536 
537 uint64_t
538 _Unwind_GetCFA(struct _Unwind_Context *context)
539 {
540 	return (context->cfa);
541 }
542