From cf27c8a7a25092be8444f6b78d02e91872fadbf7 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 19 Jul 2021 17:35:00 -0700 Subject: [PATCH] osdbg: implement thread info api --- .../libstratosphere/include/stratosphere.hpp | 1 + .../stratosphere/os/os_thread_types.hpp | 6 +- .../include/stratosphere/osdbg.hpp | 19 ++ .../stratosphere/osdbg/osdbg_thread.hpp | 19 ++ .../stratosphere/osdbg/osdbg_thread_api.hpp | 29 +++ .../osdbg/osdbg_thread_api_impl.hpp | 50 +++++ .../stratosphere/osdbg/osdbg_thread_types.hpp | 49 +++++ .../source/os/impl/os_thread_manager.cpp | 3 + .../impl/osdbg_thread_info.os.horizon.cpp | 176 ++++++++++++++++++ .../impl/osdbg_thread_info.os.horizon.hpp | 38 ++++ .../osdbg_thread_local_region.os.horizon.cpp | 142 ++++++++++++++ .../osdbg_thread_local_region.os.horizon.hpp | 100 ++++++++++ .../impl/osdbg_thread_type.os.horizon.hpp | 149 +++++++++++++++ .../source/osdbg/impl/osdbg_types.hpp | 27 +++ .../source/osdbg/osdbg_thread.cpp | 62 ++++++ .../libvapours/include/vapours/results.hpp | 1 + .../include/vapours/results/osdbg_results.hpp | 27 +++ .../include/vapours/svc/svc_types_dmnt.hpp | 2 +- .../creport/source/creport_threads.cpp | 19 +- 19 files changed, 912 insertions(+), 7 deletions(-) create mode 100644 libraries/libstratosphere/include/stratosphere/osdbg.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/osdbg/osdbg_thread.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/osdbg/osdbg_thread_api.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/osdbg/osdbg_thread_api_impl.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/osdbg/osdbg_thread_types.hpp create mode 100644 libraries/libstratosphere/source/osdbg/impl/osdbg_thread_info.os.horizon.cpp create mode 100644 libraries/libstratosphere/source/osdbg/impl/osdbg_thread_info.os.horizon.hpp create mode 100644 libraries/libstratosphere/source/osdbg/impl/osdbg_thread_local_region.os.horizon.cpp create mode 100644 libraries/libstratosphere/source/osdbg/impl/osdbg_thread_local_region.os.horizon.hpp create mode 100644 libraries/libstratosphere/source/osdbg/impl/osdbg_thread_type.os.horizon.hpp create mode 100644 libraries/libstratosphere/source/osdbg/impl/osdbg_types.hpp create mode 100644 libraries/libstratosphere/source/osdbg/osdbg_thread.cpp create mode 100644 libraries/libvapours/include/vapours/results/osdbg_results.hpp diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index d63eeaf0e..c39805152 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -71,6 +71,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/os/os_thread_types.hpp b/libraries/libstratosphere/include/stratosphere/os/os_thread_types.hpp index b0ddb9648..a0662fd66 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_thread_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_thread_types.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; diff --git a/libraries/libstratosphere/include/stratosphere/osdbg.hpp b/libraries/libstratosphere/include/stratosphere/osdbg.hpp new file mode 100644 index 000000000..9be885924 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/osdbg.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 . + */ + +#pragma once + +#include diff --git a/libraries/libstratosphere/include/stratosphere/osdbg/osdbg_thread.hpp b/libraries/libstratosphere/include/stratosphere/osdbg/osdbg_thread.hpp new file mode 100644 index 000000000..83cebe9c0 --- /dev/null +++ b/libraries/libstratosphere/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 . + */ +#pragma once + +#include +#include \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/osdbg/osdbg_thread_api.hpp b/libraries/libstratosphere/include/stratosphere/osdbg/osdbg_thread_api.hpp new file mode 100644 index 000000000..dcffca8a1 --- /dev/null +++ b/libraries/libstratosphere/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 . + */ +#pragma once +#include +#include + +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); + +} diff --git a/libraries/libstratosphere/include/stratosphere/osdbg/osdbg_thread_api_impl.hpp b/libraries/libstratosphere/include/stratosphere/osdbg/osdbg_thread_api_impl.hpp new file mode 100644 index 000000000..6593ce127 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/osdbg/osdbg_thread_api_impl.hpp @@ -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 . + */ +#pragma once +#include +#include + +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; + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/osdbg/osdbg_thread_types.hpp b/libraries/libstratosphere/include/stratosphere/osdbg/osdbg_thread_types.hpp new file mode 100644 index 000000000..2b112b257 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/osdbg/osdbg_thread_types.hpp @@ -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 . + */ +#pragma once +#include + +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; + }; + +} diff --git a/libraries/libstratosphere/source/os/impl/os_thread_manager.cpp b/libraries/libstratosphere/source/os/impl/os_thread_manager.cpp index b55db71d4..b83d2b771 100644 --- a/libraries/libstratosphere/source/os/impl/os_thread_manager.cpp +++ b/libraries/libstratosphere/source/os/impl/os_thread_manager.cpp @@ -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); diff --git a/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_info.os.horizon.cpp b/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_info.os.horizon.cpp new file mode 100644 index 000000000..6b2c7d0d3 --- /dev/null +++ b/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_info.os.horizon.cpp @@ -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 . + */ +#include +#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(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(std::addressof(thread_type.lp64)), info->_debug_handle, reinterpret_cast(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(std::addressof(thread_type.ilp32)), info->_debug_handle, reinterpret_cast(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(std::addressof(thread_type.stratosphere)), info->_debug_handle, reinterpret_cast(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(thread_type_impl.stack); + info->_argument = reinterpret_cast(thread_type_impl.argument); + info->_function = reinterpret_cast(thread_type_impl.function); + info->_name_pointer = reinterpret_cast(thread_type_impl.name_pointer); + } + break; + case ThreadTypeType_Libnx: + { + /* Read in the thread type. */ + R_TRY(svc::ReadDebugProcessMemory(reinterpret_cast(std::addressof(thread_type.libnx)), info->_debug_handle, reinterpret_cast(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(thread_type_impl.stack_mirror); + + /* Parse thread entry args. */ + { + LibnxThreadEntryArgs thread_entry_args; + + if (R_SUCCEEDED(svc::ReadDebugProcessMemory(reinterpret_cast(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(); + } + +} diff --git a/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_info.os.horizon.hpp b/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_info.os.horizon.hpp new file mode 100644 index 000000000..2850f47e7 --- /dev/null +++ b/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_info.os.horizon.hpp @@ -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 . + */ +#pragma once +#include +#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); + } + +} diff --git a/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_local_region.os.horizon.cpp b/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_local_region.os.horizon.cpp new file mode 100644 index 000000000..9c93ed515 --- /dev/null +++ b/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_local_region.os.horizon.cpp @@ -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 . + */ +#include +#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(std::addressof(tlr)), info->_debug_handle, reinterpret_cast(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(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(std::addressof(magic)), info->_debug_handle, reinterpret_cast(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(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(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(std::addressof(entry_args)), info->_debug_handle, arg, sizeof(entry_args)))) { + return; + } + + info->_thread_type = reinterpret_cast(entry_args.t); + } else { + info->_thread_type_type = ThreadTypeType_Nintendo; + info->_thread_type = reinterpret_cast(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); + } + } + +} diff --git a/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_local_region.os.horizon.hpp b/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_local_region.os.horizon.hpp new file mode 100644 index 000000000..dc51a6155 --- /dev/null +++ b/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_local_region.os.horizon.hpp @@ -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 . + */ +#pragma once +#include +#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(info->_debug_info_create_thread.tls_address); + } + + void GetTargetThreadType(ThreadInfo *info); + +} diff --git a/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_type.os.horizon.hpp b/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_type.os.horizon.hpp new file mode 100644 index 000000000..f5ba193d4 --- /dev/null +++ b/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_type.os.horizon.hpp @@ -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 . + */ +#pragma once +#include +#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; + }; + +} diff --git a/libraries/libstratosphere/source/osdbg/impl/osdbg_types.hpp b/libraries/libstratosphere/source/osdbg/impl/osdbg_types.hpp new file mode 100644 index 000000000..ad7deaa30 --- /dev/null +++ b/libraries/libstratosphere/source/osdbg/impl/osdbg_types.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 . + */ +#pragma once +#include + +namespace ams::osdbg::impl { + + template + using AlignedStorageIlp32 = typename std::aligned_storage::type; + + template + using AlignedStorageLp64 = typename std::aligned_storage::type; + +} diff --git a/libraries/libstratosphere/source/osdbg/osdbg_thread.cpp b/libraries/libstratosphere/source/osdbg/osdbg_thread.cpp new file mode 100644 index 000000000..0b5c8446f --- /dev/null +++ b/libraries/libstratosphere/source/osdbg/osdbg_thread.cpp @@ -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 . + */ +#include + +#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(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(thread_info->_thread_type)); + } else { + util::TSNPrintf(dst, os::ThreadNameLengthMax, "Thread_0x%p", reinterpret_cast(thread_info->_thread_type)); + } + + return ResultSuccess(); + } + } + +} diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index 6e64fe90c..a9534855f 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/results/osdbg_results.hpp b/libraries/libvapours/include/vapours/results/osdbg_results.hpp new file mode 100644 index 000000000..f4cb919d5 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/osdbg_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 . + */ + +#pragma once +#include + +namespace ams::osdbg { + + R_DEFINE_NAMESPACE_RESULT_MODULE(7); + + R_DEFINE_ERROR_RESULT(CannotGetThreadInfo, 1); + R_DEFINE_ERROR_RESULT(UnsupportedThreadVersion, 2); + +} diff --git a/libraries/libvapours/include/vapours/svc/svc_types_dmnt.hpp b/libraries/libvapours/include/vapours/svc/svc_types_dmnt.hpp index a0788d428..0f21d8a6c 100644 --- a/libraries/libvapours/include/vapours/svc/svc_types_dmnt.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_types_dmnt.hpp @@ -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 { diff --git a/stratosphere/creport/source/creport_threads.cpp b/stratosphere/creport/source/creport_threads.cpp index 162a0533f..d33c3ae82 100644 --- a/stratosphere/creport/source/creport_threads.cpp +++ b/stratosphere/creport/source/creport_threads.cpp @@ -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(&thread_tls[0x1E0])) != LibnxThreadVarMagic) { - u8 thread_type[0x1D0]; + u8 thread_type[0x1C0]; const u64 thread_type_addr = *(reinterpret_cast(&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(&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(&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(&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(&thread_type[0x1A0])) == thread_type_addr + 0x180) { + std::memcpy(this->name, thread_type + 0x180, NameLengthMax); + } } } }