summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Zach Hilman2019-05-17 21:43:26 -0400
committerGravatar Zach Hilman2019-05-25 16:06:53 -0400
commit5574be21ccd8cf86eaba5c50d30ad898bafc8ea8 (patch)
tree08dab8c037370c284f46b56335d9b7d8e68ce839 /src
parentcore: Track load offsets of NSO modules (diff)
downloadyuzu-5574be21ccd8cf86eaba5c50d30ad898bafc8ea8.tar.gz
yuzu-5574be21ccd8cf86eaba5c50d30ad898bafc8ea8.tar.xz
yuzu-5574be21ccd8cf86eaba5c50d30ad898bafc8ea8.zip
arm_interface: Expand backtrace generation
Returns results as a vector of entries for further processing. Logs addresses, offsets, and mangled name.
Diffstat (limited to 'src')
-rw-r--r--src/core/arm/arm_interface.cpp190
-rw-r--r--src/core/arm/arm_interface.h11
2 files changed, 194 insertions, 7 deletions
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 2223cbeed..2945fcff8 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -2,26 +2,202 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map>
6#include <optional>
7#include "common/bit_field.h"
5#include "common/common_types.h" 8#include "common/common_types.h"
6#include "common/logging/log.h" 9#include "common/logging/log.h"
7#include "core/arm/arm_interface.h" 10#include "core/arm/arm_interface.h"
11#include "core/core.h"
8#include "core/memory.h" 12#include "core/memory.h"
9 13
10namespace Core { 14namespace Core {
11void ARM_Interface::LogBacktrace() const {
12 VAddr fp = GetReg(29);
13 VAddr lr = GetReg(30);
14 const VAddr sp = GetReg(13);
15 const VAddr pc = GetPC();
16 15
17 LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc); 16namespace {
17
18constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
19constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
20constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
21constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
22
23enum class ELFSymbolType : u8 {
24 None = 0,
25 Object = 1,
26 Function = 2,
27 Section = 3,
28 File = 4,
29 Common = 5,
30 TLS = 6,
31};
32
33enum class ELFSymbolBinding : u8 {
34 Local = 0,
35 Global = 1,
36 Weak = 2,
37};
38
39enum class ELFSymbolVisibility : u8 {
40 Default = 0,
41 Internal = 1,
42 Hidden = 2,
43 Protected = 3,
44};
45
46struct ELFSymbol {
47 u32 name_index;
48 union {
49 u8 info;
50
51 BitField<0, 4, ELFSymbolType> type;
52 BitField<4, 4, ELFSymbolBinding> binding;
53 };
54 ELFSymbolVisibility visibility;
55 u16 sh_index;
56 u64 value;
57 u64 size;
58};
59static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
60
61using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
62
63Symbols GetSymbols(VAddr text_offset) {
64 const auto mod_offset = text_offset + Memory::Read32(text_offset + 4);
65
66 if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
67 Memory::Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
68 return {};
69 }
70
71 const auto dynamic_offset = Memory::Read32(mod_offset + 0x4) + mod_offset;
72
73 VAddr string_table_offset{};
74 VAddr symbol_table_offset{};
75 u64 symbol_entry_size{};
76
77 VAddr dynamic_index = dynamic_offset;
78 while (true) {
79 const auto tag = Memory::Read64(dynamic_index);
80 const auto value = Memory::Read64(dynamic_index + 0x8);
81 dynamic_index += 0x10;
82
83 if (tag == ELF_DYNAMIC_TAG_NULL)
84 break;
85
86 if (tag == ELF_DYNAMIC_TAG_STRTAB)
87 string_table_offset = value;
88 else if (tag == ELF_DYNAMIC_TAG_SYMTAB)
89 symbol_table_offset = value;
90 else if (tag == ELF_DYNAMIC_TAG_SYMENT)
91 symbol_entry_size = value;
92 }
93
94 if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
95 return {};
96 }
97
98 const auto string_table_address = text_offset + string_table_offset;
99 const auto symbol_table_address = text_offset + symbol_table_offset;
100
101 Symbols out;
102
103 VAddr symbol_index = symbol_table_address;
104 while (symbol_index < string_table_address) {
105 ELFSymbol symbol{};
106 Memory::ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
107
108 VAddr string_offset = string_table_address + symbol.name_index;
109 std::string name;
110 for (u8 c = Memory::Read8(string_offset); c != 0; c = Memory::Read8(++string_offset)) {
111 name += static_cast<char>(c);
112 }
113
114 symbol_index += symbol_entry_size;
115 out.push_back({symbol, name});
116 }
117
118 return out;
119}
120
121std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) {
122 const auto iter =
123 std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) {
124 const auto& [symbol, name] = pair;
125 const auto end_address = symbol.value + symbol.size;
126 return func_address >= symbol.value && func_address < end_address;
127 });
128
129 if (iter == symbols.end())
130 return std::nullopt;
131 return iter->second;
132}
133
134} // Anonymous namespace
135
136constexpr u64 SEGMENT_BASE = 0x7100000000ull;
137
138std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
139 std::vector<BacktraceEntry> out;
140
141 auto fp = GetReg(29);
142 auto lr = GetReg(30);
143
18 while (true) { 144 while (true) {
19 LOG_ERROR(Core_ARM, "{:016X}", lr); 145 out.push_back({"", 0, lr, 0});
20 if (!fp) { 146 if (!fp) {
21 break; 147 break;
22 } 148 }
23 lr = Memory::Read64(fp + 8) - 4; 149 lr = Memory::Read64(fp + 8) - 4;
24 fp = Memory::Read64(fp); 150 fp = Memory::Read64(fp);
25 } 151 }
152
153 const auto& modules{System::GetInstance().GetRegisteredNSOModules()};
154 std::map<std::string, Symbols> symbols;
155 for (const auto& module : modules) {
156 symbols.insert_or_assign(module.second, GetSymbols(module.first));
157 }
158
159 for (auto& entry : out) {
160 VAddr base = 0;
161 for (const auto& module : modules) {
162 if (entry.original_address >= module.first) {
163 entry.module = module.second;
164 base = module.first;
165 break;
166 }
167 }
168
169 entry.offset = entry.original_address - base;
170 entry.address = SEGMENT_BASE + entry.offset;
171
172 if (entry.module.empty())
173 entry.module = "unknown";
174
175 const auto symbol_set = symbols.find(entry.module);
176 if (symbol_set != symbols.end()) {
177 const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
178 if (symbol.has_value()) {
179 // TODO(DarkLordZach): Add demangling of symbol names.
180 entry.name = *symbol;
181 }
182 }
183 }
184
185 return out;
26} 186}
187
188void ARM_Interface::LogBacktrace() const {
189 const VAddr sp = GetReg(13);
190 const VAddr pc = GetPC();
191 LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
192 LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
193 "Offset", "Symbol");
194 LOG_ERROR(Core_ARM, "{}", std::string(100, '-'));
195
196 const auto backtrace = GetBacktrace();
197 for (const auto& entry : backtrace) {
198 LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address,
199 entry.original_address, entry.offset, entry.name);
200 }
201}
202
27} // namespace Core 203} // namespace Core
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 978b1518f..c6691a8e1 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <vector>
8#include "common/common_types.h" 9#include "common/common_types.h"
9 10
10namespace Common { 11namespace Common {
@@ -152,6 +153,16 @@ public:
152 /// Prepare core for thread reschedule (if needed to correctly handle state) 153 /// Prepare core for thread reschedule (if needed to correctly handle state)
153 virtual void PrepareReschedule() = 0; 154 virtual void PrepareReschedule() = 0;
154 155
156 struct BacktraceEntry {
157 std::string module;
158 u64 address;
159 u64 original_address;
160 u64 offset;
161 std::string name;
162 };
163
164 std::vector<BacktraceEntry> GetBacktrace() const;
165
155 /// fp (= r29) points to the last frame record. 166 /// fp (= r29) points to the last frame record.
156 /// Note that this is the frame record for the *previous* frame, not the current one. 167 /// Note that this is the frame record for the *previous* frame, not the current one.
157 /// Note we need to subtract 4 from our last read to get the proper address 168 /// Note we need to subtract 4 from our last read to get the proper address