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 #include "src/eh-frame.h" | 5 #include "src/eh-frame.h" |
6 #include "src/objects-inl.h" | 6 |
7 #include "src/objects.h" | 7 #include <iomanip> |
8 #include <ostream> | |
9 | |
10 #if V8_TARGET_ARCH_X64 | |
11 #include "src/x64/eh-frame-x64.h" | |
12 #elif V8_TARGET_ARCH_ARM | |
13 #include "src/arm/eh-frame-arm.h" | |
14 #elif V8_TARGET_ARCH_ARM64 | |
15 #include "src/arm64/eh-frame-arm64.h" | |
16 #else | |
17 | |
18 // Dummy placeholders | |
8 | 19 |
9 namespace v8 { | 20 namespace v8 { |
10 namespace internal { | 21 namespace internal { |
11 | 22 |
12 static const int DW_EH_PE_pcrel = 0x10; | 23 static const Register kInitialBaseRegister = {Register::kCode_no_reg}; |
13 static const int DW_EH_PE_datarel = 0x30; | 24 static const int kInitialBaseOffset = 0; |
14 static const int DW_EH_PE_udata4 = 0x03; | 25 static const int kDataAlignmentFactor = 1; |
15 static const int DW_EH_PE_sdata4 = 0x0b; | 26 static const byte kCIE[8] = {0}; |
16 | 27 #if ENABLE_DISASSEMBLER |
17 const int EhFrameHdr::kCIESize = 0; | 28 static const int kInitialStateOffsetInCIE = 0; |
18 | 29 #endif |
19 static const int kVersionSize = 1; | 30 |
20 static const int kEncodingSpecifiersSize = 3; | 31 const char* DwarfRegisterCodeToString(int) { |
32 UNIMPLEMENTED(); | |
33 return nullptr; | |
34 } | |
35 | |
36 int RegisterToDwarfCode(Register) { | |
37 UNIMPLEMENTED(); | |
38 return -1; | |
39 } | |
40 | |
41 } // namespace internal | |
42 } // namespace v8 | |
43 | |
44 #endif | |
45 | |
46 namespace v8 { | |
47 namespace internal { | |
48 | |
49 STATIC_CONST_MEMBER_DEFINITION const uint32_t EhFrameWriter::kInt32Placeholder; | |
50 | |
51 STATIC_CONST_MEMBER_DEFINITION const int EhFrameWriter::kCIESize = sizeof(kCIE); | |
52 static_assert(EhFrameWriter::kCIESize % kPointerSize == 0, | |
53 "CIE size must be a multiple of pointer size"); | |
54 | |
55 STATIC_CONST_MEMBER_DEFINITION const int EhFrameWriter::kFDEOffset = kCIESize; | |
56 STATIC_CONST_MEMBER_DEFINITION const int | |
57 EhFrameWriter::kProcedureAddressOffset = kFDEOffset + 2 * kInt32Size; | |
rmcilroy
2016/06/30 15:23:10
I think it would be better if these were relative
Stefano Sanfilippo
2016/07/04 18:21:14
Done.
| |
58 STATIC_CONST_MEMBER_DEFINITION const int EhFrameWriter::kProcedureSizeOffset = | |
59 kFDEOffset + 3 * kInt32Size; | |
60 #ifdef ENABLE_DISASSEMBLER | |
61 STATIC_CONST_MEMBER_DEFINITION const int EhFrameWriter::kFDEDirectivesOffset = | |
rmcilroy
2016/06/30 15:23:10
This is only used in DisassembleToStream, just def
Stefano Sanfilippo
2016/07/04 18:21:14
Done.
| |
62 kFDEOffset + 4 * kInt32Size + 1; | |
63 #endif | |
64 | |
65 const char* DwarfRegisterCodeToString(int code); | |
66 int RegisterToDwarfCode(Register name); | |
rmcilroy
2016/06/30 15:23:10
these should be methods in EhFrameWriter
Stefano Sanfilippo
2016/07/04 18:21:14
Done.
| |
67 | |
68 EhFrameWriter::EhFrameWriter() | |
69 : last_pc_offset_(0), | |
70 #if DEBUG | |
71 eh_frame_finalised_(false), | |
72 #endif | |
73 base_register_(kInitialBaseRegister), | |
74 base_offset_(kInitialBaseOffset) { | |
75 WriteBytes(&kCIE[0], sizeof(kCIE)); | |
76 WriteFDEHeader(); | |
77 } | |
78 | |
79 void EhFrameWriter::WriteFDEHeader() { | |
80 DCHECK_EQ(eh_frame_offset(), kFDEOffset); | |
81 WriteInt32(kInt32Placeholder); // Placeholder for size of the FDE | |
82 WriteInt32(kCIESize + kInt32Size); // Backwards offset to the CIE | |
83 DCHECK_EQ(eh_frame_offset(), kProcedureAddressOffset); | |
84 WriteInt32(kInt32Placeholder); // Placeholder for pointer to procedure | |
85 DCHECK_EQ(eh_frame_offset(), kProcedureSizeOffset); | |
86 WriteInt32(kInt32Placeholder); // Placeholder for size of the procedure | |
87 WriteByte(0); // No augmentation data | |
88 } | |
89 | |
90 void EhFrameWriter::AdvanceLocation(int pc_offset) { | |
91 DCHECK_GE(pc_offset, last_pc_offset_); | |
92 uint32_t delta = pc_offset - last_pc_offset_; | |
93 | |
94 if (delta <= kLocationMask) { | |
95 WriteByte((kLocationTag << kLocationMaskSize) | (delta & kLocationMask)); | |
96 } else if (delta <= kMaxUInt8) { | |
97 WriteByte(kAdvanceLoc1); | |
98 WriteByte(delta); | |
99 } else if (delta <= kMaxUInt16) { | |
100 WriteByte(kAdvanceLoc2); | |
101 WriteInt16(delta); | |
102 } else { | |
103 WriteByte(kAdvanceLoc4); | |
104 WriteInt32(delta); | |
105 } | |
106 | |
107 last_pc_offset_ = pc_offset; | |
108 } | |
109 | |
110 void EhFrameWriter::DefineBaseAddressOffset(int base_offset) { | |
111 DCHECK_GE(base_offset, 0); | |
112 WriteByte(kDefCFAOffset); | |
113 WriteULEB128(base_offset); | |
114 base_offset_ = base_offset; | |
115 } | |
116 | |
117 void EhFrameWriter::DefineBaseAddressRegister(Register name) { | |
118 int code = RegisterToDwarfCode(name); | |
119 WriteByte(kDefCFARegister); | |
120 WriteULEB128(code); | |
121 base_register_ = name; | |
122 } | |
123 | |
124 void EhFrameWriter::DefineBaseAddressRegisterOffset(Register name, | |
125 int base_offset) { | |
126 int code = RegisterToDwarfCode(name); | |
127 WriteByte(kDefCFA); | |
128 WriteULEB128(code); | |
129 WriteULEB128(base_offset); | |
130 base_offset_ = base_offset; | |
131 base_register_ = name; | |
132 } | |
133 | |
134 void EhFrameWriter::SaveRegisterToStack(Register name, int offset) { | |
135 int code = RegisterToDwarfCode(name); | |
136 DCHECK_GE(code, 0); | |
137 DCHECK_LE(code, kSavedRegisterMask); | |
138 DCHECK_EQ(offset % kDataAlignmentFactor, 0); | |
139 WriteByte((kSavedRegisterTag << kSavedRegisterMaskSize) | | |
140 (code & kSavedRegisterMask)); | |
141 WriteULEB128(offset / std::abs(kDataAlignmentFactor)); | |
142 } | |
143 | |
144 void EhFrameWriter::RegisterIsValid(Register name) { | |
145 int code = RegisterToDwarfCode(name); | |
146 DCHECK_GE(code, 0); | |
147 WriteByte(kSameValue); | |
148 WriteULEB128(code); | |
149 } | |
150 | |
151 void EhFrameWriter::Finish(int code_size) { | |
152 DCHECK_GE(eh_frame_buffer_.length(), kCIESize); | |
153 | |
rmcilroy
2016/06/30 15:23:10
DCHECK(!eh_frame_finalized_)
Stefano Sanfilippo
2016/07/04 18:21:14
Done.
| |
154 // Add padding | |
155 int unpadded_fde_size = eh_frame_buffer_.length() - kCIESize; | |
156 int padded_fde_size = RoundUp(unpadded_fde_size, 8); | |
157 int fde_padding_size = padded_fde_size - unpadded_fde_size; | |
158 | |
159 static const byte kFDEPadding[] = {kNop, kNop, kNop, kNop, | |
160 kNop, kNop, kNop, kNop}; | |
161 DCHECK_LT(fde_padding_size, static_cast<int>(sizeof(kFDEPadding))); | |
162 WriteBytes(&kFDEPadding[0], fde_padding_size); | |
163 | |
164 // Write the size of the FDE now that we know it. | |
165 PatchInt32(kFDEOffset, padded_fde_size); | |
166 | |
167 // Write the size and offset to procedure. | |
168 PatchInt32(kProcedureAddressOffset, | |
169 -(RoundUp(code_size, 8) + kProcedureAddressOffset)); | |
170 PatchInt32(kProcedureSizeOffset, code_size); | |
171 | |
172 // Terminate the .eh_frame. | |
173 static const byte kEhFrameTerminator[kEhFrameTerminatorSize] = {0}; | |
174 WriteBytes(&kEhFrameTerminator[0], kEhFrameTerminatorSize); | |
175 | |
176 // Write .eh_frame_hdr | |
177 EhFrameHdr eh_frame_hdr(code_size, eh_frame_offset()); | |
178 WriteBytes(reinterpret_cast<const byte*>(&eh_frame_hdr), | |
179 EhFrameHdr::kRecordSize); | |
180 | |
181 #if DEBUG | |
182 eh_frame_finalised_ = true; | |
183 #endif | |
184 } | |
185 | |
186 void EhFrameWriter::GetEhFrame(CodeDesc* desc) { | |
187 DCHECK(eh_frame_finalised_); | |
188 desc->unwinding_info_size = eh_frame_buffer_.length(); | |
189 desc->unwinding_info = eh_frame_buffer_.begin(); | |
190 } | |
191 | |
192 void EhFrameWriter::WriteULEB128(uint32_t value) { | |
193 do { | |
194 byte chunk = value & 0x7f; | |
195 value >>= 7; | |
196 if (value != 0) chunk |= 0x80; | |
197 eh_frame_buffer_.Add(chunk); | |
198 } while (value != 0); | |
199 } | |
200 | |
201 #ifdef ENABLE_DISASSEMBLER | |
202 | |
203 namespace { | |
204 | |
205 uint32_t DecodeULEB128(const byte* encoded, int* encoded_size) { | |
206 const byte* cur = encoded; | |
207 uint32_t decoded_value = 0; | |
208 | |
209 do { | |
210 decoded_value <<= 7; | |
211 decoded_value += static_cast<uint32_t>(static_cast<unsigned>(*cur & 0x7f)); | |
212 } while (*cur++ >= 0x80); | |
213 | |
214 *encoded_size = static_cast<int>(cur - encoded); | |
215 return decoded_value; | |
216 } | |
217 | |
218 class StreamModifiersScope final { | |
219 public: | |
220 explicit StreamModifiersScope(std::ostream* stream) | |
221 : stream_(stream), flags_(stream->flags()) {} | |
222 ~StreamModifiersScope() { stream_->flags(flags_); } | |
223 | |
224 private: | |
225 std::ostream* stream_; | |
226 std::ios::fmtflags flags_; | |
227 }; | |
228 | |
229 } // namespace | |
230 | |
231 // static | |
232 void EhFrameWriter::DumpDWARFDirectives(std::ostream& stream, // NOLINT | |
233 const byte* begin, const byte* end) { | |
234 StreamModifiersScope modifiers_scope(&stream); | |
235 | |
236 const byte* cur = begin; | |
237 uint32_t offset_in_procedure = 0; | |
238 | |
239 while (cur != end) { | |
240 stream << reinterpret_cast<const void*>(cur) << " "; | |
241 | |
242 if (((*cur >> kLocationMaskSize) & 0xff) == kLocationTag) { | |
243 int value = *cur & kLocationMask; | |
244 cur += sizeof(byte); | |
245 offset_in_procedure += value; | |
246 stream << "| pc_offset=" << std::dec << offset_in_procedure | |
247 << " (delta=0x" << std::hex << value << ")\n"; | |
248 continue; | |
249 } | |
250 | |
251 if (((*cur >> kSavedRegisterMaskSize) & 0xff) == kSavedRegisterTag) { | |
252 stream << "| " << DwarfRegisterCodeToString(*cur & kLocationMask); | |
253 cur += sizeof(byte); | |
254 int decoded_size = 0; | |
255 int decoded_offset = static_cast<int>(DecodeULEB128(cur, &decoded_size)); | |
256 cur += decoded_size; | |
257 stream << " saved at base" << std::showpos << std::dec | |
258 << decoded_offset * kDataAlignmentFactor << '\n'; | |
259 continue; | |
260 } | |
261 | |
262 uint8_t bytecode = *cur; | |
263 cur += sizeof(byte); | |
264 | |
265 switch (bytecode) { | |
266 case kAdvanceLoc1: { | |
267 unsigned value = *reinterpret_cast<const uint8_t*>(cur); | |
268 cur += sizeof(uint8_t); | |
269 offset_in_procedure += value; | |
270 stream << "| pc_offset=" << std::dec << offset_in_procedure | |
271 << " (delta=0x" << std::hex << value << ")\n"; | |
272 break; | |
273 } | |
274 case kAdvanceLoc2: { | |
275 uint16_t value = ReadUnalignedUInt16(cur); | |
276 cur += sizeof(uint16_t); | |
277 offset_in_procedure += value; | |
278 stream << "| pc_offset=" << std::dec << offset_in_procedure | |
279 << " (delta=0x" << std::hex << value << ")\n"; | |
280 break; | |
281 } | |
282 case kAdvanceLoc4: { | |
283 uint32_t value = ReadUnalignedUInt32(cur); | |
284 offset_in_procedure += value; | |
285 cur += sizeof(uint32_t); | |
286 stream << "| pc_offset=" << std::dec << offset_in_procedure | |
287 << " (delta=0x" << std::hex << value << ")\n"; | |
288 break; | |
289 } | |
290 case kDefCFA: { | |
291 int decoded_size = 0; | |
292 int base_register = DecodeULEB128(cur, &decoded_size); | |
293 cur += decoded_size; | |
294 int base_offset = DecodeULEB128(cur, &decoded_size); | |
295 cur += decoded_size; | |
296 stream << "| base_register=" << DwarfRegisterCodeToString(base_register) | |
297 << ", base_offset=0x" << std::hex << base_offset << '\n'; | |
298 break; | |
299 } | |
300 case kDefCFAOffset: { | |
301 int decoded_size = 0; | |
302 stream << "| base_offset=0x" << std::hex | |
303 << DecodeULEB128(cur, &decoded_size) << '\n'; | |
304 cur += decoded_size; | |
305 break; | |
306 } | |
307 case kDefCFARegister: { | |
308 int decoded_size = 0; | |
309 stream << "| base_register=" | |
310 << DwarfRegisterCodeToString(DecodeULEB128(cur, &decoded_size)) | |
311 << '\n'; | |
312 cur += decoded_size; | |
313 break; | |
314 } | |
315 case kSameValue: { | |
316 int decoded_size = 0; | |
317 stream << "| " | |
318 << DwarfRegisterCodeToString(DecodeULEB128(cur, &decoded_size)) | |
319 << " to initial value\n"; | |
320 cur += decoded_size; | |
321 break; | |
322 } | |
323 case kNop: | |
324 stream << "| nop\n"; | |
325 break; | |
326 default: | |
327 UNREACHABLE(); | |
328 return; | |
329 } | |
330 } | |
331 } | |
332 | |
333 // static | |
334 void EhFrameWriter::DisassembleToStream(std::ostream& stream, // NOLINT | |
335 const byte* start, const byte* end) { | |
336 const byte* cie_directives_start = start + kInitialStateOffsetInCIE; | |
337 const byte* cie_directives_end = start + kCIESize; | |
338 DCHECK_LE(cie_directives_start, cie_directives_end); | |
339 | |
340 stream << reinterpret_cast<const void*>(start) << " .eh_frame: CIE\n"; | |
341 DumpDWARFDirectives(stream, cie_directives_start, cie_directives_end); | |
342 | |
343 const byte* procedure_offset_address = start + kProcedureAddressOffset; | |
344 int32_t procedure_offset = | |
345 ReadUnalignedValue<int32_t>(procedure_offset_address); | |
346 | |
347 const byte* procedure_size_address = start + kProcedureSizeOffset; | |
348 uint32_t procedure_size = ReadUnalignedUInt32(procedure_size_address); | |
349 | |
350 const byte* fde_start = start + kCIESize; | |
351 stream << reinterpret_cast<const void*>(fde_start) << " .eh_frame: FDE\n" | |
352 << reinterpret_cast<const void*>(procedure_offset_address) | |
353 << " | procedure offset=" << procedure_offset << '\n' | |
354 << reinterpret_cast<const void*>(procedure_size_address) | |
355 << " | procedure size=" << procedure_size << '\n'; | |
356 | |
357 const byte* fde_directives_start = start + kFDEDirectivesOffset; | |
358 const byte* fde_directives_end = | |
359 end - EhFrameHdr::kRecordSize - kEhFrameTerminatorSize; | |
360 DCHECK_LE(fde_directives_start, fde_directives_end); | |
361 | |
362 DumpDWARFDirectives(stream, fde_directives_start, fde_directives_end); | |
363 | |
364 const byte* fde_terminator_start = fde_directives_end; | |
365 stream << reinterpret_cast<const void*>(fde_terminator_start) | |
366 << " .eh_frame: terminator\n"; | |
367 | |
368 const byte* eh_frame_hdr_start = | |
369 fde_terminator_start + kEhFrameTerminatorSize; | |
370 stream << reinterpret_cast<const void*>(eh_frame_hdr_start) | |
371 << " .eh_frame_hdr: placeholder\n"; | |
372 } | |
373 | |
374 #endif | |
21 | 375 |
22 // | 376 // |
23 // In order to calculate offsets in the .eh_frame_hdr, we must know the layout | 377 // In order to calculate offsets in the .eh_frame_hdr, we must know the layout |
24 // of the DSO generated by perf inject, which is assumed to be the following: | 378 // of the DSO generated by perf inject, which is assumed to be the following: |
25 // | 379 // |
26 // | ... | | | 380 // | ... | | |
27 // +---------------+ <-- (F) --- | Larger offsets in file | 381 // +---------------+ <-- (F) --- | Larger offsets in file |
28 // | | ^ | | 382 // | | ^ | |
29 // | Instructions | | .text v | 383 // | Instructions | | .text v |
30 // | | v | 384 // | | v |
(...skipping 13 matching lines...) Expand all Loading... | |
44 // | version | ^ | 398 // | version | ^ |
45 // +---------------+ | | 399 // +---------------+ | |
46 // | encoding | | | 400 // | encoding | | |
47 // | specifiers | | | 401 // | specifiers | | |
48 // +---------------+ <---(A) | .eh_frame_hdr | 402 // +---------------+ <---(A) | .eh_frame_hdr |
49 // | offset to | | | 403 // | offset to | | |
50 // | .eh_frame | | | 404 // | .eh_frame | | |
51 // +---------------+ | | 405 // +---------------+ | |
52 // | ... | ... | 406 // | ... | ... |
53 // | 407 // |
54 // (F) is aligned at a 16-byte boundary. | 408 // (F) is aligned to a 16-byte boundary. |
55 // (D) is aligned at a 8-byte boundary. | 409 // (D) is aligned to a 8-byte boundary. |
56 // (B) is aligned at a 4-byte boundary. | 410 // (B) is aligned to a 4-byte boundary. |
57 // (E), (C) and (A) have no alignment requirements. | 411 // (C) is aligned to an addressing unit size boundary. |
412 // (E) and (A) have no alignment requirements. | |
58 // | 413 // |
59 // The distance between (A) and (B) is 4 bytes. | 414 // The distance between (A) and (B) is 4 bytes. |
60 // | 415 // |
61 // The size of the .eh_frame is required to be a multiple of the pointer size, | 416 // The size of the FDE is required to be a multiple of the pointer size, which |
62 // which means that (B) will be naturally aligned to a 4-byte boundary on all | 417 // means that (B) will be naturally aligned to a 4-byte boundary on all the |
63 // the architectures we support. | 418 // architectures we support. |
64 // | 419 // |
65 // Because (E) has no alignment requirements, there is padding between (E) and | 420 // Because (E) has no alignment requirements, there is padding between (E) and |
66 // (D). (F) is aligned at a 16-byte boundary, thus to a 8-byte one as well. | 421 // (D). (F) is aligned at a 16-byte boundary, thus to a 8-byte one as well. |
67 // | 422 // |
68 EhFrameHdr::EhFrameHdr(Code* code) { | 423 EhFrameHdr::EhFrameHdr(int code_size, int eh_frame_size) { |
69 int code_size = code->is_crankshafted() ? code->safepoint_table_offset() | 424 version_ = kEhFrameHdrVersion; |
70 : code->instruction_size(); | |
71 version_ = 1; | |
72 eh_frame_ptr_encoding_ = DW_EH_PE_sdata4 | DW_EH_PE_pcrel; | |
73 lut_size_encoding_ = DW_EH_PE_udata4; | |
74 lut_entries_encoding_ = DW_EH_PE_sdata4 | DW_EH_PE_datarel; | |
75 | 425 |
76 // .eh_frame pointer and LUT | 426 eh_frame_ptr_encoding_ = kSData4 | kPcRel; |
77 if (code->has_unwinding_info()) { | 427 lut_size_encoding_ = kUData4; |
78 DCHECK_GE(code->unwinding_info_size(), EhFrameHdr::kRecordSize); | 428 lut_entries_encoding_ = kSData4 | kDataRel; |
79 int eh_frame_size = code->unwinding_info_size() - EhFrameHdr::kRecordSize; | |
80 | 429 |
81 offset_to_eh_frame_ = | 430 offset_to_eh_frame_ = -(eh_frame_size + kFDEVersionSize + |
82 -(eh_frame_size + kVersionSize + kEncodingSpecifiersSize); // A -> D | 431 kFDEEncodingSpecifiersSize); // A -> D |
83 lut_entries_number_ = 1; | 432 lut_entries_number_ = 1; |
84 offset_to_procedure_ = -(RoundUp(code_size, 8) + eh_frame_size); // B -> F | 433 offset_to_procedure_ = -(RoundUp(code_size, 8) + eh_frame_size); // B -> F |
85 offset_to_fde_ = -(eh_frame_size - kCIESize); // B -> C | 434 offset_to_fde_ = -(eh_frame_size - EhFrameWriter::kCIESize); // B -> C |
86 } else { | 435 } |
87 // Create a dummy table | 436 |
88 offset_to_eh_frame_ = 0; | 437 // static |
89 lut_entries_number_ = 0; | 438 EhFrameHdr EhFrameHdr::MakeDummy() { |
90 offset_to_procedure_ = 0; | 439 EhFrameHdr dummy_frame; |
91 offset_to_fde_ = 0; | 440 dummy_frame.version_ = kEhFrameHdrVersion; |
92 } | 441 dummy_frame.eh_frame_ptr_encoding_ = kSData4 | kPcRel; |
442 dummy_frame.lut_size_encoding_ = kUData4; | |
443 dummy_frame.lut_entries_encoding_ = kSData4 | kDataRel; | |
444 dummy_frame.offset_to_eh_frame_ = 0; | |
445 dummy_frame.lut_entries_number_ = 0; | |
446 dummy_frame.offset_to_procedure_ = 0; | |
447 dummy_frame.offset_to_fde_ = 0; | |
448 return dummy_frame; | |
93 } | 449 } |
94 | 450 |
95 } // namespace internal | 451 } // namespace internal |
96 } // namespace v8 | 452 } // namespace v8 |
OLD | NEW |