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
13namespace v8 {
14namespace internal {
15
16class V8_EXPORT_PRIVATE EhFrameConstants final
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
68class 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);
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.
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
117  int last_pc_offset() const { return last_pc_offset_; }
118  Register base_register() const { return base_register_; }
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
129  void WriteByte(byte value) { eh_frame_buffer_.push_back(value); }
130  void WriteOpcode(EhFrameConstants::DwarfOpcodes opcode) {
131    WriteByte(static_cast<byte>(opcode));
132  }
133  void WriteBytes(const byte* start, int size) {
134    eh_frame_buffer_.insert(eh_frame_buffer_.end(), start, start + size);
135  }
136  void WriteInt16(uint16_t value) {
137    WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value));
138  }
139  void WriteInt32(uint32_t value) {
140    WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value));
141  }
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
169  int GetProcedureAddressOffset() const {
170    return fde_offset() + EhFrameConstants::kProcedureAddressOffsetInFde;
171  }
172
173  int GetProcedureSizeOffset() const {
174    return fde_offset() + EhFrameConstants::kProcedureSizeOffsetInFde;
175  }
176
177  int eh_frame_offset() const {
178    return static_cast<int>(eh_frame_buffer_.size());
179  }
180
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
203class V8_EXPORT_PRIVATE EhFrameIterator {
204 public:
205  EhFrameIterator(const byte* start, const byte* end)
206      : start_(start), next_(start), end_(end) {
207    DCHECK_LE(start, end);
208  }
209
210  void SkipCie() {
211    DCHECK_EQ(next_, start_);
212    next_ += ReadUnalignedUInt32(next_) + kInt32Size;
213  }
214
215  void SkipToFdeDirectives() {
216    SkipCie();
217    // Skip the FDE header.
218    Skip(kDirectivesOffsetInFde);
219  }
220
221  void Skip(int how_many) {
222    DCHECK_GE(how_many, 0);
223    next_ += how_many;
224    DCHECK_LE(next_, end_);
225  }
226
227  uint32_t GetNextUInt32() { return GetNextValue<uint32_t>(); }
228  uint16_t GetNextUInt16() { return GetNextValue<uint16_t>(); }
229  byte GetNextByte() { return GetNextValue<byte>(); }
230  EhFrameConstants::DwarfOpcodes GetNextOpcode() {
231    return static_cast<EhFrameConstants::DwarfOpcodes>(GetNextByte());
232  }
233
234  uint32_t GetNextULeb128();
235  int32_t GetNextSLeb128();
236
237  bool Done() const {
238    DCHECK_LE(next_, end_);
239    return next_ == end_;
240  }
241
242  int GetCurrentOffset() const {
243    DCHECK_GE(next_, start_);
244    return static_cast<int>(next_ - start_);
245  }
246
247  int GetBufferSize() { return static_cast<int>(end_ - start_); }
248
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>
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
275class EhFrameDisassembler final {
276 public:
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