Skip to content

[libunwind] Add initial ARM64EC support #138583

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 13, 2025
Merged

Conversation

cjacek
Copy link
Contributor

@cjacek cjacek commented May 5, 2025

ARM64EC defines __x86_64__, which is sufficient to make most C/C++ code behave correctly. To preserve an external ABI compatible with x86_64, this patch uses the x86_64 context layout and implements unw_getcontext by storing the appropriate aarch64 registers according to the mapping defined by the ARM64EC ABI.

ARM64EC defines __x86_64__, which is sufficient to make most C/C++ code behave correctly.
To preserve an external ABI compatible with x86_64, this patch uses the x86_64 context
layout and implements unw_getcontext by storing the appropriate aarch64 registers according
to the mapping defined by the ARM64EC ABI.
@cjacek cjacek requested a review from mstorsjo May 5, 2025 20:47
@cjacek cjacek requested a review from a team as a code owner May 5, 2025 20:47
@llvmbot
Copy link
Member

llvmbot commented May 5, 2025

@llvm/pr-subscribers-libunwind

Author: Jacek Caban (cjacek)

Changes

ARM64EC defines __x86_64__, which is sufficient to make most C/C++ code behave correctly. To preserve an external ABI compatible with x86_64, this patch uses the x86_64 context layout and implements unw_getcontext by storing the appropriate aarch64 registers according to the mapping defined by the ARM64EC ABI.


Full diff: https://github.com/llvm/llvm-project/pull/138583.diff

2 Files Affected:

  • (modified) libunwind/src/UnwindRegistersRestore.S (+1-1)
  • (modified) libunwind/src/UnwindRegistersSave.S (+49)
diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S
index 1702d016c368b..5e199188945df 100644
--- a/libunwind/src/UnwindRegistersRestore.S
+++ b/libunwind/src/UnwindRegistersRestore.S
@@ -66,7 +66,7 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_x86_jumpto)
   # skip fs
   # skip gs
 
-#elif defined(__x86_64__)
+#elif defined(__x86_64__) && !defined(__arm64ec__)
 
 DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_x86_64_jumpto)
 #
diff --git a/libunwind/src/UnwindRegistersSave.S b/libunwind/src/UnwindRegistersSave.S
index a489a8ba6df15..12fd81d49299b 100644
--- a/libunwind/src/UnwindRegistersSave.S
+++ b/libunwind/src/UnwindRegistersSave.S
@@ -65,6 +65,53 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
   xorl  %eax, %eax    # return UNW_ESUCCESS
   ret
 
+#elif defined(__arm64ec__)
+
+//
+// extern int __unw_getcontext(unw_context_t* thread_state)
+//
+// On entry:
+//  thread_state pointer is in x0
+//
+  .section .text,"xr",discard,"#__unw_getcontext"
+  .p2align 2
+DEFINE_LIBUNWIND_FUNCTION("#__unw_getcontext")
+  stp    x8, x27, [x0, #0x000]  // rax, rbx
+  stp    x0, x1,  [x0, #0x010]  // rcx, rdx
+  stp    x26,x25, [x0, #0x020]  // rdi, rsi
+  mov    x1, sp
+  stp    fp, x1,  [x0, #0x030]  // rbp, rsp
+  stp    x2, x3,  [x0, #0x040]  // r8,  r9
+  stp    x4, x5,  [x0, #0x050]  // r10, r11
+  stp    x19,x20, [x0, #0x060]  // r12, r13
+  stp    x21,x22, [x0, #0x070]  // r14, r15
+  str    x30,     [x0, #0x080]  // store return address as pc
+  stp    q0, q1,  [x0, #0x0b0]  // xmm0, xmm1
+  stp    q2, q3,  [x0, #0x0d0]  // xmm2, xmm3
+  stp    q4, q5,  [x0, #0x0f0]  // xmm4, xmm5
+  stp    q6, q7,  [x0, #0x110]  // xmm6, xmm7
+  stp    q8, q9,  [x0, #0x130]  // xmm8, xmm9
+  stp    q10,q11, [x0, #0x150]  // xmm10,xmm11
+  stp    q12,q13, [x0, #0x170]  // xmm12,xmm13
+  stp    q14,q15, [x0, #0x190]  // xmm14,xmm15
+  mov    x0, #0                 // return UNW_ESUCCESS
+  ret
+
+  .globl "#unw_getcontext"
+  .set "#unw_getcontext", "#__unw_getcontext"
+  .weak_anti_dep __unw_getcontext
+  .set __unw_getcontext, "#__unw_getcontext"
+  .weak_anti_dep unw_getcontext
+  .set unw_getcontext, "#unw_getcontext"
+
+  .section .hybmp$x,"yi"
+  .symidx "#__unw_getcontext"
+  .symidx $ientry_thunk$cdecl$i8$i8
+  .word 1
+  .text
+
+  EXPORT_SYMBOL(unw_getcontext)
+
 #elif defined(__x86_64__)
 
 #
@@ -1181,7 +1228,9 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
 
 #endif
 
+#ifndef __arm64ec__
   WEAK_ALIAS(__unw_getcontext, unw_getcontext)
+#endif
 
 #endif /* !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__wasm__) */
 

@cjacek
Copy link
Contributor Author

cjacek commented May 5, 2025

This is enough to pass most tests, except for forced unwind. I suspect that test requires a similar change to #137949. ARM64EC modules can contain SEH in both x86_64 format (for x86_64 code, accessible through PE headers, this should already work) and ARM format (for ARM64 code, accessible through CHPE metadata, which is not yet implemented). I haven’t looked into that part in detail yet.

__libunwind_Registers_x86_64_jumpto can be skipped, as the SEH cursor already uses RtlRestoreContext. If we were to implement it, it would be more complex than on other targets, when jumping to x86 code, we’d need to hand off execution to the emulator. The simplest way to handle this would be to implement the entire function
in C and delegate to RtlRestoreContext.

Due to the additional requirements for symbol mangling and unmangled aliases on ARM64EC, the WEAK_ALIAS macro isn't suitable in its current form. While it could be extended to support this, keeping WEAK_ALIAS simple and containing the complexity within the ARM64EC-specific code seemed like the cleaner approach.

Copy link
Member

@mstorsjo mstorsjo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Quite amazing if this is the only change needed, if the existing #ifdef __x86_64__ work as needed here (except for the force unwinding tests).

@cjacek cjacek merged commit 84c1564 into llvm:main May 13, 2025
83 checks passed
@cjacek cjacek deleted the arm64ec-libunwind branch May 13, 2025 13:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants