[dart2native] Allow for signing of Windows standalone executables.
Instead of simply concatenating the dartaotruntime executable and the ELF snapshot, we instead insert the ELF snapshot as a COFF section, so the resulting PE file can be signed. TEST=pkg/dartdev/test/commands/compile_test Bug: https://github.com/dart-lang/sdk/issues/39106 Change-Id: Iaafb5833fcb9c69c0dd374b64769f22b8b9f53a2 Cq-Include-Trybots: luci.dart.try:pkg-win-release-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/236100 Reviewed-by: Aske Simon Christensen <askesc@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Tess Strickland <sstrickl@google.com>
This commit is contained in:
committed by
Commit Bot
parent
e773e6e713
commit
cdb703859a
@@ -5,8 +5,8 @@
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:dart2native/dart2native_macho.dart'
|
||||
show writeAppendedMachOExecutable;
|
||||
import 'dart2native_macho.dart' show writeAppendedMachOExecutable;
|
||||
import 'dart2native_pe.dart' show writeAppendedPortableExecutable;
|
||||
|
||||
// Maximum page size across all supported architectures (arm64 macOS has 16K
|
||||
// pages, some arm64 Linux distributions have 64K pages).
|
||||
@@ -20,6 +20,9 @@ Future writeAppendedExecutable(
|
||||
if (Platform.isMacOS) {
|
||||
return await writeAppendedMachOExecutable(
|
||||
dartaotruntimePath, payloadPath, outputPath);
|
||||
} else if (Platform.isWindows) {
|
||||
return await writeAppendedPortableExecutable(
|
||||
dartaotruntimePath, payloadPath, outputPath);
|
||||
}
|
||||
|
||||
final dartaotruntime = File(dartaotruntimePath);
|
||||
|
||||
@@ -0,0 +1,329 @@
|
||||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
int align(int size, int base) {
|
||||
final int over = size % base;
|
||||
if (over != 0) {
|
||||
return size + (base - over);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
class BytesBacked {
|
||||
ByteData data;
|
||||
|
||||
BytesBacked(this.data);
|
||||
|
||||
int get size => data.lengthInBytes;
|
||||
|
||||
Future<void> write(RandomAccessFile output) async {
|
||||
await output.writeFrom(Uint8List.sublistView(data));
|
||||
}
|
||||
}
|
||||
|
||||
class CoffFileHeader extends BytesBacked {
|
||||
CoffFileHeader._(ByteData data) : super(data);
|
||||
|
||||
static const _fileHeaderSize = 20;
|
||||
static const _sectionCountOffset = 2;
|
||||
static const _optionalHeaderSizeOffset = 16;
|
||||
|
||||
static CoffFileHeader fromTypedData(TypedData source, int offset) {
|
||||
if (source.lengthInBytes < offset + _fileHeaderSize) {
|
||||
throw 'File is truncated within the COFF file header';
|
||||
}
|
||||
final buffer = Uint8List(_fileHeaderSize);
|
||||
buffer.setAll(
|
||||
0, Uint8List.sublistView(source, offset, offset + _fileHeaderSize));
|
||||
return CoffFileHeader._(ByteData.sublistView(buffer));
|
||||
}
|
||||
|
||||
int get sectionCount => data.getUint16(_sectionCountOffset, Endian.little);
|
||||
set sectionCount(int value) =>
|
||||
data.setUint16(_sectionCountOffset, value, Endian.little);
|
||||
|
||||
int get optionalHeaderSize =>
|
||||
data.getUint16(_optionalHeaderSizeOffset, Endian.little);
|
||||
}
|
||||
|
||||
class CoffOptionalHeader extends BytesBacked {
|
||||
CoffOptionalHeader._(ByteData data) : super(data);
|
||||
|
||||
static const _pe32Magic = 0x10b;
|
||||
static const _pe32PlusMagic = 0x20b;
|
||||
|
||||
static const _magicOffset = 0;
|
||||
static const _sectionAlignmentOffset = 32;
|
||||
static const _fileAlignmentOffset = 36;
|
||||
static const _imageSizeOffset = 56;
|
||||
static const _headersSizeOffset = 60;
|
||||
|
||||
static CoffOptionalHeader fromTypedData(
|
||||
TypedData source, int offset, int size) {
|
||||
if (source.lengthInBytes < offset + size) {
|
||||
throw 'File is truncated within the COFF optional header';
|
||||
}
|
||||
final buffer = Uint8List(size);
|
||||
buffer.setAll(0, Uint8List.sublistView(source, offset, offset + size));
|
||||
final data = ByteData.sublistView(buffer);
|
||||
final magic = data.getUint16(_magicOffset, Endian.little);
|
||||
if (magic != _pe32Magic && magic != _pe32PlusMagic) {
|
||||
throw 'Not a PE32 or PE32+ image file';
|
||||
}
|
||||
return CoffOptionalHeader._(data);
|
||||
}
|
||||
|
||||
// The alignment used for virtual addresses of sections, _not_ file offsets.
|
||||
int get sectionAlignment =>
|
||||
data.getUint32(_sectionAlignmentOffset, Endian.little);
|
||||
|
||||
// The alignment used for file offsets of section data and other contents.
|
||||
int get fileAlignment => data.getUint32(_fileAlignmentOffset, Endian.little);
|
||||
|
||||
int get headersSize => data.getUint32(_headersSizeOffset, Endian.little);
|
||||
set headersSize(int value) =>
|
||||
data.setUint32(_headersSizeOffset, value, Endian.little);
|
||||
|
||||
int get imageSize => data.getUint32(_imageSizeOffset, Endian.little);
|
||||
set imageSize(int value) =>
|
||||
data.setUint32(_imageSizeOffset, value, Endian.little);
|
||||
}
|
||||
|
||||
class CoffSectionHeader extends BytesBacked {
|
||||
CoffSectionHeader._(ByteData data) : super(data);
|
||||
|
||||
static const _virtualSizeOffset = 8;
|
||||
static const _virtualAddressOffset = 12;
|
||||
static const _fileSizeOffset = 16;
|
||||
static const _fileOffsetOffset = 20;
|
||||
static const _characteristicsOffset = 36;
|
||||
|
||||
static const _discardableFlag = 0x02000000;
|
||||
|
||||
String get name => String.fromCharCodes(Uint8List.sublistView(data, 0, 8));
|
||||
set name(String name) {
|
||||
// Each section header has only eight bytes for the section name.
|
||||
// First reset it to zeroes, then copy over the UTF-8 encoded version.
|
||||
final buffer = Uint8List.sublistView(data, 0, 8);
|
||||
buffer.fillRange(0, 8, 0);
|
||||
buffer.setAll(0, utf8.encode(name));
|
||||
}
|
||||
|
||||
int get virtualAddress =>
|
||||
data.getUint32(_virtualAddressOffset, Endian.little);
|
||||
set virtualAddress(int offset) =>
|
||||
data.setUint32(_virtualAddressOffset, offset, Endian.little);
|
||||
|
||||
int get virtualSize => data.getUint32(_virtualSizeOffset, Endian.little);
|
||||
set virtualSize(int offset) =>
|
||||
data.setUint32(_virtualSizeOffset, offset, Endian.little);
|
||||
|
||||
int get fileOffset => data.getUint32(_fileOffsetOffset, Endian.little);
|
||||
set fileOffset(int offset) =>
|
||||
data.setUint32(_fileOffsetOffset, offset, Endian.little);
|
||||
|
||||
int get fileSize => data.getUint32(_fileSizeOffset, Endian.little);
|
||||
set fileSize(int offset) =>
|
||||
data.setUint32(_fileSizeOffset, offset, Endian.little);
|
||||
|
||||
int get characteristics =>
|
||||
data.getUint32(_characteristicsOffset, Endian.little);
|
||||
set characteristics(int value) =>
|
||||
data.setUint32(_characteristicsOffset, value, Endian.little);
|
||||
|
||||
bool get isDiscardable => characteristics & _discardableFlag != 0;
|
||||
set isDiscardable(bool value) {
|
||||
if (value) {
|
||||
characteristics |= _discardableFlag;
|
||||
} else {
|
||||
characteristics &= ~_discardableFlag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CoffSectionTable extends BytesBacked {
|
||||
CoffSectionTable._(ByteData data) : super(data);
|
||||
|
||||
static const _entrySize = 40;
|
||||
|
||||
static CoffSectionTable fromTypedData(
|
||||
TypedData source, int offset, int sections) {
|
||||
final size = sections * _entrySize;
|
||||
if (source.lengthInBytes < offset + size) {
|
||||
throw 'File is truncated within the COFF section table';
|
||||
}
|
||||
final buffer = Uint8List(size);
|
||||
buffer.setAll(0, Uint8List.sublistView(source, offset, offset + size));
|
||||
return CoffSectionTable._(ByteData.sublistView(buffer));
|
||||
}
|
||||
|
||||
Iterable<CoffSectionHeader> get entries sync* {
|
||||
for (int i = 0; i < size; i += _entrySize) {
|
||||
yield CoffSectionHeader._(ByteData.sublistView(data, i, i + _entrySize));
|
||||
}
|
||||
}
|
||||
|
||||
int get addressEnd => entries.fold(
|
||||
0, (i, entry) => max(i, entry.virtualAddress + entry.virtualSize));
|
||||
int get offsetEnd =>
|
||||
entries.fold(0, (i, entry) => max(i, entry.fileOffset + entry.fileSize));
|
||||
|
||||
CoffSectionHeader allocateNewSectionHeader() {
|
||||
final newBuffer = Uint8List(size + _entrySize);
|
||||
newBuffer.setAll(0, Uint8List.sublistView(data));
|
||||
data = ByteData.sublistView(newBuffer);
|
||||
return CoffSectionHeader._(
|
||||
ByteData.sublistView(data, size - _entrySize, size));
|
||||
}
|
||||
}
|
||||
|
||||
class CoffHeaders {
|
||||
final int _coffOffset;
|
||||
final CoffFileHeader fileHeader;
|
||||
final CoffOptionalHeader optionalHeader;
|
||||
final CoffSectionTable sectionTable;
|
||||
|
||||
CoffHeaders._(this._coffOffset, this.fileHeader, this.optionalHeader,
|
||||
this.sectionTable);
|
||||
|
||||
static CoffHeaders fromTypedData(TypedData source, int offset) {
|
||||
final fileHeader = CoffFileHeader.fromTypedData(source, offset);
|
||||
final optionalHeader = CoffOptionalHeader.fromTypedData(
|
||||
source, offset + fileHeader.size, fileHeader.optionalHeaderSize);
|
||||
final sectionTable = CoffSectionTable.fromTypedData(
|
||||
source,
|
||||
offset + fileHeader.size + optionalHeader.size,
|
||||
fileHeader.sectionCount);
|
||||
return CoffHeaders._(offset, fileHeader, optionalHeader, sectionTable);
|
||||
}
|
||||
|
||||
// Keep in sync with kSnapshotSectionName in snapshot_utils.cc.
|
||||
static const _snapshotSectionName = "snapshot";
|
||||
|
||||
int get size => optionalHeader.headersSize;
|
||||
|
||||
void addSnapshotSectionHeader(int length) {
|
||||
final oldSize = size;
|
||||
final address =
|
||||
align(sectionTable.addressEnd, optionalHeader.sectionAlignment);
|
||||
final offset = align(sectionTable.offsetEnd, optionalHeader.fileAlignment);
|
||||
|
||||
// Create and fill the new section header entry.
|
||||
final newHeader = sectionTable.allocateNewSectionHeader();
|
||||
newHeader.name = _snapshotSectionName;
|
||||
newHeader.virtualAddress = address;
|
||||
newHeader.virtualSize = length;
|
||||
newHeader.fileOffset = offset;
|
||||
newHeader.fileSize = align(length, optionalHeader.fileAlignment);
|
||||
newHeader.isDiscardable = true;
|
||||
// Leave the rest of the header fields with zero values.
|
||||
|
||||
// Increment the number of sections in the file header.
|
||||
fileHeader.sectionCount += 1;
|
||||
|
||||
// Adjust the sizes stored in the optional header.
|
||||
optionalHeader.headersSize = align(
|
||||
_coffOffset + fileHeader.size + optionalHeader.size + sectionTable.size,
|
||||
optionalHeader.fileAlignment);
|
||||
optionalHeader.imageSize += align(length, optionalHeader.fileAlignment);
|
||||
|
||||
// If the size of the headers changed, we'll need to adjust the section
|
||||
// offsets and image size accordingly.
|
||||
final headersSizeDiff = size - oldSize;
|
||||
if (headersSizeDiff > 0) {
|
||||
// Safety check that section virtual addresses need not be adjusted, as
|
||||
// that requires rewriting much more of the fields and section contents.
|
||||
// (Generally, the size of the headers is much smaller than the section
|
||||
// alignment and so this is not expected to happen.)
|
||||
if (size ~/ optionalHeader.sectionAlignment !=
|
||||
oldSize ~/ optionalHeader.sectionAlignment) {
|
||||
throw 'Adding the snapshot would require adjusting virtual addresses';
|
||||
}
|
||||
assert(headersSizeDiff % optionalHeader.fileAlignment == 0);
|
||||
for (final entry in sectionTable.entries) {
|
||||
entry.fileOffset += headersSizeDiff;
|
||||
}
|
||||
optionalHeader.imageSize += headersSizeDiff;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> write(RandomAccessFile output) async {
|
||||
await fileHeader.write(output);
|
||||
await optionalHeader.write(output);
|
||||
await sectionTable.write(output);
|
||||
// Pad to the recorded headers size, which includes the MS-DOS stub.
|
||||
final written = await output.position();
|
||||
await output.writeFrom(Uint8List(size - written));
|
||||
}
|
||||
}
|
||||
|
||||
class PortableExecutable {
|
||||
final Uint8List source;
|
||||
final CoffHeaders headers;
|
||||
final int sourceFileHeaderOffset;
|
||||
final int sourceSectionContentsOffset;
|
||||
|
||||
PortableExecutable._(this.source, this.headers, this.sourceFileHeaderOffset,
|
||||
this.sourceSectionContentsOffset);
|
||||
|
||||
static const _expectedPESignature = <int>[80, 69, 0, 0];
|
||||
static const _offsetForPEOffset = 0x3c;
|
||||
|
||||
static Future<PortableExecutable> fromFile(File file) async {
|
||||
final source = await file.readAsBytes();
|
||||
final byteData = ByteData.sublistView(source);
|
||||
final peOffset = byteData.getUint32(_offsetForPEOffset, Endian.little);
|
||||
for (int i = 0; i < _expectedPESignature.length; i++) {
|
||||
if (byteData.getUint8(peOffset + i) != _expectedPESignature[i]) {
|
||||
throw 'Not a Portable Executable file';
|
||||
}
|
||||
}
|
||||
final fileHeaderOffset = peOffset + _expectedPESignature.length;
|
||||
final headers = CoffHeaders.fromTypedData(source, fileHeaderOffset);
|
||||
final sectionContentsOffset = headers.size;
|
||||
return PortableExecutable._(
|
||||
source, headers, fileHeaderOffset, sectionContentsOffset);
|
||||
}
|
||||
|
||||
Future<void> _fileAlignSectionEnd(RandomAccessFile output) async {
|
||||
final current = await output.position();
|
||||
final padding =
|
||||
align(current, headers.optionalHeader.fileAlignment) - current;
|
||||
await output.writeFrom(Uint8List(padding));
|
||||
}
|
||||
|
||||
Future<void> appendSnapshotAndWrite(File output, File snapshot) async {
|
||||
final stream = await output.open(mode: FileMode.write);
|
||||
// Write MS-DOS stub.
|
||||
await stream.writeFrom(source, 0, sourceFileHeaderOffset);
|
||||
// Write headers with additional snapshot section.
|
||||
final snapshotBytes = await snapshot.readAsBytes();
|
||||
headers.addSnapshotSectionHeader(snapshotBytes.length);
|
||||
await headers.write(stream);
|
||||
// Write original section contents with alignment padding.
|
||||
await stream.writeFrom(source, sourceSectionContentsOffset);
|
||||
await _fileAlignSectionEnd(stream);
|
||||
// Write snapshot with alignment padding.
|
||||
await stream.writeFrom(snapshotBytes);
|
||||
await _fileAlignSectionEnd(stream);
|
||||
await stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Writes an "appended" dart runtime + script snapshot file in a format
|
||||
// compatible with Portable Executable files.
|
||||
Future writeAppendedPortableExecutable(
|
||||
String dartaotruntimePath, String payloadPath, String outputPath) async {
|
||||
File originalExecutableFile = File(dartaotruntimePath);
|
||||
File newSegmentFile = File(payloadPath);
|
||||
File outputFile = File(outputPath);
|
||||
|
||||
final pe = await PortableExecutable.fromFile(originalExecutableFile);
|
||||
await pe.appendSnapshotAndWrite(outputFile, newSegmentFile);
|
||||
}
|
||||
@@ -16,6 +16,9 @@
|
||||
#if defined(DART_TARGET_OS_MACOS)
|
||||
#include <platform/mach_o.h>
|
||||
#endif
|
||||
#if defined(DART_TARGET_OS_WINDOWS)
|
||||
#include <platform/pe.h>
|
||||
#endif
|
||||
#include "platform/utils.h"
|
||||
|
||||
#define LOG_SECTION_BOUNDARIES false
|
||||
@@ -260,7 +263,7 @@ AppSnapshot* Snapshot::TryReadAppendedAppSnapshotElfFromMachO(
|
||||
// Parse the first 4bytes and extract the magic number.
|
||||
uint32_t magic;
|
||||
file->SetPosition(0);
|
||||
file->Read(&magic, sizeof(uint32_t));
|
||||
file->ReadFully(&magic, sizeof(uint32_t));
|
||||
|
||||
const bool is64Bit =
|
||||
magic == mach_o::MH_MAGIC_64 || magic == mach_o::MH_CIGAM_64;
|
||||
@@ -289,11 +292,11 @@ AppSnapshot* Snapshot::TryReadAppendedAppSnapshotElfFromMachO(
|
||||
return nullptr;
|
||||
} else {
|
||||
mach_o::mach_header_64 header;
|
||||
file->Read(&header, sizeof(header));
|
||||
file->ReadFully(&header, sizeof(header));
|
||||
|
||||
for (uint32_t i = 0; i < header.ncmds; ++i) {
|
||||
mach_o::load_command command;
|
||||
file->Read(&command, sizeof(mach_o::load_command));
|
||||
file->ReadFully(&command, sizeof(mach_o::load_command));
|
||||
|
||||
file->SetPosition(file->Position() - sizeof(command));
|
||||
if (command.cmd != mach_o::LC_SEGMENT &&
|
||||
@@ -303,11 +306,11 @@ AppSnapshot* Snapshot::TryReadAppendedAppSnapshotElfFromMachO(
|
||||
}
|
||||
|
||||
mach_o::segment_command_64 segment;
|
||||
file->Read(&segment, sizeof(segment));
|
||||
file->ReadFully(&segment, sizeof(segment));
|
||||
|
||||
for (uint32_t j = 0; j < segment.nsects; ++j) {
|
||||
mach_o::section_64 section;
|
||||
file->Read(§ion, sizeof(section));
|
||||
file->ReadFully(§ion, sizeof(section));
|
||||
|
||||
if (segment.cmd == mach_o::LC_SEGMENT_64 &&
|
||||
strcmp(section.segname, kMachOAppSnapshotSegmentName) == 0 &&
|
||||
@@ -323,7 +326,7 @@ AppSnapshot* Snapshot::TryReadAppendedAppSnapshotElfFromMachO(
|
||||
|
||||
std::unique_ptr<uint8_t[]> snapshot(new uint8_t[section.size]);
|
||||
file->SetPosition(section.offset);
|
||||
file->Read(snapshot.get(), sizeof(uint8_t) * section.size);
|
||||
file->ReadFully(snapshot.get(), sizeof(uint8_t) * section.size);
|
||||
|
||||
Dart_LoadedElf* handle = Dart_LoadELF_Memory(
|
||||
snapshot.get(), section.size, &error, &vm_data_buffer,
|
||||
@@ -347,12 +350,101 @@ AppSnapshot* Snapshot::TryReadAppendedAppSnapshotElfFromMachO(
|
||||
}
|
||||
#endif // defined(DART_TARGET_OS_MACOS)
|
||||
|
||||
#if defined(DART_TARGET_OS_WINDOWS)
|
||||
// Keep in sync with CoffSectionTable._snapshotSectionName from
|
||||
// pkg/dart2native/lib/dart2native_pe.dart.
|
||||
static const char kSnapshotSectionName[] = "snapshot";
|
||||
// Ignore the null terminator, as it won't be present if the string length is
|
||||
// exactly pe::kCoffSectionNameSize.
|
||||
static_assert(sizeof(kSnapshotSectionName) - 1 <= pe::kCoffSectionNameSize,
|
||||
"Section name of snapshot too large");
|
||||
|
||||
AppSnapshot* Snapshot::TryReadAppendedAppSnapshotElfFromPE(
|
||||
const char* container_path) {
|
||||
File* const file = File::Open(NULL, container_path, File::kRead);
|
||||
if (file == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
RefCntReleaseScope<File> rs(file);
|
||||
|
||||
// Ensure file is actually PE-formatted.
|
||||
if (!IsPEFormattedBinary(container_path)) {
|
||||
Syslog::PrintErr(
|
||||
"Attempted load target was not formatted as expected: "
|
||||
"expected PE32 or PE32+ image file.\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Parse the offset into the PE contents (i.e., skipping the MS-DOS stub).
|
||||
uint32_t pe_offset;
|
||||
file->SetPosition(pe::kPEOffsetOffset);
|
||||
file->ReadFully(&pe_offset, sizeof(pe_offset));
|
||||
|
||||
// Skip past the magic bytes to the COFF file header and COFF optional header.
|
||||
const intptr_t coff_offset = pe_offset + sizeof(pe::kPEMagic);
|
||||
file->SetPosition(coff_offset);
|
||||
pe::coff_file_header file_header;
|
||||
file->ReadFully(&file_header, sizeof(file_header));
|
||||
// The optional header follows directly after the file header.
|
||||
pe::coff_optional_header opt_header;
|
||||
file->ReadFully(&opt_header, sizeof(opt_header));
|
||||
|
||||
// Skip to the section table.
|
||||
const intptr_t coff_symbol_table_offset =
|
||||
coff_offset + sizeof(file_header) + file_header.optional_header_size;
|
||||
file->SetPosition(coff_symbol_table_offset);
|
||||
for (intptr_t i = 0; i < file_header.num_sections; i++) {
|
||||
pe::coff_section_header section_header;
|
||||
file->ReadFully(§ion_header, sizeof(section_header));
|
||||
if (strncmp(section_header.name, kSnapshotSectionName,
|
||||
pe::kCoffSectionNameSize) == 0) {
|
||||
// We have to do the loading manually even though currently the snapshot
|
||||
// data is at the end of the file because the file alignment for
|
||||
// PE sections can be less than the page size, and TryReadAppSnapshotElf
|
||||
// won't work if the file offset isn't page-aligned.
|
||||
const char* error = nullptr;
|
||||
const uint8_t* vm_data_buffer = nullptr;
|
||||
const uint8_t* vm_instructions_buffer = nullptr;
|
||||
const uint8_t* isolate_data_buffer = nullptr;
|
||||
const uint8_t* isolate_instructions_buffer = nullptr;
|
||||
|
||||
const intptr_t offset = section_header.file_offset;
|
||||
const intptr_t size = section_header.file_size;
|
||||
|
||||
std::unique_ptr<uint8_t[]> snapshot(new uint8_t[size]);
|
||||
file->SetPosition(offset);
|
||||
file->ReadFully(snapshot.get(), sizeof(uint8_t) * size);
|
||||
|
||||
Dart_LoadedElf* const handle =
|
||||
Dart_LoadELF_Memory(snapshot.get(), size, &error, &vm_data_buffer,
|
||||
&vm_instructions_buffer, &isolate_data_buffer,
|
||||
&isolate_instructions_buffer);
|
||||
|
||||
if (handle == nullptr) {
|
||||
Syslog::PrintErr("Loading failed: %s\n", error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ElfAppSnapshot(handle, vm_data_buffer, vm_instructions_buffer,
|
||||
isolate_data_buffer,
|
||||
isolate_instructions_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
#endif // defined(DART_TARGET_OS_WINDOWS)
|
||||
|
||||
AppSnapshot* Snapshot::TryReadAppendedAppSnapshotElf(
|
||||
const char* container_path) {
|
||||
#if defined(DART_TARGET_OS_MACOS)
|
||||
if (IsMachOFormattedBinary(container_path)) {
|
||||
return TryReadAppendedAppSnapshotElfFromMachO(container_path);
|
||||
}
|
||||
#elif defined(DART_TARGET_OS_WINDOWS)
|
||||
if (IsPEFormattedBinary(container_path)) {
|
||||
return TryReadAppendedAppSnapshotElfFromPE(container_path);
|
||||
}
|
||||
#endif
|
||||
|
||||
File* file = File::Open(NULL, container_path, File::kRead);
|
||||
@@ -472,6 +564,57 @@ bool Snapshot::IsMachOFormattedBinary(const char* filename) {
|
||||
}
|
||||
#endif // defined(DART_TARGET_OS_MACOS)
|
||||
|
||||
#if defined(DART_TARGET_OS_WINDOWS)
|
||||
bool Snapshot::IsPEFormattedBinary(const char* filename) {
|
||||
File* file = File::Open(NULL, filename, File::kRead);
|
||||
if (file == nullptr) {
|
||||
return false;
|
||||
}
|
||||
RefCntReleaseScope<File> rs(file);
|
||||
|
||||
// Parse the PE offset.
|
||||
uint32_t pe_offset;
|
||||
// Ensure the file is long enough to contain the PE offset.
|
||||
if (file->Length() <
|
||||
static_cast<intptr_t>(pe::kPEOffsetOffset + sizeof(pe_offset))) {
|
||||
return false;
|
||||
}
|
||||
file->SetPosition(pe::kPEOffsetOffset);
|
||||
file->Read(&pe_offset, sizeof(pe_offset));
|
||||
|
||||
// Ensure the file is long enough to contain the PE magic bytes.
|
||||
if (file->Length() <
|
||||
static_cast<intptr_t>(pe_offset + sizeof(pe::kPEMagic))) {
|
||||
return false;
|
||||
}
|
||||
// Check the magic bytes.
|
||||
file->SetPosition(pe_offset);
|
||||
for (size_t i = 0; i < sizeof(pe::kPEMagic); i++) {
|
||||
char c;
|
||||
file->Read(&c, sizeof(c));
|
||||
if (c != pe::kPEMagic[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that there is a coff optional header.
|
||||
pe::coff_file_header file_header;
|
||||
pe::coff_optional_header opt_header;
|
||||
file->Read(&file_header, sizeof(file_header));
|
||||
if (file_header.optional_header_size < sizeof(opt_header)) {
|
||||
return false;
|
||||
}
|
||||
file->Read(&opt_header, sizeof(opt_header));
|
||||
// Check the magic bytes in the coff optional header.
|
||||
if (opt_header.magic != pe::kPE32Magic &&
|
||||
opt_header.magic != pe::kPE32PlusMagic) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // defined(DART_TARGET_OS_WINDOWS)
|
||||
|
||||
AppSnapshot* Snapshot::TryReadAppSnapshot(const char* script_uri,
|
||||
bool force_load_elf_from_memory,
|
||||
bool decode_uri) {
|
||||
|
||||
@@ -41,6 +41,9 @@ class Snapshot {
|
||||
#if defined(DART_TARGET_OS_MACOS)
|
||||
static bool IsMachOFormattedBinary(const char* container_path);
|
||||
#endif
|
||||
#if defined(DART_TARGET_OS_WINDOWS)
|
||||
static bool IsPEFormattedBinary(const char* container_path);
|
||||
#endif
|
||||
|
||||
static AppSnapshot* TryReadAppendedAppSnapshotElf(const char* container_path);
|
||||
static AppSnapshot* TryReadAppSnapshot(
|
||||
@@ -62,6 +65,10 @@ class Snapshot {
|
||||
static AppSnapshot* TryReadAppendedAppSnapshotElfFromMachO(
|
||||
const char* container_path);
|
||||
#endif
|
||||
#if defined(DART_TARGET_OS_WINDOWS)
|
||||
static AppSnapshot* TryReadAppendedAppSnapshotElfFromPE(
|
||||
const char* container_path);
|
||||
#endif
|
||||
|
||||
DISALLOW_ALLOCATION();
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(Snapshot);
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef RUNTIME_PLATFORM_PE_H_
|
||||
#define RUNTIME_PLATFORM_PE_H_
|
||||
|
||||
#include <platform/globals.h>
|
||||
|
||||
namespace dart {
|
||||
|
||||
namespace pe {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
static const intptr_t kPEOffsetOffset = 0x3c;
|
||||
static const char kPEMagic[] = {'P', 'E', '\0', '\0'};
|
||||
|
||||
struct coff_file_header {
|
||||
uint16_t machine;
|
||||
uint16_t num_sections;
|
||||
uint32_t timestamp;
|
||||
uint32_t symbol_table_offset;
|
||||
uint32_t num_symbols;
|
||||
uint16_t optional_header_size;
|
||||
uint16_t characteristics;
|
||||
};
|
||||
|
||||
// Does not include the BaseOfData field for PE32 (not PE32+) files, but we
|
||||
// don't need that to load a snapshot out of a PE file.
|
||||
struct coff_optional_header {
|
||||
uint16_t magic;
|
||||
uint8_t linker_major_version;
|
||||
uint8_t linker_minor_version;
|
||||
uint32_t code_size;
|
||||
uint32_t initialized_data_size;
|
||||
uint32_t uninitialized_data_size;
|
||||
uint32_t entry_point_address;
|
||||
uint32_t code_base_address;
|
||||
};
|
||||
|
||||
static const uint16_t kPE32Magic = 0x10b;
|
||||
static const uint16_t kPE32PlusMagic = 0x20b;
|
||||
|
||||
static const intptr_t kCoffSectionNameSize = 8;
|
||||
|
||||
struct coff_section_header {
|
||||
char name[kCoffSectionNameSize];
|
||||
uint32_t virtual_size;
|
||||
uint32_t virtual_address;
|
||||
uint32_t file_size;
|
||||
uint32_t file_offset;
|
||||
uint32_t relocations_offset;
|
||||
uint32_t line_numbers_offset;
|
||||
uint16_t num_relocations;
|
||||
uint16_t num_line_numbers;
|
||||
uint32_t characteristics;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
} // namespace pe
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // RUNTIME_PLATFORM_PE_H_
|
||||
Reference in New Issue
Block a user