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