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