// 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" #include "src/zone/zone-containers.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-.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 is the one to which all s in SaveRegisterToStack // directives are relative. It is given by + . // // The 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 + . // The 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(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(&value), sizeof(value)); } void WriteInt32(uint32_t value) { WriteBytes(reinterpret_cast(&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(eh_frame_buffer_.size()); } int fde_offset() const { return cie_size_; } // Platform specific functions implemented in eh-frame-.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 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(); } uint16_t GetNextUInt16() { return GetNextValue(); } byte GetNextByte() { return GetNextValue(); } EhFrameConstants::DwarfOpcodes GetNextOpcode() { return static_cast(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(next_ - start_); } int GetBufferSize() { return static_cast(end_ - start_); } const void* current_address() const { return reinterpret_cast(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 T GetNextValue() { T result; DCHECK_LE(next_ + sizeof(result), end_); result = ReadUnalignedValue(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