| | |
| | |
| |
|
| | #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) { |
| | |
| | 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) { |
| | |
| | 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) { |
| | |
| | return std::nullopt; |
| | } |
| |
|
| | return memory.ReadCString(name_pointer, 256); |
| | } |
| |
|
| | std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory, |
| | const Kernel::KThread& thread) { |
| | |
| | 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) { |
| | |
| | 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) { |
| | |
| | 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, ""}); |
| |
|
| | |
| | |
| | |
| | |
| | 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, ""}); |
| |
|
| | |
| | |
| | |
| | |
| | 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; |
| | } |
| |
|
| | } |
| |
|
| | 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(); |
| |
|
| | |
| | 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; |
| | } |
| |
|
| | |
| | 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; |
| | } |
| |
|
| | |
| | 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; |
| |
|
| | |
| | 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)) { |
| | |
| | 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) { |
| | |
| | module_path.path[PathLengthMax - 1] = '\0'; |
| |
|
| | |
| | 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; |
| | } |
| | } |
| |
|
| | |
| | modules.emplace(svc_mem_info.base_address, |
| | std::string_view(path_pointer, path_end)); |
| | } |
| | } |
| | } |
| |
|
| | |
| | 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) { |
| | |
| | auto modules = FindModules(process); |
| |
|
| | if (modules.size() >= 2) { |
| | |
| | return std::next(modules.begin())->first; |
| | } else if (!modules.empty()) { |
| | |
| | return modules.begin()->first; |
| | } |
| |
|
| | |
| | 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); |
| | } |
| |
|
| | } |
| |
|