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