2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A
2N/A/*
2N/A * UNWIND - Unwind library
2N/A */
2N/A
2N/A/*
2N/A * ===================== stack walk ====================
2N/A *
2N/A * Stack walk-back starts with the user code at the top of the stack
2N/A * calling a language specific support routine which calls the generic
2N/A * unwind code. The unwind code captures
2N/A * information which can be used to partially build an _Unwind_Context
2N/A * for the user code containing:
2N/A *
2N/A * callee saves registers <current values>
2N/A * PC
2N/A * %rbp
2N/A * %rsp
2N/A *
2N/A * Using that pc location the unwind info for the function is found.
2N/A * Then the CFA operations encoded in the unwind info are interepreted to get
2N/A *
2N/A * callee saves registers <values on entry>
2N/A * the return address
2N/A * cannonical frame address
2N/A *
2N/A * completing the context for the user function (See
2N/A * _Unw_Rollback_Registers()) .
2N/A *
2N/A * The values computed above are equivalent to the info which would have been
2N/A * captured from the caller and are used to initialize the callers context
2N/A * (see _Unw_Propagate_Registers()) which can be completed.
2N/A *
2N/A * Using the same two-step procedure
2N/A * context records for each frame down the stack may be constructed
2N/A * in turn. The ABI defined interface to _Unwind_Context provides
2N/A * access to
2N/A *
2N/A * callee saves registers <current values>
2N/A * current PC
2N/A * frame pointer
2N/A *
2N/A * and allows changing
2N/A *
2N/A * PC
2N/A * values of integer argument registers
2N/A *
2N/A * (changed values take effect if context is "installed" - think
2N/A * setcontext(2))
2N/A *
2N/A */
2N/A
2N/A/*
2N/A *
2N/A * | |
2N/A * | local storage for start() | <FP == 0>
2N/A * | |
2N/A * --------------------------------.
2N/A * | |
2N/A * | .......... |
2N/A * | | <- CFA for bar()
2N/A * --------------------------------.
2N/A * | |
2N/A * | local storage for bar() |
2N/A * | | <- SP for bar(), CFA for foo()
2N/A * ................................
2N/A * | pc for bar() |
2N/A * --------------------------------
2N/A * | |
2N/A * | local storage for foo() |
2N/A * | | <- SP for foo(), CFA for ex_throw()
2N/A * ................................
2N/A * | pc for foo() - PC3 |
2N/A * ................................
2N/A * | saved RBP from foo() - BP3 | <- FP for ex_throw() == FP2
2N/A * --------------------------------
2N/A * | |
2N/A * | local storage for ex_throw() |
2N/A * | | <- SP for ex_throw(), CFA for Unw()
2N/A * ................................
2N/A * | pc for ex_throw() - PC2 |
2N/A * ................................
2N/A * | saved RBP from ex_throw() | <- FP for Unw() == FP1
2N/A * --------------------------------
2N/A * | |
2N/A * | local storage for Unw() |
2N/A * | | <- SP for Unw() == SP1
2N/A *
2N/A * We know that Unw() and ex_throw save and have an FP
2N/A *
2N/A */
2N/A
2N/A#ifdef _LIBCRUN_
2N/A#define _Unwind_DeleteException _SUNW_Unwind_DeleteException
2N/A#define _Unwind_ForcedUnwind _SUNW_Unwind_ForcedUnwind
2N/A#define _Unwind_GetCFA _SUNW_Unwind_GetCFA
2N/A#define _Unwind_GetGR _SUNW_Unwind_GetGR
2N/A#define _Unwind_GetIP _SUNW_Unwind_GetIP
2N/A#define _Unwind_GetLanguageSpecificData _SUNW_Unwind_GetLanguageSpecificData
2N/A#define _Unwind_GetRegionStart _SUNW_Unwind_GetRegionStart
2N/A#define _Unwind_RaiseException _SUNW_Unwind_RaiseException
2N/A#define _Unwind_Resume _SUNW_Unwind_Resume
2N/A#define _Unwind_SetGR _SUNW_Unwind_SetGR
2N/A#define _Unwind_SetIP _SUNW_Unwind_SetIP
2N/A#else
2N/A#pragma weak _SUNW_Unwind_DeleteException = _Unwind_DeleteException
2N/A#pragma weak _SUNW_Unwind_ForcedUnwind = _Unwind_ForcedUnwind
2N/A#pragma weak _SUNW_Unwind_GetCFA = _Unwind_GetCFA
2N/A#pragma weak _SUNW_Unwind_GetGR = _Unwind_GetGR
2N/A#pragma weak _SUNW_Unwind_GetIP = _Unwind_GetIP
2N/A#pragma weak _SUNW_Unwind_GetLanguageSpecificData = \
2N/A _Unwind_GetLanguageSpecificData
2N/A#pragma weak _SUNW_Unwind_GetRegionStart = _Unwind_GetRegionStart
2N/A#pragma weak _SUNW_Unwind_RaiseException = _Unwind_RaiseException
2N/A#pragma weak _SUNW_Unwind_Resume = _Unwind_Resume
2N/A#pragma weak _SUNW_Unwind_SetGR = _Unwind_SetGR
2N/A#pragma weak _SUNW_Unwind_SetIP = _Unwind_SetIP
2N/A#endif
2N/A
2N/A#include "lint.h"
2N/A#include <string.h>
2N/A#include "stack_unwind.h"
2N/A#include "reg_num.h"
2N/A#include "unwind_context.h"
2N/A
2N/Aconst _Unwind_Action _UA_SEARCH_PHASE = 1;
2N/Aconst _Unwind_Action _UA_CLEANUP_PHASE = 2;
2N/Aconst _Unwind_Action _UA_HANDLER_FRAME = 4;
2N/Aconst _Unwind_Action _UA_FORCE_UNWIND = 8;
2N/A
2N/Avoid _Unw_capture_regs(uint64_t *regs);
2N/Avoid _Unw_jmp(uint64_t pc, uint64_t *regs);
2N/A
2N/Astatic void
2N/Acopy_ctx(struct _Unwind_Context *ctx1, struct _Unwind_Context *ctx2)
2N/A{
2N/A if (ctx1 != ctx2) {
2N/A (void) memcpy(ctx2, ctx1, sizeof (*ctx2));
2N/A }
2N/A}
2N/A
2N/Astatic _Unwind_Personality_Fn
2N/Actx_who(struct _Unwind_Context *ctx)
2N/A{
2N/A return (ctx->pfn);
2N/A}
2N/A
2N/A/* ARGSUSED */
2N/A_Unwind_Reason_Code
2N/A_Unw_very_boring_personality(int version, int actions, uint64_t exclass,
2N/A struct _Unwind_Exception *exception_object,
2N/A struct _Unwind_Context *ctx)
2N/A{
2N/A _Unwind_Reason_Code res = _URC_CONTINUE_UNWIND;
2N/A uint64_t fp;
2N/A
2N/A fp = _Unwind_GetCFA(ctx);
2N/A if (fp == 0 || _Unwind_GetIP(ctx) == 0) {
2N/A return (_URC_END_OF_STACK);
2N/A }
2N/A return (res);
2N/A}
2N/A
2N/A/*
2N/A * The only static variables in this code - changed by debugging hook below
2N/A */
2N/Astatic int using_ehf = 1;
2N/Astatic uintptr_t def_per_fcn = (uintptr_t)&_Unw_very_boring_personality;
2N/A
2N/Avoid
2N/A_SUNW_Unw_set_defaults(int use, uintptr_t def_per)
2N/A{
2N/A using_ehf = use;
2N/A def_per_fcn = def_per;
2N/A}
2N/A
2N/Astatic void
2N/Acomplete_context(struct _Unwind_Context *ctx)
2N/A{
2N/A struct eh_frame_fields sf;
2N/A struct eh_frame_fields *sfp = 0;
2N/A
2N/A ctx->pfn = (_Unwind_Personality_Fn)def_per_fcn;
2N/A ctx->lsda = 0;
2N/A ctx->func = 0;
2N/A ctx->range = 0;
2N/A ctx->fde = 0;
2N/A if (using_ehf && (0 != _Unw_EhfhLookup(ctx))) {
2N/A sfp = _Unw_Decode_FDE(&sf, ctx);
2N/A }
2N/A (void) _Unw_Rollback_Registers(sfp, ctx);
2N/A}
2N/A
2N/A/*
2N/A * input: FP1 (or FP2 if from _Unwind_Resume (from_landing_pad))
2N/A *
2N/A * FP2 = FP1[0];
2N/A * BP3 = FP2[0];
2N/A * PC3 = FP2[1];
2N/A * SP3 = FP2 + 16;
2N/A *
2N/A * output: PC3, SP3, and BP3
2N/A *
2N/A * remaining callee saves registers are also captured in context
2N/A */
2N/Astatic void
2N/Afinish_capture(struct _Unwind_Context *ctx, int from_landing_pad)
2N/A{
2N/A uint64_t fp1 = ctx->current_regs[FP_RBP];
2N/A uint64_t fp2 = from_landing_pad ? fp1 : ((uint64_t *)fp1)[0];
2N/A
2N/A ctx->pc = ((uint64_t *)fp2)[1];
2N/A ctx->current_regs[SP_RSP] = fp2 + 16;
2N/A ctx->current_regs[FP_RBP] = ((uint64_t *)fp2)[0];
2N/A complete_context(ctx);
2N/A}
2N/A
2N/Astatic int
2N/Adown_one(struct _Unwind_Context *old_ctx, struct _Unwind_Context *new_ctx)
2N/A{
2N/A uint64_t old_cfa = old_ctx->cfa;
2N/A uint64_t old_pc = old_ctx->pc;
2N/A uint64_t new_cfa;
2N/A
2N/A if (old_cfa == 0 || old_pc == 0) {
2N/A new_ctx->pc = 0;
2N/A new_ctx->cfa = 0;
2N/A new_ctx->ra = 0;
2N/A return (1);
2N/A }
2N/A if (old_ctx->ra == 0) {
2N/A new_ctx->pc = 0;
2N/A new_ctx->cfa = 0;
2N/A new_ctx->ra = 0;
2N/A return (0);
2N/A }
2N/A /* now shift ----------------------------- */
2N/A _Unw_Propagate_Registers(old_ctx, new_ctx);
2N/A complete_context(new_ctx);
2N/A new_cfa = new_ctx->cfa;
2N/A if ((new_cfa < old_cfa) || (new_cfa & 7)) {
2N/A new_ctx->pc = 0;
2N/A new_ctx->cfa = 0;
2N/A new_ctx->ra = 0;
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Astatic void
2N/Ajmp_ctx(struct _Unwind_Context *ctx)
2N/A{
2N/A _Unw_jmp(ctx->pc, ctx->current_regs);
2N/A}
2N/A
2N/A/*
2N/A * Here starts the real work - the entry points from either a language
2N/A * runtime or directly from user code.
2N/A *
2N/A * The two ..._Body functions are intended as private interfaces for
2N/A * Sun code as well so should remain accessible.
2N/A */
2N/A_Unwind_Reason_Code
2N/A_Unwind_RaiseException_Body(struct _Unwind_Exception *exception_object,
2N/A struct _Unwind_Context *entry_ctx, int phase)
2N/A{
2N/A struct _Unwind_Context context;
2N/A struct _Unwind_Context *ctx = &context;
2N/A _Unwind_Reason_Code res;
2N/A
2N/A if (phase & _UA_SEARCH_PHASE) {
2N/A finish_capture(entry_ctx, 0);
2N/A copy_ctx(entry_ctx, ctx);
2N/A
2N/A for (;;) {
2N/A res = (*ctx_who(ctx))(1, phase,
2N/A exception_object->exception_class,
2N/A exception_object, ctx);
2N/A if (res != _URC_CONTINUE_UNWIND)
2N/A break;
2N/A if (down_one(ctx, ctx))
2N/A return (_URC_FATAL_PHASE1_ERROR);
2N/A }
2N/A switch (res) {
2N/A case _URC_HANDLER_FOUND:
2N/A exception_object->private_2 = _Unwind_GetCFA(ctx);
2N/A break;
2N/A default:
2N/A return (res);
2N/A /* NOTREACHED */
2N/A break;
2N/A }
2N/A } else {
2N/A finish_capture(entry_ctx, 1);
2N/A if (down_one(entry_ctx, entry_ctx))
2N/A return (_URC_FATAL_PHASE2_ERROR);
2N/A }
2N/A
2N/A phase = _UA_CLEANUP_PHASE;
2N/A copy_ctx(entry_ctx, ctx);
2N/A
2N/A for (;;) {
2N/A if (exception_object->private_2 == _Unwind_GetCFA(ctx)) {
2N/A phase |= _UA_HANDLER_FRAME;
2N/A }
2N/A res = (*ctx_who(ctx))(1, phase,
2N/A exception_object->exception_class,
2N/A exception_object, ctx);
2N/A if ((phase & _UA_HANDLER_FRAME) && res != _URC_INSTALL_CONTEXT)
2N/A return (_URC_FATAL_PHASE2_ERROR);
2N/A if (res != _URC_CONTINUE_UNWIND)
2N/A break;
2N/A if (down_one(ctx, ctx))
2N/A return (_URC_FATAL_PHASE2_ERROR);
2N/A }
2N/A switch (res) {
2N/A case _URC_INSTALL_CONTEXT:
2N/A exception_object->private_1 = 0;
2N/A jmp_ctx(ctx); /* does not return */
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A return (res);
2N/A}
2N/A
2N/A_Unwind_Reason_Code
2N/A_Unwind_RaiseException(struct _Unwind_Exception *exception_object)
2N/A{
2N/A struct _Unwind_Context entry_context;
2N/A struct _Unwind_Context *entry_ctx = &entry_context;
2N/A
2N/A _Unw_capture_regs(entry_ctx->current_regs);
2N/A
2N/A return (_Unwind_RaiseException_Body(exception_object, entry_ctx,
2N/A _UA_SEARCH_PHASE));
2N/A}
2N/A
2N/A_Unwind_Reason_Code
2N/A_Unwind_ForcedUnwind_Body(struct _Unwind_Exception *exception_object,
2N/A _Unwind_Stop_Fn stop, void *stop_parameter,
2N/A struct _Unwind_Context *ctx, int resume)
2N/A{
2N/A _Unwind_Reason_Code res;
2N/A int phase = _UA_CLEANUP_PHASE | _UA_FORCE_UNWIND;
2N/A
2N/A int again;
2N/A int doper;
2N/A
2N/A finish_capture(ctx, resume);
2N/A if (resume && down_one(ctx, ctx))
2N/A return (_URC_FATAL_PHASE2_ERROR);
2N/A
2N/A do {
2N/A again = 0;
2N/A doper = 0;
2N/A res = (*stop)(1, phase,
2N/A exception_object->exception_class,
2N/A exception_object, ctx, stop_parameter);
2N/A switch (res) {
2N/A case _URC_CONTINUE_UNWIND:
2N/A /* keep going - don't call personality */
2N/A again = 1;
2N/A break;
2N/A case _URC_NO_REASON:
2N/A /* keep going - do call personality */
2N/A again = 1;
2N/A doper = 1;
2N/A break;
2N/A case _URC_NORMAL_STOP: /* done */
2N/A break;
2N/A case _URC_INSTALL_CONTEXT: /* resume execution */
2N/A break;
2N/A default: /* failure */
2N/A break;
2N/A }
2N/A if (doper) {
2N/A res = (*ctx_who(ctx))(1, phase,
2N/A exception_object->exception_class,
2N/A exception_object, ctx);
2N/A }
2N/A switch (res) {
2N/A case _URC_INSTALL_CONTEXT:
2N/A exception_object->private_1 = (uint64_t)stop;
2N/A exception_object->private_2 = (uint64_t)stop_parameter;
2N/A jmp_ctx(ctx); /* does not return */
2N/A break;
2N/A case _URC_CONTINUE_UNWIND:
2N/A case _URC_NO_REASON:
2N/A break;
2N/A case _URC_END_OF_STACK:
2N/A ctx->cfa = ctx->ra = ctx->pc = 0;
2N/A res = (*stop)(1, phase,
2N/A exception_object->exception_class,
2N/A exception_object, ctx, stop_parameter);
2N/A return (_URC_END_OF_STACK);
2N/A /* NOTREACHED */
2N/A break;
2N/A default:
2N/A again = 0;
2N/A break;
2N/A }
2N/A if (again) {
2N/A if (down_one(ctx, ctx)) {
2N/A return (_URC_FATAL_PHASE2_ERROR);
2N/A }
2N/A }
2N/A } while (again);
2N/A
2N/A return (res);
2N/A}
2N/A
2N/A_Unwind_Reason_Code
2N/A_Unwind_ForcedUnwind(struct _Unwind_Exception *exception_object,
2N/A _Unwind_Stop_Fn stop, void *stop_parameter)
2N/A{
2N/A struct _Unwind_Context context;
2N/A struct _Unwind_Context *ctx = &context;
2N/A
2N/A _Unw_capture_regs(ctx->current_regs);
2N/A
2N/A return (_Unwind_ForcedUnwind_Body(exception_object, stop,
2N/A stop_parameter, ctx, 0));
2N/A}
2N/A
2N/Avoid
2N/A_Unwind_Resume(struct _Unwind_Exception *exception_object)
2N/A{
2N/A
2N/A struct _Unwind_Context context;
2N/A struct _Unwind_Context *ctx = &context;
2N/A
2N/A _Unw_capture_regs(ctx->current_regs);
2N/A
2N/A if (exception_object->private_1)
2N/A (void) _Unwind_ForcedUnwind_Body(exception_object,
2N/A (_Unwind_Stop_Fn)exception_object->private_1,
2N/A (void *)exception_object->private_2,
2N/A ctx, 1);
2N/A else
2N/A (void) _Unwind_RaiseException_Body(exception_object, ctx,
2N/A _UA_CLEANUP_PHASE);
2N/A}
2N/A
2N/A/* Calls destructor function for exception object */
2N/Avoid
2N/A_Unwind_DeleteException(struct _Unwind_Exception *exception_object)
2N/A{
2N/A if (exception_object->exception_cleanup != 0)
2N/A (*(exception_object->exception_cleanup))(_URC_NO_REASON,
2N/A exception_object);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * stack frame context accessors defined in ABI
2N/A * (despite all the dire text in the ABI these are reliable Get/Set routines)
2N/A * Note: RA is handled as GR value
2N/A */
2N/Auint64_t
2N/A_Unwind_GetGR(struct _Unwind_Context *context, int index)
2N/A{
2N/A uint64_t res = 0;
2N/A if (index <= EIR_R15) {
2N/A res = context->current_regs[index];
2N/A } else if (index == RET_ADD) {
2N/A res = context->ra;
2N/A }
2N/A return (res);
2N/A}
2N/A
2N/A
2N/Avoid
2N/A_Unwind_SetGR(struct _Unwind_Context *context, int index,
2N/Auint64_t new_value)
2N/A{
2N/A if (index <= EIR_R15) {
2N/A context->current_regs[index] = new_value;
2N/A } else if (index == RET_ADD) {
2N/A context->ra = new_value;
2N/A }
2N/A}
2N/A
2N/A
2N/Auint64_t
2N/A_Unwind_GetIP(struct _Unwind_Context *context)
2N/A{
2N/A return (context->pc);
2N/A}
2N/A
2N/Avoid
2N/A_Unwind_SetIP(struct _Unwind_Context *context, uint64_t new_value)
2N/A{
2N/A context->pc = new_value;
2N/A}
2N/A
2N/A
2N/Avoid *
2N/A_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context)
2N/A{
2N/A return (context->lsda);
2N/A}
2N/A
2N/A
2N/Auint64_t
2N/A_Unwind_GetRegionStart(struct _Unwind_Context *context)
2N/A{
2N/A return (context->func);
2N/A}
2N/A
2N/Auint64_t
2N/A_Unwind_GetCFA(struct _Unwind_Context *context)
2N/A{
2N/A return (context->cfa);
2N/A}