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 NON_EXPORTED_BASE(AllStatic)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); IncreaseBaseAddressOffset(int base_delta)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. RecordRegisterSavedToStack(Register name,int offset)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 last_pc_offset()116 int last_pc_offset() const { return last_pc_offset_; } base_register()117 Register base_register() const { return base_register_; } base_offset()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 WriteByte(byte value)128 void WriteByte(byte value) { eh_frame_buffer_.push_back(value); } WriteOpcode(EhFrameConstants::DwarfOpcodes opcode)129 void WriteOpcode(EhFrameConstants::DwarfOpcodes opcode) { 130 WriteByte(static_cast<byte>(opcode)); 131 } WriteBytes(const byte * start,int size)132 void WriteBytes(const byte* start, int size) { 133 eh_frame_buffer_.insert(eh_frame_buffer_.end(), start, start + size); 134 } WriteInt16(uint16_t value)135 void WriteInt16(uint16_t value) { 136 WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value)); 137 } WriteInt32(uint32_t value)138 void WriteInt32(uint32_t value) { 139 WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value)); 140 } PatchInt32(int base_offset,uint32_t value)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 GetProcedureAddressOffset()168 int GetProcedureAddressOffset() const { 169 return fde_offset() + EhFrameConstants::kProcedureAddressOffsetInFde; 170 } 171 GetProcedureSizeOffset()172 int GetProcedureSizeOffset() const { 173 return fde_offset() + EhFrameConstants::kProcedureSizeOffsetInFde; 174 } 175 eh_frame_offset()176 int eh_frame_offset() const { 177 return static_cast<int>(eh_frame_buffer_.size()); 178 } 179 fde_offset()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: EhFrameIterator(const byte * start,const byte * end)204 EhFrameIterator(const byte* start, const byte* end) 205 : start_(start), next_(start), end_(end) { 206 DCHECK_LE(start, end); 207 } 208 SkipCie()209 void SkipCie() { 210 DCHECK_EQ(next_, start_); 211 next_ += ReadUnalignedUInt32(next_) + kInt32Size; 212 } 213 SkipToFdeDirectives()214 void SkipToFdeDirectives() { 215 SkipCie(); 216 // Skip the FDE header. 217 Skip(kDirectivesOffsetInFde); 218 } 219 Skip(int how_many)220 void Skip(int how_many) { 221 DCHECK_GE(how_many, 0); 222 next_ += how_many; 223 DCHECK_LE(next_, end_); 224 } 225 GetNextUInt32()226 uint32_t GetNextUInt32() { return GetNextValue<uint32_t>(); } GetNextUInt16()227 uint16_t GetNextUInt16() { return GetNextValue<uint16_t>(); } GetNextByte()228 byte GetNextByte() { return GetNextValue<byte>(); } GetNextOpcode()229 EhFrameConstants::DwarfOpcodes GetNextOpcode() { 230 return static_cast<EhFrameConstants::DwarfOpcodes>(GetNextByte()); 231 } 232 233 uint32_t GetNextULeb128(); 234 int32_t GetNextSLeb128(); 235 Done()236 bool Done() const { 237 DCHECK_LE(next_, end_); 238 return next_ == end_; 239 } 240 GetCurrentOffset()241 int GetCurrentOffset() const { 242 DCHECK_GE(next_, start_); 243 return static_cast<int>(next_ - start_); 244 } 245 GetBufferSize()246 int GetBufferSize() { return static_cast<int>(end_ - start_); } 247 current_address()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> GetNextValue()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: EhFrameDisassembler(const byte * start,const byte * end)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