Skip to content

Commit

Permalink
[libunwind][WebAssembly] Support Wasm EH
Browse files Browse the repository at this point in the history
This adds Wasm-specific libunwind port to support Wasm exception
handling (https://github.com/WebAssembly/exception-handling).

Wasm EH requires `__USING_WASM_EXCEPTIONS__` to be defined. This adds
`Unwind-wasm.c`, which defines libunwind APIs for Wasm. This also adds a
`thread_local` struct of type `_Unwind_LandingPadContext`, which serves
as a medium for input/output data between the user code and the
personality function. How all these work is explained in
https://github.com/WebAssembly/tool-conventions/blob/main/EHScheme.md.
(The doc is old and "You Shouldn't Prune Unreachable Resumes" section
doesn't apply anymore, but otherwise it should be good)

The bulk of these changes was added back in Mar 2020 in
emscripten-core/emscripten#10577 to emscripten
repo and has been used ever since. Now we'd like to upstream this so
that other toolchains that don't use emscripten libraries, e.g., WASI,
can use this too.

Companion patch: D158918

Reviewed By: dschuff, #libunwind, phosek

Differential Revision: https://reviews.llvm.org/D158919
  • Loading branch information
aheejin committed Sep 22, 2023
1 parent e6cbba7 commit 058222b
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 1 deletion.
121 changes: 121 additions & 0 deletions libunwind/src/Unwind-wasm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//
// Implements Wasm exception handling proposal
// (https://github.com/WebAssembly/exception-handling) based C++ exceptions
//
//===----------------------------------------------------------------------===//

#include "config.h"
#include "unwind.h"
#include <stdbool.h>
#include <threads.h>

#ifdef __USING_WASM_EXCEPTIONS__

_Unwind_Reason_Code __gxx_personality_wasm0(int version, _Unwind_Action actions,
uint64_t exceptionClass,
_Unwind_Exception *unwind_exception,
_Unwind_Context *context);

struct _Unwind_LandingPadContext {
// Input information to personality function
uintptr_t lpad_index; // landing pad index
uintptr_t lsda; // LSDA address

// Output information computed by personality function
uintptr_t selector; // selector value
};

// Communication channel between compiler-generated user code and personality
// function
thread_local struct _Unwind_LandingPadContext __wasm_lpad_context;

/// Calls to this function is in landing pads in compiler-generated user code.
/// In other EH schemes, stack unwinding is done by libunwind library, which
/// calls the personality function for each each frame it lands. On the other
/// hand, WebAssembly stack unwinding process is performed by a VM, and the
/// personality function cannot be called from there. So the compiler inserts
/// a call to this function in landing pads in the user code, which in turn
/// calls the personality function.
_Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
struct _Unwind_Exception *exception_object =
(struct _Unwind_Exception *)exception_ptr;
_LIBUNWIND_TRACE_API("_Unwind_CallPersonality(exception_object=%p)",
(void *)exception_object);

// Reset the selector.
__wasm_lpad_context.selector = 0;

// Call personality function. Wasm does not have two-phase unwinding, so we
// only do the cleanup phase.
return __gxx_personality_wasm0(
1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
(struct _Unwind_Context *)&__wasm_lpad_context);
}

/// Called by __cxa_throw.
_LIBUNWIND_EXPORT _Unwind_Reason_Code
_Unwind_RaiseException(_Unwind_Exception *exception_object) {
_LIBUNWIND_TRACE_API("_Unwind_RaiseException(exception_object=%p)",
(void *)exception_object);
// Use Wasm EH's 'throw' instruction.
__builtin_wasm_throw(0, exception_object);
}

/// Called by __cxa_end_catch.
_LIBUNWIND_EXPORT void
_Unwind_DeleteException(_Unwind_Exception *exception_object) {
_LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)",
(void *)(exception_object));
if (exception_object->exception_cleanup != NULL)
(*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT,
exception_object);
}

/// Called by personality handler to alter register values.
_LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index,
uintptr_t value) {
_LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, index=%d, value=%lu)",
(void *)context, index, value);
// We only use this function to set __wasm_lpad_context.selector field, which
// is index 1 in the personality function.
if (index == 1)
((struct _Unwind_LandingPadContext *)context)->selector = value;
}

/// Called by personality handler to get instruction pointer.
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) {
// The result will be used as an 1-based index after decrementing 1, so we
// increment 2 here
uintptr_t result =
((struct _Unwind_LandingPadContext *)context)->lpad_index + 2;
_LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => %lu", (void *)context,
result);
return result;
}

/// Not used in Wasm.
_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context,
uintptr_t value) {}

/// Called by personality handler to get LSDA for current frame.
_LIBUNWIND_EXPORT uintptr_t
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
uintptr_t result = ((struct _Unwind_LandingPadContext *)context)->lsda;
_LIBUNWIND_TRACE_API("_Unwind_GetLanguageSpecificData(context=%p) => 0x%lx",
(void *)context, result);
return result;
}

/// Not used in Wasm.
_LIBUNWIND_EXPORT uintptr_t
_Unwind_GetRegionStart(struct _Unwind_Context *context) {
return 0;
}

#endif
2 changes: 1 addition & 1 deletion libunwind/src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
__asm__(".globl " SYMBOL_NAME(aliasname)); \
__asm__(SYMBOL_NAME(aliasname) " = " SYMBOL_NAME(name)); \
_LIBUNWIND_ALIAS_VISIBILITY(SYMBOL_NAME(aliasname))
#elif defined(__ELF__) || defined(_AIX)
#elif defined(__ELF__) || defined(_AIX) || defined(__wasm__)
#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \
extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname \
__attribute__((weak, alias(#name)));
Expand Down

0 comments on commit 058222b

Please sign in to comment.