mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2025-04-17 12:41:40 +00:00
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
This commit is contained in:
parent
6d3a825dbf
commit
3e768ed9c0
103
src/common/dwarf/cfi_assembler.cc
Normal file
103
src/common/dwarf/cfi_assembler.cc
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
149
src/common/dwarf/cfi_assembler.h
Normal file
149
src/common/dwarf/cfi_assembler.h
Normal file
|
@ -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_
|
|
@ -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__
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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 §ion = ".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
|
||||
|
||||
|
|
1928
src/common/dwarf/dwarf2reader_cfi_unittest.cc
Normal file
1928
src/common/dwarf/dwarf2reader_cfi_unittest.cc
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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, ®ister_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");
|
||||
|
|
187
src/common/linux/dwarf_cfi_to_module.cc
Normal file
187
src/common/linux/dwarf_cfi_to_module.cc
Normal file
|
@ -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 ®) {
|
||||
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 ®) {
|
||||
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
|
154
src/common/linux/dwarf_cfi_to_module.h
Normal file
154
src/common/linux/dwarf_cfi_to_module.h
Normal file
|
@ -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 §ion)
|
||||
: 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 ®);
|
||||
|
||||
// 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 ®);
|
||||
|
||||
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> ®ister_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> ®ister_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
|
274
src/common/linux/dwarf_cfi_to_module_unittest.cc
Normal file
274
src/common/linux/dwarf_cfi_to_module_unittest.cc
Normal file
|
@ -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 §ion)
|
||||
: Reporter(file, section) { }
|
||||
MOCK_METHOD2(UnnamedRegister, void(size_t offset, int reg));
|
||||
MOCK_METHOD2(UndefinedNotSupported, void(size_t offset, const string ®));
|
||||
MOCK_METHOD2(ExpressionsNotSupported, void(size_t offset, const string ®));
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue