1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-31 01:30:02 +02:00

* Updated breakpad to latest version.

This commit is contained in:
Christian Muehlhaeuser
2012-06-24 18:25:34 +02:00
parent 0a3a9a7e97
commit d3eb5c3f88
186 changed files with 9184 additions and 5835 deletions

39
thirdparty/breakpad/common/basictypes.h vendored Normal file
View File

@@ -0,0 +1,39 @@
// Copyright (c) 2011 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.
#ifndef COMMON_BASICTYPES_H_
#define COMMON_BASICTYPES_H_
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
#endif // COMMON_BASICTYPES_H_

View File

@@ -176,11 +176,21 @@ void DIEDispatcher::ProcessAttributeBuffer(uint64 offset,
void DIEDispatcher::ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string& data) {
const std::string& data) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeString(attr, form, data);
}
void DIEDispatcher::ProcessAttributeSignature(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 signature) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeSignature(attr, form, signature);
}
} // namespace dwarf2reader

View File

@@ -208,7 +208,10 @@ class DIEHandler {
uint64 len) { }
virtual void ProcessAttributeString(enum DwarfAttribute attr,
enum DwarfForm form,
const string& data) { }
const std::string& data) { }
virtual void ProcessAttributeSignature(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 signture) { }
// Once we have reported all the DIE's attributes' values, we call
// this member function. If it returns false, we skip all the DIE's
@@ -313,7 +316,11 @@ class DIEDispatcher: public Dwarf2Handler {
void ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string &data);
const std::string &data);
void ProcessAttributeSignature(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 signature);
void EndDIE(uint64 offset);
private:
@@ -347,7 +354,7 @@ class DIEDispatcher: public Dwarf2Handler {
// - When we decide to ignore a subtree, we only push an entry on
// the stack for the root of the tree being ignored, rather than
// pushing lots of stack entries with handler_ set to NULL.
stack<HandlerStack> die_handlers_;
std::stack<HandlerStack> die_handlers_;
// The root handler. We don't push it on die_handlers_ until we
// actually get the StartDIE call for the root.

View File

@@ -32,10 +32,16 @@
// dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher.
#include <string>
#include <utility>
#include "breakpad_googletest_includes.h"
#include "common/dwarf/dwarf2diehandler.h"
using std::make_pair;
using std::string;
using ::testing::_;
using ::testing::ContainerEq;
using ::testing::ElementsAreArray;
@@ -65,6 +71,8 @@ class MockDIEHandler: public DIEHandler {
void(DwarfAttribute, DwarfForm, const char *, uint64));
MOCK_METHOD3(ProcessAttributeString,
void(DwarfAttribute, DwarfForm, const string &));
MOCK_METHOD3(ProcessAttributeSignature,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD0(EndAttributes, bool());
MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
const AttributeList &));
@@ -83,6 +91,8 @@ class MockRootDIEHandler: public RootDIEHandler {
void(DwarfAttribute, DwarfForm, const char *, uint64));
MOCK_METHOD3(ProcessAttributeString,
void(DwarfAttribute, DwarfForm, const string &));
MOCK_METHOD3(ProcessAttributeSignature,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD0(EndAttributes, bool());
MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
const AttributeList &));
@@ -238,6 +248,11 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) {
(DwarfForm) 0x15762fec,
StrEq(str)))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler,
ProcessAttributeSignature((DwarfAttribute) 0x58790d72,
(DwarfForm) 0x4159f138,
0x94682463613e6a5fULL))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler, EndAttributes())
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler, FindChildHandler(_, _, _))
@@ -279,6 +294,10 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) {
(DwarfAttribute) 0x310ed065,
(DwarfForm) 0x15762fec,
str);
die_dispatcher.ProcessAttributeSignature(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x58790d72,
(DwarfForm) 0x4159f138,
0x94682463613e6a5fULL);
// Finish the root DIE (and thus the CU).
die_dispatcher.EndDIE(0xe2222da01e29f2a9LL);

View File

@@ -143,7 +143,13 @@ enum DwarfForm {
DW_FORM_ref4 = 0x13,
DW_FORM_ref8 = 0x14,
DW_FORM_ref_udata = 0x15,
DW_FORM_indirect = 0x16
DW_FORM_indirect = 0x16,
// Added in DWARF 4:
DW_FORM_sec_offset = 0x17,
DW_FORM_exprloc = 0x18,
DW_FORM_flag_present = 0x19,
DW_FORM_ref_sig8 = 0x20
};
// Attribute names and codes

View File

@@ -39,6 +39,7 @@
#include <string.h>
#include <map>
#include <memory>
#include <stack>
#include <utility>
@@ -74,7 +75,7 @@ void CompilationUnit::ReadAbbrevs() {
iter = sections_.find("__debug_abbrev");
assert(iter != sections_.end());
abbrevs_ = new vector<Abbrev>;
abbrevs_ = new std::vector<Abbrev>;
abbrevs_->resize(1);
// The only way to check whether we are reading over the end of the
@@ -121,7 +122,7 @@ void CompilationUnit::ReadAbbrevs() {
const enum DwarfAttribute name =
static_cast<enum DwarfAttribute>(nametemp);
const enum DwarfForm form = static_cast<enum DwarfForm>(formtemp);
abbrev.attributes.push_back(make_pair(name, form));
abbrev.attributes.push_back(std::make_pair(name, form));
}
assert(abbrev.number == abbrevs_->size());
abbrevs_->push_back(abbrev);
@@ -150,41 +151,35 @@ const char* CompilationUnit::SkipAttribute(const char* start,
&len));
start += len;
return SkipAttribute(start, form);
break;
case DW_FORM_flag_present:
return start;
case DW_FORM_data1:
case DW_FORM_flag:
case DW_FORM_ref1:
return start + 1;
break;
case DW_FORM_ref2:
case DW_FORM_data2:
return start + 2;
break;
case DW_FORM_ref4:
case DW_FORM_data4:
return start + 4;
break;
case DW_FORM_ref8:
case DW_FORM_data8:
case DW_FORM_ref_sig8:
return start + 8;
break;
case DW_FORM_string:
return start + strlen(start) + 1;
break;
case DW_FORM_udata:
case DW_FORM_ref_udata:
reader_->ReadUnsignedLEB128(start, &len);
return start + len;
break;
case DW_FORM_sdata:
reader_->ReadSignedLEB128(start, &len);
return start + len;
break;
case DW_FORM_addr:
return start + reader_->AddressSize();
break;
case DW_FORM_ref_addr:
// DWARF2 and 3 differ on whether ref_addr is address size or
// offset size.
@@ -194,27 +189,21 @@ const char* CompilationUnit::SkipAttribute(const char* start,
} else if (header_.version == 3) {
return start + reader_->OffsetSize();
}
break;
case DW_FORM_block1:
return start + 1 + reader_->ReadOneByte(start);
break;
case DW_FORM_block2:
return start + 2 + reader_->ReadTwoBytes(start);
break;
case DW_FORM_block4:
return start + 4 + reader_->ReadFourBytes(start);
break;
case DW_FORM_block: {
case DW_FORM_block:
case DW_FORM_exprloc: {
uint64 size = reader_->ReadUnsignedLEB128(start, &len);
return start + size + len;
}
break;
case DW_FORM_strp:
return start + reader_->OffsetSize();
break;
default:
fprintf(stderr,"Unhandled form type");
case DW_FORM_sec_offset:
return start + reader_->OffsetSize();
}
fprintf(stderr,"Unhandled form type");
return NULL;
@@ -326,85 +315,78 @@ const char* CompilationUnit::ProcessAttribute(
&len));
start += len;
return ProcessAttribute(dieoffset, start, attr, form);
break;
case DW_FORM_flag_present:
handler_->ProcessAttributeUnsigned(dieoffset, attr, form, 1);
return start;
case DW_FORM_data1:
case DW_FORM_flag:
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
reader_->ReadOneByte(start));
return start + 1;
break;
case DW_FORM_data2:
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
reader_->ReadTwoBytes(start));
return start + 2;
break;
case DW_FORM_data4:
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
reader_->ReadFourBytes(start));
return start + 4;
break;
case DW_FORM_data8:
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
reader_->ReadEightBytes(start));
return start + 8;
break;
case DW_FORM_string: {
const char* str = start;
handler_->ProcessAttributeString(dieoffset, attr, form,
str);
return start + strlen(str) + 1;
}
break;
case DW_FORM_udata:
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
reader_->ReadUnsignedLEB128(start,
&len));
return start + len;
break;
case DW_FORM_sdata:
handler_->ProcessAttributeSigned(dieoffset, attr, form,
reader_->ReadSignedLEB128(start, &len));
return start + len;
break;
case DW_FORM_addr:
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
reader_->ReadAddress(start));
return start + reader_->AddressSize();
break;
case DW_FORM_sec_offset:
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
reader_->ReadOffset(start));
return start + reader_->OffsetSize();
case DW_FORM_ref1:
handler_->ProcessAttributeReference(dieoffset, attr, form,
reader_->ReadOneByte(start)
+ offset_from_section_start_);
return start + 1;
break;
case DW_FORM_ref2:
handler_->ProcessAttributeReference(dieoffset, attr, form,
reader_->ReadTwoBytes(start)
+ offset_from_section_start_);
return start + 2;
break;
case DW_FORM_ref4:
handler_->ProcessAttributeReference(dieoffset, attr, form,
reader_->ReadFourBytes(start)
+ offset_from_section_start_);
return start + 4;
break;
case DW_FORM_ref8:
handler_->ProcessAttributeReference(dieoffset, attr, form,
reader_->ReadEightBytes(start)
+ offset_from_section_start_);
return start + 8;
break;
case DW_FORM_ref_udata:
handler_->ProcessAttributeReference(dieoffset, attr, form,
reader_->ReadUnsignedLEB128(start,
&len)
+ offset_from_section_start_);
return start + len;
break;
case DW_FORM_ref_addr:
// DWARF2 and 3 differ on whether ref_addr is address size or
// offset size.
@@ -419,35 +401,36 @@ const char* CompilationUnit::ProcessAttribute(
return start + reader_->OffsetSize();
}
break;
case DW_FORM_ref_sig8:
handler_->ProcessAttributeSignature(dieoffset, attr, form,
reader_->ReadEightBytes(start));
return start + 8;
case DW_FORM_block1: {
uint64 datalen = reader_->ReadOneByte(start);
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 1,
datalen);
datalen);
return start + 1 + datalen;
}
break;
case DW_FORM_block2: {
uint64 datalen = reader_->ReadTwoBytes(start);
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 2,
datalen);
datalen);
return start + 2 + datalen;
}
break;
case DW_FORM_block4: {
uint64 datalen = reader_->ReadFourBytes(start);
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 4,
datalen);
datalen);
return start + 4 + datalen;
}
break;
case DW_FORM_block: {
case DW_FORM_block:
case DW_FORM_exprloc: {
uint64 datalen = reader_->ReadUnsignedLEB128(start, &len);
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + len,
datalen);
datalen);
return start + datalen + len;
}
break;
case DW_FORM_strp: {
assert(string_buffer_ != NULL);
@@ -459,11 +442,8 @@ const char* CompilationUnit::ProcessAttribute(
str);
return start + reader_->OffsetSize();
}
break;
default:
fprintf(stderr, "Unhandled form type");
}
fprintf(stderr, "Unhandled form type");
fprintf(stderr, "Unhandled form type\n");
return NULL;
}
@@ -493,7 +473,7 @@ void CompilationUnit::ProcessDIEs() {
else
lengthstart += 4;
stack<uint64> die_stack;
std::stack<uint64> die_stack;
while (dieptr < (lengthstart + header_.length)) {
// We give the user the absolute offset from the beginning of
@@ -583,7 +563,7 @@ void LineInfo::ReadHeader() {
header_.opcode_base = reader_->ReadOneByte(lineptr);
lineptr += 1;
header_.std_opcode_lengths = new vector<unsigned char>;
header_.std_opcode_lengths = new std::vector<unsigned char>;
header_.std_opcode_lengths->resize(header_.opcode_base + 1);
(*header_.std_opcode_lengths)[0] = 0;
for (int i = 1; i < header_.opcode_base; i++) {
@@ -1024,7 +1004,7 @@ class CallFrameInfo::RegisterRule: public CallFrameInfo::Rule {
// Rule: EXPRESSION evaluates to the address at which the register is saved.
class CallFrameInfo::ExpressionRule: public CallFrameInfo::Rule {
public:
explicit ExpressionRule(const string &expression)
explicit ExpressionRule(const std::string &expression)
: expression_(expression) { }
~ExpressionRule() { }
bool Handle(Handler *handler, uint64 address, int reg) const {
@@ -1038,13 +1018,13 @@ class CallFrameInfo::ExpressionRule: public CallFrameInfo::Rule {
}
Rule *Copy() const { return new ExpressionRule(*this); }
private:
string expression_;
std::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)
explicit ValExpressionRule(const std::string &expression)
: expression_(expression) { }
~ValExpressionRule() { }
bool Handle(Handler *handler, uint64 address, int reg) const {
@@ -1059,7 +1039,7 @@ class CallFrameInfo::ValExpressionRule: public CallFrameInfo::Rule {
}
Rule *Copy() const { return new ValExpressionRule(*this); }
private:
string expression_;
std::string expression_;
};
// A map from register numbers to rules.
@@ -1096,7 +1076,7 @@ class CallFrameInfo::RuleMap {
private:
// A map from register numbers to Rules.
typedef map<int, Rule *> RuleByNumber;
typedef std::map<int, Rule *> RuleByNumber;
// Remove all register rules and clear cfa_rule_.
void Clear();
@@ -1240,7 +1220,7 @@ class CallFrameInfo::State {
unsigned register_number; // A register number.
uint64 offset; // An offset or address.
long signed_offset; // A signed offset.
string expression; // A DWARF expression.
std::string expression; // A DWARF expression.
};
// Parse CFI instruction operands from STATE's instruction stream as
@@ -1341,7 +1321,7 @@ class CallFrameInfo::State {
// A stack of saved states, for DW_CFA_remember_state and
// DW_CFA_restore_state.
stack<RuleMap> saved_rules_;
std::stack<RuleMap> saved_rules_;
};
bool CallFrameInfo::State::InterpretCIE(const CIE &cie) {
@@ -1427,7 +1407,7 @@ bool CallFrameInfo::State::ParseOperands(const char *format,
if (len > bytes_left || expression_length > bytes_left - len)
return ReportIncomplete();
cursor_ += len;
operands->expression = string(cursor_, expression_length);
operands->expression = std::string(cursor_, expression_length);
cursor_ += expression_length;
break;
}
@@ -1877,20 +1857,14 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
cie->version = reader_->ReadOneByte(cursor);
cursor++;
// If we don't recognize the version, we can't parse any more fields
// of the CIE. For DWARF CFI, we handle versions 1 through 3 (there
// was never a version 2 of CFI data). For .eh_frame, we handle only
// version 1.
if (eh_frame_) {
if (cie->version != 1) {
reporter_->UnrecognizedVersion(cie->offset, cie->version);
return false;
}
} else {
if (cie->version < 1 || cie->version > 3) {
reporter_->UnrecognizedVersion(cie->offset, cie->version);
return false;
}
// If we don't recognize the version, we can't parse any more fields of the
// CIE. For DWARF CFI, we handle versions 1 through 3 (there was never a
// version 2 of CFI data). For .eh_frame, we handle versions 1 and 3 as well;
// the difference between those versions seems to be the same as for
// .debug_frame.
if (cie->version < 1 || cie->version > 3) {
reporter_->UnrecognizedVersion(cie->offset, cie->version);
return false;
}
const char *augmentation_start = cursor;
@@ -1898,7 +1872,8 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
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);
cie->augmentation = std::string(augmentation_start,
cursor - augmentation_start);
// Skip the terminating '\0'.
cursor++;
@@ -2285,7 +2260,7 @@ void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) {
}
void CallFrameInfo::Reporter::UnrecognizedAugmentation(uint64 offset,
const string &aug) {
const std::string &aug) {
fprintf(stderr,
"%s: CFI frame description entry at offset 0x%llx in '%s':"
" CIE specifies unrecognized augmentation: '%s'\n",

View File

@@ -50,8 +50,6 @@
#include "common/dwarf/dwarf2enums.h"
#include "common/dwarf/types.h"
using namespace std;
namespace dwarf2reader {
struct LineStateMachine;
class Dwarf2Handler;
@@ -59,8 +57,9 @@ class LineInfoHandler;
// This maps from a string naming a section to a pair containing a
// the data for the section, and the size of the section.
typedef map<string, pair<const char*, uint64> > SectionMap;
typedef list<pair<enum DwarfAttribute, enum DwarfForm> > AttributeList;
typedef std::map<std::string, std::pair<const char*, uint64> > SectionMap;
typedef std::list<std::pair<enum DwarfAttribute, enum DwarfForm> >
AttributeList;
typedef AttributeList::iterator AttributeIterator;
typedef AttributeList::const_iterator ConstAttributeIterator;
@@ -75,7 +74,7 @@ struct LineInfoHeader {
uint8 opcode_base;
// Use a pointer so that signalsafe_addr2line is able to use this structure
// without heap allocation problem.
vector<unsigned char> *std_opcode_lengths;
std::vector<unsigned char> *std_opcode_lengths;
};
class LineInfo {
@@ -157,7 +156,7 @@ class LineInfoHandler {
// Called when we define a directory. NAME is the directory name,
// DIR_NUM is the directory number
virtual void DefineDir(const string& name, uint32 dir_num) { }
virtual void DefineDir(const std::string& name, uint32 dir_num) { }
// Called when we define a filename. NAME is the filename, FILE_NUM
// is the file number which is -1 if the file index is the next
@@ -166,7 +165,7 @@ class LineInfoHandler {
// directory index for the directory name of this file, MOD_TIME is
// the modification time of the file, and LENGTH is the length of
// the file
virtual void DefineFile(const string& name, int32 file_num,
virtual void DefineFile(const std::string& name, int32 file_num,
uint32 dir_num, uint64 mod_time,
uint64 length) { }
@@ -313,7 +312,7 @@ class CompilationUnit {
// Set of DWARF2/3 abbreviations for this compilation unit. Indexed
// by abbreviation number, which means that abbrevs_[0] is not
// valid.
vector<Abbrev>* abbrevs_;
std::vector<Abbrev>* abbrevs_;
// String section buffer and length, if we have a string section.
// This is here to avoid doing a section lookup for strings in
@@ -392,7 +391,16 @@ class Dwarf2Handler {
virtual void ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string& data) { }
const std::string& data) { }
// Called when we have an attribute whose value is the 64-bit signature
// of a type unit in the .debug_types section. OFFSET is the offset of
// the DIE whose attribute we're reporting. ATTR and FORM are the
// attribute's name and form. SIGNATURE is the type unit's signature.
virtual void ProcessAttributeSignature(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 signature) { }
// Called when finished processing the DIE at OFFSET.
// Because DWARF2/3 specifies a tree of DIEs, you may get starts
@@ -691,7 +699,7 @@ class CallFrameInfo {
// A common information entry (CIE).
struct CIE: public Entry {
uint8 version; // CFI data version number
string augmentation; // vendor format extension markers
std::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
@@ -825,7 +833,7 @@ class CallFrameInfo::Handler {
// 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,
uint8 version, const std::string &augmentation,
unsigned return_address) = 0;
// When the Entry function returns true, the parser calls these
@@ -874,13 +882,13 @@ class CallFrameInfo::Handler {
// 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;
const std::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;
const std::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
@@ -957,8 +965,8 @@ class CallFrameInfo::Reporter {
// 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")
Reporter(const std::string &filename,
const std::string &section = ".debug_frame")
: filename_(filename), section_(section) { }
virtual ~Reporter() { }
@@ -990,7 +998,7 @@ class CallFrameInfo::Reporter {
// 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);
const std::string &augmentation);
// The pointer encoding ENCODING, specified by the CIE at OFFSET, is not
// a valid encoding.
@@ -1031,10 +1039,10 @@ class CallFrameInfo::Reporter {
protected:
// The name of the file whose CFI we're reading.
string filename_;
std::string filename_;
// The name of the CFI section in that file.
string section_;
std::string section_;
};
} // namespace dwarf2reader

View File

@@ -33,6 +33,7 @@
#include <stdlib.h>
#include <string>
#include <vector>
// The '.eh_frame' format, used by the Linux C++ ABI for exception
@@ -75,6 +76,7 @@ using dwarf2reader::ENDIANNESS_LITTLE;
using dwarf2reader::ByteReader;
using dwarf2reader::CallFrameInfo;
using std::string;
using std::vector;
using testing::InSequence;
using testing::Return;

View File

@@ -0,0 +1,486 @@
// Copyright (c) 2012, 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_die_unittest.cc: Unit tests for dwarf2reader::CompilationUnit
#include <stdlib.h>
#include <iostream>
#include <string>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/dwarf2reader_test_common.h"
#include "common/dwarf/dwarf2reader.h"
#include "google_breakpad/common/breakpad_types.h"
using google_breakpad::test_assembler::Endianness;
using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::Section;
using google_breakpad::test_assembler::kBigEndian;
using google_breakpad::test_assembler::kLittleEndian;
using dwarf2reader::AttributeList;
using dwarf2reader::ByteReader;
using dwarf2reader::CompilationUnit;
using dwarf2reader::Dwarf2Handler;
using dwarf2reader::DwarfAttribute;
using dwarf2reader::DwarfForm;
using dwarf2reader::DwarfHasChild;
using dwarf2reader::DwarfTag;
using dwarf2reader::ENDIANNESS_BIG;
using dwarf2reader::ENDIANNESS_LITTLE;
using dwarf2reader::SectionMap;
using std::string;
using std::vector;
using testing::InSequence;
using testing::Pointee;
using testing::Return;
using testing::Sequence;
using testing::Test;
using testing::TestWithParam;
using testing::_;
class MockDwarf2Handler: public Dwarf2Handler {
public:
MOCK_METHOD5(StartCompilationUnit, bool(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version));
MOCK_METHOD3(StartDIE, bool(uint64 offset, enum DwarfTag tag,
const AttributeList& attrs));
MOCK_METHOD4(ProcessAttributeUnsigned, void(uint64 offset,
DwarfAttribute attr,
enum DwarfForm form,
uint64 data));
MOCK_METHOD4(ProcessAttributeSigned, void(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
int64 data));
MOCK_METHOD4(ProcessAttributeReference, void(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data));
MOCK_METHOD5(ProcessAttributeBuffer, void(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const char* data,
uint64 len));
MOCK_METHOD4(ProcessAttributeString, void(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const std::string& data));
MOCK_METHOD4(ProcessAttributeSignature, void(uint64 offset,
DwarfAttribute attr,
enum DwarfForm form,
uint64 signature));
MOCK_METHOD1(EndDIE, void(uint64 offset));
};
struct DIEFixture {
DIEFixture() {
// Fix the initial offset of the .debug_info and .debug_abbrev sections.
info.start() = 0;
abbrevs.start() = 0;
// Default expectations for the data handler.
EXPECT_CALL(handler, StartCompilationUnit(_, _, _, _, _)).Times(0);
EXPECT_CALL(handler, StartDIE(_, _, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeUnsigned(_, _, _, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeSigned(_, _, _, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeReference(_, _, _, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeBuffer(_, _, _, _, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeString(_, _, _, _)).Times(0);
EXPECT_CALL(handler, EndDIE(_)).Times(0);
}
// Return a reference to a section map whose .debug_info section refers
// to |info|, and whose .debug_abbrev section refers to |abbrevs|. This
// function returns a reference to the same SectionMap each time; new
// calls wipe out maps established by earlier calls.
const SectionMap &MakeSectionMap() {
// Copy the sections' contents into strings that will live as long as
// the map itself.
assert(info.GetContents(&info_contents));
assert(abbrevs.GetContents(&abbrevs_contents));
section_map.clear();
section_map[".debug_info"].first = info_contents.data();
section_map[".debug_info"].second = info_contents.size();
section_map[".debug_abbrev"].first = abbrevs_contents.data();
section_map[".debug_abbrev"].second = abbrevs_contents.size();
return section_map;
}
TestCompilationUnit info;
TestAbbrevTable abbrevs;
MockDwarf2Handler handler;
string abbrevs_contents, info_contents;
SectionMap section_map;
};
struct DwarfHeaderParams {
DwarfHeaderParams(Endianness endianness, size_t format_size,
int version, size_t address_size)
: endianness(endianness), format_size(format_size),
version(version), address_size(address_size) { }
Endianness endianness;
size_t format_size; // 4-byte or 8-byte DWARF offsets
int version;
size_t address_size;
};
class DwarfHeader: public DIEFixture,
public TestWithParam<DwarfHeaderParams> { };
TEST_P(DwarfHeader, Header) {
Label abbrev_table = abbrevs.Here();
abbrevs.Abbrev(1, dwarf2reader::DW_TAG_compile_unit,
dwarf2reader::DW_children_yes)
.Attribute(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_string)
.EndAbbrev()
.EndTable();
info.set_format_size(GetParam().format_size);
info.set_endianness(GetParam().endianness);
info.Header(GetParam().version, abbrev_table, GetParam().address_size)
.ULEB128(1) // DW_TAG_compile_unit, with children
.AppendCString("sam") // DW_AT_name, DW_FORM_string
.D8(0); // end of children
info.Finish();
{
InSequence s;
EXPECT_CALL(handler,
StartCompilationUnit(0, GetParam().address_size,
GetParam().format_size, _,
GetParam().version))
.WillOnce(Return(true));
EXPECT_CALL(handler, StartDIE(_, dwarf2reader::DW_TAG_compile_unit, _))
.WillOnce(Return(true));
EXPECT_CALL(handler, ProcessAttributeString(_, dwarf2reader::DW_AT_name,
dwarf2reader::DW_FORM_string,
"sam"))
.WillOnce(Return());
EXPECT_CALL(handler, EndDIE(_))
.WillOnce(Return());
}
ByteReader byte_reader(GetParam().endianness == kLittleEndian ?
ENDIANNESS_LITTLE : ENDIANNESS_BIG);
CompilationUnit parser(MakeSectionMap(), 0, &byte_reader, &handler);
EXPECT_EQ(parser.Start(), info_contents.size());
}
INSTANTIATE_TEST_CASE_P(
HeaderVariants, DwarfHeader,
::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4),
DwarfHeaderParams(kLittleEndian, 4, 2, 8),
DwarfHeaderParams(kLittleEndian, 4, 3, 4),
DwarfHeaderParams(kLittleEndian, 4, 3, 8),
DwarfHeaderParams(kLittleEndian, 4, 4, 4),
DwarfHeaderParams(kLittleEndian, 4, 4, 8),
DwarfHeaderParams(kLittleEndian, 8, 2, 4),
DwarfHeaderParams(kLittleEndian, 8, 2, 8),
DwarfHeaderParams(kLittleEndian, 8, 3, 4),
DwarfHeaderParams(kLittleEndian, 8, 3, 8),
DwarfHeaderParams(kLittleEndian, 8, 4, 4),
DwarfHeaderParams(kLittleEndian, 8, 4, 8),
DwarfHeaderParams(kBigEndian, 4, 2, 4),
DwarfHeaderParams(kBigEndian, 4, 2, 8),
DwarfHeaderParams(kBigEndian, 4, 3, 4),
DwarfHeaderParams(kBigEndian, 4, 3, 8),
DwarfHeaderParams(kBigEndian, 4, 4, 4),
DwarfHeaderParams(kBigEndian, 4, 4, 8),
DwarfHeaderParams(kBigEndian, 8, 2, 4),
DwarfHeaderParams(kBigEndian, 8, 2, 8),
DwarfHeaderParams(kBigEndian, 8, 3, 4),
DwarfHeaderParams(kBigEndian, 8, 3, 8),
DwarfHeaderParams(kBigEndian, 8, 4, 4),
DwarfHeaderParams(kBigEndian, 8, 4, 8)));
struct DwarfFormsFixture: public DIEFixture {
// Start a compilation unit, as directed by |params|, containing one
// childless DIE of the given tag, with one attribute of the given name
// and form. The 'info' fixture member is left just after the abbrev
// code, waiting for the attribute value to be appended.
void StartSingleAttributeDIE(const DwarfHeaderParams &params,
DwarfTag tag, DwarfAttribute name,
DwarfForm form) {
// Create the abbreviation table.
Label abbrev_table = abbrevs.Here();
abbrevs.Abbrev(1, tag, dwarf2reader::DW_children_no)
.Attribute(name, form)
.EndAbbrev()
.EndTable();
// Create the compilation unit, up to the attribute value.
info.set_format_size(params.format_size);
info.set_endianness(params.endianness);
info.Header(params.version, abbrev_table, params.address_size)
.ULEB128(1); // abbrev code
}
// Set up handler to expect a compilation unit matching |params|,
// containing one childless DIE of the given tag, in the sequence s. Stop
// just before the expectations.
void ExpectBeginCompilationUnit(const DwarfHeaderParams &params,
DwarfTag tag, uint64 offset=0) {
EXPECT_CALL(handler,
StartCompilationUnit(offset, params.address_size,
params.format_size, _,
params.version))
.InSequence(s)
.WillOnce(Return(true));
EXPECT_CALL(handler, StartDIE(_, tag, _))
.InSequence(s)
.WillOnce(Return(true));
}
void ExpectEndCompilationUnit() {
EXPECT_CALL(handler, EndDIE(_))
.InSequence(s)
.WillOnce(Return());
}
void ParseCompilationUnit(const DwarfHeaderParams &params, uint64 offset=0) {
ByteReader byte_reader(params.endianness == kLittleEndian ?
ENDIANNESS_LITTLE : ENDIANNESS_BIG);
CompilationUnit parser(MakeSectionMap(), offset, &byte_reader, &handler);
EXPECT_EQ(offset + parser.Start(), info_contents.size());
}
// The sequence to which the fixture's methods append expectations.
Sequence s;
};
struct DwarfForms: public DwarfFormsFixture,
public TestWithParam<DwarfHeaderParams> { };
TEST_P(DwarfForms, addr) {
StartSingleAttributeDIE(GetParam(), dwarf2reader::DW_TAG_compile_unit,
dwarf2reader::DW_AT_low_pc,
dwarf2reader::DW_FORM_addr);
u_int64_t value;
if (GetParam().address_size == 4) {
value = 0xc8e9ffcc;
info.D32(value);
} else {
value = 0xe942517fc2768564ULL;
info.D64(value);
}
info.Finish();
ExpectBeginCompilationUnit(GetParam(), dwarf2reader::DW_TAG_compile_unit);
EXPECT_CALL(handler, ProcessAttributeUnsigned(_, dwarf2reader::DW_AT_low_pc,
dwarf2reader::DW_FORM_addr,
value))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, block2_empty) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7,
(DwarfAttribute) 0xe52c4463,
dwarf2reader::DW_FORM_block2);
info.D16(0);
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7);
EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463,
dwarf2reader::DW_FORM_block2,
_, 0))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, block2) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7,
(DwarfAttribute) 0xe52c4463,
dwarf2reader::DW_FORM_block2);
unsigned char data[258];
memset(data, '*', sizeof(data));
info.D16(sizeof(data))
.Append(data, sizeof(data));
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7);
EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463,
dwarf2reader::DW_FORM_block2,
Pointee('*'), 258))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, flag_present) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x3e449ac2,
(DwarfAttribute) 0x359d1972,
dwarf2reader::DW_FORM_flag_present);
// DW_FORM_flag_present occupies no space in the DIE.
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x3e449ac2);
EXPECT_CALL(handler,
ProcessAttributeUnsigned(_, (DwarfAttribute) 0x359d1972,
dwarf2reader::DW_FORM_flag_present,
1))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, sec_offset) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x1d971689,
(DwarfAttribute) 0xa060bfd1,
dwarf2reader::DW_FORM_sec_offset);
u_int64_t value;
if (GetParam().format_size == 4) {
value = 0xacc9c388;
info.D32(value);
} else {
value = 0xcffe5696ffe3ed0aULL;
info.D64(value);
}
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x1d971689);
EXPECT_CALL(handler, ProcessAttributeUnsigned(_, (DwarfAttribute) 0xa060bfd1,
dwarf2reader::DW_FORM_sec_offset,
value))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, exprloc) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0xb6d167bb,
(DwarfAttribute) 0xba3ae5cb,
dwarf2reader::DW_FORM_exprloc);
info.ULEB128(29)
.Append(29, 173);
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0xb6d167bb);
EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xba3ae5cb,
dwarf2reader::DW_FORM_exprloc,
Pointee(173), 29))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, ref_sig8) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b,
(DwarfAttribute) 0xd708d908,
dwarf2reader::DW_FORM_ref_sig8);
info.D64(0xf72fa0cb6ddcf9d6ULL);
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b);
EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908,
dwarf2reader::DW_FORM_ref_sig8,
0xf72fa0cb6ddcf9d6ULL))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
// A value passed to ProcessAttributeSignature is just an absolute number,
// not an offset within the compilation unit as most of the other
// DW_FORM_ref forms are. Check that the reader doesn't try to apply any
// offset to the signature, by reading it from a compilation unit that does
// not start at the beginning of the section.
TEST_P(DwarfForms, ref_sig8_not_first) {
info.Append(98, '*');
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b,
(DwarfAttribute) 0xd708d908,
dwarf2reader::DW_FORM_ref_sig8);
info.D64(0xf72fa0cb6ddcf9d6ULL);
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b, 98);
EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908,
dwarf2reader::DW_FORM_ref_sig8,
0xf72fa0cb6ddcf9d6ULL))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam(), 98);
}
// Tests for the other attribute forms could go here.
INSTANTIATE_TEST_CASE_P(
HeaderVariants, DwarfForms,
::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4),
DwarfHeaderParams(kLittleEndian, 4, 2, 8),
DwarfHeaderParams(kLittleEndian, 4, 3, 4),
DwarfHeaderParams(kLittleEndian, 4, 3, 8),
DwarfHeaderParams(kLittleEndian, 4, 4, 4),
DwarfHeaderParams(kLittleEndian, 4, 4, 8),
DwarfHeaderParams(kLittleEndian, 8, 2, 4),
DwarfHeaderParams(kLittleEndian, 8, 2, 8),
DwarfHeaderParams(kLittleEndian, 8, 3, 4),
DwarfHeaderParams(kLittleEndian, 8, 3, 8),
DwarfHeaderParams(kLittleEndian, 8, 4, 4),
DwarfHeaderParams(kLittleEndian, 8, 4, 8),
DwarfHeaderParams(kBigEndian, 4, 2, 4),
DwarfHeaderParams(kBigEndian, 4, 2, 8),
DwarfHeaderParams(kBigEndian, 4, 3, 4),
DwarfHeaderParams(kBigEndian, 4, 3, 8),
DwarfHeaderParams(kBigEndian, 4, 4, 4),
DwarfHeaderParams(kBigEndian, 4, 4, 8),
DwarfHeaderParams(kBigEndian, 8, 2, 4),
DwarfHeaderParams(kBigEndian, 8, 2, 8),
DwarfHeaderParams(kBigEndian, 8, 3, 4),
DwarfHeaderParams(kBigEndian, 8, 3, 8),
DwarfHeaderParams(kBigEndian, 8, 4, 4),
DwarfHeaderParams(kBigEndian, 8, 4, 8)));

