Revert "[libunwind] Fix execution flow imbalance when using C++ Exceptions"#167659
Revert "[libunwind] Fix execution flow imbalance when using C++ Exceptions"#167659
Conversation
…tions (#…" This reverts commit cf35502.
|
@llvm/pr-subscribers-lldb Author: Sylvestre Ledru (sylvestre) ChangesReverts llvm/llvm-project#165066 Patch is 26.00 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/167659.diff 12 Files Affected:
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index 28649fafb23d5..9d4c8344150f6 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -1832,9 +1832,8 @@ inline const char *Registers_ppc64::getRegisterName(int regNum) {
/// Registers_arm64 holds the register state of a thread in a 64-bit arm
/// process.
class _LIBUNWIND_HIDDEN Registers_arm64;
+extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *);
extern "C" int64_t __libunwind_Registers_arm64_za_disable();
-extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *,
- unsigned walkedFrames);
#if defined(_LIBUNWIND_USE_GCS)
extern "C" void *__libunwind_shstk_get_jump_target() {
@@ -1862,17 +1861,10 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
v128 getVectorRegister(int num) const;
void setVectorRegister(int num, v128 value);
static const char *getRegisterName(int num);
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
- _LIBUNWIND_TRACE_NO_INLINE
- void returnto(unsigned walkedFrames) {
- __libunwind_Registers_arm64_jumpto(this, walkedFrames);
- }
-#else
- void jumpto() {
- zaDisable();
- __libunwind_Registers_arm64_jumpto(this, 0);
+ void jumpto() {
+ zaDisable();
+ __libunwind_Registers_arm64_jumpto(this);
}
-#endif
static constexpr int lastDwarfRegNum() {
return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64;
}
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index d7348254af07b..7ec5f9e91578a 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -472,9 +472,7 @@ class _LIBUNWIND_HIDDEN AbstractUnwindCursor {
virtual void getInfo(unw_proc_info_t *) {
_LIBUNWIND_ABORT("getInfo not implemented");
}
- _LIBUNWIND_TRACE_NO_INLINE virtual void jumpto() {
- _LIBUNWIND_ABORT("jumpto not implemented");
- }
+ virtual void jumpto() { _LIBUNWIND_ABORT("jumpto not implemented"); }
virtual bool isSignalFrame() {
_LIBUNWIND_ABORT("isSignalFrame not implemented");
}
@@ -491,12 +489,6 @@ class _LIBUNWIND_HIDDEN AbstractUnwindCursor {
virtual void saveVFPAsX() { _LIBUNWIND_ABORT("saveVFPAsX not implemented"); }
#endif
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
- virtual void setWalkedFrames(unsigned) {
- _LIBUNWIND_ABORT("setWalkedFrames not implemented");
- }
-#endif
-
#ifdef _AIX
virtual uintptr_t getDataRelBase() {
_LIBUNWIND_ABORT("getDataRelBase not implemented");
@@ -973,8 +965,7 @@ class UnwindCursor : public AbstractUnwindCursor{
virtual void setFloatReg(int, unw_fpreg_t);
virtual int step(bool stage2 = false);
virtual void getInfo(unw_proc_info_t *);
- _LIBUNWIND_TRACE_NO_INLINE
- virtual void jumpto();
+ virtual void jumpto();
virtual bool isSignalFrame();
virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off);
virtual void setInfoBasedOnIPRegister(bool isReturnAddress = false);
@@ -983,10 +974,6 @@ class UnwindCursor : public AbstractUnwindCursor{
virtual void saveVFPAsX();
#endif
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
- virtual void setWalkedFrames(unsigned);
-#endif
-
#ifdef _AIX
virtual uintptr_t getDataRelBase();
#endif
@@ -1369,9 +1356,6 @@ class UnwindCursor : public AbstractUnwindCursor{
defined(_LIBUNWIND_TARGET_HAIKU)
bool _isSigReturn = false;
#endif
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
- uint32_t _walkedFrames;
-#endif
};
@@ -1426,46 +1410,7 @@ void UnwindCursor<A, R>::setFloatReg(int regNum, unw_fpreg_t value) {
}
template <typename A, typename R> void UnwindCursor<A, R>::jumpto() {
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
- /*
-
- The value of `_walkedFrames` is computed in `unwind_phase2` and represents the
- number of frames walked starting `unwind_phase2` to get to the landing pad.
-
- ```
- // uc is initialized by __unw_getcontext in the parent frame.
- // The first stack frame walked is unwind_phase2.
- unsigned framesWalked = 1;
- ```
-
- To that, we need to add the number of function calls in libunwind between
- `unwind_phase2` & `__libunwind_Registers_arm64_jumpto` which performs the long
- jump, to rebalance the execution flow.
-
- ```
- frame #0: libunwind.1.dylib`__libunwind_Registers_arm64_jumpto at UnwindRegistersRestore.S:646
- frame #1: libunwind.1.dylib`libunwind::Registers_arm64::returnto at Registers.hpp:2291:3
- frame #2: libunwind.1.dylib`libunwind::UnwindCursor<libunwind::LocalAddressSpace, libunwind::Registers_arm64>::jumpto at UnwindCursor.hpp:1474:14
- frame #3: libunwind.1.dylib`__unw_resume at libunwind.cpp:375:7
- frame #4: libunwind.1.dylib`__unw_resume_with_frames_walked at libunwind.cpp:363:10
- frame #5: libunwind.1.dylib`unwind_phase2 at UnwindLevel1.c:328:9
- frame #6: libunwind.1.dylib`_Unwind_RaiseException at UnwindLevel1.c:480:10
- frame #7: libc++abi.dylib`__cxa_throw at cxa_exception.cpp:295:5
- ...
- ```
-
- If we look at the backtrace from `__libunwind_Registers_arm64_jumpto`, we see
- there are 5 frames on the stack to reach `unwind_phase2`. However, only 4 of
- them will never return, since `__libunwind_Registers_arm64_jumpto` returns
- back to the landing pad, so we need to subtract 1 to the number of
- `_EXTRA_LIBUNWIND_FRAMES_WALKED`.
- */
-
- static constexpr size_t _EXTRA_LIBUNWIND_FRAMES_WALKED = 5 - 1;
- _registers.returnto(_walkedFrames + _EXTRA_LIBUNWIND_FRAMES_WALKED);
-#else
_registers.jumpto();
-#endif
}
#ifdef __arm__
@@ -1474,13 +1419,6 @@ template <typename A, typename R> void UnwindCursor<A, R>::saveVFPAsX() {
}
#endif
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
-template <typename A, typename R>
-void UnwindCursor<A, R>::setWalkedFrames(unsigned walkedFrames) {
- _walkedFrames = walkedFrames;
-}
-#endif
-
#ifdef _AIX
template <typename A, typename R>
uintptr_t UnwindCursor<A, R>::getDataRelBase() {
diff --git a/libunwind/src/UnwindLevel1.c b/libunwind/src/UnwindLevel1.c
index 79398bac8b531..b0cd60dfb9141 100644
--- a/libunwind/src/UnwindLevel1.c
+++ b/libunwind/src/UnwindLevel1.c
@@ -48,15 +48,16 @@
// avoided when invoking the `jumpto()` function. To do this, we use inline
// assemblies to "goto" the `jumpto()` for these architectures.
#if !defined(_LIBUNWIND_USE_CET) && !defined(_LIBUNWIND_USE_GCS)
-#define __unw_phase2_resume(cursor, payload) \
+#define __unw_phase2_resume(cursor, fn) \
do { \
- __unw_resume_with_frames_walked((cursor), (payload)); \
+ (void)fn; \
+ __unw_resume((cursor)); \
} while (0)
#elif defined(_LIBUNWIND_TARGET_I386)
#define __shstk_step_size (4)
-#define __unw_phase2_resume(cursor, payload) \
+#define __unw_phase2_resume(cursor, fn) \
do { \
- _LIBUNWIND_POP_SHSTK_SSP((payload)); \
+ _LIBUNWIND_POP_SHSTK_SSP((fn)); \
void *shstkRegContext = __libunwind_shstk_get_registers((cursor)); \
void *shstkJumpAddress = __libunwind_shstk_get_jump_target(); \
__asm__ volatile("push %%edi\n\t" \
@@ -66,9 +67,9 @@
} while (0)
#elif defined(_LIBUNWIND_TARGET_X86_64)
#define __shstk_step_size (8)
-#define __unw_phase2_resume(cursor, payload) \
+#define __unw_phase2_resume(cursor, fn) \
do { \
- _LIBUNWIND_POP_SHSTK_SSP((payload)); \
+ _LIBUNWIND_POP_SHSTK_SSP((fn)); \
void *shstkRegContext = __libunwind_shstk_get_registers((cursor)); \
void *shstkJumpAddress = __libunwind_shstk_get_jump_target(); \
__asm__ volatile("jmpq *%%rdx\n\t" ::"D"(shstkRegContext), \
@@ -76,17 +77,16 @@
} while (0)
#elif defined(_LIBUNWIND_TARGET_AARCH64)
#define __shstk_step_size (8)
-#define __unw_phase2_resume(cursor, payload) \
+#define __unw_phase2_resume(cursor, fn) \
do { \
- _LIBUNWIND_POP_SHSTK_SSP((payload)); \
+ _LIBUNWIND_POP_SHSTK_SSP((fn)); \
void *shstkRegContext = __libunwind_shstk_get_registers((cursor)); \
void *shstkJumpAddress = __libunwind_shstk_get_jump_target(); \
__asm__ volatile("mov x0, %0\n\t" \
- "mov x1, wzr\n\t" \
"br %1\n\t" \
: \
: "r"(shstkRegContext), "r"(shstkJumpAddress) \
- : "x0", "x1"); \
+ : "x0"); \
} while (0)
#endif
@@ -205,8 +205,6 @@ extern int __unw_step_stage2(unw_cursor_t *);
#if defined(_LIBUNWIND_USE_GCS)
// Enable the GCS target feature to permit gcspop instructions to be used.
__attribute__((target("+gcs")))
-#else
-_LIBUNWIND_TRACE_NO_INLINE
#endif
static _Unwind_Reason_Code
unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor,
@@ -351,8 +349,6 @@ unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor,
#if defined(_LIBUNWIND_USE_GCS)
// Enable the GCS target feature to permit gcspop instructions to be used.
__attribute__((target("+gcs")))
-#else
-_LIBUNWIND_TRACE_NO_INLINE
#endif
static _Unwind_Reason_Code
unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor,
diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S
index 76a80344034f7..fd306ed8c5230 100644
--- a/libunwind/src/UnwindRegistersRestore.S
+++ b/libunwind/src/UnwindRegistersRestore.S
@@ -645,26 +645,13 @@ Lnovec:
#endif
//
-// extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *, unsigned);
+// extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *);
//
// On entry:
// thread_state pointer is in x0
-// walked_frames counter is in x1
//
.p2align 2
DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
-
- #if defined(_LIBUNWIND_TRACE_RET_INJECT)
- cbz w1, 1f
- 0:
- subs w1, w1, #1
- adr x16, #8
- ret x16
-
- b.ne 0b
- 1:
- #endif
-
// skip restore of x0,x1 for now
ldp x2, x3, [x0, #0x010]
ldp x4, x5, [x0, #0x020]
diff --git a/libunwind/src/assembly.h b/libunwind/src/assembly.h
index 84c9d526f1d75..f0fcd006f2073 100644
--- a/libunwind/src/assembly.h
+++ b/libunwind/src/assembly.h
@@ -132,10 +132,6 @@
#if defined(__APPLE__)
-#if defined(__aarch64__) || defined(__arm64__) || defined(__arm64e__)
-#define _LIBUNWIND_TRACE_RET_INJECT 1
-#endif
-
#define SYMBOL_IS_FUNC(name)
#define HIDDEN_SYMBOL(name) .private_extern name
#if defined(_LIBUNWIND_HIDE_SYMBOLS)
diff --git a/libunwind/src/config.h b/libunwind/src/config.h
index f017403fa2234..deb5a4d4d73d4 100644
--- a/libunwind/src/config.h
+++ b/libunwind/src/config.h
@@ -28,9 +28,6 @@
#define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 1
#define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1
#endif
- #if defined(__aarch64__) || defined(__arm64__) || defined(__arm64e__)
- #define _LIBUNWIND_TRACE_RET_INJECT 1
- #endif
#elif defined(_WIN32)
#ifdef __SEH__
#define _LIBUNWIND_SUPPORT_SEH_UNWIND 1
@@ -64,12 +61,6 @@
#endif
#endif
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
-#define _LIBUNWIND_TRACE_NO_INLINE __attribute__((noinline, disable_tail_calls))
-#else
-#define _LIBUNWIND_TRACE_NO_INLINE
-#endif
-
#if defined(_LIBUNWIND_HIDE_SYMBOLS)
// The CMake file passes -fvisibility=hidden to control ELF/Mach-O visibility.
#define _LIBUNWIND_EXPORT
diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp
index b3036396c379d..4f6fed1cd933a 100644
--- a/libunwind/src/libunwind.cpp
+++ b/libunwind/src/libunwind.cpp
@@ -247,27 +247,7 @@ _LIBUNWIND_HIDDEN int __unw_get_proc_info(unw_cursor_t *cursor,
}
_LIBUNWIND_WEAK_ALIAS(__unw_get_proc_info, unw_get_proc_info)
-/// Rebalance the execution flow by injecting the right amount of `ret`
-/// instruction relatively to the amount of `walkedFrames` then resume execution
-/// at cursor position (aka longjump).
-_LIBUNWIND_HIDDEN int __unw_resume_with_frames_walked(unw_cursor_t *cursor,
- unsigned walkedFrames) {
- _LIBUNWIND_TRACE_API("__unw_resume(cursor=%p, walkedFrames=%u)",
- static_cast<void *>(cursor), walkedFrames);
-#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
- // Inform the ASan runtime that now might be a good time to clean stuff up.
- __asan_handle_no_return();
-#endif
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
- AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
- co->setWalkedFrames(walkedFrames);
-#endif
- return __unw_resume(cursor);
-}
-_LIBUNWIND_WEAK_ALIAS(__unw_resume_with_frames_walked,
- unw_resume_with_frames_walked)
-
-/// Legacy function. Resume execution at cursor position (aka longjump).
+/// Resume execution at cursor position (aka longjump).
_LIBUNWIND_HIDDEN int __unw_resume(unw_cursor_t *cursor) {
_LIBUNWIND_TRACE_API("__unw_resume(cursor=%p)", static_cast<void *>(cursor));
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
diff --git a/libunwind/src/libunwind_ext.h b/libunwind/src/libunwind_ext.h
index f5da90d7bd3b7..ed503ceb70c5a 100644
--- a/libunwind/src/libunwind_ext.h
+++ b/libunwind/src/libunwind_ext.h
@@ -30,11 +30,7 @@ extern int __unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *);
extern int __unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *);
extern int __unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t);
extern int __unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t);
-_LIBUNWIND_TRACE_NO_INLINE
- extern int __unw_resume_with_frames_walked(unw_cursor_t *, unsigned);
-// `__unw_resume` is a legacy function. Use `__unw_resume_with_frames_walked` instead.
-_LIBUNWIND_TRACE_NO_INLINE
- extern int __unw_resume(unw_cursor_t *);
+extern int __unw_resume(unw_cursor_t *);
#ifdef __arm__
/* Save VFP registers in FSTMX format (instead of FSTMD). */
diff --git a/lldb/packages/Python/lldbsuite/test/decorators.py b/lldb/packages/Python/lldbsuite/test/decorators.py
index 23d2165e07f7e..454196e1b0264 100644
--- a/lldb/packages/Python/lldbsuite/test/decorators.py
+++ b/lldb/packages/Python/lldbsuite/test/decorators.py
@@ -647,31 +647,6 @@ def is_out_of_tree_debugserver():
return skipTestIfFn(is_out_of_tree_debugserver)(func)
-def skipIfOutOfTreeLibunwind(func):
- """Decorate the item to skip tests if libunwind was not built in-tree."""
-
- def is_out_of_tree_libunwind():
- if not configuration.llvm_tools_dir:
- return "out-of-tree libunwind"
-
- # llvm_tools_dir is typically <build>/bin, so lib is a sibling.
- llvm_lib_dir = os.path.join(
- os.path.dirname(configuration.llvm_tools_dir), "lib"
- )
-
- if not os.path.isdir(llvm_lib_dir):
- return "out-of-tree libunwind"
-
- # Check for libunwind library (any extension).
- for filename in os.listdir(llvm_lib_dir):
- if filename.startswith("libunwind.") or filename.startswith("unwind."):
- return None
-
- return "out-of-tree libunwind"
-
- return skipTestIfFn(is_out_of_tree_libunwind)(func)
-
-
def skipIfRemote(func):
"""Decorate the item to skip tests if testing remotely."""
return unittest.skipIf(lldb.remote_platform, "skip on remote platform")(func)
diff --git a/lldb/test/API/functionalities/unwind/libunwind_ret_injection/Makefile b/lldb/test/API/functionalities/unwind/libunwind_ret_injection/Makefile
deleted file mode 100644
index 4698eaa815b83..0000000000000
--- a/lldb/test/API/functionalities/unwind/libunwind_ret_injection/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-CXX_SOURCES := main.cpp
-
-# Build with C++ exceptions enabled
-CXXFLAGS := -g -O0 -fexceptions
-
-include Makefile.rules
diff --git a/lldb/test/API/functionalities/unwind/libunwind_ret_injection/TestLibUnwindRetInjection.py b/lldb/test/API/functionalities/unwind/libunwind_ret_injection/TestLibUnwindRetInjection.py
deleted file mode 100644
index e03234d1b5077..0000000000000
--- a/lldb/test/API/functionalities/unwind/libunwind_ret_injection/TestLibUnwindRetInjection.py
+++ /dev/null
@@ -1,177 +0,0 @@
-"""
-Test that libunwind correctly injects 'ret' instructions to rebalance execution flow
-when unwinding C++ exceptions. This is important for Apple Processor Trace analysis.
-"""
-
-import lldb
-import os
-from lldbsuite.test.decorators import *
-from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbutil
-from lldbsuite.test import configuration
-
-
-class LibunwindRetInjectionTestCase(TestBase):
- @skipIf(archs=no_match(["arm64", "arm64e", "aarch64"]))
- @skipUnlessDarwin
- @skipIfOutOfTreeLibunwind
- def test_ret_injection_on_exception_unwind(self):
- """Test that __libunwind_Registers_arm64_jumpto receives correct walkedFrames count and injects the right number of ret instructions."""
- self.build()
-
- exe = self.getBuildArtifact("a.out")
- target = self.dbg.CreateTarget(exe)
- self.assertTrue(target, VALID_TARGET)
-
- # Find the just-built libunwind, not the system one.
- # llvm_tools_dir is typically <build>/bin, so lib is a sibling.
- self.assertIsNotNone(
- configuration.llvm_tools_dir,
- "llvm_tools_dir must be set to find in-tree libunwind",
- )
-
- llvm_lib_dir = os.path.join(
- os.path.dirname(configuration.llvm_tools_dir), "lib"
- )
-
- # Find the libunwind library (platform-agnostic).
- libunwind_path = None
- for filename in os.listdir(llvm_lib_dir):
- if filename.startswith("libunwind.") or filename.startswith("unwind."):
- libunwind_path = os.path.join(llvm_lib_dir, filename)
- break
-
- self.assertIsNotNone(
- libunwind_path, f"Could not find libunwind in {llvm_lib_dir}"
- )
-
- # Set breakpoint in __libunwind_Registers_arm64_jumpto.
- # This is the function that performs the actual jump and ret injection.
- bp = target.BreakpointCreateByName("__libunwind_Registers_arm64_jumpto")
- self.assertTrue(bp.IsValid())
- self.assertGreater(bp.GetNumLocations(), 0)
-
- # Set up DYLD_INSERT_LIBRARIES to use the just-built libunwind.
- launch_info = lldb.SBLaunchInfo(None)
- env = target.GetEnvironment()
- env.Set("DYLD_INSERT_LIBRARIES", libunwind_path, True)
- launch_info.SetEnvironment(env, False)
-
- # Launch the process with our custom libunwind.
- error = lldb.SBError()
- process = target.Launch(launch_info, error)
- self.assertSuccess(
- error, f"Failed to launch process with libunwind at {libunwind_path}"
- )
- self.assertTrue(process, PROCESS_IS_VALID)
-
- # We should hit the breakpoint in __libunwind_Registers_arm64_jumpto
- # during the exception unwinding phase 2.
- threads = lldbutil.get_threads_stopped_at_breakpoint(process, bp)
- self.assertEqual(len(threads), 1, "Should have stopped at breakpoint")
-
- thread = threads[0]
- frame = thread.GetFrameAtIndex(0)
-
- # Verify we're in __libunwind_Registers_arm64_jumpto.
- function_name = frame.GetFunctionName()
- ...
[truncated]
|
|
@llvm/pr-subscribers-libunwind Author: Sylvestre Ledru (sylvestre) ChangesReverts llvm/llvm-project#165066 Patch is 26.00 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/167659.diff 12 Files Affected:
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index 28649fafb23d5..9d4c8344150f6 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -1832,9 +1832,8 @@ inline const char *Registers_ppc64::getRegisterName(int regNum) {
/// Registers_arm64 holds the register state of a thread in a 64-bit arm
/// process.
class _LIBUNWIND_HIDDEN Registers_arm64;
+extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *);
extern "C" int64_t __libunwind_Registers_arm64_za_disable();
-extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *,
- unsigned walkedFrames);
#if defined(_LIBUNWIND_USE_GCS)
extern "C" void *__libunwind_shstk_get_jump_target() {
@@ -1862,17 +1861,10 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
v128 getVectorRegister(int num) const;
void setVectorRegister(int num, v128 value);
static const char *getRegisterName(int num);
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
- _LIBUNWIND_TRACE_NO_INLINE
- void returnto(unsigned walkedFrames) {
- __libunwind_Registers_arm64_jumpto(this, walkedFrames);
- }
-#else
- void jumpto() {
- zaDisable();
- __libunwind_Registers_arm64_jumpto(this, 0);
+ void jumpto() {
+ zaDisable();
+ __libunwind_Registers_arm64_jumpto(this);
}
-#endif
static constexpr int lastDwarfRegNum() {
return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64;
}
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index d7348254af07b..7ec5f9e91578a 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -472,9 +472,7 @@ class _LIBUNWIND_HIDDEN AbstractUnwindCursor {
virtual void getInfo(unw_proc_info_t *) {
_LIBUNWIND_ABORT("getInfo not implemented");
}
- _LIBUNWIND_TRACE_NO_INLINE virtual void jumpto() {
- _LIBUNWIND_ABORT("jumpto not implemented");
- }
+ virtual void jumpto() { _LIBUNWIND_ABORT("jumpto not implemented"); }
virtual bool isSignalFrame() {
_LIBUNWIND_ABORT("isSignalFrame not implemented");
}
@@ -491,12 +489,6 @@ class _LIBUNWIND_HIDDEN AbstractUnwindCursor {
virtual void saveVFPAsX() { _LIBUNWIND_ABORT("saveVFPAsX not implemented"); }
#endif
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
- virtual void setWalkedFrames(unsigned) {
- _LIBUNWIND_ABORT("setWalkedFrames not implemented");
- }
-#endif
-
#ifdef _AIX
virtual uintptr_t getDataRelBase() {
_LIBUNWIND_ABORT("getDataRelBase not implemented");
@@ -973,8 +965,7 @@ class UnwindCursor : public AbstractUnwindCursor{
virtual void setFloatReg(int, unw_fpreg_t);
virtual int step(bool stage2 = false);
virtual void getInfo(unw_proc_info_t *);
- _LIBUNWIND_TRACE_NO_INLINE
- virtual void jumpto();
+ virtual void jumpto();
virtual bool isSignalFrame();
virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off);
virtual void setInfoBasedOnIPRegister(bool isReturnAddress = false);
@@ -983,10 +974,6 @@ class UnwindCursor : public AbstractUnwindCursor{
virtual void saveVFPAsX();
#endif
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
- virtual void setWalkedFrames(unsigned);
-#endif
-
#ifdef _AIX
virtual uintptr_t getDataRelBase();
#endif
@@ -1369,9 +1356,6 @@ class UnwindCursor : public AbstractUnwindCursor{
defined(_LIBUNWIND_TARGET_HAIKU)
bool _isSigReturn = false;
#endif
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
- uint32_t _walkedFrames;
-#endif
};
@@ -1426,46 +1410,7 @@ void UnwindCursor<A, R>::setFloatReg(int regNum, unw_fpreg_t value) {
}
template <typename A, typename R> void UnwindCursor<A, R>::jumpto() {
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
- /*
-
- The value of `_walkedFrames` is computed in `unwind_phase2` and represents the
- number of frames walked starting `unwind_phase2` to get to the landing pad.
-
- ```
- // uc is initialized by __unw_getcontext in the parent frame.
- // The first stack frame walked is unwind_phase2.
- unsigned framesWalked = 1;
- ```
-
- To that, we need to add the number of function calls in libunwind between
- `unwind_phase2` & `__libunwind_Registers_arm64_jumpto` which performs the long
- jump, to rebalance the execution flow.
-
- ```
- frame #0: libunwind.1.dylib`__libunwind_Registers_arm64_jumpto at UnwindRegistersRestore.S:646
- frame #1: libunwind.1.dylib`libunwind::Registers_arm64::returnto at Registers.hpp:2291:3
- frame #2: libunwind.1.dylib`libunwind::UnwindCursor<libunwind::LocalAddressSpace, libunwind::Registers_arm64>::jumpto at UnwindCursor.hpp:1474:14
- frame #3: libunwind.1.dylib`__unw_resume at libunwind.cpp:375:7
- frame #4: libunwind.1.dylib`__unw_resume_with_frames_walked at libunwind.cpp:363:10
- frame #5: libunwind.1.dylib`unwind_phase2 at UnwindLevel1.c:328:9
- frame #6: libunwind.1.dylib`_Unwind_RaiseException at UnwindLevel1.c:480:10
- frame #7: libc++abi.dylib`__cxa_throw at cxa_exception.cpp:295:5
- ...
- ```
-
- If we look at the backtrace from `__libunwind_Registers_arm64_jumpto`, we see
- there are 5 frames on the stack to reach `unwind_phase2`. However, only 4 of
- them will never return, since `__libunwind_Registers_arm64_jumpto` returns
- back to the landing pad, so we need to subtract 1 to the number of
- `_EXTRA_LIBUNWIND_FRAMES_WALKED`.
- */
-
- static constexpr size_t _EXTRA_LIBUNWIND_FRAMES_WALKED = 5 - 1;
- _registers.returnto(_walkedFrames + _EXTRA_LIBUNWIND_FRAMES_WALKED);
-#else
_registers.jumpto();
-#endif
}
#ifdef __arm__
@@ -1474,13 +1419,6 @@ template <typename A, typename R> void UnwindCursor<A, R>::saveVFPAsX() {
}
#endif
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
-template <typename A, typename R>
-void UnwindCursor<A, R>::setWalkedFrames(unsigned walkedFrames) {
- _walkedFrames = walkedFrames;
-}
-#endif
-
#ifdef _AIX
template <typename A, typename R>
uintptr_t UnwindCursor<A, R>::getDataRelBase() {
diff --git a/libunwind/src/UnwindLevel1.c b/libunwind/src/UnwindLevel1.c
index 79398bac8b531..b0cd60dfb9141 100644
--- a/libunwind/src/UnwindLevel1.c
+++ b/libunwind/src/UnwindLevel1.c
@@ -48,15 +48,16 @@
// avoided when invoking the `jumpto()` function. To do this, we use inline
// assemblies to "goto" the `jumpto()` for these architectures.
#if !defined(_LIBUNWIND_USE_CET) && !defined(_LIBUNWIND_USE_GCS)
-#define __unw_phase2_resume(cursor, payload) \
+#define __unw_phase2_resume(cursor, fn) \
do { \
- __unw_resume_with_frames_walked((cursor), (payload)); \
+ (void)fn; \
+ __unw_resume((cursor)); \
} while (0)
#elif defined(_LIBUNWIND_TARGET_I386)
#define __shstk_step_size (4)
-#define __unw_phase2_resume(cursor, payload) \
+#define __unw_phase2_resume(cursor, fn) \
do { \
- _LIBUNWIND_POP_SHSTK_SSP((payload)); \
+ _LIBUNWIND_POP_SHSTK_SSP((fn)); \
void *shstkRegContext = __libunwind_shstk_get_registers((cursor)); \
void *shstkJumpAddress = __libunwind_shstk_get_jump_target(); \
__asm__ volatile("push %%edi\n\t" \
@@ -66,9 +67,9 @@
} while (0)
#elif defined(_LIBUNWIND_TARGET_X86_64)
#define __shstk_step_size (8)
-#define __unw_phase2_resume(cursor, payload) \
+#define __unw_phase2_resume(cursor, fn) \
do { \
- _LIBUNWIND_POP_SHSTK_SSP((payload)); \
+ _LIBUNWIND_POP_SHSTK_SSP((fn)); \
void *shstkRegContext = __libunwind_shstk_get_registers((cursor)); \
void *shstkJumpAddress = __libunwind_shstk_get_jump_target(); \
__asm__ volatile("jmpq *%%rdx\n\t" ::"D"(shstkRegContext), \
@@ -76,17 +77,16 @@
} while (0)
#elif defined(_LIBUNWIND_TARGET_AARCH64)
#define __shstk_step_size (8)
-#define __unw_phase2_resume(cursor, payload) \
+#define __unw_phase2_resume(cursor, fn) \
do { \
- _LIBUNWIND_POP_SHSTK_SSP((payload)); \
+ _LIBUNWIND_POP_SHSTK_SSP((fn)); \
void *shstkRegContext = __libunwind_shstk_get_registers((cursor)); \
void *shstkJumpAddress = __libunwind_shstk_get_jump_target(); \
__asm__ volatile("mov x0, %0\n\t" \
- "mov x1, wzr\n\t" \
"br %1\n\t" \
: \
: "r"(shstkRegContext), "r"(shstkJumpAddress) \
- : "x0", "x1"); \
+ : "x0"); \
} while (0)
#endif
@@ -205,8 +205,6 @@ extern int __unw_step_stage2(unw_cursor_t *);
#if defined(_LIBUNWIND_USE_GCS)
// Enable the GCS target feature to permit gcspop instructions to be used.
__attribute__((target("+gcs")))
-#else
-_LIBUNWIND_TRACE_NO_INLINE
#endif
static _Unwind_Reason_Code
unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor,
@@ -351,8 +349,6 @@ unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor,
#if defined(_LIBUNWIND_USE_GCS)
// Enable the GCS target feature to permit gcspop instructions to be used.
__attribute__((target("+gcs")))
-#else
-_LIBUNWIND_TRACE_NO_INLINE
#endif
static _Unwind_Reason_Code
unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor,
diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S
index 76a80344034f7..fd306ed8c5230 100644
--- a/libunwind/src/UnwindRegistersRestore.S
+++ b/libunwind/src/UnwindRegistersRestore.S
@@ -645,26 +645,13 @@ Lnovec:
#endif
//
-// extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *, unsigned);
+// extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *);
//
// On entry:
// thread_state pointer is in x0
-// walked_frames counter is in x1
//
.p2align 2
DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
-
- #if defined(_LIBUNWIND_TRACE_RET_INJECT)
- cbz w1, 1f
- 0:
- subs w1, w1, #1
- adr x16, #8
- ret x16
-
- b.ne 0b
- 1:
- #endif
-
// skip restore of x0,x1 for now
ldp x2, x3, [x0, #0x010]
ldp x4, x5, [x0, #0x020]
diff --git a/libunwind/src/assembly.h b/libunwind/src/assembly.h
index 84c9d526f1d75..f0fcd006f2073 100644
--- a/libunwind/src/assembly.h
+++ b/libunwind/src/assembly.h
@@ -132,10 +132,6 @@
#if defined(__APPLE__)
-#if defined(__aarch64__) || defined(__arm64__) || defined(__arm64e__)
-#define _LIBUNWIND_TRACE_RET_INJECT 1
-#endif
-
#define SYMBOL_IS_FUNC(name)
#define HIDDEN_SYMBOL(name) .private_extern name
#if defined(_LIBUNWIND_HIDE_SYMBOLS)
diff --git a/libunwind/src/config.h b/libunwind/src/config.h
index f017403fa2234..deb5a4d4d73d4 100644
--- a/libunwind/src/config.h
+++ b/libunwind/src/config.h
@@ -28,9 +28,6 @@
#define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 1
#define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1
#endif
- #if defined(__aarch64__) || defined(__arm64__) || defined(__arm64e__)
- #define _LIBUNWIND_TRACE_RET_INJECT 1
- #endif
#elif defined(_WIN32)
#ifdef __SEH__
#define _LIBUNWIND_SUPPORT_SEH_UNWIND 1
@@ -64,12 +61,6 @@
#endif
#endif
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
-#define _LIBUNWIND_TRACE_NO_INLINE __attribute__((noinline, disable_tail_calls))
-#else
-#define _LIBUNWIND_TRACE_NO_INLINE
-#endif
-
#if defined(_LIBUNWIND_HIDE_SYMBOLS)
// The CMake file passes -fvisibility=hidden to control ELF/Mach-O visibility.
#define _LIBUNWIND_EXPORT
diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp
index b3036396c379d..4f6fed1cd933a 100644
--- a/libunwind/src/libunwind.cpp
+++ b/libunwind/src/libunwind.cpp
@@ -247,27 +247,7 @@ _LIBUNWIND_HIDDEN int __unw_get_proc_info(unw_cursor_t *cursor,
}
_LIBUNWIND_WEAK_ALIAS(__unw_get_proc_info, unw_get_proc_info)
-/// Rebalance the execution flow by injecting the right amount of `ret`
-/// instruction relatively to the amount of `walkedFrames` then resume execution
-/// at cursor position (aka longjump).
-_LIBUNWIND_HIDDEN int __unw_resume_with_frames_walked(unw_cursor_t *cursor,
- unsigned walkedFrames) {
- _LIBUNWIND_TRACE_API("__unw_resume(cursor=%p, walkedFrames=%u)",
- static_cast<void *>(cursor), walkedFrames);
-#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
- // Inform the ASan runtime that now might be a good time to clean stuff up.
- __asan_handle_no_return();
-#endif
-#ifdef _LIBUNWIND_TRACE_RET_INJECT
- AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
- co->setWalkedFrames(walkedFrames);
-#endif
- return __unw_resume(cursor);
-}
-_LIBUNWIND_WEAK_ALIAS(__unw_resume_with_frames_walked,
- unw_resume_with_frames_walked)
-
-/// Legacy function. Resume execution at cursor position (aka longjump).
+/// Resume execution at cursor position (aka longjump).
_LIBUNWIND_HIDDEN int __unw_resume(unw_cursor_t *cursor) {
_LIBUNWIND_TRACE_API("__unw_resume(cursor=%p)", static_cast<void *>(cursor));
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
diff --git a/libunwind/src/libunwind_ext.h b/libunwind/src/libunwind_ext.h
index f5da90d7bd3b7..ed503ceb70c5a 100644
--- a/libunwind/src/libunwind_ext.h
+++ b/libunwind/src/libunwind_ext.h
@@ -30,11 +30,7 @@ extern int __unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *);
extern int __unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *);
extern int __unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t);
extern int __unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t);
-_LIBUNWIND_TRACE_NO_INLINE
- extern int __unw_resume_with_frames_walked(unw_cursor_t *, unsigned);
-// `__unw_resume` is a legacy function. Use `__unw_resume_with_frames_walked` instead.
-_LIBUNWIND_TRACE_NO_INLINE
- extern int __unw_resume(unw_cursor_t *);
+extern int __unw_resume(unw_cursor_t *);
#ifdef __arm__
/* Save VFP registers in FSTMX format (instead of FSTMD). */
diff --git a/lldb/packages/Python/lldbsuite/test/decorators.py b/lldb/packages/Python/lldbsuite/test/decorators.py
index 23d2165e07f7e..454196e1b0264 100644
--- a/lldb/packages/Python/lldbsuite/test/decorators.py
+++ b/lldb/packages/Python/lldbsuite/test/decorators.py
@@ -647,31 +647,6 @@ def is_out_of_tree_debugserver():
return skipTestIfFn(is_out_of_tree_debugserver)(func)
-def skipIfOutOfTreeLibunwind(func):
- """Decorate the item to skip tests if libunwind was not built in-tree."""
-
- def is_out_of_tree_libunwind():
- if not configuration.llvm_tools_dir:
- return "out-of-tree libunwind"
-
- # llvm_tools_dir is typically <build>/bin, so lib is a sibling.
- llvm_lib_dir = os.path.join(
- os.path.dirname(configuration.llvm_tools_dir), "lib"
- )
-
- if not os.path.isdir(llvm_lib_dir):
- return "out-of-tree libunwind"
-
- # Check for libunwind library (any extension).
- for filename in os.listdir(llvm_lib_dir):
- if filename.startswith("libunwind.") or filename.startswith("unwind."):
- return None
-
- return "out-of-tree libunwind"
-
- return skipTestIfFn(is_out_of_tree_libunwind)(func)
-
-
def skipIfRemote(func):
"""Decorate the item to skip tests if testing remotely."""
return unittest.skipIf(lldb.remote_platform, "skip on remote platform")(func)
diff --git a/lldb/test/API/functionalities/unwind/libunwind_ret_injection/Makefile b/lldb/test/API/functionalities/unwind/libunwind_ret_injection/Makefile
deleted file mode 100644
index 4698eaa815b83..0000000000000
--- a/lldb/test/API/functionalities/unwind/libunwind_ret_injection/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-CXX_SOURCES := main.cpp
-
-# Build with C++ exceptions enabled
-CXXFLAGS := -g -O0 -fexceptions
-
-include Makefile.rules
diff --git a/lldb/test/API/functionalities/unwind/libunwind_ret_injection/TestLibUnwindRetInjection.py b/lldb/test/API/functionalities/unwind/libunwind_ret_injection/TestLibUnwindRetInjection.py
deleted file mode 100644
index e03234d1b5077..0000000000000
--- a/lldb/test/API/functionalities/unwind/libunwind_ret_injection/TestLibUnwindRetInjection.py
+++ /dev/null
@@ -1,177 +0,0 @@
-"""
-Test that libunwind correctly injects 'ret' instructions to rebalance execution flow
-when unwinding C++ exceptions. This is important for Apple Processor Trace analysis.
-"""
-
-import lldb
-import os
-from lldbsuite.test.decorators import *
-from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbutil
-from lldbsuite.test import configuration
-
-
-class LibunwindRetInjectionTestCase(TestBase):
- @skipIf(archs=no_match(["arm64", "arm64e", "aarch64"]))
- @skipUnlessDarwin
- @skipIfOutOfTreeLibunwind
- def test_ret_injection_on_exception_unwind(self):
- """Test that __libunwind_Registers_arm64_jumpto receives correct walkedFrames count and injects the right number of ret instructions."""
- self.build()
-
- exe = self.getBuildArtifact("a.out")
- target = self.dbg.CreateTarget(exe)
- self.assertTrue(target, VALID_TARGET)
-
- # Find the just-built libunwind, not the system one.
- # llvm_tools_dir is typically <build>/bin, so lib is a sibling.
- self.assertIsNotNone(
- configuration.llvm_tools_dir,
- "llvm_tools_dir must be set to find in-tree libunwind",
- )
-
- llvm_lib_dir = os.path.join(
- os.path.dirname(configuration.llvm_tools_dir), "lib"
- )
-
- # Find the libunwind library (platform-agnostic).
- libunwind_path = None
- for filename in os.listdir(llvm_lib_dir):
- if filename.startswith("libunwind.") or filename.startswith("unwind."):
- libunwind_path = os.path.join(llvm_lib_dir, filename)
- break
-
- self.assertIsNotNone(
- libunwind_path, f"Could not find libunwind in {llvm_lib_dir}"
- )
-
- # Set breakpoint in __libunwind_Registers_arm64_jumpto.
- # This is the function that performs the actual jump and ret injection.
- bp = target.BreakpointCreateByName("__libunwind_Registers_arm64_jumpto")
- self.assertTrue(bp.IsValid())
- self.assertGreater(bp.GetNumLocations(), 0)
-
- # Set up DYLD_INSERT_LIBRARIES to use the just-built libunwind.
- launch_info = lldb.SBLaunchInfo(None)
- env = target.GetEnvironment()
- env.Set("DYLD_INSERT_LIBRARIES", libunwind_path, True)
- launch_info.SetEnvironment(env, False)
-
- # Launch the process with our custom libunwind.
- error = lldb.SBError()
- process = target.Launch(launch_info, error)
- self.assertSuccess(
- error, f"Failed to launch process with libunwind at {libunwind_path}"
- )
- self.assertTrue(process, PROCESS_IS_VALID)
-
- # We should hit the breakpoint in __libunwind_Registers_arm64_jumpto
- # during the exception unwinding phase 2.
- threads = lldbutil.get_threads_stopped_at_breakpoint(process, bp)
- self.assertEqual(len(threads), 1, "Should have stopped at breakpoint")
-
- thread = threads[0]
- frame = thread.GetFrameAtIndex(0)
-
- # Verify we're in __libunwind_Registers_arm64_jumpto.
- function_name = frame.GetFunctionName()
- ...
[truncated]
|
You can test this locally with the following command:git-clang-format --diff origin/main HEAD --extensions hpp,cpp,h,c -- libunwind/src/Registers.hpp libunwind/src/UnwindCursor.hpp libunwind/src/UnwindLevel1.c libunwind/src/assembly.h libunwind/src/config.h libunwind/src/libunwind.cpp libunwind/src/libunwind_ext.h --diff_from_common_commit
View the diff from clang-format here.diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 7ec5f9e91..b4c5bbcaf 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -965,7 +965,7 @@ public:
virtual void setFloatReg(int, unw_fpreg_t);
virtual int step(bool stage2 = false);
virtual void getInfo(unw_proc_info_t *);
- virtual void jumpto();
+ virtual void jumpto();
virtual bool isSignalFrame();
virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off);
virtual void setInfoBasedOnIPRegister(bool isReturnAddress = false);
diff --git a/libunwind/src/UnwindLevel1.c b/libunwind/src/UnwindLevel1.c
index b0cd60dfb..3887d6c51 100644
--- a/libunwind/src/UnwindLevel1.c
+++ b/libunwind/src/UnwindLevel1.c
@@ -206,9 +206,9 @@ extern int __unw_step_stage2(unw_cursor_t *);
// Enable the GCS target feature to permit gcspop instructions to be used.
__attribute__((target("+gcs")))
#endif
-static _Unwind_Reason_Code
-unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor,
- _Unwind_Exception *exception_object) {
+static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc,
+ unw_cursor_t *cursor,
+ _Unwind_Exception *exception_object) {
__unw_init_local(cursor, uc);
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_obj=%p)",
diff --git a/libunwind/src/config.h b/libunwind/src/config.h
index deb5a4d4d..bcf9173d9 100644
--- a/libunwind/src/config.h
+++ b/libunwind/src/config.h
@@ -27,7 +27,7 @@
#else
#define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 1
#define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1
- #endif
+#endif
#elif defined(_WIN32)
#ifdef __SEH__
#define _LIBUNWIND_SUPPORT_SEH_UNWIND 1
|
|
@sylvestre #167743 should fix the issue. Merging it as soon as it passes the PR testing. |
|
Thank you |
Reverts #165066
for causing
#167658