From 3c6d965e1aa482d459bb248c91e93bfc5d431b6f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 10 Mar 2022 18:15:25 -0800 Subject: [PATCH] symbols: use same strategy on windows as on macOS --- libraries/config/templates/stratosphere.mk | 2 +- .../diag/impl/diag_symbol_impl.os.generic.cpp | 49 +-- .../diag/impl/diag_symbol_impl.os.windows.cpp | 291 ++++++++++++++++++ 3 files changed, 295 insertions(+), 47 deletions(-) create mode 100644 libraries/libstratosphere/source/diag/impl/diag_symbol_impl.os.windows.cpp diff --git a/libraries/config/templates/stratosphere.mk b/libraries/config/templates/stratosphere.mk index 1d62d0750..cde981742 100644 --- a/libraries/config/templates/stratosphere.mk +++ b/libraries/config/templates/stratosphere.mk @@ -80,7 +80,7 @@ endif ifeq ($(ATMOSPHERE_BOARD),nx-hac-001) export LIBS := -lstratosphere -lnx else ifeq ($(ATMOSPHERE_BOARD),generic_windows) -export LIBS := -lstratosphere -lwinmm -lws2_32 -lbcrypt -lbfd -liberty -lintl -lz +export LIBS := -lstratosphere -lwinmm -lws2_32 -lbcrypt else ifeq ($(ATMOSPHERE_BOARD),generic_linux) export LIBS := -lstratosphere -pthread -lbfd else ifeq ($(ATMOSPHERE_BOARD),generic_macos) diff --git a/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.os.generic.cpp b/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.os.generic.cpp index aa6144c54..9a351ae47 100644 --- a/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.os.generic.cpp +++ b/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.os.generic.cpp @@ -19,15 +19,8 @@ #define PACKAGE "stratosphere" #define PACKAGE_VERSION STRINGIFY(ATMOSPHERE_RELEASE_VERSION_MAJOR.ATMOSPHERE_RELEASE_VERSION_MINOR.ATMOSPHERE_RELEASE_VERSION_MICRO) -/* msys2 mingw64 puts headers inside binutils/ */ -#if defined(ATMOSPHERE_OS_WINDOWS) -#include -#include -#else -#include -#endif - #if defined(ATMOSPHERE_OS_LINUX) +#include #include #include #include @@ -119,15 +112,7 @@ namespace ams::diag::impl { } /* Get our module base/size. */ - #if defined(ATMOSPHERE_OS_WINDOWS) - { - MODULEINFO module_info; - if (::GetModuleInformation(::GetCurrentProcess(), ::GetModuleHandleA(nullptr), std::addressof(module_info), sizeof(module_info))) { - m_module_address = reinterpret_cast(module_info.lpBaseOfDll); - m_module_size = static_cast(module_info.SizeOfImage); - } - } - #elif defined(ATMOSPHERE_OS_LINUX) + #if defined(ATMOSPHERE_OS_LINUX) { m_module_address = _r_debug.r_map->l_addr; @@ -166,18 +151,6 @@ namespace ams::diag::impl { if (m_module_address <= address && address < m_module_address + m_module_size) { displacement = m_module_address; - - #if defined(ATMOSPHERE_OS_WINDOWS) - { - #if defined(__MINGW64__) - displacement -= UINT64_C(0x140000000); - #elif defined(__MINGW32__) - displacement -= UINT64_C(0x400000); - #else - #error "Unknown build context for windows module base!" - #endif - } - #endif } return displacement; @@ -242,23 +215,7 @@ namespace ams::diag::impl { } private: static void GetExecutablePath(char *dst, size_t dst_size) { - #if defined(ATMOSPHERE_OS_WINDOWS) - { - /* Get the module file name. */ - wchar_t module_file_name[0x1000]; - if (::GetModuleFileNameW(0, module_file_name, util::size(module_file_name)) == 0) { - dst[0] = 0; - return; - } - - /* Convert to utf-8. */ - const auto res = ::WideCharToMultiByte(CP_UTF8, 0, module_file_name, -1, dst, dst_size, nullptr, nullptr); - if (res == 0) { - dst[0] = 0; - return; - } - } - #elif defined(ATMOSPHERE_OS_LINUX) + #if defined(ATMOSPHERE_OS_LINUX) { if (::readlink("/proc/self/exe", dst, dst_size) == -1) { dst[0] = 0; diff --git a/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.os.windows.cpp b/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.os.windows.cpp new file mode 100644 index 000000000..15d9ea6ae --- /dev/null +++ b/libraries/libstratosphere/source/diag/impl/diag_symbol_impl.os.windows.cpp @@ -0,0 +1,291 @@ +/* + * 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 +#include "diag_symbol_impl.hpp" + +#include +extern "C" { + + void __module_offset_helper() { /* ... */ } + +} + +namespace ams::diag::impl { + + namespace { + + class CurrentExecutableHelper { + private: + struct SymbolInfo { + uintptr_t address; + const char *name; + }; + private: + os::NativeHandle m_file_handle; + os::NativeHandle m_map_handle; + void *m_file_map; + size_t m_file_size; + SymbolInfo *m_symbols; + size_t m_num_symbol; + uintptr_t m_module_address; + size_t m_module_size; + uintptr_t m_module_displacement; + private: + CurrentExecutableHelper() : m_file_handle(INVALID_HANDLE_VALUE), m_map_handle(INVALID_HANDLE_VALUE), m_file_map(nullptr), m_file_size(0), m_symbols(nullptr), m_num_symbol(0), m_module_address(0), m_module_size(0), m_module_displacement(0) { + /* Open the current executable. */ + auto exe_handle = OpenExecutableFile(); + if (exe_handle == INVALID_HANDLE_VALUE) { + return; + } + ON_SCOPE_EXIT { if (exe_handle != INVALID_HANDLE_VALUE) { ::CloseHandle(exe_handle); } }; + + /* Get the exe size. */ + LARGE_INTEGER exe_size_li; + if (::GetFileSizeEx(exe_handle, std::addressof(exe_size_li)) == 0) { + return; + } + + /* Check the exe size. */ + s64 exe_size = exe_size_li.QuadPart; + if (exe_size == 0) { + return; + } + + /* Create a file mapping. */ + auto map_handle = ::CreateFileMappingW(exe_handle, nullptr, PAGE_READONLY, 0, 0, nullptr); + if (map_handle == INVALID_HANDLE_VALUE) { + return; + } + ON_SCOPE_EXIT { if (map_handle != INVALID_HANDLE_VALUE) { ::CloseHandle(map_handle); } }; + + /* Map the file. */ + void *exe_map = ::MapViewOfFile(map_handle, FILE_MAP_READ, 0, 0, 0); + if (exe_map == nullptr) { + return; + } + ON_SCOPE_EXIT { if (exe_map != nullptr) { ::UnmapViewOfFile(exe_map); } }; + + /* Get/parse the DOS header. */ + const uintptr_t exe_start = reinterpret_cast(exe_map); + const auto *dos_header = reinterpret_cast(exe_start); + if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) { + return; + } + + /* Set up the nt headers. */ + const auto *nt32 = reinterpret_cast(exe_start + dos_header->e_lfanew); + const auto *nt64 = reinterpret_cast(exe_start + dos_header->e_lfanew); + if (nt32->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 || nt32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64) { + nt32 = nullptr; + } else { + nt64 = nullptr; + } + + #define EXE_NT_HEADER(x) ((nt64 != nullptr) ? nt64->x : nt32->x) + + if (EXE_NT_HEADER(Signature) != IMAGE_NT_SIGNATURE) { + return; + } + + /* Check that the optional header is really present. */ + if (EXE_NT_HEADER(FileHeader.SizeOfOptionalHeader) < sizeof(IMAGE_OPTIONAL_HEADER32)) { + return; + } + + /* Get sections. */ + const auto *sec = nt64 != nullptr ? IMAGE_FIRST_SECTION(nt64) : IMAGE_FIRST_SECTION(nt32); + + /* Get the symbol table. */ + const auto *symtab = reinterpret_cast(exe_start + EXE_NT_HEADER(FileHeader.PointerToSymbolTable)); + const size_t nsym = EXE_NT_HEADER(FileHeader.NumberOfSymbols); + const char *strtab = reinterpret_cast(symtab + nsym); + + /* Find the section containing our code. */ + int text_section = -1; + for (size_t i = 0; i < nsym; ++i) { + if (ISFCN(symtab[i].Type)) { + const char *name = symtab[i].N.Name.Short == 0 ? strtab + symtab[i].N.Name.Long : reinterpret_cast(symtab[i].N.ShortName); + if (std::strcmp(name, "__module_offset_helper") == 0) { + AMS_ASSERT(text_section == -1); + AMS_ASSERT(m_module_displacement == 0); + + m_module_displacement = reinterpret_cast(&__module_offset_helper) - symtab[i].Value; + text_section = symtab[i].SectionNumber; + + AMS_ASSERT(m_module_displacement != 0); + AMS_ASSERT(text_section != -1); + break; + } + } + } + + /* Determine the number of functions. */ + size_t funcs = 0; + for (size_t i = 0; i < nsym; ++i) { + if (ISFCN(symtab[i].Type) && symtab[i].SectionNumber == text_section) { + ++funcs; + } + } + + /* Allocate functions. */ + m_symbols = reinterpret_cast(std::malloc(sizeof(SymbolInfo) * funcs)); + if (m_symbols == nullptr) { + return; + } + + /* Set all symbols. */ + m_num_symbol = 0; + for (size_t i = 0; i < nsym; ++i) { + if (ISFCN(symtab[i].Type) && symtab[i].SectionNumber == text_section) { + m_symbols[m_num_symbol].address = symtab[i].Value; + m_symbols[m_num_symbol].name = symtab[i].N.Name.Short == 0 ? strtab + symtab[i].N.Name.Long : reinterpret_cast(symtab[i].N.ShortName); + + ++m_num_symbol; + } + } + AMS_ASSERT(m_num_symbol == funcs); + + /* Sort the symbols. */ + std::sort(m_symbols + 0, m_symbols + m_num_symbol, [] (const SymbolInfo &lhs, const SymbolInfo &rhs) { + return lhs.address < rhs.address; + }); + + m_module_address = 0; + m_module_size = sec[text_section - 1].Misc.VirtualSize; + + if (m_module_displacement != 0 && m_module_size > 0 && m_num_symbol > 0) { + std::swap(m_file_handle, exe_handle); + std::swap(m_map_handle, map_handle); + std::swap(m_file_map, exe_map); + m_file_size = exe_size; + } + } + + ~CurrentExecutableHelper() { + if (m_file_map != nullptr) { + ::UnmapViewOfFile(m_file_map); + } + if (m_map_handle != nullptr) { + ::CloseHandle(m_map_handle); + } + if (m_file_handle != nullptr) { + ::CloseHandle(m_file_handle); + } + } + public: + static CurrentExecutableHelper &GetInstance() { + AMS_FUNCTION_LOCAL_STATIC(CurrentExecutableHelper, s_current_executable_helper_instance); + return s_current_executable_helper_instance; + } + private: + size_t GetSymbolSizeImpl(const SymbolInfo *symbol) const { + /* Do our best to guess. */ + if (symbol != m_symbols + m_num_symbol - 1) { + return (symbol + 1)->address - symbol->address; + } else if (m_module_address + m_module_size >= symbol->address) { + return m_module_address + m_module_size - symbol->address; + } else { + return 0; + } + } + + const SymbolInfo *GetBestSymbol(uintptr_t address) const { + address -= m_module_displacement; + + const SymbolInfo *best_symbol = std::lower_bound(m_symbols + 0, m_symbols + m_num_symbol, address, [](const SymbolInfo &lhs, uintptr_t rhs) { + return lhs.address < rhs; + }); + + if (best_symbol == m_symbols + m_num_symbol) { + return nullptr; + } + + if (best_symbol->address != address && best_symbol > m_symbols) { + --best_symbol; + } + + const auto vma = best_symbol->address; + const auto end = vma + this->GetSymbolSizeImpl(best_symbol); + + if (vma <= address && address < end) { + return best_symbol; + } else { + return nullptr; + } + } + public: + uintptr_t GetSymbolName(char *dst, size_t dst_size, uintptr_t address) const { + if (m_file_handle == INVALID_HANDLE_VALUE) { + return 0; + } + + /* Get the symbol. */ + const auto *symbol = this->GetBestSymbol(address); + if (symbol == nullptr) { + return 0; + } + + /* Print the symbol. */ + const char *name = symbol->name; + + int cpp_name_status = 0; + if (char *demangled = abi::__cxa_demangle(name, nullptr, 0, std::addressof(cpp_name_status)); cpp_name_status == 0) { + AMS_ASSERT(demangled != nullptr); + util::TSNPrintf(dst, dst_size, "%s", demangled); + std::free(demangled); + } else { + util::TSNPrintf(dst, dst_size, "%s", name); + } + + return symbol->address + m_module_displacement; + } + + size_t GetSymbolSize(uintptr_t address) const { + if (m_file_handle == INVALID_HANDLE_VALUE) { + return 0; + } + + /* Get the symbol. */ + const auto *symbol = this->GetBestSymbol(address); + if (symbol == nullptr) { + return 0; + } + + return this->GetSymbolSizeImpl(symbol); + } + private: + static os::NativeHandle OpenExecutableFile() { + /* Get the module file name. */ + wchar_t module_file_name[0x1000]; + if (::GetModuleFileNameW(0, module_file_name, util::size(module_file_name)) == 0) { + return INVALID_HANDLE_VALUE; + } + + return ::CreateFileW(module_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + } + }; + + } + + uintptr_t GetSymbolNameImpl(char *dst, size_t dst_size, uintptr_t address) { + return CurrentExecutableHelper::GetInstance().GetSymbolName(dst, dst_size, address); + } + + size_t GetSymbolSizeImpl(uintptr_t address) { + return CurrentExecutableHelper::GetInstance().GetSymbolSize(address); + } + +}