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