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 NON_EXPORTED_BASE(AllStatic)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); IncreaseBaseAddressOffset(int base_delta)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. RecordRegisterSavedToStack(Register name,int offset)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 last_pc_offset()117 int last_pc_offset() const { return last_pc_offset_; } base_register()118 Register base_register() const { return base_register_; } base_offset()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 WriteByte(byte value)129 void WriteByte(byte value) { eh_frame_buffer_.push_back(value); } WriteOpcode(EhFrameConstants::DwarfOpcodes opcode)130 void WriteOpcode(EhFrameConstants::DwarfOpcodes opcode) { 131 WriteByte(static_cast<byte>(opcode)); 132 } WriteBytes(const byte * start,int size)133 void WriteBytes(const byte* start, int size) { 134 eh_frame_buffer_.insert(eh_frame_buffer_.end(), start, start + size); 135 } WriteInt16(uint16_t value)136 void WriteInt16(uint16_t value) { 137 WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value)); 138 } WriteInt32(uint32_t value)139 void WriteInt32(uint32_t value) { 140 WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value)); 141 } PatchInt32(int base_offset,uint32_t value)142 void PatchInt32(int base_offset, uint32_t value) { 143 DCHECK_EQ(ReadUnalignedUInt32(eh_frame_buffer_.data() + base_offset), 144 kInt32Placeholder); 145 DCHECK_LT(base_offset + kInt32Size, eh_frame_offset()); 146 WriteUnalignedUInt32(eh_frame_buffer_.data() + base_offset, value); 147 } 148 149 // Write the common information entry, which includes encoding specifiers, 150 // alignment factors, the return address (pseudo) register code and the 151 // directives to construct the initial state of the unwinding table. 152 void WriteCie(); 153 154 // Write the header of the function data entry, containing a pointer to the 155 // correspondent CIE and the position and size of the associated routine. 156 void WriteFdeHeader(); 157 158 // Write the contents of the .eh_frame_hdr section, including encoding 159 // specifiers and the routine => FDE lookup table. 160 void WriteEhFrameHdr(int code_size); 161 162 // Write nops until the size reaches a multiple of 8 bytes. 163 void WritePaddingToAlignedSize(int unpadded_size); 164 165 // Internal version that directly accepts a DWARF register code, needed for 166 // handling pseudo-registers on some platforms. 167 void RecordRegisterSavedToStack(int register_code, int offset); 168 GetProcedureAddressOffset()169 int GetProcedureAddressOffset() const { 170 return fde_offset() + EhFrameConstants::kProcedureAddressOffsetInFde; 171 } 172 GetProcedureSizeOffset()173 int GetProcedureSizeOffset() const { 174 return fde_offset() + EhFrameConstants::kProcedureSizeOffsetInFde; 175 } 176 eh_frame_offset()177 int eh_frame_offset() const { 178 return static_cast<int>(eh_frame_buffer_.size()); 179 } 180 fde_offset()181 int fde_offset() const { return cie_size_; } 182 183 // Platform specific functions implemented in eh-frame-<arch>.cc 184 185 static int RegisterToDwarfCode(Register name); 186 187 // Write directives to build the initial state in the CIE. 188 void WriteInitialStateInCie(); 189 190 // Write the return address (pseudo) register code. 191 void WriteReturnAddressRegisterCode(); 192 193 int cie_size_; 194 int last_pc_offset_; 195 InternalState writer_state_; 196 Register base_register_; 197 int base_offset_; 198 ZoneVector<byte> eh_frame_buffer_; 199 200 DISALLOW_COPY_AND_ASSIGN(EhFrameWriter); 201 }; 202 203 class V8_EXPORT_PRIVATE EhFrameIterator { 204 public: EhFrameIterator(const byte * start,const byte * end)205 EhFrameIterator(const byte* start, const byte* end) 206 : start_(start), next_(start), end_(end) { 207 DCHECK_LE(start, end); 208 } 209 SkipCie()210 void SkipCie() { 211 DCHECK_EQ(next_, start_); 212 next_ += ReadUnalignedUInt32(next_) + kInt32Size; 213 } 214 SkipToFdeDirectives()215 void SkipToFdeDirectives() { 216 SkipCie(); 217 // Skip the FDE header. 218 Skip(kDirectivesOffsetInFde); 219 } 220 Skip(int how_many)221 void Skip(int how_many) { 222 DCHECK_GE(how_many, 0); 223 next_ += how_many; 224 DCHECK_LE(next_, end_); 225 } 226 GetNextUInt32()227 uint32_t GetNextUInt32() { return GetNextValue<uint32_t>(); } GetNextUInt16()228 uint16_t GetNextUInt16() { return GetNextValue<uint16_t>(); } GetNextByte()229 byte GetNextByte() { return GetNextValue<byte>(); } GetNextOpcode()230 EhFrameConstants::DwarfOpcodes GetNextOpcode() { 231 return static_cast<EhFrameConstants::DwarfOpcodes>(GetNextByte()); 232 } 233 234 uint32_t GetNextULeb128(); 235 int32_t GetNextSLeb128(); 236 Done()237 bool Done() const { 238 DCHECK_LE(next_, end_); 239 return next_ == end_; 240 } 241 GetCurrentOffset()242 int GetCurrentOffset() const { 243 DCHECK_GE(next_, start_); 244 return static_cast<int>(next_ - start_); 245 } 246 GetBufferSize()247 int GetBufferSize() { return static_cast<int>(end_ - start_); } 248 current_address()249 const void* current_address() const { 250 return reinterpret_cast<const void*>(next_); 251 } 252 253 private: 254 static const int kDirectivesOffsetInFde = 4 * kInt32Size + 1; 255 256 static uint32_t DecodeULeb128(const byte* encoded, int* encoded_size); 257 static int32_t DecodeSLeb128(const byte* encoded, int* encoded_size); 258 259 template <typename T> GetNextValue()260 T GetNextValue() { 261 T result; 262 DCHECK_LE(next_ + sizeof(result), end_); 263 result = ReadUnalignedValue<T>(next_); 264 next_ += sizeof(result); 265 return result; 266 } 267 268 const byte* start_; 269 const byte* next_; 270 const byte* end_; 271 }; 272 273 #ifdef ENABLE_DISASSEMBLER 274 275 class EhFrameDisassembler final { 276 public: EhFrameDisassembler(const byte * start,const byte * end)277 EhFrameDisassembler(const byte* start, const byte* end) 278 : start_(start), end_(end) { 279 DCHECK_LT(start, end); 280 } 281 282 void DisassembleToStream(std::ostream& stream); // NOLINT 283 284 private: 285 static void DumpDwarfDirectives(std::ostream& stream, // NOLINT 286 const byte* start, const byte* end); 287 288 static const char* DwarfRegisterCodeToString(int code); 289 290 const byte* start_; 291 const byte* end_; 292 293 DISALLOW_COPY_AND_ASSIGN(EhFrameDisassembler); 294 }; 295 296 #endif 297 298 } // namespace internal 299 } // namespace v8 300 301 #endif 302