| //===-- Hermetic integration test for ucontext routines -------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // This is a hermetic integration test for getcontext and setcontext. |
| // We use a hermetic test here because the heavier unit test infrastructure |
| // (like GTest) interferes with context switching, stack frame management, |
| // and floating-point state restoration, causing spurious failures. |
| |
| #include "test/IntegrationTest/test.h" |
| |
| #include "include/llvm-libc-types/ucontext_t.h" |
| |
| #include "src/ucontext/getcontext.h" |
| #include "src/ucontext/setcontext.h" |
| |
| void basic_stub_test() { |
| ucontext_t ctx; |
| static volatile int jumped = 0; |
| |
| int ret = LIBC_NAMESPACE::getcontext(&ctx); |
| ASSERT_EQ(ret, 0); |
| |
| if (!jumped) { |
| jumped = 1; |
| LIBC_NAMESPACE::setcontext(&ctx); |
| ASSERT_TRUE(false); // Should not happen |
| } |
| |
| ASSERT_TRUE(true); |
| } |
| |
| void register_preservation_test() { |
| ucontext_t ctx; |
| static volatile int jumped = 0; |
| |
| long checked_r12, checked_r13, checked_r14, checked_r15; |
| |
| { |
| register long r12_val asm("r12") = 0x1212121212121212; |
| register long r13_val asm("r13") = 0x1313131313131313; |
| register long r14_val asm("r14") = 0x1414141414141414; |
| register long r15_val asm("r15") = 0x1515151515151515; |
| |
| register void *rdi_val asm("rdi") = &ctx; |
| |
| asm volatile("call *%[getcontext_ptr]" |
| : "+r"(rdi_val), "+r"(r12_val), "+r"(r13_val), "+r"(r14_val), |
| "+r"(r15_val) |
| : [getcontext_ptr] "r"((void *)LIBC_NAMESPACE::getcontext) |
| : "memory", "rax", "rcx", "rdx", "rsi"); |
| |
| checked_r12 = r12_val; |
| checked_r13 = r13_val; |
| checked_r14 = r14_val; |
| checked_r15 = r15_val; |
| } |
| |
| if (!jumped) { |
| jumped = 1; |
| |
| // Modify registers to ensure they are restored from context |
| asm volatile("movq $0, %%r12\n\t" |
| "movq $0, %%r13\n\t" |
| "movq $0, %%r14\n\t" |
| "movq $0, %%r15\n\t" :: |
| : "r12", "r13", "r14", "r15"); |
| |
| register const ucontext_t *rdi_set asm("rdi") = &ctx; |
| asm volatile("call *%[setcontext_ptr]" ::"r"(rdi_set), |
| [setcontext_ptr] "r"((void *)LIBC_NAMESPACE::setcontext) |
| : "memory"); |
| |
| ASSERT_TRUE(false); // Should not reach here |
| } |
| |
| ASSERT_EQ(checked_r12, (long)0x1212121212121212); |
| ASSERT_EQ(checked_r13, (long)0x1313131313131313); |
| ASSERT_EQ(checked_r14, (long)0x1414141414141414); |
| ASSERT_EQ(checked_r15, (long)0x1515151515151515); |
| } |
| |
| void test_rbx_rdx() { |
| ucontext_t ctx; |
| static volatile int jumped = 0; |
| |
| long checked_rbx, checked_rdx; |
| |
| { |
| register long rbx_val asm("rbx") = 0xBBBBBBBBBBBBBBBB; |
| register long rdx_val asm("rdx") = 0xDDDDDDDDDDDDDDDD; |
| |
| register void *rdi_val asm("rdi") = &ctx; |
| |
| asm volatile("call *%[getcontext_ptr]" |
| : "+r"(rdi_val), "+r"(rbx_val), "+r"(rdx_val) |
| : [getcontext_ptr] "r"((void *)LIBC_NAMESPACE::getcontext) |
| : "memory", "rax", "rcx", "rsi"); |
| |
| checked_rbx = rbx_val; |
| checked_rdx = rdx_val; |
| } |
| |
| if (!jumped) { |
| jumped = 1; |
| |
| asm volatile("movq $0, %%rbx\n\t" |
| "movq $0, %%rdx\n\t" :: |
| : "rbx", "rdx"); |
| |
| register const ucontext_t *rdi_set asm("rdi") = &ctx; |
| asm volatile("call *%[setcontext_ptr]" ::"r"(rdi_set), |
| [setcontext_ptr] "r"((void *)LIBC_NAMESPACE::setcontext) |
| : "memory"); |
| |
| ASSERT_TRUE(false); |
| } |
| |
| ASSERT_EQ(checked_rbx, (long)0xBBBBBBBBBBBBBBBB); |
| ASSERT_EQ(checked_rdx, (long)0xDDDDDDDDDDDDDDDD); |
| } |
| |
| void test_r8_r11() { |
| ucontext_t ctx; |
| static volatile int jumped = 0; |
| |
| long checked_r8, checked_r9, checked_r10, checked_r11; |
| |
| { |
| register long r8_val asm("r8") = 0x0808080808080808; |
| register long r9_val asm("r9") = 0x0909090909090909; |
| register long r10_val asm("r10") = 0x1010101010101010; |
| register long r11_val asm("r11") = 0x1111111111111111; |
| |
| register void *rdi_val asm("rdi") = &ctx; |
| |
| asm volatile("call *%[getcontext_ptr]" |
| : "+r"(rdi_val), "+r"(r8_val), "+r"(r9_val), "+r"(r10_val), |
| "+r"(r11_val) |
| : [getcontext_ptr] "r"((void *)LIBC_NAMESPACE::getcontext) |
| : "memory", "rax", "rcx", "rdx", "rsi"); |
| |
| checked_r8 = r8_val; |
| checked_r9 = r9_val; |
| checked_r10 = r10_val; |
| checked_r11 = r11_val; |
| } |
| |
| if (!jumped) { |
| jumped = 1; |
| |
| asm volatile("movq $0, %%r8\n\t" |
| "movq $0, %%r9\n\t" |
| "movq $0, %%r10\n\t" |
| "movq $0, %%r11\n\t" :: |
| : "r8", "r9", "r10", "r11"); |
| |
| register const ucontext_t *rdi_set asm("rdi") = &ctx; |
| asm volatile("call *%[setcontext_ptr]" ::"r"(rdi_set), |
| [setcontext_ptr] "r"((void *)LIBC_NAMESPACE::setcontext) |
| : "memory"); |
| |
| ASSERT_TRUE(false); |
| } |
| |
| ASSERT_EQ(checked_r8, (long)0x0808080808080808); |
| ASSERT_EQ(checked_r9, (long)0x0909090909090909); |
| ASSERT_EQ(checked_r10, (long)0x1010101010101010); |
| ASSERT_EQ(checked_r11, (long)0x1111111111111111); |
| } |
| |
| TEST_MAIN() { |
| basic_stub_test(); |
| register_preservation_test(); |
| test_rbx_rdx(); |
| test_r8_r11(); |
| return 0; |
| } |