1 //===-- MinidumpTypes.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 "MinidumpTypes.h"
11 
12 // C includes
13 // C++ includes
14 
15 using namespace lldb_private;
16 using namespace minidump;
17 
Parse(llvm::ArrayRef<uint8_t> & data)18 const MinidumpHeader *MinidumpHeader::Parse(llvm::ArrayRef<uint8_t> &data) {
19   const MinidumpHeader *header = nullptr;
20   Status error = consumeObject(data, header);
21 
22   const MinidumpHeaderConstants signature =
23       static_cast<const MinidumpHeaderConstants>(
24           static_cast<const uint32_t>(header->signature));
25   const MinidumpHeaderConstants version =
26       static_cast<const MinidumpHeaderConstants>(
27           static_cast<const uint32_t>(header->version) & 0x0000ffff);
28   // the high 16 bits of the version field are implementation specific
29 
30   if (error.Fail() || signature != MinidumpHeaderConstants::Signature ||
31       version != MinidumpHeaderConstants::Version)
32     return nullptr;
33 
34   return header;
35 }
36 
37 // Minidump string
38 llvm::Optional<std::string>
parseMinidumpString(llvm::ArrayRef<uint8_t> & data)39 lldb_private::minidump::parseMinidumpString(llvm::ArrayRef<uint8_t> &data) {
40   std::string result;
41 
42   const uint32_t *source_length_ptr;
43   Status error = consumeObject(data, source_length_ptr);
44 
45   // Copy non-aligned source_length data into aligned memory.
46   uint32_t source_length;
47   std::memcpy(&source_length, source_length_ptr, sizeof(source_length));
48 
49   if (error.Fail() || source_length > data.size() || source_length % 2 != 0)
50     return llvm::None;
51 
52   auto source_start = reinterpret_cast<const llvm::UTF16 *>(data.data());
53   // source_length is the length of the string in bytes we need the length of
54   // the string in UTF-16 characters/code points (16 bits per char) that's why
55   // it's divided by 2
56   const auto source_end = source_start + source_length / 2;
57   // resize to worst case length
58   result.resize(UNI_MAX_UTF8_BYTES_PER_CODE_POINT * source_length / 2);
59   auto result_start = reinterpret_cast<llvm::UTF8 *>(&result[0]);
60   const auto result_end = result_start + result.size();
61   llvm::ConvertUTF16toUTF8(&source_start, source_end, &result_start, result_end,
62                            llvm::strictConversion);
63   const auto result_size =
64       std::distance(reinterpret_cast<llvm::UTF8 *>(&result[0]), result_start);
65   result.resize(result_size); // shrink to actual length
66 
67   return result;
68 }
69 
70 // MinidumpThread
Parse(llvm::ArrayRef<uint8_t> & data)71 const MinidumpThread *MinidumpThread::Parse(llvm::ArrayRef<uint8_t> &data) {
72   const MinidumpThread *thread = nullptr;
73   Status error = consumeObject(data, thread);
74   if (error.Fail())
75     return nullptr;
76 
77   return thread;
78 }
79 
80 llvm::ArrayRef<MinidumpThread>
ParseThreadList(llvm::ArrayRef<uint8_t> & data)81 MinidumpThread::ParseThreadList(llvm::ArrayRef<uint8_t> &data) {
82   const auto orig_size = data.size();
83   const llvm::support::ulittle32_t *thread_count;
84   Status error = consumeObject(data, thread_count);
85   if (error.Fail() || *thread_count * sizeof(MinidumpThread) > data.size())
86     return {};
87 
88   // Compilers might end up padding an extra 4 bytes depending on how the
89   // structure is padded by the compiler and the #pragma pack settings.
90   if (4 + *thread_count * sizeof(MinidumpThread) < orig_size)
91     data = data.drop_front(4);
92 
93   return llvm::ArrayRef<MinidumpThread>(
94       reinterpret_cast<const MinidumpThread *>(data.data()), *thread_count);
95 }
96 
97 // MinidumpSystemInfo
98 const MinidumpSystemInfo *
Parse(llvm::ArrayRef<uint8_t> & data)99 MinidumpSystemInfo::Parse(llvm::ArrayRef<uint8_t> &data) {
100   const MinidumpSystemInfo *system_info;
101   Status error = consumeObject(data, system_info);
102   if (error.Fail())
103     return nullptr;
104 
105   return system_info;
106 }
107 
108 // MinidumpMiscInfo
Parse(llvm::ArrayRef<uint8_t> & data)109 const MinidumpMiscInfo *MinidumpMiscInfo::Parse(llvm::ArrayRef<uint8_t> &data) {
110   const MinidumpMiscInfo *misc_info;
111   Status error = consumeObject(data, misc_info);
112   if (error.Fail())
113     return nullptr;
114 
115   return misc_info;
116 }
117 
GetPid() const118 llvm::Optional<lldb::pid_t> MinidumpMiscInfo::GetPid() const {
119   uint32_t pid_flag =
120       static_cast<const uint32_t>(MinidumpMiscInfoFlags::ProcessID);
121   if (flags1 & pid_flag)
122     return llvm::Optional<lldb::pid_t>(process_id);
123 
124   return llvm::None;
125 }
126 
127 // Linux Proc Status
128 // it's stored as an ascii string in the file
129 llvm::Optional<LinuxProcStatus>
Parse(llvm::ArrayRef<uint8_t> & data)130 LinuxProcStatus::Parse(llvm::ArrayRef<uint8_t> &data) {
131   LinuxProcStatus result;
132   result.proc_status =
133       llvm::StringRef(reinterpret_cast<const char *>(data.data()), data.size());
134   data = data.drop_front(data.size());
135 
136   llvm::SmallVector<llvm::StringRef, 0> lines;
137   result.proc_status.split(lines, '\n', 42);
138   // /proc/$pid/status has 41 lines, but why not use 42?
139   for (auto line : lines) {
140     if (line.consume_front("Pid:")) {
141       line = line.trim();
142       if (!line.getAsInteger(10, result.pid))
143         return result;
144     }
145   }
146 
147   return llvm::None;
148 }
149 
GetPid() const150 lldb::pid_t LinuxProcStatus::GetPid() const { return pid; }
151 
152 // Module stuff
Parse(llvm::ArrayRef<uint8_t> & data)153 const MinidumpModule *MinidumpModule::Parse(llvm::ArrayRef<uint8_t> &data) {
154   const MinidumpModule *module = nullptr;
155   Status error = consumeObject(data, module);
156   if (error.Fail())
157     return nullptr;
158 
159   return module;
160 }
161 
162 llvm::ArrayRef<MinidumpModule>
ParseModuleList(llvm::ArrayRef<uint8_t> & data)163 MinidumpModule::ParseModuleList(llvm::ArrayRef<uint8_t> &data) {
164   const auto orig_size = data.size();
165   const llvm::support::ulittle32_t *modules_count;
166   Status error = consumeObject(data, modules_count);
167   if (error.Fail() || *modules_count * sizeof(MinidumpModule) > data.size())
168     return {};
169 
170   // Compilers might end up padding an extra 4 bytes depending on how the
171   // structure is padded by the compiler and the #pragma pack settings.
172   if (4 + *modules_count * sizeof(MinidumpModule) < orig_size)
173     data = data.drop_front(4);
174 
175   return llvm::ArrayRef<MinidumpModule>(
176       reinterpret_cast<const MinidumpModule *>(data.data()), *modules_count);
177 }
178 
179 // Exception stuff
180 const MinidumpExceptionStream *
Parse(llvm::ArrayRef<uint8_t> & data)181 MinidumpExceptionStream::Parse(llvm::ArrayRef<uint8_t> &data) {
182   const MinidumpExceptionStream *exception_stream = nullptr;
183   Status error = consumeObject(data, exception_stream);
184   if (error.Fail())
185     return nullptr;
186 
187   return exception_stream;
188 }
189 
190 llvm::ArrayRef<MinidumpMemoryDescriptor>
ParseMemoryList(llvm::ArrayRef<uint8_t> & data)191 MinidumpMemoryDescriptor::ParseMemoryList(llvm::ArrayRef<uint8_t> &data) {
192   const auto orig_size = data.size();
193   const llvm::support::ulittle32_t *mem_ranges_count;
194   Status error = consumeObject(data, mem_ranges_count);
195   if (error.Fail() ||
196       *mem_ranges_count * sizeof(MinidumpMemoryDescriptor) > data.size())
197     return {};
198 
199   // Compilers might end up padding an extra 4 bytes depending on how the
200   // structure is padded by the compiler and the #pragma pack settings.
201   if (4 + *mem_ranges_count * sizeof(MinidumpMemoryDescriptor) < orig_size)
202     data = data.drop_front(4);
203 
204   return llvm::makeArrayRef(
205       reinterpret_cast<const MinidumpMemoryDescriptor *>(data.data()),
206       *mem_ranges_count);
207 }
208 
209 std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t>
ParseMemory64List(llvm::ArrayRef<uint8_t> & data)210 MinidumpMemoryDescriptor64::ParseMemory64List(llvm::ArrayRef<uint8_t> &data) {
211   const llvm::support::ulittle64_t *mem_ranges_count;
212   Status error = consumeObject(data, mem_ranges_count);
213   if (error.Fail() ||
214       *mem_ranges_count * sizeof(MinidumpMemoryDescriptor64) > data.size())
215     return {};
216 
217   const llvm::support::ulittle64_t *base_rva;
218   error = consumeObject(data, base_rva);
219   if (error.Fail())
220     return {};
221 
222   return std::make_pair(
223       llvm::makeArrayRef(
224           reinterpret_cast<const MinidumpMemoryDescriptor64 *>(data.data()),
225           *mem_ranges_count),
226       *base_rva);
227 }
228 
229 std::vector<const MinidumpMemoryInfo *>
ParseMemoryInfoList(llvm::ArrayRef<uint8_t> & data)230 MinidumpMemoryInfo::ParseMemoryInfoList(llvm::ArrayRef<uint8_t> &data) {
231   const MinidumpMemoryInfoListHeader *header;
232   Status error = consumeObject(data, header);
233   if (error.Fail() ||
234       header->size_of_header < sizeof(MinidumpMemoryInfoListHeader) ||
235       header->size_of_entry < sizeof(MinidumpMemoryInfo))
236     return {};
237 
238   data = data.drop_front(header->size_of_header -
239                          sizeof(MinidumpMemoryInfoListHeader));
240 
241   if (header->size_of_entry * header->num_of_entries > data.size())
242     return {};
243 
244   std::vector<const MinidumpMemoryInfo *> result;
245   result.reserve(header->num_of_entries);
246 
247   for (uint64_t i = 0; i < header->num_of_entries; ++i) {
248     result.push_back(reinterpret_cast<const MinidumpMemoryInfo *>(
249         data.data() + i * header->size_of_entry));
250   }
251 
252   return result;
253 }
254