diff --git a/libc/src/__support/assembly.h b/libc/src/__support/assembly.h new file mode 100644 index 0000000000000..5b1d4cb230886 --- /dev/null +++ b/libc/src/__support/assembly.h @@ -0,0 +1,34 @@ +//===-- assembly.h - libc assembler support macros based on compiler-rt's -===// +// +// 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 LLVM_LIBC_SRC___SUPPORT_ASSEMBLY_H +#define LLVM_LIBC_SRC___SUPPORT_ASSEMBLY_H + +#ifndef __ASSEMBLER__ +#error "No not include assembly.h in non-asm sources" +#endif + +#define GLUE2_(a, b) a##b +#define GLUE(a, b) GLUE2_(a, b) +#define SYMBOL_NAME(name) GLUE(__USER_LABEL_PREFIX__, name) + +#if defined(__ELF__) && (defined(__GNU__) || defined(__FreeBSD__) || \ + defined(__Fuchsia__) || defined(__linux__)) + +// clang-format off +#define NO_EXEC_STACK_DIRECTIVE .section .note.GNU-stack, "", @progbits +#define SYMBOL_IS_FUNC(name) .type SYMBOL_NAME(name), %function +#define END_FUNC(name) .size SYMBOL_NAME(name), . - SYMBOL_NAME(name) +// clang-format on + +#else // !ELF +#define NO_EXEC_STACK_DIRECTIVE +#define SYMBOL_IS_FUNC(name) +#define END_FUNC(name) +#endif // ELF + +#endif // LLVM_LIBC_SRC___SUPPORT_ASSEMBLY_H diff --git a/libc/src/setjmp/x86_64/CMakeLists.txt b/libc/src/setjmp/x86_64/CMakeLists.txt index 9899c00e7c4a6..38f2b872e673c 100644 --- a/libc/src/setjmp/x86_64/CMakeLists.txt +++ b/libc/src/setjmp/x86_64/CMakeLists.txt @@ -1,14 +1,9 @@ add_entrypoint_object( setjmp SRCS - setjmp.cpp + setjmp.S HDRS ../setjmp_impl.h - DEPENDS - libc.include.setjmp - COMPILE_OPTIONS - -O3 - -fno-omit-frame-pointer ) add_entrypoint_object( diff --git a/libc/src/setjmp/x86_64/setjmp.S b/libc/src/setjmp/x86_64/setjmp.S new file mode 100644 index 0000000000000..10a68f0f700a6 --- /dev/null +++ b/libc/src/setjmp/x86_64/setjmp.S @@ -0,0 +1,42 @@ +//===-- Implementation of setjmp --------------------------------*- ASM -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/__support/assembly.h" +#include "src/setjmp/x86_64/setjmp.h" + +#define paste(ns) _ZN22 ## ns ## 6setjmpEP9__jmp_buf +#define expand(x) paste(x) +#define LIBC_NAMESPACE_setjmp expand(LIBC_NAMESPACE) +// aka _ZN22__llvm_libc_19_0_0_git6setjmpEP9__jmp_buf +// aka __llvm_libc_19_0_0_git::setjmp(__jmp_buf*) + +.global SYMBOL_NAME(setjmp) +.global SYMBOL_NAME(LIBC_NAMESPACE_setjmp) + +SYMBOL_IS_FUNC(setjmp) +SYMBOL_IS_FUNC(LIBC_NAMESPACE_setjmp) + +SYMBOL_NAME(setjmp): +SYMBOL_NAME(LIBC_NAMESPACE_setjmp): + + mov %rbx, RBX_OFFSET(%rdi) + mov %rbp, RBP_OFFSET(%rdi) + mov %r12, R12_OFFSET(%rdi) + mov %r13, R13_OFFSET(%rdi) + mov %r14, R14_OFFSET(%rdi) + mov %r15, R15_OFFSET(%rdi) + lea 8(%rsp), %rax + mov %rax, RSP_OFFSET(%rdi) + mov (%rsp), %rax + mov %rax, RIP_OFFSET(%rdi) + xor %eax, %eax + ret +END_FUNC(setjmp) +END_FUNC(LIBC_NAMESPACE_setjmp) + +NO_EXEC_STACK_DIRECTIVE diff --git a/libc/src/setjmp/x86_64/setjmp.cpp b/libc/src/setjmp/x86_64/setjmp.cpp deleted file mode 100644 index 8b6981d4cc34a..0000000000000 --- a/libc/src/setjmp/x86_64/setjmp.cpp +++ /dev/null @@ -1,56 +0,0 @@ -//===-- Implementation of setjmp ------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "src/__support/common.h" -#include "src/setjmp/setjmp_impl.h" - -#if !defined(LIBC_TARGET_ARCH_IS_X86_64) -#error "Invalid file include" -#endif - -namespace LIBC_NAMESPACE { - -LLVM_LIBC_FUNCTION(int, setjmp, (__jmp_buf * buf)) { - register __UINT64_TYPE__ rbx __asm__("rbx"); - register __UINT64_TYPE__ r12 __asm__("r12"); - register __UINT64_TYPE__ r13 __asm__("r13"); - register __UINT64_TYPE__ r14 __asm__("r14"); - register __UINT64_TYPE__ r15 __asm__("r15"); - - // We want to store the register values as is. So, we will suppress the - // compiler warnings about the uninitialized variables declared above. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuninitialized" - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->rbx) : "r"(rbx) :); - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r12) : "r"(r12) :); - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r13) : "r"(r13) :); - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r14) : "r"(r14) :); - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r15) : "r"(r15) :); -#pragma GCC diagnostic pop - - // We want the rbp of the caller, which is what __builtin_frame_address(1) - // should return. But, compilers generate a warning that calling - // __builtin_frame_address with non-zero argument is unsafe. So, we use - // the knowledge of the x86_64 ABI to fetch the callers rbp. As per the ABI, - // the rbp of the caller is pushed on to the stack and then new top is saved - // in this function's rbp. So, we fetch it from location at which this - // functions's rbp is pointing. - buf->rbp = *reinterpret_cast<__UINTPTR_TYPE__ *>(__builtin_frame_address(0)); - - // The callers stack address is exactly 2 pointer widths ahead of the current - // frame pointer - between the current frame pointer and the rsp of the caller - // are the return address (pushed by the x86_64 call instruction) and the - // previous stack pointer as required by the x86_64 ABI. - // The stack pointer is ahead because the stack grows down on x86_64. - buf->rsp = reinterpret_cast<__UINTPTR_TYPE__>(__builtin_frame_address(0)) + - sizeof(__UINTPTR_TYPE__) * 2; - buf->rip = reinterpret_cast<__UINTPTR_TYPE__>(__builtin_return_address(0)); - return 0; -} - -} // namespace LIBC_NAMESPACE diff --git a/libc/src/setjmp/x86_64/setjmp.h b/libc/src/setjmp/x86_64/setjmp.h new file mode 100644 index 0000000000000..71aae43fd09cf --- /dev/null +++ b/libc/src/setjmp/x86_64/setjmp.h @@ -0,0 +1,28 @@ +//===-- setjmp.h - shared constants between C++ and asm sources (x86_64) --===// +// +// 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 LLVM_LIBC_SRC_SETJMP_X86_64_SETJMP_H +#define LLVM_LIBC_SRC_SETJMP_X86_64_SETJMP_H + +#ifdef __ASSEMBLER__ +#define UL(x) x +#else +#define UL(x) x##UL +#endif + +// Brittle! Changing the layout of __jmp_buf will break this! +#define RBX_OFFSET UL(0) +#define RBP_OFFSET UL(8) +#define R12_OFFSET UL(16) +#define R13_OFFSET UL(24) +#define R14_OFFSET UL(32) +#define R15_OFFSET UL(40) +#define RSP_OFFSET UL(48) +#define RIP_OFFSET UL(56) + +#endif // LLVM_LIBC_SRC_SETJMP_X86_64_SETJMP_H diff --git a/libc/test/include/CMakeLists.txt b/libc/test/include/CMakeLists.txt index 8d8dff53169f6..4d42f348d5e00 100644 --- a/libc/test/include/CMakeLists.txt +++ b/libc/test/include/CMakeLists.txt @@ -69,3 +69,14 @@ add_libc_test( DEPENDS libc.include.llvm-libc-macros.stdckdint_macros ) + +add_libc_test( + setjmp_test + SUITE + libc_include_tests + SRCS + setjmp_test.cpp + DEPENDS + libc.include.llvm-libc-macros.offsetof_macro + libc.include.llvm-libc-types.jmp_buf + ) diff --git a/libc/test/include/setjmp_test.cpp b/libc/test/include/setjmp_test.cpp new file mode 100644 index 0000000000000..14d1e89b317d2 --- /dev/null +++ b/libc/test/include/setjmp_test.cpp @@ -0,0 +1,30 @@ +//===-- Unittests for setjmp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDSList-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "include/llvm-libc-macros/offsetof-macro.h" +#include "include/llvm-libc-types/jmp_buf.h" +#include "src/setjmp/x86_64/setjmp.h" +#include "test/UnitTest/Test.h" + +// If this test fails, then *_OFFSET macro definitions in +// libc/src/setjmp/x86_64/setjmp.S need to be fixed. This is a simple change +// detector. +TEST(LlvmLibcSetjmpTest, JmpBufLayout) { +#ifdef __x86_64__ + ASSERT_EQ(offsetof(__jmp_buf, rbx), RBX_OFFSET); + ASSERT_EQ(offsetof(__jmp_buf, rbp), RBP_OFFSET); + ASSERT_EQ(offsetof(__jmp_buf, r12), R12_OFFSET); + ASSERT_EQ(offsetof(__jmp_buf, r13), R13_OFFSET); + ASSERT_EQ(offsetof(__jmp_buf, r14), R14_OFFSET); + ASSERT_EQ(offsetof(__jmp_buf, r15), R15_OFFSET); + ASSERT_EQ(offsetof(__jmp_buf, rsp), RSP_OFFSET); + ASSERT_EQ(offsetof(__jmp_buf, rip), RIP_OFFSET); + ASSERT_EQ(sizeof(__jmp_buf), 64UL); + ASSERT_EQ(alignof(__jmp_buf), 8UL); +#endif // __x86_64__ +}