michael@0: // -*- mode: c++ -*- michael@0: michael@0: // Copyright (c) 2010, Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: // Original author: Jim Blandy michael@0: michael@0: // Implementation of google_breakpad::DwarfCFIToModule. michael@0: // See dwarf_cfi_to_module.h for details. michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "common/dwarf_cfi_to_module.h" michael@0: #include "common/logging.h" michael@0: michael@0: namespace google_breakpad { michael@0: michael@0: using std::ostringstream; michael@0: michael@0: vector DwarfCFIToModule::RegisterNames::MakeVector( michael@0: const char* const* strings, michael@0: size_t size) { michael@0: vector names(size, NULL); michael@0: for (size_t i = 0; i < size; ++i) { michael@0: names[i] = ToUniqueString(strings[i]); michael@0: } michael@0: michael@0: return names; michael@0: } michael@0: michael@0: vector DwarfCFIToModule::RegisterNames::I386() { michael@0: static const char *const names[] = { michael@0: "$eax", "$ecx", "$edx", "$ebx", "$esp", "$ebp", "$esi", "$edi", michael@0: "$eip", "$eflags", "$unused1", michael@0: "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", michael@0: "$unused2", "$unused3", michael@0: "$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", michael@0: "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", michael@0: "$fcw", "$fsw", "$mxcsr", michael@0: "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused4", "$unused5", michael@0: "$tr", "$ldtr" michael@0: }; michael@0: michael@0: return MakeVector(names, sizeof(names) / sizeof(names[0])); michael@0: } michael@0: michael@0: vector DwarfCFIToModule::RegisterNames::X86_64() { michael@0: static const char *const names[] = { michael@0: "$rax", "$rdx", "$rcx", "$rbx", "$rsi", "$rdi", "$rbp", "$rsp", michael@0: "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", michael@0: "$rip", michael@0: "$xmm0","$xmm1","$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", michael@0: "$xmm8","$xmm9","$xmm10","$xmm11","$xmm12","$xmm13","$xmm14","$xmm15", michael@0: "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", michael@0: "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", michael@0: "$rflags", michael@0: "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused1", "$unused2", michael@0: "$fs.base", "$gs.base", "$unused3", "$unused4", michael@0: "$tr", "$ldtr", michael@0: "$mxcsr", "$fcw", "$fsw" michael@0: }; michael@0: michael@0: return MakeVector(names, sizeof(names) / sizeof(names[0])); michael@0: } michael@0: michael@0: // Per ARM IHI 0040A, section 3.1 michael@0: vector DwarfCFIToModule::RegisterNames::ARM() { michael@0: static const char *const names[] = { michael@0: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", michael@0: "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", michael@0: "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", michael@0: "fps", "cpsr", "", "", "", "", "", "", michael@0: "", "", "", "", "", "", "", "", michael@0: "", "", "", "", "", "", "", "", michael@0: "", "", "", "", "", "", "", "", michael@0: "", "", "", "", "", "", "", "", michael@0: "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", michael@0: "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15", michael@0: "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23", michael@0: "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31", michael@0: "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7" michael@0: }; michael@0: michael@0: return MakeVector(names, sizeof(names) / sizeof(names[0])); michael@0: } michael@0: michael@0: bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length, michael@0: uint8 version, const string &augmentation, michael@0: unsigned return_address) { michael@0: assert(!entry_); michael@0: michael@0: // If dwarf2reader::CallFrameInfo can handle this version and michael@0: // augmentation, then we should be okay with that, so there's no michael@0: // need to check them here. michael@0: michael@0: // Get ready to collect entries. michael@0: entry_ = new Module::StackFrameEntry; michael@0: entry_->address = address; michael@0: entry_->size = length; michael@0: entry_offset_ = offset; michael@0: return_address_ = return_address; michael@0: michael@0: // Breakpad STACK CFI records must provide a .ra rule, but DWARF CFI michael@0: // may not establish any rule for .ra if the return address column michael@0: // is an ordinary register, and that register holds the return michael@0: // address on entry to the function. So establish an initial .ra michael@0: // rule citing the return address register. michael@0: if (return_address_ < register_names_.size()) michael@0: entry_->initial_rules[ustr__ZDra()] michael@0: = Module::Expr(register_names_[return_address_], 0, false); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: const UniqueString* DwarfCFIToModule::RegisterName(int i) { michael@0: assert(entry_); michael@0: if (i < 0) { michael@0: assert(i == kCFARegister); michael@0: return ustr__ZDcfa(); michael@0: } michael@0: unsigned reg = i; michael@0: if (reg == return_address_) michael@0: return ustr__ZDra(); michael@0: michael@0: // Ensure that a non-empty name exists for this register value. michael@0: if (reg < register_names_.size() && register_names_[reg] != ustr__empty()) michael@0: return register_names_[reg]; michael@0: michael@0: reporter_->UnnamedRegister(entry_offset_, reg); michael@0: char buf[30]; michael@0: sprintf(buf, "unnamed_register%u", reg); michael@0: return ToUniqueString(buf); michael@0: } michael@0: michael@0: void DwarfCFIToModule::Record(Module::Address address, int reg, michael@0: const Module::Expr &rule) { michael@0: assert(entry_); michael@0: michael@0: // Is this one of this entry's initial rules? michael@0: if (address == entry_->address) michael@0: entry_->initial_rules[RegisterName(reg)] = rule; michael@0: // File it under the appropriate address. michael@0: else michael@0: entry_->rule_changes[address][RegisterName(reg)] = rule; michael@0: } michael@0: michael@0: bool DwarfCFIToModule::UndefinedRule(uint64 address, int reg) { michael@0: reporter_->UndefinedNotSupported(entry_offset_, RegisterName(reg)); michael@0: // Treat this as a non-fatal error. michael@0: return true; michael@0: } michael@0: michael@0: bool DwarfCFIToModule::SameValueRule(uint64 address, int reg) { michael@0: // reg + 0 michael@0: Module::Expr rule michael@0: = Module::Expr(RegisterName(reg), 0, false); michael@0: Record(address, reg, rule); michael@0: return true; michael@0: } michael@0: michael@0: bool DwarfCFIToModule::OffsetRule(uint64 address, int reg, michael@0: int base_register, long offset) { michael@0: // *(base_register + offset) michael@0: Module::Expr rule michael@0: = Module::Expr(RegisterName(base_register), offset, true); michael@0: Record(address, reg, rule); michael@0: return true; michael@0: } michael@0: michael@0: bool DwarfCFIToModule::ValOffsetRule(uint64 address, int reg, michael@0: int base_register, long offset) { michael@0: // base_register + offset michael@0: Module::Expr rule michael@0: = Module::Expr(RegisterName(base_register), offset, false); michael@0: Record(address, reg, rule); michael@0: return true; michael@0: } michael@0: michael@0: bool DwarfCFIToModule::RegisterRule(uint64 address, int reg, michael@0: int base_register) { michael@0: // base_register + 0 michael@0: Module::Expr rule michael@0: = Module::Expr(RegisterName(base_register), 0, false); michael@0: Record(address, reg, rule); michael@0: return true; michael@0: } michael@0: michael@0: bool DwarfCFIToModule::ExpressionRule(uint64 address, int reg, michael@0: const string &expression) { michael@0: reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg)); michael@0: // Treat this as a non-fatal error. michael@0: return true; michael@0: } michael@0: michael@0: bool DwarfCFIToModule::ValExpressionRule(uint64 address, int reg, michael@0: const string &expression) { michael@0: reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg)); michael@0: // Treat this as a non-fatal error. michael@0: return true; michael@0: } michael@0: michael@0: bool DwarfCFIToModule::End() { michael@0: module_->AddStackFrameEntry(entry_); michael@0: entry_ = NULL; michael@0: return true; michael@0: } michael@0: michael@0: void DwarfCFIToModule::Reporter::UnnamedRegister(size_t offset, int reg) { michael@0: BPLOG(INFO) << file_ << ", section '" << section_ michael@0: << "': the call frame entry at offset 0x" michael@0: << std::setbase(16) << offset << std::setbase(10) michael@0: << " refers to register " << reg << ", whose name we don't know"; michael@0: } michael@0: michael@0: void DwarfCFIToModule::Reporter::UndefinedNotSupported( michael@0: size_t offset, michael@0: const UniqueString* reg) { michael@0: BPLOG(INFO) << file_ << ", section '" << section_ michael@0: << "': the call frame entry at offset 0x" michael@0: << std::setbase(16) << offset << std::setbase(10) michael@0: << " sets the rule for register '" << FromUniqueString(reg) michael@0: << "' to 'undefined', but the Breakpad symbol file format cannot " michael@0: << " express this"; michael@0: } michael@0: michael@0: void DwarfCFIToModule::Reporter::ExpressionsNotSupported( michael@0: size_t offset, michael@0: const UniqueString* reg) { michael@0: static uint64_t n_complaints = 0; // This isn't threadsafe michael@0: n_complaints++; michael@0: if (!is_power_of_2(n_complaints)) michael@0: return; michael@0: BPLOG(INFO) << file_ << ", section '" << section_ michael@0: << "': the call frame entry at offset 0x" michael@0: << std::setbase(16) << offset << std::setbase(10) michael@0: << " uses a DWARF expression to describe how to recover register '" michael@0: << FromUniqueString(reg) << "', but this translator cannot yet " michael@0: << "translate DWARF expressions to Breakpad postfix expressions (shown " michael@0: << n_complaints << " times)"; michael@0: } michael@0: michael@0: } // namespace google_breakpad