blob: 891dbd94d34a919d36bc8bc5d8df21f5c7ec5c57 [file] [log] [blame]
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/snapshot/embedded/platform-embedded-file-writer-win.h"
#include <algorithm>
#include "src/common/globals.h" // For V8_OS_WIN64
#if defined(V8_OS_WIN64)
#include "src/builtins/builtins.h"
#include "src/diagnostics/unwinding-info-win64.h"
#include "src/snapshot/embedded/embedded-data.h"
#include "src/snapshot/embedded/embedded-file-writer.h"
#endif // V8_OS_WIN64
namespace v8 {
namespace internal {
// V8_CC_MSVC is true for both MSVC and clang on windows. clang can handle
// __asm__-style inline assembly but MSVC cannot, and thus we need a more
// precise compiler detection that can distinguish between the two. clang on
// windows sets both __clang__ and _MSC_VER, MSVC sets only _MSC_VER.
#if defined(_MSC_VER) && !defined(__clang__)
#define V8_COMPILER_IS_MSVC
#endif
// MSVC uses MASM for x86 and x64, while it has a ARMASM for ARM32 and
// ARMASM64 for ARM64. Since ARMASM and ARMASM64 accept a slightly tweaked
// version of ARM assembly language, they are referred to together in Visual
// Studio project files as MARMASM.
//
// ARM assembly language docs:
// http://infocenter.arm.com/help/topic/com.arm.doc.dui0802b/index.html
// Microsoft ARM assembler and assembly language docs:
// https://docs.microsoft.com/en-us/cpp/assembler/arm/arm-assembler-reference
// Name mangling.
// Symbols are prefixed with an underscore on 32-bit architectures.
#if !defined(V8_TARGET_ARCH_X64) && !defined(V8_TARGET_ARCH_ARM64)
#define SYMBOL_PREFIX "_"
#else
#define SYMBOL_PREFIX ""
#endif
// Notes:
//
// Cross-bitness builds are unsupported. It's thus safe to detect bitness
// through compile-time defines.
//
// Cross-compiler builds (e.g. with mixed use of clang / MSVC) are likewise
// unsupported and hence the compiler can also be detected through compile-time
// defines.
namespace {
#if defined(V8_OS_WIN_X64)
void WriteUnwindInfoEntry(PlatformEmbeddedFileWriterWin* w,
const char* unwind_info_symbol,
const char* embedded_blob_data_symbol,
uint64_t rva_start, uint64_t rva_end) {
w->DeclareRvaToSymbol(embedded_blob_data_symbol, rva_start);
w->DeclareRvaToSymbol(embedded_blob_data_symbol, rva_end);
w->DeclareRvaToSymbol(unwind_info_symbol);
}
void EmitUnwindData(PlatformEmbeddedFileWriterWin* w,
const char* unwind_info_symbol,
const char* embedded_blob_data_symbol,
const EmbeddedData* blob,
const win64_unwindinfo::BuiltinUnwindInfo* unwind_infos) {
// Emit an UNWIND_INFO (XDATA) struct, which contains the unwinding
// information that is used for all builtin functions.
DCHECK(win64_unwindinfo::CanEmitUnwindInfoForBuiltins());
w->Comment("xdata for all the code in the embedded blob.");
w->DeclareExternalFunction(CRASH_HANDLER_FUNCTION_NAME_STRING);
w->StartXdataSection();
{
w->DeclareLabel(unwind_info_symbol);
std::vector<uint8_t> xdata =
win64_unwindinfo::GetUnwindInfoForBuiltinFunctions();
DCHECK(!xdata.empty());
w->IndentedDataDirective(kByte);
for (size_t i = 0; i < xdata.size(); i++) {
if (i > 0) fprintf(w->fp(), ",");
w->HexLiteral(xdata[i]);
}
w->Newline();
w->Comment(" ExceptionHandler");
w->DeclareRvaToSymbol(CRASH_HANDLER_FUNCTION_NAME_STRING);
}
w->EndXdataSection();
w->Newline();
// Emit a RUNTIME_FUNCTION (PDATA) entry for each builtin function, as
// documented here:
// https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64.
w->Comment(
"pdata for all the code in the embedded blob (structs of type "
"RUNTIME_FUNCTION).");
w->Comment(" BeginAddress");
w->Comment(" EndAddress");
w->Comment(" UnwindInfoAddress");
w->StartPdataSection();
{
STATIC_ASSERT(Builtins::kAllBuiltinsAreIsolateIndependent);
Address prev_builtin_end_offset = 0;
for (int i = 0; i < Builtins::builtin_count; i++) {
// Some builtins are leaf functions from the point of view of Win64 stack
// walking: they do not move the stack pointer and do not require a PDATA
// entry because the return address can be retrieved from [rsp].
if (unwind_infos[i].is_leaf_function()) continue;
uint64_t builtin_start_offset = blob->InstructionStartOfBuiltin(i) -
reinterpret_cast<Address>(blob->code());
uint32_t builtin_size = blob->InstructionSizeOfBuiltin(i);
const std::vector<int>& xdata_desc = unwind_infos[i].fp_offsets();
if (xdata_desc.empty()) {
// Some builtins do not have any "push rbp - mov rbp, rsp" instructions
// to start a stack frame. We still emit a PDATA entry as if they had,
// relying on the fact that we can find the previous frame address from
// rbp in most cases. Note that since the function does not really start
// with a 'push rbp' we need to specify the start RVA in the PDATA entry
// a few bytes before the beginning of the function, if it does not
// overlap the end of the previous builtin.
WriteUnwindInfoEntry(
w, unwind_info_symbol, embedded_blob_data_symbol,
std::max(prev_builtin_end_offset,
builtin_start_offset - win64_unwindinfo::kRbpPrefixLength),
builtin_start_offset + builtin_size);
} else {
// Some builtins have one or more "push rbp - mov rbp, rsp" sequences,
// but not necessarily at the beginning of the function. In this case
// we want to yield a PDATA entry for each block of instructions that
// emit an rbp frame. If the function does not start with 'push rbp'
// we also emit a PDATA entry for the initial block of code up to the
// first 'push rbp', like in the case above.
if (xdata_desc[0] > 0) {
WriteUnwindInfoEntry(w, unwind_info_symbol, embedded_blob_data_symbol,
std::max(prev_builtin_end_offset,
builtin_start_offset -
win64_unwindinfo::kRbpPrefixLength),
builtin_start_offset + xdata_desc[0]);
}
for (size_t j = 0; j < xdata_desc.size(); j++) {
int chunk_start = xdata_desc[j];
int chunk_end =
(j < xdata_desc.size() - 1) ? xdata_desc[j + 1] : builtin_size;
WriteUnwindInfoEntry(w, unwind_info_symbol, embedded_blob_data_symbol,
builtin_start_offset + chunk_start,
builtin_start_offset + chunk_end);
}
}
prev_builtin_end_offset = builtin_start_offset + builtin_size;
w->Newline();
}
}
w->EndPdataSection();
w->Newline();
}
#elif defined(V8_OS_WIN_ARM64)
void EmitUnwindData(PlatformEmbeddedFileWriterWin* w,
const char* unwind_info_symbol,
const char* embedded_blob_data_symbol,
const EmbeddedData* blob,
const win64_unwindinfo::BuiltinUnwindInfo* unwind_infos) {
DCHECK(win64_unwindinfo::CanEmitUnwindInfoForBuiltins());
// Fairly arbitrary but should fit all symbol names.
static constexpr int kTemporaryStringLength = 256;
i::EmbeddedVector<char, kTemporaryStringLength> unwind_info_full_symbol;
// Emit a RUNTIME_FUNCTION (PDATA) entry for each builtin function, as
// documented here:
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling.
w->Comment(
"pdata for all the code in the embedded blob (structs of type "
"RUNTIME_FUNCTION).");
w->Comment(" BeginAddress");
w->Comment(" UnwindInfoAddress");
w->StartPdataSection();
std::vector<int> code_chunks;
std::vector<win64_unwindinfo::FrameOffsets> fp_adjustments;
STATIC_ASSERT(Builtins::kAllBuiltinsAreIsolateIndependent);
for (int i = 0; i < Builtins::builtin_count; i++) {
if (unwind_infos[i].is_leaf_function()) continue;
uint64_t builtin_start_offset = blob->InstructionStartOfBuiltin(i) -
reinterpret_cast<Address>(blob->code());
uint32_t builtin_size = blob->InstructionSizeOfBuiltin(i);
const std::vector<int>& xdata_desc = unwind_infos[i].fp_offsets();
const std::vector<win64_unwindinfo::FrameOffsets>& xdata_fp_adjustments =
unwind_infos[i].fp_adjustments();
DCHECK_EQ(xdata_desc.size(), xdata_fp_adjustments.size());
for (size_t j = 0; j < xdata_desc.size(); j++) {
int chunk_start = xdata_desc[j];
int chunk_end =
(j < xdata_desc.size() - 1) ? xdata_desc[j + 1] : builtin_size;
int chunk_len = ::RoundUp(chunk_end - chunk_start, kInstrSize);
while (chunk_len > 0) {
int allowed_chunk_len =
std::min(chunk_len, win64_unwindinfo::kMaxFunctionLength);
chunk_len -= win64_unwindinfo::kMaxFunctionLength;
// Record the chunk length and fp_adjustment for emitting UNWIND_INFO
// later.
code_chunks.push_back(allowed_chunk_len);
fp_adjustments.push_back(xdata_fp_adjustments[j]);
i::SNPrintF(unwind_info_full_symbol, "%s_%u", unwind_info_symbol,
code_chunks.size());
w->DeclareRvaToSymbol(embedded_blob_data_symbol,
builtin_start_offset + chunk_start);
w->DeclareRvaToSymbol(unwind_info_full_symbol.begin());
}
}
}
w->EndPdataSection();
w->Newline();
// Emit an UNWIND_INFO (XDATA) structs, which contains the unwinding
// information.
w->DeclareExternalFunction(CRASH_HANDLER_FUNCTION_NAME_STRING);
w->StartXdataSection();
{
for (size_t i = 0; i < code_chunks.size(); i++) {
i::SNPrintF(unwind_info_full_symbol, "%s_%u", unwind_info_symbol, i + 1);
w->DeclareLabel(unwind_info_full_symbol.begin());
std::vector<uint8_t> xdata =
win64_unwindinfo::GetUnwindInfoForBuiltinFunction(code_chunks[i],
fp_adjustments[i]);
w->IndentedDataDirective(kByte);
for (size_t j = 0; j < xdata.size(); j++) {
if (j > 0) fprintf(w->fp(), ",");
w->HexLiteral(xdata[j]);
}
w->Newline();
w->DeclareRvaToSymbol(CRASH_HANDLER_FUNCTION_NAME_STRING);
}
}
w->EndXdataSection();
w->Newline();
}
#endif // V8_OS_WIN_X64
} // namespace
const char* PlatformEmbeddedFileWriterWin::DirectiveAsString(
DataDirective directive) {
#if defined(V8_COMPILER_IS_MSVC)
if (target_arch_ != EmbeddedTargetArch::kArm64) {
switch (directive) {
case kByte:
return "BYTE";
case kLong:
return "DWORD";
case kQuad:
return "QWORD";
default:
UNREACHABLE();
}
} else {
switch (directive) {
case kByte:
return "DCB";
case kLong:
return "DCDU";
case kQuad:
return "DCQU";
default:
UNREACHABLE();
}
}
#else
switch (directive) {
case kByte:
return ".byte";
case kLong:
return ".long";
case kQuad:
return ".quad";
case kOcta:
return ".octa";
}
UNREACHABLE();
#endif
}
void PlatformEmbeddedFileWriterWin::MaybeEmitUnwindData(
const char* unwind_info_symbol, const char* embedded_blob_data_symbol,
const EmbeddedData* blob, const void* unwind_infos) {
// Windows ARM64 supports cross build which could require unwind info for
// host_os. Ignore this case because it is only used in build time.
#if defined(V8_OS_WIN_ARM64)
if (target_arch_ != EmbeddedTargetArch::kArm64) {
return;
}
#endif // V8_OS_WIN_ARM64
#if defined(V8_OS_WIN64)
if (win64_unwindinfo::CanEmitUnwindInfoForBuiltins()) {
EmitUnwindData(this, unwind_info_symbol, embedded_blob_data_symbol, blob,
reinterpret_cast<const win64_unwindinfo::BuiltinUnwindInfo*>(
unwind_infos));
}
#endif // V8_OS_WIN64
}
// Windows, MSVC
// -----------------------------------------------------------------------------
#if defined(V8_COMPILER_IS_MSVC)
// For x64 MSVC builds we emit assembly in MASM syntax.
// See https://docs.microsoft.com/en-us/cpp/assembler/masm/directives-reference.
// For Arm build, we emit assembly in MARMASM syntax.
// Note that the same mksnapshot has to be used to compile the host and target.
// The AARCH64 ABI requires instructions be 4-byte-aligned and Windows does
// not have a stricter alignment requirement (see the TEXTAREA macro of
// kxarm64.h in the Windows SDK), so code is 4-byte-aligned.
// The data fields in the emitted assembly tend to be accessed with 8-byte
// LDR instructions, so data is 8-byte-aligned.
//
// armasm64's warning A4228 states
// Alignment value exceeds AREA alignment; alignment not guaranteed
// To ensure that ALIGN directives are honored, their values are defined as
// equal to their corresponding AREA's ALIGN attributes.
#define ARM64_DATA_ALIGNMENT_POWER (3)
#define ARM64_DATA_ALIGNMENT (1 << ARM64_DATA_ALIGNMENT_POWER)
#define ARM64_CODE_ALIGNMENT_POWER (2)
#define ARM64_CODE_ALIGNMENT (1 << ARM64_CODE_ALIGNMENT_POWER)
void PlatformEmbeddedFileWriterWin::SectionText() {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " AREA |.text|, CODE, ALIGN=%d, READONLY\n",
ARM64_CODE_ALIGNMENT_POWER);
} else {
fprintf(fp_, ".CODE\n");
}
}
void PlatformEmbeddedFileWriterWin::SectionData() {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " AREA |.data|, DATA, ALIGN=%d, READWRITE\n",
ARM64_DATA_ALIGNMENT_POWER);
} else {
fprintf(fp_, ".DATA\n");
}
}
void PlatformEmbeddedFileWriterWin::SectionRoData() {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " AREA |.rodata|, DATA, ALIGN=%d, READONLY\n",
ARM64_DATA_ALIGNMENT_POWER);
} else {
fprintf(fp_, ".CONST\n");
}
}
void PlatformEmbeddedFileWriterWin::DeclareUint32(const char* name,
uint32_t value) {
DeclareSymbolGlobal(name);
fprintf(fp_, "%s%s %s %d\n", SYMBOL_PREFIX, name, DirectiveAsString(kLong),
value);
}
void PlatformEmbeddedFileWriterWin::DeclarePointerToSymbol(const char* name,
const char* target) {
DeclareSymbolGlobal(name);
fprintf(fp_, "%s%s %s %s%s\n", SYMBOL_PREFIX, name,
DirectiveAsString(PointerSizeDirective()), SYMBOL_PREFIX, target);
}
void PlatformEmbeddedFileWriterWin::StartPdataSection() {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " AREA |.pdata|, DATA, ALIGN=%d, READONLY\n",
ARM64_DATA_ALIGNMENT_POWER);
} else {
fprintf(fp_, "OPTION DOTNAME\n");
fprintf(fp_, ".pdata SEGMENT DWORD READ ''\n");
}
}
void PlatformEmbeddedFileWriterWin::EndPdataSection() {
if (target_arch_ != EmbeddedTargetArch::kArm64) {
fprintf(fp_, ".pdata ENDS\n");
}
}
void PlatformEmbeddedFileWriterWin::StartXdataSection() {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " AREA |.xdata|, DATA, ALIGN=%d, READONLY\n",
ARM64_DATA_ALIGNMENT_POWER);
} else {
fprintf(fp_, "OPTION DOTNAME\n");
fprintf(fp_, ".xdata SEGMENT DWORD READ ''\n");
}
}
void PlatformEmbeddedFileWriterWin::EndXdataSection() {
if (target_arch_ != EmbeddedTargetArch::kArm64) {
fprintf(fp_, ".xdata ENDS\n");
}
}
void PlatformEmbeddedFileWriterWin::DeclareExternalFunction(const char* name) {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " EXTERN %s \n", name);
} else {
fprintf(fp_, "EXTERN %s : PROC\n", name);
}
}
void PlatformEmbeddedFileWriterWin::DeclareRvaToSymbol(const char* name,
uint64_t offset) {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
if (offset > 0) {
fprintf(fp_, " DCD %s + %llu\n", name, offset);
} else {
fprintf(fp_, " DCD %s\n", name);
}
// The default relocation entry generated by MSVC armasm64.exe for DCD
// directive is IMAGE_REL_ARM64_ADDR64 which represents relocation for
// 64-bit pointer instead of 32-bit RVA. Append RELOC with
// IMAGE_REL_ARM64_ADDR32NB(2) to generate correct relocation entry for
// 32-bit RVA.
fprintf(fp_, " RELOC 2\n");
} else {
if (offset > 0) {
fprintf(fp_, "DD IMAGEREL %s+%llu\n", name, offset);
} else {
fprintf(fp_, "DD IMAGEREL %s\n", name);
}
}
}
void PlatformEmbeddedFileWriterWin::DeclareSymbolGlobal(const char* name) {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " EXPORT %s%s\n", SYMBOL_PREFIX, name);
} else {
fprintf(fp_, "PUBLIC %s%s\n", SYMBOL_PREFIX, name);
}
}
void PlatformEmbeddedFileWriterWin::AlignToCodeAlignment() {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " ALIGN %d\n", ARM64_CODE_ALIGNMENT);
} else {
// Diverges from other platforms due to compile error
// 'invalid combination with segment alignment'.
fprintf(fp_, "ALIGN 4\n");
}
}
void PlatformEmbeddedFileWriterWin::AlignToDataAlignment() {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " ALIGN %d\n", ARM64_DATA_ALIGNMENT);
} else {
fprintf(fp_, "ALIGN 4\n");
}
}
void PlatformEmbeddedFileWriterWin::Comment(const char* string) {
fprintf(fp_, "; %s\n", string);
}
void PlatformEmbeddedFileWriterWin::DeclareLabel(const char* name) {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, "%s%s\n", SYMBOL_PREFIX, name);
} else {
fprintf(fp_, "%s%s LABEL %s\n", SYMBOL_PREFIX, name,
DirectiveAsString(kByte));
}
}
void PlatformEmbeddedFileWriterWin::SourceInfo(int fileid, const char* filename,
int line) {
// TODO(mvstanton): output source information for MSVC.
// Its syntax is #line <line> "<filename>"
}
// TODO(mmarchini): investigate emitting size annotations for Windows
void PlatformEmbeddedFileWriterWin::DeclareFunctionBegin(const char* name,
uint32_t size) {
if (ENABLE_CONTROL_FLOW_INTEGRITY_BOOL) {
DeclareSymbolGlobal(name);
}
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, "%s%s FUNCTION\n", SYMBOL_PREFIX, name);
} else {
fprintf(fp_, "%s%s PROC\n", SYMBOL_PREFIX, name);
}
}
void PlatformEmbeddedFileWriterWin::DeclareFunctionEnd(const char* name) {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " ENDFUNC\n");
} else {
fprintf(fp_, "%s%s ENDP\n", SYMBOL_PREFIX, name);
}
}
int PlatformEmbeddedFileWriterWin::HexLiteral(uint64_t value) {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
return fprintf(fp_, "0x%" PRIx64, value);
} else {
return fprintf(fp_, "0%" PRIx64 "h", value);
}
}
void PlatformEmbeddedFileWriterWin::FilePrologue() {
if (target_arch_ != EmbeddedTargetArch::kArm64 &&
target_arch_ != EmbeddedTargetArch::kX64) {
// x86 falls into this case
fprintf(fp_, ".MODEL FLAT\n");
}
}
void PlatformEmbeddedFileWriterWin::DeclareExternalFilename(
int fileid, const char* filename) {}
void PlatformEmbeddedFileWriterWin::FileEpilogue() {
if (target_arch_ == EmbeddedTargetArch::kArm64) {
fprintf(fp_, " END\n");
} else {
fprintf(fp_, "END\n");
}
}
int PlatformEmbeddedFileWriterWin::IndentedDataDirective(
DataDirective directive) {
return fprintf(fp_, " %s ", DirectiveAsString(directive));
}
#undef ARM64_DATA_ALIGNMENT_POWER
#undef ARM64_DATA_ALIGNMENT
#undef ARM64_CODE_ALIGNMENT_POWER
#undef ARM64_CODE_ALIGNMENT
// All Windows builds without MSVC.
// -----------------------------------------------------------------------------
#else
void PlatformEmbeddedFileWriterWin::SectionText() {
fprintf(fp_, ".section .text\n");
}
void PlatformEmbeddedFileWriterWin::SectionData() {
fprintf(fp_, ".section .data\n");
}
void PlatformEmbeddedFileWriterWin::SectionRoData() {
fprintf(fp_, ".section .rdata\n");
}
void PlatformEmbeddedFileWriterWin::DeclareUint32(const char* name,
uint32_t value) {
DeclareSymbolGlobal(name);
DeclareLabel(name);
IndentedDataDirective(kLong);
fprintf(fp_, "%d", value);
Newline();
}
void PlatformEmbeddedFileWriterWin::DeclarePointerToSymbol(const char* name,
const char* target) {
DeclareSymbolGlobal(name);
DeclareLabel(name);
fprintf(fp_, " %s %s%s\n", DirectiveAsString(PointerSizeDirective()),
SYMBOL_PREFIX, target);
}
void PlatformEmbeddedFileWriterWin::StartPdataSection() {
fprintf(fp_, ".section .pdata\n");
}
void PlatformEmbeddedFileWriterWin::EndPdataSection() {}
void PlatformEmbeddedFileWriterWin::StartXdataSection() {
fprintf(fp_, ".section .xdata\n");
}
void PlatformEmbeddedFileWriterWin::EndXdataSection() {}
void PlatformEmbeddedFileWriterWin::DeclareExternalFunction(const char* name) {}
void PlatformEmbeddedFileWriterWin::DeclareRvaToSymbol(const char* name,
uint64_t offset) {
if (offset > 0) {
fprintf(fp_, ".rva %s + %" PRIu64 "\n", name, offset);
} else {
fprintf(fp_, ".rva %s\n", name);
}
}
void PlatformEmbeddedFileWriterWin::DeclareSymbolGlobal(const char* name) {
fprintf(fp_, ".global %s%s\n", SYMBOL_PREFIX, name);
}
void PlatformEmbeddedFileWriterWin::AlignToCodeAlignment() {
fprintf(fp_, ".balign 32\n");
}
void PlatformEmbeddedFileWriterWin::AlignToDataAlignment() {
// On Windows ARM64, s390, PPC and possibly more platforms, aligned load
// instructions are used to retrieve v8_Default_embedded_blob_ and/or
// v8_Default_embedded_blob_size_. The generated instructions require the
// load target to be aligned at 8 bytes (2^3).
fprintf(fp_, ".balign 8\n");
}
void PlatformEmbeddedFileWriterWin::Comment(const char* string) {
fprintf(fp_, "// %s\n", string);
}
void PlatformEmbeddedFileWriterWin::DeclareLabel(const char* name) {
fprintf(fp_, "%s%s:\n", SYMBOL_PREFIX, name);
}
void PlatformEmbeddedFileWriterWin::SourceInfo(int fileid, const char* filename,
int line) {
// BUG(9944): Use .cv_loc to ensure CodeView information is used on
// Windows.
}
// TODO(mmarchini): investigate emitting size annotations for Windows
void PlatformEmbeddedFileWriterWin::DeclareFunctionBegin(const char* name,
uint32_t size) {
DeclareLabel(name);
if (target_arch_ == EmbeddedTargetArch::kArm64) {
// Windows ARM64 assembly is in GAS syntax, but ".type" is invalid directive
// in PE/COFF for Windows.
DeclareSymbolGlobal(name);
} else {
// The directives for inserting debugging information on Windows come
// from the PE (Portable Executable) and COFF (Common Object File Format)
// standards. Documented here:
// https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format
//
// .scl 2 means StorageClass external.
// .type 32 means Type Representation Function.
fprintf(fp_, ".def %s%s; .scl 2; .type 32; .endef;\n", SYMBOL_PREFIX, name);
}
}
void PlatformEmbeddedFileWriterWin::DeclareFunctionEnd(const char* name) {}
int PlatformEmbeddedFileWriterWin::HexLiteral(uint64_t value) {
return fprintf(fp_, "0x%" PRIx64, value);
}
void PlatformEmbeddedFileWriterWin::FilePrologue() {}
void PlatformEmbeddedFileWriterWin::DeclareExternalFilename(
int fileid, const char* filename) {
// BUG(9944): Use .cv_filename to ensure CodeView information is used on
// Windows.
}
void PlatformEmbeddedFileWriterWin::FileEpilogue() {}
int PlatformEmbeddedFileWriterWin::IndentedDataDirective(
DataDirective directive) {
return fprintf(fp_, " %s ", DirectiveAsString(directive));
}
#endif
DataDirective PlatformEmbeddedFileWriterWin::ByteChunkDataDirective() const {
#if defined(V8_COMPILER_IS_MSVC)
// Windows MASM doesn't have an .octa directive, use QWORDs instead.
// Note: MASM *really* does not like large data streams. It takes over 5
// minutes to assemble the ~350K lines of embedded.S produced when using
// BYTE directives in a debug build. QWORD produces roughly 120KLOC and
// reduces assembly time to ~40 seconds. Still terrible, but much better
// than before. See also: https://crbug.com/v8/8475.
return kQuad;
#else
return PlatformEmbeddedFileWriterBase::ByteChunkDataDirective();
#endif
}
int PlatformEmbeddedFileWriterWin::WriteByteChunk(const uint8_t* data) {
#if defined(V8_COMPILER_IS_MSVC)
DCHECK_EQ(ByteChunkDataDirective(), kQuad);
const uint64_t* quad_ptr = reinterpret_cast<const uint64_t*>(data);
return HexLiteral(*quad_ptr);
#else
return PlatformEmbeddedFileWriterBase::WriteByteChunk(data);
#endif
}
#undef SYMBOL_PREFIX
#undef V8_ASSEMBLER_IS_MASM
#undef V8_ASSEMBLER_IS_MARMASM
#undef V8_COMPILER_IS_MSVC
} // namespace internal
} // namespace v8