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
18namespace v8 {
19namespace 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
56namespace {
57
58#if defined(V8_OS_WIN_X64)
59
60void 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
69void 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
176void 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
271const 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
312void 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
358void 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
367void 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
376void 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
385void 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
392void 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
399void 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
409void PlatformEmbeddedFileWriterWin::EndPdataSection() {
410  if (target_arch_ != EmbeddedTargetArch::kArm64) {
411    fprintf(fp_, ".pdata ENDS\n");
412  }
413}
414
415void 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
425void PlatformEmbeddedFileWriterWin::EndXdataSection() {
426  if (target_arch_ != EmbeddedTargetArch::kArm64) {
427    fprintf(fp_, ".xdata ENDS\n");
428  }
429}
430
431void 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
439void 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
462void 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
470void 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
480void 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
489void PlatformEmbeddedFileWriterWin::Comment(const char* string) {
490  fprintf(fp_, "; %s\n", string);
491}
492
493void 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
503void 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
510void 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
524void 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
533int 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
542void 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
550void PlatformEmbeddedFileWriterWin::DeclareExternalFilename(
551    int fileid, const char* filename) {}
552
553void PlatformEmbeddedFileWriterWin::FileEpilogue() {
554  if (target_arch_ == EmbeddedTargetArch::kArm64) {
555    fprintf(fp_, "  END\n");
556  } else {
557    fprintf(fp_, "END\n");
558  }
559}
560
561int 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.
583void PlatformEmbeddedFileWriterWin::SectionText() {
584  fprintf(fp_, ".section .text$hot,\"xr\"\n");
585}
586
587void PlatformEmbeddedFileWriterWin::SectionData() {
588  fprintf(fp_, ".section .data\n");
589}
590
591void PlatformEmbeddedFileWriterWin::SectionRoData() {
592  fprintf(fp_, ".section .rdata\n");
593}
594
595void 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
604void 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
612void PlatformEmbeddedFileWriterWin::StartPdataSection() {
613  fprintf(fp_, ".section .pdata\n");
614}
615
616void PlatformEmbeddedFileWriterWin::EndPdataSection() {}
617
618void PlatformEmbeddedFileWriterWin::StartXdataSection() {
619  fprintf(fp_, ".section .xdata\n");
620}
621
622void PlatformEmbeddedFileWriterWin::EndXdataSection() {}
623
624void PlatformEmbeddedFileWriterWin::DeclareExternalFunction(const char* name) {}
625
626void 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
635void PlatformEmbeddedFileWriterWin::DeclareSymbolGlobal(const char* name) {
636  fprintf(fp_, ".global %s%s\n", SYMBOL_PREFIX, name);
637}
638
639void 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
655void 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
663void PlatformEmbeddedFileWriterWin::Comment(const char* string) {
664  fprintf(fp_, "// %s\n", string);
665}
666
667void PlatformEmbeddedFileWriterWin::DeclareLabel(const char* name) {
668  fprintf(fp_, "%s%s:\n", SYMBOL_PREFIX, name);
669}
670
671void 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
678void 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
698void PlatformEmbeddedFileWriterWin::DeclareFunctionEnd(const char* name) {}
699
700int PlatformEmbeddedFileWriterWin::HexLiteral(uint64_t value) {
701  return fprintf(fp_, "0x%" PRIx64, value);
702}
703
704void PlatformEmbeddedFileWriterWin::FilePrologue() {}
705
706void 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
712void PlatformEmbeddedFileWriterWin::FileEpilogue() {}
713
714int PlatformEmbeddedFileWriterWin::IndentedDataDirective(
715    DataDirective directive) {
716  return fprintf(fp_, "  %s ", DirectiveAsString(directive));
717}
718
719#endif
720
721DataDirective 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
735int 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