# Define options.
option(LIBUNWIND_BUILD_32_BITS "Build 32 bit libunwind" ${LLVM_BUILD_32_BITS})
+option(LIBUNWIND_ENABLE_CET "Build libunwind with CET enabled." OFF)
option(LIBUNWIND_ENABLE_ASSERTIONS "Enable assertions independent of build mode." ON)
option(LIBUNWIND_ENABLE_PEDANTIC "Compile with pedantic enabled." ON)
option(LIBUNWIND_ENABLE_WERROR "Fail and stop if a warning is triggered." OFF)
message(FATAL_ERROR "libunwind must be built as either a shared or static library.")
endif()
+if (LIBUNWIND_ENABLE_CET AND MSVC)
+ message(FATAL_ERROR "libunwind CET support is not available for MSVC!")
+endif()
+
# Check that we can build with 32 bits if requested.
if (CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT WIN32)
if (LIBUNWIND_BUILD_32_BITS AND NOT LLVM_BUILD_32_BITS) # Don't duplicate the output from LLVM
add_compile_flags_if_supported(-Werror=return-type)
+if (LIBUNWIND_ENABLE_CET)
+ add_compile_flags_if_supported(-fcf-protection=full)
+ add_compile_flags_if_supported(-mshstk)
+ if (NOT LIBUNWIND_SUPPORTS_FCF_PROTECTION_EQ_FULL_FLAG)
+ message(SEND_ERROR "Compiler doesn't support CET -fcf-protection option!")
+ endif()
+ if (NOT LIBUNWIND_SUPPORTS_MSHSTK_FLAG)
+ message(SEND_ERROR "Compiler doesn't support CET -mshstk option!")
+ endif()
+endif()
+
# Get warning flags
add_compile_flags_if_supported(-W)
add_compile_flags_if_supported(-Wall)
AddressSpace.hpp
assembly.h
CompactUnwinder.hpp
+ cet_unwind.h
config.h
dwarf2.h
DwarfInstructions.hpp
#include <stdint.h>
#include <string.h>
-#include "libunwind.h"
+#include "cet_unwind.h"
#include "config.h"
+#include "libunwind.h"
namespace libunwind {
#if defined(_LIBUNWIND_TARGET_I386)
class _LIBUNWIND_HIDDEN Registers_x86;
extern "C" void __libunwind_Registers_x86_jumpto(Registers_x86 *);
+
+#if defined(_LIBUNWIND_USE_CET)
+extern "C" void *__libunwind_cet_get_jump_target() {
+ return reinterpret_cast<void *>(&__libunwind_Registers_x86_jumpto);
+}
+#endif
+
/// Registers_x86 holds the register state of a thread in a 32-bit intel
/// process.
class _LIBUNWIND_HIDDEN Registers_x86 {
/// process.
class _LIBUNWIND_HIDDEN Registers_x86_64;
extern "C" void __libunwind_Registers_x86_64_jumpto(Registers_x86_64 *);
+
+#if defined(_LIBUNWIND_USE_CET)
+extern "C" void *__libunwind_cet_get_jump_target() {
+ return reinterpret_cast<void *>(&__libunwind_Registers_x86_64_jumpto);
+}
+#endif
+
class _LIBUNWIND_HIDDEN Registers_x86_64 {
public:
Registers_x86_64();
#ifndef __UNWINDCURSOR_HPP__
#define __UNWINDCURSOR_HPP__
+#include "cet_unwind.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef __arm__
virtual void saveVFPAsX() { _LIBUNWIND_ABORT("saveVFPAsX not implemented"); }
#endif
+
+#if defined(_LIBUNWIND_USE_CET)
+ virtual void *get_registers() {
+ _LIBUNWIND_ABORT("get_registers not implemented");
+ }
+#endif
};
#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32)
virtual void saveVFPAsX();
#endif
+#if defined(_LIBUNWIND_USE_CET)
+ virtual void *get_registers() { return &_registers; }
+#endif
// libunwind does not and should not depend on C++ library which means that we
// need our own defition of inline placement new.
static void *operator new(size_t, UnwindCursor<A, R> *p) { return p; }
buf, bufLen, offset);
}
+#if defined(_LIBUNWIND_USE_CET)
+extern "C" void *__libunwind_cet_get_registers(unw_cursor_t *cursor) {
+ AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
+ return co->get_registers();
+}
+#endif
} // namespace libunwind
#endif // __UNWINDCURSOR_HPP__
#include <stdio.h>
#include <string.h>
+#include "cet_unwind.h"
#include "config.h"
#include "libunwind.h"
#include "libunwind_ext.h"
#ifndef _LIBUNWIND_SUPPORT_SEH_UNWIND
+// When CET is enabled, each "call" instruction will push return address to
+// CET shadow stack, each "ret" instruction will pop current CET shadow stack
+// top and compare it with target address which program will return.
+// In exception handing, some stack frames will be skipped before jumping to
+// landing pad and we must adjust CET shadow stack accordingly.
+// _LIBUNWIND_POP_CET_SSP is used to adjust CET shadow stack pointer and we
+// directly jump to __libunwind_Registerts_x86/x86_64_jumpto instead of using
+// a regular function call to avoid pushing to CET shadow stack again.
+#if !defined(_LIBUNWIND_USE_CET)
+#define __unw_phase2_resume(cursor, fn) __unw_resume((cursor))
+#elif defined(_LIBUNWIND_TARGET_I386)
+#define __unw_phase2_resume(cursor, fn) \
+ do { \
+ _LIBUNWIND_POP_CET_SSP((fn)); \
+ void *cetRegContext = __libunwind_cet_get_registers((cursor)); \
+ void *cetJumpAddress = __libunwind_cet_get_jump_target(); \
+ __asm__ volatile("push %%edi\n\t" \
+ "sub $4, %%esp\n\t" \
+ "jmp *%%edx\n\t" :: "D"(cetRegContext), \
+ "d"(cetJumpAddress)); \
+ } while (0)
+#elif defined(_LIBUNWIND_TARGET_X86_64)
+#define __unw_phase2_resume(cursor, fn) \
+ do { \
+ _LIBUNWIND_POP_CET_SSP((fn)); \
+ void *cetRegContext = __libunwind_cet_get_registers((cursor)); \
+ void *cetJumpAddress = __libunwind_cet_get_jump_target(); \
+ __asm__ volatile("jmpq *%%rdx\n\t" :: "D"(cetRegContext), \
+ "d"(cetJumpAddress)); \
+ } while (0)
+#endif
+
static _Unwind_Reason_Code
unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) {
__unw_init_local(cursor, uc);
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)",
(void *)exception_object);
+ // uc is initialized by __unw_getcontext in the parent frame. The first stack
+ // frame walked is unwind_phase2.
+ unsigned framesWalked = 1;
// Walk each frame until we reach where search phase said to stop.
while (true) {
}
#endif
+ ++framesWalked;
// If there is a personality routine, tell it we are unwinding.
if (frameInfo.handler != 0) {
_Unwind_Personality_Fn p =
", sp=0x%" PRIxPTR,
(void *)exception_object, pc, sp);
}
- __unw_resume(cursor);
- // __unw_resume() only returns if there was an error.
+
+ __unw_phase2_resume(cursor, framesWalked);
+ // __unw_phase2_resume() only returns if there was an error.
return _URC_FATAL_PHASE2_ERROR;
default:
// Personality routine returned an unknown result code.
_Unwind_Stop_Fn stop, void *stop_parameter) {
__unw_init_local(cursor, uc);
+ // uc is initialized by __unw_getcontext in the parent frame. The first stack
+ // frame walked is unwind_phase2_forced.
+ unsigned framesWalked = 1;
// Walk each frame until we reach where search phase said to stop
while (__unw_step(cursor) > 0) {
return _URC_FATAL_PHASE2_ERROR;
}
+ ++framesWalked;
// If there is a personality routine, tell it we are unwinding.
if (frameInfo.handler != 0) {
_Unwind_Personality_Fn p =
"_URC_INSTALL_CONTEXT",
(void *)exception_object);
// We may get control back if landing pad calls _Unwind_Resume().
- __unw_resume(cursor);
+ __unw_phase2_resume(cursor, framesWalked);
break;
default:
// Personality routine returned an unknown result code.
# + return address +
# +-----------------------+ <-- SP
# + +
+
+ _LIBUNWIND_CET_ENDBR
movl 4(%esp), %eax
# set up eax and ret on new stack location
movl 28(%eax), %edx # edx holds new stack pointer
# skip ss
# skip eflags
pop %eax # eax was already pushed on new stack
- ret # eip was already pushed on new stack
+ pop %ecx
+ jmp *%ecx
# skip cs
# skip ds
# skip es
# On entry, thread_state pointer is in rdi
#endif
+ _LIBUNWIND_CET_ENDBR
movq 56(%rdi), %rax # rax holds new stack pointer
subq $16, %rax
movq %rax, 56(%rdi)
#endif
movq 56(%rdi), %rsp # cut back rsp to new location
pop %rdi # rdi was saved here earlier
- ret # rip was saved here
+ pop %rcx
+ jmpq *%rcx
#elif defined(__powerpc64__)
# + +
#
DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
+
+ _LIBUNWIND_CET_ENDBR
push %eax
movl 8(%esp), %eax
movl %ebx, 4(%eax)
#define TMP %rsi
#endif
+ _LIBUNWIND_CET_ENDBR
movq %rax, (PTR)
movq %rbx, 8(PTR)
movq %rcx, 16(PTR)
#ifndef UNWIND_ASSEMBLY_H
#define UNWIND_ASSEMBLY_H
+#if (defined(__i386__) || defined(__x86_64__)) && defined(__linux__)
+#include <cet.h>
+#define _LIBUNWIND_CET_ENDBR _CET_ENDBR
+#else
+#define _LIBUNWIND_CET_ENDBR
+#endif
+
#if defined(__powerpc64__)
#define SEPARATOR ;
#define PPC64_OFFS_SRR0 0
--- /dev/null
+//===--------------------------- cet_unwind.h -----------------------------===//
+//
+// 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
+//
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LIBUNWIND_CET_UNWIND_H
+#define LIBUNWIND_CET_UNWIND_H
+
+#include "libunwind.h"
+#include <cet.h>
+#include <immintrin.h>
+
+// Currently, CET is implemented on Linux x86 platforms.
+#if defined(_LIBUNWIND_TARGET_LINUX) && defined(__CET__) && defined(__SHSTK__)
+#define _LIBUNWIND_USE_CET 1
+#endif
+
+#if defined(_LIBUNWIND_USE_CET)
+#define _LIBUNWIND_POP_CET_SSP(x) \
+ do { \
+ unsigned long ssp = _get_ssp(); \
+ if (ssp != 0) { \
+ unsigned int tmp = (x); \
+ while (tmp > 255) { \
+ _inc_ssp(255); \
+ tmp -= 255; \
+ } \
+ _inc_ssp(tmp); \
+ } \
+ } while (0)
+#endif
+
+extern void *__libunwind_cet_get_registers(unw_cursor_t *);
+extern void *__libunwind_cet_get_jump_target();
+
+#endif
#include <libunwind.h>
-#include "libunwind_ext.h"
#include "config.h"
+#include "libunwind_ext.h"
#include <stdlib.h>
endif()
pythonize_bool(LIBUNWIND_BUILD_32_BITS)
+pythonize_bool(LIBUNWIND_ENABLE_CET)
pythonize_bool(LIBCXX_ENABLE_SHARED)
pythonize_bool(LIBUNWIND_ENABLE_SHARED)
pythonize_bool(LIBUNWIND_ENABLE_THREADS)
if not self.get_lit_bool('enable_threads', True):
self.cxx.compile_flags += ['-D_LIBUNWIND_HAS_NO_THREADS']
self.config.available_features.add('libunwind-no-threads')
+ if self.get_lit_bool('x86_cet', False):
+ self.cxx.compile_flags += ['-fcf-protection=full']
super(Configuration, self).configure_compile_flags()
def configure_compile_flags_header_includes(self):
config.sysroot = "@LIBUNWIND_SYSROOT@"
config.gcc_toolchain = "@LIBUNWIND_GCC_TOOLCHAIN@"
config.cxx_ext_threads = @LIBUNWIND_BUILD_EXTERNAL_THREAD_LIBRARY@
+config.x86_cet = @LIBUNWIND_ENABLE_CET@
site.addsitedir(os.path.join(config.libunwind_src_root, 'test'))
site.addsitedir(os.path.join(config.libcxx_src_root, 'utils'))