Linux: Resolve backtrace symbols directly from .symtab instead of .dynsym (#385)

This commit is contained in:
goeiecool9999 2022-10-20 13:12:16 +02:00 committed by GitHub
parent 271a4e4719
commit 9df1325d14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 182 additions and 16 deletions

View File

@ -35,10 +35,6 @@ elseif(UNIX)
add_compile_options(-Wno-ambiguous-reversed-operator)
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)
endif()

View File

@ -43,6 +43,13 @@ PRIVATE
)
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
# have to link to CemuCommon
target_precompile_headers(CemuCommon PUBLIC precompiled.h)

View 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 {};
}

View 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;
};

View File

@ -2,41 +2,60 @@
#include <execinfo.h>
#include <string.h>
#include <string>
#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)
{
ELFSymbolTable symTable;
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 parenthesesClose = traceLine.find_last_of(')');
size_t offsetPlus = traceLine.find_last_of('+');
if (!parenthesesOpen || !parenthesesClose || !offsetPlus ||
offsetPlus < parenthesesOpen || offsetPlus > parenthesesClose)
{
// something unexpected was read. fall back to default string
// fall back to default string
std::cerr << traceLine << std::endl;
continue;
}
std::string symbolName = traceLine.substr(parenthesesOpen+1,offsetPlus-parenthesesOpen-1);
int status = -1;
char* demangled = abi::__cxa_demangle(symbolName.c_str(), nullptr, nullptr, &status);
if (demangled)
// attempt to resolve symbol from regular symbol table if missing from dynamic symbol table
uint64 newOffset = -1;
std::string_view symbolName = traceLine.substr(parenthesesOpen+1, offsetPlus-parenthesesOpen-1);
if (symbolName.empty())
{
std::cerr << traceLine.substr(0, parenthesesOpen+1);
std::cerr << demangled;
std::cerr << traceLine.substr(offsetPlus) << std::endl;
free(demangled);
uint64 symbolOffset = StringHelpers::ToInt64(traceLine.substr(offsetPlus+1,offsetPlus+1-parenthesesClose-1));
symbolName = symTable.OffsetToSymbol(symbolOffset, newOffset);
}
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
{
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
void handlerDumpingSignal(int sig)
@ -61,6 +80,7 @@ void handlerDumpingSignal(int sig)
// print out all the frames to stderr
fprintf(stderr, "Error: signal %d:\n", sig);
#if BOOST_OS_LINUX
char** symbol_trace = backtrace_symbols(array, size);
if (symbol_trace)
@ -72,6 +92,9 @@ void handlerDumpingSignal(int sig)
{
std::cerr << "Failed to read backtrace" << std::endl;
}
#else
backtrace_symbols_fd(array, size, STDERR_FILENO);
#endif
if (GetConfig().crash_dump == CrashDump::Enabled)
{