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.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 EhFrameWriter(const EhFrameWriter&) = delete; 76 EhFrameWriter& operator=(const EhFrameWriter&) = delete; 77 78 // The empty frame is a hack to trigger fp-based unwinding in Linux perf 79 // compiled with libunwind support when processing DWARF-based call graphs. 80 // 81 // It is effectively a valid eh_frame_hdr with an empty look up table. 82 // 83 static void WriteEmptyEhFrame(std::ostream& stream); 84 85 // Write the CIE and FDE header. Call it before any other method. 86 void Initialize(); 87 88 void AdvanceLocation(int pc_offset); 89 90 // The <base_address> is the one to which all <offset>s in SaveRegisterToStack 91 // directives are relative. It is given by <base_register> + <base_offset>. 92 // 93 // The <base_offset> must be positive or 0. 94 // 95 void SetBaseAddressRegister(Register base_register); 96 void SetBaseAddressOffset(int base_offset); IncreaseBaseAddressOffset(int base_delta)97 void IncreaseBaseAddressOffset(int base_delta) { 98 SetBaseAddressOffset(base_offset_ + base_delta); 99 } 100 void SetBaseAddressRegisterAndOffset(Register base_register, int base_offset); 101 102 // Register saved at location <base_address> + <offset>. 103 // The <offset> must be a multiple of EhFrameConstants::kDataAlignment. RecordRegisterSavedToStack(Register name,int offset)104 void RecordRegisterSavedToStack(Register name, int offset) { 105 RecordRegisterSavedToStack(RegisterToDwarfCode(name), offset); 106 } 107 108 // Directly accepts a DWARF register code, needed for 109 // handling pseudo-registers on some platforms. 110 void RecordRegisterSavedToStack(int dwarf_register_code, int offset); 111 112 // The register has not been modified from the previous frame. 113 void RecordRegisterNotModified(Register name); 114 void RecordRegisterNotModified(int dwarf_register_code); 115 116 // The register follows the rule defined in the CIE. 117 void RecordRegisterFollowsInitialRule(Register name); 118 void RecordRegisterFollowsInitialRule(int dwarf_register_code); 119 120 void Finish(int code_size); 121 122 // Remember to call Finish() before GetEhFrame(). 123 // 124 // The EhFrameWriter instance owns the buffer pointed by 125 // CodeDesc::unwinding_info, and must outlive any use of the CodeDesc. 126 // 127 void GetEhFrame(CodeDesc* desc); 128 last_pc_offset()129 int last_pc_offset() const { return last_pc_offset_; } base_register()130 Register base_register() const { return base_register_; } base_offset()131 int base_offset() const { return base_offset_; } 132 133 private: 134 enum class InternalState { kUndefined, kInitialized, kFinalized }; 135 136 static const uint32_t kInt32Placeholder = 0xdeadc0de; 137 138 void WriteSLeb128(int32_t value); 139 void WriteULeb128(uint32_t value); 140 WriteByte(byte value)141 void WriteByte(byte value) { eh_frame_buffer_.push_back(value); } WriteOpcode(EhFrameConstants::DwarfOpcodes opcode)142 void WriteOpcode(EhFrameConstants::DwarfOpcodes opcode) { 143 WriteByte(static_cast<byte>(opcode)); 144 } WriteBytes(const byte * start,int size)145 void WriteBytes(const byte* start, int size) { 146 eh_frame_buffer_.insert(eh_frame_buffer_.end(), start, start + size); 147 } WriteInt16(uint16_t value)148 void WriteInt16(uint16_t value) { 149 WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value)); 150 } WriteInt32(uint32_t value)151 void WriteInt32(uint32_t value) { 152 WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value)); 153 } PatchInt32(int base_offset,uint32_t value)154 void PatchInt32(int base_offset, uint32_t value) { 155 DCHECK_EQ( 156 base::ReadUnalignedValue<uint32_t>( 157 reinterpret_cast<Address>(eh_frame_buffer_.data()) + base_offset), 158 kInt32Placeholder); 159 DCHECK_LT(base_offset + kInt32Size, eh_frame_offset()); 160 base::WriteUnalignedValue<uint32_t>( 161 reinterpret_cast<Address>(eh_frame_buffer_.data()) + base_offset, 162 value); 163 } 164 165 // Write the common information entry, which includes encoding specifiers, 166 // alignment factors, the return address (pseudo) register code and the 167 // directives to construct the initial state of the unwinding table. 168 void WriteCie(); 169 170 // Write the header of the function data entry, containing a pointer to the 171 // correspondent CIE and the position and size of the associated routine. 172 void WriteFdeHeader(); 173 174 // Write the contents of the .eh_frame_hdr section, including encoding 175 // specifiers and the routine => FDE lookup table. 176 void WriteEhFrameHdr(int code_size); 177 178 // Write nops until the size reaches a multiple of 8 bytes. 179 void WritePaddingToAlignedSize(int unpadded_size); 180 GetProcedureAddressOffset()181 int GetProcedureAddressOffset() const { 182 return fde_offset() + EhFrameConstants::kProcedureAddressOffsetInFde; 183 } 184 GetProcedureSizeOffset()185 int GetProcedureSizeOffset() const { 186 return fde_offset() + EhFrameConstants::kProcedureSizeOffsetInFde; 187 } 188 eh_frame_offset()189 int eh_frame_offset() const { 190 return static_cast<int>(eh_frame_buffer_.size()); 191 } 192 fde_offset()193 int fde_offset() const { return cie_size_; } 194 195 // Platform specific functions implemented in eh-frame-<arch>.cc 196 197 static int RegisterToDwarfCode(Register name); 198 199 // Write directives to build the initial state in the CIE. 200 void WriteInitialStateInCie(); 201 202 // Write the return address (pseudo) register code. 203 void WriteReturnAddressRegisterCode(); 204 205 int cie_size_; 206 int last_pc_offset_; 207 InternalState writer_state_; 208 Register base_register_; 209 int base_offset_; 210 ZoneVector<byte> eh_frame_buffer_; 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 EhFrameDisassembler(const EhFrameDisassembler&) = delete; 294 EhFrameDisassembler& operator=(const EhFrameDisassembler&) = delete; 295 296 void DisassembleToStream(std::ostream& stream); 297 298 private: 299 static void DumpDwarfDirectives(std::ostream& stream, const byte* start, 300 const byte* end); 301 302 static const char* DwarfRegisterCodeToString(int code); 303 304 const byte* start_; 305 const byte* end_; 306 }; 307 308 #endif 309 310 } // namespace internal 311 } // namespace v8 312 313 #endif // V8_DIAGNOSTICS_EH_FRAME_H_ 314