Basic handling of CIE version 4 in dwarf reading
CIE looks like it's been emitted by clang since ~May 2015 [1]. This means that we didn't have any CFI because this parse aborted, which meant that all stack walks reverted to stack scanning. Allow expected values for address size and segment descriptor size through so that dump_syms can generate at least somewhat reasonable data. [1]: http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20150518/277292.html R=mark@chromium.org BUG=chromium:627529 Change-Id: I6dc92f51c4afd25c2adff92c09ccb8bb03bf9112 Reviewed-on: https://chromium-review.googlesource.com/406012 Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
13c634f6a1
commit
bbebd8d5e7
@ -46,7 +46,9 @@ CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor,
|
|||||||
unsigned return_address_register,
|
unsigned return_address_register,
|
||||||
uint8_t version,
|
uint8_t version,
|
||||||
const string &augmentation,
|
const string &augmentation,
|
||||||
bool dwarf64) {
|
bool dwarf64,
|
||||||
|
uint8_t address_size,
|
||||||
|
uint8_t segment_size) {
|
||||||
assert(!entry_length_);
|
assert(!entry_length_);
|
||||||
entry_length_ = new PendingLength();
|
entry_length_ = new PendingLength();
|
||||||
in_fde_ = false;
|
in_fde_ = false;
|
||||||
@ -63,6 +65,10 @@ CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor,
|
|||||||
}
|
}
|
||||||
D8(version);
|
D8(version);
|
||||||
AppendCString(augmentation);
|
AppendCString(augmentation);
|
||||||
|
if (version >= 4) {
|
||||||
|
D8(address_size);
|
||||||
|
D8(segment_size);
|
||||||
|
}
|
||||||
ULEB128(code_alignment_factor);
|
ULEB128(code_alignment_factor);
|
||||||
LEB128(data_alignment_factor);
|
LEB128(data_alignment_factor);
|
||||||
if (version == 1)
|
if (version == 1)
|
||||||
|
@ -138,7 +138,9 @@ class CFISection: public Section {
|
|||||||
unsigned return_address_register,
|
unsigned return_address_register,
|
||||||
uint8_t version = 3,
|
uint8_t version = 3,
|
||||||
const string &augmentation = "",
|
const string &augmentation = "",
|
||||||
bool dwarf64 = false);
|
bool dwarf64 = false,
|
||||||
|
uint8_t address_size = 8,
|
||||||
|
uint8_t segment_size = 0);
|
||||||
|
|
||||||
// Append a Frame Description Entry header to this section with the
|
// Append a Frame Description Entry header to this section with the
|
||||||
// given values. If dwarf64 is true, use the 64-bit DWARF initial
|
// given values. If dwarf64 is true, use the 64-bit DWARF initial
|
||||||
|
@ -2253,11 +2253,11 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
|||||||
cursor++;
|
cursor++;
|
||||||
|
|
||||||
// If we don't recognize the version, we can't parse any more fields of the
|
// 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
|
// CIE. For DWARF CFI, we handle versions 1 through 4 (there was never a
|
||||||
// version 2 of CFI data). For .eh_frame, we handle versions 1 and 3 as well;
|
// version 2 of CFI data). For .eh_frame, we handle versions 1 and 4 as well;
|
||||||
// the difference between those versions seems to be the same as for
|
// the difference between those versions seems to be the same as for
|
||||||
// .debug_frame.
|
// .debug_frame.
|
||||||
if (cie->version < 1 || cie->version > 3) {
|
if (cie->version < 1 || cie->version > 4) {
|
||||||
reporter_->UnrecognizedVersion(cie->offset, cie->version);
|
reporter_->UnrecognizedVersion(cie->offset, cie->version);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2287,16 +2287,36 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cie->version >= 4) {
|
||||||
|
uint8_t address_size = *cursor++;
|
||||||
|
if (address_size != 8) {
|
||||||
|
// TODO(scottmg): Only supporting x64 for now.
|
||||||
|
reporter_->UnexpectedAddressSize(cie->offset, address_size);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t segment_size = *cursor++;
|
||||||
|
if (segment_size != 0) {
|
||||||
|
// TODO(scottmg): Only supporting x64 for now.
|
||||||
|
// I would have perhaps expected 4 here, but LLVM emits a 0, near
|
||||||
|
// http://llvm.org/docs/doxygen/html/MCDwarf_8cpp_source.html#l00606. As
|
||||||
|
// we are not using the value, only succeed for now if it's the expected
|
||||||
|
// 0.
|
||||||
|
reporter_->UnexpectedSegmentSize(cie->offset, segment_size);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the code alignment factor.
|
// Parse the code alignment factor.
|
||||||
cie->code_alignment_factor = reader_->ReadUnsignedLEB128(cursor, &len);
|
cie->code_alignment_factor = reader_->ReadUnsignedLEB128(cursor, &len);
|
||||||
if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
|
if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
|
||||||
cursor += len;
|
cursor += len;
|
||||||
|
|
||||||
// Parse the data alignment factor.
|
// Parse the data alignment factor.
|
||||||
cie->data_alignment_factor = reader_->ReadSignedLEB128(cursor, &len);
|
cie->data_alignment_factor = reader_->ReadSignedLEB128(cursor, &len);
|
||||||
if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
|
if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
|
||||||
cursor += len;
|
cursor += len;
|
||||||
|
|
||||||
// Parse the return address register. This is a ubyte in version 1, and
|
// Parse the return address register. This is a ubyte in version 1, and
|
||||||
// a ULEB128 in version 3.
|
// a ULEB128 in version 3.
|
||||||
if (cie->version == 1) {
|
if (cie->version == 1) {
|
||||||
@ -2407,7 +2427,7 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CallFrameInfo::ReadFDEFields(FDE *fde) {
|
bool CallFrameInfo::ReadFDEFields(FDE *fde) {
|
||||||
const uint8_t *cursor = fde->fields;
|
const uint8_t *cursor = fde->fields;
|
||||||
size_t size;
|
size_t size;
|
||||||
@ -2648,6 +2668,22 @@ void CallFrameInfo::Reporter::BadCIEId(uint64 offset, uint64 cie_offset) {
|
|||||||
filename_.c_str(), offset, section_.c_str(), cie_offset);
|
filename_.c_str(), offset, section_.c_str(), cie_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CallFrameInfo::Reporter::UnexpectedAddressSize(uint64 offset,
|
||||||
|
uint8_t address_size) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s: CFI frame description entry at offset 0x%llx in '%s':"
|
||||||
|
" CIE specifies unexpected address size: %d\n",
|
||||||
|
filename_.c_str(), offset, section_.c_str(), address_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallFrameInfo::Reporter::UnexpectedSegmentSize(uint64 offset,
|
||||||
|
uint8_t segment_size) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s: CFI frame description entry at offset 0x%llx in '%s':"
|
||||||
|
" CIE specifies unexpected segment size: %d\n",
|
||||||
|
filename_.c_str(), offset, section_.c_str(), segment_size);
|
||||||
|
}
|
||||||
|
|
||||||
void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) {
|
void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"%s: CFI frame description entry at offset 0x%llx in '%s':"
|
"%s: CFI frame description entry at offset 0x%llx in '%s':"
|
||||||
|
@ -1227,6 +1227,14 @@ class CallFrameInfo::Reporter {
|
|||||||
// there is not a CIE.
|
// there is not a CIE.
|
||||||
virtual void BadCIEId(uint64 offset, uint64 cie_offset);
|
virtual void BadCIEId(uint64 offset, uint64 cie_offset);
|
||||||
|
|
||||||
|
// The FDE at OFFSET refers to a CIE with an address size we don't know how
|
||||||
|
// to handle.
|
||||||
|
virtual void UnexpectedAddressSize(uint64 offset, uint8_t address_size);
|
||||||
|
|
||||||
|
// The FDE at OFFSET refers to a CIE with an segment descriptor size we
|
||||||
|
// don't know how to handle.
|
||||||
|
virtual void UnexpectedSegmentSize(uint64 offset, uint8_t segment_size);
|
||||||
|
|
||||||
// The FDE at OFFSET refers to a CIE with version number VERSION,
|
// The FDE at OFFSET refers to a CIE with version number VERSION,
|
||||||
// which we don't recognize. We cannot parse DWARF CFI if it uses
|
// which we don't recognize. We cannot parse DWARF CFI if it uses
|
||||||
// a version number we don't recognize.
|
// a version number we don't recognize.
|
||||||
|
@ -126,6 +126,8 @@ class MockCallFrameErrorReporter: public CallFrameInfo::Reporter {
|
|||||||
MOCK_METHOD1(EarlyEHTerminator, void(uint64));
|
MOCK_METHOD1(EarlyEHTerminator, void(uint64));
|
||||||
MOCK_METHOD2(CIEPointerOutOfRange, void(uint64, uint64));
|
MOCK_METHOD2(CIEPointerOutOfRange, void(uint64, uint64));
|
||||||
MOCK_METHOD2(BadCIEId, void(uint64, uint64));
|
MOCK_METHOD2(BadCIEId, void(uint64, uint64));
|
||||||
|
MOCK_METHOD2(UnexpectedAddressSize, void(uint64, uint8_t));
|
||||||
|
MOCK_METHOD2(UnexpectedSegmentSize, void(uint64, uint8_t));
|
||||||
MOCK_METHOD2(UnrecognizedVersion, void(uint64, int version));
|
MOCK_METHOD2(UnrecognizedVersion, void(uint64, int version));
|
||||||
MOCK_METHOD2(UnrecognizedAugmentation, void(uint64, const string &));
|
MOCK_METHOD2(UnrecognizedAugmentation, void(uint64, const string &));
|
||||||
MOCK_METHOD2(InvalidPointerEncoding, void(uint64, uint8));
|
MOCK_METHOD2(InvalidPointerEncoding, void(uint64, uint8));
|
||||||
@ -605,6 +607,91 @@ TEST_F(CFI, CIEVersion3ReturnColumn) {
|
|||||||
EXPECT_TRUE(parser.Start());
|
EXPECT_TRUE(parser.Start());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(CFI, CIEVersion4AdditionalFields) {
|
||||||
|
CFISection section(kBigEndian, 4);
|
||||||
|
Label cie;
|
||||||
|
section
|
||||||
|
.Mark(&cie)
|
||||||
|
// CIE version 4 with expected address and segment size.
|
||||||
|
.CIEHeader(0x0ab4758d, 0xc010fdf7, 0x89, 4, "", true, 8, 0)
|
||||||
|
.FinishEntry()
|
||||||
|
// FDE, citing that CIE.
|
||||||
|
.FDEHeader(cie, 0x86763f2b, 0x2a66dc23)
|
||||||
|
.FinishEntry();
|
||||||
|
|
||||||
|
PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion3ReturnColumn", section);
|
||||||
|
|
||||||
|
{
|
||||||
|
InSequence s;
|
||||||
|
EXPECT_CALL(handler, Entry(_, 0x86763f2b, 0x2a66dc23, 4, "", 0x89))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(handler, End()).WillOnce(Return(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
string contents;
|
||||||
|
EXPECT_TRUE(section.GetContents(&contents));
|
||||||
|
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||||
|
byte_reader.SetAddressSize(4);
|
||||||
|
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||||
|
contents.size(),
|
||||||
|
&byte_reader, &handler, &reporter);
|
||||||
|
EXPECT_TRUE(parser.Start());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CFI, CIEVersion4AdditionalFieldsUnexpectedAddressSize) {
|
||||||
|
CFISection section(kBigEndian, 4);
|
||||||
|
Label cie;
|
||||||
|
|
||||||
|
section
|
||||||
|
.Mark(&cie)
|
||||||
|
// Unexpected address size.
|
||||||
|
.CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 4, "", true, 3, 0)
|
||||||
|
.FinishEntry()
|
||||||
|
// FDE, citing that CIE.
|
||||||
|
.FDEHeader(cie, 0x86763f2b, 0x2a66dc23)
|
||||||
|
.FinishEntry();
|
||||||
|
|
||||||
|
PERHAPS_WRITE_DEBUG_FRAME_FILE("AdditionalFieldsUnexpectedAddress", section);
|
||||||
|
|
||||||
|
EXPECT_CALL(reporter, UnexpectedAddressSize(_, 3))
|
||||||
|
.WillOnce(Return());
|
||||||
|
|
||||||
|
string contents;
|
||||||
|
EXPECT_TRUE(section.GetContents(&contents));
|
||||||
|
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||||
|
byte_reader.SetAddressSize(8);
|
||||||
|
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||||
|
contents.size(),
|
||||||
|
&byte_reader, &handler, &reporter);
|
||||||
|
EXPECT_FALSE(parser.Start());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CFI, CIEVersion4AdditionalFieldsUnexpectedSegmentSize) {
|
||||||
|
CFISection section(kBigEndian, 4);
|
||||||
|
Label cie;
|
||||||
|
|
||||||
|
section
|
||||||
|
.Mark(&cie)
|
||||||
|
.CIEHeader(0xf8bc4399, 0x8cf09931, 0xf2f519b2, 4, "", true, 8, 7)
|
||||||
|
.FinishEntry()
|
||||||
|
.FDEHeader(cie, 0x7bf0fda0, 0xcbcd28d8)
|
||||||
|
.FinishEntry();
|
||||||
|
|
||||||
|
PERHAPS_WRITE_DEBUG_FRAME_FILE("AdditionalFieldsUnexpectedSegment", section);
|
||||||
|
|
||||||
|
EXPECT_CALL(reporter, UnexpectedSegmentSize(_, 7))
|
||||||
|
.WillOnce(Return());
|
||||||
|
|
||||||
|
string contents;
|
||||||
|
EXPECT_TRUE(section.GetContents(&contents));
|
||||||
|
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||||
|
byte_reader.SetAddressSize(8);
|
||||||
|
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||||
|
contents.size(),
|
||||||
|
&byte_reader, &handler, &reporter);
|
||||||
|
EXPECT_FALSE(parser.Start());
|
||||||
|
}
|
||||||
|
|
||||||
struct CFIInsnFixture: public CFIFixture {
|
struct CFIInsnFixture: public CFIFixture {
|
||||||
CFIInsnFixture() : CFIFixture() {
|
CFIInsnFixture() : CFIFixture() {
|
||||||
data_factor = 0xb6f;
|
data_factor = 0xb6f;
|
||||||
|
Loading…
Reference in New Issue
Block a user