diff --git a/exosphere/loader_stub/source/secmon_loader_error.cpp b/exosphere/loader_stub/source/secmon_loader_error.cpp
index c59cc3777..ba2bef587 100644
--- a/exosphere/loader_stub/source/secmon_loader_error.cpp
+++ b/exosphere/loader_stub/source/secmon_loader_error.cpp
@@ -18,13 +18,13 @@
 
 namespace ams::diag {
 
-    NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
-        AMS_UNUSED(file, line, func, expr, value, format);
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line) {
+        AMS_UNUSED(expr, func, line, file);
         ams::secmon::loader::ErrorReboot();
     }
 
-    NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
-        AMS_UNUSED(file, line, func, expr, value);
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const char *format, ...) {
+        AMS_UNUSED(expr, func, line, file, format);
         ams::secmon::loader::ErrorReboot();
     }
 
diff --git a/exosphere/mariko_fatal/source/fatal_abort_impl.cpp b/exosphere/mariko_fatal/source/fatal_abort_impl.cpp
index f764148b3..a049cdc0a 100644
--- a/exosphere/mariko_fatal/source/fatal_abort_impl.cpp
+++ b/exosphere/mariko_fatal/source/fatal_abort_impl.cpp
@@ -17,7 +17,7 @@
 
 namespace ams::diag {
 
-    void AbortImpl() {
+    NORETURN void AbortImpl() {
         AMS_SECMON_LOG("AbortImpl was called\n");
         AMS_LOG_FLUSH();
         reg::Write(0x4, 0xAAAAAAAA);
diff --git a/exosphere/program/rebootstub/source/rebootstub_power_off.cpp b/exosphere/program/rebootstub/source/rebootstub_power_off.cpp
index 4998f80e3..e71c3627f 100644
--- a/exosphere/program/rebootstub/source/rebootstub_power_off.cpp
+++ b/exosphere/program/rebootstub/source/rebootstub_power_off.cpp
@@ -50,7 +50,7 @@ namespace ams::rebootstub {
 
 namespace ams::diag {
 
-    void AbortImpl() {
+    NORETURN void AbortImpl() {
         /* Halt the bpmp. */
         rebootstub::Halt();
 
diff --git a/exosphere/program/sc7fw/source/sc7fw_main.cpp b/exosphere/program/sc7fw/source/sc7fw_main.cpp
index 7bcc4b910..99c883540 100644
--- a/exosphere/program/sc7fw/source/sc7fw_main.cpp
+++ b/exosphere/program/sc7fw/source/sc7fw_main.cpp
@@ -111,7 +111,7 @@ namespace ams::sc7fw {
 
 namespace ams::diag {
 
-    void AbortImpl() {
+    NORETURN void AbortImpl() {
         sc7fw::ExceptionHandler();
     }
 
diff --git a/exosphere/program/source/secmon_error.cpp b/exosphere/program/source/secmon_error.cpp
index e5bdab56b..a8d17bc2d 100644
--- a/exosphere/program/source/secmon_error.cpp
+++ b/exosphere/program/source/secmon_error.cpp
@@ -50,7 +50,7 @@ namespace ams::diag {
 
     }
 
-    void AbortImpl() {
+    NORETURN void AbortImpl() {
         /* Perform any necessary (typically none) debugging. */
         if constexpr (SaveSystemStateForDebug) {
             SaveSystemStateForDebugAbort();
diff --git a/exosphere/warmboot/source/warmboot_main.cpp b/exosphere/warmboot/source/warmboot_main.cpp
index 40212976f..d9527a9c0 100644
--- a/exosphere/warmboot/source/warmboot_main.cpp
+++ b/exosphere/warmboot/source/warmboot_main.cpp
@@ -98,7 +98,7 @@ namespace ams::warmboot {
 
 namespace ams::diag {
 
-    void AbortImpl() {
+    NORETURN void AbortImpl() {
         warmboot::ExceptionHandler();
     }
 
diff --git a/fusee/loader_stub/source/fusee_loader_error.cpp b/fusee/loader_stub/source/fusee_loader_error.cpp
index dad352275..690a54b35 100644
--- a/fusee/loader_stub/source/fusee_loader_error.cpp
+++ b/fusee/loader_stub/source/fusee_loader_error.cpp
@@ -18,13 +18,15 @@
 
 namespace ams::diag {
 
-    NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
-        AMS_UNUSED(file, line, func, expr, value, format);
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line) {
+        AMS_UNUSED(expr, func, line, file);
+
         ams::nxboot::loader::ErrorStop();
     }
 
-    NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
-        AMS_UNUSED(file, line, func, expr, value);
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const char *format, ...) {
+        AMS_UNUSED(expr, func, line, file, format);
+
         ams::nxboot::loader::ErrorStop();
     }
 
diff --git a/fusee/program/source/fusee_exception_handler.cpp b/fusee/program/source/fusee_exception_handler.cpp
index eaff48a0a..64f6cbc22 100644
--- a/fusee/program/source/fusee_exception_handler.cpp
+++ b/fusee/program/source/fusee_exception_handler.cpp
@@ -27,20 +27,36 @@ namespace ams::nxboot {
 
 namespace ams::diag {
 
-    NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
-        AMS_UNUSED(file, line, func, expr, format);
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line) {
+        AMS_UNUSED(expr, func, line, file);
 
         u32 lr;
         __asm__ __volatile__("mov %0, lr" : "=r"(lr) :: "memory");
-        nxboot::ShowFatalError("Abort called, lr=%p, value=%" PRIx64 "\n", reinterpret_cast<void *>(lr), value);
+        nxboot::ShowFatalError("Abort called, lr=%p\n", reinterpret_cast<void *>(lr));
     }
 
-    NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
-        AMS_UNUSED(file, line, func, expr);
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const char *format, ...) {
+        AMS_UNUSED(expr, func, line, file, format);
 
         u32 lr;
         __asm__ __volatile__("mov %0, lr" : "=r"(lr) :: "memory");
-        nxboot::ShowFatalError("Abort called, lr=%p, value=%" PRIx64 "\n", reinterpret_cast<void *>(lr), value);
+        nxboot::ShowFatalError("Abort called, lr=%p\n", reinterpret_cast<void *>(lr));
+    }
+
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const char *format, ...) {
+        AMS_UNUSED(expr, func, line, file, result, format);
+
+        u32 lr;
+        __asm__ __volatile__("mov %0, lr" : "=r"(lr) :: "memory");
+        nxboot::ShowFatalError("Abort called, lr=%p, result=0x%08" PRIX32 "\n", reinterpret_cast<void *>(lr), result != nullptr ? result->GetValue() : 0);
+    }
+
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const ::ams::os::UserExceptionInfo *exception_info, const char *format, ...) {
+        AMS_UNUSED(expr, func, line, file, result, exception_info, format);
+
+        u32 lr;
+        __asm__ __volatile__("mov %0, lr" : "=r"(lr) :: "memory");
+        nxboot::ShowFatalError("Abort called, lr=%p, result=0x%08" PRIX32 "\n", reinterpret_cast<void *>(lr), result != nullptr ? result->GetValue() : 0);
     }
 
     NORETURN void AbortImpl() {
@@ -49,16 +65,16 @@ namespace ams::diag {
         nxboot::ShowFatalError("Abort called, lr=%p\n", reinterpret_cast<void *>(lr));
     }
 
-    NORETURN void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
-        AMS_UNUSED(file, line, func, expr, value);
+    NORETURN void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line) {
+        AMS_UNUSED(type, expr, func, file, line);
 
         u32 lr;
         __asm__ __volatile__("mov %0, lr" : "=r"(lr) :: "memory");
         nxboot::ShowFatalError("Assert called, lr=%p\n", reinterpret_cast<void *>(lr));
     }
 
-   NORETURN void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
-        AMS_UNUSED(file, line, func, expr, value, format);
+   NORETURN void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line, const char *format, ...) {
+        AMS_UNUSED(type, expr, func, file, line, format);
 
         u32 lr;
         __asm__ __volatile__("mov %0, lr" : "=r"(lr) :: "memory");
@@ -66,17 +82,3 @@ namespace ams::diag {
     }
 
 }
-
-namespace ams::result::impl {
-
-        NORETURN void OnResultAbort(const char *file, int line, const char *func, const char *expr, Result result) {
-            ::ams::diag::AbortImpl(file, line, func, expr, result.GetValue(), "Result Abort: 2%03" PRId32 "-%04" PRId32 "", result.GetModule(), result.GetDescription());
-            AMS_INFINITE_LOOP();
-            __builtin_unreachable();
-        }
-
-        NORETURN void OnResultAbort(Result result) {
-            OnResultAbort("", 0, "", "", result);
-        }
-
-}
\ No newline at end of file
diff --git a/libraries/config/os/linux/os.mk b/libraries/config/os/linux/os.mk
index f875e822b..ee225a6f1 100644
--- a/libraries/config/os/linux/os.mk
+++ b/libraries/config/os/linux/os.mk
@@ -1,5 +1,5 @@
 export ATMOSPHERE_DEFINES  += -DATMOSPHERE_OS_LINUX
-export ATMOSPHERE_SETTINGS +=
+export ATMOSPHERE_SETTINGS += -fno-omit-frame-pointer
 export ATMOSPHERE_CFLAGS   +=
 export ATMOSPHERE_CXXFLAGS +=
 export ATMOSPHERE_ASFLAGS  +=
diff --git a/libraries/config/os/macos/os.mk b/libraries/config/os/macos/os.mk
index 6d185126c..f6e4e1afd 100644
--- a/libraries/config/os/macos/os.mk
+++ b/libraries/config/os/macos/os.mk
@@ -1,5 +1,5 @@
 export ATMOSPHERE_DEFINES  += -DATMOSPHERE_OS_MACOS
-export ATMOSPHERE_SETTINGS +=
+export ATMOSPHERE_SETTINGS += -fno-omit-frame-pointer
 export ATMOSPHERE_CFLAGS   +=
 export ATMOSPHERE_CXXFLAGS +=
 export ATMOSPHERE_ASFLAGS  +=
diff --git a/libraries/config/os/windows/os.mk b/libraries/config/os/windows/os.mk
index 7ef43a255..c63a7d8d5 100644
--- a/libraries/config/os/windows/os.mk
+++ b/libraries/config/os/windows/os.mk
@@ -1,5 +1,5 @@
 export ATMOSPHERE_DEFINES  += -DATMOSPHERE_OS_WINDOWS
-export ATMOSPHERE_SETTINGS +=
+export ATMOSPHERE_SETTINGS += -fno-omit-frame-pointer
 export ATMOSPHERE_CFLAGS   +=
 export ATMOSPHERE_CXXFLAGS +=
 export ATMOSPHERE_ASFLAGS  +=
diff --git a/libraries/config/templates/stratosphere.mk b/libraries/config/templates/stratosphere.mk
index a052c9272..63b90af9e 100644
--- a/libraries/config/templates/stratosphere.mk
+++ b/libraries/config/templates/stratosphere.mk
@@ -80,9 +80,9 @@ endif
 ifeq ($(ATMOSPHERE_BOARD),nx-hac-001)
 export LIBS	:= -lstratosphere -lnx
 else ifeq ($(ATMOSPHERE_BOARD),generic_windows)
-export LIBS	:= -lstratosphere -lwinmm -lws2_32 -lbcrypt
+export LIBS	:= -lstratosphere -lwinmm -lws2_32 -lbcrypt -lbfd -liberty -lintl -lz
 else ifeq ($(ATMOSPHERE_BOARD),generic_linux)
-export LIBS := -lstratosphere -pthread
+export LIBS := -lstratosphere -pthread -lbfd -liberty -ldl
 else ifeq ($(ATMOSPHERE_BOARD),generic_macos)
 export LIBS := -lstratosphere -pthread
 else
diff --git a/libraries/libexosphere/include/exosphere/diag/diag_detailed_assertion_impl.inc b/libraries/libexosphere/include/exosphere/diag/diag_detailed_assertion_impl.inc
index 2f37cd3c9..845d35757 100644
--- a/libraries/libexosphere/include/exosphere/diag/diag_detailed_assertion_impl.inc
+++ b/libraries/libexosphere/include/exosphere/diag/diag_detailed_assertion_impl.inc
@@ -14,23 +14,22 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
+void AbortImpl(const char *expr, const char *func, const char *file, int line) {
     #if defined(AMS_ENABLE_DETAILED_ASSERTIONS)
     {
         AMS_LOG("Abort Called\n");
         AMS_LOG("    Location:   %s:%d\n", file, line);
         AMS_LOG("    Function:   %s\n", func);
         AMS_LOG("    Expression: %s\n", expr);
-        AMS_LOG("    Value:      %016" PRIx64 "\n", value);
         AMS_LOG("\n");
     }
     #else
-    AMS_UNUSED(file, line, func, expr, value);
+    AMS_UNUSED(file, line, func, expr);
     #endif
     AbortImpl();
 }
 
-void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
+void AbortImpl(const char *expr, const char *func, const char *file, int line, const char *format, ...) {
     #if defined(AMS_ENABLE_DETAILED_ASSERTIONS)
     {
         AMS_LOG("Abort Called\n");
@@ -48,12 +47,35 @@ void AbortImpl(const char *file, int line, const char *func, const char *expr, u
         }
     }
     #else
-    AMS_UNUSED(file, line, func, expr, value, format);
+    AMS_UNUSED(file, line, func, expr, format);
     #endif
     AbortImpl();
 }
 
-void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
+void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const char *format, ...) {
+    #if defined(AMS_ENABLE_DETAILED_ASSERTIONS)
+    {
+        AMS_LOG("Abort Called\n");
+        AMS_LOG("    Location:   %s:%d\n", file, line);
+        AMS_LOG("    Function:   %s\n", func);
+        AMS_LOG("    Expression: %s\n", expr);
+        AMS_LOG("    Result:     0x%08" PRIx32 "\n", result->GetValue());
+        AMS_LOG("\n");
+        {
+            ::std::va_list vl;
+            va_start(vl, format);
+            AMS_VLOG(format, vl);
+            va_end(vl);
+            AMS_LOG("\n");
+        }
+    }
+    #else
+    AMS_UNUSED(file, line, func, expr, result, format);
+    #endif
+    AbortImpl();
+}
+
+void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line) {
     #if defined(AMS_ENABLE_DETAILED_ASSERTIONS)
     {
         AMS_LOG("Assertion Failure\n");
@@ -62,14 +84,16 @@ void AssertionFailureImpl(const char *file, int line, const char *func, const ch
         AMS_LOG("    Expression: %s\n", expr);
         AMS_LOG("    Value:      %016" PRIx64 "\n", value);
         AMS_LOG("\n");
+
+        AMS_UNUSED(type);
     }
     #else
-    AMS_UNUSED(file, line, func, expr, value);
+    AMS_UNUSED(type, expr, func, file, line);
     #endif
     AbortImpl();
 }
 
-void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
+void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line, const char *format, ...) {
     #if defined(AMS_ENABLE_DETAILED_ASSERTIONS)
     {
         AMS_LOG("Assertion Failure\n");
@@ -85,9 +109,11 @@ void AssertionFailureImpl(const char *file, int line, const char *func, const ch
             va_end(vl);
             AMS_LOG("\n");
         }
+
+        AMS_UNUSED(type);
     }
     #else
-    AMS_UNUSED(file, line, func, expr, value, format);
+    AMS_UNUSED(type, expr, func, file, line, format);
     #endif
     AbortImpl();
 }
diff --git a/libraries/libexosphere/source/impl/ams_impl_unexpected_default.cpp b/libraries/libexosphere/source/impl/ams_impl_unexpected_default.cpp
new file mode 100644
index 000000000..3b397ad59
--- /dev/null
+++ b/libraries/libexosphere/source/impl/ams_impl_unexpected_default.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <exosphere.hpp>
+
+namespace ams::impl {
+
+   NORETURN void UnexpectedDefaultImpl(const char *func, const char *file, int line) {
+        ::ams::diag::AbortImpl("", func, file, line);
+    }
+
+}
\ No newline at end of file
diff --git a/libraries/libmesosphere/include/mesosphere/kern_panic.hpp b/libraries/libmesosphere/include/mesosphere/kern_panic.hpp
index bb1dfa7ea..047a65763 100644
--- a/libraries/libmesosphere/include/mesosphere/kern_panic.hpp
+++ b/libraries/libmesosphere/include/mesosphere/kern_panic.hpp
@@ -26,12 +26,12 @@ namespace ams::kern {
 
 namespace ams::diag {
 
-    NORETURN ALWAYS_INLINE void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
+    NORETURN ALWAYS_INLINE void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line) {
         #if defined(MESOSPHERE_ENABLE_DEBUG_PRINT)
-        ::ams::kern::Panic(file, line, "ams::diag::AssertionFailureImpl: %s:%s 0x%016" PRIx64 "", func, expr, value);
+        ::ams::kern::Panic(file, line, "ams::diag::OnAssertionFailure: %d %s:%s", (type == AssertionType_Audit), func, expr);
         #else
         ::ams::kern::Panic();
-        AMS_UNUSED(file, line, func, expr, value);
+        AMS_UNUSED(type, expr, func, file, line);
         #endif
     }
 
diff --git a/libraries/libstratosphere/include/stratosphere/diag.hpp b/libraries/libstratosphere/include/stratosphere/diag.hpp
index 0a51073cc..a8f9c0e2e 100644
--- a/libraries/libstratosphere/include/stratosphere/diag.hpp
+++ b/libraries/libstratosphere/include/stratosphere/diag.hpp
@@ -20,6 +20,10 @@
 #include <stratosphere/diag/impl/diag_impl_log.hpp>
 #include <stratosphere/diag/diag_log.hpp>
 #include <stratosphere/diag/diag_sdk_log.hpp>
+#include <stratosphere/diag/diag_abort_observer.hpp>
+#include <stratosphere/diag/diag_assertion_failure_handler.hpp>
 
 #include <stratosphere/diag/impl/diag_utf8_util.hpp>
 
+#include <stratosphere/diag/diag_backtrace.hpp>
+#include <stratosphere/diag/diag_symbol.hpp>
diff --git a/libraries/libstratosphere/include/stratosphere/diag/diag_abort_observer.hpp b/libraries/libstratosphere/include/stratosphere/diag/diag_abort_observer.hpp
new file mode 100644
index 000000000..52a4605b4
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/diag/diag_abort_observer.hpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <vapours.hpp>
+#include <stratosphere/diag/diag_log_types.hpp>
+
+namespace ams::diag {
+
+    using AbortObserver = void (*)(const AbortInfo &);
+
+    struct AbortObserverHolder {
+        AbortObserver observer;
+        AbortObserverHolder *next;
+        bool is_registered;
+    };
+
+    void InitializeAbortObserverHolder(AbortObserverHolder *holder, AbortObserver observer);
+
+    void RegisterAbortObserver(AbortObserverHolder *holder);
+    void UnregisterAbortObserver(AbortObserverHolder *holder);
+    void EnableDefaultAbortObserver(bool en);
+
+    struct SdkAbortInfo {
+        AbortInfo abort_info;
+        Result result;
+        const ::ams::os::UserExceptionInfo *exc_info;
+    };
+
+    using SdkAbortObserver = void (*)(const SdkAbortInfo &);
+
+    struct SdkAbortObserverHolder {
+        SdkAbortObserver observer;
+        SdkAbortObserverHolder *next;
+        bool is_registered;
+    };
+
+    void InitializeSdkAbortObserverHolder(SdkAbortObserverHolder *holder, SdkAbortObserver observer);
+
+    void RegisterSdkAbortObserver(SdkAbortObserverHolder *holder);
+    void UnregisterSdkAbortObserver(SdkAbortObserverHolder *holder);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/diag/diag_assertion_failure_handler.hpp b/libraries/libstratosphere/include/stratosphere/diag/diag_assertion_failure_handler.hpp
new file mode 100644
index 000000000..b1466a187
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/diag/diag_assertion_failure_handler.hpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <vapours.hpp>
+#include <stratosphere/diag/diag_log_types.hpp>
+
+namespace ams::diag {
+
+    enum AssertionFailureOperation {
+        AssertionFailureOperation_Abort,
+        AssertionFailureOperation_Continue,
+    };
+
+    using AssertionFailureHandler = AssertionFailureOperation (*)(const AssertionInfo &info);
+
+    void SetAssertionFailureHandler(AssertionFailureHandler handler);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/diag/diag_backtrace.hpp b/libraries/libstratosphere/include/stratosphere/diag/diag_backtrace.hpp
new file mode 100644
index 000000000..de8bbe890
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/diag/diag_backtrace.hpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <vapours.hpp>
+
+#if defined(ATMOSPHERE_OS_HORIZON)
+    #include <stratosphere/diag/impl/diag_backtrace_impl.os.horizon.hpp>
+#elif defined(ATMOSPHERE_OS_WINDOWS)
+    #include <stratosphere/diag/impl/diag_backtrace_impl.os.windows.hpp>
+#elif defined(ATMOSPHERE_OS_LINUX)
+    #include <stratosphere/diag/impl/diag_backtrace_impl.os.linux.hpp>
+#elif defined(ATMOSPHERE_OS_MACOS)
+    #include <stratosphere/diag/impl/diag_backtrace_impl.os.macos.hpp>
+#else
+    #error "Unknown OS for diag::Backtrace"
+#endif
+
+namespace ams::diag {
+
+    size_t GetBacktrace(uintptr_t *out, size_t out_size);
+
+    #if defined(ATMOSPHERE_OS_HORIZON)
+    size_t GetBacktace(uintptr_t *out, size_t out_size, uintptr_t fp, uintptr_t sp, uintptr_t pc);
+    #endif
+
+    class Backtrace {
+        private:
+            impl::Backtrace m_impl;
+        public:
+            NOINLINE Backtrace() {
+                m_impl.Initialize();
+                m_impl.Step();
+            }
+
+            #if defined(ATMOSPHERE_OS_HORIZON)
+            Backtrace(uintptr_t fp, uintptr_t sp, uintptr_t pc) {
+                m_impl.Initialize(fp, sp, pc);
+            }
+            #endif
+
+            bool Step() { return m_impl.Step(); }
+
+            uintptr_t GetStackPointer()  const { return m_impl.GetStackPointer(); }
+            uintptr_t GetReturnAddress() const { return m_impl.GetReturnAddress(); }
+    };
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/diag/diag_symbol.hpp b/libraries/libstratosphere/include/stratosphere/diag/diag_symbol.hpp
new file mode 100644
index 000000000..d43540949
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/diag/diag_symbol.hpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <vapours.hpp>
+namespace ams::diag {
+
+    uintptr_t GetSymbolName(char *dst, size_t dst_size, uintptr_t address);
+    size_t GetSymbolSize(uintptr_t address);
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/diag/impl/diag_backtrace_impl.os.horizon.hpp b/libraries/libstratosphere/include/stratosphere/diag/impl/diag_backtrace_impl.os.horizon.hpp
new file mode 100644
index 000000000..1500b1476
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/diag/impl/diag_backtrace_impl.os.horizon.hpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <vapours.hpp>
+
+namespace ams::diag::impl {
+
+    class Backtrace {
+        private:
+            static constexpr size_t MemoryInfoBufferSize = 5;
+        public:
+            struct StackInfo {
+                uintptr_t stack_top;
+                uintptr_t stack_bottom;
+            };
+        private:
+            s64 m_memory_info_buffer[MemoryInfoBufferSize]{};
+            StackInfo *m_current_stack_info = nullptr;
+            StackInfo m_exception_stack_info{};
+            StackInfo m_normal_stack_info{};
+            uintptr_t m_fp = 0;
+            uintptr_t m_prev_fp = 0;
+            uintptr_t m_lr = 0;
+        public:
+            Backtrace() = default;
+
+            void Initialize();
+            void Initialize(uintptr_t fp, uintptr_t sp, uintptr_t pc);
+
+            bool Step();
+            uintptr_t GetStackPointer() const;
+            uintptr_t GetReturnAddress() const;
+        private:
+            void SetStackInfo(uintptr_t fp, uintptr_t sp);
+    };
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/diag/impl/diag_backtrace_impl.os.linux.hpp b/libraries/libstratosphere/include/stratosphere/diag/impl/diag_backtrace_impl.os.linux.hpp
new file mode 100644
index 000000000..23e1e40a1
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/diag/impl/diag_backtrace_impl.os.linux.hpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <vapours.hpp>
+
+namespace ams::diag::impl {
+
+    class Backtrace {
+        private:
+            static constexpr size_t BacktraceEntryCountMax = 0x80;
+        private:
+            void *m_backtrace_addresses[BacktraceEntryCountMax];
+            size_t m_index = 0;
+            size_t m_size = 0;
+        public:
+            Backtrace() = default;
+
+            void Initialize();
+
+            bool Step();
+            uintptr_t GetStackPointer() const;
+            uintptr_t GetReturnAddress() const;
+    };
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/diag/impl/diag_backtrace_impl.os.macos.hpp b/libraries/libstratosphere/include/stratosphere/diag/impl/diag_backtrace_impl.os.macos.hpp
new file mode 100644
index 000000000..23e1e40a1
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/diag/impl/diag_backtrace_impl.os.macos.hpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <vapours.hpp>
+
+namespace ams::diag::impl {
+
+    class Backtrace {
+        private:
+            static constexpr size_t BacktraceEntryCountMax = 0x80;
+        private:
+            void *m_backtrace_addresses[BacktraceEntryCountMax];
+            size_t m_index = 0;
+            size_t m_size = 0;
+        public:
+            Backtrace() = default;
+
+            void Initialize();
+
+            bool Step();
+            uintptr_t GetStackPointer() const;
+            uintptr_t GetReturnAddress() const;
+    };
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/diag/impl/diag_backtrace_impl.os.windows.hpp b/libraries/libstratosphere/include/stratosphere/diag/impl/diag_backtrace_impl.os.windows.hpp
new file mode 100644
index 000000000..23e1e40a1
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/diag/impl/diag_backtrace_impl.os.windows.hpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <vapours.hpp>
+
+namespace ams::diag::impl {
+
+    class Backtrace {
+        private:
+            static constexpr size_t BacktraceEntryCountMax = 0x80;
+        private:
+            void *m_backtrace_addresses[BacktraceEntryCountMax];
+            size_t m_index = 0;
+            size_t m_size = 0;
+        public:
+            Backtrace() = default;
+
+            void Initialize();
+
+            bool Step();
+            uintptr_t GetStackPointer() const;
+            uintptr_t GetReturnAddress() const;
+    };
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp
index 3c4e84b27..7133512cb 100644
--- a/libraries/libstratosphere/include/stratosphere/os.hpp
+++ b/libraries/libstratosphere/include/stratosphere/os.hpp
@@ -46,6 +46,7 @@
 #include <stratosphere/os/os_sdk_reply_and_receive.hpp>
 #include <stratosphere/os/os_thread.hpp>
 #include <stratosphere/os/os_sdk_thread_api.hpp>
+#include <stratosphere/os/os_sdk_thread_info.hpp>
 #include <stratosphere/os/os_message_queue.hpp>
 #include <stratosphere/os/os_light_event.hpp>
 #include <stratosphere/os/os_light_message_queue.hpp>
@@ -55,3 +56,4 @@
 #include <stratosphere/os/os_multiple_wait.hpp>
 #include <stratosphere/os/os_argument.hpp>
 #include <stratosphere/os/os_cache.hpp>
+#include <stratosphere/os/os_debug.hpp>
diff --git a/libraries/libstratosphere/include/stratosphere/os/os_debug.hpp b/libraries/libstratosphere/include/stratosphere/os/os_debug.hpp
new file mode 100644
index 000000000..1e31b0e27
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/os/os_debug.hpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include <vapours.hpp>
+#include <stratosphere/os/os_debug_types.hpp>
+#include <stratosphere/os/os_debug_api.hpp>
diff --git a/libraries/libstratosphere/include/stratosphere/os/os_debug_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_debug_api.hpp
new file mode 100644
index 000000000..6a1fd3d11
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/os/os_debug_api.hpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include <vapours.hpp>
+#include <stratosphere/os/os_debug_types.hpp>
+#include <stratosphere/os/os_tick.hpp>
+
+namespace ams::os {
+
+    void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size);
+
+    void QueryMemoryInfo(MemoryInfo *out);
+
+    Tick GetIdleTickCount();
+
+    int GetFreeThreadCount();
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/os/os_debug_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_debug_types.hpp
new file mode 100644
index 000000000..c9e05965f
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/os/os_debug_types.hpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <vapours.hpp>
+
+namespace ams::os {
+
+    struct MemoryInfo {
+        u64 total_available_memory_size;
+        size_t total_used_memory_size;
+        size_t total_memory_heap_size;
+        size_t allocated_memory_heap_size;
+        size_t program_size;
+        size_t total_thread_stack_size;
+        int thread_count;
+    };
+
+}
diff --git a/libraries/libstratosphere/include/stratosphere/os/os_sdk_thread_info.hpp b/libraries/libstratosphere/include/stratosphere/os/os_sdk_thread_info.hpp
new file mode 100644
index 000000000..933c01206
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/os/os_sdk_thread_info.hpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include <stratosphere/os/os_sdk_thread_info_types.hpp>
+#include <stratosphere/os/os_sdk_thread_info_api.hpp>
diff --git a/libraries/libstratosphere/include/stratosphere/os/os_sdk_thread_info_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_sdk_thread_info_api.hpp
new file mode 100644
index 000000000..375ba73ba
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/os/os_sdk_thread_info_api.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include <stratosphere/os/os_sdk_thread_info_types.hpp>
+#include <stratosphere/os/os_thread_types.hpp>
+
+namespace ams::os {
+
+    void GetThreadStackInfo(uintptr_t *out_stack_top, size_t *out_stack_size, const ThreadType *thread);
+
+}
\ No newline at end of file
diff --git a/libraries/libstratosphere/include/stratosphere/os/os_sdk_thread_info_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_sdk_thread_info_types.hpp
new file mode 100644
index 000000000..dafc51503
--- /dev/null
+++ b/libraries/libstratosphere/include/stratosphere/os/os_sdk_thread_info_types.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <stratosphere/os/os_common_types.hpp>
+
+namespace ams::os {
+
+    enum SdkLastThreadInfoFlag : u32 {
+        SdkLastThreadInfoFlag_ThreadInSystemCall = (1u << 0),
+    };
+
+}
diff --git a/libraries/libstratosphere/libstratosphere.mk b/libraries/libstratosphere/libstratosphere.mk
index 83a243660..e646ea02f 100644
--- a/libraries/libstratosphere/libstratosphere.mk
+++ b/libraries/libstratosphere/libstratosphere.mk
@@ -132,9 +132,9 @@ DEPENDS	:=	$(OFILES:.o=.d) $(foreach hdr,$(GCH_FILES),$(notdir $(patsubst %.hpp.
 #---------------------------------------------------------------------------------
 # main targets
 #---------------------------------------------------------------------------------
-$(OUTPUT)	:	$(OFILES)
+$(OUTPUT)	:	result_get_name.o $(filter-out result_get_name.o, $(OFILES))
 
-$(OFILES)	:	$(GCH_FILES)
+$(filter-out result_get_name.o, $(OFILES))	:	$(GCH_FILES)
 
 $(OFILES_SRC)	: $(HFILES_BIN)
 
@@ -146,12 +146,16 @@ hos_stratosphere_api.o: CXXFLAGS += -fno-lto
 init_operator_new.o: CXXFLAGS += -fno-lto
 init_libnx_shim.os.horizon.o: CXXFLAGS += -fno-lto
 
+result_get_name.o: CXXFLAGS += -fno-lto
+
 spl_secure_monitor_api.os.generic.o: CXXFLAGS += -I$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/include
 fs_id_string_impl.os.generic.o: CXXFLAGS += -I$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/include
 
 ifeq ($(ATMOSPHERE_OS_NAME),windows)
 os_%.o: CXXFLAGS += -fno-lto
 fssystem_%.o: CXXFLAGS += -fno-lto
+fssrv_%.o: CXXFLAGS += -fno-lto
+fs_%.o: CXXFLAGS += -fno-lto
 endif
 
 #---------------------------------------------------------------------------------
diff --git a/libraries/libstratosphere/source/diag/diag_abort_observer.cpp b/libraries/libstratosphere/source/diag/diag_abort_observer.cpp
new file mode 100644
index 000000000..183822b08
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/diag_abort_observer.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+#include "impl/diag_abort_observer_manager.hpp"
+
+namespace ams::diag {
+
+    namespace impl {
+
+        constinit bool g_enable_default_abort_observer = true;
+
+    }
+
+    namespace {
+
+        template<typename Holder, typename Observer>
+        void InitializeAbortObserverHolderImpl(Holder *holder, Observer observer) {
+            holder->observer      = observer;
+            holder->next          = nullptr;
+            holder->is_registered = false;
+        }
+
+    }
+
+    void InitializeAbortObserverHolder(AbortObserverHolder *holder, AbortObserver observer) {
+        InitializeAbortObserverHolderImpl(holder, observer);
+    }
+
+    void RegisterAbortObserver(AbortObserverHolder *holder) {
+        impl::GetAbortObserverManager()->RegisterObserver(holder);
+    }
+
+    void UnregisterAbortObserver(AbortObserverHolder *holder) {
+        impl::GetAbortObserverManager()->UnregisterObserver(holder);
+    }
+
+    void EnableDefaultAbortObserver(bool en) {
+        ::ams::diag::impl::g_enable_default_abort_observer = en;
+    }
+
+    void InitializeSdkAbortObserverHolder(SdkAbortObserverHolder *holder, SdkAbortObserver observer) {
+        InitializeAbortObserverHolderImpl(holder, observer);
+    }
+
+    void RegisterSdkAbortObserver(SdkAbortObserverHolder *holder) {
+        impl::GetSdkAbortObserverManager()->RegisterObserver(holder);
+    }
+
+    void UnregisterSdkAbortObserver(SdkAbortObserverHolder *holder) {
+        impl::GetSdkAbortObserverManager()->UnregisterObserver(holder);
+    }
+
+}
diff --git a/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp b/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp
index 537ccedd7..ad0472dea 100644
--- a/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp
+++ b/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp
@@ -14,7 +14,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 #include <stratosphere.hpp>
-#include "impl/diag_print_debug_string.hpp"
+#include "impl/diag_invoke_abort.hpp"
 
 namespace ams::diag {
 
@@ -41,119 +41,193 @@ namespace ams::diag {
             __builtin_unreachable();
         }
 
-        inline void DebugLog(const char *format, ...) __attribute__((format(printf, 1, 2)));
+        constinit os::SdkMutex g_assert_mutex;
+        constinit os::SdkMutex g_abort_mutex;
 
-#ifdef AMS_ENABLE_DETAILED_ASSERTIONS
-        constinit os::SdkRecursiveMutex g_debug_log_lock;
-        constinit char g_debug_buffer[0x400];
+        void PrepareAbort() {
+            #if defined(ATMOSPHERE_OS_HORIZON)
+            {
+                /* Get the thread local region. */
+                auto * const tlr = svc::GetThreadLocalRegion();
 
-        void DebugLogImpl(const char *format, ::std::va_list vl) {
-            std::scoped_lock lk(g_debug_log_lock);
+                /* Clear disable count. */
+                tlr->disable_count = 0;
 
-            util::VSNPrintf(g_debug_buffer, sizeof(g_debug_buffer), format, vl);
-
-            diag::impl::PrintDebugString(g_debug_buffer, strlen(g_debug_buffer));
+                /* If we need to, unpin. */
+                if (tlr->interrupt_flag) {
+                    svc::SynchronizePreemptionState();
+                }
+            }
+            #endif
         }
 
-        void DebugLog(const char *format, ...) {
-            ::std::va_list vl;
-            va_start(vl, format);
-            DebugLogImpl(format, vl);
-            va_end(vl);
+        AbortReason ToAbortReason(AssertionType type) {
+            switch (type) {
+                case AssertionType_Audit:  return AbortReason_Audit;
+                case AssertionType_Assert: return AbortReason_Assert;
+                default:
+                    return AbortReason_Abort;
+            }
         }
 
-#else
-        void DebugLog(const char *format, ...)  { AMS_UNUSED(format); }
-#endif
-
-    }
-
-    NORETURN NOINLINE WEAK_SYMBOL void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
-        #if defined(ATMOSPHERE_OS_HORIZON)
-        DebugLog("%016" PRIx64 ": Assertion Failure\n", os::GetCurrentProgramId().value);
-        #else
-        DebugLog("0100000000000000: Assertion Failure\n");
-        #endif
-        DebugLog("        Location:   %s:%d\n", file, line);
-        DebugLog("        Function:   %s\n", func);
-        DebugLog("        Expression: %s\n", expr);
-        DebugLog("        Value:      %016" PRIx64 "\n", value);
-        DebugLog("\n");
-#ifdef AMS_ENABLE_DETAILED_ASSERTIONS
-        {
-            ::std::va_list vl;
-            va_start(vl, format);
-            DebugLogImpl(format, vl);
-            va_end(vl);
+        AssertionFailureOperation DefaultAssertionFailureHandler(const AssertionInfo &) {
+            return AssertionFailureOperation_Abort;
         }
-#else
-        AMS_UNUSED(format);
-#endif
-        DebugLog("\n");
 
-        AbortWithValue(value);
-    }
+        constinit AssertionFailureHandler g_assertion_failure_handler = &DefaultAssertionFailureHandler;
 
-    NORETURN NOINLINE WEAK_SYMBOL void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
-        #if defined(ATMOSPHERE_OS_HORIZON)
-        DebugLog("%016" PRIx64 ": Assertion Failure\n", os::GetCurrentProgramId().value);
-        #else
-        DebugLog("0100000000000000: Assertion Failure\n");
-        #endif
-        DebugLog("        Location:   %s:%d\n", file, line);
-        DebugLog("        Function:   %s\n", func);
-        DebugLog("        Expression: %s\n", expr);
-        DebugLog("        Value:      %016" PRIx64 "\n", value);
-        DebugLog("\n");
-        DebugLog("\n");
+        void ExecuteAssertionFailureOperation(AssertionFailureOperation operation, const AssertionInfo &info)  {
+            switch (operation) {
+                case AssertionFailureOperation_Continue:
+                    break;
+                case AssertionFailureOperation_Abort:
+                    {
+                        const AbortInfo abort_info = {
+                            ToAbortReason(info.type),
+                            info.message,
+                            info.expr,
+                            info.func,
+                            info.file,
+                            info.line,
+                        };
 
-        AbortWithValue(value);
-    }
-
-    NORETURN NOINLINE WEAK_SYMBOL void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
-        #if defined(ATMOSPHERE_OS_HORIZON)
-        DebugLog("%016" PRIx64 ": Abort Called\n", os::GetCurrentProgramId().value);
-        #else
-        DebugLog("0100000000000000: Abort Called\n");
-        #endif
-        DebugLog("        Location:   %s:%d\n", file, line);
-        DebugLog("        Function:   %s\n", func);
-        DebugLog("        Expression: %s\n", expr);
-        DebugLog("        Value:      %016" PRIx64 "\n", value);
-        DebugLog("\n");
-#ifdef AMS_ENABLE_DETAILED_ASSERTIONS
-        {
-            ::std::va_list vl;
-            va_start(vl, format);
-            DebugLogImpl(format, vl);
-            va_end(vl);
+                        ::ams::diag::impl::InvokeAbortObserver(abort_info);
+                        AbortWithValue(0);
+                    }
+                    break;
+                AMS_UNREACHABLE_DEFAULT_CASE();
+            }
         }
-#else
-        AMS_UNUSED(format);
-#endif
-        DebugLog("\n");
 
-        AbortWithValue(value);
+        void InvokeAssertionFailureHandler(const AssertionInfo &info) {
+            const auto operation = g_assertion_failure_handler(info);
+            ExecuteAssertionFailureOperation(operation, info);
+        }
+
+
     }
 
-    NORETURN NOINLINE WEAK_SYMBOL void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
-        #if defined(ATMOSPHERE_OS_HORIZON)
-        DebugLog("%016" PRIx64 ": Abort Called\n", os::GetCurrentProgramId().value);
-        #else
-        DebugLog("0100000000000000: Abort Called\n");
-        #endif
-        DebugLog("        Location:   %s:%d\n", file, line);
-        DebugLog("        Function:   %s\n", func);
-        DebugLog("        Expression: %s\n", expr);
-        DebugLog("        Value:      %016" PRIx64 "\n", value);
-        DebugLog("\n");
-        DebugLog("\n");
+    NOINLINE void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line, const char *format, ...) {
+        /* Prepare to abort. */
+        PrepareAbort();
 
-        AbortWithValue(value);
+        /* Acquire exclusive assert rights. */
+        if (g_assert_mutex.IsLockedByCurrentThread()) {
+            AbortWithValue(0);
+        }
+
+        std::scoped_lock lk(g_assert_mutex);
+
+        /* Create the assertion info. */
+        std::va_list vl;
+        va_start(vl, format);
+
+        const ::ams::diag::LogMessage message = { format, std::addressof(vl) };
+
+        const AssertionInfo info = {
+            type,
+            std::addressof(message),
+            expr,
+            func,
+            file,
+            line,
+        };
+
+        InvokeAssertionFailureHandler(info);
+        va_end(vl);
     }
 
-    NORETURN NOINLINE WEAK_SYMBOL void AbortImpl() {
-        AbortWithValue(0);
+    void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line) {
+        return OnAssertionFailure(type, expr, func, file, line, "");
+    }
+
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line) {
+        const Result res = ResultSuccess();
+
+        std::va_list vl{};
+        VAbortImpl(expr, func, file, line, std::addressof(res), nullptr, "", vl);
+    }
+
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const char *fmt, ...) {
+        const Result res = ResultSuccess();
+
+        std::va_list vl;
+        va_start(vl, fmt);
+        VAbortImpl(expr, func, file, line, std::addressof(res), nullptr, fmt, vl);
+    }
+
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const char *fmt, ...) {
+        std::va_list vl;
+        va_start(vl, fmt);
+        VAbortImpl(expr, func, file, line, result, nullptr, fmt, vl);
+    }
+
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const ::ams::os::UserExceptionInfo *exc_info, const char *fmt, ...) {
+        std::va_list vl;
+        va_start(vl, fmt);
+        VAbortImpl(expr, func, file, line, result, exc_info, fmt, vl);
+    }
+
+    NORETURN NOINLINE void VAbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const ::ams::os::UserExceptionInfo *exc_info, const char *fmt, std::va_list vl) {
+        /* Prepare to abort. */
+        PrepareAbort();
+
+        /* Acquire exclusive abort rights. */
+        if (g_abort_mutex.IsLockedByCurrentThread()) {
+            AbortWithValue(result->GetValue());
+        }
+
+        std::scoped_lock lk(g_abort_mutex);
+
+        /* Create abort info. */
+        std::va_list cvl;
+        va_copy(cvl, vl);
+        const diag::LogMessage message = { fmt, std::addressof(cvl) };
+
+        const AbortInfo abort_info = {
+            AbortReason_Abort,
+            std::addressof(message),
+            expr,
+            func,
+            file,
+            line,
+        };
+        const SdkAbortInfo sdk_abort_info = {
+            abort_info,
+            *result,
+            exc_info
+        };
+
+        /* Invoke observers. */
+        ::ams::diag::impl::InvokeAbortObserver(abort_info);
+        ::ams::diag::impl::InvokeSdkAbortObserver(sdk_abort_info);
+
+        /* Abort. */
+        AbortWithValue(result->GetValue());
+    }
+
+}
+
+namespace ams::impl {
+
+    NORETURN NOINLINE void UnexpectedDefaultImpl(const char *func, const char *file, int line) {
+        /* Create abort info. */
+        std::va_list vl{};
+        const ::ams::diag::LogMessage message = { "" , std::addressof(vl) };
+        const ::ams::diag::AbortInfo abort_info = {
+            ::ams::diag::AbortReason_UnexpectedDefault,
+            std::addressof(message),
+            "",
+            func,
+            file,
+            line,
+        };
+
+        /* Invoke observers. */
+        ::ams::diag::impl::InvokeAbortObserver(abort_info);
+
+        /* Abort. */
+        ::ams::diag::AbortWithValue(0);
     }
 
 }
diff --git a/libraries/libstratosphere/source/diag/diag_assertion_impl_for_nx_asm.board.nintendo_nx.s b/libraries/libstratosphere/source/diag/diag_assertion_impl_for_nx_asm.board.nintendo_nx.s
index e29c989fe..c22914cbf 100644
--- a/libraries/libstratosphere/source/diag/diag_assertion_impl_for_nx_asm.board.nintendo_nx.s
+++ b/libraries/libstratosphere/source/diag/diag_assertion_impl_for_nx_asm.board.nintendo_nx.s
@@ -21,7 +21,19 @@
 .balign 0x10
 _ZN3ams4diag4impl23FatalErrorByResultForNxENS_6ResultE:
     /* Save x27/x28. */
-    stp     x27, x28, [sp, #0x10]
+    stp     x27, x28, [sp, #-0x10]!
+    stp     x0,  xzr, [sp, #-0x10]!
+
+    /* Inline ams::diag::impl::PrepareAbort() */
+    mrs     x27, tpidrro_el0
+    strh    wzr, [x27, #0x100]
+    ldrh    w27, [x27, #0x102]
+    cbz     w27, 0f
+    svc     #0x36
+
+0:  /* Restore the value from stack. */
+    ldr     x0, [sp]
+    add     sp, sp, #0x10
 
     /* Put magic std::abort values into x27/x28. */
     mov     x28, #0xcafe
@@ -31,7 +43,7 @@ _ZN3ams4diag4impl23FatalErrorByResultForNxENS_6ResultE:
     mov     x27, #8
 
     /* Abort */
-0:
+1:
     str     x28, [x27]
     nop
-    b       0b
+    b       1b
diff --git a/libraries/libstratosphere/source/diag/diag_backtrace.cpp b/libraries/libstratosphere/source/diag/diag_backtrace.cpp
new file mode 100644
index 000000000..f8eaedf63
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/diag_backtrace.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+#include "impl/diag_get_all_backtrace.hpp"
+
+namespace ams::diag {
+
+    size_t GetBacktrace(uintptr_t *out, size_t out_size) {
+        /* Validate pre-conditions. */
+        AMS_ASSERT(out != nullptr);
+        AMS_ASSERT(out_size > 0);
+
+        /* Create the backtrace object. */
+        ::ams::diag::Backtrace bt{};
+        bt.Step();
+
+        /* Get the backtrace. */
+        return ::ams::diag::impl::GetAllBacktrace(out, out_size, bt);
+    }
+
+    #if defined(ATMOSPHERE_OS_HORIZON)
+    size_t GetBacktace(uintptr_t *out, size_t out_size, uintptr_t fp, uintptr_t sp, uintptr_t pc) {
+        /* Validate pre-conditions. */
+        AMS_ASSERT(out != nullptr);
+        AMS_ASSERT(out_size > 0);
+
+        /* Create the backtrace object. */
+        ::ams::diag::Backtrace bt{fp, sp, pc};
+        bt.Step();
+
+        /* Get the backtrace. */
+        return ::ams::diag::impl::GetAllBacktrace(out, out_size, bt);
+    }
+    #endif
+
+
+}
diff --git a/libraries/libstratosphere/source/diag/diag_log.cpp b/libraries/libstratosphere/source/diag/diag_log.cpp
index 5870e0c98..fc7980e8e 100644
--- a/libraries/libstratosphere/source/diag/diag_log.cpp
+++ b/libraries/libstratosphere/source/diag/diag_log.cpp
@@ -45,11 +45,26 @@ namespace ams::diag::impl {
     }
 
     void VLogImpl(const LogMetaData &meta, const char *fmt, std::va_list vl) {
+        #if defined(ATMOSPHERE_OS_HORIZON)
         /* Print to stack buffer. */
         char msg_buffer[DebugPrintBufferLength];
 
         /* TODO: VFormatString using utf-8 printer. */
         const size_t len = util::VSNPrintf(msg_buffer, sizeof(msg_buffer), fmt, vl);
+        #else
+        /* Print to allocated buffer. */
+        std::va_list cvl;
+        va_copy(cvl, vl);
+        const auto out_len = util::TVSNPrintf(nullptr, 0, fmt, cvl) + 1;
+        va_end(cvl);
+
+        char *msg_buffer   = static_cast<char *>(std::malloc(out_len));
+        AMS_ABORT_UNLESS(msg_buffer != nullptr);
+        ON_SCOPE_EXIT { std::free(msg_buffer); };
+
+        /* TODO: VFormatString using utf-8 printer. */
+        const size_t len = util::TVSNPrintf(msg_buffer, out_len, fmt, vl);
+        #endif
 
         /* Call log observer. */
         CallPrintDebugString()(meta, msg_buffer, len, true, true);
diff --git a/libraries/libstratosphere/source/diag/diag_symbol.cpp b/libraries/libstratosphere/source/diag/diag_symbol.cpp
new file mode 100644
index 000000000..a9a5b496a
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/diag_symbol.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+#include "impl/diag_symbol_impl.hpp"
+
+namespace ams::diag {
+
+    uintptr_t GetSymbolName(char *dst, size_t dst_size, uintptr_t address) {
+        AMS_ASSERT(dst != nullptr);
+        AMS_ASSERT(dst_size > 0);
+        AMS_ASSERT(address > 0);
+
+        return ::ams::diag::impl::GetSymbolNameImpl(dst, dst_size, address);
+    }
+
+    size_t GetSymbolSize(uintptr_t address) {
+        AMS_ASSERT(address > 0);
+
+        return ::ams::diag::impl::GetSymbolSizeImpl(address);
+    }
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_abort_observer_manager.cpp b/libraries/libstratosphere/source/diag/impl/diag_abort_observer_manager.cpp
new file mode 100644
index 000000000..ed8c6698b
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_abort_observer_manager.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+#include "diag_abort_observer_manager.hpp"
+
+namespace ams::diag::impl {
+
+    AbortObserverManager *GetAbortObserverManager() {
+        AMS_FUNCTION_LOCAL_STATIC(AbortObserverManager, s_manager);
+
+        return std::addressof(s_manager);
+    }
+
+    SdkAbortObserverManager *GetSdkAbortObserverManager() {
+        AMS_FUNCTION_LOCAL_STATIC(SdkAbortObserverManager, s_manager);
+
+        return std::addressof(s_manager);
+    }
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_abort_observer_manager.hpp b/libraries/libstratosphere/source/diag/impl/diag_abort_observer_manager.hpp
new file mode 100644
index 000000000..e2bc6f1d2
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_abort_observer_manager.hpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <stratosphere.hpp>
+#include "diag_observer_manager.hpp"
+
+namespace ams::diag::impl {
+
+    using AbortObserverManager    = ObserverManager<AbortObserverHolder, const AbortInfo &>;
+    using SdkAbortObserverManager = ObserverManager<SdkAbortObserverHolder, const SdkAbortInfo &>;
+
+    AbortObserverManager *GetAbortObserverManager();
+    SdkAbortObserverManager *GetSdkAbortObserverManager();
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_backtrace_impl.os.generic.cpp b/libraries/libstratosphere/source/diag/impl/diag_backtrace_impl.os.generic.cpp
new file mode 100644
index 000000000..9cd49070a
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_backtrace_impl.os.generic.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+
+#if defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
+#include <fcntl.h>
+#include <unistd.h>
+#endif
+
+namespace ams::diag::impl {
+
+    namespace {
+
+        #if defined(ATMOSPHERE_ARCH_X64)
+        struct StackFrame {
+            u64 fp; /* rbp */
+            u64 lr; /* rip */
+        };
+        #elif defined(ATMOSPHERE_ARCH_X86)
+        struct StackFrame {
+            u32 fp; /* ebp */
+            u32 lr; /* eip */
+        }
+        #elif defined(ATMOSPHERE_ARCH_ARM64)
+        struct StackFrame {
+            u64 fp;
+            u64 lr;
+        };
+        #elif defined(ATMOSPHERE_ARCH_ARM)
+        struct StackFrame {
+            u32 fp;
+            u32 lr;
+        }
+        #else
+            #error "Unknown architecture for generic backtrace."
+        #endif
+
+        bool TryRead(os::NativeHandle native_handle, void *dst, size_t size, const void *address) {
+            #if defined(ATMOSPHERE_OS_WINDOWS)
+            return ::ReadProcessMemory(native_handle, address, dst, size, nullptr);
+            #elif defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
+            s32 ret;
+            do {
+                ret = ::write(native_handle, address, size);
+            } while (ret < 0 && errno == EINTR);
+
+            if (ret < 0) {
+                return false;
+            }
+
+            std::memcpy(dst, address, size);
+            return true;
+            #else
+                #error "Unknown OS for Backtrace native handle"
+            #endif
+        }
+
+    }
+
+    NOINLINE void Backtrace::Initialize() {
+        /* Clear our size. */
+        m_index = 0;
+        m_size  = 0;
+
+        /* Get the base frame pointer. */
+        const void *cur_fp = __builtin_frame_address(0);
+
+        /* Try to read stack frames, until we run out. */
+        #if defined(ATMOSPHERE_OS_WINDOWS)
+        const os::NativeHandle native_handle = ::GetCurrentProcess();
+        #elif defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
+        os::NativeHandle pipe_handles[2];
+        s32 nret;
+        do { nret = ::pipe(pipe_handles); } while (nret < 0 && errno == EINTR);
+        if (nret < 0) { return; }
+        do { nret = ::fcntl(pipe_handles[0], F_SETFL, O_NONBLOCK); } while (nret < 0 && errno == EINTR);
+        if (nret < 0) { return; }
+        do { nret = ::fcntl(pipe_handles[1], F_SETFL, O_NONBLOCK); } while (nret < 0 && errno == EINTR);
+        if (nret < 0) { return; }
+        ON_SCOPE_EXIT {
+            do { nret = ::close(pipe_handles[0]); } while (nret < 0 && errno == EINTR);
+            do { nret = ::close(pipe_handles[1]); } while (nret < 0 && errno == EINTR);
+        };
+        const os::NativeHandle native_handle = pipe_handles[1];
+        if (native_handle < 0) { return; }
+        #else
+            #error "Unknown OS for Backtrace native handle"
+        #endif
+
+        StackFrame frame;
+        while (m_size < BacktraceEntryCountMax) {
+            /* Clear the frame. */
+            frame = {};
+
+            /* Read the next frame. */
+            if (!TryRead(native_handle, std::addressof(frame), sizeof(frame), cur_fp)) {
+                break;
+            }
+
+            /* Add the return address. */
+            m_backtrace_addresses[m_size++] = reinterpret_cast<void *>(frame.lr);
+
+            /* Set the next fp. */
+            cur_fp = reinterpret_cast<const void *>(frame.fp);
+        }
+    }
+
+    bool Backtrace::Step() {
+        return (++m_index) < m_size;
+    }
+
+    uintptr_t Backtrace::GetStackPointer() const {
+        return 0;
+    }
+
+    uintptr_t Backtrace::GetReturnAddress() const {
+        return reinterpret_cast<uintptr_t>(m_backtrace_addresses[m_index]);
+    }
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_backtrace_impl.os.horizon.cpp b/libraries/libstratosphere/source/diag/impl/diag_backtrace_impl.os.horizon.cpp
new file mode 100644
index 000000000..b1f05c93f
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_backtrace_impl.os.horizon.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+
+namespace ams::diag::impl {
+
+    namespace {
+
+        uintptr_t GetAddressValue(uintptr_t address) {
+            if (address == 0) {
+                return 0;
+            }
+
+            if (!util::IsAligned(address, alignof(uintptr_t))) {
+                return 0;
+            }
+
+            return *reinterpret_cast<uintptr_t *>(address);
+        }
+
+        template<typename T, size_t N>
+        svc::MemoryInfo *GetMemoryInfoPointer(T (&arr)[N]) {
+            /* Check that there's enough space. */
+            static_assert(sizeof(T) * N <= sizeof(svc::MemoryInfo));
+            static_assert(alignof(T) >= alignof(svc::MemoryInfo));
+            return reinterpret_cast<svc::MemoryInfo *>(std::addressof(arr[0]));
+        }
+
+        bool IsValidLinkRegisterValue(uintptr_t lr, svc::MemoryInfo *info) {
+            /* Ensure the memory info is valid. */
+            Result query_res;
+
+            if (!(info->base_address <= lr && lr < info->base_address + info->size) && ({ svc::PageInfo page_info; query_res = svc::QueryMemory(info, std::addressof(page_info), lr); R_FAILED(query_res); })) {
+                AMS_SDK_LOG("Failed to get backtrace. Query memory failed. (lr: %p, result: %03d-%04d)\n", reinterpret_cast<void *>(lr), query_res.GetModule(), query_res.GetDescription());
+                return false;
+            }
+
+            /* Check that lr is valid. */
+            if (lr == 0) {
+                return false;
+            }
+
+            if (!util::IsAligned(lr, sizeof(u32))) {
+                AMS_SDK_LOG("Failed to get backtrace. The link register alignment is invalid. (lr: %p)\n", reinterpret_cast<void *>(lr));
+                return false;
+            }
+
+            /* Check that the lr points to code. */
+            if (info->permission != svc::MemoryPermission_ReadExecute) {
+                AMS_SDK_LOG("Failed to get backtrace. The link register points out of the code. (lr: %p)\n", reinterpret_cast<void *>(lr));
+                return false;
+            }
+
+            return true;
+        }
+
+        void GetNormalStackInfo(Backtrace::StackInfo *out) {
+            if (void * const fiber = nullptr /* TODO: os::GetCurrentFiber() */; fiber == nullptr) {
+                /* Get thread. */
+                auto * const thread = os::GetCurrentThread();
+                out->stack_top    = reinterpret_cast<uintptr_t>(thread->stack);
+                out->stack_bottom = reinterpret_cast<uintptr_t>(thread->stack) + thread->stack_size;
+            } else {
+                /* TODO: Fiber. */
+            }
+        }
+
+        bool GetExceptionStackInfo(Backtrace::StackInfo *out, uintptr_t sp) {
+            /* Get the current stack info. */
+            uintptr_t cur_stack   = 0;
+            size_t cur_stack_size = 0;
+            os::GetCurrentStackInfo(std::addressof(cur_stack), std::addressof(cur_stack_size));
+
+            /* Get the thread's stack info. */
+            uintptr_t thread_stack   = 0;
+            size_t thread_stack_size = 0;
+            os::GetThreadStackInfo(std::addressof(thread_stack), std::addressof(thread_stack_size), os::GetCurrentThread());
+
+            /* If the current stack is the thread stack, exception stack isn't being used. */
+            if (cur_stack == thread_stack) {
+                AMS_ASSERT(cur_stack_size == thread_stack_size);
+                return false;
+            }
+
+            /* Check if the stack pointer is contained in the current stack. */
+            if (!(cur_stack <= sp && sp < cur_stack + cur_stack_size)) {
+                return false;
+            }
+
+            /* Set the output. */
+            out->stack_top    = cur_stack;
+            out->stack_bottom = cur_stack + cur_stack_size;
+            return true;
+        }
+
+    }
+
+
+    NOINLINE void Backtrace::Initialize() {
+        /* Get the stack pointer/frame pointer. */
+        uintptr_t fp, sp;
+
+        __asm__ __volatile__(
+            #if defined(ATMOSPHERE_ARCH_ARM64)
+                "mov %[fp], fp\n"
+                "mov %[sp], sp\n"
+            #elif defined(ATMOSPHERE_ARCH_ARM)
+                "mov %[fp], x29\n"
+                "mov %[sp], sp\n"
+            #else
+                #error "Unknown architecture for Horizon fp/sp retrieval."
+            #endif
+            : [fp]"=&r"(fp), [sp]"=&r"(sp)
+            :
+            : "memory"
+        );
+
+        /* Set our stack info. */
+        this->SetStackInfo(fp, sp);
+
+        /* Step, to get our first lr. */
+        this->Step();
+    }
+
+    void Backtrace::Initialize(uintptr_t fp, uintptr_t sp, uintptr_t pc) {
+        /* Set our initial lr. */
+        m_lr = pc;
+
+        /* Set our stack info. */
+        this->SetStackInfo(fp, sp);
+    }
+
+    bool Backtrace::Step() {
+        /* We can't step without a frame pointer. */
+        if (m_fp == 0) {
+            if (m_current_stack_info != std::addressof(m_normal_stack_info)) {
+                AMS_SDK_LOG("Failed to get backtrace. The frame pointer is null.\n");
+            }
+            return false;
+        }
+
+        /* The frame pointer needs to be aligned. */
+        if (!util::IsAligned(m_fp, sizeof(uintptr_t))) {
+            AMS_SDK_LOG("Failed to get backtrace. The frame pointer alignment is invalid. (fp: %p)\n", reinterpret_cast<void *>(m_fp));
+            return false;
+        }
+
+        /* Ensure our current stack info is good. */
+        if (!(m_current_stack_info->stack_top <= m_fp && m_fp < m_current_stack_info->stack_bottom)) {
+            if (m_current_stack_info != std::addressof(m_exception_stack_info) || !(m_normal_stack_info.stack_top <= m_fp && m_fp < m_normal_stack_info.stack_bottom)) {
+                AMS_SDK_LOG("Failed to get backtrace. The frame pointer points out of the stack. (fp: %p, stack: %p-%p)\n", reinterpret_cast<void *>(m_fp), reinterpret_cast<void *>(m_current_stack_info->stack_top), reinterpret_cast<void *>(m_current_stack_info->stack_bottom));
+                return false;
+            }
+
+            m_current_stack_info = std::addressof(m_normal_stack_info);
+        } else if (m_fp <= m_prev_fp) {
+            AMS_SDK_LOG("Failed to get backtrace. The frame pointer is rewinding. (fp: %p, prev fp: %p, stack: %p-%p)\n", reinterpret_cast<void *>(m_fp), reinterpret_cast<void *>(m_prev_fp), reinterpret_cast<void *>(m_current_stack_info->stack_top), reinterpret_cast<void *>(m_current_stack_info->stack_bottom));
+            return false;
+        }
+
+        /* Update our previous fp. */
+        m_prev_fp = m_fp;
+
+        /* Read lr/fp. */
+        m_lr = GetAddressValue(m_fp + sizeof(m_fp));
+        m_fp = GetAddressValue(m_fp);
+
+        /* Check that lr is valid. */
+        if (IsValidLinkRegisterValue(m_lr, GetMemoryInfoPointer(m_memory_info_buffer))) {
+            return true;
+        } else {
+            m_lr = 0;
+            return false;
+        }
+    }
+
+    uintptr_t Backtrace::GetStackPointer() const {
+        if (m_fp != 0) {
+            return m_fp - sizeof(m_fp);
+        } else {
+            return m_current_stack_info->stack_bottom - sizeof(m_fp);
+        }
+    }
+
+    uintptr_t Backtrace::GetReturnAddress() const {
+        return m_lr;
+    }
+
+    void Backtrace::SetStackInfo(uintptr_t fp, uintptr_t sp) {
+        /* Get the normal stack info. */
+        GetNormalStackInfo(std::addressof(m_normal_stack_info));
+
+        /* Get the exception stack info. */
+        if (GetExceptionStackInfo(std::addressof(m_exception_stack_info), sp)) {
+            m_current_stack_info = std::addressof(m_exception_stack_info);
+        } else {
+            m_current_stack_info = std::addressof(m_normal_stack_info);
+        }
+
+        /* Set our frame pointer. */
+        m_fp = fp;
+    }
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_default_abort_observer.cpp b/libraries/libstratosphere/source/diag/impl/diag_default_abort_observer.cpp
new file mode 100644
index 000000000..4c0224808
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_default_abort_observer.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+#include "diag_dump_stack_trace.hpp"
+
+namespace ams::diag::impl {
+
+    #if defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_DEBUGGING)
+    namespace {
+
+        constexpr const char *ToString(AbortReason reason) {
+            switch (reason) {
+                case AbortReason_Audit:
+                    return "Auditing Assertion Failure";
+                case AbortReason_Assert:
+                    return "Assertion Failure";
+                case AbortReason_UnexpectedDefault:
+                    return "Unexpected Default";
+                case AbortReason_Abort:
+                default:
+                    return "Abort";
+            }
+        }
+
+        void DefaultPrinter(const AbortInfo &info) {
+            /* Get the thread name. */
+            const char *thread_name;
+            if (auto *cur_thread = os::GetCurrentThread(); cur_thread != nullptr) {
+                thread_name = os::GetThreadNamePointer(cur_thread);
+            } else {
+                thread_name = "unknown";
+            }
+
+            #if defined(ATMOSPHERE_OS_HORIZON)
+            {
+                u64 process_id = 0;
+                u64 thread_id  = 0;
+                svc::GetProcessId(std::addressof(process_id), svc::PseudoHandle::CurrentProcess);
+                svc::GetThreadId(std::addressof(thread_id), svc::PseudoHandle::CurrentThread);
+                AMS_SDK_LOG("%s: '%s' in %s, process=0x%02" PRIX64 ", thread=%" PRIu64 " (%s)\n%s:%d\n", ToString(info.reason), info.expr, info.func, process_id, thread_id, thread_name, info.file, info.line);
+            }
+            #elif defined(ATMOSPHERE_OS_WINDOWS)
+            {
+                DWORD process_id = ::GetCurrentProcessId();
+                DWORD thread_id  = ::GetCurrentThreadId();
+                AMS_SDK_LOG("%s: '%s' in %s, process=0x%" PRIX64 ", thread=%" PRIu64 " (%s)\n%s:%d\n", ToString(info.reason), info.expr, info.func, static_cast<u64>(process_id), static_cast<u64>(thread_id), thread_name, info.file, info.line);
+            }
+            #else
+            {
+                AMS_SDK_LOG("%s: '%s' in %s, thread=%s\n%s:%d\n", ToString(info.reason), info.expr, info.func, thread_name, info.file, info.line);
+            }
+            #endif
+
+            AMS_SDK_VLOG(info.message->fmt, *(info.message->vl));
+            AMS_SDK_LOG("\n");
+
+            TentativeDumpStackTrace();
+        }
+
+    }
+    #endif
+
+    void DefaultAbortObserver(const AbortInfo &info) {
+        #if defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING)
+        DefaultPrinter(info);
+        #else
+        AMS_UNUSED(info);
+        #endif
+    }
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_dump_stack_trace.hpp b/libraries/libstratosphere/source/diag/impl/diag_dump_stack_trace.hpp
new file mode 100644
index 000000000..295b2c89b
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_dump_stack_trace.hpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <stratosphere.hpp>
+
+namespace ams::diag::impl {
+
+    void TentativeDumpStackTrace();
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_dump_stack_trace.os.generic.cpp b/libraries/libstratosphere/source/diag/impl/diag_dump_stack_trace.os.generic.cpp
new file mode 100644
index 000000000..560b53a4c
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_dump_stack_trace.os.generic.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+#include "diag_dump_stack_trace.hpp"
+
+namespace ams::diag::impl {
+
+    void TentativeDumpStackTrace() {
+        AMS_SDK_LOG("----------------Stack Trace----------------\n");
+        {
+            /* Get the backtrace. */
+            constexpr size_t MaxBackTraceSize = 0x40;
+            uintptr_t backtrace[MaxBackTraceSize];
+            const size_t num_items = ::ams::diag::GetBacktrace(backtrace, MaxBackTraceSize);
+
+            /* Print each item. */
+            for (size_t i = 0; i < num_items; ++i) {
+                char symbol_name[0x200];
+                if (const uintptr_t symbol_base = ::ams::diag::GetSymbolName(symbol_name, sizeof(symbol_name), backtrace[i] - 1); symbol_base != 0) {
+                    AMS_SDK_LOG("0x%016" PRIX64 " [ %s+0x%" PRIX64 " ]\n", static_cast<u64>(backtrace[i]), symbol_name, static_cast<u64>(backtrace[i] - (symbol_base + 1)));
+                } else {
+                    AMS_SDK_LOG("0x%016" PRIX64 " [ unknown ]\n", static_cast<u64>(backtrace[i]));
+                }
+            }
+        }
+        AMS_SDK_LOG("-------------------------------------------\n");
+    }
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_dump_stack_trace.os.horizon.cpp b/libraries/libstratosphere/source/diag/impl/diag_dump_stack_trace.os.horizon.cpp
new file mode 100644
index 000000000..971bc538c
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_dump_stack_trace.os.horizon.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+#include "diag_dump_stack_trace.hpp"
+
+namespace ams::diag::impl {
+
+    void TentativeDumpStackTrace() {
+        /* TODO */
+    }
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_get_all_backtrace.cpp b/libraries/libstratosphere/source/diag/impl/diag_get_all_backtrace.cpp
new file mode 100644
index 000000000..a96f8c5e4
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_get_all_backtrace.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+
+namespace ams::diag::impl {
+
+    namespace {
+
+        constinit uintptr_t g_abort_impl_return_address = std::numeric_limits<uintptr_t>::max();
+
+    }
+
+    size_t GetAllBacktrace(uintptr_t *out, size_t out_size, ::ams::diag::Backtrace &bt) {
+        size_t count = 0;
+        do {
+            /* Check that we can write another return address. */
+            if (count >= out_size) {
+                break;
+            }
+
+            /* Get the current return address. */
+            const uintptr_t ret_addr = bt.GetReturnAddress();
+
+            /* If it's abort impl, reset the trace we're writing. */
+            if (ret_addr == g_abort_impl_return_address) {
+                count = 0;
+            }
+
+            /* Set the output pointer. */
+            out[count++] = ret_addr;
+        } while (bt.Step());
+
+        /* Return the number of addresses written. */
+        return count;
+    }
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_get_all_backtrace.hpp b/libraries/libstratosphere/source/diag/impl/diag_get_all_backtrace.hpp
new file mode 100644
index 000000000..1a5a7636f
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_get_all_backtrace.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <stratosphere.hpp>
+
+namespace ams::diag::impl {
+
+    void SetAbortImplAddress(uintptr_t address);
+
+    size_t GetAllBacktrace(uintptr_t *out, size_t out_size, ::ams::diag::Backtrace &bt);
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_invoke_abort.hpp b/libraries/libstratosphere/source/diag/impl/diag_invoke_abort.hpp
new file mode 100644
index 000000000..513bd0abc
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_invoke_abort.hpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <stratosphere.hpp>
+
+namespace ams::diag::impl {
+
+    void InvokeAbortObserver(const AbortInfo &info);
+    void InvokeSdkAbortObserver(const SdkAbortInfo &info);
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_invoke_abort.os.generic.cpp b/libraries/libstratosphere/source/diag/impl/diag_invoke_abort.os.generic.cpp
new file mode 100644
index 000000000..33812ab3a
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_invoke_abort.os.generic.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+#include "diag_abort_observer_manager.hpp"
+#include "diag_invoke_abort.hpp"
+
+namespace ams::diag::impl {
+
+    extern bool g_enable_default_abort_observer;
+
+    void DefaultAbortObserver(const AbortInfo &info);
+
+    void InvokeAbortObserver(const AbortInfo &info) {
+        if (g_enable_default_abort_observer) {
+            DefaultAbortObserver(info);
+        }
+
+        GetAbortObserverManager()->InvokeAllObserver(info);
+    }
+
+    void InvokeSdkAbortObserver(const SdkAbortInfo &info) {
+        GetSdkAbortObserverManager()->InvokeAllObserver(info);
+    }
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_invoke_abort.os.horizon.cpp b/libraries/libstratosphere/source/diag/impl/diag_invoke_abort.os.horizon.cpp
new file mode 100644
index 000000000..33812ab3a
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_invoke_abort.os.horizon.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+#include "diag_abort_observer_manager.hpp"
+#include "diag_invoke_abort.hpp"
+
+namespace ams::diag::impl {
+
+    extern bool g_enable_default_abort_observer;
+
+    void DefaultAbortObserver(const AbortInfo &info);
+
+    void InvokeAbortObserver(const AbortInfo &info) {
+        if (g_enable_default_abort_observer) {
+            DefaultAbortObserver(info);
+        }
+
+        GetAbortObserverManager()->InvokeAllObserver(info);
+    }
+
+    void InvokeSdkAbortObserver(const SdkAbortInfo &info) {
+        GetSdkAbortObserverManager()->InvokeAllObserver(info);
+    }
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.hpp b/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.hpp
new file mode 100644
index 000000000..cdaebf89e
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.hpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <stratosphere.hpp>
+
+namespace ams::diag::impl {
+
+    uintptr_t GetSymbolNameImpl(char *dst, size_t dst_size, uintptr_t address);
+    size_t GetSymbolSizeImpl(uintptr_t address);
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.os.generic.cpp b/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.os.generic.cpp
new file mode 100644
index 000000000..888fc7a5a
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.os.generic.cpp
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+#include "diag_symbol_impl.hpp"
+
+#define PACKAGE "stratosphere"
+#define PACKAGE_VERSION STRINGIFY(ATMOSPHERE_RELEASE_VERSION_MAJOR.ATMOSPHERE_RELEASE_VERSION_MINOR.ATMOSPHERE_RELEASE_VERSION_MICRO)
+
+/* msys2 mingw64 puts headers inside binutils/ */
+#if defined(ATMOSPHERE_OS_WINDOWS)
+#include <binutils/bfd.h>
+#include <psapi.h>
+#else
+#include <bfd.h>
+#endif
+
+#if defined(ATMOSPHERE_OS_LINUX)
+#include <unistd.h>
+#include <dlfcn.h>
+#include <link.h>
+
+extern "C" char __init_array_start;
+#endif
+
+#define HAVE_DECL_BASENAME 1
+#include <libiberty/demangle.h>
+
+namespace ams::diag::impl {
+
+    namespace {
+
+        class BfdHelper {
+            private:
+                bfd *m_handle;
+                asymbol **m_symbol;
+                size_t m_num_symbol;
+                size_t m_num_func_symbol;
+                const char *m_module_name;
+                uintptr_t m_module_address;
+                size_t m_module_size;
+            private:
+                BfdHelper() : m_handle(nullptr), m_symbol(nullptr), m_module_name(nullptr) {
+                    /* Get the current executable name. */
+                    char exe_path[4_KB] = {};
+                    GetExecutablePath(exe_path, sizeof(exe_path));
+
+                    /* Open bfd. */
+                    bfd *b = ::bfd_openr(exe_path, 0);
+                    if (b == nullptr) {
+                        return;
+                    }
+                    auto bfd_guard = SCOPE_GUARD { ::bfd_close(b); };
+
+                    /* Check the format. */
+                    if (!::bfd_check_format(b, bfd_object)) {
+                        return;
+                    }
+
+                    /* Verify the file has symbols. */
+                    if ((bfd_get_file_flags(b) & HAS_SYMS) == 0) {
+                        return;
+                    }
+
+                    /* Read the symbols. */
+                    unsigned int _;
+                    void *symbol_table;
+                    s64 num_symbols = bfd_read_minisymbols(b, false, std::addressof(symbol_table), std::addressof(_));
+                    if (num_symbols == 0) {
+                        num_symbols = bfd_read_minisymbols(b, true, std::addressof(symbol_table), std::addressof(_));
+                        if (num_symbols < 0) {
+                            return;
+                        }
+                    }
+
+                    /* We successfully got the symbol table. */
+                    bfd_guard.Cancel();
+
+                    m_handle     = b;
+                    m_symbol     = reinterpret_cast<asymbol **>(symbol_table);
+                    m_num_symbol = static_cast<size_t>(num_symbols);
+
+                    /* Sort the symbol table. */
+                    std::sort(m_symbol + 0, m_symbol + m_num_symbol, [] (asymbol *lhs, asymbol *rhs) {
+                        const bool l_func = (lhs->flags & BSF_FUNCTION);
+                        const bool r_func = (rhs->flags & BSF_FUNCTION);
+                        if (l_func == r_func) {
+                            return bfd_asymbol_value(lhs) < bfd_asymbol_value(rhs);
+                        } else {
+                            return l_func;
+                        }
+                    });
+
+                    /* Determine number of function symbols. */
+                    m_num_func_symbol = 0;
+                    for (size_t i = 0; i < m_num_symbol; ++i) {
+                        if ((m_symbol[i]->flags & BSF_FUNCTION) == 0) {
+                            m_num_func_symbol = i;
+                            break;
+                        }
+                    }
+
+                    for (int i = std::strlen(exe_path) - 1; i >= 0; --i) {
+                        if (exe_path[i] == '/' || exe_path[i] == '\\') {
+                            m_module_name = strdup(exe_path + i + 1);
+                            break;
+                        }
+                    }
+
+                    /* Get our module base/size. */
+                    #if defined(ATMOSPHERE_OS_WINDOWS)
+                    {
+                        MODULEINFO module_info;
+                        if (::GetModuleInformation(::GetCurrentProcess(), ::GetModuleHandleA(nullptr), std::addressof(module_info), sizeof(module_info))) {
+                            m_module_address = reinterpret_cast<uintptr_t>(module_info.lpBaseOfDll);
+                            m_module_size    = static_cast<size_t>(module_info.SizeOfImage);
+                        }
+                    }
+                    #elif defined(ATMOSPHERE_OS_LINUX)
+                    {
+                        m_module_address = _r_debug.r_map->l_addr;
+
+                        m_module_size = reinterpret_cast<uintptr_t>(std::addressof(__init_array_start)) - m_module_address;
+                    }
+                    #endif
+                }
+
+                ~BfdHelper() {
+                    if (m_symbol != nullptr) {
+                        std::free(m_symbol);
+                    }
+                    if (m_handle != nullptr) {
+                       ::bfd_close(m_handle);
+                    }
+                }
+            public:
+                static BfdHelper &GetInstance() {
+                    AMS_FUNCTION_LOCAL_STATIC(BfdHelper, s_bfd_helper_instance);
+                    return s_bfd_helper_instance;
+                }
+            private:
+                size_t GetSymbolSizeImpl(asymbol **symbol) const {
+                    /* Do our best to guess. */
+                    const auto vma = bfd_asymbol_value(*symbol);
+                    if (symbol != m_symbol + m_num_func_symbol - 1) {
+                        return bfd_asymbol_value(*(symbol + 1)) - vma;
+                    } else {
+                        const auto *sec = (*symbol)->section;
+                        return (sec->vma + sec->size) - vma;
+                    }
+                }
+
+                std::ptrdiff_t GetSymbolAddressDisplacement(uintptr_t address) const {
+                    std::ptrdiff_t displacement = 0;
+
+                    if (m_module_address <= address && address < m_module_address + m_module_size) {
+                        displacement = m_module_address;
+
+                        #if defined(ATMOSPHERE_OS_WINDOWS)
+                        {
+                            #if defined(__MINGW64__)
+                                displacement -= UINT64_C(0x140000000);
+                            #elif defined(__MINGW32__)
+                                displacement -= UINT64_C(0x400000);
+                            #else
+                                #error "Unknown build context for windows module base!"
+                            #endif
+                        }
+                        #endif
+                    }
+
+                    return displacement;
+                }
+
+                asymbol **GetBestSymbol(uintptr_t address) const {
+                    /* Adjust the symbol address. */
+                    address -= this->GetSymbolAddressDisplacement(address);
+
+                    asymbol **best_symbol = std::lower_bound(m_symbol + 0, m_symbol + m_num_func_symbol, address, [](asymbol *lhs, uintptr_t rhs) {
+                        return bfd_asymbol_value(lhs) < rhs;
+                    });
+
+                    if (best_symbol == m_symbol + m_num_func_symbol) {
+                        return nullptr;
+                    }
+
+                    if (bfd_asymbol_value(*best_symbol) != address && best_symbol > m_symbol) {
+                        --best_symbol;
+                    }
+
+                    const auto vma = bfd_asymbol_value(*best_symbol);
+                    const auto end = vma + this->GetSymbolSizeImpl(best_symbol);
+
+                    if (vma <= address && address < end) {
+                        return best_symbol;
+                    } else {
+                        return nullptr;
+                    }
+                }
+            public:
+                uintptr_t GetSymbolName(char *dst, size_t dst_size, uintptr_t address) const {
+                    /* Get the symbol. */
+                    auto **symbol = this->GetBestSymbol(address);
+                    if (symbol == nullptr) {
+                        return 0;
+                    }
+
+                    /* Print the symbol. */
+                    const char *name = bfd_asymbol_name(*symbol);
+                    if (auto *demangled = bfd_demangle(m_handle, name, DMGL_ANSI | DMGL_PARAMS); demangled != nullptr) {
+                        util::TSNPrintf(dst, dst_size, "%s", demangled);
+                        std::free(demangled);
+                    } else {
+                        util::TSNPrintf(dst, dst_size, "%s", name);
+                    }
+
+                    return bfd_asymbol_value(*symbol) + this->GetSymbolAddressDisplacement(address);
+                }
+
+                size_t GetSymbolSize(uintptr_t address) const {
+                    /* Get the symbol. */
+                    auto **symbol = this->GetBestSymbol(address);
+                    if (symbol == nullptr) {
+                        return 0;
+                    }
+
+                    return this->GetSymbolSizeImpl(symbol);
+                }
+            private:
+                static void GetExecutablePath(char *dst, size_t dst_size) {
+                    #if defined(ATMOSPHERE_OS_WINDOWS)
+                    {
+                        /* Get the module file name. */
+                        wchar_t module_file_name[0x1000];
+                        if (::GetModuleFileNameW(0, module_file_name, util::size(module_file_name)) == 0) {
+                            dst[0] = 0;
+                            return;
+                        }
+
+                        /* Convert to utf-8. */
+                        const auto res = ::WideCharToMultiByte(CP_UTF8, 0, module_file_name, -1, dst, dst_size, nullptr, nullptr);
+                        if (res == 0) {
+                            dst[0] = 0;
+                            return;
+                        }
+                    }
+                    #elif defined(ATMOSPHERE_OS_LINUX)
+                    {
+                        if (::readlink("/proc/self/exe", dst, dst_size) == -1) {
+                            dst[0] = 0;
+                            return;
+                        }
+                    }
+                    #elif defined(ATMOSPHERE_OS_MACOS)
+                    {
+                        if (_NSGetExecutablePath(dst, dst_size) != 0) {
+                            dst[0] = 0;
+                            return;
+                        }
+                    }
+                    #else
+                    #error "Unknown OS for BfdHelper GetExecutablePath"
+                    #endif
+                }
+        };
+
+    }
+
+    uintptr_t GetSymbolNameImpl(char *dst, size_t dst_size, uintptr_t address) {
+        return BfdHelper::GetInstance().GetSymbolName(dst, dst_size, address);
+    }
+
+    size_t GetSymbolSizeImpl(uintptr_t address) {
+        return BfdHelper::GetInstance().GetSymbolSize(address);
+    }
+
+}
diff --git a/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.os.horizon.cpp b/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.os.horizon.cpp
new file mode 100644
index 000000000..28e74a785
--- /dev/null
+++ b/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.os.horizon.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+#include "diag_symbol_impl.hpp"
+
+namespace ams::diag::impl {
+
+    uintptr_t GetSymbolNameImpl(char *dst, size_t dst_size, uintptr_t address) {
+        AMS_UNUSED(dst, dst_size, address);
+        AMS_ABORT("TODO");
+    }
+
+    size_t GetSymbolSizeImpl(uintptr_t address) {
+        AMS_UNUSED(address);
+        AMS_ABORT("TODO");
+    }
+
+}
diff --git a/libraries/libstratosphere/source/fs/fs_access_log.cpp b/libraries/libstratosphere/source/fs/fs_access_log.cpp
index 9006a921f..3da798fd9 100644
--- a/libraries/libstratosphere/source/fs/fs_access_log.cpp
+++ b/libraries/libstratosphere/source/fs/fs_access_log.cpp
@@ -144,6 +144,14 @@ namespace ams::fs::impl {
         }
     }
 
+    template<> const char *IdString::ToString<fs::MountHostOption>(fs::MountHostOption id) {
+        if (id == MountHostOption::PseudoCaseSensitive) {
+            return "MountHostOptionFlag_PseudoCaseSensitive";
+        } else {
+            return ToValueString(static_cast<int>(id._value));
+        }
+    }
+
     template<> const char *IdString::ToString<fs::BisPartitionId>(fs::BisPartitionId id) {
         switch (id) {
             using enum fs::BisPartitionId;
diff --git a/libraries/libstratosphere/source/os/impl/os_cache_impl.hpp b/libraries/libstratosphere/source/os/impl/os_cache_impl.hpp
index 7d3a23f21..663f51bbc 100644
--- a/libraries/libstratosphere/source/os/impl/os_cache_impl.hpp
+++ b/libraries/libstratosphere/source/os/impl/os_cache_impl.hpp
@@ -23,7 +23,7 @@
 #elif defined(ATMOSPHERE_OS_LINUX)
     #include "os_cache_impl.os.linux.hpp"
 #elif defined(ATMOSPHERE_OS_MACOS)
-    #include "os_cache_impl.os.linux.hpp"
+    #include "os_cache_impl.os.macos.hpp"
 #else
     #error "Unknown OS for CacheImpl"
 #endif
diff --git a/libraries/libstratosphere/source/os/impl/os_debug_impl.hpp b/libraries/libstratosphere/source/os/impl/os_debug_impl.hpp
new file mode 100644
index 000000000..d8001d308
--- /dev/null
+++ b/libraries/libstratosphere/source/os/impl/os_debug_impl.hpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <stratosphere.hpp>
+
+#if defined(ATMOSPHERE_OS_HORIZON)
+    #include "os_debug_impl.os.horizon.hpp"
+#elif defined(ATMOSPHERE_OS_WINDOWS)
+    #include "os_debug_impl.os.windows.hpp"
+#elif defined(ATMOSPHERE_OS_LINUX)
+    #include "os_debug_impl.os.linux.hpp"
+#elif defined(ATMOSPHERE_OS_MACOS)
+    #include "os_debug_impl.os.macos.hpp"
+#else
+    #error "Unknown OS for DebugImpl"
+#endif
diff --git a/libraries/libstratosphere/source/os/impl/os_debug_impl.os.horizon.hpp b/libraries/libstratosphere/source/os/impl/os_debug_impl.os.horizon.hpp
new file mode 100644
index 000000000..e35a6092b
--- /dev/null
+++ b/libraries/libstratosphere/source/os/impl/os_debug_impl.os.horizon.hpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <stratosphere.hpp>
+#include "os_thread_manager.hpp"
+
+namespace ams::os::impl {
+
+    class DebugHorizonImpl {
+        public:
+            static uintptr_t GetCurrentStackPointer() {
+                uintptr_t v;
+                __asm__ __volatile__("mov %[v], sp" : [v]"=&r"(v) :: "memory");
+                return v;
+            }
+
+            static void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size) {
+                /* Check pre-conditions. */
+                AMS_ASSERT(out_stack != nullptr);
+                AMS_ASSERT(out_size != nullptr);
+
+                /* Get the current thread. */
+                auto *cur_thread = os::impl::GetCurrentThread();
+                auto *cur_fiber  = cur_thread->current_fiber;
+
+                /* Get the current stack pointer. */
+                uintptr_t cur_sp = GetCurrentStackPointer();
+
+                /* Determine current stack extents, TODO Fiber */
+                uintptr_t stack_top = reinterpret_cast<uintptr_t>(cur_fiber == nullptr ? cur_thread->stack : /* TODO: cur_fiber->stack */ nullptr);
+                size_t stack_size   = reinterpret_cast<size_t>(cur_fiber == nullptr ? cur_thread->stack_size : /* TODO: cur_fiber->stack_size */ 0);
+
+                uintptr_t stack_bottom = stack_top + stack_size;
+
+                /* TODO: User exception handler, check if stack is out of range and use exception stack. */
+
+                /* Check that the stack pointer is in bounds. */
+                AMS_ABORT_UNLESS((stack_top <= cur_sp) && (cur_sp < stack_bottom));
+
+                /* Set the output. */
+                *out_stack = stack_top;
+                *out_size  = stack_size;
+            }
+
+            static void QueryMemoryInfo(os::MemoryInfo *out) {
+                AMS_UNUSED(out);
+                AMS_ABORT("TODO: Horizon QueryMemoryInfo");
+            }
+
+            static Tick GetIdleTickCount() {
+                u64 value;
+                R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), svc::InfoType_IdleTickCount, svc::InvalidHandle, static_cast<u64>(-1)));
+
+                return os::Tick(value);
+            }
+
+            static Tick GetThreadTickCount() {
+                u64 value;
+                R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), svc::InfoType_ThreadTickCount, svc::PseudoHandle::CurrentThread, static_cast<u64>(-1)));
+
+                return os::Tick(value);
+            }
+
+            static int GetFreeThreadCount() {
+                u64 value;
+                R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), svc::InfoType_FreeThreadCount, svc::PseudoHandle::CurrentProcess, 0));
+
+                AMS_ASSERT(value <= static_cast<u64>(std::numeric_limits<int>::max()));
+
+                return static_cast<int>(value);
+            }
+    };
+
+    using DebugImpl = DebugHorizonImpl;
+
+}
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/os/impl/os_debug_impl.os.linux.hpp b/libraries/libstratosphere/source/os/impl/os_debug_impl.os.linux.hpp
new file mode 100644
index 000000000..52f091700
--- /dev/null
+++ b/libraries/libstratosphere/source/os/impl/os_debug_impl.os.linux.hpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <stratosphere.hpp>
+
+namespace ams::os::impl {
+
+    class DebugLinuxImpl {
+        public:
+            static void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size) {
+                /* Check pre-conditions. */
+                AMS_ASSERT(out_stack != nullptr);
+                AMS_ASSERT(out_size != nullptr);
+
+                /* Get the current stack by pthread */
+                pthread_attr_t attr;
+                pthread_attr_init(std::addressof(attr));
+                ON_SCOPE_EXIT { pthread_attr_destroy(std::addressof(attr)); };
+
+                const auto getattr_res = pthread_getattr_np(pthread_self(), std::addressof(attr));
+                AMS_ABORT_UNLESS(getattr_res == 0);
+
+                /* Get the thread satck. */
+                void *base = nullptr;
+                size_t size  = 0;
+                const auto getstack_res = pthread_getattr_np(pthread_self(), std::addressof(attr));
+                AMS_ABORT_UNLESS(getstack_res == 0);
+
+                *out_stack = reinterpret_cast<uintptr_t>(base);
+                *out_size  = size;
+            }
+
+            static void QueryMemoryInfo(os::MemoryInfo *out) {
+                AMS_UNUSED(out);
+                AMS_ABORT("TODO: Linux QueryMemoryInfo");
+            }
+
+            static Tick GetIdleTickCount() {
+                return os::Tick(0);
+            }
+
+            static Tick GetThreadTickCount() {
+                return os::Tick(0);
+            }
+
+            static int GetFreeThreadCount() {
+                return 0;
+            }
+    };
+
+    using DebugImpl = DebugLinuxImpl;
+
+}
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/os/impl/os_debug_impl.os.macos.hpp b/libraries/libstratosphere/source/os/impl/os_debug_impl.os.macos.hpp
new file mode 100644
index 000000000..637460a11
--- /dev/null
+++ b/libraries/libstratosphere/source/os/impl/os_debug_impl.os.macos.hpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <stratosphere.hpp>
+
+namespace ams::os::impl {
+
+    class DebugMacosImpl {
+        public:
+            static void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size) {
+                /* Check pre-conditions. */
+                AMS_ASSERT(out_stack != nullptr);
+                AMS_ASSERT(out_size != nullptr);
+
+                /* Get the current stack by pthread */
+                pthread_attr_t attr;
+                pthread_attr_init(std::addressof(attr));
+                ON_SCOPE_EXIT { pthread_attr_destroy(std::addressof(attr)); };
+
+                const auto getattr_res = pthread_getattr_np(pthread_self(), std::addressof(attr));
+                AMS_ABORT_UNLESS(getattr_res == 0);
+
+                /* Get the thread satck. */
+                void *base = nullptr;
+                size_t size  = 0;
+                const auto getstack_res = pthread_getattr_np(pthread_self(), std::addressof(attr));
+                AMS_ABORT_UNLESS(getstack_res == 0);
+
+                *out_stack = reinterpret_cast<uintptr_t>(base);
+                *out_size  = size;
+            }
+
+            static void QueryMemoryInfo(os::MemoryInfo *out) {
+                AMS_UNUSED(out);
+                AMS_ABORT("TODO: macOS QueryMemoryInfo");
+            }
+
+            static Tick GetIdleTickCount() {
+                return os::Tick(0);
+            }
+
+            static Tick GetThreadTickCount() {
+                return os::Tick(0);
+            }
+
+            static int GetFreeThreadCount() {
+                return 0;
+            }
+    };
+
+    using DebugImpl = DebugMacosImpl;
+
+}
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/os/impl/os_debug_impl.os.windows.hpp b/libraries/libstratosphere/source/os/impl/os_debug_impl.os.windows.hpp
new file mode 100644
index 000000000..7e596f36d
--- /dev/null
+++ b/libraries/libstratosphere/source/os/impl/os_debug_impl.os.windows.hpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+#include <stratosphere.hpp>
+
+namespace ams::os::impl {
+
+    class DebugWindowsImpl {
+        public:
+            static void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size) {
+                /* Check pre-conditions. */
+                AMS_ASSERT(out_stack != nullptr);
+                AMS_ASSERT(out_size != nullptr);
+
+                /* Get the current stack by NT_TIB */
+                auto *tib = reinterpret_cast<NT_TIB *>(::NtCurrentTeb());
+
+                *out_stack = reinterpret_cast<uintptr_t>(tib->StackLimit);
+                *out_size  = reinterpret_cast<uintptr_t>(tib->StackBase) - reinterpret_cast<uintptr_t>(tib->StackLimit);
+            }
+
+            static void QueryMemoryInfo(os::MemoryInfo *out) {
+                AMS_UNUSED(out);
+                AMS_ABORT("TODO: Windows QueryMemoryInfo");
+            }
+
+            static Tick GetIdleTickCount() {
+                return os::Tick(0);
+            }
+
+            static Tick GetThreadTickCount() {
+                return os::Tick(0);
+            }
+
+            static int GetFreeThreadCount() {
+                return 0;
+            }
+    };
+
+    using DebugImpl = DebugWindowsImpl;
+
+}
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/os/os_debug.cpp b/libraries/libstratosphere/source/os/os_debug.cpp
new file mode 100644
index 000000000..57bb08ee9
--- /dev/null
+++ b/libraries/libstratosphere/source/os/os_debug.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+#include "impl/os_debug_impl.hpp"
+
+namespace ams::os {
+
+    void GetCurrentStackInfo(uintptr_t *out_stack, size_t *out_size) {
+        /* Get the current stack info. */
+        uintptr_t stack_top = 0;
+        size_t stack_size   = 0;
+        impl::DebugImpl::GetCurrentStackInfo(std::addressof(stack_top), std::addressof(stack_size));
+
+        /* Basic sanity check. */
+        uintptr_t sp = reinterpret_cast<uintptr_t>(std::addressof(stack_top));
+        AMS_ASSERT((stack_top <= sp) && (sp < (stack_top + stack_size)));
+        AMS_UNUSED(sp);
+
+        /* Set the output. */
+        if (out_stack != nullptr) {
+            *out_stack = stack_top;
+        }
+        if (out_size != nullptr) {
+            *out_size = stack_size;
+        }
+    }
+
+    void QueryMemoryInfo(MemoryInfo *out) {
+        return impl::DebugImpl::QueryMemoryInfo(out);
+    }
+
+    Tick GetIdleTickCount() {
+        return impl::DebugImpl::GetIdleTickCount();
+    }
+
+    int GetFreeThreadCount() {
+        return impl::DebugImpl::GetFreeThreadCount();
+    }
+
+}
diff --git a/libraries/libstratosphere/source/result/result_on_assertion.cpp b/libraries/libstratosphere/source/result/result_on_assertion.cpp
deleted file mode 100644
index f9eb49d9c..000000000
--- a/libraries/libstratosphere/source/result/result_on_assertion.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) Atmosphère-NX
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-#include <stratosphere.hpp>
-
-namespace ams::result::impl {
-
-        NORETURN WEAK_SYMBOL void OnResultAbort(const char *file, int line, const char *func, const char *expr, Result result) {
-            ::ams::diag::AbortImpl(file, line, func, expr, result.GetValue(), "Result Abort: 2%03d-%04d", result.GetModule(), result.GetDescription());
-            AMS_INFINITE_LOOP();
-            __builtin_unreachable();
-        }
-
-        NORETURN WEAK_SYMBOL void OnResultAbort(Result result) {
-            OnResultAbort("", 0, "", "", result);
-        }
-
-        NORETURN WEAK_SYMBOL void OnResultAssertion(const char *file, int line, const char *func, const char *expr, Result result) {
-            ::ams::diag::AssertionFailureImpl(file, line, func, expr, result.GetValue(), "Result Assertion: 2%03d-%04d", result.GetModule(), result.GetDescription());
-            AMS_INFINITE_LOOP();
-            __builtin_unreachable();
-        }
-
-        NORETURN WEAK_SYMBOL void OnResultAssertion(Result result) {
-            OnResultAssertion("", 0, "", "", result);
-        }
-
-}
\ No newline at end of file
diff --git a/libraries/libstratosphere/source/sf/hipc/sf_hipc_api.os.generic.cpp b/libraries/libstratosphere/source/sf/hipc/sf_hipc_api.os.generic.cpp
new file mode 100644
index 000000000..a6fd71d34
--- /dev/null
+++ b/libraries/libstratosphere/source/sf/hipc/sf_hipc_api.os.generic.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stratosphere.hpp>
+
+namespace ams::sf::hipc {
+
+    void AttachMultiWaitHolderForAccept(os::MultiWaitHolderType *, os::NativeHandle) {
+        AMS_ABORT("TODO: Generic ams::sf::hipc::AttachMultiWaitHolderForAccept");
+    }
+
+    void AttachMultiWaitHolderForReply(os::MultiWaitHolderType *, os::NativeHandle) {
+        AMS_ABORT("TODO: Generic ams::sf::hipc::AttachMultiWaitHolderForAccept");
+    }
+
+    Result Receive(ReceiveResult *, os::NativeHandle, const cmif::PointerAndSize &) {
+        AMS_ABORT("TODO: Generic ams::sf::hipc::Receive(ReceiveResult *, os::NativeHandle, const cmif::PointerAndSize &)");
+    }
+
+    Result Receive(bool *, os::NativeHandle, const cmif::PointerAndSize &) {
+        AMS_ABORT("TODO: Generic ams::sf::hipc::Receive(bool *, os::NativeHandle, const cmif::PointerAndSize &)");
+    }
+
+    Result Reply(os::NativeHandle, const cmif::PointerAndSize &) {
+        AMS_ABORT("TODO: Generic ams::sf::hipc::Reply");
+    }
+
+    Result CreateSession(os::NativeHandle *, os::NativeHandle *) {
+        AMS_ABORT("TODO: Generic ams::sf::hipc::CreateSession");
+    }
+
+}
diff --git a/libraries/libvapours/include/vapours/assert.hpp b/libraries/libvapours/include/vapours/assert.hpp
index 836013437..d901f2d9b 100644
--- a/libraries/libvapours/include/vapours/assert.hpp
+++ b/libraries/libvapours/include/vapours/assert.hpp
@@ -16,48 +16,103 @@
 #pragma once
 #include <vapours/common.hpp>
 
+namespace ams {
+
+    class Result;
+
+    namespace os {
+
+        struct UserExceptionInfo;
+
+    }
+
+    namespace impl {
+
+        NORETURN void UnexpectedDefaultImpl(const char *func, const char *file, int line);
+
+    }
+
+}
+
 namespace ams::diag {
 
-    NORETURN void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) __attribute__((format(printf, 6, 7)));
-    NORETURN void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value);
+    enum AssertionType {
+        AssertionType_Audit,
+        AssertionType_Assert,
+    };
 
-    NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) __attribute__((format(printf, 6, 7)));
-    NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value);
-    NORETURN void AbortImpl();
+    struct LogMessage;
+
+    struct AssertionInfo {
+        AssertionType type;
+        const LogMessage *message;
+        const char *expr;
+        const char *func;
+        const char *file;
+        int line;
+    };
+
+    enum AbortReason {
+        AbortReason_Audit,
+        AbortReason_Assert,
+        AbortReason_Abort,
+        AbortReason_UnexpectedDefault,
+    };
+
+    struct AbortInfo {
+        AbortReason reason;
+        const LogMessage *message;
+        const char *expr;
+        const char *func;
+        const char *file;
+        int line;
+    };
+
+    void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line, const char *format, ...) __attribute__((format(printf, 6, 7)));
+    void OnAssertionFailure(AssertionType type, const char *expr, const char *func, const char *file, int line);
+
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line);
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const char *format, ...) __attribute__((format(printf, 5, 6)));
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const char *format, ...) __attribute__((format(printf, 6, 7)));
+
+    NORETURN void AbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const ::ams::os::UserExceptionInfo *exception_info, const char *fmt, ...) __attribute__((format(printf, 7, 8)));
+
+    NORETURN void VAbortImpl(const char *expr, const char *func, const char *file, int line, const ::ams::Result *result, const ::ams::os::UserExceptionInfo *exception_info, const char *fmt, std::va_list vl);
 
 }
 
 #ifdef AMS_ENABLE_DETAILED_ASSERTIONS
-#define AMS_CALL_ASSERT_FAIL_IMPL(cond, ...) ::ams::diag::AssertionFailureImpl(__FILE__, __LINE__, __PRETTY_FUNCTION__, cond, 0, ## __VA_ARGS__)
-#define AMS_CALL_ABORT_IMPL(cond, ...)  ::ams::diag::AbortImpl(__FILE__, __LINE__, __PRETTY_FUNCTION__, cond, 0, ## __VA_ARGS__)
+#define AMS_CALL_ASSERT_FAIL_IMPL(type, expr, ...) ::ams::diag::OnAssertionFailure(type, expr, __PRETTY_FUNCTION__, __FILE__, __LINE__, ## __VA_ARGS__)
+#define AMS_CALL_ABORT_IMPL(expr, ...)       ::ams::diag::AbortImpl(expr, __PRETTY_FUNCTION__, __FILE__, __LINE__, ## __VA_ARGS__)
+#define AMS_UNREACHABLE_DEFAULT_CASE() default: ::ams::impl::UnexpectedDefaultImpl(__PRETTY_FUNCTION__, __FILE__, __LINE__)
 #else
-#define AMS_CALL_ASSERT_FAIL_IMPL(cond, ...) ::ams::diag::AssertionFailureImpl("", 0, "", "", 0)
-#define AMS_CALL_ABORT_IMPL(cond, ...)  ::ams::diag::AbortImpl(); AMS_UNUSED(cond, ## __VA_ARGS__)
+#define AMS_CALL_ASSERT_FAIL_IMPL(type, expr, ...) ::ams::diag::OnAssertionFailure(type, "", "", "", 0)
+#define AMS_CALL_ABORT_IMPL(expr, ...)       ::ams::diag::AbortImpl("", "", "", 0); AMS_UNUSED(expr, ## __VA_ARGS__)
+#define AMS_UNREACHABLE_DEFAULT_CASE() default: ::ams::impl::UnexpectedDefaultImpl("", "", 0)
 #endif
 
 #ifdef AMS_ENABLE_ASSERTIONS
-#define AMS_ASSERT_IMPL(expr, ...)                                                                    \
+#define AMS_ASSERT_IMPL(type, expr, ...)                                                              \
     {                                                                                                 \
         if (std::is_constant_evaluated()) {                                                           \
             AMS_ASSUME(static_cast<bool>(expr));                                                      \
         } else {                                                                                      \
             if (const bool __tmp_ams_assert_val = static_cast<bool>(expr); (!__tmp_ams_assert_val)) { \
-                AMS_CALL_ASSERT_FAIL_IMPL(#expr, ## __VA_ARGS__);                                     \
+                AMS_CALL_ASSERT_FAIL_IMPL(type, #expr, ## __VA_ARGS__);                               \
             }                                                                                         \
         }                                                                                             \
     }
 #elif defined(AMS_PRESERVE_ASSERTION_EXPRESSIONS)
-#define AMS_ASSERT_IMPL(expr, ...) AMS_UNUSED(expr, ## __VA_ARGS__)
+#define AMS_ASSERT_IMPL(type, expr, ...) AMS_UNUSED(expr, ## __VA_ARGS__)
 #else
-#define AMS_ASSERT_IMPL(expr, ...) static_cast<void>(0)
+#define AMS_ASSERT_IMPL(type, expr, ...) static_cast<void>(0)
 #endif
 
-#define AMS_ASSERT(expr, ...) AMS_ASSERT_IMPL(expr, ## __VA_ARGS__)
+#define AMS_ASSERT(expr, ...) AMS_ASSERT_IMPL(::ams::diag::AssertionType_Assert, expr, ## __VA_ARGS__)
 
-#define AMS_UNREACHABLE_DEFAULT_CASE() default: AMS_CALL_ABORT_IMPL("Unreachable default case entered")
 
 #ifdef AMS_BUILD_FOR_AUDITING
-#define AMS_AUDIT(expr, ...) AMS_ASSERT(expr, ## __VA_ARGS__)
+#define AMS_AUDIT(expr, ...) AMS_ASSERT_IMPL(::ams::diag::AssertionType_Audit, expr, ## __VA_ARGS__)
 #elif defined(AMS_PRESERVE_AUDIT_EXPRESSIONS)
 #define AMS_AUDIT(expr, ...) AMS_UNUSED(expr, ## __VA_ARGS__)
 #else
diff --git a/libraries/libvapours/include/vapours/results/cal_results.hpp b/libraries/libvapours/include/vapours/results/cal_results.hpp
index 2d99fef2a..cb770ef55 100644
--- a/libraries/libvapours/include/vapours/results/cal_results.hpp
+++ b/libraries/libvapours/include/vapours/results/cal_results.hpp
@@ -16,11 +16,10 @@
 
 #pragma once
 #include <vapours/results/results_common.hpp>
+#include <vapours/results/powctl_results.hpp>
 
 namespace ams::cal {
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(198);
-
-    R_DEFINE_ERROR_RESULT(CalibrationDataCrcError, 101);
+    using powctl::ResultCalibrationDataCrcError;
 
 }
diff --git a/libraries/libvapours/include/vapours/results/capsrv_results.hpp b/libraries/libvapours/include/vapours/results/capsrv_results.hpp
index 965d7f9d9..d353ab93f 100644
--- a/libraries/libvapours/include/vapours/results/capsrv_results.hpp
+++ b/libraries/libvapours/include/vapours/results/capsrv_results.hpp
@@ -16,9 +16,10 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::capsrv {
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(206);
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::capsrv, 206);
+
+namespace ams::capsrv {
 
     R_DEFINE_ERROR_RANGE(AlbumError, 2, 99);
         R_DEFINE_ERROR_RESULT(AlbumWorkMemoryError,                     3);
diff --git a/libraries/libvapours/include/vapours/results/creport_results.hpp b/libraries/libvapours/include/vapours/results/creport_results.hpp
index a3b4d7a62..f7d424e6e 100644
--- a/libraries/libvapours/include/vapours/results/creport_results.hpp
+++ b/libraries/libvapours/include/vapours/results/creport_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::creport {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::creport, 168);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(168);
+namespace ams::creport {
 
     R_DEFINE_ERROR_RESULT(UndefinedInstruction, 0);
     R_DEFINE_ERROR_RESULT(InstructionAbort,     1);
diff --git a/libraries/libvapours/include/vapours/results/cs_results.hpp b/libraries/libvapours/include/vapours/results/cs_results.hpp
index b1e63802f..8ff32ac73 100644
--- a/libraries/libvapours/include/vapours/results/cs_results.hpp
+++ b/libraries/libvapours/include/vapours/results/cs_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::cs {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::cs, 204);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(204);
+namespace ams::cs {
 
     R_DEFINE_ERROR_RESULT(UnknownCommand, 2);
     R_DEFINE_ERROR_RESULT(OutOfResource,  4);
diff --git a/libraries/libvapours/include/vapours/results/dd_results.hpp b/libraries/libvapours/include/vapours/results/dd_results.hpp
index 648fb6b67..dd1f042b0 100644
--- a/libraries/libvapours/include/vapours/results/dd_results.hpp
+++ b/libraries/libvapours/include/vapours/results/dd_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::dd {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::dd, 6);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(6);
+namespace ams::dd {
 
     R_DEFINE_ERROR_RESULT(EndOfQuery,              1);
     R_DEFINE_ERROR_RESULT(InvalidCurrentMemory,    2);
diff --git a/libraries/libvapours/include/vapours/results/ddsf_results.hpp b/libraries/libvapours/include/vapours/results/ddsf_results.hpp
index 73378d4e8..357254291 100644
--- a/libraries/libvapours/include/vapours/results/ddsf_results.hpp
+++ b/libraries/libvapours/include/vapours/results/ddsf_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::ddsf {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::ddsf, 30);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(30);
+namespace ams::ddsf {
 
     R_DEFINE_ERROR_RESULT(OutOfResource,      1);
     R_DEFINE_ERROR_RESULT(NotSupported,       2);
diff --git a/libraries/libvapours/include/vapours/results/debug_results.hpp b/libraries/libvapours/include/vapours/results/debug_results.hpp
index d93cff64f..6db0d7223 100644
--- a/libraries/libvapours/include/vapours/results/debug_results.hpp
+++ b/libraries/libvapours/include/vapours/results/debug_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::dbg {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::dbg, 183);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(183);
+namespace ams::dbg {
 
     R_DEFINE_ERROR_RESULT(CannotDebug,     1);
     R_DEFINE_ERROR_RESULT(AlreadyAttached, 2);
diff --git a/libraries/libvapours/include/vapours/results/dmnt_results.hpp b/libraries/libvapours/include/vapours/results/dmnt_results.hpp
index 29323d327..f6203c9b4 100644
--- a/libraries/libvapours/include/vapours/results/dmnt_results.hpp
+++ b/libraries/libvapours/include/vapours/results/dmnt_results.hpp
@@ -17,34 +17,34 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::dmnt {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::dmnt, 13);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(13);
+namespace ams::dmnt {
 
     R_DEFINE_ERROR_RESULT(Unknown,           1);
     R_DEFINE_ERROR_RESULT(DebuggingDisabled, 2);
 
     /* Atmosphere extension. */
-    namespace cheat {
+    // namespace cheat {
 
-        R_DEFINE_ABSTRACT_ERROR_RANGE(CheatError, 6500, 6599);
-            R_DEFINE_ERROR_RESULT(CheatNotAttached,     6500);
-            R_DEFINE_ERROR_RESULT(CheatNullBuffer,      6501);
-            R_DEFINE_ERROR_RESULT(CheatInvalidBuffer,   6502);
-            R_DEFINE_ERROR_RESULT(CheatUnknownId,       6503);
-            R_DEFINE_ERROR_RESULT(CheatOutOfResource,   6504);
-            R_DEFINE_ERROR_RESULT(CheatInvalid,         6505);
-            R_DEFINE_ERROR_RESULT(CheatCannotDisable,   6506);
+        R_DEFINE_ABSTRACT_ERROR_RANGE_NS(cheat, CheatError, 6500, 6599);
+            R_DEFINE_ERROR_RESULT_NS(cheat, CheatNotAttached,     6500);
+            R_DEFINE_ERROR_RESULT_NS(cheat, CheatNullBuffer,      6501);
+            R_DEFINE_ERROR_RESULT_NS(cheat, CheatInvalidBuffer,   6502);
+            R_DEFINE_ERROR_RESULT_NS(cheat, CheatUnknownId,       6503);
+            R_DEFINE_ERROR_RESULT_NS(cheat, CheatOutOfResource,   6504);
+            R_DEFINE_ERROR_RESULT_NS(cheat, CheatInvalid,         6505);
+            R_DEFINE_ERROR_RESULT_NS(cheat, CheatCannotDisable,   6506);
 
-        R_DEFINE_ABSTRACT_ERROR_RANGE(FrozenAddressError, 6600, 6699);
-            R_DEFINE_ERROR_RESULT(FrozenAddressInvalidWidth,  6600);
-            R_DEFINE_ERROR_RESULT(FrozenAddressAlreadyExists, 6601);
-            R_DEFINE_ERROR_RESULT(FrozenAddressNotFound,      6602);
-            R_DEFINE_ERROR_RESULT(FrozenAddressOutOfResource, 6603);
+        R_DEFINE_ABSTRACT_ERROR_RANGE_NS(cheat, FrozenAddressError, 6600, 6699);
+            R_DEFINE_ERROR_RESULT_NS(cheat, FrozenAddressInvalidWidth,  6600);
+            R_DEFINE_ERROR_RESULT_NS(cheat, FrozenAddressAlreadyExists, 6601);
+            R_DEFINE_ERROR_RESULT_NS(cheat, FrozenAddressNotFound,      6602);
+            R_DEFINE_ERROR_RESULT_NS(cheat, FrozenAddressOutOfResource, 6603);
 
-        R_DEFINE_ABSTRACT_ERROR_RANGE(VirtualMachineError, 6700, 6799);
-            R_DEFINE_ERROR_RESULT(VirtualMachineInvalidConditionDepth, 6700);
+        R_DEFINE_ABSTRACT_ERROR_RANGE_NS(cheat, VirtualMachineError, 6700, 6799);
+            R_DEFINE_ERROR_RESULT_NS(cheat, VirtualMachineInvalidConditionDepth, 6700);
 
-    }
+    // }
 
 }
diff --git a/libraries/libvapours/include/vapours/results/erpt_results.hpp b/libraries/libvapours/include/vapours/results/erpt_results.hpp
index 3df7473e6..10c1b9aa9 100644
--- a/libraries/libvapours/include/vapours/results/erpt_results.hpp
+++ b/libraries/libvapours/include/vapours/results/erpt_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::erpt {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::erpt, 147);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(147);
+namespace ams::erpt {
 
     R_DEFINE_ERROR_RESULT(NotInitialized,            1);
     R_DEFINE_ERROR_RESULT(AlreadyInitialized,        2);
diff --git a/libraries/libvapours/include/vapours/results/err_results.hpp b/libraries/libvapours/include/vapours/results/err_results.hpp
index 88003f656..6dd43c88f 100644
--- a/libraries/libvapours/include/vapours/results/err_results.hpp
+++ b/libraries/libvapours/include/vapours/results/err_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::err {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::err, 162);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(162);
+namespace ams::err {
 
     R_DEFINE_ERROR_RESULT(ApplicationAbort,       1);
     R_DEFINE_ERROR_RESULT(SystemProgramAbort,     2);
diff --git a/libraries/libvapours/include/vapours/results/exosphere_results.hpp b/libraries/libvapours/include/vapours/results/exosphere_results.hpp
index 128cac072..be02b8779 100644
--- a/libraries/libvapours/include/vapours/results/exosphere_results.hpp
+++ b/libraries/libvapours/include/vapours/results/exosphere_results.hpp
@@ -17,12 +17,11 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
+/* Please note: These results are all custom, and not official. */
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::exosphere, 444);
+
 namespace ams::exosphere {
 
-    /* Please note: These results are all custom, and not official. */
-    R_DEFINE_NAMESPACE_RESULT_MODULE(444);
-
-
     /* Result 1-1000 reserved for Atmosphere. */
     R_DEFINE_ERROR_RESULT(NotPresent,       1);
     R_DEFINE_ERROR_RESULT(VersionMismatch,  2);
diff --git a/libraries/libvapours/include/vapours/results/fatal_results.hpp b/libraries/libvapours/include/vapours/results/fatal_results.hpp
index 68ff89405..23d757204 100644
--- a/libraries/libvapours/include/vapours/results/fatal_results.hpp
+++ b/libraries/libvapours/include/vapours/results/fatal_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::fatal {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::fatal, 163);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(163);
+namespace ams::fatal {
 
     R_DEFINE_ERROR_RESULT(AllocationFailed,                    1);
     R_DEFINE_ERROR_RESULT(NullGraphicsBuffer,                  2);
diff --git a/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp
index d3384d52a..1b9ed6057 100644
--- a/libraries/libvapours/include/vapours/results/fs_results.hpp
+++ b/libraries/libvapours/include/vapours/results/fs_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::fs {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::fs, 2);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(2);
+namespace ams::fs {
 
     R_DEFINE_ERROR_RANGE(HandledByAllProcess, 0, 999);
         R_DEFINE_ERROR_RESULT(PathNotFound,      1);
@@ -217,7 +217,7 @@ namespace ams::fs {
                 R_DEFINE_ERROR_RESULT(NcaBaseStorageOutOfRangeC, 4510);
                 R_DEFINE_ERROR_RESULT(NcaBaseStorageOutOfRangeD, 4511);
 
-                R_DEFINE_ERROR_RANGE(NcaFileSystemCorrupted, 4512, 4529);
+                R_DEFINE_ERROR_RESULT_CLASS_IMPL(NcaFileSystemCorrupted, 4512, 4529);
                     R_DEFINE_ERROR_RESULT(InvalidNcaFileSystemType,              4512);
                     R_DEFINE_ERROR_RESULT(InvalidAcidFileSize,                   4513);
                     R_DEFINE_ERROR_RESULT(InvalidAcidSize,                       4514);
diff --git a/libraries/libvapours/include/vapours/results/gpio_results.hpp b/libraries/libvapours/include/vapours/results/gpio_results.hpp
index 88e75baa6..a228c8129 100644
--- a/libraries/libvapours/include/vapours/results/gpio_results.hpp
+++ b/libraries/libvapours/include/vapours/results/gpio_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::gpio {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::gpio, 102);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(102);
+namespace ams::gpio {
 
     R_DEFINE_ERROR_RESULT(AlreadyBound,    1);
     R_DEFINE_ERROR_RESULT(AlreadyOpen,     2);
diff --git a/libraries/libvapours/include/vapours/results/hipc_results.hpp b/libraries/libvapours/include/vapours/results/hipc_results.hpp
index 99fd56447..8e557b5aa 100644
--- a/libraries/libvapours/include/vapours/results/hipc_results.hpp
+++ b/libraries/libvapours/include/vapours/results/hipc_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::sf::hipc {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::sf::hipc, 11);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(11);
+namespace ams::sf::hipc {
 
     R_DEFINE_ABSTRACT_ERROR_RANGE(OutOfResource, 100, 299);
         R_DEFINE_ERROR_RESULT(OutOfSessionMemory,    102);
diff --git a/libraries/libvapours/include/vapours/results/htc_results.hpp b/libraries/libvapours/include/vapours/results/htc_results.hpp
index 8d92196f7..ba3a87414 100644
--- a/libraries/libvapours/include/vapours/results/htc_results.hpp
+++ b/libraries/libvapours/include/vapours/results/htc_results.hpp
@@ -16,9 +16,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::htc {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::htc, 18);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(18);
+namespace ams::htc {
 
     R_DEFINE_ERROR_RESULT(ConnectionFailure,  1);
     R_DEFINE_ERROR_RESULT(NotFound,           2);
diff --git a/libraries/libvapours/include/vapours/results/htcfs_results.hpp b/libraries/libvapours/include/vapours/results/htcfs_results.hpp
index 5549d5d88..cf96274e7 100644
--- a/libraries/libvapours/include/vapours/results/htcfs_results.hpp
+++ b/libraries/libvapours/include/vapours/results/htcfs_results.hpp
@@ -16,9 +16,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::htcfs {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::htcfs, 31);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(31);
+namespace ams::htcfs {
 
     R_DEFINE_ERROR_RESULT(InvalidArgument,   3);
 
diff --git a/libraries/libvapours/include/vapours/results/htclow_results.hpp b/libraries/libvapours/include/vapours/results/htclow_results.hpp
index 0d288c141..9f6dfa52d 100644
--- a/libraries/libvapours/include/vapours/results/htclow_results.hpp
+++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp
@@ -16,9 +16,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::htclow {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::htclow, 29);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(29);
+namespace ams::htclow {
 
     R_DEFINE_ERROR_RESULT(ConnectionFailure,          1);
     R_DEFINE_ERROR_RESULT(UnknownDriverType,          3);
@@ -28,7 +28,7 @@ namespace ams::htclow {
     R_DEFINE_ERROR_RESULT(ChannelNotExist,           10);
 
     R_DEFINE_ERROR_RESULT(OutOfChannel, 151);
-    R_DEFINE_ERROR_RESULT(OutOfTask,    151);
+    R_DEFINE_ERROR_RESULT(OutOfTask,    152);
 
     R_DEFINE_ERROR_RESULT(InvalidChannelState,             200);
     R_DEFINE_ERROR_RESULT(InvalidChannelStateDisconnected, 201);
diff --git a/libraries/libvapours/include/vapours/results/htcs_results.hpp b/libraries/libvapours/include/vapours/results/htcs_results.hpp
index 6e5dba0d1..0f782d529 100644
--- a/libraries/libvapours/include/vapours/results/htcs_results.hpp
+++ b/libraries/libvapours/include/vapours/results/htcs_results.hpp
@@ -16,9 +16,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::htcs {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::htcs, 4);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(4);
+namespace ams::htcs {
 
     R_DEFINE_ERROR_RESULT(InvalidHandle, 9);
 
diff --git a/libraries/libvapours/include/vapours/results/i2c_results.hpp b/libraries/libvapours/include/vapours/results/i2c_results.hpp
index 1704009ee..2a0e06022 100644
--- a/libraries/libvapours/include/vapours/results/i2c_results.hpp
+++ b/libraries/libvapours/include/vapours/results/i2c_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::i2c {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::i2c, 101);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(101);
+namespace ams::i2c {
 
     R_DEFINE_ERROR_RESULT(NoAck,           1);
     R_DEFINE_ERROR_RESULT(BusBusy,         2);
diff --git a/libraries/libvapours/include/vapours/results/kvdb_results.hpp b/libraries/libvapours/include/vapours/results/kvdb_results.hpp
index 254d89e33..109a81bb3 100644
--- a/libraries/libvapours/include/vapours/results/kvdb_results.hpp
+++ b/libraries/libvapours/include/vapours/results/kvdb_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::kvdb {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::kvdb, 20);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(20);
+namespace ams::kvdb {
 
     R_DEFINE_ERROR_RESULT(OutOfKeyResource,        1);
     R_DEFINE_ERROR_RESULT(KeyNotFound,             2);
diff --git a/libraries/libvapours/include/vapours/results/loader_results.hpp b/libraries/libvapours/include/vapours/results/loader_results.hpp
index 691acbd44..825be3784 100644
--- a/libraries/libvapours/include/vapours/results/loader_results.hpp
+++ b/libraries/libvapours/include/vapours/results/loader_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::ldr {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::ldr, 9);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(9);
+namespace ams::ldr {
 
     R_DEFINE_ERROR_RESULT(ArgumentOverflow,       1);
     R_DEFINE_ERROR_RESULT(ArgumentCountOverflow,  2);
diff --git a/libraries/libvapours/include/vapours/results/lr_results.hpp b/libraries/libvapours/include/vapours/results/lr_results.hpp
index fd18d0228..faad916d3 100644
--- a/libraries/libvapours/include/vapours/results/lr_results.hpp
+++ b/libraries/libvapours/include/vapours/results/lr_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::lr {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::lr, 8);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(8);
+namespace ams::lr {
 
     R_DEFINE_ERROR_RESULT(ProgramNotFound,          2);
     R_DEFINE_ERROR_RESULT(DataNotFound,             3);
diff --git a/libraries/libvapours/include/vapours/results/ncm_results.hpp b/libraries/libvapours/include/vapours/results/ncm_results.hpp
index 8c4a2aea7..a3b4fb80e 100644
--- a/libraries/libvapours/include/vapours/results/ncm_results.hpp
+++ b/libraries/libvapours/include/vapours/results/ncm_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::ncm {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::ncm, 5);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(5);
+namespace ams::ncm {
 
     R_DEFINE_ERROR_RESULT(InvalidContentStorageBase,                1);
     R_DEFINE_ERROR_RESULT(PlaceHolderAlreadyExists,                 2);
diff --git a/libraries/libvapours/include/vapours/results/nim_results.hpp b/libraries/libvapours/include/vapours/results/nim_results.hpp
index 967bfd3e9..a006b4a4d 100644
--- a/libraries/libvapours/include/vapours/results/nim_results.hpp
+++ b/libraries/libvapours/include/vapours/results/nim_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::nim {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::nim, 137);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(137);
+namespace ams::nim {
 
     R_DEFINE_ERROR_RESULT(HttpConnectionCanceled, 70);
 
diff --git a/libraries/libvapours/include/vapours/results/ns_results.hpp b/libraries/libvapours/include/vapours/results/ns_results.hpp
index 29d386f9d..34bf8309a 100644
--- a/libraries/libvapours/include/vapours/results/ns_results.hpp
+++ b/libraries/libvapours/include/vapours/results/ns_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::ns {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::ns, 16);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(16);
+namespace ams::ns {
 
     R_DEFINE_ERROR_RESULT(Canceled,                           90);
     R_DEFINE_ERROR_RESULT(OutOfMaxRunningTask,               110);
diff --git a/libraries/libvapours/include/vapours/results/os_results.hpp b/libraries/libvapours/include/vapours/results/os_results.hpp
index 62a6344c6..b83389e84 100644
--- a/libraries/libvapours/include/vapours/results/os_results.hpp
+++ b/libraries/libvapours/include/vapours/results/os_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::os {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::os, 3);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(3);
+namespace ams::os {
 
     R_DEFINE_ERROR_RESULT(Busy,                         4);
 
diff --git a/libraries/libvapours/include/vapours/results/osdbg_results.hpp b/libraries/libvapours/include/vapours/results/osdbg_results.hpp
index 952c07307..0ced08755 100644
--- a/libraries/libvapours/include/vapours/results/osdbg_results.hpp
+++ b/libraries/libvapours/include/vapours/results/osdbg_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::osdbg {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::osdbg, 7);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(7);
+namespace ams::osdbg {
 
     R_DEFINE_ERROR_RESULT(CannotGetThreadInfo,      1);
     R_DEFINE_ERROR_RESULT(UnsupportedThreadVersion, 2);
diff --git a/libraries/libvapours/include/vapours/results/pcv_results.hpp b/libraries/libvapours/include/vapours/results/pcv_results.hpp
index eed8e316e..daf05f28c 100644
--- a/libraries/libvapours/include/vapours/results/pcv_results.hpp
+++ b/libraries/libvapours/include/vapours/results/pcv_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::pcv {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::pcv, 133);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(133);
+namespace ams::pcv {
 
     R_DEFINE_ERROR_RESULT(IllegalRequest, 16);
 
diff --git a/libraries/libvapours/include/vapours/results/pgl_results.hpp b/libraries/libvapours/include/vapours/results/pgl_results.hpp
index c909aa667..af4176de1 100644
--- a/libraries/libvapours/include/vapours/results/pgl_results.hpp
+++ b/libraries/libvapours/include/vapours/results/pgl_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::pgl {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::pgl, 228);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(228);
+namespace ams::pgl {
 
     R_DEFINE_ERROR_RESULT(NotImplemented,             1);
     R_DEFINE_ERROR_RESULT(NotAvailable,               2);
diff --git a/libraries/libvapours/include/vapours/results/pm_results.hpp b/libraries/libvapours/include/vapours/results/pm_results.hpp
index 1bb18e02c..4c9e51ad5 100644
--- a/libraries/libvapours/include/vapours/results/pm_results.hpp
+++ b/libraries/libvapours/include/vapours/results/pm_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::pm {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::pm, 15);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(15);
+namespace ams::pm {
 
     R_DEFINE_ERROR_RESULT(ProcessNotFound,    1);
     R_DEFINE_ERROR_RESULT(AlreadyStarted,     2);
diff --git a/libraries/libvapours/include/vapours/results/powctl_results.hpp b/libraries/libvapours/include/vapours/results/powctl_results.hpp
index 7a9d6d7fc..98413f677 100644
--- a/libraries/libvapours/include/vapours/results/powctl_results.hpp
+++ b/libraries/libvapours/include/vapours/results/powctl_results.hpp
@@ -17,12 +17,14 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::powctl {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::powctl, 198);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(198);
+namespace ams::powctl {
 
     R_DEFINE_ERROR_RESULT(NotSupported,    1);
     R_DEFINE_ERROR_RESULT(InvalidArgument, 2);
     R_DEFINE_ERROR_RESULT(NotAvailable,    3);
 
+    R_DEFINE_ERROR_RESULT(CalibrationDataCrcError, 101);
+
 }
diff --git a/libraries/libvapours/include/vapours/results/psc_results.hpp b/libraries/libvapours/include/vapours/results/psc_results.hpp
index 019925acf..16d8e3074 100644
--- a/libraries/libvapours/include/vapours/results/psc_results.hpp
+++ b/libraries/libvapours/include/vapours/results/psc_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::psc {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::psc, 138);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(138);
+namespace ams::psc {
 
     R_DEFINE_ERROR_RESULT(AlreadyInitialized, 2);
     R_DEFINE_ERROR_RESULT(NotInitialized,     3);
diff --git a/libraries/libvapours/include/vapours/results/pwm_results.hpp b/libraries/libvapours/include/vapours/results/pwm_results.hpp
index ed5a8ded7..247a5f317 100644
--- a/libraries/libvapours/include/vapours/results/pwm_results.hpp
+++ b/libraries/libvapours/include/vapours/results/pwm_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::pwm {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::pwm, 189);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(189);
+namespace ams::pwm {
 
     R_DEFINE_ERROR_RESULT(InvalidArgument, 2);
 
diff --git a/libraries/libvapours/include/vapours/results/results_common.hpp b/libraries/libvapours/include/vapours/results/results_common.hpp
index ec03e67d6..2ddbf37dd 100644
--- a/libraries/libvapours/include/vapours/results/results_common.hpp
+++ b/libraries/libvapours/include/vapours/results/results_common.hpp
@@ -20,8 +20,25 @@
 
 namespace ams {
 
+    const char *GetResultName(int module, int description);
+
     namespace result::impl {
 
+        #if defined(AMS_AUTO_GENERATE_RESULT_NAMES)
+        struct DummyNameHolder {
+            static constexpr bool Exists = false;
+            static constexpr const char *Name = "unknown";
+        };
+
+        template<int Module>
+        struct ResultNameSpaceExistsImpl {
+            static constexpr bool Exists = false;
+
+            template<int Description>
+            using NameHolder = DummyNameHolder;
+        };
+        #endif
+
         class ResultTraits {
             public:
                 using BaseType = u32;
@@ -113,6 +130,10 @@ namespace ams {
     static_assert(sizeof(Result) == sizeof(Result::Base::BaseType), "sizeof(Result) == sizeof(Result::Base::BaseType)");
     static_assert(std::is_trivially_destructible<Result>::value, "std::is_trivially_destructible<Result>::value");
 
+    ALWAYS_INLINE const char *GetResultName(const Result &result) {
+        return GetResultName(result.GetModule(), result.GetDescription());
+    }
+
     namespace result::impl {
 
         class ResultInternalAccessor {
@@ -230,25 +251,75 @@ namespace ams {
 }
 
 /* Macros for defining new results. */
-#define R_DEFINE_NAMESPACE_RESULT_MODULE(value) namespace impl::result { static constexpr inline ::ams::result::impl::ResultTraits::BaseType ResultModuleId = value; }
-#define R_CURRENT_NAMESPACE_RESULT_MODULE impl::result::ResultModuleId
+#if defined(AMS_AUTO_GENERATE_RESULT_NAMES)
+#define R_DEFINE_NAMESPACE_RESULT_MODULE(nmspc, value)                                                  \
+    namespace nmspc {                                                                                   \
+                                                                                                        \
+        namespace result_impl {                                                                         \
+            static constexpr inline ::ams::result::impl::ResultTraits::BaseType ResultModuleId = value; \
+                                                                                                        \
+            template<int Description>                                                                   \
+            struct ResultNameHolderImpl { static constexpr bool Exists = false; };                      \
+        }                                                                                               \
+                                                                                                        \
+    }                                                                                                   \
+                                                                                                        \
+    namespace ams::result::impl {                                                                       \
+                                                                                                        \
+        template<> struct ResultNameSpaceExistsImpl<value> {                                            \
+            static constexpr bool Exists = true;                                                        \
+                                                                                                        \
+            template<int Description>                                                                   \
+            using NameHolder = nmspc::result_impl::ResultNameHolderImpl<Description>;                   \
+        };                                                                                              \
+                                                                                                        \
+    }
+#else
+#define R_DEFINE_NAMESPACE_RESULT_MODULE(nmspc, value)                                                  \
+    namespace nmspc {                                                                                   \
+                                                                                                        \
+        namespace result_impl {                                                                         \
+            static constexpr inline ::ams::result::impl::ResultTraits::BaseType ResultModuleId = value; \
+        }                                                                                               \
+                                                                                                        \
+    }
+#endif
+
+#define R_CURRENT_NAMESPACE_RESULT_MODULE result_impl::ResultModuleId
 #define R_NAMESPACE_MODULE_ID(nmspc) nmspc::R_CURRENT_NAMESPACE_RESULT_MODULE
 
 #define R_MAKE_NAMESPACE_RESULT(nmspc, desc) static_cast<::ams::Result>(::ams::result::impl::ResultTraits::MakeValue(R_NAMESPACE_MODULE_ID(nmspc), desc))
 
-#define R_DEFINE_ERROR_RESULT_IMPL(name, desc_start, desc_end) \
+#if defined(AMS_AUTO_GENERATE_RESULT_NAMES)
+#define R_DEFINE_ERROR_RESULT_NAME_HOLDER_IMPL(name, desc_start, desc_end) \
+    template<> struct result_impl::ResultNameHolderImpl<desc_start> { static constexpr bool Exists = true; static constexpr const char *Name = #name; };
+#else
+#define R_DEFINE_ERROR_RESULT_NAME_HOLDER_IMPL(name, desc_start, desc_end)
+#endif
+
+#define R_DEFINE_ERROR_RESULT_CLASS_IMPL(name, desc_start, desc_end) \
     class Result##name final : public ::ams::result::impl::ResultErrorBase<R_CURRENT_NAMESPACE_RESULT_MODULE, desc_start>, public ::ams::result::impl::ResultErrorRangeBase<R_CURRENT_NAMESPACE_RESULT_MODULE, desc_start, desc_end> {}
 
+#define R_DEFINE_ERROR_RESULT_IMPL(name, desc_start, desc_end)         \
+    R_DEFINE_ERROR_RESULT_NAME_HOLDER_IMPL(name, desc_start, desc_end) \
+    R_DEFINE_ERROR_RESULT_CLASS_IMPL(name, desc_start, desc_end)
+
 #define R_DEFINE_ABSTRACT_ERROR_RESULT_IMPL(name, desc_start, desc_end) \
     class Result##name final : public ::ams::result::impl::ResultErrorRangeBase<R_CURRENT_NAMESPACE_RESULT_MODULE, desc_start, desc_end> {}
 
-
 #define R_DEFINE_ERROR_RESULT(name, desc) R_DEFINE_ERROR_RESULT_IMPL(name, desc, desc)
 #define R_DEFINE_ERROR_RANGE(name, start, end) R_DEFINE_ERROR_RESULT_IMPL(name, start, end)
 
 #define R_DEFINE_ABSTRACT_ERROR_RESULT(name, desc) R_DEFINE_ABSTRACT_ERROR_RESULT_IMPL(name, desc, desc)
 #define R_DEFINE_ABSTRACT_ERROR_RANGE(name, start, end) R_DEFINE_ABSTRACT_ERROR_RESULT_IMPL(name, start, end)
 
+
+#define R_DEFINE_ERROR_RESULT_NS(ns, name, desc) namespace ns { R_DEFINE_ERROR_RESULT_CLASS_IMPL(name, desc, desc); } R_DEFINE_ERROR_RESULT_NAME_HOLDER_IMPL(name, desc, desc)
+#define R_DEFINE_ERROR_RANGE_NS(ns, name, start, end) namespace ns { R_DEFINE_ERROR_RESULT_CLASS_IMPL(name, start, end); } R_DEFINE_ERROR_RESULT_NAME_HOLDER_IMPL(name, start, end)
+
+#define R_DEFINE_ABSTRACT_ERROR_RESULT_NS(ns, name, desc) namespace ns {  R_DEFINE_ABSTRACT_ERROR_RESULT_IMPL(name, desc, desc); }
+#define R_DEFINE_ABSTRACT_ERROR_RANGE_NS(ns, name, start, end) namespace ns { R_DEFINE_ABSTRACT_ERROR_RESULT_IMPL(name, start, end); }
+
 /* Remove libnx macros, replace with our own. */
 #ifndef R_SUCCEEDED
 #error "R_SUCCEEDED not defined."
@@ -384,14 +455,12 @@ namespace ams::result::impl {
 #if defined(ATMOSPHERE_BOARD_NINTENDO_NX) && defined(ATMOSPHERE_IS_STRATOSPHERE) && !defined(AMS_ENABLE_DETAILED_ASSERTIONS) && !defined(AMS_BUILD_FOR_DEBUGGING) && !defined(AMS_BUILD_FOR_AUDITING)
     #define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) do { ::ams::diag::impl::FatalErrorByResultForNx(val); AMS_INFINITE_LOOP(); AMS_ASSUME(false); } while (false)
     #define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) do { ::ams::diag::impl::FatalErrorByResultForNx(val); AMS_INFINITE_LOOP(); AMS_ASSUME(false); } while (false)
+#elif defined(ATMOSPHERE_OS_HORIZON)
+    #define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) AMS_CALL_ASSERT_FAIL_IMPL(::ams::diag::AssertionType_Assert, "ams::Result::IsSuccess()", "Failed: %s\n  Module:      %d\n  Description: %d\n  InnerValue:  0x%08" PRIX32, cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue())
+    #define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val)  AMS_CALL_ABORT_IMPL("ams::Result::IsSuccess()", "Failed: %s\n  Module:      %d\n  Description: %d\n  InnerValue:  0x%08" PRIX32, cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue())
 #else
-    #if defined(AMS_ENABLE_DETAILED_ASSERTIONS)
-        #define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) ::ams::result::impl::OnResultAssertion(__FILE__, __LINE__, __PRETTY_FUNCTION__, cond, val)
-        #define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val)  ::ams::result::impl::OnResultAbort(__FILE__, __LINE__, __PRETTY_FUNCTION__, cond, val)
-    #else
-        #define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) ::ams::result::impl::OnResultAssertion("", 0, "", "", val)
-        #define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val)  ::ams::result::impl::OnResultAbort("", 0, "", "", val)
-    #endif
+    #define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) AMS_CALL_ASSERT_FAIL_IMPL(::ams::diag::AssertionType_Assert, "ams::Result::IsSuccess()", "Failed: %s\n  Module:      %d\n  Description: %d\n  InnerValue:  0x%08" PRIX32 "\n  Name:        %s", cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue(), ::ams::GetResultName(val))
+    #define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val)  AMS_CALL_ABORT_IMPL("ams::Result::IsSuccess()", "Failed: %s\n  Module:      %d\n  Description: %d\n  InnerValue:  0x%08" PRIX32  "\n  Name:        %s", cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue(), ::ams::GetResultName(val))
 #endif
 
 /// Evaluates an expression that returns a result, and asserts the result if it would fail.
diff --git a/libraries/libvapours/include/vapours/results/ro_results.hpp b/libraries/libvapours/include/vapours/results/ro_results.hpp
index 076b34551..c187749dc 100644
--- a/libraries/libvapours/include/vapours/results/ro_results.hpp
+++ b/libraries/libvapours/include/vapours/results/ro_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::ro {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::ro, 22);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(22);
+namespace ams::ro {
 
     R_DEFINE_ERROR_RANGE(RoError, 1, 1023);
         R_DEFINE_ERROR_RESULT(OutOfAddressSpace,        2);
diff --git a/libraries/libvapours/include/vapours/results/scs_results.hpp b/libraries/libvapours/include/vapours/results/scs_results.hpp
index 1446b0efc..59dcfe815 100644
--- a/libraries/libvapours/include/vapours/results/scs_results.hpp
+++ b/libraries/libvapours/include/vapours/results/scs_results.hpp
@@ -16,13 +16,12 @@
 
 #pragma once
 #include <vapours/results/results_common.hpp>
+#include <vapours/results/cs_results.hpp>
 
 namespace ams::scs {
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(204);
-
-    R_DEFINE_ERROR_RESULT(UnknownCommand, 2);
-    R_DEFINE_ERROR_RESULT(OutOfResource,  4);
-    R_DEFINE_ERROR_RESULT(NoSocket,       7);
+    using ams::cs::ResultUnknownCommand;
+    using ams::cs::ResultOutOfResource;
+    using ams::cs::ResultNoSocket;
 
 }
diff --git a/libraries/libvapours/include/vapours/results/sdmmc_results.hpp b/libraries/libvapours/include/vapours/results/sdmmc_results.hpp
index 25d4aa1e1..3477907ce 100644
--- a/libraries/libvapours/include/vapours/results/sdmmc_results.hpp
+++ b/libraries/libvapours/include/vapours/results/sdmmc_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::sdmmc {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::sdmmc, 24);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(24);
+namespace ams::sdmmc {
 
     R_DEFINE_ERROR_RESULT(NoDevice,      1);
     R_DEFINE_ERROR_RESULT(NotActivated,  2);
diff --git a/libraries/libvapours/include/vapours/results/settings_results.hpp b/libraries/libvapours/include/vapours/results/settings_results.hpp
index 0155b00f3..828b489b0 100644
--- a/libraries/libvapours/include/vapours/results/settings_results.hpp
+++ b/libraries/libvapours/include/vapours/results/settings_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::settings {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::settings, 105);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(105);
+namespace ams::settings {
 
     R_DEFINE_ERROR_RESULT(SettingsItemNotFound,                 11);
     R_DEFINE_ERROR_RESULT(StopIteration,                        21);
diff --git a/libraries/libvapours/include/vapours/results/sf_results.hpp b/libraries/libvapours/include/vapours/results/sf_results.hpp
index add2cd5bc..825ac175a 100644
--- a/libraries/libvapours/include/vapours/results/sf_results.hpp
+++ b/libraries/libvapours/include/vapours/results/sf_results.hpp
@@ -17,36 +17,36 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::sf {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::sf, 10);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(10);
+namespace ams::sf {
 
     R_DEFINE_ERROR_RESULT(NotSupported,             1);
     R_DEFINE_ERROR_RESULT(PreconditionViolation,    3);
 
-    namespace cmif {
+    //namespace cmif {
 
-        R_DEFINE_ERROR_RESULT(InvalidHeaderSize,    202);
-        R_DEFINE_ERROR_RESULT(InvalidInHeader,      211);
-        R_DEFINE_ERROR_RESULT(UnknownCommandId,     221);
-        R_DEFINE_ERROR_RESULT(InvalidOutRawSize,    232);
-        R_DEFINE_ERROR_RESULT(InvalidNumInObjects,  235);
-        R_DEFINE_ERROR_RESULT(InvalidNumOutObjects, 236);
-        R_DEFINE_ERROR_RESULT(InvalidInObject,      239);
+        R_DEFINE_ERROR_RESULT_NS(cmif, InvalidHeaderSize,    202);
+        R_DEFINE_ERROR_RESULT_NS(cmif, InvalidInHeader,      211);
+        R_DEFINE_ERROR_RESULT_NS(cmif, UnknownCommandId,     221);
+        R_DEFINE_ERROR_RESULT_NS(cmif, InvalidOutRawSize,    232);
+        R_DEFINE_ERROR_RESULT_NS(cmif, InvalidNumInObjects,  235);
+        R_DEFINE_ERROR_RESULT_NS(cmif, InvalidNumOutObjects, 236);
+        R_DEFINE_ERROR_RESULT_NS(cmif, InvalidInObject,      239);
 
-        R_DEFINE_ERROR_RESULT(TargetNotFound,       261);
+        R_DEFINE_ERROR_RESULT_NS(cmif, TargetNotFound,       261);
 
-        R_DEFINE_ERROR_RESULT(OutOfDomainEntries,   301);
+        R_DEFINE_ERROR_RESULT_NS(cmif, OutOfDomainEntries,   301);
 
-    }
+    //}
 
-    namespace impl {
+    //namespace impl {
 
-        R_DEFINE_ABSTRACT_ERROR_RANGE(RequestContextChanged, 800, 899);
-            R_DEFINE_ABSTRACT_ERROR_RANGE(RequestInvalidated, 801, 809);
-                R_DEFINE_ERROR_RESULT(RequestInvalidatedByUser, 802);
+        R_DEFINE_ABSTRACT_ERROR_RANGE_NS(impl, RequestContextChanged, 800, 899);
+            R_DEFINE_ABSTRACT_ERROR_RANGE_NS(impl, RequestInvalidated, 801, 809);
+                R_DEFINE_ERROR_RESULT_NS(impl, RequestInvalidatedByUser, 802);
 
-    }
+    //}
 
     R_DEFINE_ABSTRACT_ERROR_RANGE(RequestDeferred, 811, 819);
         R_DEFINE_ERROR_RESULT(RequestDeferredByUser, 812);
diff --git a/libraries/libvapours/include/vapours/results/sm_results.hpp b/libraries/libvapours/include/vapours/results/sm_results.hpp
index 28119b7cc..7b29894bb 100644
--- a/libraries/libvapours/include/vapours/results/sm_results.hpp
+++ b/libraries/libvapours/include/vapours/results/sm_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::sm {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::sm, 21);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(21);
+namespace ams::sm {
 
     R_DEFINE_ERROR_RESULT(OutOfProcesses,        1);
     R_DEFINE_ERROR_RESULT(InvalidClient,         2);
@@ -32,11 +32,11 @@ namespace ams::sm {
     R_DEFINE_ERROR_RESULT(TooLargeAccessControl, 9);
 
     /* Results 1000-2000 used as extension for Atmosphere Mitm. */
-    namespace mitm {
+    //namespace mitm {
 
-        R_DEFINE_ERROR_RESULT(ShouldForwardToSession, 1000);
-        R_DEFINE_ERROR_RESULT(ProcessNotAssociated,   1100);
+        R_DEFINE_ERROR_RESULT_NS(mitm, ShouldForwardToSession, 1000);
+        R_DEFINE_ERROR_RESULT_NS(mitm, ProcessNotAssociated,   1100);
 
-    }
+    //}
 
 }
diff --git a/libraries/libvapours/include/vapours/results/socket_results.hpp b/libraries/libvapours/include/vapours/results/socket_results.hpp
index 58915c1de..f83cbaee8 100644
--- a/libraries/libvapours/include/vapours/results/socket_results.hpp
+++ b/libraries/libvapours/include/vapours/results/socket_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::socket {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::socket, 27);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(27);
+namespace ams::socket {
 
     R_DEFINE_ERROR_RESULT(InsufficientProvidedMemory, 1);
 
diff --git a/libraries/libvapours/include/vapours/results/spl_results.hpp b/libraries/libvapours/include/vapours/results/spl_results.hpp
index c15e5cd3c..12c1199b4 100644
--- a/libraries/libvapours/include/vapours/results/spl_results.hpp
+++ b/libraries/libvapours/include/vapours/results/spl_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::spl {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::spl, 26);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(26);
+namespace ams::spl {
 
     R_DEFINE_ERROR_RANGE(SecureMonitorError, 0, 99);
         R_DEFINE_ERROR_RESULT(SecureMonitorNotSupported,          1);
diff --git a/libraries/libvapours/include/vapours/results/sprofile_results.hpp b/libraries/libvapours/include/vapours/results/sprofile_results.hpp
index 8772bdb0c..4000bd8f1 100644
--- a/libraries/libvapours/include/vapours/results/sprofile_results.hpp
+++ b/libraries/libvapours/include/vapours/results/sprofile_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::sprofile {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::sprofile, 246);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(246);
+namespace ams::sprofile {
 
     R_DEFINE_ERROR_RESULT(InvalidArgument,  100);
     R_DEFINE_ERROR_RESULT(InvalidState,     101);
diff --git a/libraries/libvapours/include/vapours/results/svc_results.hpp b/libraries/libvapours/include/vapours/results/svc_results.hpp
index 6128b6b25..7a01b5699 100644
--- a/libraries/libvapours/include/vapours/results/svc_results.hpp
+++ b/libraries/libvapours/include/vapours/results/svc_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::svc {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::svc, 1);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(1);
+namespace ams::svc {
 
     R_DEFINE_ERROR_RESULT(OutOfSessions,                7);
 
diff --git a/libraries/libvapours/include/vapours/results/time_results.hpp b/libraries/libvapours/include/vapours/results/time_results.hpp
index 863ac47c6..c83941fee 100644
--- a/libraries/libvapours/include/vapours/results/time_results.hpp
+++ b/libraries/libvapours/include/vapours/results/time_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::time {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::time, 116);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(116);
+namespace ams::time {
 
     R_DEFINE_ERROR_RESULT(NotInitialized, 0);
 
diff --git a/libraries/libvapours/include/vapours/results/tipc_results.hpp b/libraries/libvapours/include/vapours/results/tipc_results.hpp
index b537bd389..c381c87eb 100644
--- a/libraries/libvapours/include/vapours/results/tipc_results.hpp
+++ b/libraries/libvapours/include/vapours/results/tipc_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::tipc {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::tipc, 35);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(35);
+namespace ams::tipc {
 
     R_DEFINE_ERROR_RESULT(InvalidMethod,        10);
     R_DEFINE_ERROR_RESULT(InvalidMessageFormat, 15);
diff --git a/libraries/libvapours/include/vapours/results/tma_results.hpp b/libraries/libvapours/include/vapours/results/tma_results.hpp
index 55a1068b2..ce464687a 100644
--- a/libraries/libvapours/include/vapours/results/tma_results.hpp
+++ b/libraries/libvapours/include/vapours/results/tma_results.hpp
@@ -16,9 +16,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::tma {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::tma, 12);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(12);
+namespace ams::tma {
 
     R_DEFINE_ERROR_RESULT(Unknown, 1);
 
diff --git a/libraries/libvapours/include/vapours/results/updater_results.hpp b/libraries/libvapours/include/vapours/results/updater_results.hpp
index 7e86e435b..0ff0e135a 100644
--- a/libraries/libvapours/include/vapours/results/updater_results.hpp
+++ b/libraries/libvapours/include/vapours/results/updater_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::updater {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::updater, 158);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(158);
+namespace ams::updater {
 
     R_DEFINE_ERROR_RESULT(BootImagePackageNotFound, 2);
     R_DEFINE_ERROR_RESULT(InvalidBootImagePackage,  3);
diff --git a/libraries/libvapours/include/vapours/results/usb_results.hpp b/libraries/libvapours/include/vapours/results/usb_results.hpp
index a56efe782..a4185b16d 100644
--- a/libraries/libvapours/include/vapours/results/usb_results.hpp
+++ b/libraries/libvapours/include/vapours/results/usb_results.hpp
@@ -16,9 +16,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::usb {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::usb, 140);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(140);
+namespace ams::usb {
 
     R_DEFINE_ERROR_RESULT(NotInitialized,       0);
     R_DEFINE_ERROR_RESULT(AlreadyInitialized,   1);
diff --git a/libraries/libvapours/include/vapours/results/vi_results.hpp b/libraries/libvapours/include/vapours/results/vi_results.hpp
index 2012bd162..3df6c3fc7 100644
--- a/libraries/libvapours/include/vapours/results/vi_results.hpp
+++ b/libraries/libvapours/include/vapours/results/vi_results.hpp
@@ -17,9 +17,9 @@
 #pragma once
 #include <vapours/results/results_common.hpp>
 
-namespace ams::vi {
+R_DEFINE_NAMESPACE_RESULT_MODULE(ams::vi, 114);
 
-    R_DEFINE_NAMESPACE_RESULT_MODULE(114);
+namespace ams::vi {
 
     R_DEFINE_ERROR_RESULT(OperationFailed,     1);
     R_DEFINE_ERROR_RESULT(NotSupported,        6);
diff --git a/libraries/libvapours/source/result/result_get_name.cpp b/libraries/libvapours/source/result/result_get_name.cpp
new file mode 100644
index 000000000..4595d0b19
--- /dev/null
+++ b/libraries/libvapours/source/result/result_get_name.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#if !defined(ATMOSPHERE_OS_HORIZON) && (defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING))
+    #define AMS_AUTO_GENERATE_RESULT_NAMES
+#endif
+
+#include <vapours.hpp>
+
+namespace ams {
+
+    #if defined(AMS_AUTO_GENERATE_RESULT_NAMES)
+
+    #define AMS_INVOKE_MACRO_01(EXPR, n) EXPR(n); EXPR(n + (1 << 0));
+
+    #define AMS_INVOKE_MACRO_02(EXPR, n) AMS_INVOKE_MACRO_01(EXPR, n); AMS_INVOKE_MACRO_01(EXPR, n + (1 <<  1));
+    #define AMS_INVOKE_MACRO_03(EXPR, n) AMS_INVOKE_MACRO_02(EXPR, n); AMS_INVOKE_MACRO_02(EXPR, n + (1 <<  2));
+    #define AMS_INVOKE_MACRO_04(EXPR, n) AMS_INVOKE_MACRO_03(EXPR, n); AMS_INVOKE_MACRO_03(EXPR, n + (1 <<  3));
+    #define AMS_INVOKE_MACRO_05(EXPR, n) AMS_INVOKE_MACRO_04(EXPR, n); AMS_INVOKE_MACRO_04(EXPR, n + (1 <<  4));
+    #define AMS_INVOKE_MACRO_06(EXPR, n) AMS_INVOKE_MACRO_05(EXPR, n); AMS_INVOKE_MACRO_05(EXPR, n + (1 <<  5));
+    #define AMS_INVOKE_MACRO_07(EXPR, n) AMS_INVOKE_MACRO_06(EXPR, n); AMS_INVOKE_MACRO_06(EXPR, n + (1 <<  6));
+    #define AMS_INVOKE_MACRO_08(EXPR, n) AMS_INVOKE_MACRO_07(EXPR, n); AMS_INVOKE_MACRO_07(EXPR, n + (1 <<  7));
+    #define AMS_INVOKE_MACRO_09(EXPR, n) AMS_INVOKE_MACRO_08(EXPR, n); AMS_INVOKE_MACRO_08(EXPR, n + (1 <<  8));
+    #define AMS_INVOKE_MACRO_10(EXPR, n) AMS_INVOKE_MACRO_09(EXPR, n); AMS_INVOKE_MACRO_09(EXPR, n + (1 <<  9));
+    #define AMS_INVOKE_MACRO_11(EXPR, n) AMS_INVOKE_MACRO_10(EXPR, n); AMS_INVOKE_MACRO_10(EXPR, n + (1 << 10));
+    #define AMS_INVOKE_MACRO_12(EXPR, n) AMS_INVOKE_MACRO_11(EXPR, n); AMS_INVOKE_MACRO_11(EXPR, n + (1 << 11));
+    #define AMS_INVOKE_MACRO_13(EXPR, n) AMS_INVOKE_MACRO_12(EXPR, n); AMS_INVOKE_MACRO_12(EXPR, n + (1 << 12));
+
+    namespace {
+
+        template<int Module, int Description>
+        constexpr const char *GetResultNameByModuleAndDescription() {
+            return ::ams::result::impl::ResultNameSpaceExistsImpl<Module>::template NameHolder<Description>::Name;
+        }
+
+        template<int Module>
+        constexpr const char *GetResultNameByModule(int description) {
+            #define AMS_TEST_RESULT_DESCRIPTION_DEFINED(n) if constexpr (::ams::result::impl::ResultNameSpaceExistsImpl<Module>::template NameHolder<n>::Exists) { if (description == n) { return GetResultNameByModuleAndDescription<Module, n>(); } }
+
+            AMS_INVOKE_MACRO_13(AMS_TEST_RESULT_DESCRIPTION_DEFINED, 0)
+
+            return "Unknown";
+        }
+
+    }
+
+    const char *GetResultName(int module, int description) {
+        #define AMS_TEST_RESULT_MODULE_DEFINED(n) if constexpr (::ams::result::impl::ResultNameSpaceExistsImpl<n>::Exists) { if (module == n) { return GetResultNameByModule<n>(description); } }
+
+        AMS_INVOKE_MACRO_08(AMS_TEST_RESULT_MODULE_DEFINED, 0)
+
+        return "Unknown";
+    }
+
+    #else
+    const char *GetResultName(int, int) {
+        return "Unknown";
+    }
+    #endif
+
+}
diff --git a/libraries/libvapours/source/util/util_format_string.cpp b/libraries/libvapours/source/util/util_format_string.cpp
index 312d09062..9ef0bb7a6 100644
--- a/libraries/libvapours/source/util/util_format_string.cpp
+++ b/libraries/libvapours/source/util/util_format_string.cpp
@@ -394,7 +394,9 @@ namespace ams::util {
 
             /* Ensure null termination. */
             WriteCharacter('\0');
-            dst[dst_size - 1] = '\0';
+            if (dst_size > 0) {
+                dst[dst_size - 1] = '\0';
+            }
 
             /* Return number of characters that would have been printed sans the null terminator. */
             return static_cast<int>(dst_index) - 1;