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 | |
14 | namespace v8 { |
15 | namespace internal { |
16 | |
17 | class CodeDesc; |
18 | |
19 | class 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 = 3; |
66 | |
67 | static const int kEhFrameHdrVersion = 1; |
68 | static const int kEhFrameHdrSize = 20; |
69 | }; |
70 | |
71 | class 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 (); |
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 | |
210 | class 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 | |
282 | class 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 | |