blob: 3da4612f2c6703456ac01c650583d0ec20f74d16 [file] [log] [blame]
// 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.
#ifndef V8_EH_FRAME_H_
#define V8_EH_FRAME_H_
#include "src/base/compiler-specific.h"
#include "src/globals.h"
#include "src/macro-assembler.h"
namespace v8 {
namespace internal {
class V8_EXPORT_PRIVATE EhFrameConstants final
: public NON_EXPORTED_BASE(AllStatic) {
public:
enum class DwarfOpcodes : byte {
kNop = 0x00,
kAdvanceLoc1 = 0x02,
kAdvanceLoc2 = 0x03,
kAdvanceLoc4 = 0x04,
kSameValue = 0x08,
kDefCfa = 0x0c,
kDefCfaRegister = 0x0d,
kDefCfaOffset = 0x0e,
kOffsetExtendedSf = 0x11,
};
enum DwarfEncodingSpecifiers : byte {
kUData4 = 0x03,
kSData4 = 0x0b,
kPcRel = 0x10,
kDataRel = 0x30,
kOmit = 0xff,
};
static const int kLocationTag = 1;
static const int kLocationMask = 0x3f;
static const int kLocationMaskSize = 6;
static const int kSavedRegisterTag = 2;
static const int kSavedRegisterMask = 0x3f;
static const int kSavedRegisterMaskSize = 6;
static const int kFollowInitialRuleTag = 3;
static const int kFollowInitialRuleMask = 0x3f;
static const int kFollowInitialRuleMaskSize = 6;
static const int kProcedureAddressOffsetInFde = 2 * kInt32Size;
static const int kProcedureSizeOffsetInFde = 3 * kInt32Size;
static const int kInitialStateOffsetInCie = 19;
static const int kEhFrameTerminatorSize = 4;
// Defined in eh-writer-<arch>.cc
static const int kCodeAlignmentFactor;
static const int kDataAlignmentFactor;
static const int kFdeVersionSize = 1;
static const int kFdeEncodingSpecifiersSize = 3;
static const int kEhFrameHdrVersion = 1;
static const int kEhFrameHdrSize = 20;
};
class V8_EXPORT_PRIVATE EhFrameWriter {
public:
explicit EhFrameWriter(Zone* zone);
// The empty frame is a hack to trigger fp-based unwinding in Linux perf
// compiled with libunwind support when processing DWARF-based call graphs.
//
// It is effectively a valid eh_frame_hdr with an empty look up table.
//
static void WriteEmptyEhFrame(std::ostream& stream); // NOLINT
// Write the CIE and FDE header. Call it before any other method.
void Initialize();
void AdvanceLocation(int pc_offset);
// The <base_address> is the one to which all <offset>s in SaveRegisterToStack
// directives are relative. It is given by <base_register> + <base_offset>.
//
// The <base_offset> must be positive or 0.
//
void SetBaseAddressRegister(Register base_register);
void SetBaseAddressOffset(int base_offset);
void IncreaseBaseAddressOffset(int base_delta) {
SetBaseAddressOffset(base_offset_ + base_delta);
}
void SetBaseAddressRegisterAndOffset(Register base_register, int base_offset);
// Register saved at location <base_address> + <offset>.
// The <offset> must be a multiple of EhFrameConstants::kDataAlignment.
void RecordRegisterSavedToStack(Register name, int offset) {
RecordRegisterSavedToStack(RegisterToDwarfCode(name), offset);
}
// The register has not been modified from the previous frame.
void RecordRegisterNotModified(Register name);
// The register follows the rule defined in the CIE.
void RecordRegisterFollowsInitialRule(Register name);
void Finish(int code_size);
// Remember to call Finish() before GetEhFrame().
//
// The EhFrameWriter instance owns the buffer pointed by
// CodeDesc::unwinding_info, and must outlive any use of the CodeDesc.
//
void GetEhFrame(CodeDesc* desc);
int last_pc_offset() const { return last_pc_offset_; }
Register base_register() const { return base_register_; }
int base_offset() const { return base_offset_; }
private:
enum class InternalState { kUndefined, kInitialized, kFinalized };
static const uint32_t kInt32Placeholder = 0xdeadc0de;
void WriteSLeb128(int32_t value);
void WriteULeb128(uint32_t value);
void WriteByte(byte value) { eh_frame_buffer_.push_back(value); }
void WriteOpcode(EhFrameConstants::DwarfOpcodes opcode) {
WriteByte(static_cast<byte>(opcode));
}
void WriteBytes(const byte* start, int size) {
eh_frame_buffer_.insert(eh_frame_buffer_.end(), start, start + size);
}
void WriteInt16(uint16_t value) {
WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value));
}
void WriteInt32(uint32_t value) {
WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value));
}
void PatchInt32(int base_offset, uint32_t value) {
DCHECK_EQ(ReadUnalignedUInt32(eh_frame_buffer_.data() + base_offset),
kInt32Placeholder);
DCHECK_LT(base_offset + kInt32Size, eh_frame_offset());
WriteUnalignedUInt32(eh_frame_buffer_.data() + base_offset, value);
}
// Write the common information entry, which includes encoding specifiers,
// alignment factors, the return address (pseudo) register code and the
// directives to construct the initial state of the unwinding table.
void WriteCie();
// Write the header of the function data entry, containing a pointer to the
// correspondent CIE and the position and size of the associated routine.
void WriteFdeHeader();
// Write the contents of the .eh_frame_hdr section, including encoding
// specifiers and the routine => FDE lookup table.
void WriteEhFrameHdr(int code_size);
// Write nops until the size reaches a multiple of 8 bytes.
void WritePaddingToAlignedSize(int unpadded_size);
// Internal version that directly accepts a DWARF register code, needed for
// handling pseudo-registers on some platforms.
void RecordRegisterSavedToStack(int register_code, int offset);
int GetProcedureAddressOffset() const {
return fde_offset() + EhFrameConstants::kProcedureAddressOffsetInFde;
}
int GetProcedureSizeOffset() const {
return fde_offset() + EhFrameConstants::kProcedureSizeOffsetInFde;
}
int eh_frame_offset() const {
return static_cast<int>(eh_frame_buffer_.size());
}
int fde_offset() const { return cie_size_; }
// Platform specific functions implemented in eh-frame-<arch>.cc
static int RegisterToDwarfCode(Register name);
// Write directives to build the initial state in the CIE.
void WriteInitialStateInCie();
// Write the return address (pseudo) register code.
void WriteReturnAddressRegisterCode();
int cie_size_;
int last_pc_offset_;
InternalState writer_state_;
Register base_register_;
int base_offset_;
ZoneVector<byte> eh_frame_buffer_;
DISALLOW_COPY_AND_ASSIGN(EhFrameWriter);
};
class V8_EXPORT_PRIVATE EhFrameIterator {
public:
EhFrameIterator(const byte* start, const byte* end)
: start_(start), next_(start), end_(end) {
DCHECK_LE(start, end);
}
void SkipCie() {
DCHECK_EQ(next_, start_);
next_ += ReadUnalignedUInt32(next_) + kInt32Size;
}
void SkipToFdeDirectives() {
SkipCie();
// Skip the FDE header.
Skip(kDirectivesOffsetInFde);
}
void Skip(int how_many) {
DCHECK_GE(how_many, 0);
next_ += how_many;
DCHECK_LE(next_, end_);
}
uint32_t GetNextUInt32() { return GetNextValue<uint32_t>(); }
uint16_t GetNextUInt16() { return GetNextValue<uint16_t>(); }
byte GetNextByte() { return GetNextValue<byte>(); }
EhFrameConstants::DwarfOpcodes GetNextOpcode() {
return static_cast<EhFrameConstants::DwarfOpcodes>(GetNextByte());
}
uint32_t GetNextULeb128();
int32_t GetNextSLeb128();
bool Done() const {
DCHECK_LE(next_, end_);
return next_ == end_;
}
int GetCurrentOffset() const {
DCHECK_GE(next_, start_);
return static_cast<int>(next_ - start_);
}
int GetBufferSize() { return static_cast<int>(end_ - start_); }
const void* current_address() const {
return reinterpret_cast<const void*>(next_);
}
private:
static const int kDirectivesOffsetInFde = 4 * kInt32Size + 1;
static uint32_t DecodeULeb128(const byte* encoded, int* encoded_size);
static int32_t DecodeSLeb128(const byte* encoded, int* encoded_size);
template <typename T>
T GetNextValue() {
T result;
DCHECK_LE(next_ + sizeof(result), end_);
result = ReadUnalignedValue<T>(next_);
next_ += sizeof(result);
return result;
}
const byte* start_;
const byte* next_;
const byte* end_;
};
#ifdef ENABLE_DISASSEMBLER
class EhFrameDisassembler final {
public:
EhFrameDisassembler(const byte* start, const byte* end)
: start_(start), end_(end) {
DCHECK_LT(start, end);
}
void DisassembleToStream(std::ostream& stream); // NOLINT
private:
static void DumpDwarfDirectives(std::ostream& stream, // NOLINT
const byte* start, const byte* end);
static const char* DwarfRegisterCodeToString(int code);
const byte* start_;
const byte* end_;
DISALLOW_COPY_AND_ASSIGN(EhFrameDisassembler);
};
#endif
} // namespace internal
} // namespace v8
#endif