/* * 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 namespace ams::diag::impl { namespace { uintptr_t GetAddressValue(uintptr_t address) { if (address == 0) { return 0; } if (!util::IsAligned(address, alignof(uintptr_t))) { return 0; } return *reinterpret_cast(address); } template svc::MemoryInfo *GetMemoryInfoPointer(T (&arr)[N]) { /* Check that there's enough space. */ static_assert(sizeof(T) * N <= sizeof(svc::MemoryInfo)); static_assert(alignof(T) >= alignof(svc::MemoryInfo)); return reinterpret_cast(std::addressof(arr[0])); } bool IsValidLinkRegisterValue(uintptr_t lr, svc::MemoryInfo *info) { /* Ensure the memory info is valid. */ Result query_res; if (!(info->base_address <= lr && lr < info->base_address + info->size) && ({ svc::PageInfo page_info; query_res = svc::QueryMemory(info, std::addressof(page_info), lr); R_FAILED(query_res); })) { AMS_SDK_LOG("Failed to get backtrace. Query memory failed. (lr: %p, result: %03d-%04d)\n", reinterpret_cast(lr), query_res.GetModule(), query_res.GetDescription()); return false; } /* Check that lr is valid. */ if (lr == 0) { return false; } if (!util::IsAligned(lr, sizeof(u32))) { AMS_SDK_LOG("Failed to get backtrace. The link register alignment is invalid. (lr: %p)\n", reinterpret_cast(lr)); return false; } /* Check that the lr points to code. */ if (info->permission != svc::MemoryPermission_ReadExecute) { AMS_SDK_LOG("Failed to get backtrace. The link register points out of the code. (lr: %p)\n", reinterpret_cast(lr)); return false; } return true; } void GetNormalStackInfo(Backtrace::StackInfo *out) { if (void * const fiber = nullptr /* TODO: os::GetCurrentFiber() */; fiber == nullptr) { /* Get thread. */ auto * const thread = os::GetCurrentThread(); out->stack_top = reinterpret_cast(thread->stack); out->stack_bottom = reinterpret_cast(thread->stack) + thread->stack_size; } else { /* TODO: Fiber. */ } } bool GetExceptionStackInfo(Backtrace::StackInfo *out, uintptr_t sp) { /* Get the current stack info. */ uintptr_t cur_stack = 0; size_t cur_stack_size = 0; os::GetCurrentStackInfo(std::addressof(cur_stack), std::addressof(cur_stack_size)); /* Get the thread's stack info. */ uintptr_t thread_stack = 0; size_t thread_stack_size = 0; os::GetThreadStackInfo(std::addressof(thread_stack), std::addressof(thread_stack_size), os::GetCurrentThread()); /* If the current stack is the thread stack, exception stack isn't being used. */ if (cur_stack == thread_stack) { AMS_ASSERT(cur_stack_size == thread_stack_size); return false; } /* Check if the stack pointer is contained in the current stack. */ if (!(cur_stack <= sp && sp < cur_stack + cur_stack_size)) { return false; } /* Set the output. */ out->stack_top = cur_stack; out->stack_bottom = cur_stack + cur_stack_size; return true; } } NOINLINE void Backtrace::Initialize() { /* Get the stack pointer/frame pointer. */ uintptr_t fp, sp; __asm__ __volatile__( #if defined(ATMOSPHERE_ARCH_ARM64) "mov %[fp], fp\n" "mov %[sp], sp\n" #elif defined(ATMOSPHERE_ARCH_ARM) "mov %[fp], x29\n" "mov %[sp], sp\n" #else #error "Unknown architecture for Horizon fp/sp retrieval." #endif : [fp]"=&r"(fp), [sp]"=&r"(sp) : : "memory" ); /* Set our stack info. */ this->SetStackInfo(fp, sp); /* Step, to get our first lr. */ this->Step(); } void Backtrace::Initialize(uintptr_t fp, uintptr_t sp, uintptr_t pc) { /* Set our initial lr. */ m_lr = pc; /* Set our stack info. */ this->SetStackInfo(fp, sp); } bool Backtrace::Step() { /* We can't step without a frame pointer. */ if (m_fp == 0) { if (m_current_stack_info != std::addressof(m_normal_stack_info)) { AMS_SDK_LOG("Failed to get backtrace. The frame pointer is null.\n"); } return false; } /* The frame pointer needs to be aligned. */ if (!util::IsAligned(m_fp, sizeof(uintptr_t))) { AMS_SDK_LOG("Failed to get backtrace. The frame pointer alignment is invalid. (fp: %p)\n", reinterpret_cast(m_fp)); return false; } /* Ensure our current stack info is good. */ if (!(m_current_stack_info->stack_top <= m_fp && m_fp < m_current_stack_info->stack_bottom)) { if (m_current_stack_info != std::addressof(m_exception_stack_info) || !(m_normal_stack_info.stack_top <= m_fp && m_fp < m_normal_stack_info.stack_bottom)) { AMS_SDK_LOG("Failed to get backtrace. The frame pointer points out of the stack. (fp: %p, stack: %p-%p)\n", reinterpret_cast(m_fp), reinterpret_cast(m_current_stack_info->stack_top), reinterpret_cast(m_current_stack_info->stack_bottom)); return false; } m_current_stack_info = std::addressof(m_normal_stack_info); } else if (m_fp <= m_prev_fp) { AMS_SDK_LOG("Failed to get backtrace. The frame pointer is rewinding. (fp: %p, prev fp: %p, stack: %p-%p)\n", reinterpret_cast(m_fp), reinterpret_cast(m_prev_fp), reinterpret_cast(m_current_stack_info->stack_top), reinterpret_cast(m_current_stack_info->stack_bottom)); return false; } /* Update our previous fp. */ m_prev_fp = m_fp; /* Read lr/fp. */ m_lr = GetAddressValue(m_fp + sizeof(m_fp)); m_fp = GetAddressValue(m_fp); /* Check that lr is valid. */ if (IsValidLinkRegisterValue(m_lr, GetMemoryInfoPointer(m_memory_info_buffer))) { return true; } else { m_lr = 0; return false; } } uintptr_t Backtrace::GetStackPointer() const { if (m_fp != 0) { return m_fp - sizeof(m_fp); } else { return m_current_stack_info->stack_bottom - sizeof(m_fp); } } uintptr_t Backtrace::GetReturnAddress() const { return m_lr; } void Backtrace::SetStackInfo(uintptr_t fp, uintptr_t sp) { /* Get the normal stack info. */ GetNormalStackInfo(std::addressof(m_normal_stack_info)); /* Get the exception stack info. */ if (GetExceptionStackInfo(std::addressof(m_exception_stack_info), sp)) { m_current_stack_info = std::addressof(m_exception_stack_info); } else { m_current_stack_info = std::addressof(m_normal_stack_info); } /* Set our frame pointer. */ m_fp = fp; } }