mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-11-22 17:19:18 +01:00
Linux: Resolve backtrace symbols directly from .symtab instead of .dynsym (#385)
This commit is contained in:
parent
271a4e4719
commit
9df1325d14
@ -35,10 +35,6 @@ elseif(UNIX)
|
|||||||
add_compile_options(-Wno-ambiguous-reversed-operator)
|
add_compile_options(-Wno-ambiguous-reversed-operator)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT APPLE)
|
|
||||||
add_link_options(-rdynamic)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_compile_options(-Wno-multichar -Wno-invalid-offsetof -Wno-switch -Wno-ignored-attributes -Wno-deprecated-enum-enum-conversion)
|
add_compile_options(-Wno-multichar -Wno-invalid-offsetof -Wno-switch -Wno-ignored-attributes -Wno-deprecated-enum-enum-conversion)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -43,6 +43,13 @@ PRIVATE
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
target_sources(CemuCommon PRIVATE
|
||||||
|
ExceptionHandler/ELFSymbolTable.cpp
|
||||||
|
ExceptionHandler/ELFSymbolTable.h
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
# All the targets wanting to use the precompiled.h header
|
# All the targets wanting to use the precompiled.h header
|
||||||
# have to link to CemuCommon
|
# have to link to CemuCommon
|
||||||
target_precompile_headers(CemuCommon PUBLIC precompiled.h)
|
target_precompile_headers(CemuCommon PUBLIC precompiled.h)
|
||||||
|
108
src/Common/ExceptionHandler/ELFSymbolTable.cpp
Normal file
108
src/Common/ExceptionHandler/ELFSymbolTable.cpp
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#include "Common/ExceptionHandler/ELFSymbolTable.h"
|
||||||
|
#include "Common/FileStream.h"
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
uint16 ELFSymbolTable::FindSection(int type, const std::string_view& name)
|
||||||
|
{
|
||||||
|
if (!shTable || !shStrTable)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (uint16 i = 0; i < header->e_shnum; ++i)
|
||||||
|
{
|
||||||
|
auto& entry = shTable[i];
|
||||||
|
if(entry.sh_type == type && std::string_view{&shStrTable[entry.sh_name]} == name)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ELFSymbolTable::SectionPointer(uint16 index)
|
||||||
|
{
|
||||||
|
return SectionPointer(shTable[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ELFSymbolTable::SectionPointer(const Elf64_Shdr& section)
|
||||||
|
{
|
||||||
|
return (void*)(mappedExecutable + section.sh_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
ELFSymbolTable::ELFSymbolTable()
|
||||||
|
{
|
||||||
|
// create file handle
|
||||||
|
int fd = open("/proc/self/exe", O_RDONLY);
|
||||||
|
if (!fd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// retrieve file size.
|
||||||
|
struct stat filestats;
|
||||||
|
if (fstat(fd, &filestats))
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mappedExecutableSize = filestats.st_size;
|
||||||
|
|
||||||
|
// attempt to map the file
|
||||||
|
mappedExecutable = (uint8*)(mmap(nullptr, mappedExecutableSize, PROT_READ, MAP_PRIVATE, fd, 0));
|
||||||
|
close(fd);
|
||||||
|
if (!mappedExecutable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// verify signature
|
||||||
|
header = (Elf64_Ehdr*)(mappedExecutable);
|
||||||
|
constexpr uint8 signature[] = {0x7f, 0x45, 0x4c, 0x46};
|
||||||
|
for (size_t i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
if (signature[i] != header->e_ident[i])
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shTable = (Elf64_Shdr*)(mappedExecutable + header->e_shoff);
|
||||||
|
|
||||||
|
Elf64_Shdr& shStrn = shTable[header->e_shstrndx];
|
||||||
|
shStrTable = (char*)(mappedExecutable + shStrn.sh_offset);
|
||||||
|
|
||||||
|
strTable = (char*)SectionPointer(FindSection(SHT_STRTAB, ".strtab"));
|
||||||
|
|
||||||
|
Elf64_Shdr& symTabShdr = shTable[FindSection(SHT_SYMTAB, ".symtab")];
|
||||||
|
if (symTabShdr.sh_entsize == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
symTableLen = symTabShdr.sh_size / symTabShdr.sh_entsize;
|
||||||
|
symTable = (Elf64_Sym*)(SectionPointer(symTabShdr));
|
||||||
|
}
|
||||||
|
|
||||||
|
ELFSymbolTable::~ELFSymbolTable()
|
||||||
|
{
|
||||||
|
if (mappedExecutable)
|
||||||
|
munmap(mappedExecutable, mappedExecutableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view ELFSymbolTable::OffsetToSymbol(uint64 ptr, uint64& fromStart) const
|
||||||
|
{
|
||||||
|
if(!symTable || !strTable)
|
||||||
|
{
|
||||||
|
fromStart = -1;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto entry = symTable+1; entry < symTable+symTableLen; ++entry)
|
||||||
|
{
|
||||||
|
if (ELF64_ST_TYPE(entry->st_info) != STT_FUNC)
|
||||||
|
continue;
|
||||||
|
auto begin = entry->st_value;
|
||||||
|
auto size = entry->st_size;
|
||||||
|
if(ptr >= begin && ptr < begin+size)
|
||||||
|
{
|
||||||
|
fromStart = ptr-begin;
|
||||||
|
return &strTable[entry->st_name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fromStart = -1;
|
||||||
|
return {};
|
||||||
|
}
|
32
src/Common/ExceptionHandler/ELFSymbolTable.h
Normal file
32
src/Common/ExceptionHandler/ELFSymbolTable.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <memory>
|
||||||
|
#include <elf.h>
|
||||||
|
|
||||||
|
class ELFSymbolTable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string_view OffsetToSymbol(uint64 ptr, uint64& fromStart) const;
|
||||||
|
|
||||||
|
ELFSymbolTable();
|
||||||
|
~ELFSymbolTable();
|
||||||
|
private:
|
||||||
|
uint8* mappedExecutable = nullptr;
|
||||||
|
size_t mappedExecutableSize = 0;
|
||||||
|
|
||||||
|
Elf64_Ehdr* header = nullptr;
|
||||||
|
|
||||||
|
Elf64_Shdr* shTable = nullptr;
|
||||||
|
char* shStrTable = nullptr;
|
||||||
|
|
||||||
|
Elf64_Sym* symTable = nullptr;
|
||||||
|
uint64 symTableLen = 0;
|
||||||
|
char* strTable = nullptr;
|
||||||
|
|
||||||
|
uint16 FindSection(int type, const std::string_view& name);
|
||||||
|
|
||||||
|
void* SectionPointer (uint16 index);
|
||||||
|
void* SectionPointer(const Elf64_Shdr& section);
|
||||||
|
|
||||||
|
// ownership of mapped memory, cannot copy.
|
||||||
|
ELFSymbolTable(const ELFSymbolTable&) = delete;
|
||||||
|
};
|
@ -2,41 +2,60 @@
|
|||||||
#include <execinfo.h>
|
#include <execinfo.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "config/CemuConfig.h"
|
#include "config/CemuConfig.h"
|
||||||
|
#include "util/helpers/StringHelpers.h"
|
||||||
|
|
||||||
|
#if BOOST_OS_LINUX
|
||||||
|
#include "ELFSymbolTable.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BOOST_OS_LINUX
|
||||||
void demangleAndPrintBacktrace(char** backtrace, size_t size)
|
void demangleAndPrintBacktrace(char** backtrace, size_t size)
|
||||||
{
|
{
|
||||||
|
ELFSymbolTable symTable;
|
||||||
for (char** i = backtrace; i < backtrace + size; i++)
|
for (char** i = backtrace; i < backtrace + size; i++)
|
||||||
{
|
{
|
||||||
std::string traceLine{*i};
|
std::string_view traceLine{*i};
|
||||||
|
|
||||||
|
// basic check to see if the backtrace line matches expected format
|
||||||
size_t parenthesesOpen = traceLine.find_last_of('(');
|
size_t parenthesesOpen = traceLine.find_last_of('(');
|
||||||
size_t parenthesesClose = traceLine.find_last_of(')');
|
size_t parenthesesClose = traceLine.find_last_of(')');
|
||||||
size_t offsetPlus = traceLine.find_last_of('+');
|
size_t offsetPlus = traceLine.find_last_of('+');
|
||||||
if (!parenthesesOpen || !parenthesesClose || !offsetPlus ||
|
if (!parenthesesOpen || !parenthesesClose || !offsetPlus ||
|
||||||
offsetPlus < parenthesesOpen || offsetPlus > parenthesesClose)
|
offsetPlus < parenthesesOpen || offsetPlus > parenthesesClose)
|
||||||
{
|
{
|
||||||
// something unexpected was read. fall back to default string
|
// fall back to default string
|
||||||
std::cerr << traceLine << std::endl;
|
std::cerr << traceLine << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string symbolName = traceLine.substr(parenthesesOpen+1,offsetPlus-parenthesesOpen-1);
|
// attempt to resolve symbol from regular symbol table if missing from dynamic symbol table
|
||||||
int status = -1;
|
uint64 newOffset = -1;
|
||||||
char* demangled = abi::__cxa_demangle(symbolName.c_str(), nullptr, nullptr, &status);
|
std::string_view symbolName = traceLine.substr(parenthesesOpen+1, offsetPlus-parenthesesOpen-1);
|
||||||
if (demangled)
|
if (symbolName.empty())
|
||||||
{
|
{
|
||||||
std::cerr << traceLine.substr(0, parenthesesOpen+1);
|
uint64 symbolOffset = StringHelpers::ToInt64(traceLine.substr(offsetPlus+1,offsetPlus+1-parenthesesClose-1));
|
||||||
std::cerr << demangled;
|
symbolName = symTable.OffsetToSymbol(symbolOffset, newOffset);
|
||||||
std::cerr << traceLine.substr(offsetPlus) << std::endl;
|
}
|
||||||
free(demangled);
|
|
||||||
|
std::cerr << traceLine.substr(0, parenthesesOpen+1);
|
||||||
|
|
||||||
|
std::cerr << boost::core::demangle(symbolName.empty() ? "" : symbolName.data());
|
||||||
|
|
||||||
|
// print relative or existing symbol offset.
|
||||||
|
std::cerr << '+';
|
||||||
|
if (newOffset != -1)
|
||||||
|
{
|
||||||
|
std::cerr << std::hex << newOffset;
|
||||||
|
std::cerr << traceLine.substr(parenthesesClose) << std::endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::cerr << traceLine << std::endl;
|
std::cerr << traceLine.substr(offsetPlus+1) << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// handle signals that would dump core, print stacktrace and then dump depending on config
|
// handle signals that would dump core, print stacktrace and then dump depending on config
|
||||||
void handlerDumpingSignal(int sig)
|
void handlerDumpingSignal(int sig)
|
||||||
@ -61,6 +80,7 @@ void handlerDumpingSignal(int sig)
|
|||||||
// print out all the frames to stderr
|
// print out all the frames to stderr
|
||||||
fprintf(stderr, "Error: signal %d:\n", sig);
|
fprintf(stderr, "Error: signal %d:\n", sig);
|
||||||
|
|
||||||
|
#if BOOST_OS_LINUX
|
||||||
char** symbol_trace = backtrace_symbols(array, size);
|
char** symbol_trace = backtrace_symbols(array, size);
|
||||||
|
|
||||||
if (symbol_trace)
|
if (symbol_trace)
|
||||||
@ -72,6 +92,9 @@ void handlerDumpingSignal(int sig)
|
|||||||
{
|
{
|
||||||
std::cerr << "Failed to read backtrace" << std::endl;
|
std::cerr << "Failed to read backtrace" << std::endl;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
backtrace_symbols_fd(array, size, STDERR_FILENO);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (GetConfig().crash_dump == CrashDump::Enabled)
|
if (GetConfig().crash_dump == CrashDump::Enabled)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user