Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: src/eh-frame.cc

Issue 2023503002: Reland Implement .eh_frame writer and disassembler. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@eh-frame-base
Patch Set: Improve disassembler, get rid of PatchProcedureBoundariesInEhFrame. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 <iomanip>
7 #include <limits>
8 #include <ostream>
rmcilroy 2016/06/28 10:37:33 Newline before <iomanip> and after <ostream> (a ne
Stefano Sanfilippo 2016/06/29 15:16:20 Done.
6 #include "src/objects-inl.h" 9 #include "src/objects-inl.h"
7 #include "src/objects.h" 10 #include "src/objects.h"
8 11
12 #if V8_TARGET_ARCH_X64
13 #include "src/x64/eh-frame-x64.h"
14 #elif V8_TARGET_ARCH_ARM
15 #include "src/arm/eh-frame-arm.h"
16 #elif V8_TARGET_ARCH_ARM64
17 #include "src/arm64/eh-frame-arm64.h"
18 #else
19
20 // Dummy placeholders
21
9 namespace v8 { 22 namespace v8 {
10 namespace internal { 23 namespace internal {
11 24
12 static const int DW_EH_PE_pcrel = 0x10; 25 static const int kInitialCFARegister = 0;
13 static const int DW_EH_PE_datarel = 0x30; 26 static const int kInitialCFAOffset = 0;
14 static const int DW_EH_PE_udata4 = 0x03; 27 static const int kDataAlignmentFactor = 1;
15 static const int DW_EH_PE_sdata4 = 0x0b; 28 static const int kInitialStateOffsetInCIE = 0;
16 29
17 const int EhFrameHdr::kCIESize = 0; 30 static const byte kCIE[8] = {0};
31
32 const char* GetRegisterName(int register_number) {
33 UNIMPLEMENTED();
34 return nullptr;
35 }
36
37 } // namespace internal
38 } // namespace v8
39
40 #endif
41
42 namespace v8 {
43 namespace internal {
44
45 static_assert(sizeof(kCIE) % kPointerSize == 0,
46 "CIE must be aligned to pointer size");
47
48 static const char DW_CFA_nop = 0x00;
49
50 static const byte DW_CFA_advance_loc1 = 0x02;
51 static const byte DW_CFA_advance_loc2 = 0x03;
52 static const byte DW_CFA_advance_loc4 = 0x04;
53 static const byte DW_CFA_same_value = 0x08;
54 static const byte DW_CFA_def_cfa = 0x0c;
55 static const byte DW_CFA_def_cfa_register = 0x0d;
56 static const byte DW_CFA_def_cfa_offset = 0x0e;
rmcilroy 2016/06/28 10:37:33 Move all these constants to be private constants i
Stefano Sanfilippo 2016/06/29 15:16:20 Done.
57
58 static const byte DW_EH_PE_pcrel = 0x10;
59 static const byte DW_EH_PE_datarel = 0x30;
60 static const byte DW_EH_PE_udata4 = 0x03;
61 static const byte DW_EH_PE_sdata4 = 0x0b;
rmcilroy 2016/06/28 10:37:32 These are all unused - either move to the arch spe
Stefano Sanfilippo 2016/06/29 15:16:21 Actually, these are used in the EhFrameHdr ctor be
62
63 static const byte kEhFrameTerminator[] = {0x00, 0x00, 0x00, 0x00};
64
65 static const byte kFDEPadding[] = {DW_CFA_nop, DW_CFA_nop, DW_CFA_nop,
rmcilroy 2016/06/28 10:37:32 Move into finish function where it's used.
Stefano Sanfilippo 2016/06/29 15:16:20 Done.
66 DW_CFA_nop, DW_CFA_nop, DW_CFA_nop,
67 DW_CFA_nop, DW_CFA_nop};
68
69 const char* GetRegisterName(int register_number);
70
71 const int EhFrameWriter::kCIESize = sizeof(kCIE);
rmcilroy 2016/06/28 10:37:33 I think you need STATIC_CONST_MEMBER_DEFINITION fo
Stefano Sanfilippo 2016/06/29 15:16:21 Done.
72
73 EhFrameWriter::EhFrameWriter()
74 : last_pc_offset_(0),
75 cfa_register_(kInitialCFARegister),
76 cfa_offset_(kInitialCFAOffset) {
77 WriteBytes(&kCIE[0], sizeof(kCIE));
78 WriteFDEHeader();
79 }
80
81 static const int kFDEHeaderSize = 4 * kInt32Size + 1;
rmcilroy 2016/06/28 10:37:32 Move this with the rest of the constants.
Stefano Sanfilippo 2016/06/29 15:16:20 Done.
82
83 void EhFrameWriter::WriteFDEHeader() {
84 WriteInt32(0); // Placeholder for size of the FDE
85 WriteInt32(sizeof(kCIE) + kInt32Size); // Backwards offset to the CIE
86 WriteInt32(0); // Placeholder for pointer to procedure
87 WriteInt32(0); // Placeholder for size of the procedure
88 WriteByte(0); // No augmentation data
89 }
90
91 void EhFrameWriter::AdvanceLocation(int pc_offset) {
92 DCHECK_GE(pc_offset, last_pc_offset_);
93 // No way to handle 64bit values, but that's an unrealistic delta anyway.
rmcilroy 2016/06/28 10:37:32 No need for this comment, the pc_offset is already
Stefano Sanfilippo 2016/06/29 15:16:21 Done.
94 uint32_t delta = pc_offset - last_pc_offset_;
95
96 if (delta <= 0x3f) {
97 WriteByte((1 << 6) | (delta & 0x3f)); // DW_CFA_advance_loc
rmcilroy 2016/06/28 10:37:33 use a constant instead of 0x3f
Stefano Sanfilippo 2016/06/29 15:16:20 Done.
98 } else if (delta <= std::numeric_limits<uint8_t>::max()) {
rmcilroy 2016/06/28 10:37:32 kMaxUInt8
Stefano Sanfilippo 2016/06/29 15:16:20 Done.
99 WriteByte(DW_CFA_advance_loc1);
100 WriteByte(delta);
101 } else if (delta <= std::numeric_limits<uint16_t>::max()) {
rmcilroy 2016/06/28 10:37:32 kMaxUInt16
Stefano Sanfilippo 2016/06/29 15:16:21 Done.
102 WriteByte(DW_CFA_advance_loc2);
103 WriteInt16(delta);
104 } else {
105 WriteByte(DW_CFA_advance_loc4);
106 WriteInt32(delta);
107 }
108
109 last_pc_offset_ = pc_offset;
110 }
111
112 void EhFrameWriter::DefineCFAOffset(int cfa_offset) {
113 DCHECK_GE(cfa_offset, 0);
114 WriteByte(DW_CFA_def_cfa_offset);
115 WriteULEB128(cfa_offset);
116 cfa_offset_ = cfa_offset;
117 }
118
119 void EhFrameWriter::DefineCFARegister(int code) {
120 WriteByte(DW_CFA_def_cfa_register);
121 WriteULEB128(code);
122 cfa_register_ = code;
123 }
124
125 void EhFrameWriter::DefineCFARegisterOffset(int code, int cfa_offset) {
126 WriteByte(DW_CFA_def_cfa);
127 WriteULEB128(code);
128 WriteULEB128(cfa_offset);
129 cfa_offset_ = cfa_offset;
130 cfa_register_ = code;
131 }
132
133 // Following the DWARF terminology, this method should be called Offset(),
134 // but that name is way too generic.
rmcilroy 2016/06/28 10:37:33 Don't use the DWARF terminology - could you rename
Stefano Sanfilippo 2016/06/29 15:16:20 Comment removed. Please let me know if you think t
135 void EhFrameWriter::SaveRegisterToStack(int code, int offset) {
136 DCHECK_GE(code, 0);
137 DCHECK_LE(code, 0x3f);
138 DCHECK_EQ(offset % kDataAlignmentFactor, 0);
139
140 WriteByte((2 << 6) | (code & 0x3f)); // DW_CFA_offset
rmcilroy 2016/06/28 10:37:33 constant
Stefano Sanfilippo 2016/06/29 15:16:21 Done.
141 WriteULEB128(offset / std::abs(kDataAlignmentFactor));
142 }
143
144 // This method should be SameValue(), but again that name is too generic.
rmcilroy 2016/06/28 10:37:32 ditto
Stefano Sanfilippo 2016/06/29 15:16:21 Done.
145 void EhFrameWriter::RegisterHasInitialValue(int code) {
146 DCHECK_GE(code, 0);
147 WriteByte(DW_CFA_same_value);
148 WriteULEB128(code);
149 }
150
151 void EhFrameWriter::Finish(int code_size) {
152 DCHECK_GE(unwinding_info_.length(), kCIESize);
153
154 // Add padding
155 int unpadded_fde_size = unwinding_info_.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 DCHECK_LT(fde_padding_size, static_cast<int>(sizeof(kFDEPadding)));
160 WriteBytes(&kFDEPadding[0], fde_padding_size);
161
162 // Write the size of the FDE now that we know it.
163 int fde_offset = kCIESize;
rmcilroy 2016/06/28 10:37:33 Please have these as static consts, and DCHECK tha
Stefano Sanfilippo 2016/06/29 15:16:20 Done.
164 PatchInt32(fde_offset, padded_fde_size);
165
166 // Write the size and offset to procedure.
167 int function_boundaries_offset = fde_offset + 2 * kInt32Size;
168 PatchInt32(function_boundaries_offset,
169 -(RoundUp(code_size, 8) + function_boundaries_offset));
170
171 int code_size_offset = function_boundaries_offset + kInt32Size;
172 PatchInt32(code_size_offset, code_size);
173
174 // Terminate the .eh_frame.
175 WriteBytes(&kEhFrameTerminator[0], sizeof(kEhFrameTerminator));
176
177 // Add .eh_frame_hdr placeholder.
178 unwinding_info_.AddBlock(0x00, EhFrameHdr::kRecordSize);
rmcilroy 2016/06/28 10:37:33 Could you not just add the actual EhFrameHdr block
Stefano Sanfilippo 2016/06/29 15:16:21 Yes. This change resulted in a bit of refactoring
179 }
180
181 // Remember to call Finish() before GetUnwindingInfo()!
rmcilroy 2016/06/28 10:37:33 This comment here is useless since it's in the cc
Stefano Sanfilippo 2016/06/29 15:16:20 Done. The only way to know if Finish() was called
182 void EhFrameWriter::GetEhFrame(CodeDesc* desc) {
183 desc->unwinding_info_size = unwinding_info_.length();
184 desc->unwinding_info = unwinding_info_.begin();
185 }
186
187 void EhFrameWriter::WriteULEB128(uint32_t value) {
188 do {
189 byte chunk = value & 0x7f;
190 value >>= 7;
191 if (value != 0) chunk |= 0x80;
192 unwinding_info_.Add(chunk);
193 } while (value != 0);
194 }
195
196 static uint32_t DecodeULEB128(const byte* encoded, int* encoded_size) {
197 const byte* cur = encoded;
198 uint32_t decoded_value = 0;
199
200 do {
201 decoded_value <<= 7;
202 decoded_value += static_cast<uint32_t>(static_cast<unsigned>(*cur & 0x7f));
203 } while (*cur++ >= 0x80);
204
205 *encoded_size = static_cast<int>(cur - encoded);
206 return decoded_value;
207 }
208
rmcilroy 2016/06/28 10:37:32 #ifdef ENABLE_DISASSEMBLER
Stefano Sanfilippo 2016/06/29 15:16:21 Done.
209 namespace {
210
211 class StreamModifiersScope final {
212 public:
213 explicit StreamModifiersScope(std::ostream* stream)
214 : stream_(stream), flags_(stream->flags()) {
215 DCHECK_NOT_NULL(stream);
rmcilroy 2016/06/28 10:37:33 No need for this, it will already have exploded in
Stefano Sanfilippo 2016/06/29 15:16:21 Fair enough. Done.
216 }
217 ~StreamModifiersScope() { stream_->flags(flags_); }
218
219 private:
220 std::ostream* stream_;
221 std::ios::fmtflags flags_;
222 };
223
224 } // namespace
225
226 static void DumpDWARFDirectives(std::ostream& stream, // NOLINT
rmcilroy 2016/06/28 10:37:32 Put this in the annonymous name space instead of s
Stefano Sanfilippo 2016/06/29 15:16:21 Done.
227 const byte* begin, const byte* end) {
228 StreamModifiersScope modifiers_scope(&stream);
229
230 const byte* cur = begin;
231 uint32_t offset_in_procedure = 0;
232
233 while (cur != end) {
234 stream << reinterpret_cast<const void*>(cur) << " ";
235
236 if (((*cur >> 6) & 0xff) == 1) {
rmcilroy 2016/06/28 10:37:32 Use constants
Stefano Sanfilippo 2016/06/29 15:16:20 Done.
237 int value = *cur & 0x3f;
238 cur += sizeof(byte);
239 offset_in_procedure += value;
240 stream << "| pc_offset=" << std::dec << offset_in_procedure
241 << " (delta=0x" << std::hex << value << ")\n";
242 continue;
243 }
244
245 if (((*cur >> 6) & 0xff) == 2) {
rmcilroy 2016/06/28 10:37:33 ditto
Stefano Sanfilippo 2016/06/29 15:16:21 Done.
246 stream << "| " << GetRegisterName(*cur & 0x3f);
247 cur += sizeof(byte);
248 int decoded_size = 0;
249 int decoded_offset = static_cast<int>(DecodeULEB128(cur, &decoded_size));
250 cur += decoded_size;
251 stream << " saved at cfa" << std::showpos << std::dec
252 << decoded_offset * kDataAlignmentFactor << '\n';
253 continue;
254 }
255
256 switch (*cur) {
257 case DW_CFA_advance_loc1: {
258 cur += sizeof(byte);
rmcilroy 2016/06/28 10:37:32 You always cur += sizeof(byte) - just do it once a
Stefano Sanfilippo 2016/06/29 15:16:21 Done.
259 unsigned value = *reinterpret_cast<const uint8_t*>(cur);
260 cur += sizeof(uint8_t);
261 offset_in_procedure += value;
262 stream << "| pc_offset=" << std::dec << offset_in_procedure
263 << " (delta=0x" << std::hex << value << ")\n";
264 break;
265 }
266 case DW_CFA_advance_loc2: {
267 cur += sizeof(byte);
268 uint16_t value = *reinterpret_cast<const uint16_t*>(cur);
rmcilroy 2016/06/28 10:37:33 This won't work on machines that don't support una
Stefano Sanfilippo 2016/06/29 15:16:21 Done.
269 cur += sizeof(uint16_t);
270 offset_in_procedure += value;
271 stream << "| pc_offset=" << std::dec << offset_in_procedure
272 << " (delta=0x" << std::hex << value << ")\n";
273 break;
274 }
275 case DW_CFA_advance_loc4: {
276 cur += sizeof(byte);
277 uint32_t value = *reinterpret_cast<const uint32_t*>(cur);
278 offset_in_procedure += value;
279 cur += sizeof(uint32_t);
280 stream << "| pc_offset=" << std::dec << offset_in_procedure
281 << " (delta=0x" << std::hex << value << ")\n";
282 break;
283 }
284 case DW_CFA_def_cfa: {
285 cur += sizeof(byte);
286 int decoded_size = 0;
287 int cfa_register = DecodeULEB128(cur, &decoded_size);
288 cur += decoded_size;
289 int cfa_initial_offset = DecodeULEB128(cur, &decoded_size);
290 cur += decoded_size;
291 stream << "| cfa_register=" << GetRegisterName(cfa_register)
292 << ", cfa_offset=0x" << std::hex << cfa_initial_offset << '\n';
293 break;
294 }
295 case DW_CFA_def_cfa_offset: {
296 cur += sizeof(byte);
297 int decoded_size = 0;
298 stream << "| cfa_offset=0x" << std::hex
299 << DecodeULEB128(cur, &decoded_size) << '\n';
300 cur += decoded_size;
301 break;
302 }
303 case DW_CFA_def_cfa_register: {
304 cur += sizeof(byte);
305 int decoded_size = 0;
306 stream << "| cfa_register="
307 << GetRegisterName(DecodeULEB128(cur, &decoded_size)) << '\n';
308 cur += decoded_size;
309 break;
310 }
311 case DW_CFA_same_value: {
312 cur += sizeof(byte);
313 int decoded_size = 0;
314 stream << "| " << GetRegisterName(DecodeULEB128(cur, &decoded_size))
315 << " to initial value\n";
316 cur += decoded_size;
317 break;
318 }
319 case DW_CFA_nop:
320 cur += sizeof(byte);
321 stream << "| nop\n";
322 break;
323 default:
324 stream << "| UNKNOWN BYTECODE 0x" << std::hex << std::setw(2)
325 << std::setfill('0') << static_cast<unsigned>(*cur) << '\n';
rmcilroy 2016/06/28 10:37:32 Just the UNREACHABLE here, no need to print the un
Stefano Sanfilippo 2016/06/29 15:16:21 Done.
326 UNREACHABLE();
327 return;
328 }
329 }
330 }
331
332 void EhFrameWriter::DisassembleToStream(std::ostream& stream, // NOLINT
333 const byte* start, const byte* end) {
334 const byte* cie_directives_start = start + kInitialStateOffsetInCIE;
335 const byte* cie_directives_end = start + sizeof(kCIE);
336 DCHECK_LE(cie_directives_start, cie_directives_end);
337
338 const byte* fde_start = start + sizeof(kCIE);
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 = fde_start + 2 * kInt32Size;
rmcilroy 2016/06/28 10:37:32 Constant offsets
Stefano Sanfilippo 2016/06/29 15:16:20 Done.
344 int32_t procedure_offset =
345 *reinterpret_cast<const int32_t*>(procedure_offset_address);
346
347 const byte* procedure_size_address = fde_start + 3 * kInt32Size;
rmcilroy 2016/06/28 10:37:32 ditto
Stefano Sanfilippo 2016/06/29 15:16:21 Done.
348 uint32_t procedure_size =
349 *reinterpret_cast<const uint32_t*>(procedure_size_address);
350
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 = fde_start + kFDEHeaderSize;
358 const byte* fde_directives_end =
359 end - EhFrameHdr::kRecordSize - sizeof(kEhFrameTerminator);
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 + sizeof(kEhFrameTerminator);
370 stream << reinterpret_cast<const void*>(eh_frame_hdr_start)
371 << " .eh_frame_hdr: placeholder\n";
rmcilroy 2016/06/28 10:37:32 Why bother printing the terminator and placeholder
Stefano Sanfilippo 2016/06/29 15:16:20 I believe it might be useful to check the exact la
372 }
18 373
19 static const int kVersionSize = 1; 374 static const int kVersionSize = 1;
20 static const int kEncodingSpecifiersSize = 3; 375 static const int kEncodingSpecifiersSize = 3;
rmcilroy 2016/06/28 10:37:32 Move with other constants
Stefano Sanfilippo 2016/06/29 15:16:21 Done.
21 376
22 // 377 //
23 // In order to calculate offsets in the .eh_frame_hdr, we must know the layout 378 // 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: 379 // of the DSO generated by perf inject, which is assumed to be the following:
25 // 380 //
26 // | ... | | 381 // | ... | |
27 // +---------------+ <-- (F) --- | Larger offsets in file 382 // +---------------+ <-- (F) --- | Larger offsets in file
28 // | | ^ | 383 // | | ^ |
29 // | Instructions | | .text v 384 // | Instructions | | .text v
30 // | | v 385 // | | v
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
75 430
76 // .eh_frame pointer and LUT 431 // .eh_frame pointer and LUT
77 if (code->has_unwinding_info()) { 432 if (code->has_unwinding_info()) {
78 DCHECK_GE(code->unwinding_info_size(), EhFrameHdr::kRecordSize); 433 DCHECK_GE(code->unwinding_info_size(), EhFrameHdr::kRecordSize);
79 int eh_frame_size = code->unwinding_info_size() - EhFrameHdr::kRecordSize; 434 int eh_frame_size = code->unwinding_info_size() - EhFrameHdr::kRecordSize;
80 435
81 offset_to_eh_frame_ = 436 offset_to_eh_frame_ =
82 -(eh_frame_size + kVersionSize + kEncodingSpecifiersSize); // A -> D 437 -(eh_frame_size + kVersionSize + kEncodingSpecifiersSize); // A -> D
83 lut_entries_number_ = 1; 438 lut_entries_number_ = 1;
84 offset_to_procedure_ = -(RoundUp(code_size, 8) + eh_frame_size); // B -> F 439 offset_to_procedure_ = -(RoundUp(code_size, 8) + eh_frame_size); // B -> F
85 offset_to_fde_ = -(eh_frame_size - kCIESize); // B -> C 440 offset_to_fde_ = -(eh_frame_size - EhFrameWriter::kCIESize); // B -> C
86 } else { 441 } else {
87 // Create a dummy table 442 // Create a dummy table
88 offset_to_eh_frame_ = 0; 443 offset_to_eh_frame_ = 0;
89 lut_entries_number_ = 0; 444 lut_entries_number_ = 0;
90 offset_to_procedure_ = 0; 445 offset_to_procedure_ = 0;
91 offset_to_fde_ = 0; 446 offset_to_fde_ = 0;
92 } 447 }
93 } 448 }
94 449
95 } // namespace internal 450 } // namespace internal
96 } // namespace v8 451 } // namespace v8
OLDNEW
« src/eh-frame.h ('K') | « src/eh-frame.h ('k') | src/full-codegen/full-codegen.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698