mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-06-02 15:49:48 -04:00
abort/error: print backtrace, abuse templates, overhaul result/diag (macos not done yet)
This commit is contained in:
parent
18168d54c3
commit
646f84bad1
118 changed files with 2843 additions and 369 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -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 */
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue