Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"

This reverts commit 15b7df8ef1.
This commit is contained in:
souldbminersmwc
2025-11-09 16:14:52 -05:00
parent 22ec140738
commit 21a3f953d7
3804 changed files with 435 additions and 570162 deletions

View File

@@ -1,33 +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>
#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);
}
}

View File

@@ -1,28 +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/>.
*/
#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();
}

View File

@@ -1,135 +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>
#if defined(ATMOSPHERE_OS_WINDOWS)
#include <stratosphere/windows.hpp>
#elif 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]);
}
}

View File

@@ -1,217 +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::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;
}
}

View File

@@ -1,88 +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>
#include "diag_dump_stack_trace.hpp"
#if defined(ATMOSPHERE_OS_WINDOWS)
#include <stratosphere/windows.hpp>
#endif
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
}
}

View File

@@ -1,23 +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/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::diag::impl {
void TentativeDumpStackTrace();
}

View File

@@ -1,42 +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>
#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));
} else {
AMS_SDK_LOG("0x%016" PRIX64 " [ unknown ]\n", static_cast<u64>(backtrace[i]));
}
}
}
AMS_SDK_LOG("-------------------------------------------\n");
}
}

View File

@@ -1,25 +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>
#include "diag_dump_stack_trace.hpp"
namespace ams::diag::impl {
void TentativeDumpStackTrace() {
/* TODO */
}
}

View File

@@ -1,54 +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::diag::impl {
namespace {
constinit uintptr_t g_abort_impl_return_address = std::numeric_limits<uintptr_t>::max();
}
void SetAbortImplReturnAddress(uintptr_t address) {
g_abort_impl_return_address = address;
}
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;
}
}

View File

@@ -1,25 +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/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::diag::impl {
void SetAbortImplReturnAddress(uintptr_t address);
size_t GetAllBacktrace(uintptr_t *out, size_t out_size, ::ams::diag::Backtrace &bt);
}

View File

@@ -1,24 +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/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::diag::impl {
void InvokeAbortObserver(const AbortInfo &info);
void InvokeSdkAbortObserver(const SdkAbortInfo &info);
}

View File

@@ -1,38 +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>
#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);
}
}

View File

@@ -1,38 +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>
#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);
}
}

View File

@@ -1,61 +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::diag::impl {
namespace {
constexpr inline uintptr_t ModulePathLengthOffset = 4;
constexpr inline uintptr_t ModulePathOffset = 8;
}
uintptr_t GetModuleInfoForHorizon(const char **out_path, size_t *out_path_length, size_t *out_module_size, uintptr_t address) {
/* Check for null address. */
if (address == 0) {
return 0;
}
/* Get module info. */
ro::impl::ExceptionInfo exception_info;
if (!ro::impl::GetExceptionInfo(std::addressof(exception_info), address)) {
return 0;
}
/* Locate the path in the first non-read-execute segment. */
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
auto cur_address = exception_info.module_address;
while (cur_address < exception_info.module_address + exception_info.module_size) {
if (R_FAILED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), cur_address))) {
return 0;
}
if (mem_info.permission != svc::MemoryPermission_ReadExecute) {
break;
}
cur_address += mem_info.size;
}
/* Set output info. */
*out_path = reinterpret_cast<const char *>(cur_address + ModulePathOffset);
*out_path_length = *reinterpret_cast<const u32 *>(cur_address + ModulePathLengthOffset);
*out_module_size = exception_info.module_size;
return exception_info.module_address;
}
}

View File

@@ -1,157 +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/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::diag::impl {
template<typename Holder, typename Context>
class ObserverManager {
NON_COPYABLE(ObserverManager);
NON_MOVEABLE(ObserverManager);
private:
Holder *m_observer_list_head;
Holder **m_observer_list_tail;
os::ReaderWriterLock m_lock;
public:
constexpr ObserverManager() : m_observer_list_head(nullptr), m_observer_list_tail(std::addressof(m_observer_list_head)), m_lock() {
/* ... */
}
constexpr ~ObserverManager() {
if (std::is_constant_evaluated()) {
this->UnregisterAllObserverLocked();
} else {
this->UnregisterAllObserver();
}
}
void RegisterObserver(Holder *holder) {
/* Acquire a write hold on our lock. */
std::scoped_lock lk(m_lock);
this->RegisterObserverLocked(holder);
}
void UnregisterObserver(Holder *holder) {
/* Acquire a write hold on our lock. */
std::scoped_lock lk(m_lock);
/* Check that we can unregister. */
AMS_ASSERT(holder->is_registered);
/* Remove the holder. */
if (m_observer_list_head == holder) {
m_observer_list_head = holder->next;
if (m_observer_list_tail == std::addressof(holder->next)) {
m_observer_list_tail = std::addressof(m_observer_list_head);
}
} else {
for (auto *cur = m_observer_list_head; cur != nullptr; cur = cur->next) {
if (cur->next == holder) {
cur->next = holder->next;
if (m_observer_list_tail == std::addressof(holder->next)) {
m_observer_list_tail = std::addressof(cur->next);
}
break;
}
}
}
/* Set unregistered. */
holder->next = nullptr;
}
void UnregisterAllObserver() {
/* Acquire a write hold on our lock. */
std::scoped_lock lk(m_lock);
this->UnregisterAllObserverLocked();
}
void InvokeAllObserver(const Context &context) {
/* Use the holder's observer. */
InvokeAllObserver(context, [] (const Holder &holder, const Context &context) {
holder.observer(context);
});
}
template<typename Observer>
void InvokeAllObserver(const Context &context, Observer observer) {
/* Acquire a read hold on our lock. */
std::shared_lock lk(m_lock);
/* Invoke all observers. */
for (const auto *holder = m_observer_list_head; holder != nullptr; holder = holder->next) {
observer(*holder, context);
}
}
protected:
constexpr void RegisterObserverLocked(Holder *holder) {
/* Check that we can register. */
AMS_ASSERT(!holder->is_registered);
/* Insert the holder. */
*m_observer_list_tail = holder;
m_observer_list_tail = std::addressof(holder->next);
/* Set registered. */
holder->next = nullptr;
holder->is_registered = true;
}
constexpr void UnregisterAllObserverLocked() {
/* Unregister all observers. */
for (auto *holder = m_observer_list_head; holder != nullptr; holder = holder->next) {
holder->is_registered = false;
}
/* Reset head/fail. */
m_observer_list_head = nullptr;
m_observer_list_tail = std::addressof(m_observer_list_head);
}
};
template<typename Holder, typename Context>
class ObserverManagerWithDefaultHolder : public ObserverManager<Holder, Context> {
private:
Holder m_default_holder;
public:
template<typename Initializer, typename... Args>
constexpr ObserverManagerWithDefaultHolder(Initializer initializer, Args &&... args) : ObserverManager<Holder, Context>(), m_default_holder{} {
/* Initialize the default observer. */
initializer(std::addressof(m_default_holder), std::forward<Args>(args)...);
/* Register the default observer. */
if (std::is_constant_evaluated()) {
this->RegisterObserverLocked(std::addressof(m_default_holder));
} else {
this->RegisterObserver(std::addressof(m_default_holder));
}
}
Holder &GetDefaultObserverHolder() {
return m_default_holder;
}
const Holder &GetDefaulObservertHolder() const {
return m_default_holder;
}
};
}

View File

@@ -1,29 +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/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::diag::impl {
void PrintDebugString(const char *msg, size_t size);
inline void PrintDebugString(const char *msg) {
AMS_AUDIT(msg != nullptr);
PrintDebugString(msg, std::strlen(msg));
}
}

View File

@@ -1,29 +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>
#include "diag_print_debug_string.hpp"
namespace ams::diag::impl {
void PrintDebugString(const char *msg, size_t size) {
AMS_AUDIT(msg != nullptr || size == 0);
if (size != 0) {
svc::OutputDebugString(msg, size);
}
}
}

View File

@@ -1,32 +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>
#include "diag_print_debug_string.hpp"
namespace ams::diag::impl {
NOINLINE void PrintDebugString(const char *msg, size_t size) {
AMS_AUDIT(msg != nullptr || size == 0);
if (size == 0) {
return;
}
/* TODO: Printf? */
printf("%s", msg);
}
}

View File

