/* * Copyright (c) Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #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; } R_SUCCEED(); } Result GetThreadArgumentAndStackPointer(u64 *out_arg, u64 *out_sp, ThreadInfo *info) { /* Read the thread context. */ svc::ThreadContext thread_context; R_TRY(svc::GetDebugThreadContext(std::addressof(thread_context), info->_debug_handle, info->_debug_info_create_thread.thread_id, svc::ThreadContextFlag_General | svc::ThreadContextFlag_Control)); /* Argument is in r0. */ *out_arg = thread_context.r[0]; /* Stack pointer varies by architecture. */ if (Is64BitArch(info)) { *out_sp = thread_context.sp; } else { *out_sp = thread_context.r[13]; } R_SUCCEED(); } void DetectStratosphereThread(ThreadInfo *info) { /* Stratosphere threads are initially misdetected as libnx threads. */ if (info->_thread_type_type != ThreadTypeType_Libnx || info->_thread_type == nullptr) { return; } /* Convert to a parent pointer. */ os::ThreadType *stratosphere_ptr = util::GetParentPointer<&os::ThreadType::thread_impl_storage>(std::addressof(info->_thread_type->libnx)); /* Read the magic. */ u16 magic; if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast(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); } } }