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