1 //===-- ObjectFileBreakpad.cpp -------------------------------- -*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h"
11 #include "lldb/Core/ModuleSpec.h"
12 #include "lldb/Core/PluginManager.h"
13 #include "lldb/Core/Section.h"
14 #include "lldb/Utility/DataBuffer.h"
15 #include "llvm/ADT/StringExtras.h"
18 using namespace lldb_private;
19 using namespace lldb_private::breakpad;
25 static llvm::Optional<Header> parse(llvm::StringRef text);
28 enum class Token { Unknown, Module, Info, File, Func, Public, Stack };
31 static Token toToken(llvm::StringRef str) {
32 return llvm::StringSwitch<Token>(str)
33 .Case("MODULE", Token::Module)
34 .Case("INFO", Token::Info)
35 .Case("FILE", Token::File)
36 .Case("FUNC", Token::Func)
37 .Case("PUBLIC", Token::Public)
38 .Case("STACK", Token::Stack)
39 .Default(Token::Unknown);
42 static llvm::StringRef toString(Token t) {
59 llvm_unreachable("Unknown token!");
62 static llvm::Triple::OSType toOS(llvm::StringRef str) {
64 return llvm::StringSwitch<Triple::OSType>(str)
65 .Case("Linux", Triple::Linux)
66 .Case("mac", Triple::MacOSX)
67 .Case("windows", Triple::Win32)
68 .Default(Triple::UnknownOS);
71 static llvm::Triple::ArchType toArch(llvm::StringRef str) {
73 return llvm::StringSwitch<Triple::ArchType>(str)
74 .Case("arm", Triple::arm)
75 .Case("arm64", Triple::aarch64)
76 .Case("mips", Triple::mips)
77 .Case("ppc", Triple::ppc)
78 .Case("ppc64", Triple::ppc64)
79 .Case("s390", Triple::systemz)
80 .Case("sparc", Triple::sparc)
81 .Case("sparcv9", Triple::sparcv9)
82 .Case("x86", Triple::x86)
83 .Case("x86_64", Triple::x86_64)
84 .Default(Triple::UnknownArch);
87 static llvm::StringRef consume_front(llvm::StringRef &str, size_t n) {
88 llvm::StringRef result = str.take_front(n);
89 str = str.drop_front(n);
93 static UUID parseModuleId(llvm::Triple::OSType os, llvm::StringRef str) {
95 llvm::support::ulittle32_t uuid1;
96 llvm::support::ulittle16_t uuid2[2];
98 llvm::support::ulittle32_t age;
100 static_assert(sizeof(data) == 20, "");
101 // The textual module id encoding should be between 33 and 40 bytes long,
102 // depending on the size of the age field, which is of variable length.
103 // The first three chunks of the id are encoded in big endian, so we need to
105 if (str.size() < 33 || str.size() > 40)
108 if (to_integer(consume_front(str, 8), t, 16))
112 for (int i = 0; i < 2; ++i) {
113 if (to_integer(consume_front(str, 4), t, 16))
118 for (int i = 0; i < 8; ++i) {
119 if (!to_integer(consume_front(str, 2), data.uuid3[i], 16))
122 if (to_integer(str, t, 16))
127 // On non-windows, the age field should always be zero, so we don't include to
128 // match the native uuid format of these platforms.
129 return UUID::fromData(&data, os == llvm::Triple::Win32 ? 20 : 16);
132 llvm::Optional<Header> Header::parse(llvm::StringRef text) {
133 // A valid module should start with something like:
134 // MODULE Linux x86_64 E5894855C35DCCCCCCCCCCCCCCCCCCCC0 a.out
135 // optionally followed by
136 // INFO CODE_ID 554889E55DC3CCCCCCCCCCCCCCCCCCCC [a.exe]
137 llvm::StringRef token, line;
138 std::tie(line, text) = text.split('\n');
139 std::tie(token, line) = getToken(line);
140 if (toToken(token) != Token::Module)
143 std::tie(token, line) = getToken(line);
145 triple.setOS(toOS(token));
146 if (triple.getOS() == llvm::Triple::UnknownOS)
149 std::tie(token, line) = getToken(line);
150 triple.setArch(toArch(token));
151 if (triple.getArch() == llvm::Triple::UnknownArch)
154 llvm::StringRef module_id;
155 std::tie(module_id, line) = getToken(line);
157 std::tie(line, text) = text.split('\n');
158 std::tie(token, line) = getToken(line);
159 if (token == "INFO") {
160 std::tie(token, line) = getToken(line);
161 if (token != "CODE_ID")
164 std::tie(token, line) = getToken(line);
165 // If we don't have any text following the code id (e.g. on linux), we
166 // should use the module id as UUID. Otherwise, we revert back to the module
168 if (line.trim().empty()) {
170 if (uuid.SetFromStringRef(token, token.size() / 2) != token.size())
173 return Header{ArchSpec(triple), uuid};
177 // We reach here if we don't have a INFO CODE_ID section, or we chose not to
178 // use it. In either case, we need to properly decode the module id, whose
179 // fields are encoded in big-endian.
180 UUID uuid = parseModuleId(triple.getOS(), module_id);
184 return Header{ArchSpec(triple), uuid};
187 void ObjectFileBreakpad::Initialize() {
188 PluginManager::RegisterPlugin(GetPluginNameStatic(),
189 GetPluginDescriptionStatic(), CreateInstance,
190 CreateMemoryInstance, GetModuleSpecifications);
193 void ObjectFileBreakpad::Terminate() {
194 PluginManager::UnregisterPlugin(CreateInstance);
197 ConstString ObjectFileBreakpad::GetPluginNameStatic() {
198 static ConstString g_name("breakpad");
202 ObjectFile *ObjectFileBreakpad::CreateInstance(
203 const ModuleSP &module_sp, DataBufferSP &data_sp, offset_t data_offset,
204 const FileSpec *file, offset_t file_offset, offset_t length) {
206 data_sp = MapFileData(*file, length, file_offset);
211 auto text = toStringRef(data_sp->GetData());
212 llvm::Optional<Header> header = Header::parse(text);
216 // Update the data to contain the entire file if it doesn't already
217 if (data_sp->GetByteSize() < length) {
218 data_sp = MapFileData(*file, length, file_offset);
224 return new ObjectFileBreakpad(module_sp, data_sp, data_offset, file,
225 file_offset, length, std::move(header->arch),
226 std::move(header->uuid));
229 ObjectFile *ObjectFileBreakpad::CreateMemoryInstance(
230 const ModuleSP &module_sp, DataBufferSP &data_sp,
231 const ProcessSP &process_sp, addr_t header_addr) {
235 size_t ObjectFileBreakpad::GetModuleSpecifications(
236 const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset,
237 offset_t file_offset, offset_t length, ModuleSpecList &specs) {
238 auto text = toStringRef(data_sp->GetData());
239 llvm::Optional<Header> header = Header::parse(text);
242 ModuleSpec spec(file, std::move(header->arch));
243 spec.GetUUID() = std::move(header->uuid);
248 ObjectFileBreakpad::ObjectFileBreakpad(const ModuleSP &module_sp,
249 DataBufferSP &data_sp,
250 offset_t data_offset,
251 const FileSpec *file, offset_t offset,
252 offset_t length, ArchSpec arch,
254 : ObjectFile(module_sp, file, offset, length, data_sp, data_offset),
255 m_arch(std::move(arch)), m_uuid(std::move(uuid)) {}
257 bool ObjectFileBreakpad::ParseHeader() {
258 // We already parsed the header during initialization.
262 Symtab *ObjectFileBreakpad::GetSymtab() {
267 bool ObjectFileBreakpad::GetUUID(UUID *uuid) {
272 void ObjectFileBreakpad::CreateSections(SectionList &unified_section_list) {
275 m_sections_ap = llvm::make_unique<SectionList>();
277 Token current_section = Token::Unknown;
278 offset_t section_start;
279 llvm::StringRef text = toStringRef(m_data.GetData());
280 uint32_t next_section_id = 1;
281 auto maybe_add_section = [&](const uint8_t *end_ptr) {
282 if (current_section == Token::Unknown)
283 return; // We have been called before parsing the first line.
285 offset_t end_offset = end_ptr - m_data.GetDataStart();
286 auto section_sp = std::make_shared<Section>(
287 GetModule(), this, next_section_id++,
288 ConstString(toString(current_section)), eSectionTypeOther,
289 /*file_vm_addr*/ 0, /*vm_size*/ 0, section_start,
290 end_offset - section_start, /*log2align*/ 0, /*flags*/ 0);
291 m_sections_ap->AddSection(section_sp);
292 unified_section_list.AddSection(section_sp);
294 while (!text.empty()) {
295 llvm::StringRef line;
296 std::tie(line, text) = text.split('\n');
298 Token token = toToken(getToken(line).first);
299 if (token == Token::Unknown) {
300 // We assume this is a line record, which logically belongs to the Func
301 // section. Errors will be handled when parsing the Func section.
304 if (token == current_section)
307 // Changing sections, finish off the previous one, if there was any.
308 maybe_add_section(line.bytes_begin());
309 // And start a new one.
310 current_section = token;
311 section_start = line.bytes_begin() - m_data.GetDataStart();
313 // Finally, add the last section.
314 maybe_add_section(m_data.GetDataEnd());