 Chromium Code Reviews
 Chromium Code Reviews Issue 2023503002:
  Reland Implement .eh_frame writer and disassembler.  (Closed) 
  Base URL: https://chromium.googlesource.com/v8/v8.git@eh-frame-base
    
  
    Issue 2023503002:
  Reland Implement .eh_frame writer and disassembler.  (Closed) 
  Base URL: https://chromium.googlesource.com/v8/v8.git@eh-frame-base| Index: src/eh-frame.h | 
| diff --git a/src/eh-frame.h b/src/eh-frame.h | 
| index 75781acb502ee8f420e93b240e7515e6e9f7348d..bfb54cd5a125635b1b97c583aac8ccfc623cf2bf 100644 | 
| --- a/src/eh-frame.h | 
| +++ b/src/eh-frame.h | 
| @@ -5,36 +5,282 @@ | 
| #ifndef V8_EH_FRAME_H_ | 
| #define V8_EH_FRAME_H_ | 
| -#include <cstdint> | 
| +#include "src/macro-assembler.h" | 
| namespace v8 { | 
| namespace internal { | 
| -class Code; | 
| +class EhFrameConstants final { | 
| + 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 kDataAlignmentFactor; | 
| + | 
| + static const int kFdeVersionSize = 1; | 
| + static const int kFdeEncodingSpecifiersSize = 3; | 
| + | 
| + static const int kEhFrameHdrVersion = 1; | 
| + static const int kEhFrameHdrSize = 20; | 
| +}; | 
| + | 
| +class EhFrameWriter { | 
| + public: | 
| + explicit EhFrameWriter(Zone* zone); | 
| + | 
| + // The dummy frame is a hack to trigger fp-based unwinding in Linux perf | 
| 
rmcilroy
2016/07/08 09:20:32
/s/dummy/empty
 
Stefano Sanfilippo
2016/07/08 09:32:13
Done.
 | 
| + // compiled with libunwind support when collecting 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>. | 
| + 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> | 
| + 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 encoder | 
| + // specifiers and the routine => FDE lookup table. | 
| + void WriteEhFrameHdr(int code_size); | 
| + | 
| + // Write nops to the buffer until the size reaches a multiple of 8 bytes. | 
| + void WritePaddingTo8ByteAlignment(); | 
| -class EhFrameHdr final { | 
| + // 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_; | 
| +}; | 
| + | 
| +class EhFrameIterator { | 
| public: | 
| - static const int kRecordSize = 20; | 
| - static const int kCIESize; | 
| + 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(); | 
| - explicit EhFrameHdr(Code* code); | 
| + bool Done() const { | 
| + DCHECK_LE(next_, end_); | 
| + return next_ == end_; | 
| + } | 
| - int32_t offset_to_eh_frame() const { return offset_to_eh_frame_; } | 
| - uint32_t lut_entries_number() const { return lut_entries_number_; } | 
| - int32_t offset_to_procedure() const { return offset_to_procedure_; } | 
| - int32_t offset_to_fde() const { return offset_to_fde_; } | 
| + 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: | 
| - uint8_t version_; | 
| - uint8_t eh_frame_ptr_encoding_; | 
| - uint8_t lut_size_encoding_; | 
| - uint8_t lut_entries_encoding_; | 
| - int32_t offset_to_eh_frame_; | 
| - uint32_t lut_entries_number_; | 
| - int32_t offset_to_procedure_; | 
| - int32_t offset_to_fde_; | 
| + 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_; | 
| }; | 
| +#endif | 
| + | 
| } // namespace internal | 
| } // namespace v8 |