aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjimblandy <jimblandy@4c0a9323-5329-0410-9bdc-e9ce6186880e>2010-03-16 16:31:49 +0000
committerjimblandy <jimblandy@4c0a9323-5329-0410-9bdc-e9ce6186880e>2010-03-16 16:31:49 +0000
commit3e768ed9c01a244cdb1bc0d6aec34fb25821fbcc (patch)
treebc77f789150d26cb57c7743d15b9e0f7b052bc49
parentBreakpad: Add minidump processor support for DWARF Call Frame Information. (diff)
downloadbreakpad-3e768ed9c01a244cdb1bc0d6aec34fb25821fbcc.tar.xz
Breakpad Linux dumper: Add support for dumping DWARF CFI as STACK CFI records.
Define a new DWARF parser class, dwarf2reader::CallFrameInfo. Extend google_breakpad::Module to store and write out 'STACK CFI' records. Define a new google_breakpad::DwarfCFIToModule class, to accept DWARF CFI data from the parser and populate a Module with the equivalent STACK CFI records. Extend the Linux symbol dumping tool, dump_syms, to use dwarf2reader::CallFrameInfo, google_breakpad::DwarfCFIToModule, and google_breakpad::Module to extract DWARF CFI from the executable or shared library files and write it to the Breakpad symbol file. Define CFISection, a new class derived from TestAssembler::Section, for use in creating DWARF CFI data for test cases. a=jimblandy, r=nealsid git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@550 4c0a9323-5329-0410-9bdc-e9ce6186880e
-rw-r--r--src/common/dwarf/cfi_assembler.cc103
-rw-r--r--src/common/dwarf/cfi_assembler.h149
-rw-r--r--src/common/dwarf/dwarf2enums.h43
-rw-r--r--src/common/dwarf/dwarf2reader.cc1229
-rw-r--r--src/common/dwarf/dwarf2reader.h468
-rw-r--r--src/common/dwarf/dwarf2reader_cfi_unittest.cc1928
-rw-r--r--src/common/linux/dump_symbols.cc133
-rw-r--r--src/common/linux/dwarf_cfi_to_module.cc187
-rw-r--r--src/common/linux/dwarf_cfi_to_module.h154
-rw-r--r--src/common/linux/dwarf_cfi_to_module_unittest.cc274
-rw-r--r--src/common/linux/module.cc46
-rw-r--r--src/common/linux/module.h65
-rw-r--r--src/common/linux/module_unittest.cc112
-rw-r--r--src/tools/linux/dump_syms/Makefile55
14 files changed, 4935 insertions, 11 deletions
diff --git a/src/common/dwarf/cfi_assembler.cc b/src/common/dwarf/cfi_assembler.cc
new file mode 100644
index 00000000..07995458
--- /dev/null
+++ b/src/common/dwarf/cfi_assembler.cc
@@ -0,0 +1,103 @@
+// 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>
+
+// cfi_assembler.cc: Implementation of google_breakpad::CFISection class.
+// See cfi_assembler.h for details.
+
+#include <cassert>
+
+#include "common/dwarf/cfi_assembler.h"
+#include "common/dwarf/dwarf2enums.h"
+
+namespace google_breakpad {
+
+CFISection &CFISection::CIEHeader(u_int64_t code_alignment_factor,
+ int data_alignment_factor,
+ unsigned return_address_register,
+ u_int8_t version,
+ const string &augmentation,
+ bool dwarf64) {
+ assert(!entry_length_);
+ entry_length_ = new PendingLength();
+
+ if (dwarf64) {
+ D32(0xffffffff);
+ D64(entry_length_->length);
+ entry_length_->start = Here();
+ D64(0xffffffffffffffffULL); // CIE distinguished value
+ } else {
+ D32(entry_length_->length);
+ entry_length_->start = Here();
+ D32(0xffffffff); // CIE distinguished value
+ }
+ D8(version);
+ AppendCString(augmentation);
+ ULEB128(code_alignment_factor);
+ LEB128(data_alignment_factor);
+ if (version == 1)
+ D8(return_address_register);
+ else
+ ULEB128(return_address_register);
+ return *this;
+}
+
+CFISection &CFISection::FDEHeader(Label cie_pointer,
+ u_int64_t initial_location,
+ u_int64_t address_range,
+ bool dwarf64) {
+ assert(!entry_length_);
+ entry_length_ = new PendingLength();
+
+ if (dwarf64) {
+ D32(0xffffffff);
+ D64(entry_length_->length);
+ entry_length_->start = Here();
+ D64(cie_pointer);
+ } else {
+ D32(entry_length_->length);
+ entry_length_->start = Here();
+ D32(cie_pointer);
+ }
+ Append(endianness(), address_size_, initial_location);
+ Append(endianness(), address_size_, address_range);
+ return *this;
+}
+
+CFISection &CFISection::FinishEntry() {
+ assert(entry_length_);
+ Align(address_size_, dwarf2reader::DW_CFA_nop);
+ entry_length_->length = Here() - entry_length_->start;
+ delete entry_length_;
+ entry_length_ = NULL;
+ return *this;
+}
+
+};
diff --git a/src/common/dwarf/cfi_assembler.h b/src/common/dwarf/cfi_assembler.h
new file mode 100644
index 00000000..449a8078
--- /dev/null
+++ b/src/common/dwarf/cfi_assembler.h
@@ -0,0 +1,149 @@
+// -*- mode: C++ -*-
+
+// 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>
+
+// cfi-assembler.h: Define CFISection, a class for creating properly
+// (and improperly) formatted DWARF CFI data for unit tests.
+
+#ifndef PROCESSOR_CFI_ASSEMBLER_H_
+#define PROCESSOR_CFI_ASSEMBLER_H_
+
+#include <string>
+
+#include "google_breakpad/common/breakpad_types.h"
+#include "processor/test_assembler.h"
+
+namespace google_breakpad {
+
+using google_breakpad::TestAssembler::Endianness;
+using google_breakpad::TestAssembler::Label;
+using google_breakpad::TestAssembler::Section;
+using std::string;
+
+class CFISection: public Section {
+ public:
+ // Create a CFISection whose endianness is ENDIANNESS, and where
+ // machine addresses are ADDRESS_SIZE bytes long.
+ CFISection(Endianness endianness, size_t address_size)
+ : Section(endianness), address_size_(address_size),
+ entry_length_(NULL) {
+ // The 'start', 'Here', and 'Mark' members of a CFISection all refer
+ // to section offsets.
+ start() = 0;
+ }
+
+ // Return this CFISection's address size.
+ size_t AddressSize() const { return address_size_; }
+
+ // Append a Common Information Entry header to this section with the
+ // given values. If dwarf64 is true, use the 64-bit DWARF initial
+ // length format for the CIE's initial length. Return a reference to
+ // this section. You should call FinishEntry after writing the last
+ // instruction for the CIE.
+ //
+ // Before calling this function, you will typically want to use Mark
+ // or Here to make a label to pass to FDEHeader that refers to this
+ // CIE's position in the section.
+ CFISection &CIEHeader(u_int64_t code_alignment_factor,
+ int data_alignment_factor,
+ unsigned return_address_register,
+ u_int8_t version = 3,
+ const string &augmentation = "",
+ bool dwarf64 = false);
+
+ // Append a Frame Description Entry header to this section with the
+ // given values. If dwarf64 is true, use the 64-bit DWARF initial
+ // length format for the CIE's initial length. Return a reference to
+ // this section. You should call FinishEntry after writing the last
+ // instruction for the CIE.
+ //
+ // This function doesn't support entries that are longer than
+ // 0xffffff00 bytes. (The "initial length" is always a 32-bit
+ // value.) Nor does it support .debug_frame sections longer than
+ // 0xffffff00 bytes.
+ CFISection &FDEHeader(Label cie_pointer,
+ u_int64_t initial_location,
+ u_int64_t address_range,
+ bool dwarf64 = false);
+
+ // Note the current position as the end of the last CIE or FDE we
+ // started, after padding with DW_CFA_nops for alignment. This
+ // defines the label representing the entry's length, cited in the
+ // entry's header. Return a reference to this section.
+ CFISection &FinishEntry();
+
+ // Append the contents of BLOCK as a DW_FORM_block value: an
+ // unsigned LEB128 length, followed by that many bytes of data.
+ CFISection &Block(const string &block) {
+ ULEB128(block.size());
+ Append(block);
+ return *this;
+ }
+
+ // Restate some member functions, to keep chaining working nicely.
+ CFISection &Mark(Label *label) { Section::Mark(label); return *this; }
+ CFISection &D8(u_int8_t v) { Section::D8(v); return *this; }
+ CFISection &D16(u_int16_t v) { Section::D16(v); return *this; }
+ CFISection &D16(Label v) { Section::D16(v); return *this; }
+ CFISection &D32(u_int32_t v) { Section::D32(v); return *this; }
+ CFISection &D32(const Label &v) { Section::D32(v); return *this; }
+ CFISection &D64(u_int64_t v) { Section::D64(v); return *this; }
+ CFISection &D64(const Label &v) { Section::D64(v); return *this; }
+ CFISection &LEB128(long long v) { Section::LEB128(v); return *this; }
+ CFISection &ULEB128(u_int64_t v) { Section::ULEB128(v); return *this; }
+
+ private:
+ // A length value that we've appended to the section, but is not yet
+ // known. LENGTH is the appended value; START is a label referring
+ // to the start of the data whose length was cited.
+ struct PendingLength {
+ Label length;
+ Label start;
+ };
+
+ // The size of a machine address for the data in this section.
+ size_t address_size_;
+
+ // The length value for the current entry.
+ //
+ // Oddly, this must be dynamically allocated. Labels never get new
+ // values; they only acquire constraints on the value they already
+ // have, or assert if you assign them something incompatible. So
+ // each header needs truly fresh Label objects to cite in their
+ // headers and track their positions. The alternative is explicit
+ // destructor invocation and a placement new. Ick.
+ PendingLength *entry_length_;
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_CFI_ASSEMBLER_H_
diff --git a/src/common/dwarf/dwarf2enums.h b/src/common/dwarf/dwarf2enums.h
index 9e6c0278..1f8e628c 100644
--- a/src/common/dwarf/dwarf2enums.h
+++ b/src/common/dwarf/dwarf2enums.h
@@ -530,5 +530,48 @@ enum DwarfInline {
DW_INL_declared_inlined =0x3,
};
+// Call Frame Info instructions.
+enum DwarfCFI
+ {
+ DW_CFA_advance_loc = 0x40,
+ DW_CFA_offset = 0x80,
+ DW_CFA_restore = 0xc0,
+ DW_CFA_nop = 0x00,
+ DW_CFA_set_loc = 0x01,
+ DW_CFA_advance_loc1 = 0x02,
+ DW_CFA_advance_loc2 = 0x03,
+ DW_CFA_advance_loc4 = 0x04,
+ DW_CFA_offset_extended = 0x05,
+ DW_CFA_restore_extended = 0x06,
+ DW_CFA_undefined = 0x07,
+ DW_CFA_same_value = 0x08,
+ DW_CFA_register = 0x09,
+ DW_CFA_remember_state = 0x0a,
+ DW_CFA_restore_state = 0x0b,
+ DW_CFA_def_cfa = 0x0c,
+ DW_CFA_def_cfa_register = 0x0d,
+ DW_CFA_def_cfa_offset = 0x0e,
+ DW_CFA_def_cfa_expression = 0x0f,
+ DW_CFA_expression = 0x10,
+ DW_CFA_offset_extended_sf = 0x11,
+ DW_CFA_def_cfa_sf = 0x12,
+ DW_CFA_def_cfa_offset_sf = 0x13,
+ DW_CFA_val_offset = 0x14,
+ DW_CFA_val_offset_sf = 0x15,
+ DW_CFA_val_expression = 0x16,
+
+ // Opcodes in this range are reserved for user extensions.
+ DW_CFA_lo_user = 0x1c,
+ DW_CFA_hi_user = 0x3f,
+
+ // SGI/MIPS specific.
+ DW_CFA_MIPS_advance_loc8 = 0x1d,
+
+ // GNU extensions.
+ DW_CFA_GNU_window_save = 0x2d,
+ DW_CFA_GNU_args_size = 0x2e,
+ DW_CFA_GNU_negative_offset_extended = 0x2f
+ };
+
} // namespace dwarf2reader
#endif // COMMON_DWARF_DWARF2ENUMS_H__
diff --git a/src/common/dwarf/dwarf2reader.cc b/src/common/dwarf/dwarf2reader.cc
index ec30a309..fb6d7c4d 100644
--- a/src/common/dwarf/dwarf2reader.cc
+++ b/src/common/dwarf/dwarf2reader.cc
@@ -26,12 +26,15 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// Implementation of dwarf2reader::LineInfo and dwarf2reader::CompilationUnit.
-// See dwarf2reader.h for details.
+// CFI reader author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// Implementation of dwarf2reader::LineInfo, dwarf2reader::CompilationUnit,
+// and dwarf2reader::CallFrameInfo. See dwarf2reader.h for details.
#include <cassert>
#include <cstdio>
#include <cstring>
+#include <map>
#include <memory>
#include <stack>
#include <utility>
@@ -864,4 +867,1226 @@ void LineInfo::ReadLines() {
after_header_ = lengthstart + header_.total_length;
}
+// A DWARF rule for recovering the address or value of a register, or
+// computing the canonical frame address. There is one subclass of this for
+// each '*Rule' member function in CallFrameInfo::Handler.
+//
+// It's annoying that we have to handle Rules using pointers (because
+// the concrete instances can have an arbitrary size). They're small,
+// so it would be much nicer if we could just handle them by value
+// instead of fretting about ownership and destruction.
+//
+// It seems like all these could simply be instances of std::tr1::bind,
+// except that we need instances to be EqualityComparable, too.
+//
+// This could logically be nested within State, but then the qualified names
+// get horrendous.
+class CallFrameInfo::Rule {
+ public:
+ virtual ~Rule() { }
+
+ // Tell HANDLER that, at ADDRESS in the program, REGISTER can be
+ // recovered using this rule. If REGISTER is kCFARegister, then this rule
+ // describes how to compute the canonical frame address. Return what the
+ // HANDLER member function returned.
+ virtual bool Handle(Handler *handler,
+ uint64 address, int register) const = 0;
+
+ // Equality on rules. We use these to decide which rules we need
+ // to report after a DW_CFA_restore_state instruction.
+ virtual bool operator==(const Rule &rhs) const = 0;
+
+ bool operator!=(const Rule &rhs) const { return ! (*this == rhs); }
+
+ // Return a pointer to a copy of this rule.
+ virtual Rule *Copy() const = 0;
+
+ // If this is a base+offset rule, change its base register to REG.
+ // Otherwise, do nothing. (Ugly, but required for DW_CFA_def_cfa_register.)
+ virtual void SetBaseRegister(unsigned reg) { }
+
+ // If this is a base+offset rule, change its offset to OFFSET. Otherwise,
+ // do nothing. (Ugly, but required for DW_CFA_def_cfa_offset.)
+ virtual void SetOffset(long long offset) { }
+};
+
+// Rule: the value the register had in the caller cannot be recovered.
+class CallFrameInfo::UndefinedRule: public CallFrameInfo::Rule {
+ public:
+ UndefinedRule() { }
+ ~UndefinedRule() { }
+ bool Handle(Handler *handler, uint64 address, int reg) const {
+ return handler->UndefinedRule(address, reg);
+ }
+ bool operator==(const Rule &rhs) const {
+ // dynamic_cast is prohibited by Google C++ Style Guide, but justified.
+ const UndefinedRule *our_rhs = dynamic_cast<const UndefinedRule *>(&rhs);
+ return (our_rhs != NULL);
+ }
+ Rule *Copy() const { return new UndefinedRule(*this); }
+};
+
+// Rule: the register's value is the same as that it had in the caller.
+class CallFrameInfo::SameValueRule: public CallFrameInfo::Rule {
+ public:
+ SameValueRule() { }
+ ~SameValueRule() { }
+ bool Handle(Handler *handler, uint64 address, int reg) const {
+ return handler->SameValueRule(address, reg);
+ }
+ bool operator==(const Rule &rhs) const {
+ // dynamic_cast is prohibited by Google C++ Style Guide, but justified.
+ const SameValueRule *our_rhs = dynamic_cast<const SameValueRule *>(&rhs);
+ return (our_rhs != NULL);
+ }
+ Rule *Copy() const { return new SameValueRule(*this); }
+};
+
+// Rule: the register is saved at OFFSET from BASE_REGISTER. BASE_REGISTER
+// may be CallFrameInfo::Handler::kCFARegister.
+class CallFrameInfo::OffsetRule: public CallFrameInfo::Rule {
+ public:
+ OffsetRule(int base_register, long offset)
+ : base_register_(base_register), offset_(offset) { }
+ ~OffsetRule() { }
+ bool Handle(Handler *handler, uint64 address, int reg) const {
+ return handler->OffsetRule(address, reg, base_register_, offset_);
+ }
+ bool operator==(const Rule &rhs) const {
+ // dynamic_cast is prohibited by Google C++ Style Guide, but justified.
+ const OffsetRule *our_rhs = dynamic_cast<const OffsetRule *>(&rhs);
+ return (our_rhs &&
+ base_register_ == our_rhs->base_register_ &&
+ offset_ == our_rhs->offset_);
+ }
+ Rule *Copy() const { return new OffsetRule(*this); }
+ // We don't actually need SetBaseRegister or SetOffset here, since they
+ // are only ever applied to CFA rules, for DW_CFA_def_cfa_offset, and it
+ // doesn't make sense to use OffsetRule for computing the CFA: it
+ // computes the address at which a register is saved, not a value.
+ private:
+ int base_register_;
+ int offset_;
+};
+
+// Rule: the value the register had in the caller is the value of
+// BASE_REGISTER plus offset. BASE_REGISTER may be
+// CallFrameInfo::Handler::kCFARegister.
+class CallFrameInfo::ValOffsetRule: public CallFrameInfo::Rule {
+ public:
+ ValOffsetRule(int base_register, long offset)
+ : base_register_(base_register), offset_(offset) { }
+ ~ValOffsetRule() { }
+ bool Handle(Handler *handler, uint64 address, int reg) const {
+ return handler->ValOffsetRule(address, reg, base_register_, offset_);
+ }
+ bool operator==(const Rule &rhs) const {
+ // dynamic_cast is prohibited by Google C++ Style Guide, but justified.
+ const ValOffsetRule *our_rhs = dynamic_cast<const ValOffsetRule *>(&rhs);
+ return (our_rhs &&
+ base_register_ == our_rhs->base_register_ &&
+ offset_ == our_rhs->offset_);
+ }
+ Rule *Copy() const { return new ValOffsetRule(*this); }
+ void SetBaseRegister(unsigned reg) { base_register_ = reg; }
+ void SetOffset(long long offset) { offset_ = offset; }
+ private:
+ int base_register_;
+ int offset_;
+};
+
+// Rule: the register has been saved in another register REGISTER_NUMBER_.
+class CallFrameInfo::RegisterRule: public CallFrameInfo::Rule {
+ public:
+ explicit RegisterRule(int register_number)
+ : register_number_(register_number) { }
+ ~RegisterRule() { }
+ bool Handle(Handler *handler, uint64 address, int reg) const {
+ return handler->RegisterRule(address, reg, register_number_);
+ }
+ bool operator==(const Rule &rhs) const {
+ // dynamic_cast is prohibited by Google C++ Style Guide, but justified.
+ const RegisterRule *our_rhs = dynamic_cast<const RegisterRule *>(&rhs);
+ return (our_rhs && register_number_ == our_rhs->register_number_);
+ }
+ Rule *Copy() const { return new RegisterRule(*this); }
+ private:
+ int register_number_;
+};
+
+// Rule: EXPRESSION evaluates to the address at which the register is saved.
+class CallFrameInfo::ExpressionRule: public CallFrameInfo::Rule {
+ public:
+ explicit ExpressionRule(const string &expression)
+ : expression_(expression) { }
+ ~ExpressionRule() { }
+ bool Handle(Handler *handler, uint64 address, int reg) const {
+ return handler->ExpressionRule(address, reg, expression_);
+ }
+ bool operator==(const Rule &rhs) const {
+ // dynamic_cast is prohibited by Google C++ Style Guide, but justified.
+ const ExpressionRule *our_rhs = dynamic_cast<const ExpressionRule *>(&rhs);
+ return (our_rhs && expression_ == our_rhs->expression_);
+ }
+ Rule *Copy() const { return new ExpressionRule(*this); }
+ private:
+ string expression_;
+};
+
+// Rule: EXPRESSION evaluates to the address at which the register is saved.
+class CallFrameInfo::ValExpressionRule: public CallFrameInfo::Rule {
+ public:
+ explicit ValExpressionRule(const string &expression)
+ : expression_(expression) { }
+ ~ValExpressionRule() { }
+ bool Handle(Handler *handler, uint64 address, int reg) const {
+ return handler->ValExpressionRule(address, reg, expression_);
+ }
+ bool operator==(const Rule &rhs) const {
+ // dynamic_cast is prohibited by Google C++ Style Guide, but justified.
+ const ValExpressionRule *our_rhs =
+ dynamic_cast<const ValExpressionRule *>(&rhs);
+ return (our_rhs && expression_ == our_rhs->expression_);
+ }
+ Rule *Copy() const { return new ValExpressionRule(*this); }
+ private:
+ string expression_;
+};
+
+// A map from register numbers to rules.
+class CallFrameInfo::RuleMap {
+ public:
+ RuleMap() : cfa_rule_(NULL) { }
+ RuleMap(const RuleMap &rhs) : cfa_rule_(NULL) { *this = rhs; }
+ ~RuleMap() { Clear(); }
+
+ RuleMap &operator=(const RuleMap &rhs);
+
+ // Set the rule for computing the CFA to RULE. Take ownership of RULE.
+ void SetCFARule(Rule *rule) { delete cfa_rule_; cfa_rule_ = rule; }
+
+ // Return the current CFA rule. Unlike RegisterRule, this RuleMap retains
+ // ownership of the rule. We use this for DW_CFA_def_cfa_offset and
+ // DW_CFA_def_cfa_register, and for detecting references to the CFA before
+ // a rule for it has been established.
+ Rule *CFARule() const { return cfa_rule_; }
+
+ // Return the rule for REG, or NULL if there is none. The caller takes
+ // ownership of the result.
+ Rule *RegisterRule(int reg) const;
+
+ // Set the rule for computing REG to RULE. Take ownership of RULE.
+ void SetRegisterRule(int reg, Rule *rule);
+
+ // Make all the appropriate calls to HANDLER as if we were changing from
+ // this RuleMap to NEW_RULES at ADDRESS. We use this to implement
+ // DW_CFA_restore_state, where lots of rules can change simultaneously.
+ // Return true if all handlers returned true; otherwise, return false.
+ bool HandleTransitionTo(Handler *handler, uint64 address,
+ const RuleMap &new_rules) const;
+
+ private:
+ // A map from register numbers to Rules.
+ typedef map<int, Rule *> RuleByNumber;
+
+ // Remove all register rules and clear cfa_rule_.
+ void Clear();
+
+ // The rule for computing the canonical frame address. This RuleMap owns
+ // this rule.
+ Rule *cfa_rule_;
+
+ // A map from register numbers to postfix expressions to recover
+ // their values. This RuleMap owns the Rules the map refers to.
+ RuleByNumber registers_;
+};
+
+CallFrameInfo::RuleMap &CallFrameInfo::RuleMap::operator=(const RuleMap &rhs) {
+ Clear();
+ // Since each map owns the rules it refers to, assignment must copy them.
+ if (rhs.cfa_rule_) cfa_rule_ = rhs.cfa_rule_->Copy();
+ for (RuleByNumber::const_iterator it = rhs.registers_.begin();
+ it != rhs.registers_.end(); it++)
+ registers_[it->first] = it->second->Copy();
+ return *this;
+}
+
+CallFrameInfo::Rule *CallFrameInfo::RuleMap::RegisterRule(int reg) const {
+ assert(reg != Handler::kCFARegister);
+ RuleByNumber::const_iterator it = registers_.find(reg);
+ if (it != registers_.end())
+ return it->second->Copy();
+ else
+ return NULL;
+}
+
+void CallFrameInfo::RuleMap::SetRegisterRule(int reg, Rule *rule) {
+ assert(reg != Handler::kCFARegister);
+ assert(rule);
+ Rule **slot = &registers_[reg];
+ delete *slot;
+ *slot = rule;
+}
+
+bool CallFrameInfo::RuleMap::HandleTransitionTo(
+ Handler *handler,
+ uint64 address,
+ const RuleMap &new_rules) const {
+ // Transition from cfa_rule_ to new_rules.cfa_rule_.
+ if (cfa_rule_ && new_rules.cfa_rule_) {
+ if (*cfa_rule_ != *new_rules.cfa_rule_ &&
+ !new_rules.cfa_rule_->Handle(handler, address,
+ Handler::kCFARegister))
+ return false;
+ } else if (cfa_rule_) {
+ // this RuleMap has a CFA rule but new_rules doesn't.
+ // CallFrameInfo::Handler has no way to handle this --- and shouldn't;
+ // it's garbage input. The instruction interpreter should have
+ // detected this and warned, so take no action here.
+ } else if (new_rules.cfa_rule_) {
+ // This shouldn't be possible: NEW_RULES is some prior state, and
+ // there's no way to remove entries.
+ assert(0);
+ } else {
+ // Both CFA rules are empty. No action needed.
+ }
+
+ // Traverse the two maps in order by register number, and report
+ // whatever differences we find.
+ RuleByNumber::const_iterator old_it = registers_.begin();
+ RuleByNumber::const_iterator new_it = new_rules.registers_.begin();
+ while (old_it != registers_.end() && new_it != new_rules.registers_.end()) {
+ if (old_it->first < new_it->first) {
+ // This RuleMap has an entry for old_it->first, but NEW_RULES
+ // doesn't.
+ //
+ // This isn't really the right thing to do, but since CFI generally
+ // only mentions callee-saves registers, and GCC's convention for
+ // callee-saves registers is that they are unchanged, it's a good
+ // approximation.
+ if (!handler->SameValueRule(address, old_it->first))
+ return false;
+ old_it++;
+ } else if (old_it->first > new_it->first) {
+ // NEW_RULES has entry for new_it->first, but this RuleMap
+ // doesn't. This shouldn't be possible: NEW_RULES is some prior
+ // state, and there's no way to remove entries.
+ assert(0);
+ } else {
+ // Both maps have an entry for this register. Report the new
+ // rule if it is different.
+ if (*old_it->second != *new_it->second &&
+ !new_it->second->Handle(handler, address, new_it->first))
+ return false;
+ new_it++, old_it++;
+ }
+ }
+ // Finish off entries from this RuleMap with no counterparts in new_rules.
+ while (old_it != registers_.end()) {
+ if (!handler->SameValueRule(address, old_it->first))
+ return false;
+ old_it++;
+ }
+ // Since we only make transitions from a rule set to some previously
+ // saved rule set, and we can only add rules to the map, NEW_RULES
+ // must have fewer rules than *this.
+ assert(new_it == new_rules.registers_.end());
+
+ return true;
+}
+
+// Remove all register rules and clear cfa_rule_.
+void CallFrameInfo::RuleMap::Clear() {
+ delete cfa_rule_;
+ cfa_rule_ = NULL;
+ for (RuleByNumber::iterator it = registers_.begin();
+ it != registers_.end(); it++)
+ delete it->second;
+ registers_.clear();
+}
+
+// The state of the call frame information interpreter as it processes
+// instructions from a CIE and FDE.
+class CallFrameInfo::State {
+ public:
+ // Create a call frame information interpreter state with the given
+ // reporter, reader, handler, and initial call frame info address.
+ State(ByteReader *reader, Handler *handler, Reporter *reporter,
+ uint64 address)
+ : reader_(reader), handler_(handler), reporter_(reporter),
+ address_(address), entry_(NULL), cursor_(NULL) { }
+
+ // Interpret instructions from CIE, save the resulting rule set for
+ // DW_CFA_restore instructions, and return true. On error, report
+ // the problem to reporter_ and return false.
+ bool InterpretCIE(const CIE &cie);
+
+ // Interpret instructions from FDE, and return true. On error,
+ // report the problem to reporter_ and return false.
+ bool InterpretFDE(const FDE &fde);
+
+ private:
+ // The operands of a CFI instruction, for ParseOperands.
+ struct Operands {
+ unsigned register_number; // A register number.
+ uint64 offset; // An offset or address.
+ long signed_offset; // A signed offset.
+ string expression; // A DWARF expression.
+ };
+
+ // Parse CFI instruction operands from STATE's instruction stream as
+ // described by FORMAT. On success, populate OPERANDS with the
+ // results, and return true. On failure, report the problem and
+ // return false.
+ //
+ // Each character of FORMAT should be one of the following:
+ //
+ // 'r' unsigned LEB128 register number (OPERANDS->register_number)
+ // 'o' unsigned LEB128 offset (OPERANDS->offset)
+ // 's' signed LEB128 offset (OPERANDS->signed_offset)
+ // 'a' machine-size address (OPERANDS->offset)
+ // '1' a one-byte offset (OPERANDS->offset)
+ // '2' a two-byte offset (OPERANDS->offset)
+ // '4' a four-byte offset (OPERANDS->offset)
+ // '8' an eight-byte offset (OPERANDS->offset)
+ // 'e' a DW_FORM_block holding a (OPERANDS->expression)
+ // DWARF expression
+ bool ParseOperands(const char *format, Operands *operands);
+
+ // Interpret one CFI instruction from STATE's instruction stream, update
+ // STATE, report any rule changes to handler_, and return true. On
+ // failure, report the problem and return false.
+ bool DoInstruction();
+
+ // The following Do* member functions are subroutines of DoInstruction,
+ // factoring out the actual work of operations that have several
+ // different encodings.
+
+ // Set the CFA rule to be the value of BASE_REGISTER plus OFFSET, and
+ // return true. On failure, report and return false. (Used for
+ // DW_CFA_def_cfa and DW_CFA_def_cfa_sf.)
+ bool DoDefCFA(unsigned base_register, long offset);
+
+ // Change the offset of the CFA rule to OFFSET, and return true. On
+ // failure, report and return false. (Subroutine for
+ // DW_CFA_def_cfa_offset and DW_CFA_def_cfa_offset_sf.)
+ bool DoDefCFAOffset(long offset);
+
+ // Specify that REG can be recovered using RULE, and return true. On
+ // failure, report and return false.
+ bool DoRule(unsigned reg, Rule *rule);
+
+ // Specify that REG can be found at OFFSET from the CFA, and return true.
+ // On failure, report and return false. (Subroutine for DW_CFA_offset,
+ // DW_CFA_offset_extended, and DW_CFA_offset_extended_sf.)
+ bool DoOffset(unsigned reg, long offset);
+
+ // Specify that the caller's value for REG is the CFA plus OFFSET,
+ // and return true. On failure, report and return false. (Subroutine
+ // for DW_CFA_val_offset and DW_CFA_val_offset_sf.)
+ bool DoValOffset(unsigned reg, long offset);
+
+ // Restore REG to the rule established in the CIE, and return true. On
+ // failure, report and return false. (Subroutine for DW_CFA_restore and
+ // DW_CFA_restore_extended.)
+ bool DoRestore(unsigned reg);
+
+ // Return the section offset of the instruction at cursor. For use
+ // in error messages.
+ uint64 CursorOffset() { return entry_->offset + (cursor_ - entry_->start); }
+
+ // Report that entry_ is incomplete, and return false. For brevity.
+ bool ReportIncomplete() {
+ reporter_->Incomplete(entry_->offset, entry_->kind);
+ return false;
+ }
+
+ // For reading multi-byte values with the appropriate endianness.
+ ByteReader *reader_;
+
+ // The handler to which we should report the data we find.
+ Handler *handler_;
+
+ // For reporting problems in the info we're parsing.
+ Reporter *reporter_;
+
+ // The code address to which the next instruction in the stream applies.
+ uint64 address_;
+
+ // The entry whose instructions we are currently processing. This is
+ // first a CIE, and then an FDE.
+ const Entry *entry_;
+
+ // The next instruction to process.
+ const char *cursor_;
+
+ // The current set of rules.
+ RuleMap rules_;
+
+ // The set of rules established by the CIE, used by DW_CFA_restore
+ // and DW_CFA_restore_extended. We set this after interpreting the
+ // CIE's instructions.
+ RuleMap cie_rules_;
+
+ // A stack of saved states, for DW_CFA_remember_state and
+ // DW_CFA_restore_state.
+ stack<RuleMap> saved_rules_;
+};
+
+bool CallFrameInfo::State::InterpretCIE(const CIE &cie) {
+ entry_ = &cie;
+ cursor_ = entry_->instructions;
+ while (cursor_ < entry_->end)
+ if (!DoInstruction())
+ return false;
+ // Note the rules established by the CIE, for use by DW_CFA_restore
+ // and DW_CFA_restore_extended.
+ cie_rules_ = rules_;
+ return true;
+}
+
+bool CallFrameInfo::State::InterpretFDE(const FDE &fde) {
+ entry_ = &fde;
+ cursor_ = entry_->instructions;
+ while (cursor_ < entry_->end)
+ if (!DoInstruction())
+ return false;
+ return true;
+}
+
+bool CallFrameInfo::State::ParseOperands(const char *format,
+ Operands *operands) {
+ size_t len;
+ const char *operand;
+
+ for (operand = format; *operand; operand++) {
+ size_t bytes_left = entry_->end - cursor_;
+ switch (*operand) {
+ case 'r':
+ operands->register_number = reader_->ReadUnsignedLEB128(cursor_, &len);
+ if (len > bytes_left) return ReportIncomplete();
+ cursor_ += len;
+ break;
+
+ case 'o':
+ operands->offset = reader_->ReadUnsignedLEB128(cursor_, &len);
+ if (len > bytes_left) return ReportIncomplete();
+ cursor_ += len;
+ break;
+
+ case 's':
+ operands->signed_offset = reader_->ReadSignedLEB128(cursor_, &len);
+ if (len > bytes_left) return ReportIncomplete();
+ cursor_ += len;
+ break;
+
+ case 'a':
+ if (reader_->AddressSize() > bytes_left) return ReportIncomplete();
+ operands->offset = reader_->ReadAddress(cursor_);
+ cursor_ += reader_->AddressSize();
+ break;
+
+ case '1':
+ if (1 > bytes_left) return ReportIncomplete();
+ operands->offset = static_cast<unsigned char>(*cursor_++);
+ break;
+
+ case '2':
+ if (2 > bytes_left) return ReportIncomplete();
+ operands->offset = reader_->ReadTwoBytes(cursor_);
+ cursor_ += 2;
+ break;
+
+ case '4':
+ if (4 > bytes_left) return ReportIncomplete();
+ operands->offset = reader_->ReadFourBytes(cursor_);
+ cursor_ += 4;
+ break;
+
+ case '8':
+ if (8 > bytes_left) return ReportIncomplete();
+ operands->offset = reader_->ReadEightBytes(cursor_);
+ cursor_ += 8;
+ break;
+
+ case 'e': {
+ size_t expression_length = reader_->ReadUnsignedLEB128(cursor_, &len);
+ if (len > bytes_left || expression_length > bytes_left - len)
+ return ReportIncomplete();
+ cursor_ += len;
+ operands->expression = string(cursor_, expression_length);
+ cursor_ += expression_length;
+ break;
+ }
+
+ default:
+ assert(0);
+ }
+ }
+
+ return true;
+}
+
+bool CallFrameInfo::State::DoInstruction() {
+ CIE *cie = entry_->cie;
+ Operands ops;
+
+ // Our entry's kind should have been set by now.
+ assert(entry_->kind != kUnknown);
+
+ // We shouldn't have been invoked unless there were more
+ // instructions to parse.
+ assert(cursor_ < entry_->end);
+
+ unsigned opcode = *cursor_++;
+ if ((opcode & 0xc0) != 0) {
+ switch (opcode & 0xc0) {
+ // Advance the address.
+ case DW_CFA_advance_loc: {
+ size_t code_offset = opcode & 0x3f;
+ address_ += code_offset * cie->code_alignment_factor;
+ break;
+ }
+
+ // Find a register at an offset from the CFA.
+ case DW_CFA_offset:
+ if (!ParseOperands("o", &ops) ||
+ !DoOffset(opcode & 0x3f, ops.offset * cie->data_alignment_factor))
+ return false;
+ break;
+
+ // Restore the rule established for a register by the CIE.
+ case DW_CFA_restore:
+ if (!DoRestore(opcode & 0x3f)) return false;
+ break;
+
+ // The 'if' above should have excluded this possibility.
+ default:
+ assert(0);
+ }
+
+ // Return here, so the big switch below won't be indented.
+ return true;
+ }
+
+ switch (opcode) {
+ // Set the address.
+ case DW_CFA_set_loc:
+ if (!ParseOperands("a", &ops)) return false;
+ address_ = ops.offset;
+ break;
+
+ // Advance the address.
+ case DW_CFA_advance_loc1:
+ if (!ParseOperands("1", &ops)) return false;
+ address_ += ops.offset * cie->code_alignment_factor;
+ break;
+
+ // Advance the address.
+ case DW_CFA_advance_loc2:
+ if (!ParseOperands("2", &ops)) return false;
+ address_ += ops.offset * cie->code_alignment_factor;
+ break;
+
+ // Advance the address.
+ case DW_CFA_advance_loc4:
+ if (!ParseOperands("4", &ops)) return false;
+ address_ += ops.offset * cie->code_alignment_factor;
+ break;
+
+ // Advance the address.
+ case DW_CFA_MIPS_advance_loc8:
+ if (!ParseOperands("8", &ops)) return false;
+ address_ += ops.offset * cie->code_alignment_factor;
+ break;
+
+ // Compute the CFA by adding an offset to a register.
+ case DW_CFA_def_cfa:
+ if (!ParseOperands("ro", &ops) ||
+ !DoDefCFA(ops.register_number, ops.offset))
+ return false;
+ break;
+
+ // Compute the CFA by adding an offset to a register.
+ case DW_CFA_def_cfa_sf:
+ if (!ParseOperands("rs", &ops) ||
+ !DoDefCFA(ops.register_number,
+ ops.signed_offset * cie->data_alignment_factor))
+ return false;
+ break;
+
+ // Change the base register used to compute the CFA.
+ case DW_CFA_def_cfa_register: {
+ Rule *cfa_rule = rules_.CFARule();
+ if (!cfa_rule) {
+ reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
+ return false;
+ }
+ if (!ParseOperands("r", &ops)) return false;
+ cfa_rule->SetBaseRegister(ops.register_number);
+ if (!cfa_rule->Handle(handler_, address_,
+ Handler::kCFARegister))
+ return false;
+ break;
+ }
+
+ // Change the offset used to compute the CFA.
+ case DW_CFA_def_cfa_offset:
+ if (!ParseOperands("o", &ops) ||
+ !DoDefCFAOffset(ops.offset))
+ return false;
+ break;
+
+ // Change the offset used to compute the CFA.
+ case DW_CFA_def_cfa_offset_sf:
+ if (!ParseOperands("s", &ops) ||
+ !DoDefCFAOffset(ops.signed_offset * cie->data_alignment_factor))
+ return false;
+ break;
+
+ // Specify an expression whose value is the CFA.
+ case DW_CFA_def_cfa_expression: {
+ if (!ParseOperands("e", &ops))
+ return false;
+ Rule *rule = new ValExpressionRule(ops.expression);
+ rules_.SetCFARule(rule);
+ if (!rule->Handle(handler_, address_,
+ Handler::kCFARegister))
+ return false;
+ break;
+ }
+
+ // The register's value cannot be recovered.
+ case DW_CFA_undefined: {
+ if (!ParseOperands("r", &ops) ||
+ !DoRule(ops.register_number, new UndefinedRule()))
+ return false;
+ break;
+ }
+
+ // The register's value is unchanged from its value in the caller.
+ case DW_CFA_same_value: {
+ if (!ParseOperands("r", &ops) ||
+ !DoRule(ops.register_number, new SameValueRule()))
+ return false;
+ break;
+ }
+
+ // Find a register at an offset from the CFA.
+ case DW_CFA_offset_extended:
+ if (!ParseOperands("ro", &ops) ||
+ !DoOffset(ops.register_number,
+ ops.offset * cie->data_alignment_factor))
+ return false;
+ break;
+
+ // The register is saved at an offset from the CFA.
+ case DW_CFA_offset_extended_sf:
+ if (!ParseOperands("rs", &ops) ||
+ !DoOffset(ops.register_number,
+ ops.signed_offset * cie->data_alignment_factor))
+ return false;
+ break;
+
+ // The register is saved at an offset from the CFA.
+ case DW_CFA_GNU_negative_offset_extended:
+ if (!ParseOperands("ro", &ops) ||
+ !DoOffset(ops.register_number,
+ -ops.offset * cie->data_alignment_factor))
+ return false;
+ break;
+
+ // The register's value is the sum of the CFA plus an offset.
+ case DW_CFA_val_offset:
+ if (!ParseOperands("ro", &ops) ||
+ !DoValOffset(ops.register_number,
+ ops.offset * cie->data_alignment_factor))
+ return false;
+ break;
+
+ // The register's value is the sum of the CFA plus an offset.
+ case DW_CFA_val_offset_sf:
+ if (!ParseOperands("rs", &ops) ||
+ !DoValOffset(ops.register_number,
+ ops.signed_offset * cie->data_alignment_factor))
+ return false;
+ break;
+
+ // The register has been saved in another register.
+ case DW_CFA_register: {
+ if (!ParseOperands("ro", &ops) ||
+ !DoRule(ops.register_number, new RegisterRule(ops.offset)))
+ return false;
+ break;
+ }
+
+ // An expression yields the address at which the register is saved.
+ case DW_CFA_expression: {
+ if (!ParseOperands("re", &ops) ||
+ !DoRule(ops.register_number, new ExpressionRule(ops.expression)))
+ return false;
+ break;
+ }
+
+ // An expression yields the caller's value for the register.
+ case DW_CFA_val_expression: {
+ if (!ParseOperands("re", &ops) ||
+ !DoRule(ops.register_number, new ValExpressionRule(ops.expression)))
+ return false;
+ break;
+ }
+
+ // Restore the rule established for a register by the CIE.
+ case DW_CFA_restore_extended:
+ if (!ParseOperands("r", &ops) ||
+ !DoRestore( ops.register_number))
+ return false;
+ break;
+
+ // Save the current set of rules on a stack.
+ case DW_CFA_remember_state:
+ saved_rules_.push(rules_);
+ break;
+
+ // Pop the current set of rules off the stack.
+ case DW_CFA_restore_state: {
+ if (saved_rules_.empty()) {
+ reporter_->EmptyStateStack(entry_->offset, entry_->kind,
+ CursorOffset());
+ return false;
+ }
+ const RuleMap &new_rules = saved_rules_.top();
+ if (rules_.CFARule() && !new_rules.CFARule()) {
+ reporter_->ClearingCFARule(entry_->offset, entry_->kind,
+ CursorOffset());
+ return false;
+ }
+ rules_.HandleTransitionTo(handler_, address_, new_rules);
+ rules_ = new_rules;
+ saved_rules_.pop();
+ break;
+ }
+
+ // No operation. (Padding instruction.)
+ case DW_CFA_nop:
+ break;
+
+ // A SPARC register window save: Registers 8 through 15 (%o0-%o7)
+ // are saved in registers 24 through 31 (%i0-%i7), and registers
+ // 16 through 31 (%l0-%l7 and %i0-%i7) are saved at CFA offsets
+ // (0-15 * the register size). The register numbers must be
+ // hard-coded. A GNU extension, and not a pretty one.
+ case DW_CFA_GNU_window_save: {
+ // Save %o0-%o7 in %i0-%i7.
+ for (int i = 8; i < 16; i++)
+ if (!DoRule(i, new RegisterRule(i + 16)))
+ return false;
+ // Save %l0-%l7 and %i0-%i7 at the CFA.
+ for (int i = 16; i < 32; i++)
+ // Assume that the byte reader's address size is the same as
+ // the architecture's register size. !@#%*^ hilarious.
+ if (!DoRule(i, new OffsetRule(Handler::kCFARegister,
+ (i - 16) * reader_->AddressSize())))
+ return false;
+ break;
+ }
+
+ // I'm not sure what this is. GDB doesn't use it for unwinding.
+ case DW_CFA_GNU_args_size:
+ if (!ParseOperands("o", &ops)) return false;
+ break;
+
+ // An opcode we don't recognize.
+ default: {
+ reporter_->BadInstruction(entry_->offset, entry_->kind, CursorOffset());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool CallFrameInfo::State::DoDefCFA(unsigned base_register, long offset) {
+ Rule *rule = new ValOffsetRule(base_register, offset);
+ rules_.SetCFARule(rule);
+ return rule->Handle(handler_, address_,
+ Handler::kCFARegister);
+}
+
+bool CallFrameInfo::State::DoDefCFAOffset(long offset) {
+ Rule *cfa_rule = rules_.CFARule();
+ if (!cfa_rule) {
+ reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
+ return false;
+ }
+ cfa_rule->SetOffset(offset);
+ return cfa_rule->Handle(handler_, address_,
+ Handler::kCFARegister);
+}
+
+bool CallFrameInfo::State::DoRule(unsigned reg, Rule *rule) {
+ rules_.SetRegisterRule(reg, rule);
+ return rule->Handle(handler_, address_, reg);
+}
+
+bool CallFrameInfo::State::DoOffset(unsigned reg, long offset) {
+ if (!rules_.CFARule()) {
+ reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
+ return false;
+ }
+ return DoRule(reg,
+ new OffsetRule(Handler::kCFARegister, offset));
+}
+
+bool CallFrameInfo::State::DoValOffset(unsigned reg, long offset) {
+ if (!rules_.CFARule()) {
+ reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
+ return false;
+ }
+ return DoRule(reg,
+ new ValOffsetRule(Handler::kCFARegister, offset));
+}
+
+bool CallFrameInfo::State::DoRestore(unsigned reg) {
+ // DW_CFA_restore and DW_CFA_restore_extended don't make sense in a CIE.
+ if (entry_->kind == kCIE) {
+ reporter_->RestoreInCIE(entry_->offset, CursorOffset());
+ return false;
+ }
+ Rule *rule = cie_rules_.RegisterRule(reg);
+ if (!rule) {
+ // This isn't really the right thing to do, but since CFI generally
+ // only mentions callee-saves registers, and GCC's convention for
+ // callee-saves registers is that they are unchanged, it's a good
+ // approximation.
+ rule = new SameValueRule();
+ }
+ return DoRule(reg, rule);
+}
+
+bool CallFrameInfo::ReadEntryPrologue(const char *cursor, Entry *entry) {
+ const char *buffer_end = buffer_ + buffer_length_;
+
+ // Initialize enough of ENTRY for use in error reporting.
+ entry->offset = cursor - buffer_;
+ entry->start = cursor;
+ entry->kind = kUnknown;
+ entry->end = NULL;
+
+ // Read the initial length. This sets reader_'s offset size. The length
+ // could be something like (uint64)-1, so we have to do two comparisons
+ // here.
+ size_t length_size;
+ uint64 length = reader_->ReadInitialLength(cursor, &length_size);
+ if (length_size > size_t(buffer_end - cursor) ||
+ length > size_t(buffer_end - (cursor + length_size)))
+ return ReportIncomplete(entry);
+ cursor += length_size;
+
+ // The length is the number of bytes after the initial length field;
+ // we have that position handy at this point, so compute the end
+ // now. (If we're parsing 64-bit-offset DWARF on a 32-bit machine,
+ // and the length didn't fit in a size_t, we would have rejected it
+ // above.)
+ entry->end = cursor + length;
+
+ // Parse the next field: either the offset of a CIE or a CIE id.
+ size_t offset_size = reader_->OffsetSize();
+ if (offset_size > size_t(entry->end - cursor)) return ReportIncomplete(entry);
+ entry->id = reader_->ReadOffset(cursor);
+ cursor += offset_size;
+
+ // Now we can decide what kind of entry this is.
+ if (offset_size == 4)
+ entry->kind = (entry->id == 0xffffffff) ? kCIE : kFDE;
+ else {
+ assert(offset_size == 8);
+ entry->kind = (entry->id == 0xffffffffffffffffULL) ? kCIE : kFDE;
+ }
+
+ // The fields specific to this kind of entry start here.
+ entry->fields = cursor;
+
+ entry->cie = NULL;
+
+ return true;
+}
+
+bool CallFrameInfo::ReadCIEFields(CIE *cie) {
+ const char *cursor = cie->fields;
+ size_t len;
+
+ assert(cie->kind == kCIE);
+
+ // Prepare for early exit.
+ cie->version = 0;
+ cie->augmentation.clear();
+ cie->code_alignment_factor = 0;
+ cie->data_alignment_factor = 0;
+ cie->return_address_register = 0;
+ cie->instructions = 0;
+
+ // Parse the version number.
+ if (cie->end - cursor < 1)
+ return ReportIncomplete(cie);
+ cie->version = reader_->ReadOneByte(cursor);
+ cursor++;
+
+ // If we don't recognize the version, we can't parse any more fields
+ // of the CIE.
+ if (cie->version < 1 || 3 < cie->version) {
+ reporter_->UnrecognizedVersion(cie->offset, cie->version);
+ return false;
+ }
+
+ const char *augmentation_start = cursor;
+ const void *augmentation_end =
+ memchr(augmentation_start, '\0', cie->end - augmentation_start);
+ if (! augmentation_end) return ReportIncomplete(cie);
+ cursor = static_cast<const char *>(augmentation_end);
+ cie->augmentation = string(augmentation_start, cursor - augmentation_start);
+ // Skip the terminating '\0'.
+ cursor++;
+
+ // If we don't recognize this augmentation, we can't parse any more
+ // fields of the CIE.
+ if (!cie->augmentation.empty()) {
+ // Augmentations can have arbitrary effects on the form of rest of
+ // the content, so we have to give up.
+ reporter_->UnrecognizedAugmentation(cie->offset, cie->augmentation);
+ return false;
+ }
+
+ // Parse the code alignment factor.
+ cie->code_alignment_factor = reader_->ReadUnsignedLEB128(cursor, &len);
+ if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
+ cursor += len;
+
+ // Parse the data alignment factor.
+ cie->data_alignment_factor = reader_->ReadSignedLEB128(cursor, &len);
+ if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
+ cursor += len;
+
+ // Parse the return address register. This is a ubyte in version 1, and
+ // a ULEB128 in version 3.
+ if (cie->version == 1) {
+ if (cursor >= cie->end) return ReportIncomplete(cie);
+ cie->return_address_register = uint8(*cursor++);
+ } else {
+ cie->return_address_register = reader_->ReadUnsignedLEB128(cursor, &len);
+ if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
+ cursor += len;
+ }
+
+ // The CIE's instructions start here.
+ cie->instructions = cursor;
+
+ return true;
+}
+
+bool CallFrameInfo::ReadFDEFields(FDE *fde) {
+ const char *cursor = fde->fields;
+ size_t address_size = reader_->AddressSize();
+
+ // Since both fields are of known size, we can do all bounds
+ // checking here.
+ if (size_t(fde->end - cursor) < 2 * address_size)
+ return ReportIncomplete(fde);
+
+ // Parse the start address and size.
+ fde->address = reader_->ReadAddress(cursor);
+ fde->size = reader_->ReadAddress(cursor + address_size);
+
+ // The FDE's instructions start after those.
+ fde->instructions = cursor + 2 * address_size;
+
+ return true;
+}
+
+bool CallFrameInfo::Start() {
+ const char *buffer_end = buffer_ + buffer_length_;
+ const char *cursor;
+ bool all_ok = true;
+ const char *entry_end;
+ bool ok;
+
+ // Traverse all the entries in buffer_, skipping CIEs and offering
+ // FDEs to the handler.
+ for (cursor = buffer_; cursor < buffer_end;
+ cursor = entry_end, all_ok = all_ok && ok) {
+ FDE fde;
+
+ // Read the entry's prologue.
+ if (!ReadEntryPrologue(cursor, &fde))
+ // We can't continue processing the section, because we may not
+ // have gotten the length.
+ return false;
+
+ // Make it easy to skip this entry with 'continue': assume that
+ // things are not okay until we've checked all the data, and
+ // prepare the address of the next entry.
+ ok = false;
+ entry_end = fde.end;
+
+ // In this loop, we skip CIEs. We only parse them fully when we
+ // parse an FDE that refers to them. This limits our memory
+ // consumption (beyond the buffer itself) to that needed to
+ // process the largest single entry.
+ if (fde.kind != kFDE) {
+ ok = true;
+ continue;
+ }
+
+ // Validate the CIE pointer.
+ if (fde.id > buffer_length_) {
+ reporter_->CIEPointerOutOfRange(fde.offset, fde.id);
+ continue;
+ }
+
+ CIE cie;
+
+ // Parse this FDE's CIE header.
+ if (!ReadEntryPrologue(buffer_ + fde.id, &cie))
+ continue;
+ // This had better be an actual CIE.
+ if (cie.kind != kCIE) {
+ reporter_->BadCIEId(fde.offset, fde.id);
+ continue;
+ }
+ if (!ReadCIEFields(&cie))
+ continue;
+
+ // We now have the values that govern both the CIE and the FDE.
+ cie.cie = &cie;
+ fde.cie = &cie;
+
+ // Parse the FDE's header.
+ if (!ReadFDEFields(&fde))
+ continue;
+
+ // Call Entry to ask the consumer if they're interested.
+ if (!handler_->Entry(fde.offset, fde.address, fde.size,
+ cie.version, cie.augmentation,
+ cie.return_address_register)) {
+ // The handler isn't interested in this entry. That's not an error.
+ ok = true;
+ continue;
+ }
+
+ // Interpret the CIE's instructions, and then the FDE's instructions.
+ State state(reader_, handler_, reporter_, fde.address);
+ ok = state.InterpretCIE(cie) && state.InterpretFDE(fde);
+
+ // Report the end of the entry.
+ handler_->End();
+ }
+
+ return all_ok;
+}
+
+const char *CallFrameInfo::KindName(EntryKind kind) {
+ if (kind == CallFrameInfo::kUnknown)
+ return "entry";
+ else if (kind == CallFrameInfo::kCIE)
+ return "common information entry";
+ else {
+ assert(kind == CallFrameInfo::kFDE);
+ return "frame description entry";
+ }
+}
+
+bool CallFrameInfo::ReportIncomplete(Entry *entry) {
+ reporter_->Incomplete(entry->offset, entry->kind);
+ return false;
+}
+
+void CallFrameInfo::Reporter::Incomplete(uint64 offset,
+ CallFrameInfo::EntryKind kind) {
+ fprintf(stderr,
+ "%s: CFI %s at offset 0x%llx in '%s': entry ends early\n",
+ filename_.c_str(), CallFrameInfo::KindName(kind), offset,
+ section_.c_str());
+}
+
+void CallFrameInfo::Reporter::CIEPointerOutOfRange(uint64 offset,
+ uint64 cie_offset) {
+ fprintf(stderr,
+ "%s: CFI frame description entry at offset 0x%llx in '%s':"
+ " CIE pointer is out of range: 0x%llx\n",
+ filename_.c_str(), offset, section_.c_str(), cie_offset);
+}
+
+void CallFrameInfo::Reporter::BadCIEId(uint64 offset, uint64 cie_offset) {
+ fprintf(stderr,
+ "%s: CFI frame description entry at offset 0x%llx in '%s':"
+ " CIE pointer does not point to a CIE: 0x%llx\n",
+ filename_.c_str(), offset, section_.c_str(), cie_offset);
+}
+
+void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) {
+ fprintf(stderr,
+ "%s: CFI frame description entry at offset 0x%llx in '%s':"
+ " CIE specifies unrecognized version: %d\n",
+ filename_.c_str(), offset, section_.c_str(), version);
+}
+
+void CallFrameInfo::Reporter::UnrecognizedAugmentation(uint64 offset,
+ const string &aug) {
+ fprintf(stderr,
+ "%s: CFI frame description entry at offset 0x%llx in '%s':"
+ " CIE specifies unrecognized augmentation: '%s'\n",
+ filename_.c_str(), offset, section_.c_str(), aug.c_str());
+}
+
+void CallFrameInfo::Reporter::RestoreInCIE(uint64 offset, uint64 insn_offset) {
+ fprintf(stderr,
+ "%s: CFI common information entry at offset 0x%llx in '%s':"
+ " the DW_CFA_restore instruction at offset 0x%llx"
+ " cannot be used in a common information entry\n",
+ filename_.c_str(), offset, section_.c_str(), insn_offset);
+}
+
+void CallFrameInfo::Reporter::BadInstruction(uint64 offset,
+ CallFrameInfo::EntryKind kind,
+ uint64 insn_offset) {
+ fprintf(stderr,
+ "%s: CFI %s at offset 0x%llx in section '%s':"
+ " the instruction at offset 0x%llx is unrecognized\n",
+ filename_.c_str(), CallFrameInfo::KindName(kind),
+ offset, section_.c_str(), insn_offset);
+}
+
+void CallFrameInfo::Reporter::NoCFARule(uint64 offset,
+ CallFrameInfo::EntryKind kind,
+ uint64 insn_offset) {
+ fprintf(stderr,
+ "%s: CFI %s at offset 0x%llx in section '%s':"
+ " the instruction at offset 0x%llx assumes that a CFA rule has"
+ " been set, but none has been set\n",
+ filename_.c_str(), CallFrameInfo::KindName(kind), offset,
+ section_.c_str(), insn_offset);
+}
+
+void CallFrameInfo::Reporter::EmptyStateStack(uint64 offset,
+ CallFrameInfo::EntryKind kind,
+ uint64 insn_offset) {
+ fprintf(stderr,
+ "%s: CFI %s at offset 0x%llx in section '%s':"
+ " the DW_CFA_restore_state instruction at offset 0x%llx"
+ " should pop a saved state from the stack, but the stack is empty\n",
+ filename_.c_str(), CallFrameInfo::KindName(kind), offset,
+ section_.c_str(), insn_offset);
+}
+
+void CallFrameInfo::Reporter::ClearingCFARule(uint64 offset,
+ CallFrameInfo::EntryKind kind,
+ uint64 insn_offset) {
+ fprintf(stderr,
+ "%s: CFI %s at offset 0x%llx in section '%s':"
+ " the DW_CFA_restore_state instruction at offset 0x%llx"
+ " would clear the CFA rule in effect\n",
+ filename_.c_str(), CallFrameInfo::KindName(kind), offset,
+ section_.c_str(), insn_offset);
+}
+
} // namespace dwarf2reader
diff --git a/src/common/dwarf/dwarf2reader.h b/src/common/dwarf/dwarf2reader.h
index d99de334..136b8932 100644
--- a/src/common/dwarf/dwarf2reader.h
+++ b/src/common/dwarf/dwarf2reader.h
@@ -1,3 +1,5 @@
+// -*- mode: C++ -*-
+
// Copyright (c) 2010 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -26,6 +28,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// CFI reader author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
// This file contains definitions related to the DWARF2/3 reader and
// it's handler interfaces.
// The DWARF2/3 specification can be found at
@@ -398,6 +402,470 @@ class Dwarf2Handler {
};
+// This class is a reader for DWARF's Call Frame Information. CFI
+// describes how to unwind stack frames --- even for functions that do
+// not follow fixed conventions for saving registers, whose frame size
+// varies as they execute, etc.
+//
+// CFI describes, at each machine instruction, how to compute the
+// stack frame's base address, how to find the return address, and
+// where to find the saved values of the caller's registers (if the
+// callee has stashed them somewhere to free up the registers for its
+// own use).
+//
+// For example, suppose we have a function whose machine code looks
+// like this (imagine an assembly language that looks like C, for a
+// machine with 32-bit registers, and a stack that grows towards lower
+// addresses):
+//
+// func: ; entry point; return address at sp
+// func+0: sp = sp - 16 ; allocate space for stack frame
+// func+1: sp[12] = r0 ; save r0 at sp+12
+// ... ; other code, not frame-related
+// func+10: sp -= 4; *sp = x ; push some x on the stack
+// ... ; other code, not frame-related
+// func+20: r0 = sp[16] ; restore saved r0
+// func+21: sp += 20 ; pop whole stack frame
+// func+22: pc = *sp; sp += 4 ; pop return address and jump to it
+//
+// DWARF CFI is (a very compressed representation of) a table with a
+// row for each machine instruction address and a column for each
+// register showing how to restore it, if possible.
+//
+// A special column named "CFA", for "Canonical Frame Address", tells how
+// to compute the base address of the frame; registers' entries may
+// refer to the CFA in describing where the registers are saved.
+//
+// Another special column, named "RA", represents the return address.
+//
+// For example, here is a complete (uncompressed) table describing the
+// function above:
+//
+// insn cfa r0 r1 ... ra
+// =======================================
+// func+0: sp cfa[0]
+// func+1: sp+16 cfa[0]
+// func+2: sp+16 cfa[-4] cfa[0]
+// func+11: sp+20 cfa[-4] cfa[0]
+// func+21: sp+20 cfa[0]
+// func+22: sp cfa[0]
+//
+// Some things to note here:
+//
+// - Each row describes the state of affairs *before* executing the
+// instruction at the given address. Thus, the row for func+0
+// describes the state before we allocate the stack frame. In the
+// next row, the formula for computing the CFA has changed,
+// reflecting that allocation.
+//
+// - The other entries are written in terms of the CFA; this allows
+// them to remain unchanged as the stack pointer gets bumped around.
+// For example, the rule for recovering the return address (the "ra"
+// column) remains unchanged throughout the function, even as the
+// stack pointer takes on three different offsets from the return
+// address.
+//
+// - Although we haven't shown it, most calling conventions designate
+// "callee-saves" and "caller-saves" registers. The callee must
+// preserve the values of callee-saves registers; if it uses them,
+// it must save their original values somewhere, and restore them
+// before it returns. In contrast, the callee is free to trash
+// caller-saves registers; if the callee uses these, it will
+// probably not bother to save them anywhere, and the CFI will
+// probably mark their values as "unrecoverable".
+//
+// (However, since the caller cannot assume the callee was going to
+// save them, caller-saves registers are probably dead in the caller
+// anyway, so compilers usually don't generate CFA for caller-saves
+// registers.)
+//
+// - Exactly where the CFA points is a matter of convention that
+// depends on the architecture and ABI in use. In the example, the
+// CFA is the value the stack pointer had upon entry to the
+// function, pointing at the saved return address. But on the x86,
+// the call frame information generated by GCC follows the
+// convention that the CFA is the address *after* the saved return
+// address.
+//
+// But by definition, the CFA remains constant throughout the
+// lifetime of the frame. This makes it a useful value for other
+// columns to refer to. It is also gives debuggers a useful handle
+// for identifying a frame.
+//
+// If you look at the table above, you'll notice that a given entry is
+// often the same as the one immediately above it: most instructions
+// change only one or two aspects of the stack frame, if they affect
+// it at all. The DWARF format takes advantage of this fact, and
+// reduces the size of the data by mentioning only the addresses and
+// columns at which changes take place. So for the above, DWARF CFI
+// data would only actually mention the following:
+//
+// insn cfa r0 r1 ... ra
+// =======================================
+// func+0: sp cfa[0]
+// func+1: sp+16
+// func+2: cfa[-4]
+// func+11: sp+20
+// func+21: r0
+// func+22: sp
+//
+// In fact, this is the way the parser reports CFI to the consumer: as
+// a series of statements of the form, "At address X, column Y changed
+// to Z," and related conventions for describing the initial state.
+//
+// Naturally, it would be impractical to have to scan the entire
+// program's CFI, noting changes as we go, just to recover the
+// unwinding rules in effect at one particular instruction. To avoid
+// this, CFI data is grouped into "entries", each of which covers a
+// specified range of addresses and begins with a complete statement
+// of the rules for all recoverable registers at that starting
+// address. Each entry typically covers a single function.
+//
+// Thus, to compute the contents of a given row of the table --- that
+// is, rules for recovering the CFA, RA, and registers at a given
+// instruction --- the consumer should find the entry that covers that
+// instruction's address, start with the initial state supplied at the
+// beginning of the entry, and work forward until it has processed all
+// the changes up to and including those for the present instruction.
+//
+// There are seven kinds of rules that can appear in an entry of the
+// table:
+//
+// - "undefined": The given register is not preserved by the callee;
+// its value cannot be recovered.
+//
+// - "same value": This register has the same value it did in the callee.
+//
+// - offset(N): The register is saved at offset N from the CFA.
+//
+// - val_offset(N): The value the register had in the caller is the
+// CFA plus offset N. (This is usually only useful for describing
+// the stack pointer.)
+//
+// - register(R): The register's value was saved in another register R.
+//
+// - expression(E): Evaluating the DWARF expression E using the
+// current frame's registers' values yields the address at which the
+// register was saved.
+//
+// - val_expression(E): Evaluating the DWARF expression E using the
+// current frame's registers' values yields the value the register
+// had in the caller.
+
+class CallFrameInfo {
+ public:
+ // The different kinds of entries one finds in CFI. Used internally,
+ // and for error reporting.
+ enum EntryKind { kUnknown, kCIE, kFDE };
+
+ // The handler class to which the parser hands the parsed call frame
+ // information. Defined below.
+ class Handler;
+
+ // A reporter class, which CallFrameInfo uses to report errors
+ // encountered while parsing call frame information. Defined below.
+ class Reporter;
+
+ // Create a DWARF CFI parser. BUFFER points to the contents of the
+ // .debug_frame section to parse; BUFFER_LENGTH is its length in
+ // bytes. REPORTER is an error reporter the parser should use to
+ // report problems. READER is a ByteReader instance that has the
+ // endianness and address size set properly. Report the data we find
+ // to HANDLER.
+ CallFrameInfo(const char *buffer, size_t buffer_length,
+ ByteReader *reader, Handler *handler,
+ Reporter *reporter)
+ : buffer_(buffer),
+ buffer_length_(buffer_length),
+ reader_(reader),
+ handler_(handler),
+ reporter_(reporter) { }
+ ~CallFrameInfo() { }
+
+ // Parse the entries in BUFFER, reporting what we find to HANDLER.
+ // Return true if we reach the end of the section successfully, or
+ // false if we encounter an error.
+ bool Start();
+
+ // Return the textual name of KIND. For error reporting.
+ static const char *KindName(EntryKind kind);
+
+ private:
+
+ struct CIE;
+
+ // A CFI entry, either an FDE or a CIE.
+ struct Entry {
+ // The starting offset of the entry in the section, for error
+ // reporting.
+ size_t offset;
+
+ // The start of this entry in the buffer.
+ const char *start;
+
+ // The end of this entry's common prologue (initial length and id), and
+ // the start of this entry's kind-specific fields.
+ const char *fields;
+
+ // The start of this entry's instructions.
+ const char *instructions;
+
+ // The address past the entry's last byte in the buffer. (Note that
+ // since offset points to the entry's initial length field, and the
+ // length field is the number of bytes after that field, this is not
+ // simply buffer_ + offset + length.)
+ const char *end;
+
+ // The CIE pointer or CIE id field.
+ uint64 id;
+
+ // The kind of entry we're parsing.
+ //
+ // This may be kUnknown at times, since we want to be able to
+ // count on it for error reporting even before we've finished
+ // parsing enough to tell what kind of entry we're looking at.
+ EntryKind kind;
+
+ // The CIE that applies to this entry, if we've parsed it. If this is a
+ // CIE, then this field points to this structure.
+ CIE *cie;
+ };
+
+ // A common information entry (CIE).
+ struct CIE: public Entry {
+ uint8 version; // CFI data version number
+ string augmentation; // vendor format extension markers
+ uint64 code_alignment_factor; // scale for code address adjustments
+ int data_alignment_factor; // scale for stack pointer adjustments
+ unsigned return_address_register; // which register holds the return addr
+ };
+
+ // A frame description entry (FDE).
+ struct FDE: public Entry {
+ uint64 address; // start address of described code
+ uint64 size; // size of described code, in bytes
+ };
+
+ // Internal use.
+ class Rule;
+ class UndefinedRule;
+ class SameValueRule;
+ class OffsetRule;
+ class ValOffsetRule;
+ class RegisterRule;
+ class ExpressionRule;
+ class ValExpressionRule;
+ class RuleMap;
+ class State;
+
+ // Parse the initial length and id of a CFI entry, either a CIE or an
+ // FDE. CURSOR points to the beginning of the data to parse.
+ // On success, populate ENTRY as appropriate, and return true.
+ // On failure, report the problem, and return false.
+ bool ReadEntryPrologue(const char *cursor, Entry *entry);
+
+ // Parse the fields of a CIE after the entry prologue. Assume that the
+ // 'Entry' fields of CIE are populated; use CIE->fields and CIE->end as
+ // the start and limit for parsing. On success, populate the rest of
+ // *CIE, and return true; on failure, report the problem and return
+ // false.
+ bool ReadCIEFields(CIE *cie);
+
+ // Parse the fields of an FDE after the entry prologue. Assume that the
+ // 'Entry' fields of *FDE are initialized; use FDE->fields and FDE->end
+ // as the start and limit for parsing. Assume that FDE->cie is fully
+ // initialized. On success, populate the rest of *FDE, and return true;
+ // on failure, report the problem and return false.
+ bool ReadFDEFields(FDE *fde);
+
+ // Report that ENTRY is incomplete, and return false. This is just a
+ // trivial wrapper for invoking reporter_->Incomplete; it provides a
+ // little brevity.
+ bool ReportIncomplete(Entry *entry);
+
+ // The contents of the DWARF .debug_info section we're parsing.
+ const char *buffer_;
+ size_t buffer_length_;
+
+ // For reading multi-byte values with the appropriate endianness.
+ ByteReader *reader_;
+
+ // The handler to which we should report the data we find.
+ Handler *handler_;
+
+ // For reporting problems in the info we're parsing.
+ Reporter *reporter_;
+};
+
+// The handler class for CallFrameInfo. The a CFI parser calls the
+// member functions of a handler object to report the data it finds.
+class CallFrameInfo::Handler {
+ public:
+ // The pseudo-register number for the canonical frame address.
+ enum { kCFARegister = -1 };
+
+ Handler() { }
+ virtual ~Handler() { }
+
+ // The parser has found CFI for the machine code at ADDRESS,
+ // extending for LENGTH bytes. OFFSET is the offset of the frame
+ // description entry in the section, for use in error messages.
+ // VERSION is the version number of the CFI format. AUGMENTATION is
+ // a string describing any producer-specific extensions present in
+ // the data. RETURN_ADDRESS is the number of the register that holds
+ // the address to which the function should return.
+ //
+ // Entry should return true to process this CFI, or false to skip to
+ // the next entry.
+ //
+ // The parser invokes Entry for each Frame Description Entry (FDE)
+ // it finds. The parser doesn't report Common Information Entries
+ // to the handler explicitly; instead, if the handler elects to
+ // process a given FDE, the parser reiterates the appropriate CIE's
+ // contents at the beginning of the FDE's rules.
+ virtual bool Entry(size_t offset, uint64 address, uint64 length,
+ uint8 version, const string &augmentation,
+ unsigned return_address) = 0;
+
+ // When the Entry function returns true, the parser calls these
+ // handler functions repeatedly to describe the rules for recovering
+ // registers at each instruction in the given range of machine code.
+ // Immediately after a call to Entry, the handler should assume that
+ // the rule for each callee-saves register is "unchanged" --- that
+ // is, that the register still has the value it had in the caller.
+ //
+ // If a *Rule function returns true, we continue processing this entry's
+ // instructions. If a *Rule function returns false, we stop evaluating
+ // instructions, and skip to the next entry. Either way, we call End
+ // before going on to the next entry.
+ //
+ // In all of these functions, if the REG parameter is kCFARegister, then
+ // the rule describes how to find the canonical frame address.
+ // kCFARegister may be passed as a BASE_REGISTER argument, meaning that
+ // the canonical frame address should be used as the base address for the
+ // computation. All other REG values will be positive.
+
+ // At ADDRESS, register REG's value is not recoverable.
+ virtual bool UndefinedRule(uint64 address, int reg) = 0;
+
+ // At ADDRESS, register REG's value is the same as that it had in
+ // the caller.
+ virtual bool SameValueRule(uint64 address, int reg) = 0;
+
+ // At ADDRESS, register REG has been saved at offset OFFSET from
+ // BASE_REGISTER.
+ virtual bool OffsetRule(uint64 address, int reg,
+ int base_register, long offset) = 0;
+
+ // At ADDRESS, the caller's value of register REG is the current
+ // value of BASE_REGISTER plus OFFSET. (This rule doesn't provide an
+ // address at which the register's value is saved.)
+ virtual bool ValOffsetRule(uint64 address, int reg,
+ int base_register, long offset) = 0;
+
+ // At ADDRESS, register REG has been saved in BASE_REGISTER. This differs
+ // from ValOffsetRule(ADDRESS, REG, BASE_REGISTER, 0), in that
+ // BASE_REGISTER is the "home" for REG's saved value: if you want to
+ // assign to a variable whose home is REG in the calling frame, you
+ // should put the value in BASE_REGISTER.
+ virtual bool RegisterRule(uint64 address, int reg, int base_register) = 0;
+
+ // At ADDRESS, the DWARF expression EXPRESSION yields the address at
+ // which REG was saved.
+ virtual bool ExpressionRule(uint64 address, int reg,
+ const string &expression) = 0;
+
+ // At ADDRESS, the DWARF expression EXPRESSION yields the caller's
+ // value for REG. (This rule doesn't provide an address at which the
+ // register's value is saved.)
+ virtual bool ValExpressionRule(uint64 address, int reg,
+ const string &expression) = 0;
+
+ // Indicate that the rules for the address range reported by the
+ // last call to Entry are complete. End should return true if
+ // everything is okay, or false if an error has occurred and parsing
+ // should stop.
+ virtual bool End() = 0;
+};
+
+// The CallFrameInfo class makes calls on an instance of this class to
+// report errors or warn about problems in the data it is parsing. The
+// default definitions of these methods print a message to stderr, but
+// you can make a derived class that overrides them.
+class CallFrameInfo::Reporter {
+ public:
+ // Create an error reporter which attributes troubles to the section
+ // named SECTION in FILENAME.
+ //
+ // Normally SECTION would be .debug_frame, but the Mac puts CFI data
+ // in a Mach-O section named __debug_frame. If we support
+ // Linux-style exception handling data, we could be reading an
+ // .eh_frame section.
+ Reporter(const string &filename,
+ const string &section = ".debug_frame")
+ : filename_(filename), section_(section) { }
+ virtual ~Reporter() { }
+
+ // The CFI entry at OFFSET ends too early to be well-formed. KIND
+ // indicates what kind of entry it is; KIND can be kUnknown if we
+ // haven't parsed enough of the entry to tell yet.
+ virtual void Incomplete(uint64 offset, CallFrameInfo::EntryKind kind);
+
+ // The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the
+ // section is not that large.
+ virtual void CIEPointerOutOfRange(uint64 offset, uint64 cie_offset);
+
+ // The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the entry
+ // there is not a CIE.
+ virtual void BadCIEId(uint64 offset, uint64 cie_offset);
+
+ // The FDE at OFFSET refers to a CIE with version number VERSION,
+ // which we don't recognize. We cannot parse DWARF CFI if it uses
+ // a version number we don't recognize.
+ virtual void UnrecognizedVersion(uint64 offset, int version);
+
+ // The FDE at OFFSET refers to a CIE with augmentation AUGMENTATION,
+ // which we don't recognize. We cannot parse DWARF CFI if it uses
+ // augmentations we don't recognize.
+ virtual void UnrecognizedAugmentation(uint64 offset,
+ const string &augmentation);
+
+ // The CIE at OFFSET contains a DW_CFA_restore instruction at
+ // INSN_OFFSET, which may not appear in a CIE.
+ virtual void RestoreInCIE(uint64 offset, uint64 insn_offset);
+
+ // The entry at OFFSET, of kind KIND, has an unrecognized
+ // instruction at INSN_OFFSET.
+ virtual void BadInstruction(uint64 offset, CallFrameInfo::EntryKind kind,
+ uint64 insn_offset);
+
+ // The instruction at INSN_OFFSET in the entry at OFFSET, of kind
+ // KIND, establishes a rule that cites the CFA, but we have not
+ // established a CFA rule yet.
+ virtual void NoCFARule(uint64 offset, CallFrameInfo::EntryKind kind,
+ uint64 insn_offset);
+
+ // The instruction at INSN_OFFSET in the entry at OFFSET, of kind
+ // KIND, is a DW_CFA_restore_state instruction, but the stack of
+ // saved states is empty.
+ virtual void EmptyStateStack(uint64 offset, CallFrameInfo::EntryKind kind,
+ uint64 insn_offset);
+
+ // The DW_CFA_remember_state instruction at INSN_OFFSET in the entry
+ // at OFFSET, of kind KIND, would restore a state that has no CFA
+ // rule, whereas the current state does have a CFA rule. This is
+ // bogus input, which the CallFrameInfo::Handler interface doesn't
+ // (and shouldn't) have any way to report.
+ virtual void ClearingCFARule(uint64 offset, CallFrameInfo::EntryKind kind,
+ uint64 insn_offset);
+
+ protected:
+ // The name of the file whose CFI we're reading.
+ string filename_;
+
+ // The name of the CFI section in that file.
+ string section_;
+};
} // namespace dwarf2reader
diff --git a/src/common/dwarf/dwarf2reader_cfi_unittest.cc b/src/common/dwarf/dwarf2reader_cfi_unittest.cc
new file mode 100644
index 00000000..6083df71
--- /dev/null
+++ b/src/common/dwarf/dwarf2reader_cfi_unittest.cc
@@ -0,0 +1,1928 @@
+// 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>
+
+// dwarf2reader_cfi_unittest.cc: Unit tests for dwarf2reader::CallFrameInfo
+
+#include <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "common/dwarf/bytereader.h"
+#include "common/dwarf/cfi_assembler.h"
+#include "common/dwarf/dwarf2reader.h"
+#include "google_breakpad/common/breakpad_types.h"
+
+using google_breakpad::CFISection;
+using google_breakpad::TestAssembler::Label;
+using google_breakpad::TestAssembler::kBigEndian;
+using google_breakpad::TestAssembler::kLittleEndian;
+using google_breakpad::TestAssembler::Section;
+
+using dwarf2reader::ENDIANNESS_BIG;
+using dwarf2reader::ENDIANNESS_LITTLE;
+using dwarf2reader::ByteReader;
+using dwarf2reader::CallFrameInfo;
+
+using std::vector;
+using testing::InSequence;
+using testing::Return;
+using testing::Sequence;
+using testing::Test;
+using testing::_;
+
+class MockCallFrameInfoHandler: public CallFrameInfo::Handler {
+ public:
+ MOCK_METHOD6(Entry, bool(size_t offset, uint64 address, uint64 length,
+ uint8 version, const string &augmentation,
+ unsigned return_address));
+ MOCK_METHOD2(UndefinedRule, bool(uint64 address, int reg));
+ MOCK_METHOD2(SameValueRule, bool(uint64 address, int reg));
+ MOCK_METHOD4(OffsetRule, bool(uint64 address, int reg, int base_register,
+ long offset));
+ MOCK_METHOD4(ValOffsetRule, bool(uint64 address, int reg, int base_register,
+ long offset));
+ MOCK_METHOD3(RegisterRule, bool(uint64 address, int reg, int base_register));
+ MOCK_METHOD3(ExpressionRule, bool(uint64 address, int reg,
+ const string &expression));
+ MOCK_METHOD3(ValExpressionRule, bool(uint64 address, int reg,
+ const string &expression));
+ MOCK_METHOD0(End, bool());
+};
+
+class MockCallFrameErrorReporter: public CallFrameInfo::Reporter {
+ public:
+ MockCallFrameErrorReporter() : Reporter("mock filename", "mock section") { }
+ MOCK_METHOD2(Incomplete, void(uint64, CallFrameInfo::EntryKind));
+ MOCK_METHOD2(CIEPointerOutOfRange, void(uint64, uint64));
+ MOCK_METHOD2(BadCIEId, void(uint64, uint64));
+ MOCK_METHOD2(UnrecognizedVersion, void(uint64, int version));
+ MOCK_METHOD2(UnrecognizedAugmentation, void(uint64, const string &));
+ MOCK_METHOD2(RestoreInCIE, void(uint64, uint64));
+ MOCK_METHOD3(BadInstruction, void(uint64, CallFrameInfo::EntryKind, uint64));
+ MOCK_METHOD3(NoCFARule, void(uint64, CallFrameInfo::EntryKind, uint64));
+ MOCK_METHOD3(EmptyStateStack, void(uint64, CallFrameInfo::EntryKind, uint64));
+};
+
+struct CFIFixture {
+
+ enum { kCFARegister = CallFrameInfo::Handler::kCFARegister };
+
+ CFIFixture() {
+ // Default expectations for the data handler.
+ //
+ // - Leave Entry and End without expectations, as it's probably a
+ // good idea to set those explicitly in each test.
+ //
+ // - Expect the *Rule functions to not be called,
+ // so that each test can simply list the calls they expect.
+ //
+ // I gather I could use StrictMock for this, but the manual seems
+ // to suggest using that only as a last resort, and this isn't so
+ // bad.
+ EXPECT_CALL(handler, UndefinedRule(_, _)).Times(0);
+ EXPECT_CALL(handler, SameValueRule(_, _)).Times(0);
+ EXPECT_CALL(handler, OffsetRule(_, _, _, _)).Times(0);
+ EXPECT_CALL(handler, ValOffsetRule(_, _, _, _)).Times(0);
+ EXPECT_CALL(handler, RegisterRule(_, _, _)).Times(0);
+ EXPECT_CALL(handler, ExpressionRule(_, _, _)).Times(0);
+ EXPECT_CALL(handler, ValExpressionRule(_, _, _)).Times(0);
+
+ // Default expectations for the error/warning reporer.
+ EXPECT_CALL(reporter, Incomplete(_, _)).Times(0);
+ EXPECT_CALL(reporter, CIEPointerOutOfRange(_, _)).Times(0);
+ EXPECT_CALL(reporter, BadCIEId(_, _)).Times(0);
+ EXPECT_CALL(reporter, UnrecognizedVersion(_, _)).Times(0);
+ EXPECT_CALL(reporter, UnrecognizedAugmentation(_, _)).Times(0);
+ EXPECT_CALL(reporter, RestoreInCIE(_, _)).Times(0);
+ EXPECT_CALL(reporter, BadInstruction(_, _, _)).Times(0);
+ EXPECT_CALL(reporter, NoCFARule(_, _, _)).Times(0);
+ EXPECT_CALL(reporter, EmptyStateStack(_, _, _)).Times(0);
+ }
+
+ MockCallFrameInfoHandler handler;
+ MockCallFrameErrorReporter reporter;
+};
+
+class CFI: public CFIFixture, public Test { };
+
+TEST_F(CFI, EmptyRegion) {
+ EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
+ EXPECT_CALL(handler, End()).Times(0);
+ static const char data[1] = { 42 };
+
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ CallFrameInfo parser(data, 0, &byte_reader, &handler, &reporter);
+ EXPECT_TRUE(parser.Start());
+}
+
+TEST_F(CFI, IncompleteLength32) {
+ CFISection section(kBigEndian, 8);
+ section
+ // Not even long enough for an initial length.
+ .D16(0xa0f)
+ // Padding to keep valgrind happy. We subtract these off when we
+ // construct the parser.
+ .D16(0);
+
+ EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
+ EXPECT_CALL(handler, End()).Times(0);
+
+ EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown))
+ .WillOnce(Return());
+
+ string contents;
+ ASSERT_TRUE(section.GetContents(&contents));
+
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(8);
+ CallFrameInfo parser(contents.data(), contents.size() - 2,
+ &byte_reader, &handler, &reporter);
+ EXPECT_FALSE(parser.Start());
+}
+
+TEST_F(CFI, IncompleteLength64) {
+ CFISection section(kLittleEndian, 4);
+ section
+ // An incomplete 64-bit DWARF initial length.
+ .D32(0xffffffff).D32(0x71fbaec2)
+ // Padding to keep valgrind happy. We subtract these off when we
+ // construct the parser.
+ .D32(0);
+
+ EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
+ EXPECT_CALL(handler, End()).Times(0);
+
+ EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown))
+ .WillOnce(Return());
+
+ string contents;
+ ASSERT_TRUE(section.GetContents(&contents));
+
+ ByteReader byte_reader(ENDIANNESS_LITTLE);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(contents.data(), contents.size() - 4,
+ &byte_reader, &handler, &reporter);
+ EXPECT_FALSE(parser.Start());
+}
+
+TEST_F(CFI, IncompleteId32) {
+ CFISection section(kBigEndian, 8);
+ section
+ .D32(3) // Initial length, not long enough for id
+ .D8(0xd7).D8(0xe5).D8(0xf1) // incomplete id
+ .CIEHeader(8727, 3983, 8889, 3, "")
+ .FinishEntry();
+
+ EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
+ EXPECT_CALL(handler, End()).Times(0);
+
+ EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown))
+ .WillOnce(Return());
+
+ string contents;
+ ASSERT_TRUE(section.GetContents(&contents));
+
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(8);
+ CallFrameInfo parser(contents.data(), contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_FALSE(parser.Start());
+}
+
+TEST_F(CFI, BadId32) {
+ CFISection section(kBigEndian, 8);
+ section
+ .D32(0x100) // Initial length
+ .D32(0xe802fade) // bogus ID
+ .Append(0x100 - 4, 0x42); // make the length true
+ section
+ .CIEHeader(1672, 9872, 8529, 3, "")
+ .FinishEntry();
+
+ EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
+ EXPECT_CALL(handler, End()).Times(0);
+
+ EXPECT_CALL(reporter, CIEPointerOutOfRange(_, 0xe802fade))
+ .WillOnce(Return());
+
+ string contents;
+ ASSERT_TRUE(section.GetContents(&contents));
+
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(8);
+ CallFrameInfo parser(contents.data(), contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_FALSE(parser.Start());
+}
+
+// A lone CIE shouldn't cause any handler calls.
+TEST_F(CFI, SingleCIE) {
+ CFISection section(kLittleEndian, 4);
+ section.CIEHeader(0xffe799a8, 0x3398dcdd, 0x6e9683de, 3, "");
+ section.Append(10, dwarf2reader::DW_CFA_nop);
+ section.FinishEntry();
+
+ EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
+ EXPECT_CALL(handler, End()).Times(0);
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_LITTLE);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(contents.data(), contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_TRUE(parser.Start());
+}
+
+// One FDE, one CIE.
+TEST_F(CFI, OneFDE) {
+ CFISection section(kBigEndian, 4);
+ Label cie;
+ section
+ .Mark(&cie)
+ .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 3, "")
+ .FinishEntry()
+ .FDEHeader(cie, 0x7714740d, 0x3d5a10cd)
+ .FinishEntry();
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, 0x7714740d, 0x3d5a10cd, 3, "", 0x6b6efb87))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(contents.data(), contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_TRUE(parser.Start());
+}
+
+// Two FDEs share a CIE.
+TEST_F(CFI, TwoFDEsOneCIE) {
+ CFISection section(kBigEndian, 4);
+ Label cie;
+ section
+ // First FDE.
+ .FDEHeader(cie, 0xa42744df, 0xa3b42121)
+ .FinishEntry()
+ // CIE.
+ .Mark(&cie)
+ .CIEHeader(0x04f7dc7b, 0x3d00c05f, 0xbd43cb59, 3, "")
+ .FinishEntry()
+ // Second FDE.
+ .FDEHeader(cie, 0x6057d391, 0x700f608d)
+ .FinishEntry();
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, 0xa42744df, 0xa3b42121, 3, "", 0xbd43cb59))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, 0x6057d391, 0x700f608d, 3, "", 0xbd43cb59))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(contents.data(), contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_TRUE(parser.Start());
+}
+
+// Two FDEs, two CIEs.
+TEST_F(CFI, TwoFDEsTwoCIEs) {
+ CFISection section(kLittleEndian, 8);
+ Label cie1, cie2;
+ section
+ // First CIE.
+ .Mark(&cie1)
+ .CIEHeader(0x694d5d45, 0x4233221b, 0xbf45e65a, 3, "")
+ .FinishEntry()
+ // First FDE which cites second CIE.
+ .FDEHeader(cie2, 0x778b27dfe5871f05ULL, 0x324ace3448070926ULL)
+ .FinishEntry()
+ // Second FDE, which cites first CIE.
+ .FDEHeader(cie1, 0xf6054ca18b10bf5fULL, 0x45fdb970d8bca342ULL)
+ .FinishEntry()
+ // Second CIE.
+ .Mark(&cie2)
+ .CIEHeader(0xfba3fad7, 0x6287e1fd, 0x61d2c581, 2, "")
+ .FinishEntry();
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, 0x778b27dfe5871f05ULL, 0x324ace3448070926ULL, 2,
+ "", 0x61d2c581))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, 0xf6054ca18b10bf5fULL, 0x45fdb970d8bca342ULL, 3,
+ "", 0xbf45e65a))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_LITTLE);
+ byte_reader.SetAddressSize(8);
+ CallFrameInfo parser(contents.data(), contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_TRUE(parser.Start());
+}
+
+// An FDE whose CIE specifies a version we don't recognize.
+TEST_F(CFI, BadVersion) {
+ CFISection section(kBigEndian, 4);
+ Label cie1, cie2;
+ section
+ .Mark(&cie1)
+ .CIEHeader(0xca878cf0, 0x7698ec04, 0x7b616f54, 0x52, "")
+ .FinishEntry()
+ // We should skip this entry, as its CIE specifies a version we
+ // don't recognize.
+ .FDEHeader(cie1, 0x08852292, 0x2204004a)
+ .FinishEntry()
+ // Despite the above, we should visit this entry.
+ .Mark(&cie2)
+ .CIEHeader(0x7c3ae7c9, 0xb9b9a512, 0x96cb3264, 3, "")
+ .FinishEntry()
+ .FDEHeader(cie2, 0x2094735a, 0x6e875501)
+ .FinishEntry();
+
+ EXPECT_CALL(reporter, UnrecognizedVersion(_, 0x52))
+ .WillOnce(Return());
+
+ {
+ InSequence s;
+ // We should see no mention of the first FDE, but we should get
+ // a call to Entry for the second.
+ EXPECT_CALL(handler, Entry(_, 0x2094735a, 0x6e875501, 3, "",
+ 0x96cb3264))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End())
+ .WillOnce(Return(true));
+ }
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(contents.data(), contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_FALSE(parser.Start());
+}
+
+// An FDE whose CIE specifies an augmentation we don't recognize.
+TEST_F(CFI, BadAugmentation) {
+ CFISection section(kBigEndian, 4);
+ Label cie1, cie2;
+ section
+ .Mark(&cie1)
+ .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 3, "spaniels!")
+ .FinishEntry()
+ // We should skip this entry, as its CIE specifies an
+ // augmentation we don't recognize.
+ .FDEHeader(cie1, 0x7714740d, 0x3d5a10cd)
+ .FinishEntry()
+ // Despite the above, we should visit this entry.
+ .Mark(&cie2)
+ .CIEHeader(0xf8bc4399, 0x8cf09931, 0xf2f519b2, 3, "")
+ .FinishEntry()
+ .FDEHeader(cie2, 0x7bf0fda0, 0xcbcd28d8)
+ .FinishEntry();
+
+ EXPECT_CALL(reporter, UnrecognizedAugmentation(_, "spaniels!"))
+ .WillOnce(Return());
+
+ {
+ InSequence s;
+ // We should see no mention of the first FDE, but we should get
+ // a call to Entry for the second.
+ EXPECT_CALL(handler, Entry(_, 0x7bf0fda0, 0xcbcd28d8, 3, "",
+ 0xf2f519b2))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End())
+ .WillOnce(Return(true));
+ }
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(contents.data(), contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_FALSE(parser.Start());
+}
+
+// The return address column field is a byte in CFI version 1
+// (DWARF2), but a ULEB128 value in version 3 (DWARF3).
+TEST_F(CFI, CIEVersion1ReturnColumn) {
+ CFISection section(kBigEndian, 4);
+ Label cie;
+ section
+ // CIE, using the version 1 format: return column is a ubyte.
+ .Mark(&cie)
+ // Use a value for the return column that is parsed differently
+ // as a ubyte and as a ULEB128.
+ .CIEHeader(0xbcdea24f, 0x5be28286, 0x9f, 1, "")
+ .FinishEntry()
+ // FDE, citing that CIE.
+ .FDEHeader(cie, 0xb8d347b5, 0x825e55dc)
+ .FinishEntry();
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler, Entry(_, 0xb8d347b5, 0x825e55dc, 1, "", 0x9f))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(contents.data(), contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_TRUE(parser.Start());
+}
+
+// The return address column field is a byte in CFI version 1
+// (DWARF2), but a ULEB128 value in version 3 (DWARF3).
+TEST_F(CFI, CIEVersion3ReturnColumn) {
+ CFISection section(kBigEndian, 4);
+ Label cie;
+ section
+ // CIE, using the version 3 format: return column is a ULEB128.
+ .Mark(&cie)
+ // Use a value for the return column that is parsed differently
+ // as a ubyte and as a ULEB128.
+ .CIEHeader(0x0ab4758d, 0xc010fdf7, 0x89, 3, "")
+ .FinishEntry()
+ // FDE, citing that CIE.
+ .FDEHeader(cie, 0x86763f2b, 0x2a66dc23)
+ .FinishEntry();
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler, Entry(_, 0x86763f2b, 0x2a66dc23, 3, "", 0x89))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(contents.data(), contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_TRUE(parser.Start());
+}
+
+struct CFIInsnFixture: public CFIFixture {
+ CFIInsnFixture() : CFIFixture() {
+ data_factor = 0xb6f;
+ return_register = 0x9be1ed9f;
+ version = 3;
+ cfa_base_register = 0x383a3aa;
+ cfa_offset = 0xf748;
+ }
+
+ // Prepare SECTION to receive FDE instructions.
+ //
+ // - Append a stock CIE header that establishes the fixture's
+ // code_factor, data_factor, return_register, version, and
+ // augmentation values.
+ // - Have the CIE set up a CFA rule using cfa_base_register and
+ // cfa_offset.
+ // - Append a stock FDE header, referring to the above CIE, for the
+ // fde_size bytes at fde_start. Choose fde_start and fde_size
+ // appropriately for the section's address size.
+ // - Set appropriate expectations on handler in sequence s for the
+ // frame description entry and the CIE's CFA rule.
+ //
+ // On return, SECTION is ready to have FDE instructions appended to
+ // it, and its FinishEntry member called.
+ void StockCIEAndFDE(CFISection *section) {
+ // Choose appropriate constants for our address size.
+ if (section->AddressSize() == 4) {
+ fde_start = 0xc628ecfbU;
+ fde_size = 0x5dee04a2;
+ code_factor = 0x60b;
+ } else {
+ assert(section->AddressSize() == 8);
+ fde_start = 0x0005c57ce7806bd3ULL;
+ fde_size = 0x2699521b5e333100ULL;
+ code_factor = 0x01008e32855274a8ULL;
+ }
+
+ // Create the CIE.
+ (*section)
+ .Mark(&cie_label)
+ .CIEHeader(code_factor, data_factor, return_register, version,
+ "")
+ .D8(dwarf2reader::DW_CFA_def_cfa)
+ .ULEB128(cfa_base_register)
+ .ULEB128(cfa_offset)
+ .FinishEntry();
+
+ // Create the FDE.
+ section->FDEHeader(cie_label, fde_start, fde_size);
+
+ // Expect an Entry call for the FDE and a ValOffsetRule call for the
+ // CIE's CFA rule.
+ EXPECT_CALL(handler, Entry(_, fde_start, fde_size, version, "",
+ return_register))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(fde_start, kCFARegister,
+ cfa_base_register, cfa_offset))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ }
+
+ // Run the contents of SECTION through a CallFrameInfo parser,
+ // expecting parser.Start to return SUCCEEDS
+ void ParseSection(CFISection *section, bool succeeds = true) {
+ string contents;
+ EXPECT_TRUE(section->GetContents(&contents));
+ dwarf2reader::Endianness endianness;
+ if (section->endianness() == kBigEndian)
+ endianness = ENDIANNESS_BIG;
+ else {
+ assert(section->endianness() == kLittleEndian);
+ endianness = ENDIANNESS_LITTLE;
+ }
+ ByteReader byte_reader(endianness);
+ byte_reader.SetAddressSize(section->AddressSize());
+ CallFrameInfo parser(contents.data(), contents.size(),
+ &byte_reader, &handler, &reporter);
+ if (succeeds)
+ EXPECT_TRUE(parser.Start());
+ else
+ EXPECT_FALSE(parser.Start());
+ }
+
+ Label cie_label;
+ Sequence s;
+ uint64 code_factor;
+ int data_factor;
+ unsigned return_register;
+ unsigned version;
+ unsigned cfa_base_register;
+ int cfa_offset;
+ uint64 fde_start, fde_size;
+};
+
+class CFIInsn: public CFIInsnFixture, public Test { };
+
+TEST_F(CFIInsn, DW_CFA_set_loc) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_set_loc).D32(0xb1ee3e7a)
+ // Use DW_CFA_def_cfa to force a handler call that we can use to
+ // check the effect of the DW_CFA_set_loc.
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x4defb431).ULEB128(0x6d17b0ee)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(0xb1ee3e7a, kCFARegister, 0x4defb431, 0x6d17b0ee))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_advance_loc) {
+ CFISection section(kBigEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_advance_loc | 0x2a)
+ // Use DW_CFA_def_cfa to force a handler call that we can use to
+ // check the effect of the DW_CFA_advance_loc.
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x5bbb3715).ULEB128(0x0186c7bf)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start + 0x2a * code_factor,
+ kCFARegister, 0x5bbb3715, 0x0186c7bf))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_advance_loc1) {
+ CFISection section(kLittleEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_advance_loc1).D8(0xd8)
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x69d5696a).ULEB128(0x1eb7fc93)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule((fde_start + 0xd8 * code_factor),
+ kCFARegister, 0x69d5696a, 0x1eb7fc93))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_advance_loc2) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_advance_loc2).D16(0x3adb)
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x3a368bed).ULEB128(0x3194ee37)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule((fde_start + 0x3adb * code_factor),
+ kCFARegister, 0x3a368bed, 0x3194ee37))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_advance_loc4) {
+ CFISection section(kBigEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_advance_loc4).D32(0x15813c88)
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x135270c5).ULEB128(0x24bad7cb)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule((fde_start + 0x15813c88ULL * code_factor),
+ kCFARegister, 0x135270c5, 0x24bad7cb))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_MIPS_advance_loc8) {
+ code_factor = 0x2d;
+ CFISection section(kBigEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_MIPS_advance_loc8).D64(0x3c4f3945b92c14ULL)
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0xe17ed602).ULEB128(0x3d162e7f)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule((fde_start + 0x3c4f3945b92c14ULL * code_factor),
+ kCFARegister, 0xe17ed602, 0x3d162e7f))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_def_cfa) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x4e363a85).ULEB128(0x815f9aa7)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x4e363a85, 0x815f9aa7))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_def_cfa_sf) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa_sf).ULEB128(0x8ccb32b7).LEB128(0x9ea)
+ .D8(dwarf2reader::DW_CFA_def_cfa_sf).ULEB128(0x9b40f5da).LEB128(-0x40a2)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x8ccb32b7,
+ 0x9ea * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x9b40f5da,
+ -0x40a2 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_def_cfa_register) {
+ CFISection section(kLittleEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa_register).ULEB128(0x3e7e9363)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x3e7e9363, cfa_offset))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+// DW_CFA_def_cfa_register should have no effect when applied to a
+// non-base/offset rule.
+TEST_F(CFIInsn, DW_CFA_def_cfa_registerBadRule) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("needle in a haystack")
+ .D8(dwarf2reader::DW_CFA_def_cfa_register).ULEB128(0xf1b49e49)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValExpressionRule(fde_start, kCFARegister,
+ "needle in a haystack"))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_def_cfa_offset) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x1e8e3b9b)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, cfa_base_register,
+ 0x1e8e3b9b))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_def_cfa_offset_sf) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa_offset_sf).LEB128(0x970)
+ .D8(dwarf2reader::DW_CFA_def_cfa_offset_sf).LEB128(-0x2cd)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, cfa_base_register,
+ 0x970 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, cfa_base_register,
+ -0x2cd * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+// DW_CFA_def_cfa_offset should have no effect when applied to a
+// non-base/offset rule.
+TEST_F(CFIInsn, DW_CFA_def_cfa_offsetBadRule) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("six ways to Sunday")
+ .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x1e8e3b9b)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValExpressionRule(fde_start, kCFARegister, "six ways to Sunday"))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_def_cfa_expression) {
+ CFISection section(kLittleEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("eating crow")
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ValExpressionRule(fde_start, kCFARegister,
+ "eating crow"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_undefined) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x300ce45d)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, UndefinedRule(fde_start, 0x300ce45d))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_same_value) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x3865a760)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, SameValueRule(fde_start, 0x3865a760))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_offset) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_offset | 0x2c).ULEB128(0x9f6)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x2c, kCFARegister, 0x9f6 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_offset_extended) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_offset_extended).ULEB128(0x402b).ULEB128(0xb48)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x402b, kCFARegister, 0xb48 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_offset_extended_sf) {
+ CFISection section(kBigEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_offset_extended_sf)
+ .ULEB128(0x997c23ee).LEB128(0x2d00)
+ .D8(dwarf2reader::DW_CFA_offset_extended_sf)
+ .ULEB128(0x9519eb82).LEB128(-0xa77)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x997c23ee,
+ kCFARegister, 0x2d00 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x9519eb82,
+ kCFARegister, -0xa77 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_val_offset) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x623562fe).ULEB128(0x673)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, 0x623562fe,
+ kCFARegister, 0x673 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_val_offset_sf) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_offset_sf).ULEB128(0x6f4f).LEB128(0xaab)
+ .D8(dwarf2reader::DW_CFA_val_offset_sf).ULEB128(0x2483).LEB128(-0x8a2)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, 0x6f4f,
+ kCFARegister, 0xaab * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, 0x2483,
+ kCFARegister, -0x8a2 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_register) {
+ CFISection section(kLittleEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0x278d18f9).ULEB128(0x1a684414)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, RegisterRule(fde_start, 0x278d18f9, 0x1a684414))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_expression) {
+ CFISection section(kBigEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_expression).ULEB128(0xa1619fb2)
+ .Block("plus ça change, plus c'est la même chose")
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ExpressionRule(fde_start, 0xa1619fb2,
+ "plus ça change, plus c'est la même chose"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_val_expression) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0xc5e4a9e3)
+ .Block("he who has the gold makes the rules")
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValExpressionRule(fde_start, 0xc5e4a9e3,
+ "he who has the gold makes the rules"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_restore) {
+ CFISection section(kLittleEndian, 8);
+ code_factor = 0x01bd188a9b1fa083ULL;
+ data_factor = -0x1ac8;
+ return_register = 0x8c35b049;
+ version = 2;
+ fde_start = 0x2d70fe998298bbb1ULL;
+ fde_size = 0x46ccc2e63cf0b108ULL;
+ Label cie;
+ section
+ .Mark(&cie)
+ .CIEHeader(code_factor, data_factor, return_register, version,
+ "")
+ // Provide a CFA rule, because register rules require them.
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x6ca1d50e).ULEB128(0x372e38e8)
+ // Provide an offset(N) rule for register 0x3c.
+ .D8(dwarf2reader::DW_CFA_offset | 0x3c).ULEB128(0xb348)
+ .FinishEntry()
+ // In the FDE...
+ .FDEHeader(cie, fde_start, fde_size)
+ // At a second address, provide a new offset(N) rule for register 0x3c.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 0x13)
+ .D8(dwarf2reader::DW_CFA_offset | 0x3c).ULEB128(0x9a50)
+ // At a third address, restore the original rule for register 0x3c.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 0x01)
+ .D8(dwarf2reader::DW_CFA_restore | 0x3c)
+ .FinishEntry();
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, fde_start, fde_size, version, "", return_register))
+ .WillOnce(Return(true));
+ // CIE's CFA rule.
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x6ca1d50e, 0x372e38e8))
+ .WillOnce(Return(true));
+ // CIE's rule for register 0x3c.
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x3c, kCFARegister, 0xb348 * data_factor))
+ .WillOnce(Return(true));
+ // FDE's rule for register 0x3c.
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start + 0x13 * code_factor, 0x3c,
+ kCFARegister, 0x9a50 * data_factor))
+ .WillOnce(Return(true));
+ // Restore CIE's rule for register 0x3c.
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start + (0x13 + 0x01) * code_factor, 0x3c,
+ kCFARegister, 0xb348 * data_factor))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_restoreNoRule) {
+ CFISection section(kBigEndian, 4);
+ code_factor = 0x005f78143c1c3b82ULL;
+ data_factor = 0x25d0;
+ return_register = 0xe8;
+ version = 1;
+ fde_start = 0x4062e30f;
+ fde_size = 0x5302a389;
+ Label cie;
+ section
+ .Mark(&cie)
+ .CIEHeader(code_factor, data_factor, return_register, version, "")
+ // Provide a CFA rule, because register rules require them.
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x470aa334).ULEB128(0x099ef127)
+ .FinishEntry()
+ // In the FDE...
+ .FDEHeader(cie, fde_start, fde_size)
+ // At a second address, provide an offset(N) rule for register 0x2c.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 0x7)
+ .D8(dwarf2reader::DW_CFA_offset | 0x2c).ULEB128(0x1f47)
+ // At a third address, restore the (missing) CIE rule for register 0x2c.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 0xb)
+ .D8(dwarf2reader::DW_CFA_restore | 0x2c)
+ .FinishEntry();
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, fde_start, fde_size, version, "", return_register))
+ .WillOnce(Return(true));
+ // CIE's CFA rule.
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x470aa334, 0x099ef127))
+ .WillOnce(Return(true));
+ // FDE's rule for register 0x2c.
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start + 0x7 * code_factor, 0x2c,
+ kCFARegister, 0x1f47 * data_factor))
+ .WillOnce(Return(true));
+ // Restore CIE's (missing) rule for register 0x2c.
+ EXPECT_CALL(handler,
+ SameValueRule(fde_start + (0x7 + 0xb) * code_factor, 0x2c))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_restore_extended) {
+ CFISection section(kBigEndian, 4);
+ code_factor = 0x126e;
+ data_factor = -0xd8b;
+ return_register = 0x77711787;
+ version = 3;
+ fde_start = 0x01f55a45;
+ fde_size = 0x452adb80;
+ Label cie;
+ section
+ .Mark(&cie)
+ .CIEHeader(code_factor, data_factor, return_register, version,
+ "", true /* dwarf64 */ )
+ // Provide a CFA rule, because register rules require them.
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x56fa0edd).ULEB128(0x097f78a5)
+ // Provide an offset(N) rule for register 0x0f9b8a1c.
+ .D8(dwarf2reader::DW_CFA_offset_extended)
+ .ULEB128(0x0f9b8a1c).ULEB128(0xc979)
+ .FinishEntry()
+ // In the FDE...
+ .FDEHeader(cie, fde_start, fde_size)
+ // At a second address, provide a new offset(N) rule for reg 0x0f9b8a1c.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 0x3)
+ .D8(dwarf2reader::DW_CFA_offset_extended)
+ .ULEB128(0x0f9b8a1c).ULEB128(0x3b7b)
+ // At a third address, restore the original rule for register 0x0f9b8a1c.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 0x04)
+ .D8(dwarf2reader::DW_CFA_restore_extended).ULEB128(0x0f9b8a1c)
+ .FinishEntry();
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, fde_start, fde_size, version, "", return_register))
+ .WillOnce(Return(true));
+ // CIE's CFA rule.
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x56fa0edd, 0x097f78a5))
+ .WillOnce(Return(true));
+ // CIE's rule for register 0x0f9b8a1c.
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x0f9b8a1c, kCFARegister,
+ 0xc979 * data_factor))
+ .WillOnce(Return(true));
+ // FDE's rule for register 0x0f9b8a1c.
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start + 0x3 * code_factor, 0x0f9b8a1c,
+ kCFARegister, 0x3b7b * data_factor))
+ .WillOnce(Return(true));
+ // Restore CIE's rule for register 0x0f9b8a1c.
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start + (0x3 + 0x4) * code_factor, 0x0f9b8a1c,
+ kCFARegister, 0xc979 * data_factor))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_remember_and_restore_state) {
+ CFISection section(kLittleEndian, 8);
+ StockCIEAndFDE(&section);
+
+ // We create a state, save it, modify it, and then restore. We
+ // refer to the state that is overridden the restore as the
+ // "outgoing" state, and the restored state the "incoming" state.
+ //
+ // Register outgoing incoming expect
+ // 1 offset(N) no rule new "same value" rule
+ // 2 register(R) offset(N) report changed rule
+ // 3 offset(N) offset(M) report changed offset
+ // 4 offset(N) offset(N) no report
+ // 5 offset(N) no rule new "same value" rule
+ section
+ // Create the "incoming" state, which we will save and later restore.
+ .D8(dwarf2reader::DW_CFA_offset | 2).ULEB128(0x9806)
+ .D8(dwarf2reader::DW_CFA_offset | 3).ULEB128(0x995d)
+ .D8(dwarf2reader::DW_CFA_offset | 4).ULEB128(0x7055)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ // Advance to a new instruction; an implementation could legitimately
+ // ignore all but the final rule for a given register at a given address.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ // Create the "outgoing" state, which we will discard.
+ .D8(dwarf2reader::DW_CFA_offset | 1).ULEB128(0xea1a)
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(2).ULEB128(0x1d2a3767)
+ .D8(dwarf2reader::DW_CFA_offset | 3).ULEB128(0xdd29)
+ .D8(dwarf2reader::DW_CFA_offset | 5).ULEB128(0xf1ce)
+ // At a third address, restore the incoming state.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ uint64 addr = fde_start;
+
+ // Expect the incoming rules to be reported.
+ EXPECT_CALL(handler, OffsetRule(addr, 2, kCFARegister, 0x9806 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0x995d * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(addr, 4, kCFARegister, 0x7055 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+
+ addr += code_factor;
+
+ // After the save, we establish the outgoing rule set.
+ EXPECT_CALL(handler, OffsetRule(addr, 1, kCFARegister, 0xea1a * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, RegisterRule(addr, 2, 0x1d2a3767))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0xdd29 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(addr, 5, kCFARegister, 0xf1ce * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+
+ addr += code_factor;
+
+ // Finally, after the restore, expect to see the differences from
+ // the outgoing to the incoming rules reported.
+ EXPECT_CALL(handler, SameValueRule(addr, 1))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(addr, 2, kCFARegister, 0x9806 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0x995d * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, SameValueRule(addr, 5))
+ .InSequence(s).WillOnce(Return(true));
+
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+// Check that restoring a rule set reports changes to the CFA rule.
+TEST_F(CFIInsn, DW_CFA_remember_and_restore_stateCFA) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+
+ section
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x90481102)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor, kCFARegister,
+ cfa_base_register, 0x90481102))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor * 2, kCFARegister,
+ cfa_base_register, cfa_offset))
+ .InSequence(s).WillOnce(Return(true));
+
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_nop) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_nop)
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x3fb8d4f1).ULEB128(0x078dc67b)
+ .D8(dwarf2reader::DW_CFA_nop)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x3fb8d4f1, 0x078dc67b))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_GNU_window_save) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_GNU_window_save)
+ .FinishEntry();
+
+ // Don't include all the rules in any particular sequence.
+
+ // The caller's %o0-%o7 have become the callee's %i0-%i7. This is
+ // the GCC register numbering.
+ for (int i = 8; i < 16; i++)
+ EXPECT_CALL(handler, RegisterRule(fde_start, i, i + 16))
+ .WillOnce(Return(true));
+ // The caller's %l0-%l7 and %i0-%i7 have been saved at the top of
+ // its frame.
+ for (int i = 16; i < 32; i++)
+ EXPECT_CALL(handler, OffsetRule(fde_start, i, kCFARegister, (i-16) * 4))
+ .WillOnce(Return(true));
+
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_GNU_args_size) {
+ CFISection section(kLittleEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_GNU_args_size).ULEB128(0xeddfa520)
+ // Verify that we see this, meaning we parsed the above properly.
+ .D8(dwarf2reader::DW_CFA_offset | 0x23).ULEB128(0x269)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x23, kCFARegister, 0x269 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_GNU_negative_offset_extended) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_GNU_negative_offset_extended)
+ .ULEB128(0x430cc87a).ULEB128(0x613)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x430cc87a,
+ kCFARegister, -0x613 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+// Three FDEs: skip the second
+TEST_F(CFIInsn, SkipFDE) {
+ CFISection section(kBigEndian, 4);
+ Label cie;
+ section
+ // CIE, used by all FDEs.
+ .Mark(&cie)
+ .CIEHeader(0x010269f2, 0x9177, 0xedca5849, 2, "")
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x42ed390b).ULEB128(0x98f43aad)
+ .FinishEntry()
+ // First FDE.
+ .FDEHeader(cie, 0xa870ebdd, 0x60f6aa4)
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0x3a860351).ULEB128(0x6c9a6bcf)
+ .FinishEntry()
+ // Second FDE.
+ .FDEHeader(cie, 0xc534f7c0, 0xf6552e9, true /* dwarf64 */)
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0x1b62c234).ULEB128(0x26586b18)
+ .FinishEntry()
+ // Third FDE.
+ .FDEHeader(cie, 0xf681cfc8, 0x7e4594e)
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0x26c53934).ULEB128(0x18eeb8a4)
+ .FinishEntry();
+
+ {
+ InSequence s;
+
+ // Process the first FDE.
+ EXPECT_CALL(handler, Entry(_, 0xa870ebdd, 0x60f6aa4, 2, "", 0xedca5849))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(0xa870ebdd, kCFARegister,
+ 0x42ed390b, 0x98f43aad))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, RegisterRule(0xa870ebdd, 0x3a860351, 0x6c9a6bcf))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End())
+ .WillOnce(Return(true));
+
+ // Skip the second FDE.
+ EXPECT_CALL(handler, Entry(_, 0xc534f7c0, 0xf6552e9, 2, "", 0xedca5849))
+ .WillOnce(Return(false));
+
+ // Process the third FDE.
+ EXPECT_CALL(handler, Entry(_, 0xf681cfc8, 0x7e4594e, 2, "", 0xedca5849))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(0xf681cfc8, kCFARegister,
+ 0x42ed390b, 0x98f43aad))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, RegisterRule(0xf681cfc8, 0x26c53934, 0x18eeb8a4))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End())
+ .WillOnce(Return(true));
+ }
+
+ ParseSection(&section);
+}
+
+// Quit processing in the middle of an entry's instructions.
+TEST_F(CFIInsn, QuitMidentry) {
+ CFISection section(kLittleEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0xe0cf850d).ULEB128(0x15aab431)
+ .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x46750aa5).Block("meat")
+ .FinishEntry();
+
+ EXPECT_CALL(handler, RegisterRule(fde_start, 0xe0cf850d, 0x15aab431))
+ .InSequence(s).WillOnce(Return(false));
+ EXPECT_CALL(handler, End())
+ .InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section, false);
+}
+
+class CFIRestore: public CFIInsnFixture, public Test { };
+
+TEST_F(CFIRestore, RestoreUndefinedRuleUnchanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x0bac878e)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, UndefinedRule(fde_start, 0x0bac878e))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreUndefinedRuleChanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x7dedff5f)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x7dedff5f)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, UndefinedRule(fde_start, 0x7dedff5f))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, SameValueRule(fde_start + code_factor, 0x7dedff5f))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(fde_start + 2 * code_factor, 0x7dedff5f))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreSameValueRuleUnchanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0xadbc9b3a)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, SameValueRule(fde_start, 0xadbc9b3a))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreSameValueRuleChanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x3d90dcb5)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x3d90dcb5)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, SameValueRule(fde_start, 0x3d90dcb5))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0x3d90dcb5))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, SameValueRule(fde_start + 2 * code_factor, 0x3d90dcb5))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreOffsetRuleUnchanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_offset | 0x14).ULEB128(0xb6f)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, OffsetRule(fde_start, 0x14,
+ kCFARegister, 0xb6f * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreOffsetRuleChanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0xeb7)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x21)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, OffsetRule(fde_start, 0x21,
+ kCFARegister, 0xeb7 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0x21))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(fde_start + 2 * code_factor, 0x21,
+ kCFARegister, 0xeb7 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreOffsetRuleChangedOffset) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0x134)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0xf4f)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, OffsetRule(fde_start, 0x21,
+ kCFARegister, 0x134 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(fde_start + code_factor, 0x21,
+ kCFARegister, 0xf4f * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(fde_start + 2 * code_factor, 0x21,
+ kCFARegister, 0x134 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreValOffsetRuleUnchanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x829caee6).ULEB128(0xe4c)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ValOffsetRule(fde_start, 0x829caee6,
+ kCFARegister, 0xe4c * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreValOffsetRuleChanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0xf17c36d6).ULEB128(0xeb7)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xf17c36d6)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ValOffsetRule(fde_start, 0xf17c36d6,
+ kCFARegister, 0xeb7 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xf17c36d6))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(fde_start + 2 * code_factor, 0xf17c36d6,
+ kCFARegister, 0xeb7 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreValOffsetRuleChangedValOffset) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0x562)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0xe88)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ValOffsetRule(fde_start, 0x2cf0ab1b,
+ kCFARegister, 0x562 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor, 0x2cf0ab1b,
+ kCFARegister, 0xe88 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(fde_start + 2 * code_factor, 0x2cf0ab1b,
+ kCFARegister, 0x562 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreRegisterRuleUnchanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0x77514acc).ULEB128(0x464de4ce)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, RegisterRule(fde_start, 0x77514acc, 0x464de4ce))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreRegisterRuleChanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0xe39acce5).ULEB128(0x095f1559)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xe39acce5)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, RegisterRule(fde_start, 0xe39acce5, 0x095f1559))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xe39acce5))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, RegisterRule(fde_start + 2 * code_factor, 0xe39acce5,
+ 0x095f1559))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreRegisterRuleChangedRegister) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0x16607d6a)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0xbabb4742)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, RegisterRule(fde_start, 0xd40e21b1, 0x16607d6a))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, RegisterRule(fde_start + code_factor, 0xd40e21b1,
+ 0xbabb4742))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, RegisterRule(fde_start + 2 * code_factor, 0xd40e21b1,
+ 0x16607d6a))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreExpressionRuleUnchanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x666ae152).Block("dwarf")
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ExpressionRule(fde_start, 0x666ae152, "dwarf"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreExpressionRuleChanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_expression).ULEB128(0xb5ca5c46).Block("elf")
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xb5ca5c46)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ExpressionRule(fde_start, 0xb5ca5c46, "elf"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xb5ca5c46))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ExpressionRule(fde_start + 2 * code_factor, 0xb5ca5c46,
+ "elf"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreExpressionRuleChangedExpression) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x500f5739).Block("smurf")
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x500f5739).Block("orc")
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ExpressionRule(fde_start, 0x500f5739, "smurf"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ExpressionRule(fde_start + code_factor, 0x500f5739,
+ "orc"))
+ .InSequence(s).WillOnce(Return(true));
+ // Expectations are not wishes.
+ EXPECT_CALL(handler, ExpressionRule(fde_start + 2 * code_factor, 0x500f5739,
+ "smurf"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreValExpressionRuleUnchanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x666ae152)
+ .Block("hideous")
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ValExpressionRule(fde_start, 0x666ae152, "hideous"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreValExpressionRuleChanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0xb5ca5c46)
+ .Block("revolting")
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xb5ca5c46)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ValExpressionRule(fde_start, 0xb5ca5c46, "revolting"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xb5ca5c46))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ValExpressionRule(fde_start + 2 * code_factor, 0xb5ca5c46,
+ "revolting"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreValExpressionRuleChangedValExpression) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x500f5739)
+ .Block("repulsive")
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x500f5739)
+ .Block("nauseous")
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ValExpressionRule(fde_start, 0x500f5739, "repulsive"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ValExpressionRule(fde_start + code_factor, 0x500f5739,
+ "nauseous"))
+ .InSequence(s).WillOnce(Return(true));
+ // Expectations are not wishes.
+ EXPECT_CALL(handler, ValExpressionRule(fde_start + 2 * code_factor, 0x500f5739,
+ "repulsive"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+// These tests require manual inspection of the test output.
+struct CFIReporterFixture {
+ CFIReporterFixture() : reporter("test file name", "test section name") { }
+ CallFrameInfo::Reporter reporter;
+};
+
+class CFIReporter: public CFIReporterFixture, public Test { };
+
+TEST_F(CFIReporter, Incomplete) {
+ reporter.Incomplete(0x0102030405060708ULL, CallFrameInfo::kUnknown);
+}
+
+TEST_F(CFIReporter, CIEPointerOutOfRange) {
+ reporter.CIEPointerOutOfRange(0x0123456789abcdefULL, 0xfedcba9876543210ULL);
+}
+
+TEST_F(CFIReporter, BadCIEId) {
+ reporter.BadCIEId(0x0123456789abcdefULL, 0xfedcba9876543210ULL);
+}
+
+TEST_F(CFIReporter, UnrecognizedVersion) {
+ reporter.UnrecognizedVersion(0x0123456789abcdefULL, 43);
+}
+
+TEST_F(CFIReporter, UnrecognizedAugmentation) {
+ reporter.UnrecognizedAugmentation(0x0123456789abcdefULL, "poodles");
+}
+
+TEST_F(CFIReporter, RestoreInCIE) {
+ reporter.RestoreInCIE(0x0123456789abcdefULL, 0xfedcba9876543210ULL);
+}
+
+TEST_F(CFIReporter, BadInstruction) {
+ reporter.BadInstruction(0x0123456789abcdefULL, CallFrameInfo::kFDE,
+ 0xfedcba9876543210ULL);
+}
+
+TEST_F(CFIReporter, NoCFARule) {
+ reporter.NoCFARule(0x0123456789abcdefULL, CallFrameInfo::kCIE,
+ 0xfedcba9876543210ULL);
+}
+
+TEST_F(CFIReporter, EmptyStateStack) {
+ reporter.EmptyStateStack(0x0123456789abcdefULL, CallFrameInfo::kFDE,
+ 0xfedcba9876543210ULL);
+}
+
+TEST_F(CFIReporter, ClearingCFARule) {
+ reporter.ClearingCFARule(0x0123456789abcdefULL, CallFrameInfo::kFDE,
+ 0xfedcba9876543210ULL);
+}
diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc
index 993bdbd5..79d7ad36 100644
--- a/src/common/linux/dump_symbols.cc
+++ b/src/common/linux/dump_symbols.cc
@@ -49,6 +49,7 @@
#include "common/dwarf/dwarf2diehandler.h"
#include "common/linux/dump_stabs.h"
#include "common/linux/dump_symbols.h"
+#include "common/linux/dwarf_cfi_to_module.h"
#include "common/linux/dwarf_cu_to_module.h"
#include "common/linux/dwarf_line_to_module.h"
#include "common/linux/file_id.h"
@@ -59,6 +60,7 @@
namespace {
using google_breakpad::DumpStabsHandler;
+using google_breakpad::DwarfCFIToModule;
using google_breakpad::DwarfCUToModule;
using google_breakpad::DwarfLineToModule;
using google_breakpad::Module;
@@ -215,6 +217,119 @@ static bool LoadDwarf(const string &dwarf_filename,
return true;
}
+// Fill REGISTER_NAMES with the register names appropriate to the
+// machine architecture given in HEADER, indexed by the register
+// numbers used in DWARF call frame information. Return true on
+// success, or false if we don't recognize HEADER's machine
+// architecture.
+static bool DwarfCFIRegisterNames(const ElfW(Ehdr) *elf_header,
+ vector<string> *register_names)
+{
+ static const char *const i386_names[] = {
+ "$eax", "$ecx", "$edx", "$ebx", "$esp", "$ebp", "$esi", "$edi",
+ "$eip", "$eflags", "$unused1",
+ "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7",
+ "$unused2", "$unused3",
+ "$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7",
+ "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7",
+ "$fcw", "$fsw", "$mxcsr",
+ "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused4", "$unused5",
+ "$tr", "$ldtr",
+ NULL
+ };
+
+ static const char *const x86_64_names[] = {
+ "$rax", "$rdx", "$rcx", "$rbx", "$rsi", "$rdi", "$rbp", "$rsp",
+ "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15",
+ "$rip",
+ "$xmm0","$xmm1","$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7",
+ "$xmm8","$xmm9","$xmm10","$xmm11","$xmm12","$xmm13","$xmm14","$xmm15",
+ "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7",
+ "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7",
+ "$rflags",
+ "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused1", "$unused2",
+ "$fs.base", "$gs.base", "$unused3", "$unused4",
+ "$tr", "$ldtr",
+ "$mxcsr", "$fcw", "$fsw",
+ NULL
+ };
+
+ const char * const *name_table;
+ switch (elf_header->e_machine) {
+ case EM_386:
+ name_table = i386_names;
+ break;
+
+ case EM_X86_64:
+ name_table = x86_64_names;
+ break;
+
+ default:
+ return false;
+ }
+
+ register_names->clear();
+ for (int i = 0; name_table[i]; i++)
+ register_names->push_back(name_table[i]);
+ return true;
+}
+
+static bool LoadDwarfCFI(const string &dwarf_filename,
+ const ElfW(Ehdr) *elf_header,
+ const char *section_name,
+ const ElfW(Shdr) *section,
+ Module *module) {
+ // Find the appropriate set of register names for this file's
+ // architecture.
+ vector<string> register_names;
+ if (!DwarfCFIRegisterNames(elf_header, &register_names)) {
+ fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';"
+ " cannot convert DWARF call frame information\n",
+ dwarf_filename.c_str(), elf_header->e_machine);
+ return false;
+ }
+
+ // Figure out what endianness this file is.
+ dwarf2reader::Endianness endianness;
+ if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB)
+ endianness = dwarf2reader::ENDIANNESS_LITTLE;
+ else if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB)
+ endianness = dwarf2reader::ENDIANNESS_BIG;
+ else {
+ fprintf(stderr, "%s: bad data encoding in ELF header: %d\n",
+ dwarf_filename.c_str(), elf_header->e_ident[EI_DATA]);
+ return false;
+ }
+
+ // Find the call frame information and its size.
+ const char *cfi = reinterpret_cast<const char *>(section->sh_offset);
+ size_t cfi_size = section->sh_size;
+
+ // Plug together the parser, handler, and their entourages.
+ DwarfCFIToModule::Reporter module_reporter(dwarf_filename, section_name);
+ DwarfCFIToModule handler(module, register_names, &module_reporter);
+ dwarf2reader::ByteReader byte_reader(endianness);
+ // Since we're using the ElfW macro, we're not actually capable of
+ // processing both ELF32 and ELF64 files with the same program; that
+ // would take a bit more work. But this will work out well enough.
+ if (elf_header->e_ident[EI_CLASS] == ELFCLASS32)
+ byte_reader.SetAddressSize(4);
+ else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64)
+ byte_reader.SetAddressSize(8);
+ else {
+ fprintf(stderr, "%s: bad file class in ELF header: %d\n",
+ dwarf_filename.c_str(), elf_header->e_ident[EI_CLASS]);
+ return false;
+ }
+
+ dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename,
+ section_name);
+ dwarf2reader::CallFrameInfo parser(cfi, cfi_size, &byte_reader,
+ &handler, &dwarf_reporter);
+ parser.Start();
+ return true;
+}
+
static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
Module *module) {
// Translate all offsets in section headers into address.
@@ -228,6 +343,8 @@ static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx;
bool found_debug_info_section = false;
+
+ // Look for STABS debugging information, and load it if present.
const ElfW(Shdr) *stab_section
= FindSectionByName(".stab", sections, section_names,
elf_header->e_shnum);
@@ -240,6 +357,8 @@ static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
" debugging information\n");
}
}
+
+ // Look for DWARF debugging information, and load it if present.
const ElfW(Shdr) *dwarf_section
= FindSectionByName(".debug_info", sections, section_names,
elf_header->e_shnum);
@@ -249,6 +368,20 @@ static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
fprintf(stderr, "\".debug_info\" section found, but failed to load "
"DWARF debugging information\n");
}
+
+ // Dwarf Call Frame Information (CFI) is actually independent from
+ // the other DWARF debugging information, and can be used alone.
+ const ElfW(Shdr) *dwarf_cfi_section =
+ FindSectionByName(".debug_frame", sections, section_names,
+ elf_header->e_shnum);
+ if (dwarf_cfi_section) {
+ // Ignore the return value of this function; even without call frame
+ // information, the other debugging information could be perfectly
+ // useful.
+ LoadDwarfCFI(obj_file, elf_header, ".debug_frame",
+ dwarf_cfi_section, module);
+ }
+
if (!found_debug_info_section) {
fprintf(stderr, "file contains no debugging information"
" (no \".stab\" or \".debug_info\" sections)\n");
diff --git a/src/common/linux/dwarf_cfi_to_module.cc b/src/common/linux/dwarf_cfi_to_module.cc
new file mode 100644
index 00000000..d7946a0e
--- /dev/null
+++ b/src/common/linux/dwarf_cfi_to_module.cc
@@ -0,0 +1,187 @@
+// -*- mode: c++ -*-
+
+// 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>
+
+// Implementation of google_breakpad::DwarfCFIToModule.
+// See dwarf_cfi_to_module.h for details.
+
+#include <sstream>
+
+#include "common/linux/dwarf_cfi_to_module.h"
+
+namespace google_breakpad {
+
+using std::ostringstream;
+
+bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length,
+ uint8 version, const string &augmentation,
+ unsigned return_address) {
+ assert(!entry_);
+ // The latest CFI format version we understand is version 3.
+ if (version > 3)
+ return false;
+ // We only handle non-augmented DWARF unwinding data at the moment.
+ if (!augmentation.empty())
+ return false;
+
+ // Get ready to collect entries.
+ entry_ = new Module::StackFrameEntry;
+ entry_->address = address;
+ entry_->size = length;
+ entry_offset_ = offset;
+ return_address_ = return_address;
+
+ // Breakpad STACK CFI records must provide a .ra rule, but DWARF CFI
+ // may not establish any rule for .ra if the return address column
+ // is an ordinary register, and that register holds the return
+ // address on entry to the function. So establish an initial .ra
+ // rule citing the return address register.
+ if (return_address_ < register_names_.size())
+ entry_->initial_rules[".ra"] = register_names_[return_address_];
+
+ return true;
+}
+
+string DwarfCFIToModule::RegisterName(int i) {
+ assert(entry_);
+ if (i < 0) {
+ assert(i == kCFARegister);
+ return ".cfa";
+ }
+ unsigned reg = i;
+ if (reg == return_address_)
+ return ".ra";
+
+ if (0 <= reg && reg < register_names_.size())
+ return register_names_[reg];
+
+ reporter_->UnnamedRegister(entry_offset_, reg);
+ char buf[30];
+ sprintf(buf, "unnamed_register%u", reg);
+ return buf;
+}
+
+void DwarfCFIToModule::Record(Module::Address address, int reg,
+ const string &rule) {
+ assert(entry_);
+ // Is this one of this entry's initial rules?
+ if (address == entry_->address)
+ entry_->initial_rules[RegisterName(reg)] = rule;
+ // File it under the appropriate address.
+ else
+ entry_->rule_changes[address][RegisterName(reg)] = rule;
+}
+
+bool DwarfCFIToModule::UndefinedRule(uint64 address, int reg) {
+ reporter_->UndefinedNotSupported(entry_offset_, RegisterName(reg));
+ // Treat this as a non-fatal error.
+ return true;
+}
+
+bool DwarfCFIToModule::SameValueRule(uint64 address, int reg) {
+ ostringstream s;
+ s << RegisterName(reg);
+ Record(address, reg, s.str());
+ return true;
+}
+
+bool DwarfCFIToModule::OffsetRule(uint64 address, int reg,
+ int base_register, long offset) {
+ ostringstream s;
+ s << RegisterName(base_register) << " " << offset << " + ^";
+ Record(address, reg, s.str());
+ return true;
+}
+
+bool DwarfCFIToModule::ValOffsetRule(uint64 address, int reg,
+ int base_register, long offset) {
+ ostringstream s;
+ s << RegisterName(base_register) << " " << offset << " +";
+ Record(address, reg, s.str());
+ return true;
+}
+
+bool DwarfCFIToModule::RegisterRule(uint64 address, int reg,
+ int base_register) {
+ ostringstream s;
+ s << RegisterName(base_register);
+ Record(address, reg, s.str());
+ return true;
+}
+
+bool DwarfCFIToModule::ExpressionRule(uint64 address, int reg,
+ const string &expression) {
+ reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg));
+ // Treat this as a non-fatal error.
+ return true;
+}
+
+bool DwarfCFIToModule::ValExpressionRule(uint64 address, int reg,
+ const string &expression) {
+ reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg));
+ // Treat this as a non-fatal error.
+ return true;
+}
+
+bool DwarfCFIToModule::End() {
+ module_->AddStackFrameEntry(entry_);
+ entry_ = NULL;
+ return true;
+}
+
+void DwarfCFIToModule::Reporter::UnnamedRegister(size_t offset, int reg) {
+ fprintf(stderr, "%s, section '%s': "
+ "the call frame entry at offset 0x%zx refers to register %d,"
+ " whose name we don't know\n",
+ file_.c_str(), section_.c_str(), offset, reg);
+}
+
+void DwarfCFIToModule::Reporter::UndefinedNotSupported(size_t offset,
+ const string &reg) {
+ fprintf(stderr, "%s, section '%s': "
+ "the call frame entry at offset 0x%zx sets the rule for "
+ "register '%s' to 'undefined', but the Breakpad symbol file format"
+ " cannot express this\n",
+ file_.c_str(), section_.c_str(), offset, reg.c_str());
+}
+
+void DwarfCFIToModule::Reporter::ExpressionsNotSupported(size_t offset,
+ const string &reg) {
+ fprintf(stderr, "%s, section '%s': "
+ "the call frame entry at offset 0x%zx uses a DWARF expression to"
+ " describe how to recover register '%s', "
+ " but this translator cannot yet translate DWARF expressions to"
+ " Breakpad postfix expressions\n",
+ file_.c_str(), section_.c_str(), offset, reg.c_str());
+}
+
+} // namespace google_breakpad
diff --git a/src/common/linux/dwarf_cfi_to_module.h b/src/common/linux/dwarf_cfi_to_module.h
new file mode 100644
index 00000000..9df796f5
--- /dev/null
+++ b/src/common/linux/dwarf_cfi_to_module.h
@@ -0,0 +1,154 @@
+// -*- mode: c++ -*-
+
+// 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>
+
+// dwarf_cfi_to_module.h: Define the DwarfCFIToModule class, which
+// accepts parsed DWARF call frame info and adds it to a
+// google_breakpad::Module object, which can write that information to
+// a Breakpad symbol file.
+
+#ifndef COMMON_LINUX_DWARF_CFI_TO_MODULE_H
+#define COMMON_LINUX_DWARF_CFI_TO_MODULE_H
+
+#include <cassert>
+#include <string>
+#include <vector>
+
+#include "common/linux/module.h"
+#include "common/dwarf/dwarf2reader.h"
+
+namespace google_breakpad {
+
+using dwarf2reader::CallFrameInfo;
+using google_breakpad::Module;
+using std::string;
+using std::vector;
+
+// A class that accepts parsed call frame information from the DWARF
+// CFI parser and populates a google_breakpad::Module object with the
+// contents.
+class DwarfCFIToModule: public CallFrameInfo::Handler {
+ public:
+
+ // DwarfCFIToModule uses an instance of this class to report errors
+ // detected while converting DWARF CFI to Breakpad STACK CFI records.
+ class Reporter {
+ public:
+ // Create a reporter that writes messages to the standard error
+ // stream. FILE is the name of the file we're processing, and
+ // SECTION is the name of the section within that file that we're
+ // looking at (.debug_frame, .eh_frame, etc.).
+ Reporter(const string &file, const string &section)
+ : file_(file), section_(section) { }
+ virtual ~Reporter() { }
+
+ // The DWARF CFI entry at OFFSET cites register REG, but REG is not
+ // covered by the vector of register names passed to the
+ // DwarfCFIToModule constructor, nor does it match the return
+ // address column number for this entry.
+ virtual void UnnamedRegister(size_t offset, int reg);
+
+ // The DWARF CFI entry at OFFSET says that REG is undefined, but the
+ // Breakpad symbol file format cannot express this.
+ virtual void UndefinedNotSupported(size_t offset, const string &reg);
+
+ // The DWARF CFI entry at OFFSET says that REG uses a DWARF
+ // expression to find its value, but DwarfCFIToModule is not
+ // capable of translating DWARF expressions to Breakpad postfix
+ // expressions.
+ virtual void ExpressionsNotSupported(size_t offset, const string &reg);
+
+ protected:
+ string file_, section_;
+ };
+
+ // Create a handler for the dwarf2reader::CallFrameInfo parser that
+ // records the stack unwinding information it receives in MODULE.
+ //
+ // Use REGISTER_NAMES[I] as the name of register number I; *this
+ // keeps a reference to the vector, so the vector should remain
+ // alive for as long as the DwarfCFIToModule does.
+ //
+ // Use REPORTER for reporting problems encountered in the conversion
+ // process.
+ DwarfCFIToModule(Module *module, const vector<string> &register_names,
+ Reporter *reporter)
+ : module_(module), register_names_(register_names), reporter_(reporter),
+ entry_(NULL), return_address_(-1) { }
+ virtual ~DwarfCFIToModule() { delete entry_; }
+
+ virtual bool Entry(size_t offset, uint64 address, uint64 length,
+ uint8 version, const string &augmentation,
+ unsigned return_address);
+ virtual bool UndefinedRule(uint64 address, int reg);
+ virtual bool SameValueRule(uint64 address, int reg);
+ virtual bool OffsetRule(uint64 address, int reg,
+ int base_register, long offset);
+ virtual bool ValOffsetRule(uint64 address, int reg,
+ int base_register, long offset);
+ virtual bool RegisterRule(uint64 address, int reg, int base_register);
+ virtual bool ExpressionRule(uint64 address, int reg,
+ const string &expression);
+ virtual bool ValExpressionRule(uint64 address, int reg,
+ const string &expression);
+ virtual bool End();
+
+ private:
+ // Return the name to use for register REG.
+ string RegisterName(int i);
+
+ // Record RULE for register REG at ADDRESS.
+ void Record(Module::Address address, int reg, const string &rule);
+
+ // The module to which we should add entries.
+ Module *module_;
+
+ // Map from register numbers to register names.
+ const vector<string> &register_names_;
+
+ // The reporter to use to report problems.
+ Reporter *reporter_;
+
+ // The current entry we're constructing.
+ Module::StackFrameEntry *entry_;
+
+ // The section offset of the current frame description entry, for
+ // use in error messages.
+ size_t entry_offset_;
+
+ // The return address column for that entry.
+ unsigned return_address_;
+};
+
+} // namespace google_breakpad
+
+#endif // COMMON_LINUX_DWARF_CFI_TO_MODULE_H
diff --git a/src/common/linux/dwarf_cfi_to_module_unittest.cc b/src/common/linux/dwarf_cfi_to_module_unittest.cc
new file mode 100644
index 00000000..de769393
--- /dev/null
+++ b/src/common/linux/dwarf_cfi_to_module_unittest.cc
@@ -0,0 +1,274 @@
+// 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>
+
+// dwarf_cfi_to_module_unittest.cc: Tests for google_breakpad::DwarfCFIToModule.
+
+#include "breakpad_googletest_includes.h"
+#include "common/linux/dwarf_cfi_to_module.h"
+
+using google_breakpad::Module;
+using google_breakpad::DwarfCFIToModule;
+using testing::ContainerEq;
+using testing::Test;
+using testing::_;
+
+struct MockCFIReporter: public DwarfCFIToModule::Reporter {
+ MockCFIReporter(const string &file, const string &section)
+ : Reporter(file, section) { }
+ MOCK_METHOD2(UnnamedRegister, void(size_t offset, int reg));
+ MOCK_METHOD2(UndefinedNotSupported, void(size_t offset, const string &reg));
+ MOCK_METHOD2(ExpressionsNotSupported, void(size_t offset, const string &reg));
+};
+
+struct DwarfCFIToModuleFixture {
+ DwarfCFIToModuleFixture()
+ : module("module name", "module os", "module arch", "module id"),
+ reporter("reporter file", "reporter section"),
+ handler(&module, register_names, &reporter) {
+ register_names.push_back("reg0");
+ register_names.push_back("reg1");
+ register_names.push_back("reg2");
+ register_names.push_back("reg3");
+ register_names.push_back("reg4");
+ register_names.push_back("reg5");
+ register_names.push_back("reg6");
+ register_names.push_back("reg7");
+ register_names.push_back("sp");
+ register_names.push_back("pc");
+
+ EXPECT_CALL(reporter, UnnamedRegister(_, _)).Times(0);
+ EXPECT_CALL(reporter, UndefinedNotSupported(_, _)).Times(0);
+ EXPECT_CALL(reporter, ExpressionsNotSupported(_, _)).Times(0);
+ }
+
+ Module module;
+ vector<string> register_names;
+ MockCFIReporter reporter;
+ DwarfCFIToModule handler;
+ vector<Module::StackFrameEntry *> entries;
+};
+
+class Entry: public DwarfCFIToModuleFixture, public Test { };
+
+TEST_F(Entry, IgnoreVersion) {
+ ASSERT_FALSE(handler.Entry(0xf120e638, 0x2851bc1f7a181d6dULL,
+ 0x40589a48d66e5a88ULL, 4, "", 0x1ad80491));
+ module.GetStackFrameEntries(&entries);
+ EXPECT_EQ(0U, entries.size());
+}
+
+TEST_F(Entry, IgnoreAugmentation) {
+ ASSERT_FALSE(handler.Entry(0x3f9d228a, 0xcf9a94bb805cf5a4ULL,
+ 0xe6c41bf958d4c171ULL, 3, "snazzy", 0x444a14f3));
+ module.GetStackFrameEntries(&entries);
+ EXPECT_EQ(0U, entries.size());
+}
+
+TEST_F(Entry, Accept) {
+ ASSERT_TRUE(handler.Entry(0x3b8961b8, 0xa21069698096fc98ULL,
+ 0xb440ce248169c8d6ULL, 3, "", 0xea93c106));
+ ASSERT_TRUE(handler.End());
+ module.GetStackFrameEntries(&entries);
+ EXPECT_EQ(1U, entries.size());
+ EXPECT_EQ(0xa21069698096fc98ULL, entries[0]->address);
+ EXPECT_EQ(0xb440ce248169c8d6ULL, entries[0]->size);
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Entry, AcceptOldVersion) {
+ ASSERT_TRUE(handler.Entry(0xeb60e0fc, 0x75b8806bb09eab78ULL,
+ 0xc771f44958d40bbcULL, 1, "", 0x093c945e));
+ ASSERT_TRUE(handler.End());
+ module.GetStackFrameEntries(&entries);
+ EXPECT_EQ(1U, entries.size());
+ EXPECT_EQ(0x75b8806bb09eab78ULL, entries[0]->address);
+ EXPECT_EQ(0xc771f44958d40bbcULL, entries[0]->size);
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+struct RuleFixture: public DwarfCFIToModuleFixture {
+ RuleFixture() : DwarfCFIToModuleFixture() {
+ entry_address = 0x89327ebf86b47492ULL;
+ entry_size = 0x2f8cd573072fe02aULL;
+ return_reg = 0x7886a346;
+ }
+ void StartEntry() {
+ ASSERT_TRUE(handler.Entry(0x4445c05c, entry_address, entry_size,
+ 3, "", return_reg));
+ }
+ void CheckEntry() {
+ module.GetStackFrameEntries(&entries);
+ EXPECT_EQ(1U, entries.size());
+ EXPECT_EQ(entry_address, entries[0]->address);
+ EXPECT_EQ(entry_size, entries[0]->size);
+ }
+ uint64 entry_address, entry_size;
+ unsigned return_reg;
+};
+
+class Rule: public RuleFixture, public Test { };
+
+TEST_F(Rule, UndefinedRule) {
+ EXPECT_CALL(reporter, UndefinedNotSupported(_, "reg7"));
+ StartEntry();
+ ASSERT_TRUE(handler.UndefinedRule(entry_address, 7));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Rule, SameValueRule) {
+ StartEntry();
+ ASSERT_TRUE(handler.SameValueRule(entry_address, 6));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ Module::RuleMap expected_initial;
+ expected_initial["reg6"] = "reg6";
+ EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Rule, OffsetRule) {
+ StartEntry();
+ ASSERT_TRUE(handler.OffsetRule(entry_address + 1, return_reg,
+ DwarfCFIToModule::kCFARegister,
+ 16927065));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ Module::RuleChangeMap expected_changes;
+ expected_changes[entry_address + 1][".ra"] = ".cfa 16927065 + ^";
+ EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
+}
+
+TEST_F(Rule, OffsetRuleNegative) {
+ StartEntry();
+ ASSERT_TRUE(handler.OffsetRule(entry_address + 1,
+ DwarfCFIToModule::kCFARegister, 4, -34530721));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ Module::RuleChangeMap expected_changes;
+ expected_changes[entry_address + 1][".cfa"] = "reg4 -34530721 + ^";
+ EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
+}
+
+TEST_F(Rule, ValOffsetRule) {
+ // Use an unnamed register number, to exercise that branch of RegisterName.
+ EXPECT_CALL(reporter, UnnamedRegister(_, 10));
+ StartEntry();
+ ASSERT_TRUE(handler.ValOffsetRule(entry_address + 0x5ab7,
+ DwarfCFIToModule::kCFARegister,
+ 10, 61812979));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ Module::RuleChangeMap expected_changes;
+ expected_changes[entry_address + 0x5ab7][".cfa"] =
+ "unnamed_register10 61812979 +";
+ EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
+}
+
+TEST_F(Rule, RegisterRule) {
+ StartEntry();
+ ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 3));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ Module::RuleMap expected_initial;
+ expected_initial[".ra"] = "reg3";
+ EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Rule, ExpressionRule) {
+ EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg2"));
+ StartEntry();
+ ASSERT_TRUE(handler.ExpressionRule(entry_address + 0xf326, 2,
+ "it takes two to tango"));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Rule, ValExpressionRule) {
+ EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg0"));
+ StartEntry();
+ ASSERT_TRUE(handler.ValExpressionRule(entry_address + 0x6367, 0,
+ "bit off more than he could chew"));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Rule, DefaultReturnAddressRule) {
+ return_reg = 2;
+ StartEntry();
+ ASSERT_TRUE(handler.RegisterRule(entry_address, 0, 1));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ Module::RuleMap expected_initial;
+ expected_initial[".ra"] = "reg2";
+ expected_initial["reg0"] = "reg1";
+ EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Rule, DefaultReturnAddressRuleOverride) {
+ return_reg = 2;
+ StartEntry();
+ ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 1));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ Module::RuleMap expected_initial;
+ expected_initial[".ra"] = "reg1";
+ EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Rule, DefaultReturnAddressRuleLater) {
+ return_reg = 2;
+ StartEntry();
+ ASSERT_TRUE(handler.RegisterRule(entry_address + 1, return_reg, 1));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ Module::RuleMap expected_initial;
+ expected_initial[".ra"] = "reg2";
+ EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
+ Module::RuleChangeMap expected_changes;
+ expected_changes[entry_address + 1][".ra"] = "reg1";
+ EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
+}
+
diff --git a/src/common/linux/module.cc b/src/common/linux/module.cc
index bd0ae9f9..f6373895 100644
--- a/src/common/linux/module.cc
+++ b/src/common/linux/module.cc
@@ -52,6 +52,9 @@ Module::~Module() {
for (vector<Function *>::iterator it = functions_.begin();
it != functions_.end(); it++)
delete *it;
+ for (vector<StackFrameEntry *>::iterator it = stack_frame_entries_.begin();
+ it != stack_frame_entries_.end(); it++)
+ delete *it;
}
void Module::SetLoadAddress(Address address) {
@@ -67,6 +70,10 @@ void Module::AddFunctions(vector<Function *>::iterator begin,
functions_.insert(functions_.end(), begin, end);
}
+void Module::AddStackFrameEntry(StackFrameEntry *stack_frame_entry) {
+ stack_frame_entries_.push_back(stack_frame_entry);
+}
+
void Module::GetFunctions(vector<Function *> *vec,
vector<Function *>::iterator i) {
vec->insert(i, functions_.begin(), functions_.end());
@@ -111,6 +118,10 @@ void Module::GetFiles(vector<File *> *vec) {
vec->push_back(it->second);
}
+void Module::GetStackFrameEntries(vector<StackFrameEntry *> *vec) {
+ *vec = stack_frame_entries_;
+}
+
void Module::AssignSourceIds() {
// First, give every source file an id of -1.
for (FileByNameMap::iterator file_it = files_.begin();
@@ -144,6 +155,18 @@ bool Module::ReportError() {
return false;
}
+bool Module::WriteRuleMap(const RuleMap &rule_map, FILE *stream) {
+ for (RuleMap::const_iterator it = rule_map.begin();
+ it != rule_map.end(); it++) {
+ if (it != rule_map.begin() &&
+ 0 > putc(' ', stream))
+ return false;
+ if (0 > fprintf(stream, "%s: %s", it->first.c_str(), it->second.c_str()))
+ return false;
+ }
+ return true;
+}
+
bool Module::Write(FILE *stream) {
if (0 > fprintf(stream, "MODULE %s %s %s %s\n",
os_.c_str(), architecture_.c_str(), id_.c_str(),
@@ -183,6 +206,29 @@ bool Module::Write(FILE *stream) {
return ReportError();
}
+ // Write out 'STACK CFI INIT' and 'STACK CFI' records.
+ vector<StackFrameEntry *>::const_iterator frame_it;
+ for (frame_it = stack_frame_entries_.begin();
+ frame_it != stack_frame_entries_.end(); frame_it++) {
+ StackFrameEntry *entry = *frame_it;
+ if (0 > fprintf(stream, "STACK CFI INIT %llx %llx ",
+ (unsigned long long) entry->address - load_address_,
+ (unsigned long long) entry->size)
+ || !WriteRuleMap(entry->initial_rules, stream)
+ || 0 > putc('\n', stream))
+ return ReportError();
+
+ // Write out this entry's delta rules as 'STACK CFI' records.
+ for (RuleChangeMap::const_iterator delta_it = entry->rule_changes.begin();
+ delta_it != entry->rule_changes.end(); delta_it++) {
+ if (0 > fprintf(stream, "STACK CFI %llx ",
+ (unsigned long long) delta_it->first - load_address_)
+ || !WriteRuleMap(delta_it->second, stream)
+ || 0 > putc('\n', stream))
+ return ReportError();
+ }
+ }
+
return true;
}
diff --git a/src/common/linux/module.h b/src/common/linux/module.h
index eeda305e..7a44d4ab 100644
--- a/src/common/linux/module.h
+++ b/src/common/linux/module.h
@@ -113,6 +113,35 @@ class Module {
File *file; // The source file.
int number; // The source line number.
};
+
+ // A map from register names to postfix expressions that recover
+ // their their values. This can represent a complete set of rules to
+ // follow at some address, or a set of changes to be applied to an
+ // extant set of rules.
+ typedef map<string, string> RuleMap;
+
+ // A map from addresses to RuleMaps, representing changes that take
+ // effect at given addresses.
+ typedef map<Address, RuleMap> RuleChangeMap;
+
+ // A range of 'STACK CFI' stack walking information. An instance of
+ // this structure corresponds to a 'STACK CFI INIT' record and the
+ // subsequent 'STACK CFI' records that fall within its range.
+ struct StackFrameEntry {
+ // The starting address and number of bytes of machine code this
+ // entry covers.
+ Address address, size;
+
+ // The initial register recovery rules, in force at the starting
+ // address.
+ RuleMap initial_rules;
+
+ // A map from addresses to rule changes. To find the rules in
+ // force at a given address, start with initial_rules, and then
+ // apply the changes given in this map for all addresses up to and
+ // including the address you're interested in.
+ RuleChangeMap rule_changes;
+ };
// Create a new module with the given name, operating system,
// architecture, and ID string.
@@ -139,6 +168,12 @@ class Module {
void AddFunctions(vector<Function *>::iterator begin,
vector<Function *>::iterator end);
+ // Add STACK_FRAME_ENTRY to the module.
+ //
+ // This module owns all StackFrameEntry objects added with this
+ // function: destroying the module destroys them as well.
+ void AddStackFrameEntry(StackFrameEntry *stack_frame_entry);
+
// If this module has a file named NAME, return a pointer to it. If
// it has none, then create one and return a pointer to the new
// file. This module owns all File objects created using these
@@ -151,17 +186,26 @@ class Module {
File *FindExistingFile(const string &name);
// Insert pointers to the functions added to this module at I in
- // VEC. (Since this is effectively a copy of the function list, this
- // is mostly useful for testing; other uses should probably get a
- // more appropriate interface.)
+ // VEC. The pointed-to Functions are still owned by this module.
+ // (Since this is effectively a copy of the function list, this is
+ // mostly useful for testing; other uses should probably get a more
+ // appropriate interface.)
void GetFunctions(vector<Function *> *vec, vector<Function *>::iterator i);
// Clear VEC and fill it with pointers to the Files added to this
- // module, sorted by name. (Since this is effectively a copy of the
- // function list, this is mostly useful for testing; other uses
- // should probably get a more appropriate interface.)
+ // module, sorted by name. The pointed-to Files are still owned by
+ // this module. (Since this is effectively a copy of the file list,
+ // this is mostly useful for testing; other uses should probably get
+ // a more appropriate interface.)
void GetFiles(vector<File *> *vec);
+ // Clear VEC and fill it with pointers to the StackFrameEntry
+ // objects that have been added to this module. (Since this is
+ // effectively a copy of the stack frame entry list, this is mostly
+ // useful for testing; other uses should probably get
+ // a more appropriate interface.)
+ void GetStackFrameEntries(vector<StackFrameEntry *> *vec);
+
// Find those files in this module that are actually referred to by
// functions' line number data, and assign them source id numbers.
// Set the source id numbers for all other files --- unused by the
@@ -185,6 +229,11 @@ private:
// errno to find the appropriate cause. Return false.
static bool ReportError();
+ // Write RULE_MAP to STREAM, in the form appropriate for 'STACK CFI'
+ // records, without a final newline. Return true if all goes well;
+ // if an error occurs, return false, and leave errno set.
+ static bool WriteRuleMap(const RuleMap &rule_map, FILE *stream);
+
// Module header entries.
string name_, os_, architecture_, id_;
@@ -208,6 +257,10 @@ private:
// point to.
FileByNameMap files_; // This module's source files.
vector<Function *> functions_; // This module's functions.
+
+ // The module owns all the call frame info entries that have been
+ // added to it.
+ vector<StackFrameEntry *> stack_frame_entries_;
};
} // namespace google_breakpad
diff --git a/src/common/linux/module_unittest.cc b/src/common/linux/module_unittest.cc
index d289f132..771d91e5 100644
--- a/src/common/linux/module_unittest.cc
+++ b/src/common/linux/module_unittest.cc
@@ -42,9 +42,10 @@
#include "breakpad_googletest_includes.h"
#include "common/linux/module.h"
+using google_breakpad::Module;
using std::string;
using std::vector;
-using google_breakpad::Module;
+using testing::ContainerEq;
// Return a FILE * referring to a temporary file that will be deleted
// automatically when the stream is closed or the program exits.
@@ -162,6 +163,17 @@ TEST(Write, RelativeLoadAddress) {
m.AddFunction(function);
+ // Some stack information.
+ Module::StackFrameEntry *entry = new Module::StackFrameEntry();
+ entry->address = 0x30f9e5c83323973dULL;
+ entry->size = 0x49fc9ca7c7c13dc2ULL;
+ entry->initial_rules[".cfa"] = "he was a handsome man";
+ entry->initial_rules["and"] = "what i want to know is";
+ entry->rule_changes[0x30f9e5c83323973eULL]["how"] =
+ "do you like your blueeyed boy";
+ entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death";
+ m.AddStackFrameEntry(entry);
+
m.Write(f);
checked_fflush(f);
rewind(f);
@@ -173,7 +185,13 @@ TEST(Write, RelativeLoadAddress) {
"FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0"
" A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n"
"b03cc3106d47eb91 cf621b8d324d0eb 67519080 0\n"
- "9410dc39a798c580 1c2be6d6c5af2611 41676901 1\n",
+ "9410dc39a798c580 1c2be6d6c5af2611 41676901 1\n"
+ "STACK CFI INIT 6434d177ce326ca 49fc9ca7c7c13dc2"
+ " .cfa: he was a handsome man"
+ " and: what i want to know is\n"
+ "STACK CFI 6434d177ce326cb"
+ " Mister: Death"
+ " how: do you like your blueeyed boy\n",
contents.c_str());
}
@@ -274,6 +292,96 @@ TEST(Construct, AddFunctions) {
EXPECT_EQ((size_t) 2, vec.size());
}
+TEST(Construct, AddFrames) {
+ FILE *f = checked_tmpfile();
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // First STACK CFI entry, with no initial rules or deltas.
+ Module::StackFrameEntry *entry1 = new Module::StackFrameEntry();
+ entry1->address = 0xddb5f41285aa7757ULL;
+ entry1->size = 0x1486493370dc5073ULL;
+ m.AddStackFrameEntry(entry1);
+
+ // Second STACK CFI entry, with initial rules but no deltas.
+ Module::StackFrameEntry *entry2 = new Module::StackFrameEntry();
+ entry2->address = 0x8064f3af5e067e38ULL;
+ entry2->size = 0x0de2a5ee55509407ULL;
+ entry2->initial_rules[".cfa"] = "I think that I shall never see";
+ entry2->initial_rules["stromboli"] = "a poem lovely as a tree";
+ entry2->initial_rules["cannoli"] = "a tree whose hungry mouth is prest";
+ m.AddStackFrameEntry(entry2);
+
+ // Third STACK CFI entry, with initial rules and deltas.
+ Module::StackFrameEntry *entry3 = new Module::StackFrameEntry();
+ entry3->address = 0x5e8d0db0a7075c6cULL;
+ entry3->size = 0x1c7edb12a7aea229ULL;
+ entry3->initial_rules[".cfa"] = "Whose woods are these";
+ entry3->rule_changes[0x47ceb0f63c269d7fULL]["calzone"] =
+ "the village though";
+ entry3->rule_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
+ "he will not see me stopping here";
+ entry3->rule_changes[0x36682fad3763ffffULL]["stromboli"] =
+ "his house is in";
+ entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] =
+ "I think I know";
+ m.AddStackFrameEntry(entry3);
+
+ // Check that Write writes STACK CFI records properly.
+ m.Write(f);
+ checked_fflush(f);
+ rewind(f);
+ string contents = checked_read(f);
+ checked_fclose(f);
+ EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
+ "STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n"
+ "STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407"
+ " .cfa: I think that I shall never see"
+ " cannoli: a tree whose hungry mouth is prest"
+ " stromboli: a poem lovely as a tree\n"
+ "STACK CFI INIT 5e8d0db0a7075c6c 1c7edb12a7aea229"
+ " .cfa: Whose woods are these\n"
+ "STACK CFI 36682fad3763ffff"
+ " .cfa: I think I know"
+ " stromboli: his house is in\n"
+ "STACK CFI 47ceb0f63c269d7f"
+ " calzone: the village though"
+ " cannoli: he will not see me stopping here\n",
+ contents.c_str());
+
+ // Check that GetStackFrameEntries works.
+ vector<Module::StackFrameEntry *> entries;
+ m.GetStackFrameEntries(&entries);
+ ASSERT_EQ(3U, entries.size());
+ // Check first entry.
+ EXPECT_EQ(0xddb5f41285aa7757ULL, entries[0]->address);
+ EXPECT_EQ(0x1486493370dc5073ULL, entries[0]->size);
+ ASSERT_EQ(0U, entries[0]->initial_rules.size());
+ ASSERT_EQ(0U, entries[0]->rule_changes.size());
+ // Check second entry.
+ EXPECT_EQ(0x8064f3af5e067e38ULL, entries[1]->address);
+ EXPECT_EQ(0x0de2a5ee55509407ULL, entries[1]->size);
+ ASSERT_EQ(3U, entries[1]->initial_rules.size());
+ Module::RuleMap entry2_initial;
+ entry2_initial[".cfa"] = "I think that I shall never see";
+ entry2_initial["stromboli"] = "a poem lovely as a tree";
+ entry2_initial["cannoli"] = "a tree whose hungry mouth is prest";
+ EXPECT_THAT(entries[1]->initial_rules, ContainerEq(entry2_initial));
+ ASSERT_EQ(0U, entries[1]->rule_changes.size());
+ // Check third entry.
+ EXPECT_EQ(0x5e8d0db0a7075c6cULL, entries[2]->address);
+ EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[2]->size);
+ Module::RuleMap entry3_initial;
+ entry3_initial[".cfa"] = "Whose woods are these";
+ EXPECT_THAT(entries[2]->initial_rules, ContainerEq(entry3_initial));
+ Module::RuleChangeMap entry3_changes;
+ entry3_changes[0x36682fad3763ffffULL][".cfa"] = "I think I know";
+ entry3_changes[0x36682fad3763ffffULL]["stromboli"] = "his house is in";
+ entry3_changes[0x47ceb0f63c269d7fULL]["calzone"] = "the village though";
+ entry3_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
+ "he will not see me stopping here";
+ EXPECT_THAT(entries[2]->rule_changes, ContainerEq(entry3_changes));
+}
+
TEST(Construct, UniqueFiles) {
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
Module::File *file1 = m.FindFile("foo");
diff --git a/src/tools/linux/dump_syms/Makefile b/src/tools/linux/dump_syms/Makefile
index b02731b6..f00c34af 100644
--- a/src/tools/linux/dump_syms/Makefile
+++ b/src/tools/linux/dump_syms/Makefile
@@ -77,6 +77,7 @@ COVERAGE_SOURCES =
all:: dump_syms
dump_syms: \
bytereader.o \
+ dwarf_cfi_to_module.o \
dwarf_cu_to_module.o \
dwarf_line_to_module.o \
dump_stabs.o \
@@ -96,6 +97,8 @@ clean::
dump_syms.o: dump_syms.cc
VPATH += $(SRC)/common/linux
+dwarf_cfi_to_module.o: dwarf_cfi_to_module.cc
+COVERAGE_SOURCES += dwarf_cfi_to_module.cc
dwarf_cu_to_module.o: dwarf_cu_to_module.cc
COVERAGE_SOURCES += dwarf_cu_to_module.cc
dwarf_line_to_module.o: dwarf_line_to_module.cc
@@ -112,10 +115,11 @@ COVERAGE_SOURCES += stabs_reader.cc
VPATH += $(SRC)/common/dwarf
bytereader.o: bytereader.cc
+cfi_assembler.o: cfi_assembler.cc
dwarf2diehandler.o: dwarf2diehandler.cc
COVERAGE_SOURCES += dwarf2diehandler.cc
dwarf2reader.o: dwarf2reader.cc
-
+COVERAGE_SOURCES += dwarf2reader.cc
### Google C++ Testing Framework.
@@ -133,6 +137,13 @@ gmock-all.o: gmock-all.cc
gmock-all.o: override CPPFLAGS += $(GTEST_CPPFLAGS) $(GMOCK_CPPFLAGS)
+### google_breakpad::TestAssembler, for constructing binary test data
+VPATH += $(SRC)/processor
+TEST_ASSEMBLER_CPPFLAGS = -I$(SRC)/processor
+test_assembler.o: test_assembler.cc
+test_assembler.o: override CPPFLAGS += $(TEST_ASSEMBLER_CPPFLAGS)
+
+
### Unit tests for google_breakpad::StabsReader.
check: check-stabs_reader_unittest
check-stabs_reader_unittest: stabs_reader_unittest
@@ -170,6 +181,7 @@ clean::
check: check-module_unittest
check-module_unittest: module_unittest
module_unittest: \
+ gmock-all.o \
gtest-all.o \
gtest_main.o \
module.o \
@@ -258,6 +270,47 @@ clean::
+### Unit tests for dwarf2reader::CallFrameInfo.
+check: check-dwarf2reader_cfi_unittest
+check-dwarf2reader_cfi_unittest: dwarf2reader_cfi_unittest
+dwarf2reader_cfi_unittest: \
+ bytereader.o \
+ cfi_assembler.o \
+ dwarf2reader.o \
+ gmock-all.o \
+ gtest-all.o \
+ gtest_main.o \
+ test_assembler.o \
+ $(empty)
+CPP_EXECUTABLES += dwarf2reader_cfi_unittest
+dwarf2reader_cfi_unittest.o: dwarf2reader_cfi_unittest.cc
+dwarf2reader_cfi_unittest.o: override CPPFLAGS += $(GTEST_CPPFLAGS) \
+ $(GMOCK_CPPFLAGS) \
+ $(TEST_ASSEMBLER_CPPFLAGS)
+clean::
+ rm -f dwarf2reader_cfi_unittest
+
+
+
+### Unit tests for google_breakpad::DwarfCFIToModule.
+check: check-dwarf_cfi_to_module_unittest
+check-dwarf_cfi_to_module_unittest: dwarf_cfi_to_module_unittest
+dwarf_cfi_to_module_unittest: \
+ dwarf_cfi_to_module.o \
+ gmock-all.o \
+ gtest-all.o \
+ gtest_main.o \
+ module.o \
+ $(empty)
+CPP_EXECUTABLES += dwarf_cfi_to_module_unittest
+dwarf_cfi_to_module_unittest.o: dwarf_cfi_to_module_unittest.cc
+dwarf_cfi_to_module_unittest.o: override CPPFLAGS += $(GTEST_CPPFLAGS) \
+ $(GMOCK_CPPFLAGS)
+clean::
+ rm -f dwarf_cfi_to_module_unittest
+
+
+
### Generic compilation rules.
# Link C++ executables using the C++ compiler; see CPP_EXECUTABLES above.