minidump_writer.cc revision f66ff1afd2e0f7f16ac2f8d40984cf799f3f099b
1// Copyright (c) 2010, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14//     * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30// This code writes out minidump files:
31//   http://msdn.microsoft.com/en-us/library/ms680378(VS.85,loband).aspx
32//
33// Minidumps are a Microsoft format which Breakpad uses for recording crash
34// dumps. This code has to run in a compromised environment (the address space
35// may have received SIGSEGV), thus the following rules apply:
36//   * You may not enter the dynamic linker. This means that we cannot call
37//     any symbols in a shared library (inc libc). Because of this we replace
38//     libc functions in linux_libc_support.h.
39//   * You may not call syscalls via the libc wrappers. This rule is a subset
40//     of the first rule but it bears repeating. We have direct wrappers
41//     around the system calls in linux_syscall_support.h.
42//   * You may not malloc. There's an alternative allocator in memory.h and
43//     a canonical instance in the LinuxDumper object. We use the placement
44//     new form to allocate objects and we don't delete them.
45
46#include "client/linux/minidump_writer/minidump_writer.h"
47#include "client/minidump_file_writer-inl.h"
48
49#include <errno.h>
50#include <fcntl.h>
51#if !defined(__ANDROID__)
52#include <link.h>
53#endif
54#include <stdio.h>
55#include <unistd.h>
56#if !defined(__ANDROID__)
57#include <sys/ucontext.h>
58#include <sys/user.h>
59#endif
60#include <sys/utsname.h>
61
62#include <algorithm>
63
64#include "client/minidump_file_writer.h"
65#include "google_breakpad/common/minidump_format.h"
66#include "google_breakpad/common/minidump_cpu_amd64.h"
67#include "google_breakpad/common/minidump_cpu_x86.h"
68
69#if defined(__ANDROID__)
70#include "client/linux/android_link.h"
71#include "client/linux/android_ucontext.h"
72#endif
73#include "client/linux/handler/exception_handler.h"
74#include "client/linux/minidump_writer/line_reader.h"
75#include "client/linux/minidump_writer/linux_dumper.h"
76#include "client/linux/minidump_writer/minidump_extension_linux.h"
77#include "common/linux/linux_libc_support.h"
78#include "third_party/lss/linux_syscall_support.h"
79
80// Minidump defines register structures which are different from the raw
81// structures which we get from the kernel. These are platform specific
82// functions to juggle the ucontext and user structures into minidump format.
83#if defined(__i386)
84typedef MDRawContextX86 RawContextCPU;
85
86// Write a uint16_t to memory
87//   out: memory location to write to
88//   v: value to write.
89static void U16(void* out, uint16_t v) {
90  memcpy(out, &v, sizeof(v));
91}
92
93// Write a uint32_t to memory
94//   out: memory location to write to
95//   v: value to write.
96static void U32(void* out, uint32_t v) {
97  memcpy(out, &v, sizeof(v));
98}
99
100// Juggle an x86 user_(fp|fpx|)regs_struct into minidump format
101//   out: the minidump structure
102//   info: the collection of register structures.
103static void CPUFillFromThreadInfo(MDRawContextX86 *out,
104                                  const google_breakpad::ThreadInfo &info) {
105  out->context_flags = MD_CONTEXT_X86_ALL;
106
107  out->dr0 = info.dregs[0];
108  out->dr1 = info.dregs[1];
109  out->dr2 = info.dregs[2];
110  out->dr3 = info.dregs[3];
111  // 4 and 5 deliberatly omitted because they aren't included in the minidump
112  // format.
113  out->dr6 = info.dregs[6];
114  out->dr7 = info.dregs[7];
115
116  out->gs = info.regs.xgs;
117  out->fs = info.regs.xfs;
118  out->es = info.regs.xes;
119  out->ds = info.regs.xds;
120
121  out->edi = info.regs.edi;
122  out->esi = info.regs.esi;
123  out->ebx = info.regs.ebx;
124  out->edx = info.regs.edx;
125  out->ecx = info.regs.ecx;
126  out->eax = info.regs.eax;
127
128  out->ebp = info.regs.ebp;
129  out->eip = info.regs.eip;
130  out->cs = info.regs.xcs;
131  out->eflags = info.regs.eflags;
132  out->esp = info.regs.esp;
133  out->ss = info.regs.xss;
134
135  out->float_save.control_word = info.fpregs.cwd;
136  out->float_save.status_word = info.fpregs.swd;
137  out->float_save.tag_word = info.fpregs.twd;
138  out->float_save.error_offset = info.fpregs.fip;
139  out->float_save.error_selector = info.fpregs.fcs;
140  out->float_save.data_offset = info.fpregs.foo;
141  out->float_save.data_selector = info.fpregs.fos;
142
143  // 8 registers * 10 bytes per register.
144  memcpy(out->float_save.register_area, info.fpregs.st_space, 10 * 8);
145
146  // This matches the Intel fpsave format.
147  U16(out->extended_registers + 0, info.fpregs.cwd);
148  U16(out->extended_registers + 2, info.fpregs.swd);
149  U16(out->extended_registers + 4, info.fpregs.twd);
150  U16(out->extended_registers + 6, info.fpxregs.fop);
151  U32(out->extended_registers + 8, info.fpxregs.fip);
152  U16(out->extended_registers + 12, info.fpxregs.fcs);
153  U32(out->extended_registers + 16, info.fpregs.foo);
154  U16(out->extended_registers + 20, info.fpregs.fos);
155  U32(out->extended_registers + 24, info.fpxregs.mxcsr);
156
157  memcpy(out->extended_registers + 32, &info.fpxregs.st_space, 128);
158  memcpy(out->extended_registers + 160, &info.fpxregs.xmm_space, 128);
159}
160
161// Juggle an x86 ucontext into minidump format
162//   out: the minidump structure
163//   info: the collection of register structures.
164static void CPUFillFromUContext(MDRawContextX86 *out, const ucontext *uc,
165                                const struct _libc_fpstate* fp) {
166  const greg_t* regs = uc->uc_mcontext.gregs;
167
168  out->context_flags = MD_CONTEXT_X86_FULL |
169                       MD_CONTEXT_X86_FLOATING_POINT;
170
171  out->gs = regs[REG_GS];
172  out->fs = regs[REG_FS];
173  out->es = regs[REG_ES];
174  out->ds = regs[REG_DS];
175
176  out->edi = regs[REG_EDI];
177  out->esi = regs[REG_ESI];
178  out->ebx = regs[REG_EBX];
179  out->edx = regs[REG_EDX];
180  out->ecx = regs[REG_ECX];
181  out->eax = regs[REG_EAX];
182
183  out->ebp = regs[REG_EBP];
184  out->eip = regs[REG_EIP];
185  out->cs = regs[REG_CS];
186  out->eflags = regs[REG_EFL];
187  out->esp = regs[REG_UESP];
188  out->ss = regs[REG_SS];
189
190  out->float_save.control_word = fp->cw;
191  out->float_save.status_word = fp->sw;
192  out->float_save.tag_word = fp->tag;
193  out->float_save.error_offset = fp->ipoff;
194  out->float_save.error_selector = fp->cssel;
195  out->float_save.data_offset = fp->dataoff;
196  out->float_save.data_selector = fp->datasel;
197
198  // 8 registers * 10 bytes per register.
199  memcpy(out->float_save.register_area, fp->_st, 10 * 8);
200}
201
202#elif defined(__x86_64)
203typedef MDRawContextAMD64 RawContextCPU;
204
205static void CPUFillFromThreadInfo(MDRawContextAMD64 *out,
206                                  const google_breakpad::ThreadInfo &info) {
207  out->context_flags = MD_CONTEXT_AMD64_FULL |
208                       MD_CONTEXT_AMD64_SEGMENTS;
209
210  out->cs = info.regs.cs;
211
212  out->ds = info.regs.ds;
213  out->es = info.regs.es;
214  out->fs = info.regs.fs;
215  out->gs = info.regs.gs;
216
217  out->ss = info.regs.ss;
218  out->eflags = info.regs.eflags;
219
220  out->dr0 = info.dregs[0];
221  out->dr1 = info.dregs[1];
222  out->dr2 = info.dregs[2];
223  out->dr3 = info.dregs[3];
224  // 4 and 5 deliberatly omitted because they aren't included in the minidump
225  // format.
226  out->dr6 = info.dregs[6];
227  out->dr7 = info.dregs[7];
228
229  out->rax = info.regs.rax;
230  out->rcx = info.regs.rcx;
231  out->rdx = info.regs.rdx;
232  out->rbx = info.regs.rbx;
233
234  out->rsp = info.regs.rsp;
235
236  out->rbp = info.regs.rbp;
237  out->rsi = info.regs.rsi;
238  out->rdi = info.regs.rdi;
239  out->r8 = info.regs.r8;
240  out->r9 = info.regs.r9;
241  out->r10 = info.regs.r10;
242  out->r11 = info.regs.r11;
243  out->r12 = info.regs.r12;
244  out->r13 = info.regs.r13;
245  out->r14 = info.regs.r14;
246  out->r15 = info.regs.r15;
247
248  out->rip = info.regs.rip;
249
250  out->flt_save.control_word = info.fpregs.cwd;
251  out->flt_save.status_word = info.fpregs.swd;
252  out->flt_save.tag_word = info.fpregs.ftw;
253  out->flt_save.error_opcode = info.fpregs.fop;
254  out->flt_save.error_offset = info.fpregs.rip;
255  out->flt_save.error_selector = 0;  // We don't have this.
256  out->flt_save.data_offset = info.fpregs.rdp;
257  out->flt_save.data_selector = 0;   // We don't have this.
258  out->flt_save.mx_csr = info.fpregs.mxcsr;
259  out->flt_save.mx_csr_mask = info.fpregs.mxcr_mask;
260  memcpy(&out->flt_save.float_registers, &info.fpregs.st_space, 8 * 16);
261  memcpy(&out->flt_save.xmm_registers, &info.fpregs.xmm_space, 16 * 16);
262}
263
264static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc,
265                                const struct _libc_fpstate* fpregs) {
266  const greg_t* regs = uc->uc_mcontext.gregs;
267
268  out->context_flags = MD_CONTEXT_AMD64_FULL;
269
270  out->cs = regs[REG_CSGSFS] & 0xffff;
271
272  out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff;
273  out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff;
274
275  out->eflags = regs[REG_EFL];
276
277  out->rax = regs[REG_RAX];
278  out->rcx = regs[REG_RCX];
279  out->rdx = regs[REG_RDX];
280  out->rbx = regs[REG_RBX];
281
282  out->rsp = regs[REG_RSP];
283  out->rbp = regs[REG_RBP];
284  out->rsi = regs[REG_RSI];
285  out->rdi = regs[REG_RDI];
286  out->r8 = regs[REG_R8];
287  out->r9 = regs[REG_R9];
288  out->r10 = regs[REG_R10];
289  out->r11 = regs[REG_R11];
290  out->r12 = regs[REG_R12];
291  out->r13 = regs[REG_R13];
292  out->r14 = regs[REG_R14];
293  out->r15 = regs[REG_R15];
294
295  out->rip = regs[REG_RIP];
296
297  out->flt_save.control_word = fpregs->cwd;
298  out->flt_save.status_word = fpregs->swd;
299  out->flt_save.tag_word = fpregs->ftw;
300  out->flt_save.error_opcode = fpregs->fop;
301  out->flt_save.error_offset = fpregs->rip;
302  out->flt_save.data_offset = fpregs->rdp;
303  out->flt_save.error_selector = 0;  // We don't have this.
304  out->flt_save.data_selector = 0;  // We don't have this.
305  out->flt_save.mx_csr = fpregs->mxcsr;
306  out->flt_save.mx_csr_mask = fpregs->mxcr_mask;
307  memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16);
308  memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
309}
310
311#elif defined(__ARMEL__)
312typedef MDRawContextARM RawContextCPU;
313
314static void CPUFillFromThreadInfo(MDRawContextARM *out,
315                                  const google_breakpad::ThreadInfo &info) {
316  out->context_flags = MD_CONTEXT_ARM_FULL;
317
318  for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i)
319    out->iregs[i] = info.regs.uregs[i];
320  // No CPSR register in ThreadInfo(it's not accessible via ptrace)
321  out->cpsr = 0;
322#if !defined(__ANDROID__)
323  out->float_save.fpscr = info.fpregs.fpsr |
324    (static_cast<u_int64_t>(info.fpregs.fpcr) << 32);
325  // TODO: sort this out, actually collect floating point registers
326  memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
327  memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
328#endif
329}
330
331static void CPUFillFromUContext(MDRawContextARM *out, const ucontext *uc,
332                                const struct _libc_fpstate* fpregs) {
333  out->context_flags = MD_CONTEXT_ARM_FULL;
334
335  out->iregs[0] = uc->uc_mcontext.arm_r0;
336  out->iregs[1] = uc->uc_mcontext.arm_r1;
337  out->iregs[2] = uc->uc_mcontext.arm_r2;
338  out->iregs[3] = uc->uc_mcontext.arm_r3;
339  out->iregs[4] = uc->uc_mcontext.arm_r4;
340  out->iregs[5] = uc->uc_mcontext.arm_r5;
341  out->iregs[6] = uc->uc_mcontext.arm_r6;
342  out->iregs[7] = uc->uc_mcontext.arm_r7;
343  out->iregs[8] = uc->uc_mcontext.arm_r8;
344  out->iregs[9] = uc->uc_mcontext.arm_r9;
345  out->iregs[10] = uc->uc_mcontext.arm_r10;
346
347  out->iregs[11] = uc->uc_mcontext.arm_fp;
348  out->iregs[12] = uc->uc_mcontext.arm_ip;
349  out->iregs[13] = uc->uc_mcontext.arm_sp;
350  out->iregs[14] = uc->uc_mcontext.arm_lr;
351  out->iregs[15] = uc->uc_mcontext.arm_pc;
352
353  out->cpsr = uc->uc_mcontext.arm_cpsr;
354
355  // TODO: fix this after fixing ExceptionHandler
356  out->float_save.fpscr = 0;
357  memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
358  memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
359}
360
361#else
362#error "This code has not been ported to your platform yet."
363#endif
364
365namespace google_breakpad {
366
367class MinidumpWriter {
368 public:
369  MinidumpWriter(const char* filename,
370                 pid_t crashing_pid,
371                 const ExceptionHandler::CrashContext* context)
372      : filename_(filename),
373        siginfo_(&context->siginfo),
374        ucontext_(&context->context),
375#if !defined(__ARM_EABI__)
376        float_state_(&context->float_state),
377#else
378        // TODO: fix this after fixing ExceptionHandler
379        float_state_(NULL),
380#endif
381        crashing_tid_(context->tid),
382        dumper_(crashing_pid),
383        memory_blocks_(dumper_.allocator()) {
384  }
385
386  bool Init() {
387    return dumper_.Init() && minidump_writer_.Open(filename_) &&
388           dumper_.ThreadsSuspend();
389  }
390
391  ~MinidumpWriter() {
392    minidump_writer_.Close();
393    dumper_.ThreadsResume();
394  }
395
396  bool Dump() {
397    // The dynamic linker makes information available that helps gdb find all
398    // DSOs loaded into the program. If we can access this information, we dump
399    // it to a MD_LINUX_DSO_DEBUG stream.
400    struct r_debug* r_debug = NULL;
401    uint32_t dynamic_length = 0;
402#if !defined(__ANDROID__)
403    // The Android NDK is missing structure definitions for most of this.
404    // For now, it's simpler just to skip it.
405    for (int i = 0;;) {
406      ElfW(Dyn) dyn;
407      dynamic_length += sizeof(dyn);
408      dumper_.CopyFromProcess(&dyn, crashing_tid_, _DYNAMIC+i++, sizeof(dyn));
409      if (dyn.d_tag == DT_DEBUG) {
410        r_debug = (struct r_debug*)dyn.d_un.d_ptr;
411        continue;
412      } else if (dyn.d_tag == DT_NULL) {
413        break;
414      }
415    }
416#endif
417
418    // A minidump file contains a number of tagged streams. This is the number
419    // of stream which we write.
420    unsigned kNumWriters = 12;
421    if (r_debug)
422      ++kNumWriters;
423
424    TypedMDRVA<MDRawHeader> header(&minidump_writer_);
425    TypedMDRVA<MDRawDirectory> dir(&minidump_writer_);
426    if (!header.Allocate())
427      return false;
428    if (!dir.AllocateArray(kNumWriters))
429      return false;
430    memset(header.get(), 0, sizeof(MDRawHeader));
431
432    header.get()->signature = MD_HEADER_SIGNATURE;
433    header.get()->version = MD_HEADER_VERSION;
434    header.get()->time_date_stamp = time(NULL);
435    header.get()->stream_count = kNumWriters;
436    header.get()->stream_directory_rva = dir.position();
437
438    unsigned dir_index = 0;
439    MDRawDirectory dirent;
440
441    if (!WriteThreadListStream(&dirent))
442      return false;
443    dir.CopyIndex(dir_index++, &dirent);
444
445    if (!WriteMappings(&dirent))
446      return false;
447    dir.CopyIndex(dir_index++, &dirent);
448
449    if (!WriteMemoryListStream(&dirent))
450      return false;
451    dir.CopyIndex(dir_index++, &dirent);
452
453    if (!WriteExceptionStream(&dirent))
454      return false;
455    dir.CopyIndex(dir_index++, &dirent);
456
457    if (!WriteSystemInfoStream(&dirent))
458      return false;
459    dir.CopyIndex(dir_index++, &dirent);
460
461    dirent.stream_type = MD_LINUX_CPU_INFO;
462    if (!WriteFile(&dirent.location, "/proc/cpuinfo"))
463      NullifyDirectoryEntry(&dirent);
464    dir.CopyIndex(dir_index++, &dirent);
465
466    dirent.stream_type = MD_LINUX_PROC_STATUS;
467    if (!WriteProcFile(&dirent.location, crashing_tid_, "status"))
468      NullifyDirectoryEntry(&dirent);
469    dir.CopyIndex(dir_index++, &dirent);
470
471    dirent.stream_type = MD_LINUX_LSB_RELEASE;
472    if (!WriteFile(&dirent.location, "/etc/lsb-release"))
473      NullifyDirectoryEntry(&dirent);
474    dir.CopyIndex(dir_index++, &dirent);
475
476    dirent.stream_type = MD_LINUX_CMD_LINE;
477    if (!WriteProcFile(&dirent.location, crashing_tid_, "cmdline"))
478      NullifyDirectoryEntry(&dirent);
479    dir.CopyIndex(dir_index++, &dirent);
480
481    dirent.stream_type = MD_LINUX_ENVIRON;
482    if (!WriteProcFile(&dirent.location, crashing_tid_, "environ"))
483      NullifyDirectoryEntry(&dirent);
484    dir.CopyIndex(dir_index++, &dirent);
485
486    dirent.stream_type = MD_LINUX_AUXV;
487    if (!WriteProcFile(&dirent.location, crashing_tid_, "auxv"))
488      NullifyDirectoryEntry(&dirent);
489    dir.CopyIndex(dir_index++, &dirent);
490
491    dirent.stream_type = MD_LINUX_MAPS;
492    if (!WriteProcFile(&dirent.location, crashing_tid_, "maps"))
493      NullifyDirectoryEntry(&dirent);
494    dir.CopyIndex(dir_index++, &dirent);
495
496    if (r_debug) {
497      dirent.stream_type = MD_LINUX_DSO_DEBUG;
498      if (!WriteDSODebugStream(&dirent, r_debug, dynamic_length))
499        NullifyDirectoryEntry(&dirent);
500      dir.CopyIndex(dir_index++, &dirent);
501    }
502
503    // If you add more directory entries, don't forget to update kNumWriters,
504    // above.
505
506    dumper_.ThreadsResume();
507    return true;
508  }
509
510  // Check if the top of the stack is part of a system call that has been
511  // redirected by the seccomp sandbox. If so, try to pop the stack frames
512  // all the way back to the point where the interception happened.
513  void PopSeccompStackFrame(RawContextCPU* cpu, const MDRawThread& thread,
514                            uint8_t* stack_copy) {
515#if defined(__x86_64)
516    u_int64_t bp = cpu->rbp;
517    u_int64_t top = thread.stack.start_of_memory_range;
518    for (int i = 4; i--; ) {
519      if (bp < top ||
520          bp + sizeof(bp) > thread.stack.start_of_memory_range +
521          thread.stack.memory.data_size ||
522          bp & 1) {
523        break;
524      }
525      uint64_t old_top = top;
526      top = bp;
527      u_int8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
528      memcpy(&bp, bp_addr, sizeof(bp));
529      if (bp == 0xDEADBEEFDEADBEEFull) {
530        struct {
531          uint64_t r15;
532          uint64_t r14;
533          uint64_t r13;
534          uint64_t r12;
535          uint64_t r11;
536          uint64_t r10;
537          uint64_t r9;
538          uint64_t r8;
539          uint64_t rdi;
540          uint64_t rsi;
541          uint64_t rdx;
542          uint64_t rcx;
543          uint64_t rbx;
544          uint64_t deadbeef;
545          uint64_t rbp;
546          uint64_t fakeret;
547          uint64_t ret;
548          /* char redzone[128]; */
549        } seccomp_stackframe;
550        if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
551            top - offsetof(typeof(seccomp_stackframe), deadbeef) +
552            sizeof(seccomp_stackframe) >
553            thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
554          break;
555        }
556        memcpy(&seccomp_stackframe,
557               bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
558               sizeof(seccomp_stackframe));
559        cpu->rbx = seccomp_stackframe.rbx;
560        cpu->rcx = seccomp_stackframe.rcx;
561        cpu->rdx = seccomp_stackframe.rdx;
562        cpu->rsi = seccomp_stackframe.rsi;
563        cpu->rdi = seccomp_stackframe.rdi;
564        cpu->rbp = seccomp_stackframe.rbp;
565        cpu->rsp = top + 4*sizeof(uint64_t) + 128;
566        cpu->r8  = seccomp_stackframe.r8;
567        cpu->r9  = seccomp_stackframe.r9;
568        cpu->r10 = seccomp_stackframe.r10;
569        cpu->r11 = seccomp_stackframe.r11;
570        cpu->r12 = seccomp_stackframe.r12;
571        cpu->r13 = seccomp_stackframe.r13;
572        cpu->r14 = seccomp_stackframe.r14;
573        cpu->r15 = seccomp_stackframe.r15;
574        cpu->rip = seccomp_stackframe.fakeret;
575        return;
576      }
577    }
578#elif defined(__i386)
579    u_int32_t bp = cpu->ebp;
580    u_int32_t top = thread.stack.start_of_memory_range;
581    for (int i = 4; i--; ) {
582      if (bp < top ||
583          bp + sizeof(bp) > thread.stack.start_of_memory_range +
584          thread.stack.memory.data_size ||
585          bp & 1) {
586        break;
587      }
588      uint32_t old_top = top;
589      top = bp;
590      u_int8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
591      memcpy(&bp, bp_addr, sizeof(bp));
592      if (bp == 0xDEADBEEFu) {
593        struct {
594          uint32_t edi;
595          uint32_t esi;
596          uint32_t edx;
597          uint32_t ecx;
598          uint32_t ebx;
599          uint32_t deadbeef;
600          uint32_t ebp;
601          uint32_t fakeret;
602          uint32_t ret;
603        } seccomp_stackframe;
604        if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
605            top - offsetof(typeof(seccomp_stackframe), deadbeef) +
606            sizeof(seccomp_stackframe) >
607            thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
608          break;
609        }
610        memcpy(&seccomp_stackframe,
611               bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
612               sizeof(seccomp_stackframe));
613        cpu->ebx = seccomp_stackframe.ebx;
614        cpu->ecx = seccomp_stackframe.ecx;
615        cpu->edx = seccomp_stackframe.edx;
616        cpu->esi = seccomp_stackframe.esi;
617        cpu->edi = seccomp_stackframe.edi;
618        cpu->ebp = seccomp_stackframe.ebp;
619        cpu->esp = top + 4*sizeof(void*);
620        cpu->eip = seccomp_stackframe.fakeret;
621        return;
622      }
623    }
624#endif
625  }
626
627  // Write information about the threads.
628  bool WriteThreadListStream(MDRawDirectory* dirent) {
629    const unsigned num_threads = dumper_.threads().size();
630
631    TypedMDRVA<uint32_t> list(&minidump_writer_);
632    if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread)))
633      return false;
634
635    dirent->stream_type = MD_THREAD_LIST_STREAM;
636    dirent->location = list.location();
637
638    *list.get() = num_threads;
639
640    for (unsigned i = 0; i < num_threads; ++i) {
641      MDRawThread thread;
642      my_memset(&thread, 0, sizeof(thread));
643      thread.thread_id = dumper_.threads()[i];
644      // We have a different source of information for the crashing thread. If
645      // we used the actual state of the thread we would find it running in the
646      // signal handler with the alternative stack, which would be deeply
647      // unhelpful.
648      if ((pid_t)thread.thread_id == crashing_tid_) {
649        const void* stack;
650        size_t stack_len;
651        if (!dumper_.GetStackInfo(&stack, &stack_len, GetStackPointer()))
652          return false;
653        UntypedMDRVA memory(&minidump_writer_);
654        if (!memory.Allocate(stack_len))
655          return false;
656        uint8_t* stack_copy = (uint8_t*) dumper_.allocator()->Alloc(stack_len);
657        dumper_.CopyFromProcess(stack_copy, thread.thread_id, stack, stack_len);
658        memory.Copy(stack_copy, stack_len);
659        thread.stack.start_of_memory_range = (uintptr_t) (stack);
660        thread.stack.memory = memory.location();
661        memory_blocks_.push_back(thread.stack);
662
663        // Copy 256 bytes around crashing instruction pointer to minidump.
664        const size_t kIPMemorySize = 256;
665        u_int64_t ip = GetInstructionPointer();
666        // Bound it to the upper and lower bounds of the memory map
667        // it's contained within. If it's not in mapped memory,
668        // don't bother trying to write it.
669        bool ip_is_mapped = false;
670        MDMemoryDescriptor ip_memory_d;
671        for (unsigned i = 0; i < dumper_.mappings().size(); ++i) {
672          const MappingInfo& mapping = *dumper_.mappings()[i];
673          if (ip >= mapping.start_addr &&
674              ip < mapping.start_addr + mapping.size) {
675            ip_is_mapped = true;
676            // Try to get 128 bytes before and after the IP, but
677            // settle for whatever's available.
678            ip_memory_d.start_of_memory_range =
679              std::max(mapping.start_addr,
680                       uintptr_t(ip - (kIPMemorySize / 2)));
681            uintptr_t end_of_range =
682              std::min(uintptr_t(ip + (kIPMemorySize / 2)),
683                       uintptr_t(mapping.start_addr + mapping.size));
684            ip_memory_d.memory.data_size =
685              end_of_range - ip_memory_d.start_of_memory_range;
686            break;
687          }
688        }
689
690        if (ip_is_mapped) {
691          UntypedMDRVA ip_memory(&minidump_writer_);
692          if (!ip_memory.Allocate(ip_memory_d.memory.data_size))
693            return false;
694          uint8_t* memory_copy =
695            (uint8_t*) dumper_.allocator()->Alloc(ip_memory_d.memory.data_size);
696          dumper_.CopyFromProcess(
697            memory_copy,
698            thread.thread_id,
699            reinterpret_cast<void*>(ip_memory_d.start_of_memory_range),
700            ip_memory_d.memory.data_size);
701          ip_memory.Copy(memory_copy, ip_memory_d.memory.data_size);
702          ip_memory_d.memory = ip_memory.location();
703          memory_blocks_.push_back(ip_memory_d);
704        }
705
706        TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
707        if (!cpu.Allocate())
708          return false;
709        my_memset(cpu.get(), 0, sizeof(RawContextCPU));
710        CPUFillFromUContext(cpu.get(), ucontext_, float_state_);
711        PopSeccompStackFrame(cpu.get(), thread, stack_copy);
712        thread.thread_context = cpu.location();
713        crashing_thread_context_ = cpu.location();
714      } else {
715        ThreadInfo info;
716        if (!dumper_.ThreadInfoGet(dumper_.threads()[i], &info))
717          return false;
718        UntypedMDRVA memory(&minidump_writer_);
719        if (!memory.Allocate(info.stack_len))
720          return false;
721        uint8_t* stack_copy =
722            (uint8_t*) dumper_.allocator()->Alloc(info.stack_len);
723        dumper_.CopyFromProcess(stack_copy, thread.thread_id, info.stack,
724                                info.stack_len);
725        memory.Copy(stack_copy, info.stack_len);
726        thread.stack.start_of_memory_range = (uintptr_t)(info.stack);
727        thread.stack.memory = memory.location();
728        memory_blocks_.push_back(thread.stack);
729
730        TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
731        if (!cpu.Allocate())
732          return false;
733        my_memset(cpu.get(), 0, sizeof(RawContextCPU));
734        CPUFillFromThreadInfo(cpu.get(), info);
735        PopSeccompStackFrame(cpu.get(), thread, stack_copy);
736        thread.thread_context = cpu.location();
737      }
738
739      list.CopyIndexAfterObject(i, &thread, sizeof(thread));
740    }
741
742    return true;
743  }
744
745  static bool ShouldIncludeMapping(const MappingInfo& mapping) {
746    if (mapping.name[0] == 0 ||  // only want modules with filenames.
747        mapping.offset ||  // only want to include one mapping per shared lib.
748        mapping.size < 4096) {  // too small to get a signature for.
749      return false;
750    }
751
752    return true;
753  }
754
755  // Write information about the mappings in effect. Because we are using the
756  // minidump format, the information about the mappings is pretty limited.
757  // Because of this, we also include the full, unparsed, /proc/$x/maps file in
758  // another stream in the file.
759  bool WriteMappings(MDRawDirectory* dirent) {
760    const unsigned num_mappings = dumper_.mappings().size();
761    unsigned num_output_mappings = 0;
762
763    for (unsigned i = 0; i < dumper_.mappings().size(); ++i) {
764      const MappingInfo& mapping = *dumper_.mappings()[i];
765      if (ShouldIncludeMapping(mapping))
766        num_output_mappings++;
767    }
768
769    TypedMDRVA<uint32_t> list(&minidump_writer_);
770    if (!list.AllocateObjectAndArray(num_output_mappings, MD_MODULE_SIZE))
771      return false;
772
773    dirent->stream_type = MD_MODULE_LIST_STREAM;
774    dirent->location = list.location();
775    *list.get() = num_output_mappings;
776
777    for (unsigned i = 0, j = 0; i < num_mappings; ++i) {
778      const MappingInfo& mapping = *dumper_.mappings()[i];
779      if (!ShouldIncludeMapping(mapping))
780        continue;
781
782      MDRawModule mod;
783      my_memset(&mod, 0, MD_MODULE_SIZE);
784      mod.base_of_image = mapping.start_addr;
785      mod.size_of_image = mapping.size;
786      const size_t filepath_len = my_strlen(mapping.name);
787
788      // Figure out file name from path
789      const char* filename_ptr = mapping.name + filepath_len - 1;
790      while (filename_ptr >= mapping.name) {
791        if (*filename_ptr == '/')
792          break;
793        filename_ptr--;
794      }
795      filename_ptr++;
796      const size_t filename_len = mapping.name + filepath_len - filename_ptr;
797
798      uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX];
799      uint8_t* cv_ptr = cv_buf;
800      UntypedMDRVA cv(&minidump_writer_);
801      if (!cv.Allocate(MDCVInfoPDB70_minsize + filename_len + 1))
802        return false;
803
804      const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE;
805      memcpy(cv_ptr, &cv_signature, sizeof(cv_signature));
806      cv_ptr += sizeof(cv_signature);
807      uint8_t* signature = cv_ptr;
808      cv_ptr += sizeof(MDGUID);
809      dumper_.ElfFileIdentifierForMapping(i, signature);
810      my_memset(cv_ptr, 0, sizeof(uint32_t));  // Set age to 0 on Linux.
811      cv_ptr += sizeof(uint32_t);
812
813      // Write pdb_file_name
814      memcpy(cv_ptr, filename_ptr, filename_len + 1);
815      cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1);
816
817      mod.cv_record = cv.location();
818
819      MDLocationDescriptor ld;
820      if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld))
821        return false;
822      mod.module_name_rva = ld.rva;
823
824      list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
825    }
826
827    return true;
828  }
829
830  bool WriteMemoryListStream(MDRawDirectory* dirent) {
831    TypedMDRVA<uint32_t> list(&minidump_writer_);
832    if (!list.AllocateObjectAndArray(memory_blocks_.size(),
833                                     sizeof(MDMemoryDescriptor)))
834      return false;
835
836    dirent->stream_type = MD_MEMORY_LIST_STREAM;
837    dirent->location = list.location();
838
839    *list.get() = memory_blocks_.size();
840
841    for (size_t i = 0; i < memory_blocks_.size(); ++i) {
842      list.CopyIndexAfterObject(i, &memory_blocks_[i],
843                                sizeof(MDMemoryDescriptor));
844    }
845    return true;
846  }
847
848  bool WriteExceptionStream(MDRawDirectory* dirent) {
849    TypedMDRVA<MDRawExceptionStream> exc(&minidump_writer_);
850    if (!exc.Allocate())
851      return false;
852    my_memset(exc.get(), 0, sizeof(MDRawExceptionStream));
853
854    dirent->stream_type = MD_EXCEPTION_STREAM;
855    dirent->location = exc.location();
856
857    exc.get()->thread_id = crashing_tid_;
858    exc.get()->exception_record.exception_code = siginfo_->si_signo;
859    exc.get()->exception_record.exception_address =
860        (uintptr_t) siginfo_->si_addr;
861    exc.get()->thread_context = crashing_thread_context_;
862
863    return true;
864  }
865
866  bool WriteSystemInfoStream(MDRawDirectory* dirent) {
867    TypedMDRVA<MDRawSystemInfo> si(&minidump_writer_);
868    if (!si.Allocate())
869      return false;
870    my_memset(si.get(), 0, sizeof(MDRawSystemInfo));
871
872    dirent->stream_type = MD_SYSTEM_INFO_STREAM;
873    dirent->location = si.location();
874
875    WriteCPUInformation(si.get());
876    WriteOSInformation(si.get());
877
878    return true;
879  }
880
881  bool WriteDSODebugStream(MDRawDirectory* dirent, struct r_debug* r_debug,
882                           uint32_t dynamic_length) {
883#if defined(__ANDROID__)
884    return false;
885#else
886    // The caller provided us with a pointer to "struct r_debug". We can
887    // look up the "r_map" field to get a linked list of all loaded DSOs.
888    // Our list of DSOs potentially is different from the ones in the crashing
889    // process. So, we have to be careful to never dereference pointers
890    // directly. Instead, we use CopyFromProcess() everywhere.
891    // See <link.h> for a more detailed discussion of the how the dynamic
892    // loader communicates with debuggers.
893
894    // Count the number of loaded DSOs
895    int dso_count = 0;
896    struct r_debug debug_entry;
897    dumper_.CopyFromProcess(&debug_entry, crashing_tid_, r_debug,
898                            sizeof(debug_entry));
899    for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
900      struct link_map map;
901      dumper_.CopyFromProcess(&map, crashing_tid_, ptr, sizeof(map));
902      ptr = map.l_next;
903      dso_count++;
904    }
905
906    MDRVA linkmap_rva = minidump_writer_.kInvalidMDRVA;
907    if (dso_count > 0) {
908      // If we have at least one DSO, create an array of MDRawLinkMap
909      // entries in the minidump file.
910      TypedMDRVA<MDRawLinkMap> linkmap(&minidump_writer_);
911      if (!linkmap.AllocateArray(dso_count))
912        return false;
913      linkmap_rva = linkmap.location().rva;
914      int idx = 0;
915
916      // Iterate over DSOs and write their information to mini dump
917      for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
918        struct link_map map;
919        dumper_.CopyFromProcess(&map, crashing_tid_, ptr, sizeof(map));
920        ptr = map.l_next;
921        char filename[257] = { 0 };
922        if (map.l_name) {
923          dumper_.CopyFromProcess(filename, crashing_tid_, map.l_name,
924                                  sizeof(filename) - 1);
925        }
926        MDLocationDescriptor location;
927        if (!minidump_writer_.WriteString(filename, 0, &location))
928          return false;
929        MDRawLinkMap entry;
930        entry.name = location.rva;
931        entry.addr = (void*)map.l_addr;
932        entry.ld = (void*)map.l_ld;
933        linkmap.CopyIndex(idx++, &entry);
934      }
935    }
936
937    // Write MD_LINUX_DSO_DEBUG record
938    TypedMDRVA<MDRawDebug> debug(&minidump_writer_);
939    if (!debug.AllocateObjectAndArray(1, dynamic_length))
940      return false;
941    my_memset(debug.get(), 0, sizeof(MDRawDebug));
942    dirent->stream_type = MD_LINUX_DSO_DEBUG;
943    dirent->location = debug.location();
944
945    debug.get()->version = debug_entry.r_version;
946    debug.get()->map = linkmap_rva;
947    debug.get()->dso_count = dso_count;
948    debug.get()->brk = (void*)debug_entry.r_brk;
949    debug.get()->ldbase = (void*)debug_entry.r_ldbase;
950    debug.get()->dynamic = (void*)&_DYNAMIC;
951
952    char *dso_debug_data = new char[dynamic_length];
953    dumper_.CopyFromProcess(dso_debug_data, crashing_tid_, &_DYNAMIC,
954                            dynamic_length);
955    debug.CopyIndexAfterObject(0, dso_debug_data, dynamic_length);
956    delete[] dso_debug_data;
957
958    return true;
959#endif
960  }
961
962 private:
963#if defined(__i386)
964  uintptr_t GetStackPointer() {
965    return ucontext_->uc_mcontext.gregs[REG_ESP];
966  }
967
968  uintptr_t GetInstructionPointer() {
969    return ucontext_->uc_mcontext.gregs[REG_EIP];
970  }
971#elif defined(__x86_64)
972  uintptr_t GetStackPointer() {
973    return ucontext_->uc_mcontext.gregs[REG_RSP];
974  }
975
976  uintptr_t GetInstructionPointer() {
977    return ucontext_->uc_mcontext.gregs[REG_RIP];
978  }
979#elif defined(__ARM_EABI__)
980  uintptr_t GetStackPointer() {
981    return ucontext_->uc_mcontext.arm_sp;
982  }
983
984  uintptr_t GetInstructionPointer() {
985    return ucontext_->uc_mcontext.arm_ip;
986  }
987#else
988#error "This code has not been ported to your platform yet."
989#endif
990
991  void NullifyDirectoryEntry(MDRawDirectory* dirent) {
992    dirent->stream_type = 0;
993    dirent->location.data_size = 0;
994    dirent->location.rva = 0;
995  }
996
997  bool WriteCPUInformation(MDRawSystemInfo* sys_info) {
998    char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0};
999    static const char vendor_id_name[] = "vendor_id";
1000    static const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1;
1001
1002    struct CpuInfoEntry {
1003      const char* info_name;
1004      int value;
1005      bool found;
1006    } cpu_info_table[] = {
1007      { "processor", -1, false },
1008      { "model", 0, false },
1009      { "stepping",  0, false },
1010      { "cpu family", 0, false },
1011    };
1012
1013    // processor_architecture should always be set, do this first
1014    sys_info->processor_architecture =
1015#if defined(__i386)
1016        MD_CPU_ARCHITECTURE_X86;
1017#elif defined(__x86_64)
1018        MD_CPU_ARCHITECTURE_AMD64;
1019#elif defined(__arm__)
1020        MD_CPU_ARCHITECTURE_ARM;
1021#else
1022#error "Unknown CPU arch"
1023#endif
1024
1025    const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0);
1026    if (fd < 0)
1027      return false;
1028
1029    {
1030      PageAllocator allocator;
1031      LineReader* const line_reader = new(allocator) LineReader(fd);
1032      const char* line;
1033      unsigned line_len;
1034      while (line_reader->GetNextLine(&line, &line_len)) {
1035        for (size_t i = 0;
1036             i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
1037             i++) {
1038          CpuInfoEntry* entry = &cpu_info_table[i];
1039          if (entry->found && i)
1040            continue;
1041          if (!strncmp(line, entry->info_name, strlen(entry->info_name))) {
1042            const char* value = strchr(line, ':');
1043            if (!value)
1044              continue;
1045
1046            // the above strncmp only matches the prefix, it might be the wrong
1047            // line. i.e. we matched "model name" instead of "model".
1048            // check and make sure there is only spaces between the prefix and
1049            // the colon.
1050            const char* space_ptr = line + strlen(entry->info_name);
1051            for (; space_ptr < value; space_ptr++) {
1052              if (!isspace(*space_ptr)) {
1053                break;
1054              }
1055            }
1056            if (space_ptr != value)
1057              continue;
1058
1059            sscanf(++value, " %d", &(entry->value));
1060            entry->found = true;
1061          }
1062        }
1063
1064        // special case for vendor_id
1065        if (!strncmp(line, vendor_id_name, vendor_id_name_length)) {
1066          const char* value = strchr(line, ':');
1067          if (!value)
1068            goto popline;
1069
1070          // skip ':" and all the spaces that follows
1071          do {
1072            value++;
1073          } while (isspace(*value));
1074
1075          if (*value) {
1076            size_t length = strlen(value);
1077            if (length == 0)
1078              goto popline;
1079            // we don't want the trailing newline
1080            if (value[length - 1] == '\n')
1081              length--;
1082            // ensure we have space for the value
1083            if (length < sizeof(vendor_id))
1084              strncpy(vendor_id, value, length);
1085          }
1086        }
1087
1088 popline:
1089        line_reader->PopLine(line_len);
1090      }
1091      sys_close(fd);
1092    }
1093
1094    // make sure we got everything we wanted
1095    for (size_t i = 0;
1096         i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
1097         i++) {
1098      if (!cpu_info_table[i].found) {
1099        return false;
1100      }
1101    }
1102    // /proc/cpuinfo contains cpu id, change it into number by adding one.
1103    cpu_info_table[0].value++;
1104
1105    sys_info->number_of_processors = cpu_info_table[0].value;
1106    sys_info->processor_level      = cpu_info_table[3].value;
1107    sys_info->processor_revision   = cpu_info_table[1].value << 8 |
1108                                     cpu_info_table[2].value;
1109
1110    if (vendor_id[0] != '\0') {
1111      memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id,
1112             sizeof(sys_info->cpu.x86_cpu_info.vendor_id));
1113    }
1114    return true;
1115  }
1116
1117  bool WriteFile(MDLocationDescriptor* result, const char* filename) {
1118    const int fd = sys_open(filename, O_RDONLY, 0);
1119    if (fd < 0)
1120      return false;
1121
1122    // We can't stat the files because several of the files that we want to
1123    // read are kernel seqfiles, which always have a length of zero. So we have
1124    // to read as much as we can into a buffer.
1125    static const unsigned kBufSize = 1024 - 2*sizeof(void*);
1126    struct Buffers {
1127      struct Buffers* next;
1128      size_t len;
1129      uint8_t data[kBufSize];
1130    } *buffers =
1131        (struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers));
1132    buffers->next = NULL;
1133    buffers->len = 0;
1134
1135    size_t total = 0;
1136    for (struct Buffers* bufptr = buffers;;) {
1137      ssize_t r;
1138      do {
1139        r = sys_read(fd, &bufptr->data[bufptr->len], kBufSize - bufptr->len);
1140      } while (r == -1 && errno == EINTR);
1141
1142      if (r < 1)
1143        break;
1144
1145      total += r;
1146      bufptr->len += r;
1147      if (bufptr->len == kBufSize) {
1148        bufptr->next =
1149          (struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers));
1150        bufptr = bufptr->next;
1151        bufptr->next = NULL;
1152        bufptr->len = 0;
1153      }
1154    }
1155    sys_close(fd);
1156
1157    if (!total)
1158      return false;
1159
1160    UntypedMDRVA memory(&minidump_writer_);
1161    if (!memory.Allocate(total))
1162      return false;
1163    for (MDRVA pos = memory.position(); buffers; buffers = buffers->next) {
1164      memory.Copy(pos, &buffers->data, buffers->len);
1165      pos += buffers->len;
1166    }
1167    *result = memory.location();
1168    return true;
1169  }
1170
1171  bool WriteOSInformation(MDRawSystemInfo* sys_info) {
1172    sys_info->platform_id = MD_OS_LINUX;
1173
1174    struct utsname uts;
1175    if (uname(&uts))
1176      return false;
1177
1178    static const size_t buf_len = 512;
1179    char buf[buf_len] = {0};
1180    size_t space_left = buf_len - 1;
1181    const char* info_table[] = {
1182      uts.sysname,
1183      uts.release,
1184      uts.version,
1185      uts.machine,
1186      NULL
1187    };
1188    bool first_item = true;
1189    for (const char** cur_info = info_table; *cur_info; cur_info++) {
1190      static const char* separator = " ";
1191      size_t separator_len = strlen(separator);
1192      size_t info_len = strlen(*cur_info);
1193      if (info_len == 0)
1194        continue;
1195
1196      if (space_left < info_len + (first_item ? 0 : separator_len))
1197        break;
1198
1199      if (!first_item) {
1200        strcat(buf, separator);
1201        space_left -= separator_len;
1202      }
1203
1204      first_item = false;
1205      strcat(buf, *cur_info);
1206      space_left -= info_len;
1207    }
1208
1209    MDLocationDescriptor location;
1210    if (!minidump_writer_.WriteString(buf, 0, &location))
1211      return false;
1212    sys_info->csd_version_rva = location.rva;
1213
1214    return true;
1215  }
1216
1217  bool WriteProcFile(MDLocationDescriptor* result, pid_t pid,
1218                     const char* filename) {
1219    char buf[80];
1220    memcpy(buf, "/proc/", 6);
1221    const unsigned pid_len = my_int_len(pid);
1222    my_itos(buf + 6, pid, pid_len);
1223    buf[6 + pid_len] = '/';
1224    memcpy(buf + 6 + pid_len + 1, filename, my_strlen(filename) + 1);
1225    return WriteFile(result, buf);
1226  }
1227
1228  const char* const filename_;  // output filename
1229  const siginfo_t* const siginfo_;  // from the signal handler (see sigaction)
1230  const struct ucontext* const ucontext_;  // also from the signal handler
1231  const struct _libc_fpstate* const float_state_;  // ditto
1232  const pid_t crashing_tid_;  // the process which actually crashed
1233  LinuxDumper dumper_;
1234  MinidumpFileWriter minidump_writer_;
1235  MDLocationDescriptor crashing_thread_context_;
1236  // Blocks of memory written to the dump. These are all currently
1237  // written while writing the thread list stream, but saved here
1238  // so a memory list stream can be written afterwards.
1239  wasteful_vector<MDMemoryDescriptor> memory_blocks_;
1240};
1241
1242bool WriteMinidump(const char* filename, pid_t crashing_process,
1243                   const void* blob, size_t blob_size) {
1244  if (blob_size != sizeof(ExceptionHandler::CrashContext))
1245    return false;
1246  const ExceptionHandler::CrashContext* context =
1247      reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
1248  MinidumpWriter writer(filename, crashing_process, context);
1249  if (!writer.Init())
1250    return false;
1251  return writer.Dump();
1252}
1253
1254}  // namespace google_breakpad
1255