// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/demangle.h" #include "core/arm/debug.h" #include "core/arm/symbols.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_thread.h" #include "core/memory.h" namespace Core { namespace { std::optional GetNameFromThreadType64(Core::Memory::Memory& memory, const Kernel::KThread& thread) { // Read thread type from TLS const VAddr tls_thread_type{memory.Read64(thread.GetTlsAddress() + 0x1f8)}; const VAddr argument_thread_type{thread.GetArgument()}; if (argument_thread_type && tls_thread_type != argument_thread_type) { // Probably not created by nnsdk, no name available. return std::nullopt; } if (!tls_thread_type) { return std::nullopt; } const u16 version{memory.Read16(tls_thread_type + 0x46)}; VAddr name_pointer{}; if (version == 1) { name_pointer = memory.Read64(tls_thread_type + 0x1a0); } else { name_pointer = memory.Read64(tls_thread_type + 0x1a8); } if (!name_pointer) { // No name provided. return std::nullopt; } return memory.ReadCString(name_pointer, 256); } std::optional GetNameFromThreadType32(Core::Memory::Memory& memory, const Kernel::KThread& thread) { // Read thread type from TLS const VAddr tls_thread_type{memory.Read32(thread.GetTlsAddress() + 0x1fc)}; const VAddr argument_thread_type{thread.GetArgument()}; if (argument_thread_type && tls_thread_type != argument_thread_type) { // Probably not created by nnsdk, no name available. return std::nullopt; } if (!tls_thread_type) { return std::nullopt; } const u16 version{memory.Read16(tls_thread_type + 0x26)}; VAddr name_pointer{}; if (version == 1) { name_pointer = memory.Read32(tls_thread_type + 0xe4); } else { name_pointer = memory.Read32(tls_thread_type + 0xe8); } if (!name_pointer) { // No name provided. return std::nullopt; } return memory.ReadCString(name_pointer, 256); } constexpr std::array SegmentBases{ 0x60000000ULL, 0x7100000000ULL, }; void SymbolicateBacktrace(const Kernel::KProcess* process, std::vector& out) { auto modules = FindModules(process); const bool is_64 = process->Is64Bit(); std::map symbols; for (const auto& module : modules) { symbols.insert_or_assign(module.second, Symbols::GetSymbols(module.first, process->GetMemory(), is_64)); } for (auto& entry : out) { VAddr base = 0; for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) { const auto& module{*iter}; if (entry.original_address >= module.first) { entry.module = module.second; base = module.first; break; } } entry.offset = entry.original_address - base; entry.address = SegmentBases[is_64] + entry.offset; if (entry.module.empty()) { entry.module = "unknown"; } const auto symbol_set = symbols.find(entry.module); if (symbol_set != symbols.end()) { const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); if (symbol) { entry.name = Common::DemangleSymbol(*symbol); } } } } std::vector GetAArch64Backtrace(const Kernel::KProcess* process, const Kernel::Svc::ThreadContext& ctx) { std::vector out; auto& memory = process->GetMemory(); auto pc = ctx.pc, lr = ctx.lr, fp = ctx.fp; out.push_back({"", 0, pc, 0, ""}); // fp (= x29) points to the previous frame record. // Frame records are two words long: // fp+0 : pointer to previous frame record // fp+8 : value of lr for frame for (size_t i = 0; i < 256; i++) { out.push_back({"", 0, lr, 0, ""}); if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { break; } lr = memory.Read64(fp + 8); fp = memory.Read64(fp); } SymbolicateBacktrace(process, out); return out; } std::vector GetAArch32Backtrace(const Kernel::KProcess* process, const Kernel::Svc::ThreadContext& ctx) { std::vector out; auto& memory = process->GetMemory(); auto pc = ctx.pc, lr = ctx.lr, fp = ctx.fp; out.push_back({"", 0, pc, 0, ""}); // fp (= r11) points to the last frame record. // Frame records are two words long: // fp+0 : pointer to previous frame record // fp+4 : value of lr for frame for (size_t i = 0; i < 256; i++) { out.push_back({"", 0, lr, 0, ""}); if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { break; } lr = memory.Read32(fp + 4); fp = memory.Read32(fp); } SymbolicateBacktrace(process, out); return out; } } // namespace std::optional GetThreadName(const Kernel::KThread* thread) { const auto* process = thread->GetOwnerProcess(); if (process->Is64Bit()) { return GetNameFromThreadType64(process->GetMemory(), *thread); } else { return GetNameFromThreadType32(process->GetMemory(), *thread); } } std::string_view GetThreadWaitReason(const Kernel::KThread* thread) { switch (thread->GetWaitReasonForDebugging()) { case Kernel::ThreadWaitReasonForDebugging::Sleep: return "Sleep"; case Kernel::ThreadWaitReasonForDebugging::IPC: return "IPC"; case Kernel::ThreadWaitReasonForDebugging::Synchronization: return "Synchronization"; case Kernel::ThreadWaitReasonForDebugging::ConditionVar: return "ConditionVar"; case Kernel::ThreadWaitReasonForDebugging::Arbitration: return "Arbitration"; case Kernel::ThreadWaitReasonForDebugging::Suspended: return "Suspended"; default: return "Unknown"; } } std::string GetThreadState(const Kernel::KThread* thread) { switch (thread->GetState()) { case Kernel::ThreadState::Initialized: return "Initialized"; case Kernel::ThreadState::Waiting: return fmt::format("Waiting ({})", GetThreadWaitReason(thread)); case Kernel::ThreadState::Runnable: return "Runnable"; case Kernel::ThreadState::Terminated: return "Terminated"; default: return "Unknown"; } } Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process, Kernel::KProcessAddress base) { Kernel::KMemoryInfo mem_info; Kernel::Svc::MemoryInfo svc_mem_info; Kernel::Svc::PageInfo page_info; VAddr cur_addr{GetInteger(base)}; auto& page_table = process->GetPageTable(); // Expect: r-x Code (.text) R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); svc_mem_info = mem_info.GetSvcMemoryInfo(); cur_addr = svc_mem_info.base_address + svc_mem_info.size; if (svc_mem_info.state != Kernel::Svc::MemoryState::Code || svc_mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) { return cur_addr - 1; } // Expect: r-- Code (.rodata) R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); svc_mem_info = mem_info.GetSvcMemoryInfo(); cur_addr = svc_mem_info.base_address + svc_mem_info.size; if (svc_mem_info.state != Kernel::Svc::MemoryState::Code || svc_mem_info.permission != Kernel::Svc::MemoryPermission::Read) { return cur_addr - 1; } // Expect: rw- CodeData (.data) R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); svc_mem_info = mem_info.GetSvcMemoryInfo(); cur_addr = svc_mem_info.base_address + svc_mem_info.size; return cur_addr - 1; } Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) { Loader::AppLoader::Modules modules; auto& page_table = process->GetPageTable(); auto& memory = process->GetMemory(); VAddr cur_addr = 0; // Look for executable sections in Code or AliasCode regions. while (true) { Kernel::KMemoryInfo mem_info{}; Kernel::Svc::PageInfo page_info{}; R_ASSERT( page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); auto svc_mem_info = mem_info.GetSvcMemoryInfo(); if (svc_mem_info.permission == Kernel::Svc::MemoryPermission::ReadExecute && (svc_mem_info.state == Kernel::Svc::MemoryState::Code || svc_mem_info.state == Kernel::Svc::MemoryState::AliasCode)) { // Try to read the module name from its path. constexpr s32 PathLengthMax = 0x200; struct { u32 zero; s32 path_length; std::array path; } module_path; if (memory.ReadBlock(svc_mem_info.base_address + svc_mem_info.size, &module_path, sizeof(module_path))) { if (module_path.zero == 0 && module_path.path_length > 0) { // Truncate module name. module_path.path[PathLengthMax - 1] = '\0'; // Ignore leading directories. char* path_pointer = module_path.path.data(); for (s32 i = 0; i < std::min(PathLengthMax, module_path.path_length) && module_path.path[i] != '\0'; i++) { if (module_path.path[i] == '/' || module_path.path[i] == '\\') { path_pointer = module_path.path.data() + i + 1; } } // Insert output. modules.emplace(svc_mem_info.base_address, path_pointer); } } } // Check if we're done. const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size; if (next_address <= cur_addr) { break; } cur_addr = next_address; } return modules; } Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process) { // Do we have any loaded executable sections? auto modules = FindModules(process); if (modules.size() >= 2) { // If we have two or more, the first one is rtld and the second is main. return std::next(modules.begin())->first; } else if (!modules.empty()) { // If we only have one, this is the main module. return modules.begin()->first; } // As a last resort, use the start of the code region. return GetInteger(process->GetPageTable().GetCodeRegionStart()); } void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 address, u64 size) { for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { auto* interface = process->GetArmInterface(i); if (interface) { interface->InvalidateCacheRange(address, size); } } } std::vector GetBacktraceFromContext(const Kernel::KProcess* process, const Kernel::Svc::ThreadContext& ctx) { if (process->Is64Bit()) { return GetAArch64Backtrace(process, ctx); } else { return GetAArch32Backtrace(process, ctx); } } std::vector GetBacktrace(const Kernel::KThread* thread) { Kernel::Svc::ThreadContext ctx = thread->GetContext(); return GetBacktraceFromContext(thread->GetOwnerProcess(), ctx); } } // namespace Core