osdbg: implement thread info api

This commit is contained in:
Michael Scire 2021-07-19 17:35:00 -07:00 committed by SciresM
parent 9f1f0c7cbd
commit c6fad1b0ee
19 changed files with 912 additions and 7 deletions

View file

@ -71,6 +71,7 @@
#include <stratosphere/nim.hpp> #include <stratosphere/nim.hpp>
#include <stratosphere/ns.hpp> #include <stratosphere/ns.hpp>
#include <stratosphere/nsd.hpp> #include <stratosphere/nsd.hpp>
#include <stratosphere/osdbg.hpp>
#include <stratosphere/patcher.hpp> #include <stratosphere/patcher.hpp>
#include <stratosphere/pcv.hpp> #include <stratosphere/pcv.hpp>
#include <stratosphere/pgl.hpp> #include <stratosphere/pgl.hpp>

View file

@ -36,6 +36,8 @@ namespace ams::os {
using ThreadImpl = ::Thread; using ThreadImpl = ::Thread;
struct ThreadType { struct ThreadType {
static constexpr u16 Magic = 0xF5A5;
enum State { enum State {
State_NotInitialized = 0, State_NotInitialized = 0,
State_Initialized = 1, State_Initialized = 1,
@ -49,7 +51,9 @@ namespace ams::os {
uintptr_t reserved[4]; uintptr_t reserved[4];
u8 state; u8 state;
u8 suspend_count; u8 suspend_count;
s32 base_priority; u16 magic;
s16 base_priority;
u16 version;
char name_buffer[ThreadNameLengthMax]; char name_buffer[ThreadNameLengthMax];
const char *name_pointer; const char *name_pointer;
ThreadId thread_id; ThreadId thread_id;

View 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>

View 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_types.hpp>
#include <stratosphere/osdbg/osdbg_thread_api.hpp>

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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;
};
}

View file

@ -31,6 +31,8 @@ namespace ams::os::impl {
util::ConstructAt(thread->waitlist); util::ConstructAt(thread->waitlist);
/* Set member variables. */ /* 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->thread_impl = (thread_impl != nullptr) ? thread_impl : std::addressof(thread->thread_impl_storage);
thread->function = function; thread->function = function;
thread->argument = arg; thread->argument = arg;
@ -134,6 +136,7 @@ namespace ams::os::impl {
util::DestroyAt(thread->waitlist); util::DestroyAt(thread->waitlist);
thread->name_buffer[0] = '\x00'; thread->name_buffer[0] = '\x00';
thread->magic = 0xCCCC;
{ {
std::scoped_lock tlk(this->cs); std::scoped_lock tlk(this->cs);

View file

@ -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();
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}

View file

@ -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;
};
}

View 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;
}

View 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();
}
}
}

View file

@ -49,6 +49,7 @@
#include <vapours/results/nim_results.hpp> #include <vapours/results/nim_results.hpp>
#include <vapours/results/ns_results.hpp> #include <vapours/results/ns_results.hpp>
#include <vapours/results/os_results.hpp> #include <vapours/results/os_results.hpp>
#include <vapours/results/osdbg_results.hpp>
#include <vapours/results/pcv_results.hpp> #include <vapours/results/pcv_results.hpp>
#include <vapours/results/pgl_results.hpp> #include <vapours/results/pgl_results.hpp>
#include <vapours/results/pm_results.hpp> #include <vapours/results/pm_results.hpp>

View 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 <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);
}

View file

@ -121,7 +121,7 @@ namespace ams::svc {
struct DebugInfoCreateThread { struct DebugInfoCreateThread {
u64 thread_id; u64 thread_id;
u32 tls_address; u32 tls_address;
u32 entrypoint; /* Removed in 11.0.0 u32 entrypoint; */
}; };
struct DebugInfoExitProcess { struct DebugInfoExitProcess {

View file

@ -152,13 +152,22 @@ namespace ams::creport {
std::memcpy(this->tls, thread_tls, sizeof(this->tls)); std::memcpy(this->tls, thread_tls, sizeof(this->tls));
/* Try to detect libnx threads, and skip name parsing then. */ /* Try to detect libnx threads, and skip name parsing then. */
if (*(reinterpret_cast<u32 *>(&thread_tls[0x1E0])) != LibnxThreadVarMagic) { 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])); 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)))) { if (R_SUCCEEDED(svcReadDebugProcessMemory(thread_type, debug_handle, thread_type_addr, sizeof(thread_type)))) {
/* Check thread name is actually at thread name. */ /* Get the thread version. */
static_assert(0x1A8 - 0x188 == NameLengthMax, "NameLengthMax definition!"); const u16 thread_version = *reinterpret_cast<u16 *>(&thread_type[0x46]);
if (*(reinterpret_cast<u64 *>(&thread_type[0x1A8])) == thread_type_addr + 0x188) { if (thread_version == 0 || thread_version == 0xFFFF) {
std::memcpy(this->name, thread_type + 0x188, NameLengthMax); /* 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);
}
} }
} }
} }