@@ -1,32 +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>
#include "diag_print_debug_string.hpp"
namespace ams::diag::impl {
NOINLINE void PrintDebugString(const char *msg, size_t size) {
AMS_AUDIT(msg != nullptr || size == 0);
if (size == 0) {
return;
}
/* TODO: Printf? */
printf("%s", msg);
}
}

View File

@@ -1,32 +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>
#include "diag_print_debug_string.hpp"
namespace ams::diag::impl {
NOINLINE void PrintDebugString(const char *msg, size_t size) {
AMS_AUDIT(msg != nullptr || size == 0);
if (size == 0) {
return;
}
/* TODO: OutputDebugString? Printf? */
printf("%s", msg);
}
}

View File

@@ -1,102 +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>
/* TODO: Rename, if we change to e.g. use amsMain? */
extern "C" int main(int argc, char **argv);
namespace ams::diag::impl {
uintptr_t GetModuleInfoForHorizon(const char **out_path, size_t *out_path_length, size_t *out_module_size, uintptr_t address);
namespace {
const char *GetLastCharacterPointer(const char *str, size_t len, char c) {
for (const char *last = str + len - 1; last >= str; --last) {
if (*last == c) {
return last;
}
}
return nullptr;
}
void GetFileNameWithoutExtension(const char **out, size_t *out_size, const char *path, size_t path_length) {
const auto last_sep1 = GetLastCharacterPointer(path, path_length, '\\');
const auto last_sep2 = GetLastCharacterPointer(path, path_length, '/');
const auto ext = GetLastCharacterPointer(path, path_length, '.');
/* Handle last-separator. */
if (last_sep1 && last_sep2) {
if (last_sep1 > last_sep2) {
*out = last_sep1 + 1;
} else {
*out = last_sep2 + 1;
}
} else if (last_sep1) {
*out = last_sep1 + 1;
} else if (last_sep2) {
*out = last_sep2 + 1;
} else {
*out = path;
}
/* Handle extension. */
if (ext && ext >= *out) {
*out_size = ext - *out;
} else {
*out_size = (path + path_length) - *out;
}
}
constinit const char *g_process_name = nullptr;
constinit size_t g_process_name_size = 0;
constinit os::SdkMutex g_process_name_lock;
constinit bool g_got_process_name = false;
void EnsureProcessNameCached() {
/* Ensure process name. */
if (AMS_UNLIKELY(!g_got_process_name)) {
std::scoped_lock lk(g_process_name_lock);
if (AMS_LIKELY(!g_got_process_name)) {
const char *path;
size_t path_length;
size_t module_size;
if (GetModuleInfoForHorizon(std::addressof(path), std::addressof(path_length), std::addressof(module_size), reinterpret_cast<uintptr_t>(main)) != 0) {
GetFileNameWithoutExtension(std::addressof(g_process_name), std::addressof(g_process_name_size), path, path_length);
AMS_ASSERT(g_process_name_size == 0 || util::VerifyUtf8String(g_process_name, g_process_name_size));
} else {
g_process_name = "";
g_process_name_size = 0;
}
g_got_process_name = true;
}
}
}
}
void GetProcessNamePointer(const char **out, size_t *out_size) {
/* Ensure process name is cached. */
EnsureProcessNameCached();
/* Get cached process name. */
*out = g_process_name;
*out_size = g_process_name_size;
}
}

View File

@@ -1,24 +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/>.
*/
#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);
}

View File

@@ -1,241 +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>
#include "diag_symbol_impl.hpp"
#define PACKAGE "stratosphere"
#define PACKAGE_VERSION STRINGIFY(ATMOSPHERE_RELEASE_VERSION_MAJOR.ATMOSPHERE_RELEASE_VERSION_MINOR.ATMOSPHERE_RELEASE_VERSION_MICRO)
#if defined(ATMOSPHERE_OS_LINUX)
#include <bfd.h>
#include <unistd.h>
#include <dlfcn.h>
#include <link.h>
extern "C" char __init_array_start;
#endif
#include <cxxabi.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_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;
}
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);
int cpp_name_status = 0;
if (char *demangled = abi::__cxa_demangle(name, nullptr, 0, std::addressof(cpp_name_status)); cpp_name_status == 0) {
AMS_ASSERT(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_LINUX)
{
if (::readlink("/proc/self/exe", dst, dst_size) == -1) {
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);
}
}

