blob: 7eebac2c6935977756bdecfd9e8d6b10a0f7dcbc [file] [log] [blame]
Pavel Labath1f6b2472018-12-10 17:16:38 +00001//===-- ObjectFileBreakpad.cpp -------------------------------- -*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h"
11#include "lldb/Core/ModuleSpec.h"
12#include "lldb/Core/PluginManager.h"
13#include "lldb/Utility/DataBuffer.h"
14#include "llvm/ADT/StringExtras.h"
15
16using namespace lldb;
17using namespace lldb_private;
18using namespace lldb_private::breakpad;
19
20namespace {
21struct Header {
22 ArchSpec arch;
23 UUID uuid;
24 static llvm::Optional<Header> parse(llvm::StringRef text);
25};
26} // namespace
27
28static llvm::Triple::OSType toOS(llvm::StringRef str) {
29 using llvm::Triple;
30 return llvm::StringSwitch<Triple::OSType>(str)
31 .Case("Linux", Triple::Linux)
32 .Case("mac", Triple::MacOSX)
33 .Case("windows", Triple::Win32)
34 .Default(Triple::UnknownOS);
35}
36
37static llvm::Triple::ArchType toArch(llvm::StringRef str) {
38 using llvm::Triple;
39 return llvm::StringSwitch<Triple::ArchType>(str)
40 .Case("arm", Triple::arm)
41 .Case("arm64", Triple::aarch64)
42 .Case("mips", Triple::mips)
43 .Case("ppc", Triple::ppc)
44 .Case("ppc64", Triple::ppc64)
45 .Case("s390", Triple::systemz)
46 .Case("sparc", Triple::sparc)
47 .Case("sparcv9", Triple::sparcv9)
48 .Case("x86", Triple::x86)
49 .Case("x86_64", Triple::x86_64)
50 .Default(Triple::UnknownArch);
51}
52
53static llvm::StringRef consume_front(llvm::StringRef &str, size_t n) {
54 llvm::StringRef result = str.take_front(n);
55 str = str.drop_front(n);
56 return result;
57}
58
59static UUID parseModuleId(llvm::Triple::OSType os, llvm::StringRef str) {
60 struct uuid_data {
61 llvm::support::ulittle32_t uuid1;
62 llvm::support::ulittle16_t uuid2[2];
63 uint8_t uuid3[8];
64 llvm::support::ulittle32_t age;
65 } data;
66 static_assert(sizeof(data) == 20, "");
67 // The textual module id encoding should be between 33 and 40 bytes long,
68 // depending on the size of the age field, which is of variable length.
69 // The first three chunks of the id are encoded in big endian, so we need to
70 // byte-swap those.
71 if (str.size() < 33 || str.size() > 40)
72 return UUID();
73 uint32_t t;
74 if (to_integer(consume_front(str, 8), t, 16))
75 data.uuid1 = t;
76 else
77 return UUID();
78 for (int i = 0; i < 2; ++i) {
79 if (to_integer(consume_front(str, 4), t, 16))
80 data.uuid2[i] = t;
81 else
82 return UUID();
83 }
84 for (int i = 0; i < 8; ++i) {
85 if (!to_integer(consume_front(str, 2), data.uuid3[i], 16))
86 return UUID();
87 }
88 if (to_integer(str, t, 16))
89 data.age = t;
90 else
91 return UUID();
92
93 // On non-windows, the age field should always be zero, so we don't include to
94 // match the native uuid format of these platforms.
95 return UUID::fromData(&data, os == llvm::Triple::Win32 ? 20 : 16);
96}
97
98llvm::Optional<Header> Header::parse(llvm::StringRef text) {
99 // A valid module should start with something like:
100 // MODULE Linux x86_64 E5894855C35DCCCCCCCCCCCCCCCCCCCC0 a.out
101 // optionally followed by
102 // INFO CODE_ID 554889E55DC3CCCCCCCCCCCCCCCCCCCC [a.exe]
103 llvm::StringRef token, line;
104 std::tie(line, text) = text.split('\n');
105 std::tie(token, line) = getToken(line);
106 if (token != "MODULE")
107 return llvm::None;
108
109 std::tie(token, line) = getToken(line);
110 llvm::Triple triple;
111 triple.setOS(toOS(token));
112 if (triple.getOS() == llvm::Triple::UnknownOS)
113 return llvm::None;
114
115 std::tie(token, line) = getToken(line);
116 triple.setArch(toArch(token));
117 if (triple.getArch() == llvm::Triple::UnknownArch)
118 return llvm::None;
119
120 llvm::StringRef module_id;
121 std::tie(module_id, line) = getToken(line);
122
123 std::tie(line, text) = text.split('\n');
124 std::tie(token, line) = getToken(line);
125 if (token == "INFO") {
126 std::tie(token, line) = getToken(line);
127 if (token != "CODE_ID")
128 return llvm::None;
129
130 std::tie(token, line) = getToken(line);
131 // If we don't have any text following the code id (e.g. on linux), we
132 // should use the module id as UUID. Otherwise, we revert back to the module
133 // id.
134 if (line.trim().empty()) {
135 UUID uuid;
136 if (uuid.SetFromStringRef(token, token.size() / 2) != token.size())
137 return llvm::None;
138
139 return Header{ArchSpec(triple), uuid};
140 }
141 }
142
143 // We reach here if we don't have a INFO CODE_ID section, or we chose not to
144 // use it. In either case, we need to properly decode the module id, whose
145 // fields are encoded in big-endian.
146 UUID uuid = parseModuleId(triple.getOS(), module_id);
147 if (!uuid)
148 return llvm::None;
149
150 return Header{ArchSpec(triple), uuid};
151}
152
153void ObjectFileBreakpad::Initialize() {
154 PluginManager::RegisterPlugin(GetPluginNameStatic(),
155 GetPluginDescriptionStatic(), CreateInstance,
Pavel Labath871f2b62018-12-10 18:17:53 +0000156 CreateMemoryInstance, GetModuleSpecifications);
Pavel Labath1f6b2472018-12-10 17:16:38 +0000157}
158
159void ObjectFileBreakpad::Terminate() {
160 PluginManager::UnregisterPlugin(CreateInstance);
161}
162
163ConstString ObjectFileBreakpad::GetPluginNameStatic() {
164 static ConstString g_name("breakpad");
165 return g_name;
166}
167
168ObjectFile *ObjectFileBreakpad::CreateInstance(
169 const ModuleSP &module_sp, DataBufferSP &data_sp, offset_t data_offset,
170 const FileSpec *file, offset_t file_offset, offset_t length) {
171 if (!data_sp) {
172 data_sp = MapFileData(*file, length, file_offset);
173 if (!data_sp)
174 return nullptr;
175 data_offset = 0;
176 }
177 auto text = toStringRef(data_sp->GetData());
178 llvm::Optional<Header> header = Header::parse(text);
179 if (!header)
180 return nullptr;
181
182 // Update the data to contain the entire file if it doesn't already
183 if (data_sp->GetByteSize() < length) {
184 data_sp = MapFileData(*file, length, file_offset);
185 if (!data_sp)
186 return nullptr;
187 data_offset = 0;
188 }
189
190 return new ObjectFileBreakpad(module_sp, data_sp, data_offset, file,
191 file_offset, length, std::move(header->arch),
192 std::move(header->uuid));
193}
194
195ObjectFile *ObjectFileBreakpad::CreateMemoryInstance(
196 const ModuleSP &module_sp, DataBufferSP &data_sp,
197 const ProcessSP &process_sp, addr_t header_addr) {
198 return nullptr;
199}
200
201size_t ObjectFileBreakpad::GetModuleSpecifications(
202 const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset,
203 offset_t file_offset, offset_t length, ModuleSpecList &specs) {
204 auto text = toStringRef(data_sp->GetData());
205 llvm::Optional<Header> header = Header::parse(text);
206 if (!header)
207 return 0;
208 ModuleSpec spec(file, std::move(header->arch));
209 spec.GetUUID() = std::move(header->uuid);
210 specs.Append(spec);
211 return 1;
212}
213
214ObjectFileBreakpad::ObjectFileBreakpad(const ModuleSP &module_sp,
215 DataBufferSP &data_sp,
216 offset_t data_offset,
217 const FileSpec *file, offset_t offset,
218 offset_t length, ArchSpec arch,
219 UUID uuid)
220 : ObjectFile(module_sp, file, offset, length, data_sp, data_offset),
221 m_arch(std::move(arch)), m_uuid(std::move(uuid)) {}
222
223bool ObjectFileBreakpad::ParseHeader() {
224 // We already parsed the header during initialization.
225 return true;
226}
227
228Symtab *ObjectFileBreakpad::GetSymtab() {
229 // TODO
230 return nullptr;
231}
232
Pavel Labath1f6b2472018-12-10 17:16:38 +0000233bool ObjectFileBreakpad::GetUUID(UUID *uuid) {
234 *uuid = m_uuid;
235 return true;
236}
237
238void ObjectFileBreakpad::CreateSections(SectionList &unified_section_list) {
239 // TODO
240}