v8/src/eh-frame.cc
Clemens Hammacher 5f6510825a [cleanup] Fix remaining (D)CHECK macro usages
This CL fixes all occurences that don't require special OWNER reviews,
or can be reviewed by Michi.

After this one, we should be able to reenable the readability/check
cpplint check.

R=mstarzinger@chromium.org

Bug: v8:6837, v8:6921
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng;master.tryserver.v8:v8_linux_noi18n_rel_ng
Change-Id: Ic81d68d5534eaa795b7197fed5c41ed158361d62
Reviewed-on: https://chromium-review.googlesource.com/721120
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48670}
2017-10-18 10:12:31 +00:00

630 lines
21 KiB
C++

// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/eh-frame.h"
#include <iomanip>
#include <ostream>
#if !defined(V8_TARGET_ARCH_X64) && !defined(V8_TARGET_ARCH_ARM) && \
!defined(V8_TARGET_ARCH_ARM64)
// Placeholders for unsupported architectures.
namespace v8 {
namespace internal {
const int EhFrameConstants::kCodeAlignmentFactor = 1;
const int EhFrameConstants::kDataAlignmentFactor = 1;
void EhFrameWriter::WriteReturnAddressRegisterCode() { UNIMPLEMENTED(); }
void EhFrameWriter::WriteInitialStateInCie() { UNIMPLEMENTED(); }
int EhFrameWriter::RegisterToDwarfCode(Register) {
UNIMPLEMENTED();
return -1;
}
#ifdef ENABLE_DISASSEMBLER
const char* EhFrameDisassembler::DwarfRegisterCodeToString(int) {
UNIMPLEMENTED();
return nullptr;
}
#endif
} // namespace internal
} // namespace v8
#endif
namespace v8 {
namespace internal {
STATIC_CONST_MEMBER_DEFINITION const int
EhFrameConstants::kEhFrameTerminatorSize;
STATIC_CONST_MEMBER_DEFINITION const int EhFrameConstants::kEhFrameHdrVersion;
STATIC_CONST_MEMBER_DEFINITION const int EhFrameConstants::kEhFrameHdrSize;
STATIC_CONST_MEMBER_DEFINITION const uint32_t EhFrameWriter::kInt32Placeholder;
// static
void EhFrameWriter::WriteEmptyEhFrame(std::ostream& stream) { // NOLINT
stream.put(EhFrameConstants::kEhFrameHdrVersion);
// .eh_frame pointer encoding specifier.
stream.put(EhFrameConstants::kSData4 | EhFrameConstants::kPcRel);
// Lookup table size encoding.
stream.put(EhFrameConstants::kUData4);
// Lookup table entries encoding.
stream.put(EhFrameConstants::kSData4 | EhFrameConstants::kDataRel);
// Dummy pointers and 0 entries in the lookup table.
char dummy_data[EhFrameConstants::kEhFrameHdrSize - 4] = {0};
stream.write(&dummy_data[0], sizeof(dummy_data));
}
EhFrameWriter::EhFrameWriter(Zone* zone)
: cie_size_(0),
last_pc_offset_(0),
writer_state_(InternalState::kUndefined),
base_register_(no_reg),
base_offset_(0),
eh_frame_buffer_(zone) {}
void EhFrameWriter::Initialize() {
DCHECK_EQ(writer_state_, InternalState::kUndefined);
eh_frame_buffer_.reserve(128);
writer_state_ = InternalState::kInitialized;
WriteCie();
WriteFdeHeader();
}
void EhFrameWriter::WriteCie() {
static const int kCIEIdentifier = 0;
static const int kCIEVersion = 3;
static const int kAugmentationDataSize = 2;
static const byte kAugmentationString[] = {'z', 'L', 'R', 0};
// Placeholder for the size of the CIE.
int size_offset = eh_frame_offset();
WriteInt32(kInt32Placeholder);
// CIE identifier and version.
int record_start_offset = eh_frame_offset();
WriteInt32(kCIEIdentifier);
WriteByte(kCIEVersion);
// Augmentation data contents descriptor: LSDA and FDE encoding.
WriteBytes(&kAugmentationString[0], sizeof(kAugmentationString));
// Alignment factors.
WriteSLeb128(EhFrameConstants::kCodeAlignmentFactor);
WriteSLeb128(EhFrameConstants::kDataAlignmentFactor);
WriteReturnAddressRegisterCode();
// Augmentation data.
WriteULeb128(kAugmentationDataSize);
// No language-specific data area (LSDA).
WriteByte(EhFrameConstants::kOmit);
// FDE pointers encoding.
WriteByte(EhFrameConstants::kSData4 | EhFrameConstants::kPcRel);
// Write directives to build the initial state of the unwinding table.
DCHECK_EQ(eh_frame_offset() - size_offset,
EhFrameConstants::kInitialStateOffsetInCie);
WriteInitialStateInCie();
WritePaddingToAlignedSize(eh_frame_offset() - record_start_offset);
int record_end_offset = eh_frame_offset();
int encoded_cie_size = record_end_offset - record_start_offset;
cie_size_ = record_end_offset - size_offset;
// Patch the size of the CIE now that we know it.
PatchInt32(size_offset, encoded_cie_size);
}
void EhFrameWriter::WriteFdeHeader() {
DCHECK_NE(cie_size_, 0);
// Placeholder for size of the FDE. Will be filled in Finish().
DCHECK_EQ(eh_frame_offset(), fde_offset());
WriteInt32(kInt32Placeholder);
// Backwards offset to the CIE.
WriteInt32(cie_size_ + kInt32Size);
// Placeholder for pointer to procedure. Will be filled in Finish().
DCHECK_EQ(eh_frame_offset(), GetProcedureAddressOffset());
WriteInt32(kInt32Placeholder);
// Placeholder for size of the procedure. Will be filled in Finish().
DCHECK_EQ(eh_frame_offset(), GetProcedureSizeOffset());
WriteInt32(kInt32Placeholder);
// No augmentation data.
WriteByte(0);
}
void EhFrameWriter::WriteEhFrameHdr(int code_size) {
DCHECK_EQ(writer_state_, InternalState::kInitialized);
//
// In order to calculate offsets in the .eh_frame_hdr, we must know the layout
// of the DSO generated by perf inject, which is assumed to be the following:
//
// | ... | |
// +---------------+ <-- (F) --- | Larger offsets in file
// | | ^ |
// | Instructions | | .text v
// | | v
// +---------------+ <-- (E) ---
// |///////////////|
// |////Padding////|
// |///////////////|
// +---------------+ <-- (D) ---
// | | ^
// | CIE | |
// | | |
// +---------------+ <-- (C) |
// | | | .eh_frame
// | FDE | |
// | | |
// +---------------+ |
// | terminator | v
// +---------------+ <-- (B) ---
// | version | ^
// +---------------+ |
// | encoding | |
// | specifiers | |
// +---------------+ <---(A) | .eh_frame_hdr
// | offset to | |
// | .eh_frame | |
// +---------------+ |
// | ... | ...
//
// (F) is aligned to a 16-byte boundary.
// (D) is aligned to a 8-byte boundary.
// (B) is aligned to a 4-byte boundary.
// (C), (E) and (A) have no alignment requirements.
//
// The distance between (A) and (B) is 4 bytes.
//
// The size of the FDE is required to be a multiple of the pointer size, which
// means that (B) will be naturally aligned to a 4-byte boundary on all the
// architectures we support.
//
// Because (E) has no alignment requirements, there is padding between (E) and
// (D). (F) is aligned at a 16-byte boundary, thus to a 8-byte one as well.
//
int eh_frame_size = eh_frame_offset();
WriteByte(EhFrameConstants::kEhFrameHdrVersion);
// .eh_frame pointer encoding specifier.
WriteByte(EhFrameConstants::kSData4 | EhFrameConstants::kPcRel);
// Lookup table size encoding specifier.
WriteByte(EhFrameConstants::kUData4);
// Lookup table entries encoding specifier.
WriteByte(EhFrameConstants::kSData4 | EhFrameConstants::kDataRel);
// Pointer to .eh_frame, relative to this offset (A -> D in the diagram).
WriteInt32(-(eh_frame_size + EhFrameConstants::kFdeVersionSize +
EhFrameConstants::kFdeEncodingSpecifiersSize));
// Number of entries in the LUT, one for the only routine.
WriteInt32(1);
// Pointer to the start of the routine, relative to the beginning of the
// .eh_frame_hdr (B -> F in the diagram).
WriteInt32(-(RoundUp(code_size, 8) + eh_frame_size));
// Pointer to the start of the associated FDE, relative to the start of the
// .eh_frame_hdr (B -> C in the diagram).
WriteInt32(-(eh_frame_size - cie_size_));
DCHECK_EQ(eh_frame_offset() - eh_frame_size,
EhFrameConstants::kEhFrameHdrSize);
}
void EhFrameWriter::WritePaddingToAlignedSize(int unpadded_size) {
DCHECK_EQ(writer_state_, InternalState::kInitialized);
DCHECK_GE(unpadded_size, 0);
int padding_size = RoundUp(unpadded_size, kPointerSize) - unpadded_size;
byte nop = static_cast<byte>(EhFrameConstants::DwarfOpcodes::kNop);
static const byte kPadding[] = {nop, nop, nop, nop, nop, nop, nop, nop};
DCHECK_LE(padding_size, static_cast<int>(sizeof(kPadding)));
WriteBytes(&kPadding[0], padding_size);
}
void EhFrameWriter::AdvanceLocation(int pc_offset) {
DCHECK_EQ(writer_state_, InternalState::kInitialized);
DCHECK_GE(pc_offset, last_pc_offset_);
uint32_t delta = pc_offset - last_pc_offset_;
DCHECK_EQ(delta % EhFrameConstants::kCodeAlignmentFactor, 0u);
uint32_t factored_delta = delta / EhFrameConstants::kCodeAlignmentFactor;
if (factored_delta <= EhFrameConstants::kLocationMask) {
WriteByte((EhFrameConstants::kLocationTag
<< EhFrameConstants::kLocationMaskSize) |
(factored_delta & EhFrameConstants::kLocationMask));
} else if (factored_delta <= kMaxUInt8) {
WriteOpcode(EhFrameConstants::DwarfOpcodes::kAdvanceLoc1);
WriteByte(factored_delta);
} else if (factored_delta <= kMaxUInt16) {
WriteOpcode(EhFrameConstants::DwarfOpcodes::kAdvanceLoc2);
WriteInt16(factored_delta);
} else {
WriteOpcode(EhFrameConstants::DwarfOpcodes::kAdvanceLoc4);
WriteInt32(factored_delta);
}
last_pc_offset_ = pc_offset;
}
void EhFrameWriter::SetBaseAddressOffset(int base_offset) {
DCHECK_EQ(writer_state_, InternalState::kInitialized);
DCHECK_GE(base_offset, 0);
WriteOpcode(EhFrameConstants::DwarfOpcodes::kDefCfaOffset);
WriteULeb128(base_offset);
base_offset_ = base_offset;
}
void EhFrameWriter::SetBaseAddressRegister(Register base_register) {
DCHECK_EQ(writer_state_, InternalState::kInitialized);
int code = RegisterToDwarfCode(base_register);
WriteOpcode(EhFrameConstants::DwarfOpcodes::kDefCfaRegister);
WriteULeb128(code);
base_register_ = base_register;
}
void EhFrameWriter::SetBaseAddressRegisterAndOffset(Register base_register,
int base_offset) {
DCHECK_EQ(writer_state_, InternalState::kInitialized);
DCHECK_GE(base_offset, 0);
int code = RegisterToDwarfCode(base_register);
WriteOpcode(EhFrameConstants::DwarfOpcodes::kDefCfa);
WriteULeb128(code);
WriteULeb128(base_offset);
base_offset_ = base_offset;
base_register_ = base_register;
}
void EhFrameWriter::RecordRegisterSavedToStack(int register_code, int offset) {
DCHECK_EQ(writer_state_, InternalState::kInitialized);
DCHECK_EQ(offset % EhFrameConstants::kDataAlignmentFactor, 0);
int factored_offset = offset / EhFrameConstants::kDataAlignmentFactor;
if (factored_offset >= 0) {
DCHECK_LE(register_code, EhFrameConstants::kSavedRegisterMask);
WriteByte((EhFrameConstants::kSavedRegisterTag
<< EhFrameConstants::kSavedRegisterMaskSize) |
(register_code & EhFrameConstants::kSavedRegisterMask));
WriteULeb128(factored_offset);
} else {
WriteOpcode(EhFrameConstants::DwarfOpcodes::kOffsetExtendedSf);
WriteULeb128(register_code);
WriteSLeb128(factored_offset);
}
}
void EhFrameWriter::RecordRegisterNotModified(Register name) {
DCHECK_EQ(writer_state_, InternalState::kInitialized);
WriteOpcode(EhFrameConstants::DwarfOpcodes::kSameValue);
WriteULeb128(RegisterToDwarfCode(name));
}
void EhFrameWriter::RecordRegisterFollowsInitialRule(Register name) {
DCHECK_EQ(writer_state_, InternalState::kInitialized);
int code = RegisterToDwarfCode(name);
DCHECK_LE(code, EhFrameConstants::kFollowInitialRuleMask);
WriteByte((EhFrameConstants::kFollowInitialRuleTag
<< EhFrameConstants::kFollowInitialRuleMaskSize) |
(code & EhFrameConstants::kFollowInitialRuleMask));
}
void EhFrameWriter::Finish(int code_size) {
DCHECK_EQ(writer_state_, InternalState::kInitialized);
DCHECK_GE(eh_frame_offset(), cie_size_);
DCHECK_GE(eh_frame_offset(), fde_offset() + kInt32Size);
WritePaddingToAlignedSize(eh_frame_offset() - fde_offset() - kInt32Size);
// Write the size of the FDE now that we know it.
// The encoded size does not include the size field itself.
int encoded_fde_size = eh_frame_offset() - fde_offset() - kInt32Size;
PatchInt32(fde_offset(), encoded_fde_size);
// Write size and offset to procedure.
PatchInt32(GetProcedureAddressOffset(),
-(RoundUp(code_size, 8) + GetProcedureAddressOffset()));
PatchInt32(GetProcedureSizeOffset(), code_size);
// Terminate the .eh_frame.
static const byte kTerminator[EhFrameConstants::kEhFrameTerminatorSize] = {0};
WriteBytes(&kTerminator[0], EhFrameConstants::kEhFrameTerminatorSize);
WriteEhFrameHdr(code_size);
writer_state_ = InternalState::kFinalized;
}
void EhFrameWriter::GetEhFrame(CodeDesc* desc) {
DCHECK_EQ(writer_state_, InternalState::kFinalized);
desc->unwinding_info_size = static_cast<int>(eh_frame_buffer_.size());
desc->unwinding_info = eh_frame_buffer_.data();
}
void EhFrameWriter::WriteULeb128(uint32_t value) {
do {
byte chunk = value & 0x7f;
value >>= 7;
if (value != 0) chunk |= 0x80;
WriteByte(chunk);
} while (value != 0);
}
void EhFrameWriter::WriteSLeb128(int32_t value) {
static const int kSignBitMask = 0x40;
bool done;
do {
byte chunk = value & 0x7f;
value >>= 7;
done = ((value == 0) && ((chunk & kSignBitMask) == 0)) ||
((value == -1) && ((chunk & kSignBitMask) != 0));
if (!done) chunk |= 0x80;
WriteByte(chunk);
} while (!done);
}
uint32_t EhFrameIterator::GetNextULeb128() {
int size = 0;
uint32_t result = DecodeULeb128(next_, &size);
DCHECK_LE(next_ + size, end_);
next_ += size;
return result;
}
int32_t EhFrameIterator::GetNextSLeb128() {
int size = 0;
int32_t result = DecodeSLeb128(next_, &size);
DCHECK_LE(next_ + size, end_);
next_ += size;
return result;
}
// static
uint32_t EhFrameIterator::DecodeULeb128(const byte* encoded,
int* encoded_size) {
const byte* current = encoded;
uint32_t result = 0;
int shift = 0;
do {
DCHECK_LT(shift, 8 * static_cast<int>(sizeof(result)));
result |= (*current & 0x7f) << shift;
shift += 7;
} while (*current++ >= 128);
DCHECK_NOT_NULL(encoded_size);
*encoded_size = static_cast<int>(current - encoded);
return result;
}
// static
int32_t EhFrameIterator::DecodeSLeb128(const byte* encoded, int* encoded_size) {
static const byte kSignBitMask = 0x40;
const byte* current = encoded;
int32_t result = 0;
int shift = 0;
byte chunk;
do {
chunk = *current++;
DCHECK_LT(shift, 8 * static_cast<int>(sizeof(result)));
result |= (chunk & 0x7f) << shift;
shift += 7;
} while (chunk >= 128);
// Sign extend the result if the last chunk has the sign bit set.
if (chunk & kSignBitMask) result |= (~0ull) << shift;
DCHECK_NOT_NULL(encoded_size);
*encoded_size = static_cast<int>(current - encoded);
return result;
}
#ifdef ENABLE_DISASSEMBLER
namespace {
class StreamModifiersScope final {
public:
explicit StreamModifiersScope(std::ostream* stream)
: stream_(stream), flags_(stream->flags()) {}
~StreamModifiersScope() { stream_->flags(flags_); }
private:
std::ostream* stream_;
std::ios::fmtflags flags_;
};
} // namespace
// static
void EhFrameDisassembler::DumpDwarfDirectives(std::ostream& stream, // NOLINT
const byte* start,
const byte* end) {
StreamModifiersScope modifiers_scope(&stream);
EhFrameIterator eh_frame_iterator(start, end);
uint32_t offset_in_procedure = 0;
while (!eh_frame_iterator.Done()) {
stream << eh_frame_iterator.current_address() << " ";
byte bytecode = eh_frame_iterator.GetNextByte();
if (((bytecode >> EhFrameConstants::kLocationMaskSize) & 0xff) ==
EhFrameConstants::kLocationTag) {
int value = (bytecode & EhFrameConstants::kLocationMask) *
EhFrameConstants::kCodeAlignmentFactor;
offset_in_procedure += value;
stream << "| pc_offset=" << offset_in_procedure << " (delta=" << value
<< ")\n";
continue;
}
if (((bytecode >> EhFrameConstants::kSavedRegisterMaskSize) & 0xff) ==
EhFrameConstants::kSavedRegisterTag) {
int32_t decoded_offset = eh_frame_iterator.GetNextULeb128();
stream << "| " << DwarfRegisterCodeToString(
bytecode & EhFrameConstants::kLocationMask)
<< " saved at base" << std::showpos
<< decoded_offset * EhFrameConstants::kDataAlignmentFactor
<< std::noshowpos << '\n';
continue;
}
if (((bytecode >> EhFrameConstants::kFollowInitialRuleMaskSize) & 0xff) ==
EhFrameConstants::kFollowInitialRuleTag) {
stream << "| " << DwarfRegisterCodeToString(
bytecode & EhFrameConstants::kLocationMask)
<< " follows rule in CIE\n";
continue;
}
switch (static_cast<EhFrameConstants::DwarfOpcodes>(bytecode)) {
case EhFrameConstants::DwarfOpcodes::kOffsetExtendedSf: {
stream << "| "
<< DwarfRegisterCodeToString(eh_frame_iterator.GetNextULeb128());
int32_t decoded_offset = eh_frame_iterator.GetNextSLeb128();
stream << " saved at base" << std::showpos
<< decoded_offset * EhFrameConstants::kDataAlignmentFactor
<< std::noshowpos << '\n';
break;
}
case EhFrameConstants::DwarfOpcodes::kAdvanceLoc1: {
int value = eh_frame_iterator.GetNextByte() *
EhFrameConstants::kCodeAlignmentFactor;
offset_in_procedure += value;
stream << "| pc_offset=" << offset_in_procedure << " (delta=" << value
<< ")\n";
break;
}
case EhFrameConstants::DwarfOpcodes::kAdvanceLoc2: {
int value = eh_frame_iterator.GetNextUInt16() *
EhFrameConstants::kCodeAlignmentFactor;
offset_in_procedure += value;
stream << "| pc_offset=" << offset_in_procedure << " (delta=" << value
<< ")\n";
break;
}
case EhFrameConstants::DwarfOpcodes::kAdvanceLoc4: {
int value = eh_frame_iterator.GetNextUInt32() *
EhFrameConstants::kCodeAlignmentFactor;
offset_in_procedure += value;
stream << "| pc_offset=" << offset_in_procedure << " (delta=" << value
<< ")\n";
break;
}
case EhFrameConstants::DwarfOpcodes::kDefCfa: {
uint32_t base_register = eh_frame_iterator.GetNextULeb128();
uint32_t base_offset = eh_frame_iterator.GetNextULeb128();
stream << "| base_register=" << DwarfRegisterCodeToString(base_register)
<< ", base_offset=" << base_offset << '\n';
break;
}
case EhFrameConstants::DwarfOpcodes::kDefCfaOffset: {
stream << "| base_offset=" << eh_frame_iterator.GetNextULeb128()
<< '\n';
break;
}
case EhFrameConstants::DwarfOpcodes::kDefCfaRegister: {
stream << "| base_register="
<< DwarfRegisterCodeToString(eh_frame_iterator.GetNextULeb128())
<< '\n';
break;
}
case EhFrameConstants::DwarfOpcodes::kSameValue: {
stream << "| "
<< DwarfRegisterCodeToString(eh_frame_iterator.GetNextULeb128())
<< " not modified from previous frame\n";
break;
}
case EhFrameConstants::DwarfOpcodes::kNop:
stream << "| nop\n";
break;
default:
UNREACHABLE();
return;
}
}
}
void EhFrameDisassembler::DisassembleToStream(std::ostream& stream) { // NOLINT
// The encoded CIE size does not include the size field itself.
const int cie_size = ReadUnalignedUInt32(start_) + kInt32Size;
const int fde_offset = cie_size;
const byte* cie_directives_start =
start_ + EhFrameConstants::kInitialStateOffsetInCie;
const byte* cie_directives_end = start_ + cie_size;
DCHECK_LE(cie_directives_start, cie_directives_end);
stream << reinterpret_cast<const void*>(start_) << " .eh_frame: CIE\n";
DumpDwarfDirectives(stream, cie_directives_start, cie_directives_end);
const byte* procedure_offset_address =
start_ + fde_offset + EhFrameConstants::kProcedureAddressOffsetInFde;
int32_t procedure_offset =
ReadUnalignedValue<int32_t>(procedure_offset_address);
const byte* procedure_size_address =
start_ + fde_offset + EhFrameConstants::kProcedureSizeOffsetInFde;
uint32_t procedure_size = ReadUnalignedUInt32(procedure_size_address);
const byte* fde_start = start_ + fde_offset;
stream << reinterpret_cast<const void*>(fde_start) << " .eh_frame: FDE\n"
<< reinterpret_cast<const void*>(procedure_offset_address)
<< " | procedure_offset=" << procedure_offset << '\n'
<< reinterpret_cast<const void*>(procedure_size_address)
<< " | procedure_size=" << procedure_size << '\n';
const int fde_directives_offset = fde_offset + 4 * kInt32Size + 1;
const byte* fde_directives_start = start_ + fde_directives_offset;
const byte* fde_directives_end = end_ - EhFrameConstants::kEhFrameHdrSize -
EhFrameConstants::kEhFrameTerminatorSize;
DCHECK_LE(fde_directives_start, fde_directives_end);
DumpDwarfDirectives(stream, fde_directives_start, fde_directives_end);
const byte* fde_terminator_start = fde_directives_end;
stream << reinterpret_cast<const void*>(fde_terminator_start)
<< " .eh_frame: terminator\n";
const byte* eh_frame_hdr_start =
fde_terminator_start + EhFrameConstants::kEhFrameTerminatorSize;
stream << reinterpret_cast<const void*>(eh_frame_hdr_start)
<< " .eh_frame_hdr\n";
}
#endif
} // namespace internal
} // namespace v8