|  | // Copyright (c) 2010, Google Inc. | 
|  | // All rights reserved. | 
|  | // | 
|  | // Redistribution and use in source and binary forms, with or without | 
|  | // modification, are permitted provided that the following conditions are | 
|  | // met: | 
|  | // | 
|  | //     * Redistributions of source code must retain the above copyright | 
|  | // notice, this list of conditions and the following disclaimer. | 
|  | //     * Redistributions in binary form must reproduce the above | 
|  | // copyright notice, this list of conditions and the following disclaimer | 
|  | // in the documentation and/or other materials provided with the | 
|  | // distribution. | 
|  | //     * Neither the name of Google Inc. nor the names of its | 
|  | // contributors may be used to endorse or promote products derived from | 
|  | // this software without specific prior written permission. | 
|  | // | 
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  |  | 
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | 
|  |  | 
|  | // synth_minidump.cc: Implementation of SynthMinidump.  See synth_minidump.h | 
|  |  | 
|  | #include "processor/synth_minidump.h" | 
|  |  | 
|  | namespace google_breakpad { | 
|  |  | 
|  | namespace SynthMinidump { | 
|  |  | 
|  | Section::Section(const Dump &dump) | 
|  | : test_assembler::Section(dump.endianness()) { } | 
|  |  | 
|  | void Section::CiteLocationIn(test_assembler::Section *section) const { | 
|  | (*section).D32(size_).D32(file_offset_); | 
|  | } | 
|  |  | 
|  | void Stream::CiteStreamIn(test_assembler::Section *section) const { | 
|  | section->D32(type_); | 
|  | CiteLocationIn(section); | 
|  | } | 
|  |  | 
|  | SystemInfo::SystemInfo(const Dump &dump, | 
|  | const MDRawSystemInfo &system_info, | 
|  | const String &csd_version) | 
|  | : Stream(dump, MD_SYSTEM_INFO_STREAM) { | 
|  | D16(system_info.processor_architecture); | 
|  | D16(system_info.processor_level); | 
|  | D16(system_info.processor_revision); | 
|  | D8(system_info.number_of_processors); | 
|  | D8(system_info.product_type); | 
|  | D32(system_info.major_version); | 
|  | D32(system_info.minor_version); | 
|  | D32(system_info.build_number); | 
|  | D32(system_info.platform_id); | 
|  | csd_version.CiteStringIn(this); | 
|  | D16(system_info.suite_mask); | 
|  | D16(system_info.reserved2);           // Well, why not? | 
|  |  | 
|  | // MDCPUInformation cpu; | 
|  | if (system_info.processor_architecture == MD_CPU_ARCHITECTURE_X86) { | 
|  | D32(system_info.cpu.x86_cpu_info.vendor_id[0]); | 
|  | D32(system_info.cpu.x86_cpu_info.vendor_id[1]); | 
|  | D32(system_info.cpu.x86_cpu_info.vendor_id[2]); | 
|  | D32(system_info.cpu.x86_cpu_info.version_information); | 
|  | D32(system_info.cpu.x86_cpu_info.feature_information); | 
|  | D32(system_info.cpu.x86_cpu_info.amd_extended_cpu_features); | 
|  | } else if (system_info.processor_architecture == MD_CPU_ARCHITECTURE_ARM) { | 
|  | D32(system_info.cpu.arm_cpu_info.cpuid); | 
|  | D32(system_info.cpu.arm_cpu_info.elf_hwcaps); | 
|  | } else { | 
|  | D64(system_info.cpu.other_cpu_info.processor_features[0]); | 
|  | D64(system_info.cpu.other_cpu_info.processor_features[1]); | 
|  | } | 
|  | } | 
|  |  | 
|  | const MDRawSystemInfo SystemInfo::windows_x86 = { | 
|  | MD_CPU_ARCHITECTURE_X86,              // processor_architecture | 
|  | 6,                                    // processor_level | 
|  | 0xd08,                                // processor_revision | 
|  | 1,                                    // number_of_processors | 
|  | 1,                                    // product_type | 
|  | 5,                                    // major_version | 
|  | 1,                                    // minor_version | 
|  | 2600,                                 // build_number | 
|  | 2,                                    // platform_id | 
|  | 0xdeadbeef,                           // csd_version_rva | 
|  | 0x100,                                // suite_mask | 
|  | 0,                                    // reserved2 | 
|  | {                                     // cpu | 
|  | { // x86_cpu_info | 
|  | { 0x756e6547, 0x49656e69, 0x6c65746e }, // vendor_id | 
|  | 0x6d8,                                  // version_information | 
|  | 0xafe9fbff,                             // feature_information | 
|  | 0xffffffff                              // amd_extended_cpu_features | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | const string SystemInfo::windows_x86_csd_version = "Service Pack 2"; | 
|  |  | 
|  | String::String(const Dump &dump, const string &contents) : Section(dump) { | 
|  | D32(contents.size() * 2); | 
|  | for (string::const_iterator i = contents.begin(); i != contents.end(); i++) | 
|  | D16(*i); | 
|  | } | 
|  |  | 
|  | void String::CiteStringIn(test_assembler::Section *section) const { | 
|  | section->D32(file_offset_); | 
|  | } | 
|  |  | 
|  | void Memory::CiteMemoryIn(test_assembler::Section *section) const { | 
|  | section->D64(address_); | 
|  | CiteLocationIn(section); | 
|  | } | 
|  |  | 
|  | Context::Context(const Dump &dump, const MDRawContextX86 &context) | 
|  | : Section(dump) { | 
|  | // The caller should have properly set the CPU type flag. | 
|  | // The high 24 bits identify the CPU.  Note that context records with no CPU | 
|  | // type information can be valid (e.g. produced by ::RtlCaptureContext). | 
|  | assert(((context.context_flags & MD_CONTEXT_CPU_MASK) == 0) || | 
|  | (context.context_flags & MD_CONTEXT_X86)); | 
|  | // It doesn't make sense to store x86 registers in big-endian form. | 
|  | assert(dump.endianness() == kLittleEndian); | 
|  | D32(context.context_flags); | 
|  | D32(context.dr0); | 
|  | D32(context.dr1); | 
|  | D32(context.dr2); | 
|  | D32(context.dr3); | 
|  | D32(context.dr6); | 
|  | D32(context.dr7); | 
|  | D32(context.float_save.control_word); | 
|  | D32(context.float_save.status_word); | 
|  | D32(context.float_save.tag_word); | 
|  | D32(context.float_save.error_offset); | 
|  | D32(context.float_save.error_selector); | 
|  | D32(context.float_save.data_offset); | 
|  | D32(context.float_save.data_selector); | 
|  | // context.float_save.register_area[] contains 8-bit quantities and | 
|  | // does not need to be swapped. | 
|  | Append(context.float_save.register_area, | 
|  | sizeof(context.float_save.register_area)); | 
|  | D32(context.float_save.cr0_npx_state); | 
|  | D32(context.gs); | 
|  | D32(context.fs); | 
|  | D32(context.es); | 
|  | D32(context.ds); | 
|  | D32(context.edi); | 
|  | D32(context.esi); | 
|  | D32(context.ebx); | 
|  | D32(context.edx); | 
|  | D32(context.ecx); | 
|  | D32(context.eax); | 
|  | D32(context.ebp); | 
|  | D32(context.eip); | 
|  | D32(context.cs); | 
|  | D32(context.eflags); | 
|  | D32(context.esp); | 
|  | D32(context.ss); | 
|  | // context.extended_registers[] contains 8-bit quantities and does | 
|  | // not need to be swapped. | 
|  | Append(context.extended_registers, sizeof(context.extended_registers)); | 
|  | assert(Size() == sizeof(MDRawContextX86)); | 
|  | } | 
|  |  | 
|  | Context::Context(const Dump &dump, const MDRawContextARM &context) | 
|  | : Section(dump) { | 
|  | // The caller should have properly set the CPU type flag. | 
|  | assert((context.context_flags & MD_CONTEXT_ARM) || | 
|  | (context.context_flags & MD_CONTEXT_ARM_OLD)); | 
|  | // It doesn't make sense to store ARM registers in big-endian form. | 
|  | assert(dump.endianness() == kLittleEndian); | 
|  | D32(context.context_flags); | 
|  | for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i) | 
|  | D32(context.iregs[i]); | 
|  | D32(context.cpsr); | 
|  | D64(context.float_save.fpscr); | 
|  | for (int i = 0; i < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT; ++i) | 
|  | D64(context.float_save.regs[i]); | 
|  | for (int i = 0; i < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT; ++i) | 
|  | D32(context.float_save.extra[i]); | 
|  | assert(Size() == sizeof(MDRawContextARM)); | 
|  | } | 
|  |  | 
|  | Context::Context(const Dump &dump, const MDRawContextMIPS &context) | 
|  | : Section(dump) { | 
|  | // The caller should have properly set the CPU type flag. | 
|  | assert(context.context_flags & MD_CONTEXT_MIPS); | 
|  | D32(context.context_flags); | 
|  | D32(context._pad0); | 
|  |  | 
|  | for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i) | 
|  | D64(context.iregs[i]); | 
|  |  | 
|  | D64(context.mdhi); | 
|  | D64(context.mdlo); | 
|  |  | 
|  | for (int i = 0; i < MD_CONTEXT_MIPS_DSP_COUNT; ++i) | 
|  | D32(context.hi[i]); | 
|  |  | 
|  | for (int i = 0; i < MD_CONTEXT_MIPS_DSP_COUNT; ++i) | 
|  | D32(context.lo[i]); | 
|  |  | 
|  | D32(context.dsp_control); | 
|  | D32(context._pad1); | 
|  |  | 
|  | D64(context.epc); | 
|  | D64(context.badvaddr); | 
|  | D32(context.status); | 
|  | D32(context.cause); | 
|  |  | 
|  | for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i) | 
|  | D64(context.float_save.regs[i]); | 
|  |  | 
|  | D32(context.float_save.fpcsr); | 
|  | D32(context.float_save.fir); | 
|  |  | 
|  | assert(Size() == sizeof(MDRawContextMIPS)); | 
|  | } | 
|  |  | 
|  | Thread::Thread(const Dump &dump, | 
|  | uint32_t thread_id, const Memory &stack, const Context &context, | 
|  | uint32_t suspend_count, uint32_t priority_class, | 
|  | uint32_t priority, uint64_t teb) : Section(dump) { | 
|  | D32(thread_id); | 
|  | D32(suspend_count); | 
|  | D32(priority_class); | 
|  | D32(priority); | 
|  | D64(teb); | 
|  | stack.CiteMemoryIn(this); | 
|  | context.CiteLocationIn(this); | 
|  | assert(Size() == sizeof(MDRawThread)); | 
|  | } | 
|  |  | 
|  | Module::Module(const Dump &dump, | 
|  | uint64_t base_of_image, | 
|  | uint32_t size_of_image, | 
|  | const String &name, | 
|  | uint32_t time_date_stamp, | 
|  | uint32_t checksum, | 
|  | const MDVSFixedFileInfo &version_info, | 
|  | const Section *cv_record, | 
|  | const Section *misc_record) : Section(dump) { | 
|  | D64(base_of_image); | 
|  | D32(size_of_image); | 
|  | D32(checksum); | 
|  | D32(time_date_stamp); | 
|  | name.CiteStringIn(this); | 
|  | D32(version_info.signature); | 
|  | D32(version_info.struct_version); | 
|  | D32(version_info.file_version_hi); | 
|  | D32(version_info.file_version_lo); | 
|  | D32(version_info.product_version_hi); | 
|  | D32(version_info.product_version_lo); | 
|  | D32(version_info.file_flags_mask); | 
|  | D32(version_info.file_flags); | 
|  | D32(version_info.file_os); | 
|  | D32(version_info.file_type); | 
|  | D32(version_info.file_subtype); | 
|  | D32(version_info.file_date_hi); | 
|  | D32(version_info.file_date_lo); | 
|  | if (cv_record) | 
|  | cv_record->CiteLocationIn(this); | 
|  | else | 
|  | D32(0).D32(0); | 
|  | if (misc_record) | 
|  | misc_record->CiteLocationIn(this); | 
|  | else | 
|  | D32(0).D32(0); | 
|  | D64(0).D64(0); | 
|  | } | 
|  |  | 
|  | const MDVSFixedFileInfo Module::stock_version_info = { | 
|  | MD_VSFIXEDFILEINFO_SIGNATURE,         // signature | 
|  | MD_VSFIXEDFILEINFO_VERSION,           // struct_version | 
|  | 0x11111111,                           // file_version_hi | 
|  | 0x22222222,                           // file_version_lo | 
|  | 0x33333333,                           // product_version_hi | 
|  | 0x44444444,                           // product_version_lo | 
|  | MD_VSFIXEDFILEINFO_FILE_FLAGS_DEBUG,  // file_flags_mask | 
|  | MD_VSFIXEDFILEINFO_FILE_FLAGS_DEBUG,  // file_flags | 
|  | MD_VSFIXEDFILEINFO_FILE_OS_NT | MD_VSFIXEDFILEINFO_FILE_OS__WINDOWS32, | 
|  | // file_os | 
|  | MD_VSFIXEDFILEINFO_FILE_TYPE_APP,     // file_type | 
|  | MD_VSFIXEDFILEINFO_FILE_SUBTYPE_UNKNOWN, // file_subtype | 
|  | 0,                                    // file_date_hi | 
|  | 0                                     // file_date_lo | 
|  | }; | 
|  |  | 
|  | UnloadedModule::UnloadedModule(const Dump &dump, | 
|  | uint64_t base_of_image, | 
|  | uint32_t size_of_image, | 
|  | const String &name, | 
|  | uint32_t checksum, | 
|  | uint32_t time_date_stamp) : Section(dump) { | 
|  | D64(base_of_image); | 
|  | D32(size_of_image); | 
|  | D32(checksum); | 
|  | D32(time_date_stamp); | 
|  | name.CiteStringIn(this); | 
|  | } | 
|  |  | 
|  | UnloadedModuleList::UnloadedModuleList(const Dump &dump, uint32_t type) | 
|  | : List<UnloadedModule>(dump, type, false) { | 
|  | D32(sizeof(MDRawUnloadedModuleList)); | 
|  | D32(sizeof(MDRawUnloadedModule)); | 
|  | D32(count_label_); | 
|  | } | 
|  |  | 
|  | Exception::Exception(const Dump &dump, | 
|  | const Context &context, | 
|  | uint32_t thread_id, | 
|  | uint32_t exception_code, | 
|  | uint32_t exception_flags, | 
|  | uint64_t exception_address) | 
|  | : Stream(dump, MD_EXCEPTION_STREAM) { | 
|  | D32(thread_id); | 
|  | D32(0);  // __align | 
|  | D32(exception_code); | 
|  | D32(exception_flags); | 
|  | D64(0);  // exception_record | 
|  | D64(exception_address); | 
|  | D32(0);  // number_parameters | 
|  | D32(0);  // __align | 
|  | for (int i = 0; i < MD_EXCEPTION_MAXIMUM_PARAMETERS; ++i) | 
|  | D64(0);  // exception_information | 
|  | context.CiteLocationIn(this); | 
|  | assert(Size() == sizeof(MDRawExceptionStream)); | 
|  | } | 
|  |  | 
|  | Dump::Dump(uint64_t flags, | 
|  | Endianness endianness, | 
|  | uint32_t version, | 
|  | uint32_t date_time_stamp) | 
|  | : test_assembler::Section(endianness), | 
|  | file_start_(0), | 
|  | stream_directory_(*this), | 
|  | stream_count_(0), | 
|  | thread_list_(*this, MD_THREAD_LIST_STREAM), | 
|  | module_list_(*this, MD_MODULE_LIST_STREAM), | 
|  | unloaded_module_list_(*this, MD_UNLOADED_MODULE_LIST_STREAM), | 
|  | memory_list_(*this, MD_MEMORY_LIST_STREAM) | 
|  | { | 
|  | D32(MD_HEADER_SIGNATURE); | 
|  | D32(version); | 
|  | D32(stream_count_label_); | 
|  | D32(stream_directory_rva_); | 
|  | D32(0); | 
|  | D32(date_time_stamp); | 
|  | D64(flags); | 
|  | assert(Size() == sizeof(MDRawHeader)); | 
|  | } | 
|  |  | 
|  | Dump &Dump::Add(SynthMinidump::Section *section) { | 
|  | section->Finish(file_start_ + Size()); | 
|  | Append(*section); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | Dump &Dump::Add(Stream *stream) { | 
|  | Add(static_cast<SynthMinidump::Section *>(stream)); | 
|  | stream->CiteStreamIn(&stream_directory_); | 
|  | stream_count_++; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | Dump &Dump::Add(Memory *memory) { | 
|  | // Add the memory contents themselves to the file. | 
|  | Add(static_cast<SynthMinidump::Section *>(memory)); | 
|  |  | 
|  | // The memory list is a list of MDMemoryDescriptors, not of actual | 
|  | // memory elements. Produce a descriptor, and add that to the list. | 
|  | SynthMinidump::Section descriptor(*this); | 
|  | memory->CiteMemoryIn(&descriptor); | 
|  | memory_list_.Add(&descriptor); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | Dump &Dump::Add(Thread *thread) { | 
|  | thread_list_.Add(thread); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | Dump &Dump::Add(Module *module) { | 
|  | module_list_.Add(module); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | Dump &Dump::Add(UnloadedModule *unloaded_module) { | 
|  | unloaded_module_list_.Add(unloaded_module); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | void Dump::Finish() { | 
|  | if (!thread_list_.Empty()) Add(&thread_list_); | 
|  | if (!module_list_.Empty()) Add(&module_list_); | 
|  | if (!unloaded_module_list_.Empty()) Add(&unloaded_module_list_); | 
|  | if (!memory_list_.Empty()) Add(&memory_list_); | 
|  |  | 
|  | // Create the stream directory. We don't use | 
|  | // stream_directory_.Finish here, because the stream directory isn't | 
|  | // cited using a location descriptor; rather, the Minidump header | 
|  | // has the stream count and MDRVA. | 
|  | stream_count_label_ = stream_count_; | 
|  | stream_directory_rva_ = file_start_ + Size(); | 
|  | Append(static_cast<test_assembler::Section &>(stream_directory_)); | 
|  | } | 
|  |  | 
|  | } // namespace SynthMinidump | 
|  |  | 
|  | } // namespace google_breakpad |