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