View File

@@ -0,0 +1,149 @@
// -*- mode: c++ -*-
// Copyright (c) 2012, 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_test_common.h: Define TestCompilationUnit and
// TestAbbrevTable, classes for creating properly (and improperly)
// formatted DWARF compilation unit data for unit tests.
#ifndef COMMON_DWARF_DWARF2READER_TEST_COMMON_H__
#define COMMON_DWARF_DWARF2READER_TEST_COMMON_H__
#include "common/test_assembler.h"
#include "common/dwarf/dwarf2enums.h"
// A subclass of test_assembler::Section, specialized for constructing
// DWARF compilation units.
class TestCompilationUnit: public google_breakpad::test_assembler::Section {
public:
typedef dwarf2reader::DwarfTag DwarfTag;
typedef dwarf2reader::DwarfAttribute DwarfAttribute;
typedef dwarf2reader::DwarfForm DwarfForm;
typedef google_breakpad::test_assembler::Label Label;
// Set the section's DWARF format size (the 32-bit DWARF format or the
// 64-bit DWARF format, for lengths and section offsets --- not the
// address size) to format_size.
void set_format_size(size_t format_size) {
assert(format_size == 4 || format_size == 8);
format_size_ = format_size;
}
// Append a DWARF section offset value, of the appropriate size for this
// compilation unit.
template<typename T>
void SectionOffset(T offset) {
if (format_size_ == 4)
D32(offset);
else
D64(offset);
}
// Append a DWARF compilation unit header to the section, with the given
// DWARF version, abbrev table offset, and address size.
TestCompilationUnit &Header(int version, const Label &abbrev_offset,
size_t address_size) {
if (format_size_ == 4) {
D32(length_);
} else {
D32(0xffffffff);
D64(length_);
}
post_length_offset_ = Size();
D16(version);
SectionOffset(abbrev_offset);
D8(address_size);
return *this;
}
// Mark the end of this header's DIEs.
TestCompilationUnit &Finish() {
length_ = Size() - post_length_offset_;
return *this;
}
private:
// The DWARF format size for this compilation unit.
size_t format_size_;
// The offset of the point in the compilation unit header immediately
// after the initial length field.
u_int64_t post_length_offset_;
// The length of the compilation unit, not including the initial length field.
Label length_;
};
// A subclass of test_assembler::Section specialized for constructing DWARF
// abbreviation tables.
class TestAbbrevTable: public google_breakpad::test_assembler::Section {
public:
typedef dwarf2reader::DwarfTag DwarfTag;
typedef dwarf2reader::DwarfAttribute DwarfAttribute;
typedef dwarf2reader::DwarfForm DwarfForm;
typedef dwarf2reader::DwarfHasChild DwarfHasChild;
typedef google_breakpad::test_assembler::Label Label;
// Start a new abbreviation table entry for abbreviation code |code|,
// encoding a DIE whose tag is |tag|, and which has children if and only
// if |has_children| is true.
TestAbbrevTable &Abbrev(int code, DwarfTag tag, DwarfHasChild has_children) {
assert(code != 0);
ULEB128(code);
ULEB128(static_cast<unsigned>(tag));
D8(static_cast<unsigned>(has_children));
return *this;
};
// Add an attribute to the current abbreviation code whose name is |name|
// and whose form is |form|.
TestAbbrevTable &Attribute(DwarfAttribute name, DwarfForm form) {
ULEB128(static_cast<unsigned>(name));
ULEB128(static_cast<unsigned>(form));
return *this;
}
// Finish the current abbreviation code.
TestAbbrevTable &EndAbbrev() {
ULEB128(0);
ULEB128(0);
return *this;
}
// Finish the current abbreviation table.
TestAbbrevTable &EndTable() {
ULEB128(0);
return *this;
}
};
#endif // COMMON_DWARF_DWARF2READER_TEST_COMMON_H__

View File

