diff --git a/src/coreclr/pal/inc/unixasmmacrosarm64.inc b/src/coreclr/pal/inc/unixasmmacrosarm64.inc
index 8b3c5803b7d71f..976cc825f2eb40 100644
--- a/src/coreclr/pal/inc/unixasmmacrosarm64.inc
+++ b/src/coreclr/pal/inc/unixasmmacrosarm64.inc
@@ -359,3 +359,20 @@ $__RedirectionStubEndFuncName
 #endif
 
 .endm
+
+//-----------------------------------------------------------------------------
+// Macro used to check (in debug builds only) whether the stack is 16-bytes aligned (a requirement before calling
+// out into C++/OS code). Invoke this directly after your prolog (if the stack frame size is fixed) or directly
+// before a call (if you have a frame pointer and a dynamic stack). A breakpoint will be invoked if the stack
+// is misaligned.
+//
+.macro  CHECK_STACK_ALIGNMENT
+
+#ifdef _DEBUG
+        add     x9, sp, xzr
+        tst     x9, #15
+        beq     0f
+        EMIT_BREAKPOINT
+0:
+#endif
+.endm
diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt
index 1756d35a86b219..16ae71c162a472 100644
--- a/src/coreclr/vm/CMakeLists.txt
+++ b/src/coreclr/vm/CMakeLists.txt
@@ -704,6 +704,7 @@ else(CLR_CMAKE_TARGET_WIN32)
             ${ARCH_SOURCES_DIR}/jithelpers_singleappdomain.S
             ${ARCH_SOURCES_DIR}/jithelpers_slow.S
             ${ARCH_SOURCES_DIR}/pinvokestubs.S
+            ${ARCH_SOURCES_DIR}/redirectedhandledjitcase.S
             ${ARCH_SOURCES_DIR}/theprestubamd64.S
             ${ARCH_SOURCES_DIR}/thunktemplates.S
             ${ARCH_SOURCES_DIR}/unixasmhelpers.S
