• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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