ladybird/Libraries/LibUnwind/UnwindBase.h
Andrew Kaster 3c3c1b4013 LibUnwind: Create UnwindBase.h to capture the EH ABI in code
The header is essentially just a commented + annotated version of both
the Itanium Exception Handling ABI Level I, and the 386-specific
SystemV ABI for the same. Wrapping one's head around the spec is half
the work :^)
2020-05-14 08:50:10 +02:00

253 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2020, Andrew Kaster <andrewdkaster@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <stdint.h>
// The following defintions and comments are largely pulled from the Itanium Exception handling ABI
// Reference: https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.htm, version 1.22
// This header defines the methods, types, and constants outlined in Level I. Base ABI
// Also, from the Intel386 psABI version 1.1
// Reference: https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI
// FIXME: Configure gcc and libgcc with --with-system-libunwind :^)
extern "C" {
/// Return code used by _Unwind family of methods
enum _Unwind_Reason_Code {
_URC_NO_REASON = 0, /// Forced unwind stop routine determined this isn't the correct frame
_URC_FOREIGN_EXCEPTION_CAUGHT = 1, /// This indicates that a different runtime caught this exception.
/// Nested foreign exceptions, or rethrowing a foreign exception,
/// result in undefined behaviour.
_URC_FATAL_PHASE2_ERROR = 2, /// The personality routine encountered an error during phase 2,
/// for instance a stack corruption (call std::terminate())
_URC_FATAL_PHASE1_ERROR = 3, /// The personality routine encountered an error during phase 1,
/// other than the specific error codes defined.
_URC_NORMAL_STOP = 4, /// Exception handled(?) Spec doesn't say...
_URC_END_OF_STACK = 5, /// Reached the top of the stack without a handler :(
_URC_HANDLER_FOUND = 6, /// Success! End phase 1 lookup, continue to phase 2
_URC_INSTALL_CONTEXT = 7, /// Personality routine wants the context created
_URC_CONTINUE_UNWIND = 8 /// Destructors etc called, keep walking up stack until we reach handler
};
// Forward declaration for cleanup method below
struct _Unwind_Exception;
/// Method that knows how to destroy a particualar exception. Called when handling a foreign exception
typedef void (*_Unwind_Exception_Cleanup_Fn)(_Unwind_Reason_Code reason, struct _Unwind_Exception* exc);
/// Exception object, language agnostic
/// @note: _Unwind_Exception must be double word aligned
/// @note: the private vars are different sizes on 64-bit...
struct _Unwind_Exception {
uint64_t exception_class; ///< For identification. For c++, the four low bytes are "C++\0"
_Unwind_Exception_Cleanup_Fn exception_cleanup; ///< Used if caught by a different runtime
uint32_t private_1; ///< Private for system implementation
uint32_t private_2; ///< Private for system implementation
// Note that for C++, the language specific exception object will be located directly following this header
};
/// Opaque typedef to system unwinder implementation class
struct _Unwind_Context;
/// Raise an exception, nominally noreturn (i.e. if we return, we've got trouble)
/// @param exception_object Exception that was allocated by language specific runtime
/// It must have its exception_class and cleanup fields set
/// @returns One of the following reason codes:
/// * _URC_END_OF_STACK : No handler found during phase 1 lookup. (uncaught_exception())
/// * _URC_FATAL_PHASE1_ERROR: Stack corruption during phase 1 lookup. (terminate())
///
/// @note If the unwinder can't do phase 2 cleanup, it should return _URC_FATAL_PHASE2_ERROR
/// @note The caller of _Unwind_RaiseException can make no assumptions about the state of its stack or registers.
///
_Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception* exception_object);
/// Resume propagation of an existing exception e.g. after executing cleanup code in a partially unwound stack.
/// A call to this routine is inserted at the end of a landing pad that performed cleanup, but did not resume normal
/// execution. It causes unwinding to proceed further.
///
/// @note _Unwind_Resume should not be used to implement rethrowing. To the unwinding runtime,/
/// the catch code that rethrows was a handler, and the previous unwinding session was terminated before entering.
/// Rethrowing is implemented by calling _Unwind_RaiseException again with the same exception object.
/// This means that re-throwing an exception causes the exception handing process to begin again at phase 1.
/// @note This is the only routine in the unwind library which is expected to be called directly by generated code:
/// it will be called at the end of a landing pad in a "landing-pad" model.
///
void _Unwind_Resume(struct _Unwind_Exception* exception_object);
/// Deletes the given exception object.
/// If a given runtime resumes normal execution after catching a foreign exception, it will not know how to delete
/// that exception. Such an exception will be deleted by calling _Unwind_DeleteException.
/// This is a convenience function that calls the function pointed to by the exception_cleanup field of the exception
/// header.
///
void _Unwind_DeleteException(struct _Unwind_Exception* exception_object);
//-------------
// Context management routines
//
// Used for communicating information about the unwind context between the unwind library, personality routine,
// and compiler-generated landing pad.
// They include routines to read or set the context record images of registers in the stack frame corresponding
// to a given unwind context, and to identify the location of the current unwind descriptors and unwind frame.
//-------------
/// Get the 32-bit value of the given general register
/// The register is identified by its index in the DWARF register mapping
///
/// During the two phases of unwinding, no registers have a guaranteed value.
///
uint32_t _Unwind_GetGR(struct _Unwind_Context* context, int index);
/// Set the 32-bit value of the given register
/// The register identified by its index as for _Unwind_GetGR.
///
/// The behaviour is guaranteed only if the function is called during phase 2 of unwinding,
/// and applied to an unwind context representing a handler frame, for which the personality routine will return
/// _URC_INSTALL_CONTEXT. In that case, only registers %eax and %edx should be used.
/// These scratch registers are reserved for passing arguments between the personality routine and the landing pads.
///
void _Unwind_SetGR(struct _Unwind_Context* context, int index, uint32_t new_value);
/// Get the 32-bit value of the instruction pointer (IP).
///
/// During unwinding, the value is guaranteed to be the address of the instruction immediately following the call site
/// in the function identified by the unwind context. This value may be outside of the procedure fragment for
/// a function call that is known to not return (such as _Unwind_Resume).
///
uint32_t _Unwind_GetIP(struct _Unwind_Context* context);
/// Set the value of the instruction pointer (IP) for the routine identified by the unwind context.
///
/// The behaviour is guaranteed only when this function is called for an unwind context representing a handler frame,
/// for which the personality routine will return _URC_INSTALL_CONTEXT.
/// In this case, control will be transferred to the given address, which should be the address of a landing pad.
///
void _Unwind_SetIP(struct _Unwind_Context* context, uint32_t new_value);
/// Get language specific data area for the current stack frame.
/// Useful for retrieving information that was cached after finding the personality routine.
///
uint32_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context* context);
/// Get the address of the beginning of the procedure or code fragment described by the current unwind descriptor
/// block.
///
/// This information is required to access any data stored relative to the beginning
/// of the procedure fragment. For instance, a call site table might be stored relative
/// to the beginning of the procedure fragment that contains the calls.
/// During unwinding, the function returns the start of the procedure fragment containing the call site in the current
/// stack frame.
///
uint32_t _Unwind_GetRegionStart(struct _Unwind_Context* context);
/// This function returns the 32-bit Canonical Frame Address which is defined as
/// the value of %esp at the call site in the previous frame. This value is guaranteed
/// to be correct any time the context has been passed to a personality routine or a
/// stop function.
uint32_t _Unwind_GetCFA(struct _Unwind_Context* context);
/// Personality routine
/// The personality routine is the function in the C++ (or other language) runtime library which serves
/// as an interface between the system unwind library and
/// language-specific exception handling semantics. It is specific to the code fragment
/// described by an unwind info block, and it is always referenced via the pointer in
/// the unwind info block, and hence it has no psABI-specified name.
/// @note Both GCC and Clang will generate .cfi_personality directives for __gxx_personality_v0
/// Unless we tell the compiler we're using setjump/longjump exceptions (ew, how 90's)
///
/// @param version Better be 1
/// @param actions See below
/// @param exceptionClass [ vendor ][ language ] e.g. "CLNGC++\0" "GNUCC++\0"
/// @param exceptionObject language-specific exception
/// @param context Opaque handle for personality routine + unwinder lib state
///
typedef _Unwind_Reason_Code (*__personality_routine)(int version,
_Unwind_Action actions,
uint64_t exceptionClass,
struct _Unwind_Exception* exceptionObject,
struct _Unwind_Context* context);
/// Action parameter type for personality routine during unwind. Will be a bit-wise OR of the __UnwindActions below
typedef int _Unwind_Action;
/// Which action(s) the personality routine should perform duing unwind
enum __UnwindActions {
_UA_SEARCH_PHASE = 1, /// The personality routine should check if the current frame contains a handler,
/// and if so return _URC_HANDLER_FOUND, or otherwise return _URC_CONTINUE_UNWIND.
/// _UA_SEARCH_PHASE cannot be set at the same time as _UA_CLEANUP_PHASE.
_UA_CLEANUP_PHASE = 2, /// The personality routine should perform cleanup for the current frame.
/// The personality routine can perform this cleanup itself, by calling nested
/// procedures, and return _URC_CONTINUE_UNWIND. Alternatively, it can setup the
/// registers (including the IP) for transferring control to a "landing pad", and
/// return _URC_INSTALL_CONTEXT.
_UA_HANDLER_FRAME = 4, /// During phase 2, indicates to the personality routine that the current frame is the one
/// which was flagged as the handler frame during phase 1. The personality routine
/// is not allowed to change its mind between phase 1 and phase 2, i.e. it must handle
/// the exception in this frame in phase 2.
_UA_FORCE_UNWIND = 8 /// During phase 2, indicates that no language is allowed to "catch" the exception.
/// This flag is set while unwinding the stack for longjmp or during thread
/// cancellation. User-defined code in a catch clause may still be executed, but the
/// catch clause must resume unwinding with a call to _Unwind_Resume when finished.
};
/// Function that knows how to identify a stack frame to stop unwinding for forced unwinding
/// This is different from the usual personality routine query as it can only say yes or no to each frame
typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)(int version,
_Unwind_Action actions,
uint64_t exceptionClass,
struct _Unwind_Exception* exceptionObject,
struct _Unwind_Context* context,
void* stop_parameter);
/// Raise an exception for forced unwinding, passing along the given exception object, which should have its
/// exception_class and exception_cleanup fields set. The exception object has been allocated by the language-specific
/// runtime, and has a language-specific format, except that it must contain an _Unwind_Exception struct.
///
/// Forced unwinding is a single-phase process (phase 2 of the normal exceptionhandling process). The stop and
/// stop_parameter parameters control the termination of the unwind process, instead of the usual personality routine
/// query. The stop function parameter is called for each unwind frame, with the parameters described for the usual
/// personality routine below, plus an additional stop_parameter.
///
/// When the stop function identifies the destination frame, it transfers control (according to its own, unspecified,
/// conventions) to the user code as appropriate without returning, normally after calling _Unwind_DeleteException.
/// If not, it should return an _Unwind_Reason_Code value as follows:
/// * _URC_NO_REASON: This is not the destination frame. The unwind runtime will
/// call the frames personality routine with the _UA_FORCE_UNWIND and
// _UA_CLEANUP_PHASE flags set in actions, and then unwind to the next
/// frame and call the stop function again.
/// * _URC_END_OF_STACK: In order to allow _Unwind_ForcedUnwind to perform special processing
/// when it reaches the end of the stack, the unwind runtime will call it after the last frame is rejected,
/// with a NULL stack pointer in the context, and the stop function must catch this condition
/// (i.e. by noticing the NULL stack pointer). It may return this reason code if it cannot handle end-of-stack.
/// * _URC_FATAL_PHASE2_ERROR: The stop function may return this code for other fatal conditions, e.g. stack corruption.
/// @note: The main reason for this method is to support setjump/longjump exceptions
///
_Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception* exception_object, _Unwind_Stop_Fn stop, void* stop_parameter);
} // extern "C"