View File

@@ -1,31 +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>
#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");
}
}

View File

@@ -1,318 +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>
#include "diag_symbol_impl.hpp"
#include <unistd.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <mach-o/stab.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/param.h>
#include <sys/mman.h>
#include <cxxabi.h>
extern "C" {
void __module_offset_helper() { /* ... */ }
}
namespace ams::diag::impl {
namespace {
class CurrentExecutableHelper {
private:
struct SymbolInfo {
uintptr_t address;
const char *name;
};
private:
os::NativeHandle m_fd;
void *m_file_map;
size_t m_file_size;
SymbolInfo *m_symbols;
size_t m_num_symbol;
const char *m_module_name;
uintptr_t m_module_address;
size_t m_module_size;
uintptr_t m_module_displacement;
private:
CurrentExecutableHelper() : m_fd(-1), m_file_map(nullptr), m_file_size(0), m_symbols(nullptr), m_num_symbol(0), m_module_name(nullptr), m_module_address(0), m_module_size(0), m_module_displacement(0) {
/* Get the current executable name. */
char exe_path[4_KB] = {};
GetExecutablePath(exe_path, sizeof(exe_path));
/* Open the current executable. */
os::NativeHandle fd;
do {
fd = ::open(exe_path, O_RDONLY);
} while (fd < 0 && errno == EINTR);
if (fd < 0) {
return;
}
ON_SCOPE_EXIT { if (fd >= 0) { s32 ret; do { ret = ::close(fd); } while (ret < 0 && errno == EINTR); } };
/* Get the file size. */
struct stat st;
if (fstat(fd, std::addressof(st)) < 0) {
return;
}
/* Check that the file can be mapped. */
const size_t exe_size = st.st_size;
if (exe_size == 0) {
return;
}
/* Map the executable. */
void *exe_map = mmap(nullptr, exe_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (exe_map == MAP_FAILED) {
return;
}
ON_SCOPE_EXIT { if (exe_map != nullptr) { munmap(exe_map, exe_size); } };
/* Get the file's u32 magic. */
const uintptr_t exe_start = reinterpret_cast<uintptr_t>(exe_map);
const u32 magic = *reinterpret_cast<const u32 *>(exe_start);
/* Get/parse the mach header. */
u32 ncmds;
bool is_64;
if (magic == MH_MAGIC) {
const auto *header = reinterpret_cast<const struct mach_header *>(exe_start);
ncmds = header->ncmds;
is_64 = false;
} else if (magic == MH_MAGIC_64) {
const auto *header = reinterpret_cast<const struct mach_header_64 *>(exe_start);
ncmds = header->ncmds;
is_64 = true;
} else {
return;
}
/* Find the symbol load command. */
const auto *lc = reinterpret_cast<const struct load_command *>(exe_start + (is_64 ? sizeof(struct mach_header_64) : sizeof(struct mach_header)));
for (u32 i = 0; i < ncmds; ++i) {
/* If we encounter the symbol table, parse it. */
if (lc->cmd == LC_SYMTAB) {
if (is_64) {
this->ParseSymbolTable<struct nlist_64>(exe_start, reinterpret_cast<const struct symtab_command *>(lc));
} else {
this->ParseSymbolTable<struct nlist>(exe_start, reinterpret_cast<const struct symtab_command *>(lc));
}
break;
} else if (lc->cmd == LC_SEGMENT) {
const auto *sc = reinterpret_cast<const struct segment_command *>(lc);
if (std::strcmp(sc->segname, "__TEXT") == 0) {
AMS_ASSERT(m_module_address == 0);
m_module_address = sc->vmaddr;
m_module_size = sc->vmsize;
AMS_ASSERT(m_module_address != 0);
}
} else if (lc->cmd == LC_SEGMENT_64) {
const auto *sc = reinterpret_cast<const struct segment_command_64 *>(lc);
if (std::strcmp(sc->segname, "__TEXT") == 0) {
AMS_ASSERT(m_module_address == 0);
m_module_address = sc->vmaddr;
m_module_size = sc->vmsize;
AMS_ASSERT(m_module_address != 0);
}
}
/* Advance to the next load command. */
lc = reinterpret_cast<const struct load_command *>(reinterpret_cast<uintptr_t>(lc) + lc->cmdsize);
}
for (size_t i = 0; i < m_num_symbol; ++i) {
if (std::strcmp(m_symbols[i].name, "___module_offset_helper") == 0) {
m_module_displacement = reinterpret_cast<uintptr_t>(&__module_offset_helper) - m_symbols[i].address;
break;
}
}
if (m_module_address > 0 && m_module_size > 0 && m_num_symbol > 0) {
std::swap(m_fd, fd);
std::swap(m_file_map, exe_map);
m_file_size = exe_size;
}
}
~CurrentExecutableHelper() {
if (m_file_map != nullptr) {
munmap(m_file_map, m_file_size);
}
if (m_fd >= 0) {
s32 ret;
do { ret = ::close(m_fd); } while (ret < 0 && errno == EINTR);
}
}
public:
static CurrentExecutableHelper &GetInstance() {
AMS_FUNCTION_LOCAL_STATIC(CurrentExecutableHelper, s_current_executable_helper_instance);
return s_current_executable_helper_instance;
}
private:
template<typename NlistType>
void ParseSymbolTable(uintptr_t exe_start, const struct symtab_command *c) {
/* Check pre-conditions. */
AMS_ASSERT(m_fd == -1);
AMS_ASSERT(m_file_map == nullptr);
AMS_ASSERT(m_symbols == nullptr);
/* Get the strtab/symtab. */
const auto *symtab = reinterpret_cast<const NlistType *>(exe_start + c->symoff);
const char *strtab = reinterpret_cast<const char *>(exe_start + c->stroff);
/* Determine the number of functions. */
size_t funcs = 0;
for (size_t i = 0; i < c->nsyms; ++i) {
if (symtab[i].n_type != N_FUN || symtab[i].n_sect == NO_SECT) {
continue;
}
++funcs;
}
/* Allocate functions. */
m_symbols = reinterpret_cast<SymbolInfo *>(std::malloc(sizeof(SymbolInfo) * funcs));
if (m_symbols == nullptr) {
return;
}
/* Set all symbols. */
m_num_symbol = 0;
for (size_t i = 0; i < c->nsyms; ++i) {
if (symtab[i].n_type != N_FUN || symtab[i].n_sect == NO_SECT) {
continue;
}
m_symbols[m_num_symbol].address = symtab[i].n_value;
m_symbols[m_num_symbol].name = strtab + symtab[i].n_un.n_strx;
++m_num_symbol;
}
AMS_ASSERT(m_num_symbol == funcs);
/* Sort the symbols. */
std::sort(m_symbols + 0, m_symbols + m_num_symbol, [] (const SymbolInfo &lhs, const SymbolInfo &rhs) {
return lhs.address < rhs.address;
});
}
size_t GetSymbolSizeImpl(const SymbolInfo *symbol) const {
/* Do our best to guess. */
if (symbol != m_symbols + m_num_symbol - 1) {
return (symbol + 1)->address - symbol->address;
} else if (m_module_address + m_module_size >= symbol->address) {
return m_module_address + m_module_size - symbol->address;
} else {
return 0;
}
}
const SymbolInfo *GetBestSymbol(uintptr_t address) const {
address -= m_module_displacement;
const SymbolInfo *best_symbol = std::lower_bound(m_symbols + 0, m_symbols + m_num_symbol, address, [](const SymbolInfo &lhs, uintptr_t rhs) {
return lhs.address < rhs;
});
if (best_symbol == m_symbols + m_num_symbol) {
return nullptr;
}
if (best_symbol->address != address && best_symbol > m_symbols) {
--best_symbol;
}
const auto vma = best_symbol->address;
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 {
if (m_fd < 0) {
return 0;
}
/* Get the symbol. */
const auto *symbol = this->GetBestSymbol(address);
if (symbol == nullptr) {
return 0;
}
/* Print the symbol. */
const char *name = symbol->name;
int cpp_name_status = 0;
if (char *demangled = abi::__cxa_demangle(name, nullptr, 0, std::addressof(cpp_name_status)); cpp_name_status == 0) {
AMS_ASSERT(demangled != nullptr);
util::TSNPrintf(dst, dst_size, "%s", demangled);
std::free(demangled);
} else {
util::TSNPrintf(dst, dst_size, "%s", name);
}
return symbol->address + m_module_displacement;
}
size_t GetSymbolSize(uintptr_t address) const {
if (m_fd < 0) {
return 0;
}
/* Get the symbol. */
const auto *symbol = this->GetBestSymbol(address);
if (symbol == nullptr) {
return 0;
}
return this->GetSymbolSizeImpl(symbol);
}
private:
static void GetExecutablePath(char *dst, size_t dst_size) {
u32 len = dst_size;
if (_NSGetExecutablePath(dst, std::addressof(len)) != 0) {
dst[0] = 0;
return;
}
}
};
}
uintptr_t GetSymbolNameImpl(char *dst, size_t dst_size, uintptr_t address) {
return CurrentExecutableHelper::GetInstance().GetSymbolName(dst, dst_size, address);
}
size_t GetSymbolSizeImpl(uintptr_t address) {
return CurrentExecutableHelper::GetInstance().GetSymbolSize(address);
}
}

View File

@@ -1,292 +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>
#include <stratosphere/windows.hpp>
#include "diag_symbol_impl.hpp"
#include <cxxabi.h>
extern "C" {
void __module_offset_helper() { /* ... */ }
}
namespace ams::diag::impl {
namespace {
class CurrentExecutableHelper {
private:
struct SymbolInfo {
uintptr_t address;
const char *name;
};
private:
os::NativeHandle m_file_handle;
os::NativeHandle m_map_handle;
void *m_file_map;
size_t m_file_size;
SymbolInfo *m_symbols;
size_t m_num_symbol;
uintptr_t m_module_address;
size_t m_module_size;
uintptr_t m_module_displacement;
private:
CurrentExecutableHelper() : m_file_handle(INVALID_HANDLE_VALUE), m_map_handle(INVALID_HANDLE_VALUE), m_file_map(nullptr), m_file_size(0), m_symbols(nullptr), m_num_symbol(0), m_module_address(0), m_module_size(0), m_module_displacement(0) {
/* Open the current executable. */
auto exe_handle = OpenExecutableFile();
if (exe_handle == INVALID_HANDLE_VALUE) {
return;
}
ON_SCOPE_EXIT { if (exe_handle != INVALID_HANDLE_VALUE) { ::CloseHandle(exe_handle); } };
/* Get the exe size. */
LARGE_INTEGER exe_size_li;
if (::GetFileSizeEx(exe_handle, std::addressof(exe_size_li)) == 0) {
return;
}
/* Check the exe size. */
s64 exe_size = exe_size_li.QuadPart;
if (exe_size == 0) {
return;
}
/* Create a file mapping. */
auto map_handle = ::CreateFileMappingW(exe_handle, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (map_handle == INVALID_HANDLE_VALUE) {
return;
}
ON_SCOPE_EXIT { if (map_handle != INVALID_HANDLE_VALUE) { ::CloseHandle(map_handle); } };
/* Map the file. */
void *exe_map = ::MapViewOfFile(map_handle, FILE_MAP_READ, 0, 0, 0);
if (exe_map == nullptr) {
return;
}
ON_SCOPE_EXIT { if (exe_map != nullptr) { ::UnmapViewOfFile(exe_map); } };
/* Get/parse the DOS header. */
const uintptr_t exe_start = reinterpret_cast<uintptr_t>(exe_map);
const auto *dos_header = reinterpret_cast<const IMAGE_DOS_HEADER *>(exe_start);
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
return;
}
/* Set up the nt headers. */
const auto *nt32 = reinterpret_cast<const IMAGE_NT_HEADERS32 *>(exe_start + dos_header->e_lfanew);
const auto *nt64 = reinterpret_cast<const IMAGE_NT_HEADERS64 *>(exe_start + dos_header->e_lfanew);
if (nt32->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 || nt32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64) {
nt32 = nullptr;
} else {
nt64 = nullptr;
}
#define EXE_NT_HEADER(x) ((nt64 != nullptr) ? nt64->x : nt32->x)
if (EXE_NT_HEADER(Signature) != IMAGE_NT_SIGNATURE) {
return;
}
/* Check that the optional header is really present. */
if (EXE_NT_HEADER(FileHeader.SizeOfOptionalHeader) < sizeof(IMAGE_OPTIONAL_HEADER32)) {
return;
}
/* Get sections. */
const auto *sec = nt64 != nullptr ? IMAGE_FIRST_SECTION(nt64) : IMAGE_FIRST_SECTION(nt32);
/* Get the symbol table. */
const auto *symtab = reinterpret_cast<const IMAGE_SYMBOL *>(exe_start + EXE_NT_HEADER(FileHeader.PointerToSymbolTable));
const size_t nsym = EXE_NT_HEADER(FileHeader.NumberOfSymbols);
const char *strtab = reinterpret_cast<const char *>(symtab + nsym);
/* Find the section containing our code. */
int text_section = -1;
for (size_t i = 0; i < nsym; ++i) {
if (ISFCN(symtab[i].Type)) {
const char *name = symtab[i].N.Name.Short == 0 ? strtab + symtab[i].N.Name.Long : reinterpret_cast<const char *>(symtab[i].N.ShortName);
if (std::strcmp(name, "__module_offset_helper") == 0) {
AMS_ASSERT(text_section == -1);
AMS_ASSERT(m_module_displacement == 0);
m_module_displacement = reinterpret_cast<uintptr_t>(&__module_offset_helper) - symtab[i].Value;
text_section = symtab[i].SectionNumber;
AMS_ASSERT(m_module_displacement != 0);
AMS_ASSERT(text_section != -1);
break;
}
}
}
/* Determine the number of functions. */
size_t funcs = 0;
for (size_t i = 0; i < nsym; ++i) {
if (ISFCN(symtab[i].Type) && symtab[i].SectionNumber == text_section) {
++funcs;
}
}
/* Allocate functions. */
m_symbols = reinterpret_cast<SymbolInfo *>(std::malloc(sizeof(SymbolInfo) * funcs));
if (m_symbols == nullptr) {
return;
}
/* Set all symbols. */
m_num_symbol = 0;
for (size_t i = 0; i < nsym; ++i) {
if (ISFCN(symtab[i].Type) && symtab[i].SectionNumber == text_section) {
m_symbols[m_num_symbol].address = symtab[i].Value;
m_symbols[m_num_symbol].name = symtab[i].N.Name.Short == 0 ? strtab + symtab[i].N.Name.Long : reinterpret_cast<const char *>(symtab[i].N.ShortName);
++m_num_symbol;
}
}
AMS_ASSERT(m_num_symbol == funcs);
/* Sort the symbols. */
std::sort(m_symbols + 0, m_symbols + m_num_symbol, [] (const SymbolInfo &lhs, const SymbolInfo &rhs) {
return lhs.address < rhs.address;
});
m_module_address = 0;
m_module_size = sec[text_section - 1].Misc.VirtualSize;
if (m_module_displacement != 0 && m_module_size > 0 && m_num_symbol > 0) {
std::swap(m_file_handle, exe_handle);
std::swap(m_map_handle, map_handle);
std::swap(m_file_map, exe_map);
m_file_size = exe_size;
}
}
~CurrentExecutableHelper() {
if (m_file_map != nullptr) {
::UnmapViewOfFile(m_file_map);
}
if (m_map_handle != nullptr) {
::CloseHandle(m_map_handle);
}
if (m_file_handle != nullptr) {
::CloseHandle(m_file_handle);
}
}
public:
static CurrentExecutableHelper &GetInstance() {
AMS_FUNCTION_LOCAL_STATIC(CurrentExecutableHelper, s_current_executable_helper_instance);
return s_current_executable_helper_instance;
}
private:
size_t GetSymbolSizeImpl(const SymbolInfo *symbol) const {
/* Do our best to guess. */
if (symbol != m_symbols + m_num_symbol - 1) {
return (symbol + 1)->address - symbol->address;
} else if (m_module_address + m_module_size >= symbol->address) {
return m_module_address + m_module_size - symbol->address;
} else {
return 0;
}
}
const SymbolInfo *GetBestSymbol(uintptr_t address) const {
address -= m_module_displacement;
const SymbolInfo *best_symbol = std::lower_bound(m_symbols + 0, m_symbols + m_num_symbol, address, [](const SymbolInfo &lhs, uintptr_t rhs) {
return lhs.address < rhs;
});
if (best_symbol == m_symbols + m_num_symbol) {
return nullptr;
}
if (best_symbol->address != address && best_symbol > m_symbols) {
--best_symbol;
}
const auto vma = best_symbol->address;
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 {
if (m_file_handle == INVALID_HANDLE_VALUE) {
return 0;
}
/* Get the symbol. */
const auto *symbol = this->GetBestSymbol(address);
if (symbol == nullptr) {
return 0;
}
/* Print the symbol. */
const char *name = symbol->name;
int cpp_name_status = 0;
if (char *demangled = abi::__cxa_demangle(name, nullptr, 0, std::addressof(cpp_name_status)); cpp_name_status == 0) {
AMS_ASSERT(demangled != nullptr);
util::TSNPrintf(dst, dst_size, "%s", demangled);
std::free(demangled);
} else {
util::TSNPrintf(dst, dst_size, "%s", name);
}
return symbol->address + m_module_displacement;
}
size_t GetSymbolSize(uintptr_t address) const {
if (m_file_handle == INVALID_HANDLE_VALUE) {
return 0;
}
/* Get the symbol. */
const auto *symbol = this->GetBestSymbol(address);
if (symbol == nullptr) {
return 0;
}
return this->GetSymbolSizeImpl(symbol);
}
private:
static os::NativeHandle OpenExecutableFile() {
/* Get the module file name. */
wchar_t module_file_name[0x1000];
if (::GetModuleFileNameW(0, module_file_name, util::size(module_file_name)) == 0) {
return INVALID_HANDLE_VALUE;
}
return ::CreateFileW(module_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
}
};
}
uintptr_t GetSymbolNameImpl(char *dst, size_t dst_size, uintptr_t address) {
return CurrentExecutableHelper::GetInstance().GetSymbolName(dst, dst_size, address);
}
size_t GetSymbolSizeImpl(uintptr_t address) {
return CurrentExecutableHelper::GetInstance().GetSymbolSize(address);
}
}

View File

@@ -1,91 +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::diag::impl {
namespace {
bool IsHeadOfCharacter(u8 c) {
return (c & 0xC0) != 0x80;
}
size_t GetCharacterSize(u8 c) {
if ((c & 0x80) == 0) {
return 1;
} else if ((c & 0xE0) == 0xC0) {
return 2;
} else if ((c & 0xF0) == 0xE0) {
return 3;
} else if ((c & 0xF8) == 0xF0) {
return 4;
}
return 0;
}
const char *FindLastCharacterPointer(const char *str, size_t len) {
/* Find the head of the last character. */
const char *cur;
for (cur = str + len - 1; cur >= str && !IsHeadOfCharacter(*reinterpret_cast<const u8 *>(cur)); --cur) {
/* ... */
}
/* Return the last character. */
if (AMS_LIKELY(cur >= str)) {
return cur;
} else {
return nullptr;
}
}
}
int GetValidSizeAsUtf8String(const char *str, size_t len) {
/* Check pre-condition. */
AMS_ASSERT(str != nullptr);
/* Check if we have no data. */
if (len == 0) {
return 0;
}
/* Get the last character pointer. */
const auto *last_char_ptr = FindLastCharacterPointer(str, len);
if (last_char_ptr == nullptr) {
return -1;
}
/* Get sizes. */
const size_t actual_size = (str + len) - last_char_ptr;
const size_t last_char_size = GetCharacterSize(*reinterpret_cast<const u8 *>(last_char_ptr));
if (last_char_size == 0) {
return -1;
} else if (actual_size >= last_char_size) {
if (actual_size == last_char_size) {
return len;
} else {
return -1;
}
} else if (actual_size >= len) {
AMS_ASSERT(actual_size == len);
return -1;
} else {
return static_cast<int>(len - actual_size);
}
}
}