xref: /llvm-project/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp (revision f760f5aef404b0767c06f64da219032e3e7bb3bd)
1 //===-- 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 
16 using namespace lldb;
17 using namespace lldb_private;
18 using namespace lldb_private::breakpad;
19 
20 namespace {
21 struct Header {
22   ArchSpec arch;
23   UUID uuid;
24   static llvm::Optional<Header> parse(llvm::StringRef text);
25 };
26 } // namespace
27 
28 static 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 
37 static 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 
53 static 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 
59 static 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 
98 llvm::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 
153 void ObjectFileBreakpad::Initialize() {
154   PluginManager::RegisterPlugin(GetPluginNameStatic(),
155                                 GetPluginDescriptionStatic(), CreateInstance,
156                                 CreateMemoryInstance, GetModuleSpecifications);
157 }
158 
159 void ObjectFileBreakpad::Terminate() {
160   PluginManager::UnregisterPlugin(CreateInstance);
161 }
162 
163 ConstString ObjectFileBreakpad::GetPluginNameStatic() {
164   static ConstString g_name("breakpad");
165   return g_name;
166 }
167 
168 ObjectFile *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 
195 ObjectFile *ObjectFileBreakpad::CreateMemoryInstance(
196     const ModuleSP &module_sp, DataBufferSP &data_sp,
197     const ProcessSP &process_sp, addr_t header_addr) {
198   return nullptr;
199 }
200 
201 size_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 
214 ObjectFileBreakpad::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 
223 bool ObjectFileBreakpad::ParseHeader() {
224   // We already parsed the header during initialization.
225   return true;
226 }
227 
228 Symtab *ObjectFileBreakpad::GetSymtab() {
229   // TODO
230   return nullptr;
231 }
232 
233 bool ObjectFileBreakpad::GetUUID(UUID *uuid) {
234   *uuid = m_uuid;
235   return true;
236 }
237 
238 void ObjectFileBreakpad::CreateSections(SectionList &unified_section_list) {
239   // TODO
240 }
241