• 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_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
NON_EXPORTED_BASE(AllStatic)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);
IncreaseBaseAddressOffset(int base_delta)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.
RecordRegisterSavedToStack(Register name,int offset)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 
last_pc_offset()117   int last_pc_offset() const { return last_pc_offset_; }
base_register()118   Register base_register() const { return base_register_; }
base_offset()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 
WriteByte(byte value)129   void WriteByte(byte value) { eh_frame_buffer_.push_back(value); }
WriteOpcode(EhFrameConstants::DwarfOpcodes opcode)130   void WriteOpcode(EhFrameConstants::DwarfOpcodes opcode) {
131     WriteByte(static_cast<byte>(opcode));
132   }
WriteBytes(const byte * start,int size)133   void WriteBytes(const byte* start, int size) {
134     eh_frame_buffer_.insert(eh_frame_buffer_.end(), start, start + size);
135   }
WriteInt16(uint16_t value)136   void WriteInt16(uint16_t value) {
137     WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value));
138   }
WriteInt32(uint32_t value)139   void WriteInt32(uint32_t value) {
140     WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value));
141   }
PatchInt32(int base_offset,uint32_t value)142   void PatchInt32(int base_offset, uint32_t value) {
143     DCHECK_EQ(ReadUnalignedUInt32(eh_frame_buffer_.data() + base_offset),
144               kInt32Placeholder);
145     DCHECK_LT(base_offset + kInt32Size, eh_frame_offset());
146     WriteUnalignedUInt32(eh_frame_buffer_.data() + base_offset, value);
147   }
148 
149   // Write the common information entry, which includes encoding specifiers,
150   // alignment factors, the return address (pseudo) register code and the
151   // directives to construct the initial state of the unwinding table.
152   void WriteCie();
153 
154   // Write the header of the function data entry, containing a pointer to the
155   // correspondent CIE and the position and size of the associated routine.
156   void WriteFdeHeader();
157 
158   // Write the contents of the .eh_frame_hdr section, including encoding
159   // specifiers and the routine => FDE lookup table.
160   void WriteEhFrameHdr(int code_size);
161 
162   // Write nops until the size reaches a multiple of 8 bytes.
163   void WritePaddingToAlignedSize(int unpadded_size);
164 
165   // Internal version that directly accepts a DWARF register code, needed for
166   // handling pseudo-registers on some platforms.
167   void RecordRegisterSavedToStack(int register_code, int offset);
168 
GetProcedureAddressOffset()169   int GetProcedureAddressOffset() const {
170     return fde_offset() + EhFrameConstants::kProcedureAddressOffsetInFde;
171   }
172 
GetProcedureSizeOffset()173   int GetProcedureSizeOffset() const {
174     return fde_offset() + EhFrameConstants::kProcedureSizeOffsetInFde;
175   }
176 
eh_frame_offset()177   int eh_frame_offset() const {
178     return static_cast<int>(eh_frame_buffer_.size());
179   }
180 
fde_offset()181   int fde_offset() const { return cie_size_; }
182 
183   // Platform specific functions implemented in eh-frame-<arch>.cc
184 
185   static int RegisterToDwarfCode(Register name);
186 
187   // Write directives to build the initial state in the CIE.
188   void WriteInitialStateInCie();
189 
190   // Write the return address (pseudo) register code.
191   void WriteReturnAddressRegisterCode();
192 
193   int cie_size_;
194   int last_pc_offset_;
195   InternalState writer_state_;
196   Register base_register_;
197   int base_offset_;
198   ZoneVector<byte> eh_frame_buffer_;
199 
200   DISALLOW_COPY_AND_ASSIGN(EhFrameWriter);
201 };
202 
203 class V8_EXPORT_PRIVATE EhFrameIterator {
204  public:
EhFrameIterator(const byte * start,const byte * end)205   EhFrameIterator(const byte* start, const byte* end)
206       : start_(start), next_(start), end_(end) {
207     DCHECK_LE(start, end);
208   }
209 
SkipCie()210   void SkipCie() {
211     DCHECK_EQ(next_, start_);
212     next_ += ReadUnalignedUInt32(next_) + kInt32Size;
213   }
214 
SkipToFdeDirectives()215   void SkipToFdeDirectives() {
216     SkipCie();
217     // Skip the FDE header.
218     Skip(kDirectivesOffsetInFde);
219   }
220 
Skip(int how_many)221   void Skip(int how_many) {
222     DCHECK_GE(how_many, 0);
223     next_ += how_many;
224     DCHECK_LE(next_, end_);
225   }
226 
GetNextUInt32()227   uint32_t GetNextUInt32() { return GetNextValue<uint32_t>(); }
GetNextUInt16()228   uint16_t GetNextUInt16() { return GetNextValue<uint16_t>(); }
GetNextByte()229   byte GetNextByte() { return GetNextValue<byte>(); }
GetNextOpcode()230   EhFrameConstants::DwarfOpcodes GetNextOpcode() {
231     return static_cast<EhFrameConstants::DwarfOpcodes>(GetNextByte());
232   }
233 
234   uint32_t GetNextULeb128();
235   int32_t GetNextSLeb128();
236 
Done()237   bool Done() const {
238     DCHECK_LE(next_, end_);
239     return next_ == end_;
240   }
241 
GetCurrentOffset()242   int GetCurrentOffset() const {
243     DCHECK_GE(next_, start_);
244     return static_cast<int>(next_ - start_);
245   }
246 
GetBufferSize()247   int GetBufferSize() { return static_cast<int>(end_ - start_); }
248 
current_address()249   const void* current_address() const {
250     return reinterpret_cast<const void*>(next_);
251   }
252 
253  private:
254   static const int kDirectivesOffsetInFde = 4 * kInt32Size + 1;
255 
256   static uint32_t DecodeULeb128(const byte* encoded, int* encoded_size);
257   static int32_t DecodeSLeb128(const byte* encoded, int* encoded_size);
258 
259   template <typename T>
GetNextValue()260   T GetNextValue() {
261     T result;
262     DCHECK_LE(next_ + sizeof(result), end_);
263     result = ReadUnalignedValue<T>(next_);
264     next_ += sizeof(result);
265     return result;
266   }
267 
268   const byte* start_;
269   const byte* next_;
270   const byte* end_;
271 };
272 
273 #ifdef ENABLE_DISASSEMBLER
274 
275 class EhFrameDisassembler final {
276  public:
EhFrameDisassembler(const byte * start,const byte * end)277   EhFrameDisassembler(const byte* start, const byte* end)
278       : start_(start), end_(end) {
279     DCHECK_LT(start, end);
280   }
281 
282   void DisassembleToStream(std::ostream& stream);  // NOLINT
283 
284  private:
285   static void DumpDwarfDirectives(std::ostream& stream,  // NOLINT
286                                   const byte* start, const byte* end);
287 
288   static const char* DwarfRegisterCodeToString(int code);
289 
290   const byte* start_;
291   const byte* end_;
292 
293   DISALLOW_COPY_AND_ASSIGN(EhFrameDisassembler);
294 };
295 
296 #endif
297 
298 }  // namespace internal
299 }  // namespace v8
300 
301 #endif
302