| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- // 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<std::string> 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<std::string> 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<u64, 2> SegmentBases{
- 0x60000000ULL,
- 0x7100000000ULL,
- };
- void SymbolicateBacktrace(Kernel::KProcess* process, std::vector<BacktraceEntry>& out) {
- auto modules = FindModules(process);
- const bool is_64 = process->Is64Bit();
- std::map<std::string, Symbols::Symbols> 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<BacktraceEntry> GetAArch64Backtrace(Kernel::KProcess* process,
- const Kernel::Svc::ThreadContext& ctx) {
- std::vector<BacktraceEntry> 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<BacktraceEntry> GetAArch32Backtrace(Kernel::KProcess* process,
- const Kernel::Svc::ThreadContext& ctx) {
- std::vector<BacktraceEntry> 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<std::string> GetThreadName(const Kernel::KThread* thread) {
- 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(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<char, PathLengthMax> 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();
- char* path_end =
- path_pointer + std::min(PathLengthMax, module_path.path_length);
- 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,
- std::string_view(path_pointer, path_end));
- }
- }
- }
- // 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(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<BacktraceEntry> GetBacktraceFromContext(Kernel::KProcess* process,
- const Kernel::Svc::ThreadContext& ctx) {
- if (process->Is64Bit()) {
- return GetAArch64Backtrace(process, ctx);
- } else {
- return GetAArch32Backtrace(process, ctx);
- }
- }
- std::vector<BacktraceEntry> GetBacktrace(const Kernel::KThread* thread) {
- Kernel::Svc::ThreadContext ctx = thread->GetContext();
- return GetBacktraceFromContext(thread->GetOwnerProcess(), ctx);
- }
- } // namespace Core
|