mirror of
https://github.com/Atmosphere-NX/Atmosphere-libs.git
synced 2025-06-22 03:22:39 +02:00
143 lines
5.8 KiB
C++
143 lines
5.8 KiB
C++
/*
|
|
* 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 "osdbg_thread_info.os.horizon.hpp"
|
|
#include "osdbg_thread_type.os.horizon.hpp"
|
|
#include "osdbg_thread_local_region.os.horizon.hpp"
|
|
|
|
namespace ams::osdbg::impl {
|
|
|
|
namespace {
|
|
|
|
Result GetThreadTypePointerFromThreadLocalRegion(uintptr_t *out, ThreadInfo *info) {
|
|
/* Detect lp64. */
|
|
const bool is_lp64 = IsLp64(info);
|
|
|
|
/* Get the thread local region. */
|
|
const auto *tlr_address = GetTargetThreadLocalRegion(info);
|
|
|
|
/* Read the thread local region. */
|
|
ThreadLocalRegionCommon tlr;
|
|
R_TRY(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(tlr)), info->_debug_handle, reinterpret_cast<uintptr_t>(tlr_address), sizeof(tlr)));
|
|
|
|
/* Detect libnx vs nintendo via magic number. */
|
|
if (tlr.libnx.thread_vars.magic == LibnxThreadVars::Magic) {
|
|
info->_thread_type_type = ThreadTypeType_Libnx;
|
|
*out = reinterpret_cast<uintptr_t>(tlr.libnx.thread_vars.thread_ptr);
|
|
} else {
|
|
info->_thread_type_type = ThreadTypeType_Nintendo;
|
|
*out = is_lp64 ? tlr.lp64.p_thread_type : tlr.ilp32.p_thread_type;
|
|
}
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result GetThreadArgumentAndStackPointer(u64 *out_arg, u64 *out_sp, ThreadInfo *info) {
|
|
/* Read the thread context. */
|
|
svc::ThreadContext thread_context;
|
|
R_TRY(svc::GetDebugThreadContext(std::addressof(thread_context), info->_debug_handle, info->_debug_info_create_thread.thread_id, svc::ThreadContextFlag_General | svc::ThreadContextFlag_Control));
|
|
|
|
/* Argument is in r0. */
|
|
*out_arg = thread_context.r[0];
|
|
|
|
/* Stack pointer varies by architecture. */
|
|
if (Is64BitArch(info)) {
|
|
*out_sp = thread_context.sp;
|
|
} else {
|
|
*out_sp = thread_context.r[13];
|
|
}
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
void DetectStratosphereThread(ThreadInfo *info) {
|
|
/* Stratosphere threads are initially misdetected as libnx threads. */
|
|
if (info->_thread_type_type != ThreadTypeType_Libnx || info->_thread_type == nullptr) {
|
|
return;
|
|
}
|
|
|
|
/* Convert to a parent pointer. */
|
|
os::ThreadType *stratosphere_ptr = util::GetParentPointer<&os::ThreadType::thread_impl_storage>(std::addressof(info->_thread_type->libnx));
|
|
|
|
/* Read the magic. */
|
|
u16 magic;
|
|
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(magic)), info->_debug_handle, reinterpret_cast<uintptr_t>(std::addressof(stratosphere_ptr->magic)), sizeof(magic)))) {
|
|
return;
|
|
}
|
|
|
|
/* Check the magic. */
|
|
if (magic == os::ThreadType::Magic) {
|
|
info->_thread_type_type = ThreadTypeType_Stratosphere;
|
|
info->_thread_type = reinterpret_cast<ThreadTypeCommon *>(stratosphere_ptr);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void GetTargetThreadType(ThreadInfo *info) {
|
|
/* Ensure we exit with correct state. */
|
|
auto type_guard = SCOPE_GUARD {
|
|
info->_thread_type = nullptr;
|
|
info->_thread_type_type = ThreadTypeType_Unknown;
|
|
};
|
|
|
|
/* Read the thread type pointer. */
|
|
uintptr_t tlr_thread_type;
|
|
if (R_FAILED(GetThreadTypePointerFromThreadLocalRegion(std::addressof(tlr_thread_type), info))) {
|
|
return;
|
|
}
|
|
|
|
/* Handle the case where we have a thread type. */
|
|
if (tlr_thread_type != 0) {
|
|
info->_thread_type = reinterpret_cast<ThreadTypeCommon *>(tlr_thread_type);
|
|
DetectStratosphereThread(info);
|
|
type_guard.Cancel();
|
|
return;
|
|
}
|
|
|
|
/* Otherwise, the thread is just created, and we should read its context. */
|
|
u64 arg, sp;
|
|
if (R_FAILED(GetThreadArgumentAndStackPointer(std::addressof(arg), std::addressof(sp), info))) {
|
|
return;
|
|
}
|
|
|
|
/* We may have been bamboozled into thinking a nintendo thread was a libnx thread, so check that. */
|
|
/* Nintendo threads have argument=ThreadType, libnx threads have argument=ThreadEntryArgs. */
|
|
if (info->_thread_type_type == ThreadTypeType_Nintendo && sp == arg) {
|
|
/* It's a libnx thread, so we should parse the entry args. */
|
|
info->_thread_type_type = ThreadTypeType_Libnx;
|
|
|
|
/* Read the entry args. */
|
|
LibnxThreadEntryArgs entry_args;
|
|
if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(entry_args)), info->_debug_handle, arg, sizeof(entry_args)))) {
|
|
return;
|
|
}
|
|
|
|
info->_thread_type = reinterpret_cast<ThreadTypeCommon *>(entry_args.t);
|
|
} else {
|
|
info->_thread_type_type = ThreadTypeType_Nintendo;
|
|
info->_thread_type = reinterpret_cast<ThreadTypeCommon *>(arg);
|
|
}
|
|
|
|
/* If we got the thread type, we don't need to reset our state. */
|
|
if (info->_thread_type != nullptr) {
|
|
type_guard.Cancel();
|
|
DetectStratosphereThread(info);
|
|
}
|
|
}
|
|
|
|
}
|