[libunwind] [libunwind] [SEH] Implement parsing of ARM pdata/xdata (PR #137950)
Martin Storsjö via cfe-commits
cfe-commits at lists.llvm.org
Fri May 2 05:15:58 PDT 2025
https://github.com/mstorsjo updated https://github.com/llvm/llvm-project/pull/137950
>From 4f5614e410d1dc5147e2dacbacf64d4bd4ce7e82 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <martin at martin.st>
Date: Tue, 18 Apr 2023 15:02:54 +0300
Subject: [PATCH 1/2] [libunwind] [SEH] Implement parsing of aarch64
pdata/xdata
This is needed for forced unwind.
This adds an aarch64 case for extracting the LanguageHandler and
HandlerData fields from unwind info, in UnwindCursor::getInfoFromSEH,
corresponding to the existing case for x86_64.
This uses the struct IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA; this
only became available in WinSDK 10.0.19041.0 and mingw-w64 v11.0 (or
a mingw-w64 git snapshot after April 2023).
(This is only a build-time requirement though; the format for the
unwind data has been fixed since the start of Windows 10 on ARM64,
so this doesn't impose any runtime requirement.)
---
libunwind/src/UnwindCursor.hpp | 46 ++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index ca9927edc9990..240752e09e1ee 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -2018,6 +2018,52 @@ bool UnwindCursor<A, R>::getInfoFromSEH(pint_t pc) {
_info.handler = 0;
}
}
+#elif defined(_LIBUNWIND_TARGET_AARCH64)
+ if (unwindEntry->Flag != 0) { // Packed unwind info
+ _info.end_ip = _info.start_ip + unwindEntry->FunctionLength * 4;
+ // Only fill in the handler and LSDA if they're stale.
+ if (pc != getLastPC()) {
+ // Packed unwind info doesn't have an exception handler.
+ _info.lsda = 0;
+ _info.handler = 0;
+ }
+ } else {
+ IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *xdata =
+ reinterpret_cast<IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *>(
+ base + unwindEntry->UnwindData);
+ _info.end_ip = _info.start_ip + xdata->FunctionLength * 4;
+ // Only fill in the handler and LSDA if they're stale.
+ if (pc != getLastPC()) {
+ if (xdata->ExceptionDataPresent) {
+ uint32_t offset = 1; // The main xdata
+ uint32_t codeWords = xdata->CodeWords;
+ uint32_t epilogScopes = xdata->EpilogCount;
+ if (xdata->EpilogCount == 0 && xdata->CodeWords == 0) {
+ uint32_t extensionWord = reinterpret_cast<uint32_t *>(xdata)[1];
+ codeWords = (extensionWord >> 16) & 0xff;
+ epilogScopes = extensionWord & 0xffff;
+ offset++;
+ }
+ if (!xdata->EpilogInHeader)
+ offset += epilogScopes;
+ offset += codeWords;
+ uint32_t *exceptionHandlerInfo =
+ reinterpret_cast<uint32_t *>(xdata) + offset;
+ _dispContext.HandlerData = &exceptionHandlerInfo[1];
+ _dispContext.LanguageHandler = reinterpret_cast<EXCEPTION_ROUTINE *>(
+ base + exceptionHandlerInfo[0]);
+ _info.lsda = reinterpret_cast<unw_word_t>(_dispContext.HandlerData);
+ if (exceptionHandlerInfo[0])
+ _info.handler =
+ reinterpret_cast<unw_word_t>(__libunwind_seh_personality);
+ else
+ _info.handler = 0;
+ } else {
+ _info.lsda = 0;
+ _info.handler = 0;
+ }
+ }
+ }
#endif
setLastPC(pc);
return true;
>From b5ed49936152211b8b26ac272608ba5f45a6355f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <martin at martin.st>
Date: Tue, 18 Apr 2023 23:28:20 +0300
Subject: [PATCH 2/2] [libunwind] [SEH] Implement parsing of ARM pdata/xdata
This is generally very similar to the aarch64 case.
Contrary to aarch64, the public headers don't contain any definition
of a struct for interpreting this data, so we provide our own.
---
libunwind/src/UnwindCursor.hpp | 37 ++++++++++++++++++++++++++++------
1 file changed, 31 insertions(+), 6 deletions(-)
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 240752e09e1ee..bc84c07af0c38 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -83,6 +83,22 @@ struct UNWIND_INFO {
uint16_t UnwindCodes[2];
};
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+union UNWIND_INFO_ARM {
+ DWORD HeaderData;
+ struct {
+ DWORD FunctionLength : 18;
+ DWORD Version : 2;
+ DWORD ExceptionDataPresent : 1;
+ DWORD EpilogInHeader : 1;
+ DWORD FunctionFragment : 1;
+ DWORD EpilogCount : 5;
+ DWORD CodeWords : 4;
+ };
+};
+#pragma clang diagnostic pop
+
extern "C" _Unwind_Reason_Code __libunwind_seh_personality(
int, _Unwind_Action, uint64_t, _Unwind_Exception *,
struct _Unwind_Context *);
@@ -2018,9 +2034,18 @@ bool UnwindCursor<A, R>::getInfoFromSEH(pint_t pc) {
_info.handler = 0;
}
}
-#elif defined(_LIBUNWIND_TARGET_AARCH64)
+#elif defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_ARM)
+
+#if defined(_LIBUNWIND_TARGET_AARCH64)
+#define FUNC_LENGTH_UNIT 4
+#define XDATA_TYPE IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA
+#else
+#define FUNC_LENGTH_UNIT 2
+#define XDATA_TYPE UNWIND_INFO_ARM
+#endif
if (unwindEntry->Flag != 0) { // Packed unwind info
- _info.end_ip = _info.start_ip + unwindEntry->FunctionLength * 4;
+ _info.end_ip =
+ _info.start_ip + unwindEntry->FunctionLength * FUNC_LENGTH_UNIT;
// Only fill in the handler and LSDA if they're stale.
if (pc != getLastPC()) {
// Packed unwind info doesn't have an exception handler.
@@ -2028,10 +2053,9 @@ bool UnwindCursor<A, R>::getInfoFromSEH(pint_t pc) {
_info.handler = 0;
}
} else {
- IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *xdata =
- reinterpret_cast<IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *>(
- base + unwindEntry->UnwindData);
- _info.end_ip = _info.start_ip + xdata->FunctionLength * 4;
+ XDATA_TYPE *xdata =
+ reinterpret_cast<XDATA_TYPE *>(base + unwindEntry->UnwindData);
+ _info.end_ip = _info.start_ip + xdata->FunctionLength * FUNC_LENGTH_UNIT;
// Only fill in the handler and LSDA if they're stale.
if (pc != getLastPC()) {
if (xdata->ExceptionDataPresent) {
@@ -2039,6 +2063,7 @@ bool UnwindCursor<A, R>::getInfoFromSEH(pint_t pc) {
uint32_t codeWords = xdata->CodeWords;
uint32_t epilogScopes = xdata->EpilogCount;
if (xdata->EpilogCount == 0 && xdata->CodeWords == 0) {
+ // The extension word has got the same layout for both ARM and ARM64
uint32_t extensionWord = reinterpret_cast<uint32_t *>(xdata)[1];
codeWords = (extensionWord >> 16) & 0xff;
epilogScopes = extensionWord & 0xffff;
More information about the cfe-commits
mailing list