mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-27 12:46:03 +00:00
osdbg: implement thread info api
This commit is contained in:
parent
9f1f0c7cbd
commit
c6fad1b0ee
19 changed files with 912 additions and 7 deletions
|
@ -71,6 +71,7 @@
|
|||
#include <stratosphere/nim.hpp>
|
||||
#include <stratosphere/ns.hpp>
|
||||
#include <stratosphere/nsd.hpp>
|
||||
#include <stratosphere/osdbg.hpp>
|
||||
#include <stratosphere/patcher.hpp>
|
||||
#include <stratosphere/pcv.hpp>
|
||||
#include <stratosphere/pgl.hpp>
|
||||
|
|
|
@ -36,6 +36,8 @@ namespace ams::os {
|
|||
using ThreadImpl = ::Thread;
|
||||
|
||||
struct ThreadType {
|
||||
static constexpr u16 Magic = 0xF5A5;
|
||||
|
||||
enum State {
|
||||
State_NotInitialized = 0,
|
||||
State_Initialized = 1,
|
||||
|
@ -49,7 +51,9 @@ namespace ams::os {
|
|||
uintptr_t reserved[4];
|
||||
u8 state;
|
||||
u8 suspend_count;
|
||||
s32 base_priority;
|
||||
u16 magic;
|
||||
s16 base_priority;
|
||||
u16 version;
|
||||
char name_buffer[ThreadNameLengthMax];
|
||||
const char *name_pointer;
|
||||
ThreadId thread_id;
|
||||
|
|
19
libraries/libstratosphere/include/stratosphere/osdbg.hpp
Normal file
19
libraries/libstratosphere/include/stratosphere/osdbg.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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/osdbg/osdbg_thread.hpp>
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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/osdbg/osdbg_thread_types.hpp>
|
||||
#include <stratosphere/osdbg/osdbg_thread_api.hpp>
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/osdbg/osdbg_thread_api_impl.hpp>
|
||||
|
||||
namespace ams::osdbg {
|
||||
|
||||
struct ThreadInfo;
|
||||
|
||||
Result InitializeThreadInfo(ThreadInfo *thread_info, svc::Handle debug_handle, const svc::DebugInfoCreateProcess *create_process, const svc::DebugInfoCreateThread *create_thread);
|
||||
Result UpdateThreadInfo(ThreadInfo *thread_info);
|
||||
|
||||
Result GetThreadName(char *dst, const ThreadInfo *thread_info);
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/osdbg/osdbg_thread_types.hpp>
|
||||
|
||||
namespace ams::osdbg {
|
||||
|
||||
constexpr inline s32 GetThreadPriority(const ThreadInfo *thread_info) {
|
||||
return thread_info->_base_priority;
|
||||
}
|
||||
|
||||
constexpr inline s32 GetThreadCurrentPriority(const ThreadInfo *thread_info) {
|
||||
return thread_info->_current_priority;
|
||||
}
|
||||
|
||||
constexpr inline size_t GetThreadStackSize(const ThreadInfo *thread_info) {
|
||||
return thread_info->_stack_size;
|
||||
}
|
||||
|
||||
constexpr inline uintptr_t GetThreadStackAddress(const ThreadInfo *thread_info) {
|
||||
return thread_info->_stack;
|
||||
}
|
||||
|
||||
constexpr inline uintptr_t GetThreadFunction(const ThreadInfo *thread_info) {
|
||||
return thread_info->_function;
|
||||
}
|
||||
|
||||
constexpr inline uintptr_t GetThreadFunctionArgument(const ThreadInfo *thread_info) {
|
||||
return thread_info->_argument;
|
||||
}
|
||||
|
||||
constexpr inline uintptr_t GetThreadNamePointer(const ThreadInfo *thread_info) {
|
||||
return thread_info->_name_pointer;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::osdbg {
|
||||
|
||||
namespace impl {
|
||||
|
||||
union ThreadTypeCommon;
|
||||
|
||||
}
|
||||
|
||||
enum ThreadTypeType : u8 {
|
||||
ThreadTypeType_Unknown = 0,
|
||||
ThreadTypeType_Nintendo,
|
||||
ThreadTypeType_Stratosphere,
|
||||
ThreadTypeType_Libnx,
|
||||
};
|
||||
|
||||
struct ThreadInfo {
|
||||
s32 _base_priority;
|
||||
s32 _current_priority;
|
||||
size_t _stack_size;
|
||||
uintptr_t _stack;
|
||||
uintptr_t _argument;
|
||||
uintptr_t _function;
|
||||
uintptr_t _name_pointer;
|
||||
impl::ThreadTypeCommon *_thread_type;
|
||||
svc::Handle _debug_handle;
|
||||
ThreadTypeType _thread_type_type;
|
||||
svc::DebugInfoCreateProcess _debug_info_create_process;
|
||||
svc::DebugInfoCreateThread _debug_info_create_thread;
|
||||
};
|
||||
|
||||
}
|
|
@ -31,6 +31,8 @@ namespace ams::os::impl {
|
|||
util::ConstructAt(thread->waitlist);
|
||||
|
||||
/* Set member variables. */
|
||||
thread->magic = os::ThreadType::Magic;
|
||||
thread->version = 0;
|
||||
thread->thread_impl = (thread_impl != nullptr) ? thread_impl : std::addressof(thread->thread_impl_storage);
|
||||
thread->function = function;
|
||||
thread->argument = arg;
|
||||
|
@ -134,6 +136,7 @@ namespace ams::os::impl {
|
|||
util::DestroyAt(thread->waitlist);
|
||||
|
||||
thread->name_buffer[0] = '\x00';
|
||||
thread->magic = 0xCCCC;
|
||||
|
||||
{
|
||||
std::scoped_lock tlk(this->cs);
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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"
|
||||
#include "../../os/impl/os_thread_manager_impl.os.horizon.hpp"
|
||||
|
||||
namespace ams::osdbg::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
s32 ConvertToUserPriority(s32 horizon_priority) {
|
||||
return horizon_priority - os::impl::UserThreadPriorityOffset;
|
||||
}
|
||||
|
||||
s32 GetCurrentThreadPriorityImpl(const ThreadInfo *info) {
|
||||
u64 dummy;
|
||||
u32 horizon_priority;
|
||||
if (R_FAILED(svc::GetDebugThreadParam(std::addressof(dummy), std::addressof(horizon_priority), info->_debug_handle, info->_debug_info_create_thread.thread_id, svc::DebugThreadParam_Priority))) {
|
||||
return info->_base_priority;
|
||||
}
|
||||
|
||||
return ConvertToUserPriority(static_cast<s32>(horizon_priority));
|
||||
}
|
||||
|
||||
void FillWithCurrentInfoImpl(ThreadInfo *info, const auto &thread_type_impl) {
|
||||
/* Set fields. */
|
||||
info->_base_priority = thread_type_impl._base_priority;
|
||||
info->_current_priority = GetCurrentThreadPriorityImpl(info);
|
||||
info->_stack_size = thread_type_impl._stack_size;
|
||||
info->_stack = thread_type_impl._stack;
|
||||
info->_argument = thread_type_impl._argument;
|
||||
info->_function = thread_type_impl._thread_function;
|
||||
info->_name_pointer = thread_type_impl._name_pointer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result ThreadInfoHorizonImpl::FillWithCurrentInfo(ThreadInfo *info) {
|
||||
/* Detect lp64. */
|
||||
const bool is_lp64 = IsLp64(info);
|
||||
|
||||
/* Ensure that we have a thread type. */
|
||||
if (info->_thread_type == nullptr) {
|
||||
/* Ensure we exit with correct thread type. */
|
||||
auto thread_guard = SCOPE_GUARD { info->_thread_type = nullptr; };
|
||||
|
||||
/* Set the target thread type. */
|
||||
GetTargetThreadType(info);
|
||||
|
||||
/* If it's still nullptr, we failed to get the thread type. */
|
||||
R_UNLESS(info->_thread_type != nullptr, osdbg::ResultCannotGetThreadInfo());
|
||||
|
||||
/* Check that the thread type is valid. */
|
||||
R_UNLESS(info->_thread_type_type != ThreadTypeType_Unknown, osdbg::ResultUnsupportedThreadVersion());
|
||||
|
||||
/* We successfully got the thread type. */
|
||||
thread_guard.Cancel();
|
||||
}
|
||||
|
||||
/* Read and process the thread type. */
|
||||
ThreadTypeCommon thread_type;
|
||||
|
||||
switch (info->_thread_type_type) {
|
||||
case ThreadTypeType_Nintendo:
|
||||
if (is_lp64) {
|
||||
/* Read in the thread type. */
|
||||
R_TRY(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(thread_type.lp64)), info->_debug_handle, reinterpret_cast<uintptr_t>(info->_thread_type), sizeof(thread_type.lp64)));
|
||||
|
||||
/* Process different versions. */
|
||||
switch (thread_type.lp64._version) {
|
||||
case 0x0000:
|
||||
case 0xFFFF:
|
||||
FillWithCurrentInfoImpl(info, thread_type.lp64_v0);
|
||||
break;
|
||||
case 0x0001:
|
||||
FillWithCurrentInfoImpl(info, thread_type.lp64);
|
||||
break;
|
||||
default:
|
||||
return osdbg::ResultUnsupportedThreadVersion();
|
||||
}
|
||||
} else {
|
||||
/* Read in the thread type. */
|
||||
R_TRY(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(thread_type.ilp32)), info->_debug_handle, reinterpret_cast<uintptr_t>(info->_thread_type), sizeof(thread_type.ilp32)));
|
||||
|
||||
/* Process different versions. */
|
||||
switch (thread_type.ilp32._version) {
|
||||
case 0x0000:
|
||||
case 0xFFFF:
|
||||
FillWithCurrentInfoImpl(info, thread_type.ilp32_v0);
|
||||
break;
|
||||
case 0x0001:
|
||||
FillWithCurrentInfoImpl(info, thread_type.ilp32);
|
||||
break;
|
||||
default:
|
||||
return osdbg::ResultUnsupportedThreadVersion();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ThreadTypeType_Stratosphere:
|
||||
{
|
||||
/* Read in the thread type. */
|
||||
R_TRY(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(thread_type.stratosphere)), info->_debug_handle, reinterpret_cast<uintptr_t>(info->_thread_type), sizeof(thread_type.stratosphere)));
|
||||
|
||||
/* Set fields. */
|
||||
const auto &thread_type_impl = thread_type.stratosphere;
|
||||
|
||||
/* Check that our thread version is valid. */
|
||||
R_UNLESS(thread_type_impl.version == 0x0000 || thread_type_impl.version == 0xFFFF, osdbg::ResultUnsupportedThreadVersion());
|
||||
|
||||
info->_base_priority = thread_type_impl.base_priority;
|
||||
info->_current_priority = GetCurrentThreadPriorityImpl(info);
|
||||
info->_stack_size = thread_type_impl.stack_size;
|
||||
info->_stack = reinterpret_cast<uintptr_t>(thread_type_impl.stack);
|
||||
info->_argument = reinterpret_cast<uintptr_t>(thread_type_impl.argument);
|
||||
info->_function = reinterpret_cast<uintptr_t>(thread_type_impl.function);
|
||||
info->_name_pointer = reinterpret_cast<uintptr_t>(thread_type_impl.name_pointer);
|
||||
}
|
||||
break;
|
||||
case ThreadTypeType_Libnx:
|
||||
{
|
||||
/* Read in the thread type. */
|
||||
R_TRY(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(thread_type.libnx)), info->_debug_handle, reinterpret_cast<uintptr_t>(info->_thread_type), sizeof(thread_type.libnx)));
|
||||
|
||||
/* Set fields. */
|
||||
const auto &thread_type_impl = thread_type.libnx;
|
||||
|
||||
/* NOTE: libnx does not store/track base priority anywhere. */
|
||||
info->_base_priority = -1;
|
||||
info->_current_priority = GetCurrentThreadPriorityImpl(info);
|
||||
if (info->_current_priority != info->_base_priority) {
|
||||
info->_base_priority = info->_current_priority;
|
||||
}
|
||||
info->_stack_size = thread_type_impl.stack_sz;
|
||||
info->_stack = reinterpret_cast<uintptr_t>(thread_type_impl.stack_mirror);
|
||||
|
||||
/* Parse thread entry args. */
|
||||
{
|
||||
LibnxThreadEntryArgs thread_entry_args;
|
||||
|
||||
if (R_SUCCEEDED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(thread_entry_args)), info->_debug_handle, info->_stack + info->_stack_size, sizeof(LibnxThreadEntryArgs)))) {
|
||||
info->_argument = thread_entry_args.arg;
|
||||
info->_function = thread_entry_args.entry;
|
||||
} else {
|
||||
/* Failed to read the argument/function. */
|
||||
info->_argument = 0;
|
||||
info->_function = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Libnx threads don't have names. */
|
||||
info->_name_pointer = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return osdbg::ResultUnsupportedThreadVersion();
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "osdbg_types.hpp"
|
||||
|
||||
namespace ams::osdbg::impl {
|
||||
|
||||
class ThreadInfoHorizonImpl {
|
||||
public:
|
||||
static Result FillWithCurrentInfo(ThreadInfo *info);
|
||||
};
|
||||
|
||||
using ThreadInfoImpl = ThreadInfoHorizonImpl;
|
||||
|
||||
constexpr inline bool IsLp64(const ThreadInfo *info) {
|
||||
const auto as = info->_debug_info_create_process.flags & svc::CreateProcessFlag_AddressSpaceMask;
|
||||
return as == svc::CreateProcessFlag_AddressSpace64Bit || as == svc::CreateProcessFlag_AddressSpace64BitDeprecated;
|
||||
}
|
||||
|
||||
constexpr inline bool Is64BitArch(const ThreadInfo *info) {
|
||||
return (info->_debug_info_create_process.flags & svc::CreateProcessFlag_Is64Bit);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "osdbg_types.hpp"
|
||||
|
||||
namespace ams::osdbg::impl {
|
||||
|
||||
struct ThreadLocalRegionLp64 {
|
||||
u32 message_buffer[0x100 / sizeof(u32)];
|
||||
volatile u16 disable_counter;
|
||||
volatile u16 interrupt_flag;
|
||||
u32 reserved0;
|
||||
u64 reserved[15];
|
||||
u64 tls[10];
|
||||
u64 locale_ptr;
|
||||
s64 _errno_val;
|
||||
u64 thread_data;
|
||||
u64 eh_globals;
|
||||
u64 thread_pointer;
|
||||
u64 p_thread_type;
|
||||
};
|
||||
static_assert(sizeof(ThreadLocalRegionLp64) == sizeof(svc::ThreadLocalRegion));
|
||||
static_assert(__builtin_offsetof(ThreadLocalRegionLp64, tls) == 0x180);
|
||||
|
||||
|
||||
struct ThreadLocalRegionIlp32 {
|
||||
u32 message_buffer[0x100 / sizeof(u32)];
|
||||
volatile u16 disable_counter;
|
||||
volatile u16 interrupt_flag;
|
||||
u32 reserved[(0xC0 - 0x4) / sizeof(u32)];
|
||||
u32 tls[10];
|
||||
u32 locale_ptr;
|
||||
s32 _errno_val;
|
||||
u32 thread_data;
|
||||
u32 eh_globals;
|
||||
u32 thread_pointer;
|
||||
u32 p_thread_type;
|
||||
};
|
||||
static_assert(sizeof(ThreadLocalRegionIlp32) == sizeof(svc::ThreadLocalRegion));
|
||||
static_assert(__builtin_offsetof(ThreadLocalRegionIlp32, tls) == 0x1C0);
|
||||
|
||||
struct LibnxThreadVars {
|
||||
static constexpr u32 Magic = util::FourCC<'!','T','V','$'>::Code;
|
||||
|
||||
u32 magic;
|
||||
::Handle handle;
|
||||
::Thread *thread_ptr;
|
||||
void *reent;
|
||||
void *tls_tp;
|
||||
};
|
||||
static_assert(sizeof(LibnxThreadVars) == 0x20);
|
||||
|
||||
struct ThreadLocalRegionLibnx {
|
||||
u32 message_buffer[0x100 / sizeof(u32)];
|
||||
volatile u16 disable_counter;
|
||||
volatile u16 interrupt_flag;
|
||||
u32 reserved0;
|
||||
u64 tls[(0x1E0 - 0x108) / sizeof(u64)];
|
||||
LibnxThreadVars thread_vars;
|
||||
};
|
||||
static_assert(sizeof(ThreadLocalRegionLibnx) == sizeof(svc::ThreadLocalRegion));
|
||||
static_assert(__builtin_offsetof(ThreadLocalRegionLibnx, thread_vars) == 0x1E0);
|
||||
|
||||
struct LibnxThreadEntryArgs {
|
||||
u64 t;
|
||||
u64 entry;
|
||||
u64 arg;
|
||||
u64 reent;
|
||||
u64 tls;
|
||||
u64 padding;
|
||||
};
|
||||
|
||||
union ThreadLocalRegionCommon {
|
||||
ThreadLocalRegionIlp32 ilp32;
|
||||
ThreadLocalRegionLp64 lp64;
|
||||
ThreadLocalRegionLibnx libnx;
|
||||
};
|
||||
static_assert(sizeof(ThreadLocalRegionCommon) == sizeof(svc::ThreadLocalRegion));
|
||||
|
||||
inline ThreadLocalRegionCommon *GetTargetThreadLocalRegion(ThreadInfo *info) {
|
||||
return reinterpret_cast<ThreadLocalRegionCommon *>(info->_debug_info_create_thread.tls_address);
|
||||
}
|
||||
|
||||
void GetTargetThreadType(ThreadInfo *info);
|
||||
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 "osdbg_types.hpp"
|
||||
|
||||
namespace ams::osdbg::impl {
|
||||
|
||||
/* Check that our values are the same as Nintendo's. */
|
||||
static_assert(os::TlsSlotCountMax == 16);
|
||||
static_assert(os::SdkTlsSlotCountMax == 16);
|
||||
static_assert(os::ThreadNameLengthMax == 32);
|
||||
|
||||
struct ThreadTypeIlp32 {
|
||||
AlignedStorageIlp32<0, 2, alignof(u32)> _all_threads_node;
|
||||
AlignedStorageIlp32<0, 2, alignof(u32)> _waitable_object_list;
|
||||
u32 _padding[4];
|
||||
u8 _state;
|
||||
bool _stack_is_aliased;
|
||||
bool _auto_registered;
|
||||
u8 _suspend_count;
|
||||
s16 _base_priority;
|
||||
u16 _version;
|
||||
u32 _original_stack;
|
||||
u32 _stack;
|
||||
u32 _stack_size;
|
||||
u32 _argument;
|
||||
u32 _thread_function;
|
||||
u32 _current_fiber;
|
||||
u32 _initial_fiber;
|
||||
u32 _tls_value_array[os::TlsSlotCountMax + os::SdkTlsSlotCountMax];
|
||||
char _name_buffer[os::ThreadNameLengthMax];
|
||||
u32 _name_pointer;
|
||||
AlignedStorageIlp32<4, 0, alignof(u32)> _cs_thread;
|
||||
AlignedStorageIlp32<4, 0, alignof(u32)> _cv_thread;
|
||||
AlignedStorageIlp32<4, 0, alignof(u32)> _handle;
|
||||
u32 _lock_history;
|
||||
u32 _thread_id_low;
|
||||
u32 _thread_id_high;
|
||||
};
|
||||
static_assert(sizeof(ThreadTypeIlp32) == 0x100);
|
||||
|
||||
struct ThreadTypeIlp32Version0 {
|
||||
AlignedStorageIlp32<0, 2, alignof(u32)> _all_threads_node;
|
||||
AlignedStorageIlp32<0, 2, alignof(u32)> _waitable_object_list;
|
||||
u32 _padding[4];
|
||||
u8 _state;
|
||||
bool _stack_is_aliased;
|
||||
bool _auto_registered;
|
||||
u8 _padding1;
|
||||
s32 _base_priority;
|
||||
u32 _original_stack;
|
||||
u32 _stack;
|
||||
u32 _stack_size;
|
||||
u32 _argument;
|
||||
u32 _thread_function;
|
||||
u32 _current_fiber;
|
||||
u32 _initial_fiber;
|
||||
u32 _lock_history;
|
||||
u32 _tls_value_array[os::TlsSlotCountMax + os::SdkTlsSlotCountMax];
|
||||
char _name_buffer[os::ThreadNameLengthMax];
|
||||
u32 _name_pointer;
|
||||
AlignedStorageIlp32<4, 0, alignof(u32)> _cs_thread;
|
||||
AlignedStorageIlp32<4, 0, alignof(u32)> _cv_thread;
|
||||
AlignedStorageIlp32<4, 0, alignof(u32)> _handle;
|
||||
char _padding2[8];
|
||||
};
|
||||
static_assert(sizeof(ThreadTypeIlp32Version0) == 0x100);
|
||||
|
||||
struct ThreadTypeLp64 {
|
||||
AlignedStorageLp64<0, 2, alignof(u64)> _all_threads_node;
|
||||
AlignedStorageLp64<0, 2, alignof(u64)> _waitable_object_list;
|
||||
u64 _padding[4];
|
||||
u8 _state;
|
||||
bool _stack_is_aliased;
|
||||
bool _auto_registered;
|
||||
u8 _suspend_count;
|
||||
s16 _base_priority;
|
||||
u16 _version;
|
||||
u64 _original_stack;
|
||||
u64 _stack;
|
||||
u64 _stack_size;
|
||||
u64 _argument;
|
||||
u64 _thread_function;
|
||||
u64 _current_fiber;
|
||||
u64 _initial_fiber;
|
||||
u64 _tls_value_array[os::TlsSlotCountMax + os::SdkTlsSlotCountMax];
|
||||
char _name_buffer[os::ThreadNameLengthMax];
|
||||
u64 _name_pointer;
|
||||
AlignedStorageLp64<4, 0, alignof(u32)> _cs_thread;
|
||||
AlignedStorageLp64<4, 0, alignof(u32)> _cv_thread;
|
||||
AlignedStorageLp64<4, 0, alignof(u32)> _handle;
|
||||
u32 _lock_history;
|
||||
u64 thread_id;
|
||||
};
|
||||
static_assert(sizeof(ThreadTypeLp64) == 0x1C0);
|
||||
|
||||
struct ThreadTypeLp64Version0 {
|
||||
AlignedStorageLp64<0, 2, alignof(u64)> _all_threads_node;
|
||||
AlignedStorageLp64<0, 2, alignof(u64)> _waitable_object_list;
|
||||
u64 _padding[4];
|
||||
u8 _state;
|
||||
bool _stack_is_aliased;
|
||||
bool _auto_registered;
|
||||
u8 _suspend_count;
|
||||
s16 _base_priority;
|
||||
u16 _version;
|
||||
u64 _original_stack;
|
||||
u64 _stack;
|
||||
u64 _stack_size;
|
||||
u64 _argument;
|
||||
u64 _thread_function;
|
||||
u64 _current_fiber;
|
||||
u64 _initial_fiber;
|
||||
u32 _lock_history;
|
||||
u32 _padding2;
|
||||
u64 _tls_value_array[os::TlsSlotCountMax + os::SdkTlsSlotCountMax];
|
||||
char _name_buffer[os::ThreadNameLengthMax];
|
||||
u64 _name_pointer;
|
||||
AlignedStorageLp64<4, 0, alignof(u32)> _cs_thread;
|
||||
AlignedStorageLp64<4, 0, alignof(u32)> _cv_thread;
|
||||
AlignedStorageLp64<4, 0, alignof(u32)> _handle;
|
||||
u32 _padding3;
|
||||
};
|
||||
static_assert(sizeof(ThreadTypeLp64Version0) == 0x1C0);
|
||||
|
||||
union ThreadTypeCommon {
|
||||
ThreadTypeIlp32 ilp32;
|
||||
ThreadTypeLp64 lp64;
|
||||
ThreadTypeIlp32Version0 ilp32_v0;
|
||||
ThreadTypeLp64Version0 lp64_v0;
|
||||
os::ThreadType stratosphere;
|
||||
::Thread libnx;
|
||||
};
|
||||
|
||||
}
|
27
libraries/libstratosphere/source/osdbg/impl/osdbg_types.hpp
Normal file
27
libraries/libstratosphere/source/osdbg/impl/osdbg_types.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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::osdbg::impl {
|
||||
|
||||
template<size_t Size, int NumPointers, size_t Alignment>
|
||||
using AlignedStorageIlp32 = typename std::aligned_storage<Size + NumPointers * sizeof(u32), Alignment>::type;
|
||||
|
||||
template<size_t Size, int NumPointers, size_t Alignment>
|
||||
using AlignedStorageLp64 = typename std::aligned_storage<Size + NumPointers * sizeof(u64), Alignment>::type;
|
||||
|
||||
}
|
62
libraries/libstratosphere/source/osdbg/osdbg_thread.cpp
Normal file
62
libraries/libstratosphere/source/osdbg/osdbg_thread.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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_HORIZON)
|
||||
#include "impl/osdbg_thread_info.os.horizon.hpp"
|
||||
#else
|
||||
#error "Unknown OS for ams::osdbg::ThreadInfo"
|
||||
#endif
|
||||
|
||||
namespace ams::osdbg {
|
||||
|
||||
Result InitializeThreadInfo(ThreadInfo *thread_info, svc::Handle debug_handle, const svc::DebugInfoCreateProcess *create_process, const svc::DebugInfoCreateThread *create_thread) {
|
||||
/* Set basic fields. */
|
||||
thread_info->_thread_type = nullptr;
|
||||
thread_info->_thread_type_type = ThreadTypeType_Unknown;
|
||||
thread_info->_debug_handle = debug_handle;
|
||||
thread_info->_debug_info_create_process = *create_process;
|
||||
thread_info->_debug_info_create_thread = *create_thread;
|
||||
|
||||
/* Update the current info. */
|
||||
return impl::ThreadInfoImpl::FillWithCurrentInfo(thread_info);
|
||||
}
|
||||
|
||||
Result UpdateThreadInfo(ThreadInfo *thread_info) {
|
||||
/* Update the current info. */
|
||||
return impl::ThreadInfoImpl::FillWithCurrentInfo(thread_info);
|
||||
}
|
||||
|
||||
Result GetThreadName(char *dst, const ThreadInfo *thread_info) {
|
||||
/* Get the name pointer. */
|
||||
const auto name_pointer = GetThreadNamePointer(thread_info);
|
||||
|
||||
/* Read the name. */
|
||||
if (name_pointer != 0) {
|
||||
return svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(dst), thread_info->_debug_handle, name_pointer, os::ThreadNameLengthMax);
|
||||
} else {
|
||||
/* Special-case libnx threads. */
|
||||
if (thread_info->_thread_type_type == ThreadTypeType_Libnx) {
|
||||
util::TSNPrintf(dst, os::ThreadNameLengthMax, "libnx Thread_0x%p", reinterpret_cast<void *>(thread_info->_thread_type));
|
||||
} else {
|
||||
util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_0x%p", reinterpret_cast<void *>(thread_info->_thread_type));
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -49,6 +49,7 @@
|
|||
#include <vapours/results/nim_results.hpp>
|
||||
#include <vapours/results/ns_results.hpp>
|
||||
#include <vapours/results/os_results.hpp>
|
||||
#include <vapours/results/osdbg_results.hpp>
|
||||
#include <vapours/results/pcv_results.hpp>
|
||||
#include <vapours/results/pgl_results.hpp>
|
||||
#include <vapours/results/pm_results.hpp>
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/results/results_common.hpp>
|
||||
|
||||
namespace ams::osdbg {
|
||||
|
||||
R_DEFINE_NAMESPACE_RESULT_MODULE(7);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(CannotGetThreadInfo, 1);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedThreadVersion, 2);
|
||||
|
||||
}
|
|
@ -121,7 +121,7 @@ namespace ams::svc {
|
|||
struct DebugInfoCreateThread {
|
||||
u64 thread_id;
|
||||
u32 tls_address;
|
||||
u32 entrypoint;
|
||||
/* Removed in 11.0.0 u32 entrypoint; */
|
||||
};
|
||||
|
||||
struct DebugInfoExitProcess {
|
||||
|
|
|
@ -152,13 +152,22 @@ namespace ams::creport {
|
|||
std::memcpy(this->tls, thread_tls, sizeof(this->tls));
|
||||
/* Try to detect libnx threads, and skip name parsing then. */
|
||||
if (*(reinterpret_cast<u32 *>(&thread_tls[0x1E0])) != LibnxThreadVarMagic) {
|
||||
u8 thread_type[0x1D0];
|
||||
u8 thread_type[0x1C0];
|
||||
const u64 thread_type_addr = *(reinterpret_cast<u64 *>(&thread_tls[0x1F8]));
|
||||
if (R_SUCCEEDED(svcReadDebugProcessMemory(thread_type, debug_handle, thread_type_addr, sizeof(thread_type)))) {
|
||||
/* Check thread name is actually at thread name. */
|
||||
static_assert(0x1A8 - 0x188 == NameLengthMax, "NameLengthMax definition!");
|
||||
if (*(reinterpret_cast<u64 *>(&thread_type[0x1A8])) == thread_type_addr + 0x188) {
|
||||
std::memcpy(this->name, thread_type + 0x188, NameLengthMax);
|
||||
/* Get the thread version. */
|
||||
const u16 thread_version = *reinterpret_cast<u16 *>(&thread_type[0x46]);
|
||||
if (thread_version == 0 || thread_version == 0xFFFF) {
|
||||
/* Check thread name is actually at thread name. */
|
||||
static_assert(0x1A8 - 0x188 == NameLengthMax, "NameLengthMax definition!");
|
||||
if (*(reinterpret_cast<u64 *>(&thread_type[0x1A8])) == thread_type_addr + 0x188) {
|
||||
std::memcpy(this->name, thread_type + 0x188, NameLengthMax);
|
||||
}
|
||||
} else if (thread_version == 1) {
|
||||
static_assert(0x1A0 - 0x180 == NameLengthMax, "NameLengthMax definition!");
|
||||
if (*(reinterpret_cast<u64 *>(&thread_type[0x1A0])) == thread_type_addr + 0x180) {
|
||||
std::memcpy(this->name, thread_type + 0x180, NameLengthMax);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue