Merge pull request #8180 from liamwhite/symbols

core: extract symbol reading
This commit is contained in:
Fernando S 2022-04-11 18:40:34 +02:00 committed by GitHub
commit b86cfe159f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 231 additions and 129 deletions

View file

@ -13,6 +13,8 @@ add_library(core STATIC
arm/dynarmic/arm_exclusive_monitor.h arm/dynarmic/arm_exclusive_monitor.h
arm/exclusive_monitor.cpp arm/exclusive_monitor.cpp
arm/exclusive_monitor.h arm/exclusive_monitor.h
arm/symbols.cpp
arm/symbols.h
constants.cpp constants.cpp
constants.h constants.h
core.cpp core.cpp

View file

@ -8,134 +8,13 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/arm/symbols.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/memory.h" #include "core/memory.h"
namespace Core { namespace Core {
namespace {
constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
enum class ELFSymbolType : u8 {
None = 0,
Object = 1,
Function = 2,
Section = 3,
File = 4,
Common = 5,
TLS = 6,
};
enum class ELFSymbolBinding : u8 {
Local = 0,
Global = 1,
Weak = 2,
};
enum class ELFSymbolVisibility : u8 {
Default = 0,
Internal = 1,
Hidden = 2,
Protected = 3,
};
struct ELFSymbol {
u32 name_index;
union {
u8 info;
BitField<0, 4, ELFSymbolType> type;
BitField<4, 4, ELFSymbolBinding> binding;
};
ELFSymbolVisibility visibility;
u16 sh_index;
u64 value;
u64 size;
};
static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
Symbols GetSymbols(VAddr text_offset, Core::Memory::Memory& memory) {
const auto mod_offset = text_offset + memory.Read32(text_offset + 4);
if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
return {};
}
const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset;
VAddr string_table_offset{};
VAddr symbol_table_offset{};
u64 symbol_entry_size{};
VAddr dynamic_index = dynamic_offset;
while (true) {
const u64 tag = memory.Read64(dynamic_index);
const u64 value = memory.Read64(dynamic_index + 0x8);
dynamic_index += 0x10;
if (tag == ELF_DYNAMIC_TAG_NULL) {
break;
}
if (tag == ELF_DYNAMIC_TAG_STRTAB) {
string_table_offset = value;
} else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
symbol_table_offset = value;
} else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
symbol_entry_size = value;
}
}
if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
return {};
}
const auto string_table_address = text_offset + string_table_offset;
const auto symbol_table_address = text_offset + symbol_table_offset;
Symbols out;
VAddr symbol_index = symbol_table_address;
while (symbol_index < string_table_address) {
ELFSymbol symbol{};
memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
VAddr string_offset = string_table_address + symbol.name_index;
std::string name;
for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) {
name += static_cast<char>(c);
}
symbol_index += symbol_entry_size;
out.push_back({symbol, name});
}
return out;
}
std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) {
const auto iter =
std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) {
const auto& symbol = pair.first;
const auto end_address = symbol.value + symbol.size;
return func_address >= symbol.value && func_address < end_address;
});
if (iter == symbols.end()) {
return std::nullopt;
}
return iter->second;
}
} // Anonymous namespace
constexpr u64 SEGMENT_BASE = 0x7100000000ull; constexpr u64 SEGMENT_BASE = 0x7100000000ull;
@ -169,9 +48,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
return {}; return {};
} }
std::map<std::string, Symbols> symbols; std::map<std::string, Symbols::Symbols> symbols;
for (const auto& module : modules) { for (const auto& module : modules) {
symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); symbols.insert_or_assign(module.second,
Symbols::GetSymbols(module.first, system.Memory(),
system.CurrentProcess()->Is64BitProcess()));
} }
for (auto& entry : out) { for (auto& entry : out) {
@ -193,7 +74,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
const auto symbol_set = symbols.find(entry.module); const auto symbol_set = symbols.find(entry.module);
if (symbol_set != symbols.end()) { if (symbol_set != symbols.end()) {
const auto symbol = GetSymbolName(symbol_set->second, entry.offset); const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
if (symbol.has_value()) { if (symbol.has_value()) {
// TODO(DarkLordZach): Add demangling of symbol names. // TODO(DarkLordZach): Add demangling of symbol names.
entry.name = *symbol; entry.name = *symbol;
@ -225,9 +106,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
return {}; return {};
} }
std::map<std::string, Symbols> symbols; std::map<std::string, Symbols::Symbols> symbols;
for (const auto& module : modules) { for (const auto& module : modules) {
symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); symbols.insert_or_assign(module.second,
Symbols::GetSymbols(module.first, system.Memory(),
system.CurrentProcess()->Is64BitProcess()));
} }
for (auto& entry : out) { for (auto& entry : out) {
@ -249,7 +132,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
const auto symbol_set = symbols.find(entry.module); const auto symbol_set = symbols.find(entry.module);
if (symbol_set != symbols.end()) { if (symbol_set != symbols.end()) {
const auto symbol = GetSymbolName(symbol_set->second, entry.offset); const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
if (symbol.has_value()) { if (symbol.has_value()) {
// TODO(DarkLordZach): Add demangling of symbol names. // TODO(DarkLordZach): Add demangling of symbol names.
entry.name = *symbol; entry.name = *symbol;

190
src/core/arm/symbols.cpp Normal file
View file

@ -0,0 +1,190 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "core/arm/symbols.h"
#include "core/core.h"
#include "core/memory.h"
namespace Core {
namespace {
constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
enum class ELFSymbolType : u8 {
None = 0,
Object = 1,
Function = 2,
Section = 3,
File = 4,
Common = 5,
TLS = 6,
};
enum class ELFSymbolBinding : u8 {
Local = 0,
Global = 1,
Weak = 2,
};
enum class ELFSymbolVisibility : u8 {
Default = 0,
Internal = 1,
Hidden = 2,
Protected = 3,
};
struct ELF64Symbol {
u32 name_index;
union {
u8 info;
BitField<0, 4, ELFSymbolType> type;
BitField<4, 4, ELFSymbolBinding> binding;
};
ELFSymbolVisibility visibility;
u16 sh_index;
u64 value;
u64 size;
};
static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
struct ELF32Symbol {
u32 name_index;
u32 value;
u32 size;
union {
u8 info;
BitField<0, 4, ELFSymbolType> type;
BitField<4, 4, ELFSymbolBinding> binding;
};
ELFSymbolVisibility visibility;
u16 sh_index;
};
static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
} // Anonymous namespace
namespace Symbols {
template <typename Word, typename ELFSymbol, typename ByteReader>
static Symbols GetSymbols(ByteReader ReadBytes) {
const auto Read8{[&](u64 index) {
u8 ret;
ReadBytes(&ret, index, sizeof(u8));
return ret;
}};
const auto Read32{[&](u64 index) {
u32 ret;
ReadBytes(&ret, index, sizeof(u32));
return ret;
}};
const auto ReadWord{[&](u64 index) {
Word ret;
ReadBytes(&ret, index, sizeof(Word));
return ret;
}};
const u32 mod_offset = Read32(4);
if (Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
return {};
}
VAddr string_table_offset{};
VAddr symbol_table_offset{};
u64 symbol_entry_size{};
const auto dynamic_offset = Read32(mod_offset + 0x4) + mod_offset;
VAddr dynamic_index = dynamic_offset;
while (true) {
const Word tag = ReadWord(dynamic_index);
const Word value = ReadWord(dynamic_index + sizeof(Word));
dynamic_index += 2 * sizeof(Word);
if (tag == ELF_DYNAMIC_TAG_NULL) {
break;
}
if (tag == ELF_DYNAMIC_TAG_STRTAB) {
string_table_offset = value;
} else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
symbol_table_offset = value;
} else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
symbol_entry_size = value;
}
}
if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
return {};
}
Symbols out;
VAddr symbol_index = symbol_table_offset;
while (symbol_index < string_table_offset) {
ELFSymbol symbol{};
ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
VAddr string_offset = string_table_offset + symbol.name_index;
std::string name;
for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
name += static_cast<char>(c);
}
symbol_index += symbol_entry_size;
out[name] = std::make_pair(symbol.value, symbol.size);
}
return out;
}
Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
const auto ReadBytes{
[&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
if (is_64) {
return GetSymbols<u64, ELF64Symbol>(ReadBytes);
} else {
return GetSymbols<u32, ELF32Symbol>(ReadBytes);
}
}
Symbols GetSymbols(std::span<const u8> data, bool is_64) {
const auto ReadBytes{[&](void* ptr, size_t offset, size_t size) {
std::memcpy(ptr, data.data() + offset, size);
}};
if (is_64) {
return GetSymbols<u64, ELF64Symbol>(ReadBytes);
} else {
return GetSymbols<u32, ELF32Symbol>(ReadBytes);
}
}
std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr) {
const auto iter = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& pair) {
const auto& [name, sym_info] = pair;
const auto& [start_address, size] = sym_info;
const auto end_address = start_address + size;
return addr >= start_address && addr < end_address;
});
if (iter == symbols.cend()) {
return std::nullopt;
}
return iter->first;
}
} // namespace Symbols
} // namespace Core

27
src/core/arm/symbols.h Normal file
View file

@ -0,0 +1,27 @@
// Copyright 2022 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <optional>
#include <span>
#include <string>
#include <utility>
#include "common/common_types.h"
namespace Core::Memory {
class Memory;
} // namespace Core::Memory
namespace Core::Symbols {
using Symbols = std::map<std::string, std::pair<VAddr, std::size_t>, std::less<>>;
Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64 = true);
Symbols GetSymbols(std::span<const u8> data, bool is_64 = true);
std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr);
} // namespace Core::Symbols