1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5#include "createdump.h"
6
7DumpWriter::DumpWriter(CrashInfo& crashInfo) :
8 m_ref(1),
9 m_fd(-1),
10 m_crashInfo(crashInfo)
11{
12 m_crashInfo.AddRef();
13}
14
15DumpWriter::~DumpWriter()
16{
17 if (m_fd != -1)
18 {
19 close(m_fd);
20 m_fd = -1;
21 }
22 m_crashInfo.Release();
23}
24
25STDMETHODIMP
26DumpWriter::QueryInterface(
27 ___in REFIID InterfaceId,
28 ___out PVOID* Interface)
29{
30 if (InterfaceId == IID_IUnknown)
31 {
32 *Interface = (IUnknown*)this;
33 AddRef();
34 return S_OK;
35 }
36 else
37 {
38 *Interface = NULL;
39 return E_NOINTERFACE;
40 }
41}
42
43STDMETHODIMP_(ULONG)
44DumpWriter::AddRef()
45{
46 LONG ref = InterlockedIncrement(&m_ref);
47 return ref;
48}
49
50STDMETHODIMP_(ULONG)
51DumpWriter::Release()
52{
53 LONG ref = InterlockedDecrement(&m_ref);
54 if (ref == 0)
55 {
56 delete this;
57 }
58 return ref;
59}
60
61bool
62DumpWriter::OpenDump(const char* dumpFileName)
63{
64 m_fd = open(dumpFileName, O_WRONLY|O_CREAT|O_TRUNC, 0664);
65 if (m_fd == -1)
66 {
67 fprintf(stderr, "Could not open output %s: %s\n", dumpFileName, strerror(errno));
68 return false;
69 }
70 return true;
71}
72
73// Write the core dump file:
74// ELF header
75// Single section header (Shdr) for 64 bit program header count
76// Phdr for the PT_NOTE
77// PT_LOAD
78// PT_NOTEs
79// process info (prpsinfo_t)
80// NT_FILE entries
81// threads
82// alignment
83// memory blocks
84bool
85DumpWriter::WriteDump()
86{
87 // Write the ELF header
88 Ehdr ehdr;
89 memset(&ehdr, 0, sizeof(Ehdr));
90 ehdr.e_ident[0] = ELFMAG0;
91 ehdr.e_ident[1] = ELFMAG1;
92 ehdr.e_ident[2] = ELFMAG2;
93 ehdr.e_ident[3] = ELFMAG3;
94 ehdr.e_ident[EI_CLASS] = ELF_CLASS;
95 ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
96 ehdr.e_ident[EI_VERSION] = EV_CURRENT;
97 ehdr.e_ident[EI_OSABI] = ELFOSABI_LINUX;
98
99 ehdr.e_type = ET_CORE;
100 ehdr.e_machine = ELF_ARCH;
101 ehdr.e_version = EV_CURRENT;
102 ehdr.e_shoff = sizeof(Ehdr);
103 ehdr.e_phoff = sizeof(Ehdr) + sizeof(Shdr);
104
105 ehdr.e_ehsize = sizeof(Ehdr);
106 ehdr.e_phentsize = sizeof(Phdr);
107 ehdr.e_shentsize = sizeof(Shdr);
108
109 // The ELF header only allows UINT16 for the number of program
110 // headers. In a core dump this equates to PT_NODE and PT_LOAD.
111 //
112 // When more program headers than 65534 the first section entry
113 // is used to store the actual program header count.
114
115 // PT_NOTE + number of memory regions
116 uint64_t phnum = 1 + m_crashInfo.MemoryRegions().size();
117
118 if (phnum < PH_HDR_CANARY) {
119 ehdr.e_phnum = phnum;
120 }
121 else {
122 ehdr.e_phnum = PH_HDR_CANARY;
123 }
124
125 if (!WriteData(&ehdr, sizeof(Ehdr))) {
126 return false;
127 }
128
129 size_t offset = sizeof(Ehdr) + sizeof(Shdr) + (phnum * sizeof(Phdr));
130 size_t filesz = GetProcessInfoSize() + GetAuxvInfoSize() + GetThreadInfoSize() + GetNTFileInfoSize();
131
132 // Add single section containing the actual count
133 // of the program headers to be written.
134 Shdr shdr;
135 memset(&shdr, 0, sizeof(shdr));
136 shdr.sh_info = phnum;
137 // When section header offset is present but ehdr section num = 0
138 // then is is expected that the sh_size indicates the size of the
139 // section array or 1 in our case.
140 shdr.sh_size = 1;
141 if (!WriteData(&shdr, sizeof(shdr))) {
142 return false;
143 }
144
145 // PT_NOTE header
146 Phdr phdr;
147 memset(&phdr, 0, sizeof(Phdr));
148 phdr.p_type = PT_NOTE;
149 phdr.p_offset = offset;
150 phdr.p_filesz = filesz;
151
152 if (!WriteData(&phdr, sizeof(phdr))) {
153 return false;
154 }
155
156 // PT_NOTE sections must end on 4 byte boundary
157 // We output the NT_FILE, AUX and Thread entries
158 // AUX is aligned, NT_FILE is aligned and then we
159 // check to pad end of the thread list
160 phdr.p_type = PT_LOAD;
161 phdr.p_align = 4096;
162
163 size_t finalNoteAlignment = phdr.p_align - ((offset + filesz) % phdr.p_align);
164 if (finalNoteAlignment == phdr.p_align) {
165 finalNoteAlignment = 0;
166 }
167 offset += finalNoteAlignment;
168
169 TRACE("Writing memory region headers to core file\n");
170
171 // Write memory region note headers
172 for (const MemoryRegion& memoryRegion : m_crashInfo.MemoryRegions())
173 {
174 phdr.p_flags = memoryRegion.Permissions();
175 phdr.p_vaddr = memoryRegion.StartAddress();
176 phdr.p_memsz = memoryRegion.Size();
177
178 if (memoryRegion.IsBackedByMemory())
179 {
180 offset += filesz;
181 phdr.p_filesz = filesz = memoryRegion.Size();
182 phdr.p_offset = offset;
183 }
184 else
185 {
186 phdr.p_filesz = 0;
187 phdr.p_offset = 0;
188 }
189
190 if (!WriteData(&phdr, sizeof(phdr))) {
191 return false;
192 }
193 }
194
195 // Write process info data to core file
196 if (!WriteProcessInfo()) {
197 return false;
198 }
199
200 // Write auxv data to core file
201 if (!WriteAuxv()) {
202 return false;
203 }
204
205 // Write NT_FILE entries to the core file
206 if (!WriteNTFileInfo()) {
207 return false;
208 }
209
210 TRACE("Writing %zd thread entries to core file\n", m_crashInfo.Threads().size());
211
212 // Write all the thread's state and registers
213 for (const ThreadInfo* thread : m_crashInfo.Threads())
214 {
215 if (!WriteThread(*thread, SIGABRT)) {
216 return false;
217 }
218 }
219
220 // Zero out the end of the PT_NOTE section to the boundary
221 // and then laydown the memory blocks
222 if (finalNoteAlignment > 0) {
223 assert(finalNoteAlignment < sizeof(m_tempBuffer));
224 memset(m_tempBuffer, 0, finalNoteAlignment);
225 if (!WriteData(m_tempBuffer, finalNoteAlignment)) {
226 return false;
227 }
228 }
229
230 TRACE("Writing %zd memory regions to core file\n", m_crashInfo.MemoryRegions().size());
231
232 // Read from target process and write memory regions to core
233 uint64_t total = 0;
234 for (const MemoryRegion& memoryRegion : m_crashInfo.MemoryRegions())
235 {
236 // Only write the regions that are backed by memory
237 if (memoryRegion.IsBackedByMemory())
238 {
239 uint32_t size = memoryRegion.Size();
240 uint64_t address = memoryRegion.StartAddress();
241 total += size;
242
243 while (size > 0)
244 {
245 uint32_t bytesToRead = std::min(size, (uint32_t)sizeof(m_tempBuffer));
246 uint32_t read = 0;
247
248 if (FAILED(m_crashInfo.DataTarget()->ReadVirtual(address, m_tempBuffer, bytesToRead, &read))) {
249 fprintf(stderr, "ReadVirtual(%" PRIA PRIx64 ", %08x) FAILED\n", address, bytesToRead);
250 return false;
251 }
252
253 if (!WriteData(m_tempBuffer, read)) {
254 return false;
255 }
256
257 address += read;
258 size -= read;
259 }
260 }
261 }
262
263 printf("Written %" PRId64 " bytes (%" PRId64 " pages) to core file\n", total, total / PAGE_SIZE);
264
265 return true;
266}
267
268bool
269DumpWriter::WriteProcessInfo()
270{
271 prpsinfo_t processInfo;
272 memset(&processInfo, 0, sizeof(processInfo));
273 processInfo.pr_sname = 'R';
274 processInfo.pr_pid = m_crashInfo.Pid();
275 processInfo.pr_ppid = m_crashInfo.Ppid();
276 processInfo.pr_pgrp = m_crashInfo.Tgid();
277 strcpy_s(processInfo.pr_fname, sizeof(processInfo.pr_fname), m_crashInfo.Name());
278
279 Nhdr nhdr;
280 memset(&nhdr, 0, sizeof(nhdr));
281 nhdr.n_namesz = 5;
282 nhdr.n_descsz = sizeof(prpsinfo_t);
283 nhdr.n_type = NT_PRPSINFO;
284
285 TRACE("Writing process information to core file\n");
286
287 // Write process info data to core file
288 if (!WriteData(&nhdr, sizeof(nhdr)) ||
289 !WriteData("CORE\0PRP", 8) ||
290 !WriteData(&processInfo, sizeof(prpsinfo_t))) {
291 return false;
292 }
293 return true;
294}
295
296bool
297DumpWriter::WriteAuxv()
298{
299 Nhdr nhdr;
300 memset(&nhdr, 0, sizeof(nhdr));
301 nhdr.n_namesz = 5;
302 nhdr.n_descsz = m_crashInfo.GetAuxvSize();
303 nhdr.n_type = NT_AUXV;
304
305 TRACE("Writing %zd auxv entries to core file\n", m_crashInfo.AuxvEntries().size());
306
307 if (!WriteData(&nhdr, sizeof(nhdr)) ||
308 !WriteData("CORE\0AUX", 8)) {
309 return false;
310 }
311 for (const auto& auxvEntry : m_crashInfo.AuxvEntries())
312 {
313 if (!WriteData(&auxvEntry, sizeof(auxvEntry))) {
314 return false;
315 }
316 }
317 return true;
318}
319
320struct NTFileEntry
321{
322 unsigned long StartAddress;
323 unsigned long EndAddress;
324 unsigned long Offset;
325};
326
327// Calculate the NT_FILE entries total size
328size_t
329DumpWriter::GetNTFileInfoSize(size_t* alignmentBytes)
330{
331 size_t count = m_crashInfo.ModuleMappings().size();
332 size_t size = 0;
333
334 // Header, CORE, entry count, page size
335 size = sizeof(Nhdr) + 8 + sizeof(count) + sizeof(size);
336
337 // start_address, end_address, offset
338 size += count * sizeof(NTFileEntry);
339
340 // \0 terminator for each filename
341 size += count;
342
343 // File name storage needed
344 for (const MemoryRegion& image : m_crashInfo.ModuleMappings()) {
345 size += strlen(image.FileName());
346 }
347 // Notes must end on 4 byte alignment
348 size_t alignmentBytesNeeded = 4 - (size % 4);
349 size += alignmentBytesNeeded;
350
351 if (alignmentBytes != nullptr) {
352 *alignmentBytes = alignmentBytesNeeded;
353 }
354 return size;
355}
356
357// Write NT_FILE entries to the PT_NODE section
358//
359// Nhdr (NT_FILE)
360// Total entries
361// Page size
362// [0] start_address end_address offset
363// [1] start_address end_address offset
364// [file name]\0[file name]\0...
365bool
366DumpWriter::WriteNTFileInfo()
367{
368 Nhdr nhdr;
369 memset(&nhdr, 0, sizeof(nhdr));
370
371 // CORE + \0 and we align on 4 byte boundary
372 // so we can use CORE\0FIL for easier hex debugging
373 nhdr.n_namesz = 5;
374 nhdr.n_type = NT_FILE; // "FILE"
375
376 // Size of payload for NT_FILE after CORE tag written
377 size_t alignmentBytesNeeded = 0;
378 nhdr.n_descsz = GetNTFileInfoSize(&alignmentBytesNeeded) - sizeof(nhdr) - 8;
379
380 size_t count = m_crashInfo.ModuleMappings().size();
381 size_t pageSize = PAGE_SIZE;
382
383 TRACE("Writing %zd NT_FILE entries to core file\n", m_crashInfo.ModuleMappings().size());
384
385 if (!WriteData(&nhdr, sizeof(nhdr)) ||
386 !WriteData("CORE\0FIL", 8) ||
387 !WriteData(&count, sizeof(count)) ||
388 !WriteData(&pageSize, sizeof(pageSize))) {
389 return false;
390 }
391
392 for (const MemoryRegion& image : m_crashInfo.ModuleMappings())
393 {
394 struct NTFileEntry entry { image.StartAddress(), image.EndAddress(), image.Offset() };
395 if (!WriteData(&entry, sizeof(entry))) {
396 return false;
397 }
398 }
399
400 for (const MemoryRegion& image : m_crashInfo.ModuleMappings())
401 {
402 if (!WriteData(image.FileName(), strlen(image.FileName())) ||
403 !WriteData("\0", 1)) {
404 return false;
405 }
406 }
407
408 // Has to end on a 4 byte boundary. Debugger, readelf and such
409 // will automatically align on next 4 bytes and look for a PT_NOTE
410 // header.
411 if (alignmentBytesNeeded) {
412 if (!WriteData("\0\0\0\0", alignmentBytesNeeded)) {
413 return false;
414 }
415 }
416
417 return true;
418}
419
420bool
421DumpWriter::WriteThread(const ThreadInfo& thread, int fatal_signal)
422{
423 prstatus_t pr;
424 memset(&pr, 0, sizeof(pr));
425
426 pr.pr_info.si_signo = fatal_signal;
427 pr.pr_cursig = fatal_signal;
428 pr.pr_pid = thread.Tid();
429 pr.pr_ppid = thread.Ppid();
430 pr.pr_pgrp = thread.Tgid();
431 memcpy(&pr.pr_reg, thread.GPRegisters(), sizeof(user_regs_struct));
432
433 Nhdr nhdr;
434 memset(&nhdr, 0, sizeof(nhdr));
435
436 // Name size is CORE plus the NULL terminator
437 // The format requires 4 byte alignment so the
438 // value written in 8 bytes. Stuff the last 3
439 // bytes with the type of NT_PRSTATUS so it is
440 // easier to debug in a hex editor.
441 nhdr.n_namesz = 5;
442 nhdr.n_descsz = sizeof(prstatus_t);
443 nhdr.n_type = NT_PRSTATUS;
444 if (!WriteData(&nhdr, sizeof(nhdr)) ||
445 !WriteData("CORE\0THR", 8) ||
446 !WriteData(&pr, sizeof(prstatus_t))) {
447 return false;
448 }
449
450#if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
451 nhdr.n_descsz = sizeof(user_fpregs_struct);
452 nhdr.n_type = NT_FPREGSET;
453 if (!WriteData(&nhdr, sizeof(nhdr)) ||
454 !WriteData("CORE\0FLT", 8) ||
455 !WriteData(thread.FPRegisters(), sizeof(user_fpregs_struct))) {
456 return false;
457 }
458#endif
459
460 nhdr.n_namesz = 6;
461
462#if defined(__i386__)
463 nhdr.n_descsz = sizeof(user_fpxregs_struct);
464 nhdr.n_type = NT_PRXFPREG;
465 if (!WriteData(&nhdr, sizeof(nhdr)) ||
466 !WriteData("LINUX\0\0\0", 8) ||
467 !WriteData(thread.FPXRegisters(), sizeof(user_fpxregs_struct))) {
468 return false;
469 }
470#endif
471
472#if defined(__arm__) && defined(__VFP_FP__) && !defined(__SOFTFP__)
473 nhdr.n_descsz = sizeof(user_vfpregs_struct);
474 nhdr.n_type = NT_ARM_VFP;
475 if (!WriteData(&nhdr, sizeof(nhdr)) ||
476 !WriteData("LINUX\0\0\0", 8) ||
477 !WriteData(thread.VFPRegisters(), sizeof(user_vfpregs_struct))) {
478 return false;
479 }
480#endif
481
482 return true;
483}
484
485// Write all of the given buffer, handling short writes and EINTR. Return true iff successful.
486bool
487DumpWriter::WriteData(const void* buffer, size_t length)
488{
489 const uint8_t* data = (const uint8_t*)buffer;
490
491 size_t done = 0;
492 while (done < length) {
493 ssize_t written;
494 do {
495 written = write(m_fd, data + done, length - done);
496 } while (written == -1 && errno == EINTR);
497
498 if (written < 1) {
499 fprintf(stderr, "WriteData FAILED %s\n", strerror(errno));
500 return false;
501 }
502 done += written;
503 }
504 return true;
505}
506