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/register-arch.h"
11#include "src/v8memory.h"
12#include "src/zone/zone-containers.h"
13
14namespace v8 {
15namespace internal {
16
17class CodeDesc;
18
19class V8_EXPORT_PRIVATE EhFrameConstants final
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 kSameValue = 0x08,
28 kDefCfa = 0x0c,
29 kDefCfaRegister = 0x0d,
30 kDefCfaOffset = 0x0e,
31 kOffsetExtendedSf = 0x11,
32 };
33
34 enum DwarfEncodingSpecifiers : byte {
35 kUData4 = 0x03,
36 kSData4 = 0x0b,
37 kPcRel = 0x10,
38 kDataRel = 0x30,
39 kOmit = 0xff,
40 };
41
42 static const int kLocationTag = 1;
43 static const int kLocationMask = 0x3f;
44 static const int kLocationMaskSize = 6;
45
46 static const int kSavedRegisterTag = 2;
47 static const int kSavedRegisterMask = 0x3f;
48 static const int kSavedRegisterMaskSize = 6;
49
50 static const int kFollowInitialRuleTag = 3;
51 static const int kFollowInitialRuleMask = 0x3f;
52 static const int kFollowInitialRuleMaskSize = 6;
53
54 static const int kProcedureAddressOffsetInFde = 2 * kInt32Size;
55 static const int kProcedureSizeOffsetInFde = 3 * kInt32Size;
56
57 static const int kInitialStateOffsetInCie = 19;
58 static const int kEhFrameTerminatorSize = 4;
59
60 // Defined in eh-writer-<arch>.cc
61 static const int kCodeAlignmentFactor;
62 static const int kDataAlignmentFactor;
63
64 static const int kFdeVersionSize = 1;
65 static const int kFdeEncodingSpecifiersSize = 3;
66
67 static const int kEhFrameHdrVersion = 1;
68 static const int kEhFrameHdrSize = 20;
69};
70
71class V8_EXPORT_PRIVATE EhFrameWriter {
72 public:
73 explicit EhFrameWriter(Zone* zone);
74
75 // The empty frame is a hack to trigger fp-based unwinding in Linux perf
76 // compiled with libunwind support when processing DWARF-based call graphs.
77 //
78 // It is effectively a valid eh_frame_hdr with an empty look up table.
79 //
80 static void WriteEmptyEhFrame(std::ostream& stream); // NOLINT
81
82 // Write the CIE and FDE header. Call it before any other method.
83 void Initialize();
84
85 void AdvanceLocation(int pc_offset);
86
87 // The <base_address> is the one to which all <offset>s in SaveRegisterToStack
88 // directives are relative. It is given by <base_register> + <base_offset>.
89 //
90 // The <base_offset> must be positive or 0.
91 //
92 void SetBaseAddressRegister(Register base_register);
93 void SetBaseAddressOffset(int base_offset);
94 void IncreaseBaseAddressOffset(int base_delta) {
95 SetBaseAddressOffset(base_offset_ + base_delta);
96 }
97 void SetBaseAddressRegisterAndOffset(Register base_register, int base_offset);
98
99 // Register saved at location <base_address> + <offset>.
100 // The <offset> must be a multiple of EhFrameConstants::kDataAlignment.
101 void RecordRegisterSavedToStack(Register name, int offset) {
102 RecordRegisterSavedToStack(RegisterToDwarfCode(name), offset);
103 }
104
105 // The register has not been modified from the previous frame.
106 void RecordRegisterNotModified(Register name);
107
108 // The register follows the rule defined in the CIE.
109 void RecordRegisterFollowsInitialRule(Register name);
110
111 void Finish(int code_size);
112
113 // Remember to call Finish() before GetEhFrame().
114 //
115 // The EhFrameWriter instance owns the buffer pointed by
116 // CodeDesc::unwinding_info, and must outlive any use of the CodeDesc.
117 //
118 void GetEhFrame(CodeDesc* desc);
119
120 int last_pc_offset() const { return last_pc_offset_; }
121 Register base_register() const { return base_register_; }
122 int base_offset() const { return base_offset_; }
123
124 private:
125 enum class InternalState { kUndefined, kInitialized, kFinalized };
126
127 static const uint32_t kInt32Placeholder = 0xdeadc0de;
128
129 void WriteSLeb128(int32_t value);
130 void WriteULeb128(uint32_t value);
131
132 void WriteByte(byte value) { eh_frame_buffer_.push_back(value); }
133 void WriteOpcode(EhFrameConstants::DwarfOpcodes opcode) {
134 WriteByte(static_cast<byte>(opcode));
135 }
136 void WriteBytes(const byte* start, int size) {
137 eh_frame_buffer_.insert(eh_frame_buffer_.end(), start, start + size);
138 }
139 void WriteInt16(uint16_t value) {
140 WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value));
141 }
142 void WriteInt32(uint32_t value) {
143 WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value));
144 }
145 void PatchInt32(int base_offset, uint32_t value) {
146 DCHECK_EQ(
147 ReadUnalignedUInt32(reinterpret_cast<Address>(eh_frame_buffer_.data()) +
148 base_offset),
149 kInt32Placeholder);
150 DCHECK_LT(base_offset + kInt32Size, eh_frame_offset());
151 WriteUnalignedUInt32(
152 reinterpret_cast<Address>(eh_frame_buffer_.data()) + base_offset,
153 value);
154 }
155
156 // Write the common information entry, which includes encoding specifiers,
157 // alignment factors, the return address (pseudo) register code and the
158 // directives to construct the initial state of the unwinding table.
159 void WriteCie();
160
161 // Write the header of the function data entry, containing a pointer to the
162 // correspondent CIE and the position and size of the associated routine.
163 void WriteFdeHeader();
164
165 // Write the contents of the .eh_frame_hdr section, including encoding
166 // specifiers and the routine => FDE lookup table.
167 void WriteEhFrameHdr(int code_size);
168
169 // Write nops until the size reaches a multiple of 8 bytes.
170 void WritePaddingToAlignedSize(int unpadded_size);
171
172 // Internal version that directly accepts a DWARF register code, needed for
173 // handling pseudo-registers on some platforms.
174 void RecordRegisterSavedToStack(int register_code, int offset);
175
176 int GetProcedureAddressOffset() const {
177 return fde_offset() + EhFrameConstants::kProcedureAddressOffsetInFde;
178 }
179
180 int GetProcedureSizeOffset() const {
181 return fde_offset() + EhFrameConstants::kProcedureSizeOffsetInFde;
182 }
183
184 int eh_frame_offset() const {
185 return static_cast<int>(eh_frame_buffer_.size());
186 }
187
188 int fde_offset() const { return cie_size_; }
189
190 // Platform specific functions implemented in eh-frame-<arch>.cc
191
192 static int RegisterToDwarfCode(Register name);
193
194 // Write directives to build the initial state in the CIE.
195 void WriteInitialStateInCie();
196
197 // Write the return address (pseudo) register code.
198 void WriteReturnAddressRegisterCode();
199
200 int cie_size_;
201 int last_pc_offset_;
202 InternalState writer_state_;
203 Register base_register_;
204 int base_offset_;
205 ZoneVector<byte> eh_frame_buffer_;
206
207 DISALLOW_COPY_AND_ASSIGN(EhFrameWriter);
208};
209
210class V8_EXPORT_PRIVATE EhFrameIterator {
211 public:
212 EhFrameIterator(const byte* start, const byte* end)
213 : start_(start), next_(start), end_(end) {
214 DCHECK_LE(start, end);
215 }
216
217 void SkipCie() {
218 DCHECK_EQ(next_, start_);
219 next_ += ReadUnalignedUInt32(reinterpret_cast<Address>(next_)) + kInt32Size;
220 }
221
222 void SkipToFdeDirectives() {
223 SkipCie();
224 // Skip the FDE header.
225 Skip(kDirectivesOffsetInFde);
226 }
227
228 void Skip(int how_many) {
229 DCHECK_GE(how_many, 0);
230 next_ += how_many;
231 DCHECK_LE(next_, end_);
232 }
233
234 uint32_t GetNextUInt32() { return GetNextValue<uint32_t>(); }
235 uint16_t GetNextUInt16() { return GetNextValue<uint16_t>(); }
236 byte GetNextByte() { return GetNextValue<byte>(); }
237 EhFrameConstants::DwarfOpcodes GetNextOpcode() {
238 return static_cast<EhFrameConstants::DwarfOpcodes>(GetNextByte());
239 }
240
241 uint32_t GetNextULeb128();
242 int32_t GetNextSLeb128();
243
244 bool Done() const {
245 DCHECK_LE(next_, end_);
246 return next_ == end_;
247 }
248
249 int GetCurrentOffset() const {
250 DCHECK_GE(next_, start_);
251 return static_cast<int>(next_ - start_);
252 }
253
254 int GetBufferSize() { return static_cast<int>(end_ - start_); }
255
256 const void* current_address() const {
257 return reinterpret_cast<const void*>(next_);
258 }
259
260 private:
261 static const int kDirectivesOffsetInFde = 4 * kInt32Size + 1;
262
263 static uint32_t DecodeULeb128(const byte* encoded, int* encoded_size);
264 static int32_t DecodeSLeb128(const byte* encoded, int* encoded_size);
265
266 template <typename T>
267 T GetNextValue() {
268 T result;
269 DCHECK_LE(next_ + sizeof(result), end_);
270 result = ReadUnalignedValue<T>(reinterpret_cast<Address>(next_));
271 next_ += sizeof(result);
272 return result;
273 }
274
275 const byte* start_;
276 const byte* next_;
277 const byte* end_;
278};
279
280#ifdef ENABLE_DISASSEMBLER
281
282class EhFrameDisassembler final {
283 public:
284 EhFrameDisassembler(const byte* start, const byte* end)
285 : start_(start), end_(end) {
286 DCHECK_LT(start, end);
287 }
288
289 void DisassembleToStream(std::ostream& stream); // NOLINT
290
291 private:
292 static void DumpDwarfDirectives(std::ostream& stream, // NOLINT
293 const byte* start, const byte* end);
294
295 static const char* DwarfRegisterCodeToString(int code);
296
297 const byte* start_;
298 const byte* end_;
299
300 DISALLOW_COPY_AND_ASSIGN(EhFrameDisassembler);
301};
302
303#endif
304
305} // namespace internal
306} // namespace v8
307
308#endif // V8_EH_FRAME_H_
309