1 // Copyright 2019 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 #include "src/snapshot/embedded/platform-embedded-file-writer-win.h"
6
7 #include <algorithm>
8
9 #include "src/common/globals.h" // For V8_OS_WIN64
10
11 #if defined(V8_OS_WIN64)
12 #include "src/builtins/builtins.h"
13 #include "src/diagnostics/unwinding-info-win64.h"
14 #include "src/snapshot/embedded/embedded-data-inl.h"
15 #include "src/snapshot/embedded/embedded-file-writer.h"
16 #endif // V8_OS_WIN64
17
18 namespace v8 {
19 namespace internal {
20
21 // V8_CC_MSVC is true for both MSVC and clang on windows. clang can handle
22 // __asm__-style inline assembly but MSVC cannot, and thus we need a more
23 // precise compiler detection that can distinguish between the two. clang on
24 // windows sets both __clang__ and _MSC_VER, MSVC sets only _MSC_VER.
25 #if defined(_MSC_VER) && !defined(__clang__)
26 #define V8_COMPILER_IS_MSVC
27 #endif
28
29 // MSVC uses MASM for x86 and x64, while it has a ARMASM for ARM32 and
30 // ARMASM64 for ARM64. Since ARMASM and ARMASM64 accept a slightly tweaked
31 // version of ARM assembly language, they are referred to together in Visual
32 // Studio project files as MARMASM.
33 //
34 // ARM assembly language docs:
35 // http://infocenter.arm.com/help/topic/com.arm.doc.dui0802b/index.html
36 // Microsoft ARM assembler and assembly language docs:
37 // https://docs.microsoft.com/en-us/cpp/assembler/arm/arm-assembler-reference
38
39 // Name mangling.
40 // Symbols are prefixed with an underscore on 32-bit architectures.
41 #if !defined(V8_TARGET_ARCH_X64) && !defined(V8_TARGET_ARCH_ARM64)
42 #define SYMBOL_PREFIX "_"
43 #else
44 #define SYMBOL_PREFIX ""
45 #endif
46
47 // Notes:
48 //
49 // Cross-bitness builds are unsupported. It's thus safe to detect bitness
50 // through compile-time defines.
51 //
52 // Cross-compiler builds (e.g. with mixed use of clang / MSVC) are likewise
53 // unsupported and hence the compiler can also be detected through compile-time
54 // defines.
55
56 namespace {
57
58 #if defined(V8_OS_WIN_X64)
59
WriteUnwindInfoEntry(PlatformEmbeddedFileWriterWin * w,const char * unwind_info_symbol,const char * embedded_blob_data_symbol,uint64_t rva_start,uint64_t rva_end)60 void WriteUnwindInfoEntry(PlatformEmbeddedFileWriterWin* w,
61 const char* unwind_info_symbol,
62 const char* embedded_blob_data_symbol,
63 uint64_t rva_start, uint64_t rva_end) {
64 w->DeclareRvaToSymbol(embedded_blob_data_symbol, rva_start);
65 w->DeclareRvaToSymbol(embedded_blob_data_symbol, rva_end);
66 w->DeclareRvaToSymbol(unwind_info_symbol);
67 }
68
EmitUnwindData(PlatformEmbeddedFileWriterWin * w,const char * unwind_info_symbol,const char * embedded_blob_data_symbol,const EmbeddedData * blob,const win64_unwindinfo::BuiltinUnwindInfo * unwind_infos)69 void EmitUnwindData(PlatformEmbeddedFileWriterWin* w,
70 const char* unwind_info_symbol,
71 const char* embedded_blob_data_symbol,
72 const EmbeddedData* blob,
73 const win64_unwindinfo::BuiltinUnwindInfo* unwind_infos) {
74 // Emit an UNWIND_INFO (XDATA) struct, which contains the unwinding
75 // information that is used for all builtin functions.
76 DCHECK(win64_unwindinfo::CanEmitUnwindInfoForBuiltins());
77 w->Comment("xdata for all the code in the embedded blob.");
78 w->DeclareExternalFunction(CRASH_HANDLER_FUNCTION_NAME_STRING);
79
80 w->StartXdataSection();
81 {
82 w->DeclareLabel(unwind_info_symbol);
83
84 std::vector<uint8_t> xdata =
85 win64_unwindinfo::GetUnwindInfoForBuiltinFunctions();
86 DCHECK(!xdata.empty());
87
88 w->IndentedDataDirective(kByte);
89 for (size_t i = 0; i < xdata.size(); i++) {
90 if (i > 0) fprintf(w->fp(), ",");
91 w->HexLiteral(xdata[i]);
92 }
93 w->Newline();
94
95 w->Comment(" ExceptionHandler");
96 w->DeclareRvaToSymbol(CRASH_HANDLER_FUNCTION_NAME_STRING);
97 }
98 w->EndXdataSection();
99 w->Newline();
100
101 // Emit a RUNTIME_FUNCTION (PDATA) entry for each builtin function, as
102 // documented here:
103 // https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64.
104 w->Comment(
105 "pdata for all the code in the embedded blob (structs of type "
106 "RUNTIME_FUNCTION).");
107 w->Comment(" BeginAddress");
108 w->Comment(" EndAddress");
109 w->Comment(" UnwindInfoAddress");
110 w->StartPdataSection();
111 {
112 STATIC_ASSERT(Builtins::kAllBuiltinsAreIsolateIndependent);
113 Address prev_builtin_end_offset = 0;
114 for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
115 ++builtin) {
116 const int builtin_index = static_cast<int>(builtin);
117 // Some builtins are leaf functions from the point of view of Win64 stack
118 // walking: they do not move the stack pointer and do not require a PDATA
119 // entry because the return address can be retrieved from [rsp].
120 if (unwind_infos[builtin_index].is_leaf_function()) continue;
121
122 uint64_t builtin_start_offset = blob->InstructionStartOfBuiltin(builtin) -
123 reinterpret_cast<Address>(blob->code());
124 uint32_t builtin_size = blob->InstructionSizeOfBuiltin(builtin);
125
126 const std::vector<int>& xdata_desc =
127 unwind_infos[builtin_index].fp_offsets();
128 if (xdata_desc.empty()) {
129 // Some builtins do not have any "push rbp - mov rbp, rsp" instructions
130 // to start a stack frame. We still emit a PDATA entry as if they had,
131 // relying on the fact that we can find the previous frame address from
132 // rbp in most cases. Note that since the function does not really start
133 // with a 'push rbp' we need to specify the start RVA in the PDATA entry
134 // a few bytes before the beginning of the function, if it does not
135 // overlap the end of the previous builtin.
136 WriteUnwindInfoEntry(
137 w, unwind_info_symbol, embedded_blob_data_symbol,
138 std::max(prev_builtin_end_offset,
139 builtin_start_offset - win64_unwindinfo::kRbpPrefixLength),
140 builtin_start_offset + builtin_size);
141 } else {
142 // Some builtins have one or more "push rbp - mov rbp, rsp" sequences,
143 // but not necessarily at the beginning of the function. In this case
144 // we want to yield a PDATA entry for each block of instructions that
145 // emit an rbp frame. If the function does not start with 'push rbp'
146 // we also emit a PDATA entry for the initial block of code up to the
147 // first 'push rbp', like in the case above.
148 if (xdata_desc[0] > 0) {
149 WriteUnwindInfoEntry(w, unwind_info_symbol, embedded_blob_data_symbol,
150 std::max(prev_builtin_end_offset,
151 builtin_start_offset -
152 win64_unwindinfo::kRbpPrefixLength),
153 builtin_start_offset + xdata_desc[0]);
154 }
155
156 for (size_t j = 0; j < xdata_desc.size(); j++) {
157 int chunk_start = xdata_desc[j];
158 int chunk_end =
159 (j < xdata_desc.size() - 1) ? xdata_desc[j + 1] : builtin_size;
160 WriteUnwindInfoEntry(w, unwind_info_symbol, embedded_blob_data_symbol,
161 builtin_start_offset + chunk_start,
162 builtin_start_offset + chunk_end);
163 }
164 }
165
166 prev_builtin_end_offset = builtin_start_offset + builtin_size;
167 w->Newline();
168 }
169 }
170 w->EndPdataSection();
171 w->Newline();
172 }
173
174 #elif defined(V8_OS_WIN_ARM64)
175
176 void EmitUnwindData(PlatformEmbeddedFileWriterWin* w,
177 const char* unwind_info_symbol,
178 const char* embedded_blob_data_symbol,
179 const EmbeddedData* blob,
180 const win64_unwindinfo::BuiltinUnwindInfo* unwind_infos) {
181 DCHECK(win64_unwindinfo::CanEmitUnwindInfoForBuiltins());
182
183 // Fairly arbitrary but should fit all symbol names.
184 static constexpr int kTemporaryStringLength = 256;
185 base::EmbeddedVector<char, kTemporaryStringLength> unwind_info_full_symbol;
186
187 // Emit a RUNTIME_FUNCTION (PDATA) entry for each builtin function, as
188 // documented here:
189 // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling.
190 w->Comment(
191 "pdata for all the code in the embedded blob (structs of type "
192 "RUNTIME_FUNCTION).");
193 w->Comment(" BeginAddress");
194 w->Comment(" UnwindInfoAddress");
195 w->StartPdataSection();
196 std::vector<int> code_chunks;
197 std::vector<win64_unwindinfo::FrameOffsets> fp_adjustments;
198
199 STATIC_ASSERT(Builtins::kAllBuiltinsAreIsolateIndependent);
200 for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
201 ++builtin) {
202 const int builtin_index = static_cast<int>(builtin);
203 if (unwind_infos[builtin_index].is_leaf_function()) continue;
204
205 uint64_t builtin_start_offset = blob->InstructionStartOfBuiltin(builtin) -
206 reinterpret_cast<Address>(blob->code());
207 uint32_t builtin_size = blob->InstructionSizeOfBuiltin(builtin);
208
209 const std::vector<int>& xdata_desc =
210 unwind_infos[builtin_index].fp_offsets();
211 const std::vector<win64_unwindinfo::FrameOffsets>& xdata_fp_adjustments =
212 unwind_infos[builtin_index].fp_adjustments();
213 DCHECK_EQ(xdata_desc.size(), xdata_fp_adjustments.size());
214
215 for (size_t j = 0; j < xdata_desc.size(); j++) {
216 int chunk_start = xdata_desc[j];
217 int chunk_end =
218 (j < xdata_desc.size() - 1) ? xdata_desc[j + 1] : builtin_size;
219 int chunk_len = ::RoundUp(chunk_end - chunk_start, kInstrSize);
220
221 while (chunk_len > 0) {
222 int allowed_chunk_len =
223 std::min(chunk_len, win64_unwindinfo::kMaxFunctionLength);
224 chunk_len -= win64_unwindinfo::kMaxFunctionLength;
225
226 // Record the chunk length and fp_adjustment for emitting UNWIND_INFO
227 // later.
228 code_chunks.push_back(allowed_chunk_len);
229 fp_adjustments.push_back(xdata_fp_adjustments[j]);
230 base::SNPrintF(unwind_info_full_symbol, "%s_%u", unwind_info_symbol,
231 code_chunks.size());
232 w->DeclareRvaToSymbol(embedded_blob_data_symbol,
233 builtin_start_offset + chunk_start);
234 w->DeclareRvaToSymbol(unwind_info_full_symbol.begin());
235 }
236 }
237 }
238 w->EndPdataSection();
239 w->Newline();
240
241 // Emit an UNWIND_INFO (XDATA) structs, which contains the unwinding
242 // information.
243 w->DeclareExternalFunction(CRASH_HANDLER_FUNCTION_NAME_STRING);
244 w->StartXdataSection();
245 {
246 for (size_t i = 0; i < code_chunks.size(); i++) {
247 base::SNPrintF(unwind_info_full_symbol, "%s_%u", unwind_info_symbol,
248 i + 1);
249 w->DeclareLabel(unwind_info_full_symbol.begin());
250 std::vector<uint8_t> xdata =
251 win64_unwindinfo::GetUnwindInfoForBuiltinFunction(code_chunks[i],
252 fp_adjustments[i]);
253
254 w->IndentedDataDirective(kByte);
255 for (size_t j = 0; j < xdata.size(); j++) {
256 if (j > 0) fprintf(w->fp(), ",");
257 w->HexLiteral(xdata[j]);
258 }
259 w->Newline();
260 w->DeclareRvaToSymbol(CRASH_HANDLER_FUNCTION_NAME_STRING);
261 }
262 }
263 w->EndXdataSection();
264 w->Newline();
265 }
266
267 #endif // V8_OS_WIN_X64
268
269 } // namespace
270
DirectiveAsString(DataDirective directive)271 const char* PlatformEmbeddedFileWriterWin::DirectiveAsString(
272 DataDirective directive) {
273 #if defined(V8_COMPILER_IS_MSVC)
274 if (target_arch_ != EmbeddedTargetArch::kArm64) {
275 switch (directive) {
276 case kByte:
277 return "BYTE";
278 case kLong:
279 return "DWORD";
280 case kQuad:
281 return "QWORD";
282 default:
283 UNREACHABLE();
284 }
285 } else {
286 switch (directive) {
287 case kByte:
288 return "DCB";
289 case kLong:
290 return "DCDU";
291 case kQuad:
292 return "DCQU";
293 default:
294 UNREACHABLE();
295 }
296 }
297 #else
298 switch (directive) {
299 case kByte:
300 return ".byte";
301 case kLong:
302 return ".long";
303 case kQuad:
304 return ".quad";
305 case kOcta:
306 return ".octa";
307 }
308 UNREACHABLE();
309 #endif
310 }
311
MaybeEmitUnwindData(const char * unwind_info_symbol,const char * embedded_blob_data_symbol,const EmbeddedData * blob,const void * unwind_infos)312 void PlatformEmbeddedFileWriterWin::MaybeEmitUnwindData(
313 const char* unwind_info_symbol, const char* embedded_blob_data_symbol,
314 const EmbeddedData* blob, const void* unwind_infos) {
315 // Windows ARM64 supports cross build which could require unwind info for
316 // host_os. Ignore this case because it is only used in build time.
317 #if defined(V8_OS_WIN_ARM64)
318 if (target_arch_ != EmbeddedTargetArch::kArm64) {
319 return;
320 }
321 #endif // V8_OS_WIN_ARM64
322
323 #if defined(V8_OS_WIN64)
324 if (win64_unwindinfo::CanEmitUnwindInfoForBuiltins()) {
325 EmitUnwindData(this, unwind_info_symbol, embedded_blob_data_symbol, blob,
326 reinterpret_cast<const win64_unwindinfo::BuiltinUnwindInfo*>(
327 unwind_infos));
328 }
329 #endif // V8_OS_WIN64
330 }
331
332 // Windows, MSVC
333 // -----------------------------------------------------------------------------
334
335 #if defined(V8_COMPILER_IS_MSVC)
336
337 // For x64 MSVC builds we emit assembly in MASM syntax.
338 // See https://docs.microsoft.com/en-us/cpp/assembler/masm/directives-reference.
339 // For Arm build, we emit assembly in MARMASM syntax.
340 // Note that the same mksnapshot has to be used to compile the host and target.
341
342 // The AARCH64 ABI requires instructions be 4-byte-aligned and Windows does
343 // not have a stricter alignment requirement (see the TEXTAREA macro of
344 // kxarm64.h in the Windows SDK), so code is 4-byte-aligned.
345 // The data fields in the emitted assembly tend to be accessed with 8-byte
346 // LDR instructions, so data is 8-byte-aligned.
347 //
348 // armasm64's warning A4228 states
349 // Alignment value exceeds AREA alignment; alignment not guaranteed
350 // To ensure that ALIGN directives are honored, their values are defined as
351 // equal to their corresponding AREA's ALIGN attributes.
352
353 #define ARM64_DATA_ALIGNMENT_POWER (3)
354 #define ARM64_DATA_ALIGNMENT (1 << ARM64_DATA_ALIGNMENT_POWER)
355 #define ARM64_CODE_ALIGNMENT_POWER (2)
356 #define ARM64_CODE_ALIGNMENT (1 << ARM64_CODE_ALIGNMENT_POWER)
357
SectionText()358 void PlatformEmbeddedFileWriterWin::SectionText() {
359 if (target_arch_ == EmbeddedTargetArch::kArm64) {
360 fprintf(fp_, " AREA |.text|, CODE, ALIGN=%d, READONLY\n",
361 ARM64_CODE_ALIGNMENT_POWER);
362 } else {
363 fprintf(fp_, ".CODE\n");
364 }
365 }
366
SectionData()367 void PlatformEmbeddedFileWriterWin::SectionData() {
368 if (target_arch_ == EmbeddedTargetArch::kArm64) {
369 fprintf(fp_, " AREA |.data|, DATA, ALIGN=%d, READWRITE\n",
370 ARM64_DATA_ALIGNMENT_POWER);
371 } else {
372 fprintf(fp_, ".DATA\n");
373 }
374 }
375
SectionRoData()376 void PlatformEmbeddedFileWriterWin::SectionRoData() {
377 if (target_arch_ == EmbeddedTargetArch::kArm64) {
378 fprintf(fp_, " AREA |.rodata|, DATA, ALIGN=%d, READONLY\n",
379 ARM64_DATA_ALIGNMENT_POWER);
380 } else {
381 fprintf(fp_, ".CONST\n");
382 }
383 }
384
DeclareUint32(const char * name,uint32_t value)385 void PlatformEmbeddedFileWriterWin::DeclareUint32(const char* name,
386 uint32_t value) {
387 DeclareSymbolGlobal(name);
388 fprintf(fp_, "%s%s %s %d\n", SYMBOL_PREFIX, name, DirectiveAsString(kLong),
389 value);
390 }
391
DeclarePointerToSymbol(const char * name,const char * target)392 void PlatformEmbeddedFileWriterWin::DeclarePointerToSymbol(const char* name,
393 const char* target) {
394 DeclareSymbolGlobal(name);
395 fprintf(fp_, "%s%s %s %s%s\n", SYMBOL_PREFIX, name,
396 DirectiveAsString(PointerSizeDirective()), SYMBOL_PREFIX, target);
397 }
398
StartPdataSection()399 void PlatformEmbeddedFileWriterWin::StartPdataSection() {
400 if (target_arch_ == EmbeddedTargetArch::kArm64) {
401 fprintf(fp_, " AREA |.pdata|, DATA, ALIGN=%d, READONLY\n",
402 ARM64_DATA_ALIGNMENT_POWER);
403 } else {
404 fprintf(fp_, "OPTION DOTNAME\n");
405 fprintf(fp_, ".pdata SEGMENT DWORD READ ''\n");
406 }
407 }
408
EndPdataSection()409 void PlatformEmbeddedFileWriterWin::EndPdataSection() {
410 if (target_arch_ != EmbeddedTargetArch::kArm64) {
411 fprintf(fp_, ".pdata ENDS\n");
412 }
413 }
414
StartXdataSection()415 void PlatformEmbeddedFileWriterWin::StartXdataSection() {
416 if (target_arch_ == EmbeddedTargetArch::kArm64) {
417 fprintf(fp_, " AREA |.xdata|, DATA, ALIGN=%d, READONLY\n",
418 ARM64_DATA_ALIGNMENT_POWER);
419 } else {
420 fprintf(fp_, "OPTION DOTNAME\n");
421 fprintf(fp_, ".xdata SEGMENT DWORD READ ''\n");
422 }
423 }
424
EndXdataSection()425 void PlatformEmbeddedFileWriterWin::EndXdataSection() {
426 if (target_arch_ != EmbeddedTargetArch::kArm64) {
427 fprintf(fp_, ".xdata ENDS\n");
428 }
429 }
430
DeclareExternalFunction(const char * name)431 void PlatformEmbeddedFileWriterWin::DeclareExternalFunction(const char* name) {
432 if (target_arch_ == EmbeddedTargetArch::kArm64) {
433 fprintf(fp_, " EXTERN %s \n", name);
434 } else {
435 fprintf(fp_, "EXTERN %s : PROC\n", name);
436 }
437 }
438
DeclareRvaToSymbol(const char * name,uint64_t offset)439 void PlatformEmbeddedFileWriterWin::DeclareRvaToSymbol(const char* name,
440 uint64_t offset) {
441 if (target_arch_ == EmbeddedTargetArch::kArm64) {
442 if (offset > 0) {
443 fprintf(fp_, " DCD %s + %llu\n", name, offset);
444 } else {
445 fprintf(fp_, " DCD %s\n", name);
446 }
447 // The default relocation entry generated by MSVC armasm64.exe for DCD
448 // directive is IMAGE_REL_ARM64_ADDR64 which represents relocation for
449 // 64-bit pointer instead of 32-bit RVA. Append RELOC with
450 // IMAGE_REL_ARM64_ADDR32NB(2) to generate correct relocation entry for
451 // 32-bit RVA.
452 fprintf(fp_, " RELOC 2\n");
453 } else {
454 if (offset > 0) {
455 fprintf(fp_, "DD IMAGEREL %s+%llu\n", name, offset);
456 } else {
457 fprintf(fp_, "DD IMAGEREL %s\n", name);
458 }
459 }
460 }
461
DeclareSymbolGlobal(const char * name)462 void PlatformEmbeddedFileWriterWin::DeclareSymbolGlobal(const char* name) {
463 if (target_arch_ == EmbeddedTargetArch::kArm64) {
464 fprintf(fp_, " EXPORT %s%s\n", SYMBOL_PREFIX, name);
465 } else {
466 fprintf(fp_, "PUBLIC %s%s\n", SYMBOL_PREFIX, name);
467 }
468 }
469
AlignToCodeAlignment()470 void PlatformEmbeddedFileWriterWin::AlignToCodeAlignment() {
471 if (target_arch_ == EmbeddedTargetArch::kArm64) {
472 fprintf(fp_, " ALIGN %d\n", ARM64_CODE_ALIGNMENT);
473 } else {
474 // Diverges from other platforms due to compile error
475 // 'invalid combination with segment alignment'.
476 fprintf(fp_, "ALIGN 4\n");
477 }
478 }
479
AlignToDataAlignment()480 void PlatformEmbeddedFileWriterWin::AlignToDataAlignment() {
481 if (target_arch_ == EmbeddedTargetArch::kArm64) {
482 fprintf(fp_, " ALIGN %d\n", ARM64_DATA_ALIGNMENT);
483
484 } else {
485 fprintf(fp_, "ALIGN 4\n");
486 }
487 }
488
Comment(const char * string)489 void PlatformEmbeddedFileWriterWin::Comment(const char* string) {
490 fprintf(fp_, "; %s\n", string);
491 }
492
DeclareLabel(const char * name)493 void PlatformEmbeddedFileWriterWin::DeclareLabel(const char* name) {
494 if (target_arch_ == EmbeddedTargetArch::kArm64) {
495 fprintf(fp_, "%s%s\n", SYMBOL_PREFIX, name);
496
497 } else {
498 fprintf(fp_, "%s%s LABEL %s\n", SYMBOL_PREFIX, name,
499 DirectiveAsString(kByte));
500 }
501 }
502
SourceInfo(int fileid,const char * filename,int line)503 void PlatformEmbeddedFileWriterWin::SourceInfo(int fileid, const char* filename,
504 int line) {
505 // TODO(mvstanton): output source information for MSVC.
506 // Its syntax is #line <line> "<filename>"
507 }
508
509 // TODO(mmarchini): investigate emitting size annotations for Windows
DeclareFunctionBegin(const char * name,uint32_t size)510 void PlatformEmbeddedFileWriterWin::DeclareFunctionBegin(const char* name,
511 uint32_t size) {
512 if (ENABLE_CONTROL_FLOW_INTEGRITY_BOOL) {
513 DeclareSymbolGlobal(name);
514 }
515
516 if (target_arch_ == EmbeddedTargetArch::kArm64) {
517 fprintf(fp_, "%s%s FUNCTION\n", SYMBOL_PREFIX, name);
518
519 } else {
520 fprintf(fp_, "%s%s PROC\n", SYMBOL_PREFIX, name);
521 }
522 }
523
DeclareFunctionEnd(const char * name)524 void PlatformEmbeddedFileWriterWin::DeclareFunctionEnd(const char* name) {
525 if (target_arch_ == EmbeddedTargetArch::kArm64) {
526 fprintf(fp_, " ENDFUNC\n");
527
528 } else {
529 fprintf(fp_, "%s%s ENDP\n", SYMBOL_PREFIX, name);
530 }
531 }
532
HexLiteral(uint64_t value)533 int PlatformEmbeddedFileWriterWin::HexLiteral(uint64_t value) {
534 if (target_arch_ == EmbeddedTargetArch::kArm64) {
535 return fprintf(fp_, "0x%" PRIx64, value);
536
537 } else {
538 return fprintf(fp_, "0%" PRIx64 "h", value);
539 }
540 }
541
FilePrologue()542 void PlatformEmbeddedFileWriterWin::FilePrologue() {
543 if (target_arch_ != EmbeddedTargetArch::kArm64 &&
544 target_arch_ != EmbeddedTargetArch::kX64) {
545 // x86 falls into this case
546 fprintf(fp_, ".MODEL FLAT\n");
547 }
548 }
549
DeclareExternalFilename(int fileid,const char * filename)550 void PlatformEmbeddedFileWriterWin::DeclareExternalFilename(
551 int fileid, const char* filename) {}
552
FileEpilogue()553 void PlatformEmbeddedFileWriterWin::FileEpilogue() {
554 if (target_arch_ == EmbeddedTargetArch::kArm64) {
555 fprintf(fp_, " END\n");
556 } else {
557 fprintf(fp_, "END\n");
558 }
559 }
560
IndentedDataDirective(DataDirective directive)561 int PlatformEmbeddedFileWriterWin::IndentedDataDirective(
562 DataDirective directive) {
563 return fprintf(fp_, " %s ", DirectiveAsString(directive));
564 }
565
566 #undef ARM64_DATA_ALIGNMENT_POWER
567 #undef ARM64_DATA_ALIGNMENT
568 #undef ARM64_CODE_ALIGNMENT_POWER
569 #undef ARM64_CODE_ALIGNMENT
570
571 // All Windows builds without MSVC.
572 // -----------------------------------------------------------------------------
573
574 #else
575
576 // The directives for text section prefix come from the COFF
577 // (Common Object File Format) standards:
578 // https://llvm.org/docs/Extensions.html
579 //
580 // .text$hot means this section contains hot code.
581 // x means executable section.
582 // r means read-only section.
SectionText()583 void PlatformEmbeddedFileWriterWin::SectionText() {
584 fprintf(fp_, ".section .text$hot,\"xr\"\n");
585 }
586
SectionData()587 void PlatformEmbeddedFileWriterWin::SectionData() {
588 fprintf(fp_, ".section .data\n");
589 }
590
SectionRoData()591 void PlatformEmbeddedFileWriterWin::SectionRoData() {
592 fprintf(fp_, ".section .rdata\n");
593 }
594
DeclareUint32(const char * name,uint32_t value)595 void PlatformEmbeddedFileWriterWin::DeclareUint32(const char* name,
596 uint32_t value) {
597 DeclareSymbolGlobal(name);
598 DeclareLabel(name);
599 IndentedDataDirective(kLong);
600 fprintf(fp_, "%d", value);
601 Newline();
602 }
603
DeclarePointerToSymbol(const char * name,const char * target)604 void PlatformEmbeddedFileWriterWin::DeclarePointerToSymbol(const char* name,
605 const char* target) {
606 DeclareSymbolGlobal(name);
607 DeclareLabel(name);
608 fprintf(fp_, " %s %s%s\n", DirectiveAsString(PointerSizeDirective()),
609 SYMBOL_PREFIX, target);
610 }
611
StartPdataSection()612 void PlatformEmbeddedFileWriterWin::StartPdataSection() {
613 fprintf(fp_, ".section .pdata\n");
614 }
615
EndPdataSection()616 void PlatformEmbeddedFileWriterWin::EndPdataSection() {}
617
StartXdataSection()618 void PlatformEmbeddedFileWriterWin::StartXdataSection() {
619 fprintf(fp_, ".section .xdata\n");
620 }
621
EndXdataSection()622 void PlatformEmbeddedFileWriterWin::EndXdataSection() {}
623
DeclareExternalFunction(const char * name)624 void PlatformEmbeddedFileWriterWin::DeclareExternalFunction(const char* name) {}
625
DeclareRvaToSymbol(const char * name,uint64_t offset)626 void PlatformEmbeddedFileWriterWin::DeclareRvaToSymbol(const char* name,
627 uint64_t offset) {
628 if (offset > 0) {
629 fprintf(fp_, ".rva %s + %" PRIu64 "\n", name, offset);
630 } else {
631 fprintf(fp_, ".rva %s\n", name);
632 }
633 }
634
DeclareSymbolGlobal(const char * name)635 void PlatformEmbeddedFileWriterWin::DeclareSymbolGlobal(const char* name) {
636 fprintf(fp_, ".global %s%s\n", SYMBOL_PREFIX, name);
637 }
638
AlignToCodeAlignment()639 void PlatformEmbeddedFileWriterWin::AlignToCodeAlignment() {
640 #if V8_TARGET_ARCH_X64
641 // On x64 use 64-bytes code alignment to allow 64-bytes loop header alignment.
642 STATIC_ASSERT(64 >= kCodeAlignment);
643 fprintf(fp_, ".balign 64\n");
644 #elif V8_TARGET_ARCH_PPC64
645 // 64 byte alignment is needed on ppc64 to make sure p10 prefixed instructions
646 // don't cross 64-byte boundaries.
647 STATIC_ASSERT(64 >= kCodeAlignment);
648 fprintf(fp_, ".balign 64\n");
649 #else
650 STATIC_ASSERT(32 >= kCodeAlignment);
651 fprintf(fp_, ".balign 32\n");
652 #endif
653 }
654
AlignToDataAlignment()655 void PlatformEmbeddedFileWriterWin::AlignToDataAlignment() {
656 // On Windows ARM64, s390, PPC and possibly more platforms, aligned load
657 // instructions are used to retrieve v8_Default_embedded_blob_ and/or
658 // v8_Default_embedded_blob_size_. The generated instructions require the
659 // load target to be aligned at 8 bytes (2^3).
660 fprintf(fp_, ".balign 8\n");
661 }
662
Comment(const char * string)663 void PlatformEmbeddedFileWriterWin::Comment(const char* string) {
664 fprintf(fp_, "// %s\n", string);
665 }
666
DeclareLabel(const char * name)667 void PlatformEmbeddedFileWriterWin::DeclareLabel(const char* name) {
668 fprintf(fp_, "%s%s:\n", SYMBOL_PREFIX, name);
669 }
670
SourceInfo(int fileid,const char * filename,int line)671 void PlatformEmbeddedFileWriterWin::SourceInfo(int fileid, const char* filename,
672 int line) {
673 // BUG(9944): Use .cv_loc to ensure CodeView information is used on
674 // Windows.
675 }
676
677 // TODO(mmarchini): investigate emitting size annotations for Windows
DeclareFunctionBegin(const char * name,uint32_t size)678 void PlatformEmbeddedFileWriterWin::DeclareFunctionBegin(const char* name,
679 uint32_t size) {
680 DeclareLabel(name);
681
682 if (target_arch_ == EmbeddedTargetArch::kArm64) {
683 // Windows ARM64 assembly is in GAS syntax, but ".type" is invalid directive
684 // in PE/COFF for Windows.
685 DeclareSymbolGlobal(name);
686 } else {
687 // The directives for inserting debugging information on Windows come
688 // from the PE (Portable Executable) and COFF (Common Object File Format)
689 // standards. Documented here:
690 // https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format
691 //
692 // .scl 2 means StorageClass external.
693 // .type 32 means Type Representation Function.
694 fprintf(fp_, ".def %s%s; .scl 2; .type 32; .endef;\n", SYMBOL_PREFIX, name);
695 }
696 }
697
DeclareFunctionEnd(const char * name)698 void PlatformEmbeddedFileWriterWin::DeclareFunctionEnd(const char* name) {}
699
HexLiteral(uint64_t value)700 int PlatformEmbeddedFileWriterWin::HexLiteral(uint64_t value) {
701 return fprintf(fp_, "0x%" PRIx64, value);
702 }
703
FilePrologue()704 void PlatformEmbeddedFileWriterWin::FilePrologue() {}
705
DeclareExternalFilename(int fileid,const char * filename)706 void PlatformEmbeddedFileWriterWin::DeclareExternalFilename(
707 int fileid, const char* filename) {
708 // BUG(9944): Use .cv_filename to ensure CodeView information is used on
709 // Windows.
710 }
711
FileEpilogue()712 void PlatformEmbeddedFileWriterWin::FileEpilogue() {}
713
IndentedDataDirective(DataDirective directive)714 int PlatformEmbeddedFileWriterWin::IndentedDataDirective(
715 DataDirective directive) {
716 return fprintf(fp_, " %s ", DirectiveAsString(directive));
717 }
718
719 #endif
720
ByteChunkDataDirective() const721 DataDirective PlatformEmbeddedFileWriterWin::ByteChunkDataDirective() const {
722 #if defined(V8_COMPILER_IS_MSVC)
723 // Windows MASM doesn't have an .octa directive, use QWORDs instead.
724 // Note: MASM *really* does not like large data streams. It takes over 5
725 // minutes to assemble the ~350K lines of embedded.S produced when using
726 // BYTE directives in a debug build. QWORD produces roughly 120KLOC and
727 // reduces assembly time to ~40 seconds. Still terrible, but much better
728 // than before. See also: https://crbug.com/v8/8475.
729 return kQuad;
730 #else
731 return PlatformEmbeddedFileWriterBase::ByteChunkDataDirective();
732 #endif
733 }
734
WriteByteChunk(const uint8_t * data)735 int PlatformEmbeddedFileWriterWin::WriteByteChunk(const uint8_t* data) {
736 #if defined(V8_COMPILER_IS_MSVC)
737 DCHECK_EQ(ByteChunkDataDirective(), kQuad);
738 const uint64_t* quad_ptr = reinterpret_cast<const uint64_t*>(data);
739 return HexLiteral(*quad_ptr);
740 #else
741 return PlatformEmbeddedFileWriterBase::WriteByteChunk(data);
742 #endif
743 }
744
745 #undef SYMBOL_PREFIX
746 #undef V8_ASSEMBLER_IS_MASM
747 #undef V8_ASSEMBLER_IS_MARMASM
748 #undef V8_COMPILER_IS_MSVC
749
750 } // namespace internal
751 } // namespace v8
752