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