diff --git a/src/coreclr/vm/amd64/redirectedhandledjitcase.S b/src/coreclr/vm/amd64/redirectedhandledjitcase.S
new file mode 100644
index 00000000000000..23de3d6fee93ca
--- /dev/null
+++ b/src/coreclr/vm/amd64/redirectedhandledjitcase.S
@@ -0,0 +1,96 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+.intel_syntax noprefix
+#include "unixasmmacros.inc"
+#include "asmconstants.h"
+
+#define FaultingExceptionFrame_StackAlloc (SIZEOF__GSCookie + SIZEOF__FaultingExceptionFrame)
+#define FaultingExceptionFrame_FrameOffset SIZEOF__GSCookie
+#define OFFSET_OF_FRAME (8 + SIZEOF_GSCookie)
+
+.macro GenerateRedirectedStubWithFrame stub, target
+
+    //
+    // This is the primary function to which execution will be redirected to.
+    //
+    NESTED_ENTRY \stub, _TEXT, NoHandler
+
+        //
+        // IN: rdi: original IP before redirect
+        //     rsi: Rip from the Thread::GetAbortContext()
+        //
+
+        mov             rdx, rsp
+
+        // This push of the return address must not be recorded in the unwind
+        // info.  After this push, unwinding will work.
+        push            rdi
+
+        test            rsp, 0x0f
+        jnz             C_FUNC(\stub\()_FixRsp)
+
+LOCAL_LABEL(\stub\()_RspAligned):
+
+        // Any stack operations hereafter must be recorded in the unwind info, but
+        // only nonvolatile register locations are needed.  Anything else is only
+        // a "sub rsp, 8" to the unwinder.
+
+        alloc_stack     OFFSET_OF_FRAME + SIZEOF__FaultingExceptionFrame
+
+        END_PROLOGUE
+
+        lea             rdi, [rsp + OFFSET_OF_FRAME]
+
+        mov             dword ptr [rdi], 0                                                          // Initialize vtbl (it is not strictly necessary)
+        mov             dword ptr [rdi + OFFSETOF__FaultingExceptionFrame__m_fFilterExecuted], 0    // Initialize BOOL for personality routine
+
+        call C_FUNC(\target)
+
+        // Target should not return.
+        int             3
+
+    NESTED_END \stub, _TEXT
+
+// This function is used by the stub above to adjust the stack alignment.  The
+// stub can't conditionally push something on the stack because the unwind
+// encodings have no way to express that.
+//
+// CONSIDER: we could move the frame pointer above the FaultingExceptionFrame,
+// and detect the misalignment adjustment in
+// GetFrameFromRedirectedStubStackFrame.  This is probably less code and more
+// straightforward.
+LEAF_ENTRY \stub\()_FixRsp, _TEXT
+
+        call            LOCAL_LABEL(\stub\()_RspAligned)
+
+        // Target should not return.
+        int             3
+
+LEAF_END \stub\()_FixRsp, _TEXT
+
+.endm
+
+REDIRECT_FOR_THROW_CONTROL_FRAME_SIZE = 8
+
+NESTED_ENTRY RedirectForThrowControl2, _TEXT, NoHandler
+
+        // On entry
+        // rdi -> FaultingExceptionFrame
+        // rsi -> Rip from the Thread::GetAbortContext()
+        // rdx -> Original RSP
+
+        push_register   rax // Align stack
+
+        END_PROLOGUE
+
+        mov             [rdx - 8], rsi // Set return address slot to the Rip from the Thread::GetAbortContext()
+
+        call            C_FUNC(ThrowControlForThread)
+
+        // ThrowControlForThread doesn't return.
+        int             3
+
+NESTED_END RedirectForThrowControl2, _TEXT
+
+GenerateRedirectedStubWithFrame RedirectForThrowControl, RedirectForThrowControl2
diff --git a/src/coreclr/vm/amd64/unixstubs.cpp b/src/coreclr/vm/amd64/unixstubs.cpp
index e818594f6aa51d..0edb1aef92cc3e 100644
--- a/src/coreclr/vm/amd64/unixstubs.cpp
+++ b/src/coreclr/vm/amd64/unixstubs.cpp
@@ -5,11 +5,6 @@
 
 extern "C"
 {
-    void RedirectForThrowControl()
-    {
-        PORTABILITY_ASSERT("Implement for PAL");
-    }
-
     void STDMETHODCALLTYPE JIT_ProfilerEnterLeaveTailcallStub(UINT_PTR ProfilerHandle)
     {
     }
diff --git a/src/coreclr/vm/arm/ehhelpers.S b/src/coreclr/vm/arm/ehhelpers.S
index df6892cdc90d1b..6f44931ebe2be8 100644
--- a/src/coreclr/vm/arm/ehhelpers.S
+++ b/src/coreclr/vm/arm/ehhelpers.S
@@ -67,6 +67,31 @@ OFFSET_OF_FRAME=(4 + SIZEOF__GSCookie)
 
         .endm
 
+// ------------------------------------------------------------------
+//
+// Helpers for ThreadAbort exceptions
+//
+
+        NESTED_ENTRY RedirectForThreadAbort2, _TEXT, NoHandler
+        PROLOG_PUSH  "{r7, lr}"
+
+        // stack must be 8 byte aligned
+        CHECK_STACK_ALIGNMENT
+
+        // On entry:
+        //
+        // r0 = address of FaultingExceptionFrame
+        //
+        // Invoke the helper to setup the FaultingExceptionFrame and raise the exception
+        bl              C_FUNC(ThrowControlForThread)
+
+        // ThrowControlForThread doesn't return.
+        EMIT_BREAKPOINT
+
+        NESTED_END RedirectForThreadAbort2, _TEXT
+
+GenerateRedirectedStubWithFrame RedirectForThreadAbort, RedirectForThreadAbort2
+
 // ------------------------------------------------------------------
 
         // This helper enables us to call into a funclet after applying the non-volatiles
diff --git a/src/coreclr/vm/arm/unixstubs.cpp b/src/coreclr/vm/arm/unixstubs.cpp
index 9b313f8475f68c..878d5b003686b6 100644
--- a/src/coreclr/vm/arm/unixstubs.cpp
+++ b/src/coreclr/vm/arm/unixstubs.cpp
@@ -2,16 +2,3 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 #include "common.h"
-
-extern "C"
-{
-    void RedirectForThrowControl()
-    {
-        PORTABILITY_ASSERT("Implement for PAL");
-    }
-
-    void RedirectForThreadAbort()
-    {
-        PORTABILITY_ASSERT("Implement for PAL");
-    }
-};
diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S
index cbe14485e8df43..cdbe24ec427a98 100644
--- a/src/coreclr/vm/arm64/asmhelpers.S
+++ b/src/coreclr/vm/arm64/asmhelpers.S
@@ -697,6 +697,30 @@ NESTED_END CallEHFilterFunclet, _TEXT
 
 .endm
 
+// ------------------------------------------------------------------
+//
+// Helpers for ThreadAbort exceptions
+//
+
+        NESTED_ENTRY RedirectForThreadAbort2, _TEXT, NoHandler
+        PROLOG_SAVE_REG_PAIR_INDEXED fp,lr, -16
+
+        // stack must be 16 byte aligned
+        CHECK_STACK_ALIGNMENT
+
+        // On entry:
+        //
+        // x0 = address of FaultingExceptionFrame
+        //
+        // Invoke the helper to setup the FaultingExceptionFrame and raise the exception
+        bl              C_FUNC(ThrowControlForThread)
+
+        // ThrowControlForThread doesn't return.
+        EMIT_BREAKPOINT
+
+        NESTED_END RedirectForThreadAbort2, _TEXT
+
+GenerateRedirectedStubWithFrame RedirectForThreadAbort, RedirectForThreadAbort2
 
 // ------------------------------------------------------------------
 // ResolveWorkerChainLookupAsmStub
diff --git a/src/coreclr/vm/arm64/unixstubs.cpp b/src/coreclr/vm/arm64/unixstubs.cpp
index 9b313f8475f68c..878d5b003686b6 100644
--- a/src/coreclr/vm/arm64/unixstubs.cpp
+++ b/src/coreclr/vm/arm64/unixstubs.cpp
@@ -2,16 +2,3 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 #include "common.h"
-
-extern "C"
-{
-    void RedirectForThrowControl()
-    {
-        PORTABILITY_ASSERT("Implement for PAL");
-    }
-
-    void RedirectForThreadAbort()
-    {
-        PORTABILITY_ASSERT("Implement for PAL");
-    }
-};
diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp
index 79daadcb1b7a9b..3fa96490a2e60d 100644
--- a/src/coreclr/vm/exceptionhandling.cpp
+++ b/src/coreclr/vm/exceptionhandling.cpp
@@ -800,7 +800,7 @@ UINT_PTR ExceptionTracker::FinishSecondPass(
     {
         CopyOSContext(pThread->m_OSContext, pContextRecord);
         SetIP(pThread->m_OSContext, (PCODE)uResumePC);
-#ifdef TARGET_UNIX
+#if defined(TARGET_UNIX) && !(defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM))
         uAbortAddr = NULL;
 #else
         uAbortAddr = (UINT_PTR)COMPlusCheckForAbort(uResumePC);
@@ -833,7 +833,12 @@ UINT_PTR ExceptionTracker::FinishSecondPass(
         STRESS_LOG1(LF_EH, LL_INFO10, "resume under control: ip: %p\n", uResumePC);
 
 #ifdef TARGET_AMD64
+#ifdef TARGET_UNIX
+        pContextRecord->Rdi = uResumePC;
+        pContextRecord->Rsi = GetIP(pThread->GetAbortContext());
+#else
         pContextRecord->Rcx = uResumePC;
+#endif
 #elif defined(TARGET_ARM) || defined(TARGET_ARM64)
         // On ARM & ARM64, we save off the original PC in Lr. This is the same as done
         // in HandleManagedFault for H/W generated exceptions.
diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp
index 94b809e9f2ad40..1a454a8dc54c12 100644
--- a/src/coreclr/vm/threadsuspend.cpp
+++ b/src/coreclr/vm/threadsuspend.cpp
@@ -3956,7 +3956,9 @@ ThrowControlForThread(
     STRESS_LOG0(LF_SYNC, LL_INFO100, "ThrowControlForThread Aborting\n");
 
     // Here we raise an exception.
+    INSTALL_MANAGED_EXCEPTION_DISPATCHER
     RaiseComPlusException();
+    UNINSTALL_MANAGED_EXCEPTION_DISPATCHER
 }
 
 #if defined(FEATURE_HIJACK) && !defined(TARGET_UNIX)
diff --git a/src/libraries/System.Runtime/tests/System/Runtime/ControlledExecutionTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/ControlledExecutionTests.cs
index 8aa28ad66522c3..818b5606b65e23 100644
--- a/src/libraries/System.Runtime/tests/System/Runtime/ControlledExecutionTests.cs
+++ b/src/libraries/System.Runtime/tests/System/Runtime/ControlledExecutionTests.cs
@@ -97,13 +97,6 @@ void Test()
                     _caughtException = true;
                 }
 
-                if (!PlatformDetection.IsWindows)
-                {
-                    // Rethrowing a ThreadAbortException at the end of catch blocks is not implemented, so force it
-                    // here by calling native code (https://github.com/dotnet/runtime/issues/72703).
-                    Thread.Sleep(0);
-                }
-
                 _finishedExecution = true;
             }
         }
