Home | History | Annotate | Download | only in src
      1 // Copyright 2016 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef V8_EH_FRAME_H_
      6 #define V8_EH_FRAME_H_
      7 
      8 #include "src/base/compiler-specific.h"
      9 #include "src/globals.h"
     10 #include "src/macro-assembler.h"
     11 
     12 namespace v8 {
     13 namespace internal {
     14 
     15 class V8_EXPORT_PRIVATE EhFrameConstants final
     16     : public NON_EXPORTED_BASE(AllStatic) {
     17  public:
     18   enum class DwarfOpcodes : byte {
     19     kNop = 0x00,
     20     kAdvanceLoc1 = 0x02,
     21     kAdvanceLoc2 = 0x03,
     22     kAdvanceLoc4 = 0x04,
     23     kSameValue = 0x08,
     24     kDefCfa = 0x0c,
     25     kDefCfaRegister = 0x0d,
     26     kDefCfaOffset = 0x0e,
     27     kOffsetExtendedSf = 0x11,
     28   };
     29 
     30   enum DwarfEncodingSpecifiers : byte {
     31     kUData4 = 0x03,
     32     kSData4 = 0x0b,
     33     kPcRel = 0x10,
     34     kDataRel = 0x30,
     35     kOmit = 0xff,
     36   };
     37 
     38   static const int kLocationTag = 1;
     39   static const int kLocationMask = 0x3f;
     40   static const int kLocationMaskSize = 6;
     41 
     42   static const int kSavedRegisterTag = 2;
     43   static const int kSavedRegisterMask = 0x3f;
     44   static const int kSavedRegisterMaskSize = 6;
     45 
     46   static const int kFollowInitialRuleTag = 3;
     47   static const int kFollowInitialRuleMask = 0x3f;
     48   static const int kFollowInitialRuleMaskSize = 6;
     49 
     50   static const int kProcedureAddressOffsetInFde = 2 * kInt32Size;
     51   static const int kProcedureSizeOffsetInFde = 3 * kInt32Size;
     52 
     53   static const int kInitialStateOffsetInCie = 19;
     54   static const int kEhFrameTerminatorSize = 4;
     55 
     56   // Defined in eh-writer-<arch>.cc
     57   static const int kCodeAlignmentFactor;
     58   static const int kDataAlignmentFactor;
     59 
     60   static const int kFdeVersionSize = 1;
     61   static const int kFdeEncodingSpecifiersSize = 3;
     62 
     63   static const int kEhFrameHdrVersion = 1;
     64   static const int kEhFrameHdrSize = 20;
     65 };
     66 
     67 class V8_EXPORT_PRIVATE EhFrameWriter {
     68  public:
     69   explicit EhFrameWriter(Zone* zone);
     70 
     71   // The empty frame is a hack to trigger fp-based unwinding in Linux perf
     72   // compiled with libunwind support when processing DWARF-based call graphs.
     73   //
     74   // It is effectively a valid eh_frame_hdr with an empty look up table.
     75   //
     76   static void WriteEmptyEhFrame(std::ostream& stream);  // NOLINT
     77 
     78   // Write the CIE and FDE header. Call it before any other method.
     79   void Initialize();
     80 
     81   void AdvanceLocation(int pc_offset);
     82 
     83   // The <base_address> is the one to which all <offset>s in SaveRegisterToStack
     84   // directives are relative. It is given by <base_register> + <base_offset>.
     85   //
     86   // The <base_offset> must be positive or 0.
     87   //
     88   void SetBaseAddressRegister(Register base_register);
     89   void SetBaseAddressOffset(int base_offset);
     90   void IncreaseBaseAddressOffset(int base_delta) {
     91     SetBaseAddressOffset(base_offset_ + base_delta);
     92   }
     93   void SetBaseAddressRegisterAndOffset(Register base_register, int base_offset);
     94 
     95   // Register saved at location <base_address> + <offset>.
     96   // The <offset> must be a multiple of EhFrameConstants::kDataAlignment.
     97   void RecordRegisterSavedToStack(Register name, int offset) {
     98     RecordRegisterSavedToStack(RegisterToDwarfCode(name), offset);
     99   }
    100 
    101   // The register has not been modified from the previous frame.
    102   void RecordRegisterNotModified(Register name);
    103 
    104   // The register follows the rule defined in the CIE.
    105   void RecordRegisterFollowsInitialRule(Register name);
    106 
    107   void Finish(int code_size);
    108 
    109   // Remember to call Finish() before GetEhFrame().
    110   //
    111   // The EhFrameWriter instance owns the buffer pointed by
    112   // CodeDesc::unwinding_info, and must outlive any use of the CodeDesc.
    113   //
    114   void GetEhFrame(CodeDesc* desc);
    115 
    116   int last_pc_offset() const { return last_pc_offset_; }
    117   Register base_register() const { return base_register_; }
    118   int base_offset() const { return base_offset_; }
    119 
    120  private:
    121   enum class InternalState { kUndefined, kInitialized, kFinalized };
    122 
    123   static const uint32_t kInt32Placeholder = 0xdeadc0de;
    124 
    125   void WriteSLeb128(int32_t value);
    126   void WriteULeb128(uint32_t value);
    127 
    128   void WriteByte(byte value) { eh_frame_buffer_.push_back(value); }
    129   void WriteOpcode(EhFrameConstants::DwarfOpcodes opcode) {
    130     WriteByte(static_cast<byte>(opcode));
    131   }
    132   void WriteBytes(const byte* start, int size) {
    133     eh_frame_buffer_.insert(eh_frame_buffer_.end(), start, start + size);
    134   }
    135   void WriteInt16(uint16_t value) {
    136     WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value));
    137   }
    138   void WriteInt32(uint32_t value) {
    139     WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value));
    140   }
    141   void PatchInt32(int base_offset, uint32_t value) {
    142     DCHECK_EQ(ReadUnalignedUInt32(eh_frame_buffer_.data() + base_offset),
    143               kInt32Placeholder);
    144     DCHECK_LT(base_offset + kInt32Size, eh_frame_offset());
    145     WriteUnalignedUInt32(eh_frame_buffer_.data() + base_offset, value);
    146   }
    147 
    148   // Write the common information entry, which includes encoding specifiers,
    149   // alignment factors, the return address (pseudo) register code and the
    150   // directives to construct the initial state of the unwinding table.
    151   void WriteCie();
    152 
    153   // Write the header of the function data entry, containing a pointer to the
    154   // correspondent CIE and the position and size of the associated routine.
    155   void WriteFdeHeader();
    156 
    157   // Write the contents of the .eh_frame_hdr section, including encoding
    158   // specifiers and the routine => FDE lookup table.
    159   void WriteEhFrameHdr(int code_size);
    160 
    161   // Write nops until the size reaches a multiple of 8 bytes.
    162   void WritePaddingToAlignedSize(int unpadded_size);
    163 
    164   // Internal version that directly accepts a DWARF register code, needed for
    165   // handling pseudo-registers on some platforms.
    166   void RecordRegisterSavedToStack(int register_code, int offset);
    167 
    168   int GetProcedureAddressOffset() const {
    169     return fde_offset() + EhFrameConstants::kProcedureAddressOffsetInFde;
    170   }
    171 
    172   int GetProcedureSizeOffset() const {
    173     return fde_offset() + EhFrameConstants::kProcedureSizeOffsetInFde;
    174   }
    175 
    176   int eh_frame_offset() const {
    177     return static_cast<int>(eh_frame_buffer_.size());
    178   }
    179 
    180   int fde_offset() const { return cie_size_; }
    181 
    182   // Platform specific functions implemented in eh-frame-<arch>.cc
    183 
    184   static int RegisterToDwarfCode(Register name);
    185 
    186   // Write directives to build the initial state in the CIE.
    187   void WriteInitialStateInCie();
    188 
    189   // Write the return address (pseudo) register code.
    190   void WriteReturnAddressRegisterCode();
    191 
    192   int cie_size_;
    193   int last_pc_offset_;
    194   InternalState writer_state_;
    195   Register base_register_;
    196   int base_offset_;
    197   ZoneVector<byte> eh_frame_buffer_;
    198 
    199   DISALLOW_COPY_AND_ASSIGN(EhFrameWriter);
    200 };
    201 
    202 class V8_EXPORT_PRIVATE EhFrameIterator {
    203  public:
    204   EhFrameIterator(const byte* start, const byte* end)
    205       : start_(start), next_(start), end_(end) {
    206     DCHECK_LE(start, end);
    207   }
    208 
    209   void SkipCie() {
    210     DCHECK_EQ(next_, start_);
    211     next_ += ReadUnalignedUInt32(next_) + kInt32Size;
    212   }
    213 
    214   void SkipToFdeDirectives() {
    215     SkipCie();
    216     // Skip the FDE header.
    217     Skip(kDirectivesOffsetInFde);
    218   }
    219 
    220   void Skip(int how_many) {
    221     DCHECK_GE(how_many, 0);
    222     next_ += how_many;
    223     DCHECK_LE(next_, end_);
    224   }
    225 
    226   uint32_t GetNextUInt32() { return GetNextValue<uint32_t>(); }
    227   uint16_t GetNextUInt16() { return GetNextValue<uint16_t>(); }
    228   byte GetNextByte() { return GetNextValue<byte>(); }
    229   EhFrameConstants::DwarfOpcodes GetNextOpcode() {
    230     return static_cast<EhFrameConstants::DwarfOpcodes>(GetNextByte());
    231   }
    232 
    233   uint32_t GetNextULeb128();
    234   int32_t GetNextSLeb128();
    235 
    236   bool Done() const {
    237     DCHECK_LE(next_, end_);
    238     return next_ == end_;
    239   }
    240 
    241   int GetCurrentOffset() const {
    242     DCHECK_GE(next_, start_);
    243     return static_cast<int>(next_ - start_);
    244   }
    245 
    246   int GetBufferSize() { return static_cast<int>(end_ - start_); }
    247 
    248   const void* current_address() const {
    249     return reinterpret_cast<const void*>(next_);
    250   }
    251 
    252  private:
    253   static const int kDirectivesOffsetInFde = 4 * kInt32Size + 1;
    254 
    255   static uint32_t DecodeULeb128(const byte* encoded, int* encoded_size);
    256   static int32_t DecodeSLeb128(const byte* encoded, int* encoded_size);
    257 
    258   template <typename T>
    259   T GetNextValue() {
    260     T result;
    261     DCHECK_LE(next_ + sizeof(result), end_);
    262     result = ReadUnalignedValue<T>(next_);
    263     next_ += sizeof(result);
    264     return result;
    265   }
    266 
    267   const byte* start_;
    268   const byte* next_;
    269   const byte* end_;
    270 };
    271 
    272 #ifdef ENABLE_DISASSEMBLER
    273 
    274 class EhFrameDisassembler final {
    275  public:
    276   EhFrameDisassembler(const byte* start, const byte* end)
    277       : start_(start), end_(end) {
    278     DCHECK_LT(start, end);
    279   }
    280 
    281   void DisassembleToStream(std::ostream& stream);  // NOLINT
    282 
    283  private:
    284   static void DumpDwarfDirectives(std::ostream& stream,  // NOLINT
    285                                   const byte* start, const byte* end);
    286 
    287   static const char* DwarfRegisterCodeToString(int code);
    288 
    289   const byte* start_;
    290   const byte* end_;
    291 
    292   DISALLOW_COPY_AND_ASSIGN(EhFrameDisassembler);
    293 };
    294 
    295 #endif
    296 
    297 }  // namespace internal
    298 }  // namespace v8
    299 
    300 #endif
    301