mirror of
				https://github.com/Atmosphere-NX/Atmosphere-libs.git
				synced 2025-10-31 03:25:47 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			143 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			143 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) Atmosphère-NX
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify it
 | |
|  * under the terms and conditions of the GNU General Public License,
 | |
|  * version 2, as published by the Free Software Foundation.
 | |
|  *
 | |
|  * This program is distributed in the hope it will be useful, but WITHOUT
 | |
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 | |
|  * more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| #include <stratosphere.hpp>
 | |
| #include "osdbg_thread_info.os.horizon.hpp"
 | |
| #include "osdbg_thread_type.os.horizon.hpp"
 | |
| #include "osdbg_thread_local_region.os.horizon.hpp"
 | |
| 
 | |
| namespace ams::osdbg::impl {
 | |
| 
 | |
|     namespace {
 | |
| 
 | |
|         Result GetThreadTypePointerFromThreadLocalRegion(uintptr_t *out, ThreadInfo *info) {
 | |
|             /* Detect lp64. */
 | |
|             const bool is_lp64 = IsLp64(info);
 | |
| 
 | |
|             /* Get the thread local region. */
 | |
|             const auto *tlr_address = GetTargetThreadLocalRegion(info);
 | |
| 
 | |
|             /* Read the thread local region. */
 | |
|             ThreadLocalRegionCommon tlr;
 | |
|             R_TRY(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(tlr)), info->_debug_handle, reinterpret_cast<uintptr_t>(tlr_address), sizeof(tlr)));
 | |
| 
 | |
|             /* Detect libnx vs nintendo via magic number. */
 | |
|             if (tlr.libnx.thread_vars.magic == LibnxThreadVars::Magic) {
 | |
|                 info->_thread_type_type = ThreadTypeType_Libnx;
 | |
|                 *out = reinterpret_cast<uintptr_t>(tlr.libnx.thread_vars.thread_ptr);
 | |
|             } else {
 | |
|                 info->_thread_type_type = ThreadTypeType_Nintendo;
 | |
|                 *out = is_lp64 ? tlr.lp64.p_thread_type : tlr.ilp32.p_thread_type;
 | |
|             }
 | |
| 
 | |
|             R_SUCCEED();
 | |
|         }
 | |
| 
 | |
|         Result GetThreadArgumentAndStackPointer(u64 *out_arg, u64 *out_sp, ThreadInfo *info) {
 | |
|             /* Read the thread context. */
 | |
|             svc::ThreadContext thread_context;
 | |
|             R_TRY(svc::GetDebugThreadContext(std::addressof(thread_context), info->_debug_handle, info->_debug_info_create_thread.thread_id, svc::ThreadContextFlag_General | svc::ThreadContextFlag_Control));
 | |
| 
 | |
|             /* Argument is in r0. */
 | |
|             *out_arg = thread_context.r[0];
 | |
| 
 | |
|             /* Stack pointer varies by architecture. */
 | |
|             if (Is64BitArch(info)) {
 | |
|                 *out_sp = thread_context.sp;
 | |
|             } else {
 | |
|                 *out_sp = thread_context.r[13];
 | |
|             }
 | |
| 
 | |
|             R_SUCCEED();
 | |
|         }
 | |
| 
 | |
|         void DetectStratosphereThread(ThreadInfo *info) {
 | |
|             /* Stratosphere threads are initially misdetected as libnx threads. */
 | |
|             if (info->_thread_type_type != ThreadTypeType_Libnx || info->_thread_type == nullptr) {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             /* Convert to a parent pointer. */
 | |
|             os::ThreadType *stratosphere_ptr = util::GetParentPointer<&os::ThreadType::thread_impl_storage>(std::addressof(info->_thread_type->libnx));
 | |
| 
 | |
|             /* Read the magic. */
 | |
|             u16 magic;
 | |
|             if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(magic)), info->_debug_handle, reinterpret_cast<uintptr_t>(std::addressof(stratosphere_ptr->magic)), sizeof(magic)))) {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             /* Check the magic. */
 | |
|             if (magic == os::ThreadType::Magic) {
 | |
|                 info->_thread_type_type = ThreadTypeType_Stratosphere;
 | |
|                 info->_thread_type      = reinterpret_cast<ThreadTypeCommon *>(stratosphere_ptr);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     void GetTargetThreadType(ThreadInfo *info) {
 | |
|         /* Ensure we exit with correct state. */
 | |
|         auto type_guard = SCOPE_GUARD {
 | |
|             info->_thread_type      = nullptr;
 | |
|             info->_thread_type_type = ThreadTypeType_Unknown;
 | |
|         };
 | |
| 
 | |
|         /* Read the thread type pointer. */
 | |
|         uintptr_t tlr_thread_type;
 | |
|         if (R_FAILED(GetThreadTypePointerFromThreadLocalRegion(std::addressof(tlr_thread_type), info))) {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         /* Handle the case where we have a thread type. */
 | |
|         if (tlr_thread_type != 0) {
 | |
|             info->_thread_type = reinterpret_cast<ThreadTypeCommon *>(tlr_thread_type);
 | |
|             DetectStratosphereThread(info);
 | |
|             type_guard.Cancel();
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         /* Otherwise, the thread is just created, and we should read its context. */
 | |
|         u64 arg, sp;
 | |
|         if (R_FAILED(GetThreadArgumentAndStackPointer(std::addressof(arg), std::addressof(sp), info))) {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         /* We may have been bamboozled into thinking a nintendo thread was a libnx thread, so check that. */
 | |
|         /* Nintendo threads have argument=ThreadType, libnx threads have argument=ThreadEntryArgs. */
 | |
|         if (info->_thread_type_type == ThreadTypeType_Nintendo && sp == arg) {
 | |
|             /* It's a libnx thread, so we should parse the entry args. */
 | |
|             info->_thread_type_type = ThreadTypeType_Libnx;
 | |
| 
 | |
|             /* Read the entry args. */
 | |
|             LibnxThreadEntryArgs entry_args;
 | |
|             if (R_FAILED(svc::ReadDebugProcessMemory(reinterpret_cast<uintptr_t>(std::addressof(entry_args)), info->_debug_handle, arg, sizeof(entry_args)))) {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             info->_thread_type      = reinterpret_cast<ThreadTypeCommon *>(entry_args.t);
 | |
|         } else {
 | |
|             info->_thread_type_type = ThreadTypeType_Nintendo;
 | |
|             info->_thread_type      = reinterpret_cast<ThreadTypeCommon *>(arg);
 | |
|         }
 | |
| 
 | |
|         /* If we got the thread type, we don't need to reset our state. */
 | |
|         if (info->_thread_type != nullptr) {
 | |
|             type_guard.Cancel();
 | |
|             DetectStratosphereThread(info);
 | |
|         }
 | |
|     }
 | |
| 
 | |
| }
 |