@@ -45,8 +45,8 @@
namespace dwarf2reader {
CULineInfoHandler::CULineInfoHandler(vector<SourceFileInfo>* files,
vector<string>* dirs,
CULineInfoHandler::CULineInfoHandler(std::vector<SourceFileInfo>* files,
std::vector<std::string>* dirs,
LineMap* linemap):linemap_(linemap),
files_(files),
dirs_(dirs) {
@@ -61,13 +61,13 @@ CULineInfoHandler::CULineInfoHandler(vector<SourceFileInfo>* files,
files->push_back(s);
}
void CULineInfoHandler::DefineDir(const string& name, uint32 dir_num) {
void CULineInfoHandler::DefineDir(const std::string& name, uint32 dir_num) {
// These should never come out of order, actually
assert(dir_num == dirs_->size());
dirs_->push_back(name);
}
void CULineInfoHandler::DefineFile(const string& name,
void CULineInfoHandler::DefineFile(const std::string& name,
int32 file_num, uint32 dir_num,
uint64 mod_time, uint64 length) {
assert(dir_num >= 0);
@@ -75,7 +75,7 @@ void CULineInfoHandler::DefineFile(const string& name,
// These should never come out of order, actually.
if (file_num == (int32)files_->size() || file_num == -1) {
string dir = dirs_->at(dir_num);
std::string dir = dirs_->at(dir_num);
SourceFileInfo s;
s.lowpc = ULLONG_MAX;
@@ -95,8 +95,10 @@ void CULineInfoHandler::DefineFile(const string& name,
void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num,
uint32 line_num, uint32 column_num) {
if (file_num < files_->size()) {
linemap_->insert(make_pair(address, make_pair(files_->at(file_num).name.c_str(),
line_num)));
linemap_->insert(
std::make_pair(address,
std::make_pair(files_->at(file_num).name.c_str(),
line_num)));
if(address < files_->at(file_num).lowpc) {
files_->at(file_num).lowpc = address;
@@ -130,7 +132,8 @@ bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag,
current_function_info_->name = "";
current_function_info_->line = 0;
current_function_info_->file = "";
offset_to_funcinfo_->insert(make_pair(offset, current_function_info_));
offset_to_funcinfo_->insert(std::make_pair(offset,
current_function_info_));
};
// FALLTHROUGH
case DW_TAG_compile_unit:
@@ -146,7 +149,7 @@ bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag,
void CUFunctionInfoHandler::ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string &data) {
const std::string &data) {
if (current_function_info_) {
if (attr == DW_AT_name)
current_function_info_->name = data;
@@ -164,9 +167,9 @@ void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset,
assert(iter != sections_.end());
// this should be a scoped_ptr but we dont' use boost :-(
auto_ptr<LineInfo> lireader(new LineInfo(iter->second.first + data,
iter->second.second - data,
reader_, linehandler_));
std::auto_ptr<LineInfo> lireader(new LineInfo(iter->second.first + data,
iter->second.second - data,
reader_, linehandler_));
lireader->Start();
} else if (current_function_info_) {
switch (attr) {
@@ -219,8 +222,8 @@ void CUFunctionInfoHandler::ProcessAttributeReference(uint64 offset,
void CUFunctionInfoHandler::EndDIE(uint64 offset) {
if (current_function_info_ && current_function_info_->lowpc)
address_to_funcinfo_->insert(make_pair(current_function_info_->lowpc,
current_function_info_));
address_to_funcinfo_->insert(std::make_pair(current_function_info_->lowpc,
current_function_info_));
}
} // namespace dwarf2reader

View File

@@ -46,11 +46,11 @@ namespace dwarf2reader {
struct FunctionInfo {
// Name of the function
string name;
std::string name;
// Mangled name of the function
string mangled_name;
std::string mangled_name;
// File containing this function
string file;
std::string file;
// Line number for start of function.
uint32 line;
// Beginning address for this function
@@ -61,13 +61,13 @@ struct FunctionInfo {
struct SourceFileInfo {
// Name of the source file name
string name;
std::string name;
// Low address of source file name
uint64 lowpc;
};
typedef map<uint64, FunctionInfo*> FunctionMap;
typedef map<uint64, pair<string, uint32> > LineMap;
typedef std::map<uint64, FunctionInfo*> FunctionMap;
typedef std::map<uint64, std::pair<std::string, uint32> > LineMap;
// This class is a basic line info handler that fills in the dirs,
// file, and linemap passed into it with the data produced from the
@@ -76,18 +76,18 @@ class CULineInfoHandler: public LineInfoHandler {
public:
//
CULineInfoHandler(vector<SourceFileInfo>* files,
vector<string>* dirs,
CULineInfoHandler(std::vector<SourceFileInfo>* files,
std::vector<std::string>* dirs,
LineMap* linemap);
virtual ~CULineInfoHandler() { }
// Called when we define a directory. We just place NAME into dirs_
// at position DIR_NUM.
virtual void DefineDir(const string& name, uint32 dir_num);
virtual void DefineDir(const std::string& name, uint32 dir_num);
// Called when we define a filename. We just place
// concat(dirs_[DIR_NUM], NAME) into files_ at position FILE_NUM.
virtual void DefineFile(const string& name, int32 file_num,
virtual void DefineFile(const std::string& name, int32 file_num,
uint32 dir_num, uint64 mod_time, uint64 length);
@@ -102,14 +102,14 @@ class CULineInfoHandler: public LineInfoHandler {
private:
LineMap* linemap_;
vector<SourceFileInfo>* files_;
vector<string>* dirs_;
std::vector<SourceFileInfo>* files_;
std::vector<std::string>* dirs_;
};
class CUFunctionInfoHandler: public Dwarf2Handler {
public:
CUFunctionInfoHandler(vector<SourceFileInfo>* files,
vector<string>* dirs,
CUFunctionInfoHandler(std::vector<SourceFileInfo>* files,
std::vector<std::string>* dirs,
LineMap* linemap,
FunctionMap* offset_to_funcinfo,
FunctionMap* address_to_funcinfo,
@@ -163,7 +163,7 @@ class CUFunctionInfoHandler: public Dwarf2Handler {
virtual void ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string& data);
const std::string& data);
// Called when finished processing the DIE at OFFSET.
// Because DWARF2/3 specifies a tree of DIEs, you may get starts
@@ -172,8 +172,8 @@ class CUFunctionInfoHandler: public Dwarf2Handler {
virtual void EndDIE(uint64 offset);
private:
vector<SourceFileInfo>* files_;
vector<string>* dirs_;
std::vector<SourceFileInfo>* files_;
std::vector<std::string>* dirs_;
LineMap* linemap_;
FunctionMap* offset_to_funcinfo_;
FunctionMap* address_to_funcinfo_;

View File

@@ -33,6 +33,8 @@
#ifndef _COMMON_DWARF_TYPES_H__
#define _COMMON_DWARF_TYPES_H__
#include <stdint.h>
typedef signed char int8;
typedef short int16;
typedef int int32;

View File

@@ -132,7 +132,7 @@ string DwarfCFIToModule::RegisterName(int i) {
if (reg == return_address_)
return ra_name_;
if (0 <= reg && reg < register_names_.size())
if (reg < register_names_.size())
return register_names_[reg];
reporter_->UnnamedRegister(entry_offset_, reg);

View File

@@ -31,9 +31,15 @@
// dwarf_cfi_to_module_unittest.cc: Tests for google_breakpad::DwarfCFIToModule.
#include <string>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "common/dwarf_cfi_to_module.h"
using std::string;
using std::vector;
using google_breakpad::Module;
using google_breakpad::DwarfCFIToModule;
using testing::ContainerEq;

View File

@@ -440,7 +440,11 @@ void DwarfCUToModule::FuncHandler::Finish() {
func->address = low_pc_;
func->size = high_pc_ - low_pc_;
func->parameter_size = 0;
cu_context_->functions.push_back(func);
if (func->address) {
// If the function address is zero this is a sign that this function
// description is just empty debug data and should just be discarded.
cu_context_->functions.push_back(func);
}
} else if (inline_) {
AbstractOrigin origin(name_);
cu_context_->file_context->file_private->origins[offset_] = origin;
@@ -553,7 +557,7 @@ void DwarfCUToModule::WarningReporter::UncoveredLine(const Module::Line &line) {
void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) {
CUHeading();
fprintf(stderr, "%s: warning: function at offset 0x%" PRIx64 " has no name\n",
fprintf(stderr, "%s: warning: function at offset 0x%llx has no name\n",
filename_.c_str(), offset);
}

View File

@@ -58,9 +58,10 @@ using dwarf2reader::DwarfTag;
// Populate a google_breakpad::Module with DWARF debugging information.
//
// An instance of this class can be provided as a handler to a
// dwarf2reader::CompilationUnit DWARF parser. The handler uses the
// results of parsing to populate a google_breakpad::Module with
// source file, function, and source line information.
// dwarf2reader::DIEDispatcher, which can in turn be a handler for a
// dwarf2reader::CompilationUnit DWARF parser. The handler uses the results
// of parsing to populate a google_breakpad::Module with source file,
// function, and source line information.
class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
struct FilePrivate;
public:

View File

@@ -31,11 +31,15 @@
// dwarf_cu_to_module.cc: Unit tests for google_breakpad::DwarfCUToModule.
#include <string>
#include <utility>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "common/dwarf_cu_to_module.h"
using std::make_pair;
using std::string;
using std::vector;
using dwarf2reader::AttributeList;
@@ -144,8 +148,8 @@ class CUFixtureBase {
// The handler will consult this section map to decide what to
// pass to our line reader.
file_context_.section_map[".debug_line"] = std::make_pair(dummy_line_program_,
dummy_line_size_);
file_context_.section_map[".debug_line"] = make_pair(dummy_line_program_,
dummy_line_size_);
}
// Add a line with the given address, size, filename, and line
@@ -158,7 +162,7 @@ class CUFixtureBase {
// Use LANGUAGE for the compilation unit. More precisely, arrange
// for StartCU to pass the compilation unit's root DIE a
// DW_AT_language attribute whose value is LANGUAGE.
void SetLanguage(dwarf2reader::DwarfLanguage language) {
void SetLanguage(dwarf2reader::DwarfLanguage language) {
language_ = language;
}
@@ -192,7 +196,7 @@ class CUFixtureBase {
// not Finish. If NAME is non-zero, use it as the DW_AT_name
// attribute.
DIEHandler *StartSpecifiedDIE(DIEHandler *parent, DwarfTag tag,
uint64 offset, const char *name = NULL);
uint64 specification, const char *name = NULL);
// Define a function as a child of PARENT with the given name,
// address, and size. Call EndAttributes and Finish; one cannot

View File

@@ -41,13 +41,14 @@
// it until we actually have to deal with DWARF on Windows.
// Return true if PATH is an absolute path, false if it is relative.
static bool PathIsAbsolute(const string &path) {
static bool PathIsAbsolute(const std::string &path) {
return (path.size() >= 1 && path[0] == '/');
}
// If PATH is an absolute path, return PATH. If PATH is a relative path,
// treat it as relative to BASE and return the combined path.
static string ExpandPath(const string &path, const string &base) {
static std::string ExpandPath(const std::string &path,
const std::string &base) {
if (PathIsAbsolute(path))
return path;
return base + "/" + path;
@@ -55,14 +56,14 @@ static string ExpandPath(const string &path, const string &base) {
namespace google_breakpad {
void DwarfLineToModule::DefineDir(const string &name, uint32 dir_num) {
void DwarfLineToModule::DefineDir(const std::string &name, uint32 dir_num) {
// Directory number zero is reserved to mean the compilation
// directory. Silently ignore attempts to redefine it.
if (dir_num != 0)
directories_[dir_num] = name;
}
void DwarfLineToModule::DefineFile(const string &name, int32 file_num,
void DwarfLineToModule::DefineFile(const std::string &name, int32 file_num,
uint32 dir_num, uint64 mod_time,
uint64 length) {
if (file_num == -1)

View File

@@ -31,9 +31,13 @@
// dwarf_line_to_module.cc: Unit tests for google_breakpad::DwarfLineToModule.
#include <vector>
#include "breakpad_googletest_includes.h"
#include "common/dwarf_line_to_module.h"
using std::vector;
using google_breakpad::DwarfLineToModule;
using google_breakpad::Module;
using google_breakpad::Module;
@@ -44,7 +48,7 @@ TEST(SimpleModule, One) {
DwarfLineToModule h(&m, &lines);
h.DefineFile("file1", 0x30bf0f27, 0, 0, 0);
h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27,
h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27,
0x4c090cbf, 0x1cf9fe0d);
vector<Module::File *> files;

View File

@@ -49,7 +49,7 @@ class CPPLanguage: public Language {
}
};
const CPPLanguage CPPLanguageSingleton;
CPPLanguage CPPLanguageSingleton;
// Java language-specific operations.
class JavaLanguage: public Language {

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2010 Google Inc.
// Copyright (c) 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -226,7 +226,7 @@ class DumperLineToModule: public DwarfCUToModule::LineToModuleFunctor {
explicit DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
: byte_reader_(byte_reader) { }
void operator()(const char *program, uint64 length,
Module *module, vector<Module::Line> *lines) {
Module *module, std::vector<Module::Line> *lines) {
DwarfLineToModule handler(module, lines);
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
parser.Start();
@@ -235,7 +235,7 @@ class DumperLineToModule: public DwarfCUToModule::LineToModuleFunctor {
dwarf2reader::ByteReader *byte_reader_;
};
static bool LoadDwarf(const string &dwarf_filename,
static bool LoadDwarf(const std::string &dwarf_filename,
const ElfW(Ehdr) *elf_header,
const bool big_endian,
Module *module) {
@@ -253,8 +253,8 @@ static bool LoadDwarf(const string &dwarf_filename,
const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx;
for (int i = 0; i < num_sections; i++) {
const ElfW(Shdr) *section = &sections[i];
string name = reinterpret_cast<const char *>(section_names->sh_offset
+ section->sh_name);
std::string name = reinterpret_cast<const char *>(section_names->sh_offset +
section->sh_name);
const char *contents = reinterpret_cast<const char *>(section->sh_offset);
uint64 length = section->sh_size;
file_context.section_map[name] = std::make_pair(contents, length);
@@ -292,7 +292,7 @@ static bool LoadDwarf(const string &dwarf_filename,
// success, or false if we don't recognize HEADER's machine
// architecture.
static bool DwarfCFIRegisterNames(const ElfW(Ehdr) *elf_header,
vector<string> *register_names) {
std::vector<std::string> *register_names) {
switch (elf_header->e_machine) {
case EM_386:
*register_names = DwarfCFIToModule::RegisterNames::I386();
@@ -308,7 +308,7 @@ static bool DwarfCFIRegisterNames(const ElfW(Ehdr) *elf_header,
}
}
static bool LoadDwarfCFI(const string &dwarf_filename,
static bool LoadDwarfCFI(const std::string &dwarf_filename,
const ElfW(Ehdr) *elf_header,
const char *section_name,
const ElfW(Shdr) *section,
@@ -319,7 +319,7 @@ static bool LoadDwarfCFI(const string &dwarf_filename,
Module *module) {
// Find the appropriate set of register names for this file's
// architecture.
vector<string> register_names;
std::vector<std::string> register_names;
if (!DwarfCFIRegisterNames(elf_header, &register_names)) {
fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';"
" cannot convert DWARF call frame information\n",
@@ -728,6 +728,7 @@ namespace google_breakpad {
bool WriteSymbolFileInternal(uint8_t* obj_file,
const std::string &obj_filename,
const std::string &debug_dir,
bool cfi,
std::ostream &sym_stream) {
ElfW(Ehdr) *elf_header = reinterpret_cast<ElfW(Ehdr) *>(obj_file);
@@ -803,7 +804,7 @@ bool WriteSymbolFileInternal(uint8_t* obj_file,
return false;
}
}
if (!module.Write(sym_stream))
if (!module.Write(sym_stream, cfi))
return false;
return true;
@@ -811,6 +812,7 @@ bool WriteSymbolFileInternal(uint8_t* obj_file,
bool WriteSymbolFile(const std::string &obj_file,
const std::string &debug_dir,
bool cfi,
std::ostream &sym_stream) {
MmapWrapper map_wrapper;
ElfW(Ehdr) *elf_header = NULL;
@@ -818,7 +820,7 @@ bool WriteSymbolFile(const std::string &obj_file,
return false;
return WriteSymbolFileInternal(reinterpret_cast<uint8_t*>(elf_header),
obj_file, debug_dir, sym_stream);
obj_file, debug_dir, cfi, sym_stream);
}
} // namespace google_breakpad

View File

@@ -1,6 +1,6 @@
// -*- mode: c++ -*-
// Copyright (c) 2010, Google Inc.
// Copyright (c) 2011, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -45,8 +45,10 @@ namespace google_breakpad {
// file format.
// If OBJ_FILE has been stripped but contains a .gnu_debuglink section,
// then look for the debug file in DEBUG_DIR.
// If CFI is set to false, then omit the CFI section.
bool WriteSymbolFile(const std::string &obj_file,
const std::string &debug_dir,
bool cfi,
std::ostream &sym_stream);
} // namespace google_breakpad

View File

@@ -47,6 +47,7 @@ namespace google_breakpad {
bool WriteSymbolFileInternal(uint8_t* obj_file,
const std::string &obj_filename,
const std::string &debug_dir,
bool cfi,
std::ostream &sym_stream);
}
@@ -62,7 +63,7 @@ using std::vector;
using ::testing::Test;
class DumpSymbols : public Test {
public:
public:
void GetElfContents(ELF& elf) {
string contents;
ASSERT_TRUE(elf.GetContents(&contents));
@@ -84,6 +85,7 @@ TEST_F(DumpSymbols, Invalid) {
EXPECT_FALSE(WriteSymbolFileInternal(reinterpret_cast<uint8_t*>(&header),
"foo",
"",
true,
s));
}
@@ -105,11 +107,11 @@ TEST_F(DumpSymbols, SimplePublic32) {
SHN_UNDEF + 1);
int index = elf.AddSection(".dynstr", table, SHT_STRTAB);
elf.AddSection(".dynsym", syms,
SHT_DYNSYM, // type
SHF_ALLOC, // flags
0, // addr
index, // link
sizeof(Elf32_Sym)); // entsize
SHT_DYNSYM, // type
SHF_ALLOC, // flags
0, // addr
index, // link
sizeof(Elf32_Sym)); // entsize
elf.Finish();
GetElfContents(elf);
@@ -118,6 +120,7 @@ TEST_F(DumpSymbols, SimplePublic32) {
ASSERT_TRUE(WriteSymbolFileInternal(elfdata,
"foo",
"",
true,
s));
EXPECT_EQ("MODULE Linux x86 000000000000000000000000000000000 foo\n"
"PUBLIC 1000 0 superfunc\n",
@@ -141,11 +144,11 @@ TEST_F(DumpSymbols, SimplePublic64) {
SHN_UNDEF + 1);
int index = elf.AddSection(".dynstr", table, SHT_STRTAB);
elf.AddSection(".dynsym", syms,
SHT_DYNSYM, // type
SHF_ALLOC, // flags
0, // addr
index, // link
sizeof(Elf64_Sym)); // entsize
SHT_DYNSYM, // type
SHF_ALLOC, // flags
0, // addr
index, // link
sizeof(Elf64_Sym)); // entsize
elf.Finish();
GetElfContents(elf);
@@ -154,6 +157,7 @@ TEST_F(DumpSymbols, SimplePublic64) {
ASSERT_TRUE(WriteSymbolFileInternal(elfdata,
"foo",
"",
true,
s));
EXPECT_EQ("MODULE Linux x86_64 000000000000000000000000000000000 foo\n"
"PUBLIC 1000 0 superfunc\n",

View File

@@ -0,0 +1,179 @@
// Copyright (c) 2011, 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.
// elf_core_dump.cc: Implement google_breakpad::ElfCoreDump.
// See elf_core_dump.h for details.
#include "common/linux/elf_core_dump.h"
#include <stddef.h>
#include <string.h>
namespace google_breakpad {
// Implementation of ElfCoreDump::Note.
ElfCoreDump::Note::Note() {}
ElfCoreDump::Note::Note(const MemoryRange& content) : content_(content) {}
bool ElfCoreDump::Note::IsValid() const {
return GetHeader() != NULL;
}
const ElfCoreDump::Nhdr* ElfCoreDump::Note::GetHeader() const {
return content_.GetData<Nhdr>(0);
}
ElfCoreDump::Word ElfCoreDump::Note::GetType() const {
const Nhdr* header = GetHeader();
// 0 is not being used as a NOTE type.
return header ? header->n_type : 0;
}
MemoryRange ElfCoreDump::Note::GetName() const {
const Nhdr* header = GetHeader();
if (header) {
return content_.Subrange(sizeof(Nhdr), header->n_namesz);
}
return MemoryRange();
}
MemoryRange ElfCoreDump::Note::GetDescription() const {
const Nhdr* header = GetHeader();
if (header) {
return content_.Subrange(AlignedSize(sizeof(Nhdr) + header->n_namesz),
header->n_descsz);
}
return MemoryRange();
}
ElfCoreDump::Note ElfCoreDump::Note::GetNextNote() const {
MemoryRange next_content;
const Nhdr* header = GetHeader();
if (header) {
size_t next_offset = AlignedSize(sizeof(Nhdr) + header->n_namesz);
next_offset = AlignedSize(next_offset + header->n_descsz);
next_content =
content_.Subrange(next_offset, content_.length() - next_offset);
}
return Note(next_content);
}
// static
size_t ElfCoreDump::Note::AlignedSize(size_t size) {
size_t mask = sizeof(Word) - 1;
return (size + mask) & ~mask;
}
// Implementation of ElfCoreDump.
ElfCoreDump::ElfCoreDump() {}
ElfCoreDump::ElfCoreDump(const MemoryRange& content)
: content_(content) {
}
void ElfCoreDump::SetContent(const MemoryRange& content) {
content_ = content;
}
bool ElfCoreDump::IsValid() const {
const Ehdr* header = GetHeader();
return (header &&
header->e_ident[0] == ELFMAG0 &&
header->e_ident[1] == ELFMAG1 &&
header->e_ident[2] == ELFMAG2 &&
header->e_ident[3] == ELFMAG3 &&
header->e_ident[4] == kClass &&
header->e_version == EV_CURRENT &&
header->e_type == ET_CORE);
}
const ElfCoreDump::Ehdr* ElfCoreDump::GetHeader() const {
return content_.GetData<Ehdr>(0);
}
const ElfCoreDump::Phdr* ElfCoreDump::GetProgramHeader(unsigned index) const {
const Ehdr* header = GetHeader();
if (header) {
return reinterpret_cast<const Phdr*>(content_.GetArrayElement(
header->e_phoff, header->e_phentsize, index));
}
return NULL;
}
const ElfCoreDump::Phdr* ElfCoreDump::GetFirstProgramHeaderOfType(
Word type) const {
for (unsigned i = 0, n = GetProgramHeaderCount(); i < n; ++i) {
const Phdr* program = GetProgramHeader(i);
if (program->p_type == type) {
return program;
}
}
return NULL;
}
unsigned ElfCoreDump::GetProgramHeaderCount() const {
const Ehdr* header = GetHeader();
return header ? header->e_phnum : 0;
}
bool ElfCoreDump::CopyData(void* buffer, Addr virtual_address, size_t length) {
for (unsigned i = 0, n = GetProgramHeaderCount(); i < n; ++i) {
const Phdr* program = GetProgramHeader(i);
if (program->p_type != PT_LOAD)
continue;
size_t offset_in_segment = virtual_address - program->p_vaddr;
if (virtual_address >= program->p_vaddr &&
offset_in_segment < program->p_filesz) {
const void* data =
content_.GetData(program->p_offset + offset_in_segment, length);
if (data) {
memcpy(buffer, data, length);
return true;
}
}
}
return false;
}
ElfCoreDump::Note ElfCoreDump::GetFirstNote() const {
MemoryRange note_content;
const Phdr* program_header = GetFirstProgramHeaderOfType(PT_NOTE);
if (program_header) {
note_content = content_.Subrange(program_header->p_offset,
program_header->p_filesz);
}
return Note(note_content);
}
} // namespace google_breakpad

View File

@@ -0,0 +1,153 @@
// Copyright (c) 2011, 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.
// elf_core_dump.h: Define the google_breakpad::ElfCoreDump class, which
// encapsulates an ELF core dump file mapped into memory.
#ifndef COMMON_LINUX_ELF_CORE_DUMP_H_
#define COMMON_LINUX_ELF_CORE_DUMP_H_
#include <elf.h>
#if !defined(__ANDROID__)
#include <link.h>
#endif
#include <stddef.h>
#include "common/memory_range.h"
#if defined(__ANDROID__)
#include "common/linux/android_link.h"
#endif
namespace google_breakpad {
// A class encapsulating an ELF core dump file mapped into memory, which
// provides methods for accessing program headers and the note section.
class ElfCoreDump {
public:
// ELF types based on the value of __WORDSIZE.
typedef ElfW(Ehdr) Ehdr;
typedef ElfW(Nhdr) Nhdr;
typedef ElfW(Phdr) Phdr;
typedef ElfW(Word) Word;
typedef ElfW(Addr) Addr;
#if __WORDSIZE == 32
static const int kClass = ELFCLASS32;
#elif __WORDSIZE == 64
static const int kClass = ELFCLASS64;
#else
#error "Unsupported __WORDSIZE for ElfCoreDump."
#endif
// A class encapsulating the note content in a core dump, which provides
// methods for accessing the name and description of a note.
class Note {
public:
Note();
// Constructor that takes the note content from |content|.
explicit Note(const MemoryRange& content);
// Returns true if this note is valid, i,e. a note header is found in
// |content_|, or false otherwise.
bool IsValid() const;
// Returns the note header, or NULL if no note header is found in
// |content_|.
const Nhdr* GetHeader() const;
// Returns the note type, or 0 if no note header is found in |content_|.
Word GetType() const;
// Returns a memory range covering the note name, or an empty range
// if no valid note name is found in |content_|.
MemoryRange GetName() const;
// Returns a memory range covering the note description, or an empty
// range if no valid note description is found in |content_|.
MemoryRange GetDescription() const;
// Returns the note following this note, or an empty note if no valid
// note is found after this note.
Note GetNextNote() const;
private:
// Returns the size in bytes round up to the word alignment, specified
// for the note section, of a given size in bytes.
static size_t AlignedSize(size_t size);
// Note content.
MemoryRange content_;
};
ElfCoreDump();
// Constructor that takes the core dump content from |content|.
explicit ElfCoreDump(const MemoryRange& content);
// Sets the core dump content to |content|.
void SetContent(const MemoryRange& content);
// Returns true if a valid ELF header in the core dump, or false otherwise.
bool IsValid() const;
// Returns the ELF header in the core dump, or NULL if no ELF header
// is found in |content_|.
const Ehdr* GetHeader() const;
// Returns the |index|-th program header in the core dump, or NULL if no
// ELF header is found in |content_| or |index| is out of bounds.
const Phdr* GetProgramHeader(unsigned index) const;
// Returns the first program header of |type| in the core dump, or NULL if
// no ELF header is found in |content_| or no program header of |type| is
// found.
const Phdr* GetFirstProgramHeaderOfType(Word type) const;
// Returns the number of program headers in the core dump, or 0 if no
// ELF header is found in |content_|.
unsigned GetProgramHeaderCount() const;
// Copies |length| bytes of data starting at |virtual_address| in the core
// dump to |buffer|. |buffer| should be a valid pointer to a buffer of at
// least |length| bytes. Returns true if the data to be copied is found in
// the core dump, or false otherwise.
bool CopyData(void* buffer, Addr virtual_address, size_t length);
// Returns the first note found in the note section of the core dump, or
// an empty note if no note is found.
Note GetFirstNote() const;
private:
// Core dump content.
MemoryRange content_;
};
} // namespace google_breakpad
#endif // COMMON_LINUX_ELF_CORE_DUMP_H_

View File

@@ -0,0 +1,245 @@
// Copyright (c) 2011, 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.
// elf_core_dump_unittest.cc: Unit tests for google_breakpad::ElfCoreDump.
#include <sys/procfs.h>
#include <set>
#include <string>
#include "breakpad_googletest_includes.h"
#include "common/linux/elf_core_dump.h"
#include "common/linux/memory_mapped_file.h"
#include "common/tests/file_utils.h"
#include "common/linux/tests/crash_generator.h"
using google_breakpad::AutoTempDir;
using google_breakpad::CrashGenerator;
using google_breakpad::ElfCoreDump;
using google_breakpad::MemoryMappedFile;
using google_breakpad::MemoryRange;
using google_breakpad::WriteFile;
using std::set;
using std::string;
TEST(ElfCoreDumpTest, DefaultConstructor) {
ElfCoreDump core;
EXPECT_FALSE(core.IsValid());
EXPECT_EQ(NULL, core.GetHeader());
EXPECT_EQ(0, core.GetProgramHeaderCount());
EXPECT_EQ(NULL, core.GetProgramHeader(0));
EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD));
EXPECT_FALSE(core.GetFirstNote().IsValid());
}
TEST(ElfCoreDumpTest, TestElfHeader) {
ElfCoreDump::Ehdr header;
memset(&header, 0, sizeof(header));
AutoTempDir temp_dir;
string core_path = temp_dir.path() + "/core";
const char* core_file = core_path.c_str();
MemoryMappedFile mapped_core_file;
ElfCoreDump core;
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header) - 1));
ASSERT_TRUE(mapped_core_file.Map(core_file));
core.SetContent(mapped_core_file.content());
EXPECT_FALSE(core.IsValid());
EXPECT_EQ(NULL, core.GetHeader());
EXPECT_EQ(0, core.GetProgramHeaderCount());
EXPECT_EQ(NULL, core.GetProgramHeader(0));
EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD));
EXPECT_FALSE(core.GetFirstNote().IsValid());
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
ASSERT_TRUE(mapped_core_file.Map(core_file));
core.SetContent(mapped_core_file.content());
EXPECT_FALSE(core.IsValid());
header.e_ident[0] = ELFMAG0;
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
ASSERT_TRUE(mapped_core_file.Map(core_file));
core.SetContent(mapped_core_file.content());
EXPECT_FALSE(core.IsValid());
header.e_ident[1] = ELFMAG1;
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
ASSERT_TRUE(mapped_core_file.Map(core_file));
core.SetContent(mapped_core_file.content());
EXPECT_FALSE(core.IsValid());
header.e_ident[2] = ELFMAG2;
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
ASSERT_TRUE(mapped_core_file.Map(core_file));
core.SetContent(mapped_core_file.content());
EXPECT_FALSE(core.IsValid());
header.e_ident[3] = ELFMAG3;
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
ASSERT_TRUE(mapped_core_file.Map(core_file));
core.SetContent(mapped_core_file.content());
EXPECT_FALSE(core.IsValid());
header.e_ident[4] = ElfCoreDump::kClass;
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
ASSERT_TRUE(mapped_core_file.Map(core_file));
core.SetContent(mapped_core_file.content());
EXPECT_FALSE(core.IsValid());
header.e_version = EV_CURRENT;
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
ASSERT_TRUE(mapped_core_file.Map(core_file));
core.SetContent(mapped_core_file.content());
EXPECT_FALSE(core.IsValid());
header.e_type = ET_CORE;
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
ASSERT_TRUE(mapped_core_file.Map(core_file));
core.SetContent(mapped_core_file.content());
EXPECT_TRUE(core.IsValid());
}
TEST(ElfCoreDumpTest, ValidCoreFile) {
CrashGenerator crash_generator;
if (!crash_generator.HasDefaultCorePattern()) {
fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped "
"due to non-default core pattern");
return;
}
const unsigned kNumOfThreads = 3;
const unsigned kCrashThread = 1;
const int kCrashSignal = SIGABRT;
// TODO(benchan): Revert to use ASSERT_TRUE once the flakiness in
// CrashGenerator is identified and fixed.
if (!crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
kCrashSignal, NULL)) {
fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped "
"due to no core dump generated");
return;
}
pid_t expected_crash_thread_id = crash_generator.GetThreadId(kCrashThread);
set<pid_t> expected_thread_ids;
for (unsigned i = 0; i < kNumOfThreads; ++i) {
expected_thread_ids.insert(crash_generator.GetThreadId(i));
}
MemoryMappedFile mapped_core_file;
ASSERT_TRUE(mapped_core_file.Map(crash_generator.GetCoreFilePath().c_str()));
ElfCoreDump core;
core.SetContent(mapped_core_file.content());
EXPECT_TRUE(core.IsValid());
// Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are
// ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific):
// Thread Name Type
// -------------------------------------------------------------------
// 1st thread CORE NT_PRSTATUS
// process-wide CORE NT_PRPSINFO
// process-wide CORE NT_AUXV
// 1st thread CORE NT_FPREGSET
// 1st thread LINUX NT_PRXFPREG
// 1st thread LINUX NT_386_TLS
//
// 2nd thread CORE NT_PRSTATUS
// 2nd thread CORE NT_FPREGSET
// 2nd thread LINUX NT_PRXFPREG
// 2nd thread LINUX NT_386_TLS
//
// 3rd thread CORE NT_PRSTATUS
// 3rd thread CORE NT_FPREGSET
// 3rd thread LINUX NT_PRXFPREG
// 3rd thread LINUX NT_386_TLS
size_t num_nt_prpsinfo = 0;
size_t num_nt_prstatus = 0;
size_t num_nt_fpregset = 0;
size_t num_nt_prxfpreg = 0;
set<pid_t> actual_thread_ids;
ElfCoreDump::Note note = core.GetFirstNote();
while (note.IsValid()) {
MemoryRange name = note.GetName();
MemoryRange description = note.GetDescription();
EXPECT_FALSE(name.IsEmpty());
EXPECT_FALSE(description.IsEmpty());
switch (note.GetType()) {
case NT_PRPSINFO: {
EXPECT_TRUE(description.data() != NULL);
EXPECT_EQ(sizeof(elf_prpsinfo), description.length());
++num_nt_prpsinfo;
break;
}
case NT_PRSTATUS: {
EXPECT_TRUE(description.data() != NULL);
EXPECT_EQ(sizeof(elf_prstatus), description.length());
const elf_prstatus* status = description.GetData<elf_prstatus>(0);
actual_thread_ids.insert(status->pr_pid);
if (num_nt_prstatus == 0) {
EXPECT_EQ(expected_crash_thread_id, status->pr_pid);
EXPECT_EQ(kCrashSignal, status->pr_info.si_signo);
}
++num_nt_prstatus;
break;
}
#if defined(__i386) || defined(__x86_64)
case NT_FPREGSET: {
EXPECT_TRUE(description.data() != NULL);
EXPECT_EQ(sizeof(user_fpregs_struct), description.length());
++num_nt_fpregset;
break;
}
#endif
#if defined(__i386)
case NT_PRXFPREG: {
EXPECT_TRUE(description.data() != NULL);
EXPECT_EQ(sizeof(user_fpxregs_struct), description.length());
++num_nt_prxfpreg;
break;
}
#endif
default:
break;
}
note = note.GetNextNote();
}
EXPECT_TRUE(expected_thread_ids == actual_thread_ids);
EXPECT_EQ(1, num_nt_prpsinfo);
EXPECT_EQ(kNumOfThreads, num_nt_prstatus);
#if defined(__i386) || defined(__x86_64)
EXPECT_EQ(kNumOfThreads, num_nt_fpregset);
#endif
#if defined(__i386)
EXPECT_EQ(kNumOfThreads, num_nt_prxfpreg);
#endif
}

View File

@@ -36,22 +36,19 @@
#include <arpa/inet.h>
#include <assert.h>
#include <elf.h>
#include <fcntl.h>
#if defined(__ANDROID__)
#include <linux/elf.h>
#include "client/linux/android_link.h"
#else
#include <elf.h>
#include <link.h>
#endif
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <algorithm>
#include "common/linux/linux_libc_support.h"
#include "common/linux/memory_mapped_file.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
@@ -107,10 +104,10 @@ static void FindElfClassSection(const char *elf_base,
const Shdr* section = NULL;
for (int i = 0; i < elf_header->e_shnum; ++i) {
if (sections[i].sh_type == section_type) {
const char* section_name = (char*)(elf_base +
string_section->sh_offset +
sections[i].sh_name);
if (!my_strncmp(section_name, section_name, name_len)) {
const char* current_section_name = (char*)(elf_base +
string_section->sh_offset +
sections[i].sh_name);
if (!my_strncmp(current_section_name, section_name, name_len)) {
section = &sections[i];
break;
}
@@ -166,8 +163,7 @@ static bool FindElfSection(const void *elf_mapped_base,
template<typename ElfClass>
static bool ElfClassBuildIDNoteIdentifier(const void *section,
uint8_t identifier[kMDGUIDSize])
{
uint8_t identifier[kMDGUIDSize]) {
typedef typename ElfClass::Nhdr Nhdr;
const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
@@ -190,8 +186,7 @@ static bool ElfClassBuildIDNoteIdentifier(const void *section,
// Attempt to locate a .note.gnu.build-id section in an ELF binary
// and copy as many bytes of it as will fit into |identifier|.
static bool FindElfBuildIDNote(const void *elf_mapped_base,
uint8_t identifier[kMDGUIDSize])
{
uint8_t identifier[kMDGUIDSize]) {
void* note_section;
int note_size, elfclass;
if (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
@@ -213,7 +208,6 @@ static bool FindElfBuildIDNote(const void *elf_mapped_base,
// a simple hash by XORing the first page worth of bytes into |identifier|.
static bool HashElfTextSection(const void *elf_mapped_base,
uint8_t identifier[kMDGUIDSize]) {
void* text_section;
int text_size;
if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
@@ -234,9 +228,8 @@ static bool HashElfTextSection(const void *elf_mapped_base,
}
// static
bool FileID::ElfFileIdentifierFromMappedFile(void* base,
uint8_t identifier[kMDGUIDSize])
{
bool FileID::ElfFileIdentifierFromMappedFile(const void* base,
uint8_t identifier[kMDGUIDSize]) {
// Look for a build id note first.
if (FindElfBuildIDNote(base, identifier))
return true;
@@ -246,23 +239,11 @@ bool FileID::ElfFileIdentifierFromMappedFile(void* base,
}
bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) {
int fd = open(path_, O_RDONLY);
if (fd < 0)
return false;
struct stat st;
if (fstat(fd, &st) != 0) {
close(fd);
return false;
}
void* base = mmap(NULL, st.st_size,
PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
close(fd);
if (base == MAP_FAILED)
MemoryMappedFile mapped_file(path_);
if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
return false;
bool success = ElfFileIdentifierFromMappedFile(base, identifier);
munmap(base, st.st_size);
return success;
return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
}
// static

View File

@@ -57,7 +57,7 @@ class FileID {
// Load the identifier for the elf file mapped into memory at |base| into
// |identifier|. Return false if the identifier could not be created for the
// file.
static bool ElfFileIdentifierFromMappedFile(void* base,
static bool ElfFileIdentifierFromMappedFile(const void* base,
uint8_t identifier[kMDGUIDSize]);
// Convert the |identifier| data to a NULL terminated string. The string will

View File

@@ -33,39 +33,52 @@
#include <stdlib.h>
#include "common/linux/file_id.h"
#include "common/linux/safe_readlink.h"
#include "common/linux/synth_elf.h"
#include "common/test_assembler.h"
#include "common/tests/auto_tempdir.h"
#include "breakpad_googletest_includes.h"
using namespace google_breakpad;
using google_breakpad::SafeReadLink;
using google_breakpad::synth_elf::BuildIDNote;
using google_breakpad::synth_elf::ELF;
using google_breakpad::test_assembler::kLittleEndian;
using google_breakpad::test_assembler::Section;
namespace {
// Simply calling Section::Append(size, byte) produces a uninteresting pattern
// that tends to get hashed to 0000...0000. This populates the section with
// data to produce better hashes.
void PopulateSection(Section* section, int size, int prime_number) {
for (int i = 0; i < size; i++)
section->Append(1, (i % prime_number) % 256);
}
} // namespace
TEST(FileIDStripTest, StripSelf) {
// Calculate the File ID of this binary using
// FileID::ElfFileIdentifier, then make a copy of this binary,
// strip it, and ensure that the result is the same.
char exe_name[PATH_MAX];
ssize_t len = readlink("/proc/self/exe", exe_name, PATH_MAX - 1);
ASSERT_NE(len, -1);
exe_name[len] = '\0';
ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name));
// copy our binary to a temp file, and strip it
char templ[] = "/tmp/file-id-unittest-XXXXXX";
mktemp(templ);
AutoTempDir temp_dir;
std::string templ = temp_dir.path() + "/file-id-unittest";
char cmdline[4096];
sprintf(cmdline, "cp \"%s\" \"%s\"", exe_name, templ);
sprintf(cmdline, "cp \"%s\" \"%s\"", exe_name, templ.c_str());
ASSERT_EQ(system(cmdline), 0);
sprintf(cmdline, "strip \"%s\"", templ);
sprintf(cmdline, "strip \"%s\"", templ.c_str());
ASSERT_EQ(system(cmdline), 0);
uint8_t identifier1[sizeof(MDGUID)];
uint8_t identifier2[sizeof(MDGUID)];
FileID fileid1(exe_name);
EXPECT_TRUE(fileid1.ElfFileIdentifier(identifier1));
FileID fileid2(templ);
FileID fileid2(templ.c_str());
EXPECT_TRUE(fileid2.ElfFileIdentifier(identifier2));
char identifier_string1[37];
char identifier_string2[37];
@@ -74,7 +87,6 @@ TEST(FileIDStripTest, StripSelf) {
FileID::ConvertIdentifierToString(identifier2, identifier_string2,
37);
EXPECT_STREQ(identifier_string1, identifier_string2);
unlink(templ);
}
class FileIDTest : public testing::Test {
@@ -181,3 +193,92 @@ TEST_F(FileIDTest, BuildID) {
sizeof(identifier_string));
EXPECT_STREQ(expected_identifier_string, identifier_string);
}
// Test to make sure two files with different text sections produce
// different hashes when not using a build id.
TEST_F(FileIDTest, UniqueHashes32) {
char identifier_string_1[] =
"00000000-0000-0000-0000-000000000000";
char identifier_string_2[] =
"00000000-0000-0000-0000-000000000000";
uint8_t identifier_1[sizeof(MDGUID)];
uint8_t identifier_2[sizeof(MDGUID)];
{
ELF elf1(EM_386, ELFCLASS32, kLittleEndian);
Section foo_1(kLittleEndian);
PopulateSection(&foo_1, 32, 5);
elf1.AddSection(".foo", foo_1, SHT_PROGBITS);
Section text_1(kLittleEndian);
PopulateSection(&text_1, 4096, 17);
elf1.AddSection(".text", text_1, SHT_PROGBITS);
elf1.Finish();
GetElfContents(elf1);
}
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_1));
FileID::ConvertIdentifierToString(identifier_1, identifier_string_1,
sizeof(identifier_string_1));
{
ELF elf2(EM_386, ELFCLASS32, kLittleEndian);
Section text_2(kLittleEndian);
Section foo_2(kLittleEndian);
PopulateSection(&foo_2, 32, 5);
elf2.AddSection(".foo", foo_2, SHT_PROGBITS);
PopulateSection(&text_2, 4096, 31);
elf2.AddSection(".text", text_2, SHT_PROGBITS);
elf2.Finish();
GetElfContents(elf2);
}
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_2));
FileID::ConvertIdentifierToString(identifier_2, identifier_string_2,
sizeof(identifier_string_2));
EXPECT_STRNE(identifier_string_1, identifier_string_2);
}
// Same as UniqueHashes32, for x86-64.
TEST_F(FileIDTest, UniqueHashes64) {
char identifier_string_1[] =
"00000000-0000-0000-0000-000000000000";
char identifier_string_2[] =
"00000000-0000-0000-0000-000000000000";
uint8_t identifier_1[sizeof(MDGUID)];
uint8_t identifier_2[sizeof(MDGUID)];
{
ELF elf1(EM_X86_64, ELFCLASS64, kLittleEndian);
Section foo_1(kLittleEndian);
PopulateSection(&foo_1, 32, 5);
elf1.AddSection(".foo", foo_1, SHT_PROGBITS);
Section text_1(kLittleEndian);
PopulateSection(&text_1, 4096, 17);
elf1.AddSection(".text", text_1, SHT_PROGBITS);
elf1.Finish();
GetElfContents(elf1);
}
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_1));
FileID::ConvertIdentifierToString(identifier_1, identifier_string_1,
sizeof(identifier_string_1));
{
ELF elf2(EM_X86_64, ELFCLASS64, kLittleEndian);
Section text_2(kLittleEndian);
Section foo_2(kLittleEndian);
PopulateSection(&foo_2, 32, 5);
elf2.AddSection(".foo", foo_2, SHT_PROGBITS);
PopulateSection(&text_2, 4096, 31);
elf2.AddSection(".text", text_2, SHT_PROGBITS);
elf2.Finish();
GetElfContents(elf2);
}
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_2));
FileID::ConvertIdentifierToString(identifier_2, identifier_string_2,
sizeof(identifier_string_2));
EXPECT_STRNE(identifier_string_1, identifier_string_2);
}

View File

@@ -30,6 +30,7 @@
#include "common/linux/guid_creator.h"
#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
@@ -45,10 +46,6 @@
//
class GUIDGenerator {
public:
GUIDGenerator() {
srandom(time(NULL));
}
static u_int32_t BytesToUInt32(const u_int8_t bytes[]) {
return ((u_int32_t) bytes[0]
| ((u_int32_t) bytes[1] << 8)
@@ -63,7 +60,8 @@ class GUIDGenerator {
bytes[3] = (n >> 24) & 0xff;
}
bool CreateGUID(GUID *guid) const {
static bool CreateGUID(GUID *guid) {
InitOnce();
guid->data1 = random();
guid->data2 = (u_int16_t)(random());
guid->data3 = (u_int16_t)(random());
@@ -71,13 +69,23 @@ class GUIDGenerator {
UInt32ToBytes(&guid->data4[4], random());
return true;
}
private:
static void InitOnce() {
pthread_once(&once_control, &InitOnceImpl);
}
static void InitOnceImpl() {
srandom(time(NULL));
}
static pthread_once_t once_control;
};
// Guid generator.
const GUIDGenerator kGuidGenerator;
pthread_once_t GUIDGenerator::once_control = PTHREAD_ONCE_INIT;
bool CreateGUID(GUID *guid) {
return kGuidGenerator.CreateGUID(guid);
return GUIDGenerator::CreateGUID(guid);
}
// Parse guid to string.

View File

@@ -62,7 +62,11 @@ bool HTTPUpload::SendRequest(const string &url,
const string &proxy_user_pwd,
const string &ca_certificate_file,
string *response_body,
long *response_code,
string *error_description) {
if (response_code != NULL)
*response_code = 0;
if (!CheckParameters(parameters))
return false;
@@ -149,6 +153,11 @@ bool HTTPUpload::SendRequest(const string &url,
CURLcode (*curl_easy_perform)(CURL *);
*(void**) (&curl_easy_perform) = dlsym(curl_lib, "curl_easy_perform");
err_code = (*curl_easy_perform)(curl);
if (response_code != NULL) {
CURLcode (*curl_easy_getinfo)(CURL *, CURLINFO, ...);
*(void**) (&curl_easy_getinfo) = dlsym(curl_lib, "curl_easy_getinfo");
(*curl_easy_getinfo)(curl, CURLINFO_RESPONSE_CODE, response_code);
}
const char* (*curl_easy_strerror)(CURLcode);
*(void**) (&curl_easy_strerror) = dlsym(curl_lib, "curl_easy_strerror");
#ifndef NDEBUG

View File

@@ -53,6 +53,8 @@ class HTTPUpload {
// Only HTTP(S) URLs are currently supported. Returns true on success.
// If the request is successful and response_body is non-NULL,
// the response body will be returned in response_body.
// If response_code is non-NULL, it will be set to the HTTP response code
// received (or 0 if the request failed before getting an HTTP response).
// If the send fails, a description of the error will be
// returned in error_description.
static bool SendRequest(const string &url,
@@ -63,6 +65,7 @@ class HTTPUpload {
const string &proxy_user_pwd,
const string &ca_certificate_file,
string *response_body,
long *response_code,
string *error_description);
private:

View File

@@ -105,7 +105,7 @@ my_strtoui(int* result, const char* s) {
// Return the length of the given, non-negative integer when expressed in base
// 10.
static inline unsigned
my_int_len(int i) {
my_int_len(intmax_t i) {
if (!i)
return 1;
@@ -125,7 +125,7 @@ my_int_len(int i) {
// i: the non-negative integer to serialise.
// i_len: the length of the integer in base 10 (see |my_int_len|).
static inline void
my_itos(char* output, int i, unsigned i_len) {
my_itos(char* output, intmax_t i, unsigned i_len) {
for (unsigned index = i_len; index; --index, i /= 10)
output[index - 1] = '0' + (i % 10);
}

View File

@@ -59,8 +59,12 @@ TEST(LinuxLibcSupportTest, strcmp) {
for (unsigned i = 0; ; ++i) {
if (!test_data[i*2])
break;
ASSERT_EQ(my_strcmp(test_data[i*2], test_data[i*2 + 1]),
strcmp(test_data[i*2], test_data[i*2 + 1]));
int libc_result = strcmp(test_data[i*2], test_data[i*2 + 1]);
if (libc_result > 1)
libc_result = 1;
else if (libc_result < -1)
libc_result = -1;
ASSERT_EQ(my_strcmp(test_data[i*2], test_data[i*2 + 1]), libc_result);
}
}

View File

@@ -0,0 +1,108 @@
// Copyright (c) 2011, 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.
// memory_mapped_file.cc: Implement google_breakpad::MemoryMappedFile.
// See memory_mapped_file.h for details.
#include "common/linux/memory_mapped_file.h"
#include <fcntl.h>
#include <sys/mman.h>
#if defined(__ANDROID__)
#include <sys/stat.h>
#endif
#include <unistd.h>
#include "common/memory_range.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
MemoryMappedFile::MemoryMappedFile() {}
MemoryMappedFile::MemoryMappedFile(const char* path) {
Map(path);
}
MemoryMappedFile::~MemoryMappedFile() {
Unmap();
}
bool MemoryMappedFile::Map(const char* path) {
Unmap();
int fd = sys_open(path, O_RDONLY, 0);
if (fd == -1) {
return false;
}
#if defined(__ANDROID__)
struct stat st;
if (fstat(fd, &st) != 0) {
#elif defined(__x86_64__)
struct kernel_stat st;
if (sys_fstat(fd, &st) == -1 || st.st_size < 0) {
#else
struct kernel_stat64 st;
if (sys_fstat64(fd, &st) == -1 || st.st_size < 0) {
#endif
sys_close(fd);
return false;
}
// If the file size is zero, simply use an empty MemoryRange and return
// true. Don't bother to call mmap() even though mmap() can handle an
// empty file on some platforms.
if (st.st_size == 0) {
sys_close(fd);
return true;
}
#if defined(__x86_64__)
void* data = sys_mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
#else
void* data = sys_mmap2(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
#endif
sys_close(fd);
if (data == MAP_FAILED) {
return false;
}
content_.Set(data, st.st_size);
return true;
}
void MemoryMappedFile::Unmap() {
if (content_.data()) {
sys_munmap(const_cast<u_int8_t*>(content_.data()), content_.length());
content_.Set(NULL, 0);
}
}
} // namespace google_breakpad

View File

@@ -0,0 +1,86 @@
// Copyright (c) 2011, 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.
// memory_mapped_file.h: Define the google_breakpad::MemoryMappedFile
// class, which maps a file into memory for read-only access.
#ifndef COMMON_LINUX_MEMORY_MAPPED_FILE_H_
#define COMMON_LINUX_MEMORY_MAPPED_FILE_H_
#include "common/basictypes.h"
#include "common/memory_range.h"
namespace google_breakpad {
// A utility class for mapping a file into memory for read-only access of
// the file content. Its implementation avoids calling into libc functions
// by directly making system calls for open, close, mmap, and munmap.
class MemoryMappedFile {
public:
MemoryMappedFile();
// Constructor that calls Map() to map a file at |path| into memory.
// If Map() fails, the object behaves as if it is default constructed.
explicit MemoryMappedFile(const char* path);
~MemoryMappedFile();
// Maps a file at |path| into memory, which can then be accessed via
// content() as a MemoryRange object or via data(), and returns true on
// success. Mapping an empty file will succeed but with data() and size()
// returning NULL and 0, respectively. An existing mapping is unmapped
// before a new mapping is created.
bool Map(const char* path);
// Unmaps the memory for the mapped file. It's a no-op if no file is
// mapped.
void Unmap();
// Returns a MemoryRange object that covers the memory for the mapped
// file. The MemoryRange object is empty if no file is mapped.
const MemoryRange& content() const { return content_; }
// Returns a pointer to the beginning of the memory for the mapped file.
// or NULL if no file is mapped or the mapped file is empty.
const void* data() const { return content_.data(); }
// Returns the size in bytes of the mapped file, or zero if no file
// is mapped.
size_t size() const { return content_.length(); }
private:
// Mapped file content as a MemoryRange object.
MemoryRange content_;
DISALLOW_COPY_AND_ASSIGN(MemoryMappedFile);
};
} // namespace google_breakpad
#endif // COMMON_LINUX_MEMORY_MAPPED_FILE_H_

View File

@@ -0,0 +1,175 @@
// Copyright (c) 2011, 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.
// memory_mapped_file_unittest.cc:
// Unit tests for google_breakpad::MemoryMappedFile.
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <string>
#include "breakpad_googletest_includes.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/memory_mapped_file.h"
#include "common/tests/auto_tempdir.h"
#include "common/tests/file_utils.h"
using google_breakpad::AutoTempDir;
using google_breakpad::MemoryMappedFile;
using google_breakpad::WriteFile;
using std::string;
namespace {
class MemoryMappedFileTest : public testing::Test {
protected:
void ExpectNoMappedData(const MemoryMappedFile& mapped_file) {
EXPECT_TRUE(mapped_file.content().IsEmpty());
EXPECT_TRUE(mapped_file.data() == NULL);
EXPECT_EQ(0, mapped_file.size());
}
};
} // namespace
TEST_F(MemoryMappedFileTest, DefaultConstructor) {
MemoryMappedFile mapped_file;
ExpectNoMappedData(mapped_file);
}
TEST_F(MemoryMappedFileTest, UnmapWithoutMap) {
MemoryMappedFile mapped_file;
mapped_file.Unmap();
}
TEST_F(MemoryMappedFileTest, MapNonexistentFile) {
{
MemoryMappedFile mapped_file("nonexistent-file");
ExpectNoMappedData(mapped_file);
}
{
MemoryMappedFile mapped_file;
EXPECT_FALSE(mapped_file.Map("nonexistent-file"));
ExpectNoMappedData(mapped_file);
}
}
TEST_F(MemoryMappedFileTest, MapEmptyFile) {
AutoTempDir temp_dir;
string test_file = temp_dir.path() + "/empty_file";
ASSERT_TRUE(WriteFile(test_file.c_str(), NULL, 0));
{
MemoryMappedFile mapped_file(test_file.c_str());
ExpectNoMappedData(mapped_file);
}
{
MemoryMappedFile mapped_file;
EXPECT_TRUE(mapped_file.Map(test_file.c_str()));
ExpectNoMappedData(mapped_file);
}
}
TEST_F(MemoryMappedFileTest, MapNonEmptyFile) {
char data[256];
size_t data_size = sizeof(data);
for (size_t i = 0; i < data_size; ++i) {
data[i] = i;
}
AutoTempDir temp_dir;
string test_file = temp_dir.path() + "/test_file";
ASSERT_TRUE(WriteFile(test_file.c_str(), data, data_size));
{
MemoryMappedFile mapped_file(test_file.c_str());
EXPECT_FALSE(mapped_file.content().IsEmpty());
EXPECT_TRUE(mapped_file.data() != NULL);
EXPECT_EQ(data_size, mapped_file.size());
EXPECT_EQ(0, memcmp(data, mapped_file.data(), data_size));
}
{
MemoryMappedFile mapped_file;
EXPECT_TRUE(mapped_file.Map(test_file.c_str()));
EXPECT_FALSE(mapped_file.content().IsEmpty());
EXPECT_TRUE(mapped_file.data() != NULL);
EXPECT_EQ(data_size, mapped_file.size());
EXPECT_EQ(0, memcmp(data, mapped_file.data(), data_size));
}
}
TEST_F(MemoryMappedFileTest, RemapAfterMap) {
char data1[256];
size_t data1_size = sizeof(data1);
for (size_t i = 0; i < data1_size; ++i) {
data1[i] = i;
}
char data2[50];
size_t data2_size = sizeof(data2);
for (size_t i = 0; i < data2_size; ++i) {
data2[i] = 255 - i;
}
AutoTempDir temp_dir;
string test_file1 = temp_dir.path() + "/test_file1";
string test_file2 = temp_dir.path() + "/test_file2";
ASSERT_TRUE(WriteFile(test_file1.c_str(), data1, data1_size));
ASSERT_TRUE(WriteFile(test_file2.c_str(), data2, data2_size));
{
MemoryMappedFile mapped_file(test_file1.c_str());
EXPECT_FALSE(mapped_file.content().IsEmpty());
EXPECT_TRUE(mapped_file.data() != NULL);
EXPECT_EQ(data1_size, mapped_file.size());
EXPECT_EQ(0, memcmp(data1, mapped_file.data(), data1_size));
mapped_file.Map(test_file2.c_str());
EXPECT_FALSE(mapped_file.content().IsEmpty());
EXPECT_TRUE(mapped_file.data() != NULL);
EXPECT_EQ(data2_size, mapped_file.size());
EXPECT_EQ(0, memcmp(data2, mapped_file.data(), data2_size));
}
{
MemoryMappedFile mapped_file;
EXPECT_TRUE(mapped_file.Map(test_file1.c_str()));
EXPECT_FALSE(mapped_file.content().IsEmpty());
EXPECT_TRUE(mapped_file.data() != NULL);
EXPECT_EQ(data1_size, mapped_file.size());
EXPECT_EQ(0, memcmp(data1, mapped_file.data(), data1_size));
mapped_file.Map(test_file2.c_str());
EXPECT_FALSE(mapped_file.content().IsEmpty());
EXPECT_TRUE(mapped_file.data() != NULL);
EXPECT_EQ(data2_size, mapped_file.size());
EXPECT_EQ(0, memcmp(data2, mapped_file.data(), data2_size));
}
}

View File

@@ -0,0 +1,53 @@
// Copyright (c) 2011, 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.
// safe_readlink.cc: Implement google_breakpad::SafeReadLink.
// See safe_readlink.h for details.
#include <stddef.h>
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
bool SafeReadLink(const char* path, char* buffer, size_t buffer_size) {
// sys_readlink() does not add a NULL byte to |buffer|. In order to return
// a NULL-terminated string in |buffer|, |buffer_size| should be at least
// one byte longer than the expected path length. Also, sys_readlink()
// returns the actual path length on success, which does not count the
// NULL byte, so |result_size| should be less than |buffer_size|.
ssize_t result_size = sys_readlink(path, buffer, buffer_size);
if (result_size >= 0 && static_cast<size_t>(result_size) < buffer_size) {
buffer[result_size] = '\0';
return true;
}
return false;
}
} // namespace google_breakpad

View File

@@ -0,0 +1,65 @@
// Copyright (c) 2011, 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.
// safe_readlink.h: Define the google_breakpad::SafeReadLink function,
// which wraps sys_readlink and gurantees the result is NULL-terminated.
#ifndef COMMON_LINUX_SAFE_READLINK_H_
#define COMMON_LINUX_SAFE_READLINK_H_
#include <stddef.h>
namespace google_breakpad {
// This function wraps sys_readlink() and performs the same functionalty,
// but guarantees |buffer| is NULL-terminated if sys_readlink() returns
// no error. It takes the same arguments as sys_readlink(), but unlike
// sys_readlink(), it returns true on success.
//
// |buffer_size| specifies the size of |buffer| in bytes. As this function
// always NULL-terminates |buffer| on success, |buffer_size| should be
// at least one byte longer than the expected path length (e.g. PATH_MAX,
// which is typically defined as the maximum length of a path name
// including the NULL byte).
//
// The implementation of this function calls sys_readlink() instead of
// readlink(), it can thus be used in the context where calling to libc
// functions is discouraged.
bool SafeReadLink(const char* path, char* buffer, size_t buffer_size);
// Same as the three-argument version of SafeReadLink() but deduces the
// size of |buffer| if it is a char array of known size.
template <size_t N>
bool SafeReadLink(const char* path, char (&buffer)[N]) {
return SafeReadLink(path, buffer, sizeof(buffer));
}
} // namespace google_breakpad
#endif // COMMON_LINUX_SAFE_READLINK_H_

View File

@@ -0,0 +1,89 @@
// Copyright (c) 2011, 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.
// safe_readlink_unittest.cc: Unit tests for google_breakpad::SafeReadLink.
#include "breakpad_googletest_includes.h"
#include "common/linux/safe_readlink.h"
using google_breakpad::SafeReadLink;
TEST(SafeReadLinkTest, ZeroBufferSize) {
char buffer[1];
EXPECT_FALSE(SafeReadLink("/proc/self/exe", buffer, 0));
}
TEST(SafeReadLinkTest, BufferSizeTooSmall) {
char buffer[1];
EXPECT_FALSE(SafeReadLink("/proc/self/exe", buffer, 1));
}
TEST(SafeReadLinkTest, BoundaryBufferSize) {
char buffer[PATH_MAX];
EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer, sizeof(buffer)));
size_t path_length = strlen(buffer);
EXPECT_LT(0, path_length);
EXPECT_GT(sizeof(buffer), path_length);
// Buffer size equals to the expected path length plus 1 for the NULL byte.
char buffer2[PATH_MAX];
EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer2, path_length + 1));
EXPECT_EQ(path_length, strlen(buffer2));
EXPECT_EQ(0, strncmp(buffer, buffer2, PATH_MAX));
// Buffer size equals to the expected path length.
EXPECT_FALSE(SafeReadLink("/proc/self/exe", buffer, path_length));
}
TEST(SafeReadLinkTest, NonexistentPath) {
char buffer[PATH_MAX];
EXPECT_FALSE(SafeReadLink("nonexistent_path", buffer, sizeof(buffer)));
}
TEST(SafeReadLinkTest, NonSymbolicLinkPath) {
char actual_path[PATH_MAX];
EXPECT_TRUE(SafeReadLink("/proc/self/exe", actual_path, sizeof(actual_path)));
char buffer[PATH_MAX];
EXPECT_FALSE(SafeReadLink(actual_path, buffer, sizeof(buffer)));
}
TEST(SafeReadLinkTest, DeduceBufferSizeFromCharArray) {
char buffer[PATH_MAX];
char* buffer_pointer = buffer;
EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer_pointer, sizeof(buffer)));
size_t path_length = strlen(buffer);
// Use the template version of SafeReadLink to deduce the buffer size
// from the char array.
char buffer2[PATH_MAX];
EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer2));
EXPECT_EQ(path_length, strlen(buffer2));
EXPECT_EQ(0, strncmp(buffer, buffer2, PATH_MAX));
}

View File

@@ -0,0 +1,276 @@
// Copyright (c) 2011, 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.
// crash_generator.cc: Implement google_breakpad::CrashGenerator.
// See crash_generator.h for details.
#include "common/linux/tests/crash_generator.h"
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string>
#include "common/linux/eintr_wrapper.h"
#include "common/tests/auto_tempdir.h"
#include "common/tests/file_utils.h"
namespace {
struct ThreadData {
pthread_t thread;
pthread_barrier_t* barrier;
pid_t* thread_id_ptr;
};
const char* const kProcFilesToCopy[] = {
"auxv", "cmdline", "environ", "maps", "status"
};
const size_t kNumProcFilesToCopy =
sizeof(kProcFilesToCopy) / sizeof(kProcFilesToCopy[0]);
// Core file size limit set to 1 MB, which is big enough for test purposes.
const rlim_t kCoreSizeLimit = 1024 * 1024;
void *thread_function(void *data) {
ThreadData* thread_data = reinterpret_cast<ThreadData*>(data);
volatile pid_t thread_id = syscall(__NR_gettid);
*(thread_data->thread_id_ptr) = thread_id;
int result = pthread_barrier_wait(thread_data->barrier);
if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) {
exit(1);
}
while (true) {
pthread_yield();
}
}
} // namespace
namespace google_breakpad {
CrashGenerator::CrashGenerator()
: shared_memory_(NULL),
shared_memory_size_(0) {
}
CrashGenerator::~CrashGenerator() {
UnmapSharedMemory();
}
bool CrashGenerator::HasDefaultCorePattern() const {
char buffer[8];
ssize_t buffer_size = sizeof(buffer);
return ReadFile("/proc/sys/kernel/core_pattern", buffer, &buffer_size) &&
buffer_size == 5 && memcmp(buffer, "core", 4) == 0;
}
std::string CrashGenerator::GetCoreFilePath() const {
return temp_dir_.path() + "/core";
}
std::string CrashGenerator::GetDirectoryOfProcFilesCopy() const {
return temp_dir_.path() + "/proc";
}
pid_t CrashGenerator::GetThreadId(unsigned index) const {
return reinterpret_cast<pid_t*>(shared_memory_)[index];
}
pid_t* CrashGenerator::GetThreadIdPointer(unsigned index) {
return reinterpret_cast<pid_t*>(shared_memory_) + index;
}
bool CrashGenerator::MapSharedMemory(size_t memory_size) {
if (!UnmapSharedMemory())
return false;
void* mapped_memory = mmap(0, memory_size, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (mapped_memory == MAP_FAILED) {
perror("CrashGenerator: Failed to map shared memory");
return false;
}
memset(mapped_memory, 0, memory_size);
shared_memory_ = mapped_memory;
shared_memory_size_ = memory_size;
return true;
}
bool CrashGenerator::UnmapSharedMemory() {
if (!shared_memory_)
return true;
if (munmap(shared_memory_, shared_memory_size_) == 0) {
shared_memory_ = NULL;
shared_memory_size_ = 0;
return true;
}
perror("CrashGenerator: Failed to unmap shared memory");
return false;
}
bool CrashGenerator::SetCoreFileSizeLimit(rlim_t limit) const {
struct rlimit limits = { limit, limit };
if (setrlimit(RLIMIT_CORE, &limits) == -1) {
perror("CrashGenerator: Failed to set core file size limit");
return false;
}
return true;
}
bool CrashGenerator::CreateChildCrash(
unsigned num_threads, unsigned crash_thread, int crash_signal,
pid_t* child_pid) {
if (num_threads == 0 || crash_thread >= num_threads)
return false;
if (!MapSharedMemory(num_threads * sizeof(pid_t)))
return false;
pid_t pid = fork();
if (pid == 0) {
if (chdir(temp_dir_.path().c_str()) == -1) {
perror("CrashGenerator: Failed to change directory");
exit(1);
}
if (SetCoreFileSizeLimit(kCoreSizeLimit)) {
CreateThreadsInChildProcess(num_threads);
std::string proc_dir = GetDirectoryOfProcFilesCopy();
if (mkdir(proc_dir.c_str(), 0755) == -1) {
perror("CrashGenerator: Failed to create proc directory");
exit(1);
}
if (!CopyProcFiles(getpid(), proc_dir.c_str())) {
fprintf(stderr, "CrashGenerator: Failed to copy proc files\n");
exit(1);
}
if (kill(*GetThreadIdPointer(crash_thread), crash_signal) == -1) {
perror("CrashGenerator: Failed to kill thread by signal");
}
}
exit(1);
} else if (pid == -1) {
perror("CrashGenerator: Failed to create child process");
return false;
}
int status;
if (HANDLE_EINTR(waitpid(pid, &status, 0)) == -1) {
perror("CrashGenerator: Failed to wait for child process");
return false;
}
if (!WIFSIGNALED(status) || WTERMSIG(status) != crash_signal) {
fprintf(stderr, "CrashGenerator: Child process not killed by the expected signal\n"
" exit status=0x%x signaled=%s sig=%d expected=%d\n",
status, WIFSIGNALED(status) ? "true" : "false",
WTERMSIG(status), crash_signal);
return false;
}
if (child_pid)
*child_pid = pid;
return true;
}
bool CrashGenerator::CopyProcFiles(pid_t pid, const char* path) const {
char from_path[PATH_MAX], to_path[PATH_MAX];
for (size_t i = 0; i < kNumProcFilesToCopy; ++i) {
int num_chars = snprintf(from_path, PATH_MAX, "/proc/%d/%s",
pid, kProcFilesToCopy[i]);
if (num_chars < 0 || num_chars >= PATH_MAX)
return false;
num_chars = snprintf(to_path, PATH_MAX, "%s/%s",
path, kProcFilesToCopy[i]);
if (num_chars < 0 || num_chars >= PATH_MAX)
return false;
if (!CopyFile(from_path, to_path))
return false;
}
return true;
}
void CrashGenerator::CreateThreadsInChildProcess(unsigned num_threads) {
*GetThreadIdPointer(0) = getpid();
if (num_threads <= 1)
return;
// This method does not clean up any pthread resource, as the process
// is expected to be killed anyway.
ThreadData* thread_data = new ThreadData[num_threads];
// Create detached threads so that we do not worry about pthread_join()
// later being called or not.
pthread_attr_t thread_attributes;
if (pthread_attr_init(&thread_attributes) != 0 ||
pthread_attr_setdetachstate(&thread_attributes,
PTHREAD_CREATE_DETACHED) != 0) {
fprintf(stderr, "CrashGenerator: Failed to initialize thread attribute\n");
exit(1);
}
pthread_barrier_t thread_barrier;
if (pthread_barrier_init(&thread_barrier, NULL, num_threads) != 0) {
fprintf(stderr, "CrashGenerator: Failed to initialize thread barrier\n");
exit(1);
}
for (unsigned i = 1; i < num_threads; ++i) {
thread_data[i].barrier = &thread_barrier;
thread_data[i].thread_id_ptr = GetThreadIdPointer(i);
if (pthread_create(&thread_data[i].thread, &thread_attributes,
thread_function, &thread_data[i]) != 0) {
fprintf(stderr, "CrashGenerator: Failed to create thread %d\n", i);
exit(1);
}
}
int result = pthread_barrier_wait(&thread_barrier);
if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) {
fprintf(stderr, "CrashGenerator: Failed to wait for thread barrier\n");
exit(1);
}
pthread_barrier_destroy(&thread_barrier);
pthread_attr_destroy(&thread_attributes);
delete[] thread_data;
}
} // namespace google_breakpad

View File

@@ -0,0 +1,116 @@
// Copyright (c) 2011, 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.
// crash_generator.h: Define the google_breakpad::CrashGenerator class,
// which is used to generate a crash (and a core dump file) for testing.
#ifndef COMMON_LINUX_TESTS_CRASH_GENERATOR_H_
#define COMMON_LINUX_TESTS_CRASH_GENERATOR_H_
#include <sys/resource.h>
#include <string>
#include "common/tests/auto_tempdir.h"
namespace google_breakpad {
// A utility class for generating a crash (and a core dump file) for
// testing. It creates a child process with the specified number of
// threads, which is then termainated by the specified signal. A core
// dump file is expected to be created upon the termination of the child
// process, which can then be used for testing code that processes core
// dump files.
class CrashGenerator {
public:
CrashGenerator();
~CrashGenerator();
// Returns true if a core dump file named 'core' will be generated in
// the current directory for a test that produces a crash by checking
// if /proc/sys/kernel/core_pattern has the default value 'core'.
bool HasDefaultCorePattern() const;
// Returns the expected path of the core dump file.
std::string GetCoreFilePath() const;
// Returns the directory of a copy of proc files of the child process.
std::string GetDirectoryOfProcFilesCopy() const;
// Creates a crash (and a core dump file) by creating a child process with
// |num_threads| threads, and the terminating the child process by sending
// a signal with number |crash_signal| to the |crash_thread|-th thread.
// Returns true on success.
bool CreateChildCrash(unsigned num_threads, unsigned crash_thread,
int crash_signal, pid_t* child_pid);
// Returns the thread ID of the |index|-th thread in the child process.
// This method does not validate |index|.
pid_t GetThreadId(unsigned index) const;
private:
// Copies the following proc files of the process with |pid| to the directory
// at |path|: auxv, cmdline, environ, maps, status
// The directory must have been created. Returns true on success.
bool CopyProcFiles(pid_t pid, const char* path) const;
// Creates |num_threads| threads in the child process.
void CreateThreadsInChildProcess(unsigned num_threads);
// Sets the maximum size of core dump file (both the soft and hard limit)
// to |limit| bytes. Returns true on success.
bool SetCoreFileSizeLimit(rlim_t limit) const;
// Creates a shared memory of |memory_size| bytes for communicating thread
// IDs between the parent and child process. Returns true on success.
bool MapSharedMemory(size_t memory_size);
// Releases any shared memory created by MapSharedMemory(). Returns true on
// success.
bool UnmapSharedMemory();
// Returns the pointer to the thread ID of the |index|-th thread in the child
// process. This method does not validate |index|.
pid_t* GetThreadIdPointer(unsigned index);
// Temporary directory in which a core file is generated.
AutoTempDir temp_dir_;
// Shared memory for communicating thread IDs between the parent and
// child process.
void* shared_memory_;
// Number of bytes mapped for |shared_memory_|.
size_t shared_memory_size_;
};
} // namespace google_breakpad
#endif // COMMON_LINUX_TESTS_CRASH_GENERATOR_H_

View File

@@ -29,8 +29,6 @@
ARCHS = $(ARCHS_STANDARD_32_64_BIT)
SDKROOT = macosx10.5
SDKROOT[arch=i386] = macosx10.4
SDKROOT[arch=ppc] = macosx10.4
GCC_VERSION = 4.2
GCC_VERSION[sdk=macosx10.4][arch=*] = 4.0

View File

@@ -32,7 +32,7 @@
// Each file is sent with a name field in addition to the filename and data
// The data will be sent synchronously.
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
@interface HTTPMultipartUpload : NSObject {
@protected

View File

@@ -32,6 +32,8 @@
@interface HTTPMultipartUpload(PrivateMethods)
- (NSString *)multipartBoundary;
// Each of the following methods will append the starting multipart boundary,
// but not the ending one.
- (NSData *)formDataForKey:(NSString *)key value:(NSString *)value;
- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name;
- (NSData *)formDataForFile:(NSString *)file name:(NSString *)name;
@@ -67,11 +69,9 @@
NSString *fmt = @"--%@\r\nContent-Disposition: form-data; name=\"%@\"; "
"filename=\"minidump.dmp\"\r\nContent-Type: application/octet-stream\r\n\r\n";
NSString *pre = [NSString stringWithFormat:fmt, boundary_, escaped];
NSString *post = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary_];
[data appendData:[pre dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:contents];
[data appendData:[post dataUsingEncoding:NSUTF8StringEncoding]];
return data;
}
@@ -182,6 +182,9 @@
[postBody appendData:fileData];
}
NSString *epilogue = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary_];
[postBody appendData:[epilogue dataUsingEncoding:NSUTF8StringEncoding]];
[req setHTTPBody:postBody];
[req setHTTPMethod:@"POST"];
@@ -193,7 +196,8 @@
error:error];
[response_ retain];
[req release];
return data;
}

View File

@@ -32,6 +32,7 @@
#import <stdio.h>
#import "MachIPC.h"
#include "common/mac/bootstrap_compat.h"
namespace google_breakpad {
//==============================================================================
@@ -187,9 +188,10 @@ ReceivePort::ReceivePort(const char *receive_port_name) {
if (init_result_ != KERN_SUCCESS)
return;
init_result_ = bootstrap_register(bootstrap_port,
const_cast<char*>(receive_port_name),
port_);
init_result_ = breakpad::BootstrapRegister(
bootstrap_port,
const_cast<char*>(receive_port_name),
port_);
}
//==============================================================================

View File

@@ -134,7 +134,7 @@ class SimpleStringDictionary {
// Given |key|, returns its corresponding |value|.
// If |key| is NULL, an assert will fire or NULL will be returned. If |key|
// is not found or is an empty string, NULL is returned.
const char *GetValueForKey(const char *key);
const char *GetValueForKey(const char *key) const;
// Stores a string |value| represented by |key|. If |key| is NULL or an empty
// string, this will assert (or do nothing). If |value| is NULL then

View File

@@ -55,13 +55,13 @@ int SimpleStringDictionary::GetCount() const {
}
//==============================================================================
const char *SimpleStringDictionary::GetValueForKey(const char *key) {
const char *SimpleStringDictionary::GetValueForKey(const char *key) const {
assert(key);
if (!key)
return NULL;
for (int i = 0; i < MAX_NUM_ENTRIES; ++i) {
KeyValueEntry &entry = entries_[i];
const KeyValueEntry &entry = entries_[i];
if (entry.IsActive() && !strcmp(entry.GetKey(), key)) {
return entry.GetValue();
}

View File

@@ -0,0 +1,42 @@
// Copyright (c) 2012, 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.
#include "common/mac/bootstrap_compat.h"
namespace breakpad {
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
kern_return_t BootstrapRegister(mach_port_t bp,
name_t service_name,
mach_port_t sp) {
return bootstrap_register(bp, service_name, sp);
}
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
} // namesapce breakpad

View File

@@ -0,0 +1,54 @@
// Copyright (c) 2012, 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.
#ifndef COMMON_MAC_BOOTSTRAP_COMPAT_H_
#define COMMON_MAC_BOOTSTRAP_COMPAT_H_
#include <servers/bootstrap.h>
namespace breakpad {
// Wrapper for bootstrap_register to avoid deprecation warnings.
//
// In 10.6, it's possible to call bootstrap_check_in as the one-stop-shop for
// handling what bootstrap_register is used for. In 10.5, bootstrap_check_in
// can't check in a service whose name has not yet been registered, despite
// bootstrap_register being marked as deprecated in that OS release. Breakpad
// needs to register new service names, and in 10.5, calling
// bootstrap_register is the only way to achieve that. Attempts to call
// bootstrap_check_in for a new service name on 10.5 will result in
// BOOTSTRAP_UNKNOWN_SERVICE being returned rather than registration of the
// new service name.
kern_return_t BootstrapRegister(mach_port_t bp,
name_t service_name,
mach_port_t sp);
} // namespace breakpad
#endif // COMMON_MAC_BOOTSTRAP_COMPAT_H_

View File

@@ -1,6 +1,6 @@
// -*- mode: c++ -*-
// Copyright (c) 2010, Google Inc.
// Copyright (c) 2011, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -52,9 +52,9 @@ namespace google_breakpad {
class DumpSymbols {
public:
DumpSymbols()
DumpSymbols()
: input_pathname_(),
object_filename_(),
object_filename_(),
contents_(),
selected_object_file_(),
selected_object_name_() { }
@@ -84,9 +84,9 @@ class DumpSymbols {
// object file, then the dumper will dump the object file whose
// architecture matches that of this dumper program.
bool SetArchitecture(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype);
// If this dumper's file includes an object file for |arch_name|, then select
// that object file for dumping, and return true. Otherwise, return false,
// If this dumper's file includes an object file for |arch_name|, then select
// that object file for dumping, and return true. Otherwise, return false,
// and leave this dumper's selected architecture unchanged.
//
// By default, if this dumper's file contains only one object file, then
@@ -94,7 +94,7 @@ class DumpSymbols {
// object file, then the dumper will dump the object file whose
// architecture matches that of this dumper program.
bool SetArchitecture(const std::string &arch_name);
// Return a pointer to an array of 'struct fat_arch' structures,
// describing the object files contained in this dumper's file. Set
// *|count| to the number of elements in the array. The returned array is
@@ -109,10 +109,10 @@ class DumpSymbols {
return NULL;
}
// Read the selected object file's debugging information, and write it
// out to |stream|. Return true on success; if an error occurs, report it
// and return false.
bool WriteSymbolFile(std::ostream &stream);
// Read the selected object file's debugging information, and write it out to
// |stream|. Write the CFI section if |cfi| is true. Return true on success;
// if an error occurs, report it and return false.
bool WriteSymbolFile(std::ostream &stream, bool cfi);
private:
// Used internally.
@@ -158,7 +158,7 @@ class DumpSymbols {
// has exactly one element.
vector<struct fat_arch> object_files_;
// The object file in object_files_ selected to dump, or NULL if
// The object file in object_files_ selected to dump, or NULL if
// SetArchitecture hasn't been called yet.
const struct fat_arch *selected_object_file_;

View File

@@ -1,6 +1,6 @@
// -*- mode: c++ -*-
// Copyright (c) 2010, Google Inc.
// Copyright (c) 2011, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -79,7 +79,7 @@ namespace google_breakpad {
bool DumpSymbols::Read(NSString *filename) {
if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) {
fprintf(stderr, "Object file does not exist: %s\n",
[filename fileSystemRepresentation]);
[filename fileSystemRepresentation]);
return false;
}
@@ -101,15 +101,15 @@ bool DumpSymbols::Read(NSString *filename) {
// pathForResource:ofType:inDirectory likes.
NSString *base_name = [input_pathname_ lastPathComponent];
NSString *dwarf_resource;
do {
NSString *new_base_name = [base_name stringByDeletingPathExtension];
// If stringByDeletingPathExtension returned the name unchanged, then
// there's nothing more for us to strip off --- lose.
if ([new_base_name isEqualToString:base_name]) {
fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n",
[input_pathname_ fileSystemRepresentation]);
fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n",
[input_pathname_ fileSystemRepresentation]);
return false;
}
@@ -141,12 +141,12 @@ bool DumpSymbols::Read(NSString *filename) {
// file don't affect memory and vice versa).
NSError *error;
contents_ = [NSData dataWithContentsOfFile:object_filename_
options:0
error:&error];
options:0
error:&error];
if (!contents_) {
fprintf(stderr, "Error reading object file: %s: %s\n",
[object_filename_ fileSystemRepresentation],
[[error localizedDescription] UTF8String]);
[object_filename_ fileSystemRepresentation],
[[error localizedDescription] UTF8String]);
return false;
}
[contents_ retain];
@@ -166,7 +166,7 @@ bool DumpSymbols::Read(NSString *filename) {
fat_reader.object_files(&object_files_count);
if (object_files_count == 0) {
fprintf(stderr, "Fat binary file contains *no* architectures: %s\n",
[object_filename_ fileSystemRepresentation]);
[object_filename_ fileSystemRepresentation]);
return false;
}
object_files_.resize(object_files_count);
@@ -197,14 +197,14 @@ bool DumpSymbols::SetArchitecture(const std::string &arch_name) {
}
return arch_set;
}
string DumpSymbols::Identifier() {
FileID file_id([object_filename_ fileSystemRepresentation]);
unsigned char identifier_bytes[16];
cpu_type_t cpu_type = selected_object_file_->cputype;
if (!file_id.MachoIdentifier(cpu_type, identifier_bytes)) {
fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n",
[object_filename_ fileSystemRepresentation]);
[object_filename_ fileSystemRepresentation]);
return "";
}
@@ -243,7 +243,7 @@ bool DumpSymbols::ReadDwarf(google_breakpad::Module *module,
const mach_o::Reader &macho_reader,
const mach_o::SectionMap &dwarf_sections) const {
// Build a byte reader of the appropriate endianness.
ByteReader byte_reader(macho_reader.big_endian()
ByteReader byte_reader(macho_reader.big_endian()
? dwarf2reader::ENDIANNESS_BIG
: dwarf2reader::ENDIANNESS_LITTLE);
@@ -265,10 +265,10 @@ bool DumpSymbols::ReadDwarf(google_breakpad::Module *module,
// There had better be a __debug_info section!
if (!debug_info_section.first) {
fprintf(stderr, "%s: __DWARF segment of file has no __debug_info section\n",
selected_object_name_.c_str());
selected_object_name_.c_str());
return false;
}
// Build a line-to-module loader for the root handler to use.
DumperLineToModule line_to_module(&byte_reader);
@@ -343,7 +343,7 @@ bool DumpSymbols::ReadCFI(google_breakpad::Module *module,
// investigation, Mac OS X only uses DW_EH_PE_pcrel-based pointers, so
// this is the only base address the CFI parser will need.
byte_reader.SetCFIDataBase(section.address, cfi);
dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(selected_object_name_,
section.section_name);
dwarf2reader::CallFrameInfo parser(cfi, cfi_size,
@@ -421,7 +421,7 @@ bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer &entries,
return true;
}
bool DumpSymbols::WriteSymbolFile(std::ostream &stream) {
bool DumpSymbols::WriteSymbolFile(std::ostream &stream, bool cfi) {
// Select an object file, if SetArchitecture hasn't been called to set one
// explicitly.
if (!selected_object_file_) {
@@ -433,10 +433,10 @@ bool DumpSymbols::WriteSymbolFile(std::ostream &stream) {
const NXArchInfo *local_arch = NXGetLocalArchInfo();
if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) {
fprintf(stderr, "%s: object file contains more than one"
" architecture, none of which match the current"
" architecture, none of which match the current"
" architecture; specify an architecture explicitly"
" with '-a ARCH' to resolve the ambiguity\n",
[object_filename_ fileSystemRepresentation]);
" with '-a ARCH' to resolve the ambiguity\n",
[object_filename_ fileSystemRepresentation]);
return false;
}
}
@@ -472,7 +472,7 @@ bool DumpSymbols::WriteSymbolFile(std::ostream &stream) {
identifier += "0";
// Create a module to hold the debugging information.
Module module([module_name UTF8String], "mac", selected_arch_name,
Module module([module_name UTF8String], "mac", selected_arch_name,
identifier);
// Parse the selected object file.
@@ -481,8 +481,8 @@ bool DumpSymbols::WriteSymbolFile(std::ostream &stream) {
if (!reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes])
+ selected_object_file_->offset,
selected_object_file_->size,
selected_object_file_->cputype,
selected_object_file_->cpusubtype))
selected_object_file_->cputype,
selected_object_file_->cpusubtype))
return false;
// Walk its load commands, and deal with whatever is there.
@@ -490,7 +490,7 @@ bool DumpSymbols::WriteSymbolFile(std::ostream &stream) {
if (!reader.WalkLoadCommands(&load_command_dumper))
return false;
return module.Write(stream);
return module.Write(stream, cfi);
}
} // namespace google_breakpad

View File

@@ -51,18 +51,29 @@ extern "C" { // necessary for Leopard
namespace MacFileUtilities {
using google_breakpad::MD5Init;
using google_breakpad::MD5Update;
using google_breakpad::MD5Final;
MachoID::MachoID(const char *path)
: file_(0),
: memory_(0),
memory_size_(0),
crc_(0),
md5_context_(),
update_function_(NULL) {
strlcpy(path_, path, sizeof(path_));
}
MachoID::MachoID(const char *path, void *memory, size_t size)
: memory_(memory),
memory_size_(size),
crc_(0),
md5_context_(),
update_function_(NULL) {
strlcpy(path_, path, sizeof(path_));
file_ = open(path, O_RDONLY);
}
MachoID::~MachoID() {
if (file_ != -1)
close(file_);
}
// The CRC info is from http://en.wikipedia.org/wiki/Adler-32
@@ -144,10 +155,8 @@ void MachoID::Update(MachoWalker *walker, off_t offset, size_t size) {
bool MachoID::UUIDCommand(int cpu_type, unsigned char bytes[16]) {
struct breakpad_uuid_command uuid_cmd;
MachoWalker walker(path_, UUIDWalkerCB, &uuid_cmd);
uuid_cmd.cmd = 0;
if (!walker.WalkHeader(cpu_type))
if (!WalkHeader(cpu_type, UUIDWalkerCB, &uuid_cmd))
return false;
// If we found the command, we'll have initialized the uuid_command
@@ -162,10 +171,8 @@ bool MachoID::UUIDCommand(int cpu_type, unsigned char bytes[16]) {
bool MachoID::IDCommand(int cpu_type, unsigned char identifier[16]) {
struct dylib_command dylib_cmd;
MachoWalker walker(path_, IDWalkerCB, &dylib_cmd);
dylib_cmd.cmd = 0;
if (!walker.WalkHeader(cpu_type))
if (!WalkHeader(cpu_type, IDWalkerCB, &dylib_cmd))
return false;
// If we found the command, we'll have initialized the dylib_command
@@ -204,29 +211,39 @@ bool MachoID::IDCommand(int cpu_type, unsigned char identifier[16]) {
}
uint32_t MachoID::Adler32(int cpu_type) {
MachoWalker walker(path_, WalkerCB, this);
update_function_ = &MachoID::UpdateCRC;
crc_ = 0;
if (!walker.WalkHeader(cpu_type))
if (!WalkHeader(cpu_type, WalkerCB, this))
return 0;
return crc_;
}
bool MachoID::MD5(int cpu_type, unsigned char identifier[16]) {
MachoWalker walker(path_, WalkerCB, this);
update_function_ = &MachoID::UpdateMD5;
MD5Init(&md5_context_);
if (!walker.WalkHeader(cpu_type))
if (!WalkHeader(cpu_type, WalkerCB, this))
return false;
MD5Final(identifier, &md5_context_);
return true;
}
bool MachoID::WalkHeader(int cpu_type,
MachoWalker::LoadCommandCallback callback,
void *context) {
if (memory_) {
MachoWalker walker(memory_, memory_size_, callback, context);
return walker.WalkHeader(cpu_type);
} else {
MachoWalker walker(path_, callback, context);
return walker.WalkHeader(cpu_type);
}
}
// static
bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
bool swap, void *context) {

View File

@@ -37,15 +37,15 @@
#include <limits.h>
#include <mach-o/loader.h>
#include "common/mac/macho_walker.h"
#include "common/md5.h"
namespace MacFileUtilities {
class MachoWalker;
class MachoID {
public:
MachoID(const char *path);
MachoID(const char *path, void *memory, size_t size);
~MachoID();
// For the given |cpu_type|, return a UUID from the LC_UUID command.
@@ -80,6 +80,10 @@ class MachoID {
// Bottleneck for update routines
void Update(MachoWalker *walker, off_t offset, size_t size);
// Factory for the MachoWalker
bool WalkHeader(int cpu_type, MachoWalker::LoadCommandCallback callback,
void *context);
// The callback from the MachoWalker for CRC and MD5
static bool WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
bool swap, void *context);
@@ -95,14 +99,17 @@ class MachoID {
// File path
char path_[PATH_MAX];
// File descriptor
int file_;
// Memory region to read from
void *memory_;
// Size of the memory region
size_t memory_size_;
// The current crc value
uint32_t crc_;
// The MD5 context
MD5Context md5_context_;
google_breakpad::MD5Context md5_context_;
// The current update to call from the Update callback
UpdateFunction update_function_;

View File

@@ -38,6 +38,11 @@
#include <stdio.h>
#include <stdlib.h>
// Unfortunately, CPU_TYPE_ARM is not define for 10.4.
#if !defined(CPU_TYPE_ARM)
#define CPU_TYPE_ARM 12
#endif
namespace google_breakpad {
namespace mach_o {
@@ -230,6 +235,7 @@ bool Reader::Read(const uint8_t *buffer,
uint32_t expected_magic;
// validate that magic matches the expected cpu type
switch (expected_cpu_type) {
case CPU_TYPE_ARM:
case CPU_TYPE_I386:
expected_magic = MH_CIGAM;
break;

View File

@@ -52,6 +52,8 @@ namespace MacFileUtilities {
MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback,
void *context)
: file_(0),
memory_(NULL),
memory_size_(0),
callback_(callback),
callback_context_(context),
current_header_(NULL),
@@ -60,6 +62,18 @@ MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback,
file_ = open(path, O_RDONLY);
}
MachoWalker::MachoWalker(void *memory, size_t size,
LoadCommandCallback callback, void *context)
: file_(0),
memory_(memory),
memory_size_(size),
callback_(callback),
callback_context_(context),
current_header_(NULL),
current_header_size_(0),
current_header_offset_(0) {
}
MachoWalker::~MachoWalker() {
if (file_ != -1)
close(file_);
@@ -90,7 +104,21 @@ bool MachoWalker::WalkHeader(int cpu_type) {
}
bool MachoWalker::ReadBytes(void *buffer, size_t size, off_t offset) {
return pread(file_, buffer, size, offset) == (ssize_t)size;
if (memory_) {
if (offset < 0)
return false;
bool result = true;
if (offset + size > memory_size_) {
if (static_cast<size_t>(offset) >= memory_size_)
return false;
size = memory_size_ - offset;
result = false;
}
memcpy(buffer, static_cast<char *>(memory_) + offset, size);
return result;
} else {
return pread(file_, buffer, size, offset) == (ssize_t)size;
}
}
bool MachoWalker::CurrentHeader(struct mach_header_64 *header, off_t *offset) {

View File

@@ -52,7 +52,8 @@ class MachoWalker {
off_t offset, bool swap, void *context);
MachoWalker(const char *path, LoadCommandCallback callback, void *context);
MachoWalker(int file_descriptor, LoadCommandCallback callback, void *context);
MachoWalker(void *memory, size_t size, LoadCommandCallback callback,
void *context);
~MachoWalker();
// Begin walking the header for |cpu_type|. If |cpu_type| is 0, then the
@@ -68,7 +69,7 @@ class MachoWalker {
// Read |size| bytes from the opened file at |offset| into |buffer|
bool ReadBytes(void *buffer, size_t size, off_t offset);
// Return the current header and header offset
bool CurrentHeader(struct mach_header_64 *header, off_t *offset);
@@ -87,19 +88,25 @@ class MachoWalker {
// File descriptor to the opened file
int file_;
// Memory location to read from.
void *memory_;
// Size of the memory segment we can read from.
size_t memory_size_;
// User specified callback & context
LoadCommandCallback callback_;
void *callback_context_;
// Current header, size, and offset. The mach_header_64 is used for both
// 32-bit and 64-bit headers because they only differ in their last field
// (reserved). By adding the |current_header_size_| and the
// (reserved). By adding the |current_header_size_| and the
// |current_header_offset_|, you can determine the offset in the file just
// after the header.
struct mach_header_64 *current_header_;
unsigned long current_header_size_;
off_t current_header_offset_;
private:
MachoWalker(const MachoWalker &);
MachoWalker &operator=(const MachoWalker &);

View File

@@ -17,6 +17,8 @@
#include "common/md5.h"
namespace google_breakpad {
#ifndef WORDS_BIGENDIAN
#define byteReverse(buf, len) /* Nothing */
#else
@@ -244,3 +246,6 @@ static void MD5Transform(u32 buf[4], u32 const in[16])
buf[2] += c;
buf[3] += d;
}
} // namespace google_breakpad

View File

@@ -5,6 +5,8 @@
#include <stdint.h>
namespace google_breakpad {
typedef uint32_t u32;
typedef uint8_t u8;
@@ -14,18 +16,12 @@ struct MD5Context {
u8 in[64];
};
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void MD5Init(struct MD5Context *ctx);
void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len);
void MD5Final(unsigned char digest[16], struct MD5Context *ctx);
#ifdef __cplusplus
}
#endif
} // namespace google_breakpad
#endif // COMMON_MD5_H__

View File

@@ -145,6 +145,18 @@ class wasteful_vector {
used_(0) {
}
T& back() {
return a_[used_ - 1];
}
const T& back() const {
return a_[used_ - 1];
}
bool empty() const {
return used_ == 0;
}
void push_back(const T& new_element) {
if (used_ == allocated_)
Realloc(allocated_ * 2);

View File

@@ -0,0 +1,145 @@
// Copyright (c) 2011, 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.
// memory_range.h: Define the google_breakpad::MemoryRange class, which
// is a lightweight wrapper with a pointer and a length to encapsulate
// a contiguous range of memory.
#ifndef COMMON_MEMORY_RANGE_H_
#define COMMON_MEMORY_RANGE_H_
#include <stddef.h>
#include "google_breakpad/common/breakpad_types.h"
namespace google_breakpad {
// A lightweight wrapper with a pointer and a length to encapsulate a
// contiguous range of memory. It provides helper methods for checked
// access of a subrange of the memory. Its implemementation does not
// allocate memory or call into libc functions, and is thus safer to use
// in a crashed environment.
class MemoryRange {
public:
MemoryRange() : data_(NULL), length_(0) {}
MemoryRange(const void* data, size_t length) {
Set(data, length);
}
// Returns true if this memory range contains no data.
bool IsEmpty() const {
// Set() guarantees that |length_| is zero if |data_| is NULL.
return length_ == 0;
}
// Resets to an empty range.
void Reset() {
data_ = NULL;
length_ = 0;
}
// Sets this memory range to point to |data| and its length to |length|.
void Set(const void* data, size_t length) {
data_ = reinterpret_cast<const u_int8_t*>(data);
// Always set |length_| to zero if |data_| is NULL.
length_ = data ? length : 0;
}
// Returns true if this range covers a subrange of |sub_length| bytes
// at |sub_offset| bytes of this memory range, or false otherwise.
bool Covers(size_t sub_offset, size_t sub_length) const {
// The following checks verify that:
// 1. sub_offset is within [ 0 .. length_ - 1 ]
// 2. sub_offset + sub_length is within
// [ sub_offset .. length_ ]
return sub_offset < length_ &&
sub_offset + sub_length >= sub_offset &&
sub_offset + sub_length <= length_;
}
// Returns a raw data pointer to a subrange of |sub_length| bytes at
// |sub_offset| bytes of this memory range, or NULL if the subrange
// is out of bounds.
const void* GetData(size_t sub_offset, size_t sub_length) const {
return Covers(sub_offset, sub_length) ? (data_ + sub_offset) : NULL;
}
// Same as the two-argument version of GetData() but uses sizeof(DataType)
// as the subrange length and returns an |DataType| pointer for convenience.
template <typename DataType>
const DataType* GetData(size_t sub_offset) const {
return reinterpret_cast<const DataType*>(
GetData(sub_offset, sizeof(DataType)));
}
// Returns a raw pointer to the |element_index|-th element of an array
// of elements of length |element_size| starting at |sub_offset| bytes
// of this memory range, or NULL if the element is out of bounds.
const void* GetArrayElement(size_t element_offset,
size_t element_size,
unsigned element_index) const {
size_t sub_offset = element_offset + element_index * element_size;
return GetData(sub_offset, element_size);
}
// Same as the three-argument version of GetArrayElement() but deduces
// the element size using sizeof(ElementType) and returns an |ElementType|
// pointer for convenience.
template <typename ElementType>
const ElementType* GetArrayElement(size_t element_offset,
unsigned element_index) const {
return reinterpret_cast<const ElementType*>(
GetArrayElement(element_offset, sizeof(ElementType), element_index));
}
// Returns a subrange of |sub_length| bytes at |sub_offset| bytes of
// this memory range, or an empty range if the subrange is out of bounds.
MemoryRange Subrange(size_t sub_offset, size_t sub_length) const {
return Covers(sub_offset, sub_length) ?
MemoryRange(data_ + sub_offset, sub_length) : MemoryRange();
}
// Returns a pointer to the beginning of this memory range.
const u_int8_t* data() const { return data_; }
// Returns the length, in bytes, of this memory range.
size_t length() const { return length_; }
private:
// Pointer to the beginning of this memory range.
const u_int8_t* data_;
// Length, in bytes, of this memory range.
size_t length_;
};
} // namespace google_breakpad
#endif // COMMON_MEMORY_RANGE_H_

View File

@@ -0,0 +1,193 @@
// Copyright (c) 2011, 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.
// memory_range_unittest.cc: Unit tests for google_breakpad::MemoryRange.
#include "breakpad_googletest_includes.h"
#include "common/memory_range.h"
using google_breakpad::MemoryRange;
using testing::Message;
namespace {
const u_int32_t kBuffer[10] = { 0 };
const size_t kBufferSize = sizeof(kBuffer);
const u_int8_t* kBufferPointer = reinterpret_cast<const u_int8_t*>(kBuffer);
// Test vectors for verifying Covers, GetData, and Subrange.
const struct {
bool valid;
size_t offset;
size_t length;
} kSubranges[] = {
{ true, 0, 0 },
{ true, 0, 2 },
{ true, 0, kBufferSize },
{ true, 2, 0 },
{ true, 2, 4 },
{ true, 2, kBufferSize - 2 },
{ true, kBufferSize - 1, 1 },
{ false, kBufferSize, 0 },
{ false, kBufferSize, -1 },
{ false, kBufferSize + 1, 0 },
{ false, -1, 2 },
{ false, 1, kBufferSize },
{ false, kBufferSize - 1, 2 },
{ false, 0, -1 },
{ false, 1, -1 },
};
const size_t kNumSubranges = sizeof(kSubranges) / sizeof(kSubranges[0]);
// Test vectors for verifying GetArrayElement.
const struct {
size_t offset;
size_t size;
size_t index;
const void* const pointer;
} kElements[] = {
// Valid array elemenets
{ 0, 1, 0, kBufferPointer },
{ 0, 1, 1, kBufferPointer + 1 },
{ 0, 1, kBufferSize - 1, kBufferPointer + kBufferSize - 1 },
{ 0, 2, 1, kBufferPointer + 2 },
{ 0, 4, 2, kBufferPointer + 8 },
{ 0, 4, 9, kBufferPointer + 36 },
{ kBufferSize - 1, 1, 0, kBufferPointer + kBufferSize - 1 },
// Invalid array elemenets
{ 0, 1, kBufferSize, NULL },
{ 0, 4, 10, NULL },
{ kBufferSize - 1, 1, 1, NULL },
{ kBufferSize - 1, 2, 0, NULL },
{ kBufferSize, 1, 0, NULL },
};
const size_t kNumElements = sizeof(kElements) / sizeof(kElements[0]);
} // namespace
TEST(MemoryRangeTest, DefaultConstructor) {
MemoryRange range;
EXPECT_EQ(NULL, range.data());
EXPECT_EQ(0, range.length());
}
TEST(MemoryRangeTest, ConstructorWithDataAndLength) {
MemoryRange range(kBuffer, kBufferSize);
EXPECT_EQ(kBufferPointer, range.data());
EXPECT_EQ(kBufferSize, range.length());
}
TEST(MemoryRangeTest, Reset) {
MemoryRange range;
range.Reset();
EXPECT_EQ(NULL, range.data());
EXPECT_EQ(0, range.length());
range.Set(kBuffer, kBufferSize);
EXPECT_EQ(kBufferPointer, range.data());
EXPECT_EQ(kBufferSize, range.length());
range.Reset();
EXPECT_EQ(NULL, range.data());
EXPECT_EQ(0, range.length());
}
TEST(MemoryRangeTest, Set) {
MemoryRange range;
range.Set(kBuffer, kBufferSize);
EXPECT_EQ(kBufferPointer, range.data());
EXPECT_EQ(kBufferSize, range.length());
range.Set(NULL, 0);
EXPECT_EQ(NULL, range.data());
EXPECT_EQ(0, range.length());
}
TEST(MemoryRangeTest, SubrangeOfEmptyMemoryRange) {
MemoryRange range;
MemoryRange subrange = range.Subrange(0, 10);
EXPECT_EQ(NULL, subrange.data());
EXPECT_EQ(0, subrange.length());
}
TEST(MemoryRangeTest, SubrangeAndGetData) {
MemoryRange range(kBuffer, kBufferSize);
for (size_t i = 0; i < kNumSubranges; ++i) {
bool valid = kSubranges[i].valid;
size_t sub_offset = kSubranges[i].offset;
size_t sub_length = kSubranges[i].length;
SCOPED_TRACE(Message() << "offset=" << sub_offset
<< ", length=" << sub_length);
MemoryRange subrange = range.Subrange(sub_offset, sub_length);
if (valid) {
EXPECT_TRUE(range.Covers(sub_offset, sub_length));
EXPECT_EQ(kBufferPointer + sub_offset,
range.GetData(sub_offset, sub_length));
EXPECT_EQ(kBufferPointer + sub_offset, subrange.data());
EXPECT_EQ(sub_length, subrange.length());
} else {
EXPECT_FALSE(range.Covers(sub_offset, sub_length));
EXPECT_EQ(NULL, range.GetData(sub_offset, sub_length));
EXPECT_EQ(NULL, subrange.data());
EXPECT_EQ(0, subrange.length());
}
}
}
TEST(MemoryRangeTest, GetDataWithTemplateType) {
MemoryRange range(kBuffer, kBufferSize);
const char* char_pointer = range.GetData<char>(0);
EXPECT_EQ(reinterpret_cast<const char*>(kBufferPointer), char_pointer);
const int* int_pointer = range.GetData<int>(0);
EXPECT_EQ(reinterpret_cast<const int*>(kBufferPointer), int_pointer);
}
TEST(MemoryRangeTest, GetArrayElement) {
MemoryRange range(kBuffer, kBufferSize);
for (size_t i = 0; i < kNumElements; ++i) {
size_t element_offset = kElements[i].offset;
size_t element_size = kElements[i].size;
unsigned element_index = kElements[i].index;
const void* const element_pointer = kElements[i].pointer;
SCOPED_TRACE(Message() << "offset=" << element_offset
<< ", size=" << element_size
<< ", index=" << element_index);
EXPECT_EQ(element_pointer, range.GetArrayElement(
element_offset, element_size, element_index));
}
}
TEST(MemoryRangeTest, GetArrayElmentWithTemplateType) {
MemoryRange range(kBuffer, kBufferSize);
const char* char_pointer = range.GetArrayElement<char>(0, 0);
EXPECT_EQ(reinterpret_cast<const char*>(kBufferPointer), char_pointer);
const int* int_pointer = range.GetArrayElement<int>(0, 0);
EXPECT_EQ(reinterpret_cast<const int*>(kBufferPointer), int_pointer);
}

View File

@@ -69,6 +69,7 @@ typedef testing::Test WastefulVectorTest;
TEST(WastefulVectorTest, Setup) {
PageAllocator allocator_;
wasteful_vector<int> v(&allocator_);
ASSERT_TRUE(v.empty());
ASSERT_EQ(v.size(), 0u);
}
@@ -76,8 +77,12 @@ TEST(WastefulVectorTest, Simple) {
PageAllocator allocator_;
wasteful_vector<unsigned> v(&allocator_);
for (unsigned i = 0; i < 256; ++i)
for (unsigned i = 0; i < 256; ++i) {
v.push_back(i);
ASSERT_EQ(i, v.back());
ASSERT_EQ(&v.back(), &v[i]);
}
ASSERT_FALSE(v.empty());
ASSERT_EQ(v.size(), 256u);
for (unsigned i = 0; i < 256; ++i)
ASSERT_EQ(v[i], i);

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2010 Google Inc.
// Copyright (c) 2011 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -39,6 +39,7 @@
#include <string.h>
#include <iostream>
#include <utility>
namespace google_breakpad {
@@ -56,16 +57,17 @@ Module::Module(const string &name, const string &os,
load_address_(0) { }
Module::~Module() {
for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); it++)
for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it)
delete it->second;
for (FunctionSet::iterator it = functions_.begin();
it != functions_.end(); it++)
it != functions_.end(); ++it) {
delete *it;
}
for (vector<StackFrameEntry *>::iterator it = stack_frame_entries_.begin();
it != stack_frame_entries_.end(); it++)
it != stack_frame_entries_.end(); ++it) {
delete *it;
for (ExternSet::iterator it = externs_.begin();
it != externs_.end(); it++)
}
for (ExternSet::iterator it = externs_.begin(); it != externs_.end(); ++it)
delete *it;
}
@@ -87,7 +89,7 @@ void Module::AddFunction(Function *function) {
void Module::AddFunctions(vector<Function *>::iterator begin,
vector<Function *>::iterator end) {
for (vector<Function *>::iterator it = begin; it != end; it++)
for (vector<Function *>::iterator it = begin; it != end; ++it)
AddFunction(*it);
}
@@ -149,7 +151,7 @@ Module::File *Module::FindExistingFile(const string &name) {
void Module::GetFiles(vector<File *> *vec) {
vec->clear();
for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); it++)
for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it)
vec->push_back(it->second);
}
@@ -160,16 +162,17 @@ void Module::GetStackFrameEntries(vector<StackFrameEntry *> *vec) {
void Module::AssignSourceIds() {
// First, give every source file an id of -1.
for (FileByNameMap::iterator file_it = files_.begin();
file_it != files_.end(); file_it++)
file_it != files_.end(); ++file_it) {
file_it->second->source_id = -1;
}
// Next, mark all files actually cited by our functions' line number
// info, by setting each one's source id to zero.
for (FunctionSet::const_iterator func_it = functions_.begin();
func_it != functions_.end(); func_it++) {
func_it != functions_.end(); ++func_it) {
Function *func = *func_it;
for (vector<Line>::iterator line_it = func->lines.begin();
line_it != func->lines.end(); line_it++)
line_it != func->lines.end(); ++line_it)
line_it->file->source_id = 0;
}
@@ -179,9 +182,10 @@ void Module::AssignSourceIds() {
// lexicographical order by name, which is neat.
int next_source_id = 0;
for (FileByNameMap::iterator file_it = files_.begin();
file_it != files_.end(); file_it++)
file_it != files_.end(); ++file_it) {
if (!file_it->second->source_id)
file_it->second->source_id = next_source_id++;
}
}
bool Module::ReportError() {
@@ -192,7 +196,7 @@ bool Module::ReportError() {
bool Module::WriteRuleMap(const RuleMap &rule_map, std::ostream &stream) {
for (RuleMap::const_iterator it = rule_map.begin();
it != rule_map.end(); it++) {
it != rule_map.end(); ++it) {
if (it != rule_map.begin())
stream << ' ';
stream << it->first << ": " << it->second;
@@ -200,7 +204,7 @@ bool Module::WriteRuleMap(const RuleMap &rule_map, std::ostream &stream) {
return stream.good();
}
bool Module::Write(std::ostream &stream) {
bool Module::Write(std::ostream &stream, bool cfi) {
stream << "MODULE " << os_ << " " << architecture_ << " "
<< id_ << " " << name_ << endl;
if (!stream.good())
@@ -210,7 +214,7 @@ bool Module::Write(std::ostream &stream) {
// Write out files.
for (FileByNameMap::iterator file_it = files_.begin();
file_it != files_.end(); file_it++) {
file_it != files_.end(); ++file_it) {
File *file = file_it->second;
if (file->source_id >= 0) {
stream << "FILE " << file->source_id << " " << file->name << endl;
@@ -221,18 +225,18 @@ bool Module::Write(std::ostream &stream) {
// Write out functions and their lines.
for (FunctionSet::const_iterator func_it = functions_.begin();
func_it != functions_.end(); func_it++) {
func_it != functions_.end(); ++func_it) {
Function *func = *func_it;
stream << "FUNC " << hex
<< (func->address - load_address_) << " "
<< func->size << " "
<< func->parameter_size << " "
<< func->name << dec << endl;
if (!stream.good())
return ReportError();
for (vector<Line>::iterator line_it = func->lines.begin();
line_it != func->lines.end(); line_it++) {
line_it != func->lines.end(); ++line_it) {
stream << hex
<< (line_it->address - load_address_) << " "
<< line_it->size << " "
@@ -246,7 +250,7 @@ bool Module::Write(std::ostream &stream) {
// Write out 'PUBLIC' records.
for (ExternSet::const_iterator extern_it = externs_.begin();
extern_it != externs_.end(); extern_it++) {
extern_it != externs_.end(); ++extern_it) {
Extern *ext = *extern_it;
stream << "PUBLIC " << hex
<< (ext->address - load_address_) << " 0 "
@@ -255,34 +259,36 @@ bool Module::Write(std::ostream &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;
stream << "STACK CFI INIT " << hex
<< (entry->address - load_address_) << " "
<< entry->size << " " << dec;
if (!stream.good()
|| !WriteRuleMap(entry->initial_rules, stream))
return ReportError();
stream << endl;
// 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++) {
stream << "STACK CFI " << hex
<< (delta_it->first - load_address_) << " " << dec;
if (cfi) {
// 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;
stream << "STACK CFI INIT " << hex
<< (entry->address - load_address_) << " "
<< entry->size << " " << dec;
if (!stream.good()
|| !WriteRuleMap(delta_it->second, stream))
|| !WriteRuleMap(entry->initial_rules, stream))
return ReportError();
stream << endl;
// 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) {
stream << "STACK CFI " << hex
<< (delta_it->first - load_address_) << " " << dec;
if (!stream.good()
|| !WriteRuleMap(delta_it->second, stream))
return ReportError();
stream << endl;
}
}
}
return true;
}
} // namespace google_breakpad
} // namespace google_breakpad

View File

@@ -259,14 +259,15 @@ class Module {
// breakpad symbol format. Return true if all goes well, or false if
// an error occurs. This method writes out:
// - a header based on the values given to the constructor,
// - the source files added via FindFile, and finally
// - the functions added via AddFunctions, each with its lines.
// - the source files added via FindFile,
// - the functions added via AddFunctions, each with its lines,
// - all public records,
// - and if CFI is true, all CFI records.
// Addresses in the output are all relative to the load address
// established by SetLoadAddress.
bool Write(std::ostream &stream);
bool Write(std::ostream &stream, bool cfi);
private:
// Report an error that has occurred writing the symbol file, using
// errno to find the appropriate cause. Return false.
static bool ReportError();
@@ -287,7 +288,7 @@ class Module {
// Relation for maps whose keys are strings shared with some other
// structure.
struct CompareStringPtrs {
bool operator()(const string *x, const string *y) { return *x < *y; };
bool operator()(const string *x, const string *y) { return *x < *y; }
};
// A map from filenames to File structures. The map's keys are
@@ -315,6 +316,6 @@ class Module {
ExternSet externs_;
};
} // namespace google_breakpad
} // namespace google_breakpad
#endif // COMMON_LINUX_MODULE_H__

View File

@@ -70,7 +70,7 @@ static Module::Function *generate_duplicate_function(const string &name) {
TEST(Write, Header) {
stringstream s;
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
m.Write(s);
m.Write(s, true);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n",
contents.c_str());
@@ -91,7 +91,7 @@ TEST(Write, OneLineFunc) {
function->lines.push_back(line);
m.AddFunction(function);
m.Write(s);
m.Write(s, true);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FILE 0 file_name.cc\n"
@@ -141,7 +141,7 @@ TEST(Write, RelativeLoadAddress) {
// the module must work fine.
m.SetLoadAddress(0x2ab698b0b6407073LL);
m.Write(s);
m.Write(s, true);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FILE 0 filename-a.cc\n"
@@ -164,7 +164,7 @@ TEST(Write, OmitUnusedFiles) {
// Create some source files.
Module::File *file1 = m.FindFile("filename1");
m.FindFile("filename2"); // not used by any line
m.FindFile("filename2"); // not used by any line
Module::File *file3 = m.FindFile("filename3");
// Create a function.
@@ -197,7 +197,7 @@ TEST(Write, OmitUnusedFiles) {
EXPECT_NE(-1, vec[2]->source_id);
stringstream s;
m.Write(s);
m.Write(s, true);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FILE 0 filename1\n"
@@ -209,6 +209,52 @@ TEST(Write, OmitUnusedFiles) {
contents.c_str());
}
TEST(Write, NoCFI) {
stringstream s;
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
// Some source files. We will expect to see them in lexicographic order.
Module::File *file1 = m.FindFile("filename.cc");
// A function.
Module::Function *function = new(Module::Function);
function->name = "A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)";
function->address = 0xbec774ea5dd935f3LL;
function->size = 0x2922088f98d3f6fcLL;
function->parameter_size = 0xe5e9aa008bd5f0d0LL;
// Some source lines. The module should not sort these.
Module::Line line1 = { 0xbec774ea5dd935f3LL, 0x1c2be6d6c5af2611LL,
file1, 41676901 };
function->lines.push_back(line1);
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);
// Set the load address. Doing this after adding all the data to
// the module must work fine.
m.SetLoadAddress(0x2ab698b0b6407073LL);
m.Write(s, false);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FILE 0 filename.cc\n"
"FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0"
" A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n"
"9410dc39a798c580 1c2be6d6c5af2611 41676901 0\n",
contents.c_str());
}
TEST(Construct, AddFunctions) {
stringstream s;
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
@@ -233,7 +279,7 @@ TEST(Construct, AddFunctions) {
m.AddFunctions(vec.begin(), vec.end());
m.Write(s);
m.Write(s, true);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FUNC 2987743d0b35b13f b369db048deb3010 938e556cb5a79988"
@@ -285,7 +331,7 @@ TEST(Construct, AddFrames) {
m.AddStackFrameEntry(entry3);
// Check that Write writes STACK CFI records properly.
m.Write(s);
m.Write(s, true);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n"
@@ -361,7 +407,7 @@ TEST(Construct, DuplicateFunctions) {
m.AddFunction(function1);
m.AddFunction(function2);
m.Write(s);
m.Write(s, true);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99"
@@ -380,7 +426,7 @@ TEST(Construct, FunctionsWithSameAddress) {
m.AddFunction(function1);
m.AddFunction(function2);
m.Write(s);
m.Write(s, true);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99"
@@ -407,7 +453,7 @@ TEST(Construct, Externs) {
m.AddExtern(extern1);
m.AddExtern(extern2);
m.Write(s);
m.Write(s, true);
string contents = s.str();
EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " "
@@ -434,7 +480,7 @@ TEST(Construct, DuplicateExterns) {
m.AddExtern(extern1);
m.AddExtern(extern2);
m.Write(s);
m.Write(s, true);
string contents = s.str();
EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " "

View File

@@ -130,7 +130,7 @@ bool StabsReader::ProcessCompilationUnit() {
// There may be an N_SO entry whose name ends with a slash,
// indicating the directory in which the compilation occurred.
// The build directory defaults to NULL.
const char *build_directory = NULL;
const char *build_directory = NULL;
{
const char *name = SymbolString();
if (name[0] && name[strlen(name) - 1] == '/') {
@@ -138,7 +138,7 @@ bool StabsReader::ProcessCompilationUnit() {
++iterator_;
}
}
// We expect to see an N_SO entry with a filename next, indicating
// the start of the compilation unit.
{
@@ -212,7 +212,7 @@ bool StabsReader::ProcessCompilationUnit() {
queued_lines_.clear();
return true;
}
}
bool StabsReader::ProcessFunction() {
assert(!iterator_->at_end && iterator_->type == N_FUN);
@@ -237,7 +237,7 @@ bool StabsReader::ProcessFunction() {
return false;
}
queued_lines_.clear();
while (!iterator_->at_end) {
if (iterator_->type == N_SO || iterator_->type == N_FUN)
break;
@@ -266,8 +266,8 @@ bool StabsReader::ProcessFunction() {
if (!iterator_->at_end) {
assert(iterator_->type == N_SO || iterator_->type == N_FUN);
if (iterator_->type == N_FUN) {
const char *name = SymbolString();
if (name[0] == '\0') {
const char *symbol_name = SymbolString();
if (symbol_name[0] == '\0') {
// An N_FUN entry with no name is a terminator for this function;
// its value is the function's size.
ending_address = function_address + iterator_->value;

View File

@@ -42,7 +42,7 @@ void UTF8ToUTF16(const char *in, vector<u_int16_t> *out) {
const UTF8 *source_ptr = reinterpret_cast<const UTF8 *>(in);
const UTF8 *source_end_ptr = source_ptr + source_length;
// Erase the contents and zero fill to the expected size
out->empty();
out->clear();
out->insert(out->begin(), source_length, 0);
u_int16_t *target_ptr = &(*out)[0];
u_int16_t *target_end_ptr = target_ptr + out->capacity() * sizeof(u_int16_t);
@@ -86,7 +86,7 @@ void UTF32ToUTF16(const wchar_t *in, vector<u_int16_t> *out) {
const UTF32 *source_ptr = reinterpret_cast<const UTF32 *>(in);
const UTF32 *source_end_ptr = source_ptr + source_length;
// Erase the contents and zero fill to the expected size
out->empty();
out->clear();
out->insert(out->begin(), source_length, 0);
u_int16_t *target_ptr = &(*out)[0];
u_int16_t *target_end_ptr = target_ptr + out->capacity() * sizeof(u_int16_t);

View File

@@ -32,7 +32,7 @@
#ifndef COMMON_WINDOWS_GUID_STRING_H__
#define COMMON_WINDOWS_GUID_STRING_H__
#include <guiddef.h>
#include <Guiddef.h>
#include <string>

View File

@@ -92,13 +92,13 @@ bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) {
switch (format) {
case PDB_FILE:
if (FAILED(data_source->loadDataFromPdb(file.c_str()))) {
fprintf(stderr, "loadDataFromPdb failed\n");
fprintf(stderr, "loadDataFromPdb failed for %ws\n", file.c_str());
return false;
}
break;
case EXE_FILE:
if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) {
fprintf(stderr, "loadDataForExe failed\n");
fprintf(stderr, "loadDataForExe failed for %ws\n", file.c_str());
return false;
}
code_file_ = file;
@@ -106,7 +106,7 @@ bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) {
case ANY_FILE:
if (FAILED(data_source->loadDataFromPdb(file.c_str()))) {
if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) {
fprintf(stderr, "loadDataForPdb and loadDataFromExe failed\n");
fprintf(stderr, "loadDataForPdb and loadDataFromExe failed for %ws\n", file.c_str());
return false;
}
code_file_ = file;