| // 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 { |
| if (this) |
| (*section).D32(size_).D32(file_offset_); |
| else |
| (*section).D32(0).D32(0); |
| } |
| |
| 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); |
| cv_record->CiteLocationIn(this); |
| misc_record->CiteLocationIn(this); |
| 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 |
| }; |
| |
| 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), |
| 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; |
| } |
| |
| void Dump::Finish() { |
| if (!thread_list_.Empty()) Add(&thread_list_); |
| if (!module_list_.Empty()) Add(&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 |