[vm] Add --macho-min-os-version flag for macOS/iOS targets.
The command line flag allows the user to specify the minimum MacOS or iOS version used in the build version load command for the Mach-O dynamic library snapshot. If not specified, the minimum OS version specified in macOS Mach-O snapshots is 10.15 (Catalina) and the minimum OS version specified in iOS Mach-O snapshots is 13. This CL also changes the targeted OS version in the build version load command to be the same as the minimum OS version. Adds support to parsing build version load commands to pkg/native_stack_traces's Mach-O parser in order to test the existence and contents of build version load commands in vm/dart/unobfuscated_static_symbols. TEST=vm/dart/unobfuscated_static_symbols Issue: https://github.com/dart-lang/sdk/issues/60307 Change-Id: I3ee3ba34297d3261be7e3a1d2fb3c1da1ef0ef05 Cq-Include-Trybots: luci.dart.try:vm-aot-mac-debug-x64-try,vm-aot-mac-debug-arm64-try,vm-aot-linux-debug-arm64-try,vm-aot-linux-debug-x64-try,pkg-mac-release-try,pkg-mac-release-arm64-try,pkg-linux-release-arm64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/438901 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Tess Strickland <sstrickl@google.com>
This commit is contained in:
committed by
Commit Queue
parent
560b35add1
commit
1f5fd27c61
@@ -125,6 +125,7 @@ class LoadCommand {
|
||||
static const LC_SYMTAB = 0x2;
|
||||
static const LC_SEGMENT_64 = 0x19;
|
||||
static const LC_UUID = 0x1b;
|
||||
static const LC_BUILD_VERSION = 0x32;
|
||||
|
||||
static LoadCommand fromReader(Reader reader) {
|
||||
final start = reader.offset; // cmdsize includes size of cmd and cmdsize.
|
||||
@@ -143,6 +144,9 @@ class LoadCommand {
|
||||
case LC_UUID:
|
||||
command = UuidCommand.fromReader(reader, cmd, cmdsize);
|
||||
break;
|
||||
case LC_BUILD_VERSION:
|
||||
command = BuildVersionCommand.fromReader(reader, cmd, cmdsize);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -351,6 +355,161 @@ class UuidCommand extends LoadCommand {
|
||||
}
|
||||
}
|
||||
|
||||
class Version {
|
||||
final int x;
|
||||
final int y;
|
||||
final int z;
|
||||
|
||||
const Version(this.x, this.y, this.z);
|
||||
|
||||
static Version fromInt(int v) {
|
||||
final x = (v & 0xffffffff) >> 16;
|
||||
final y = (v & 0xffff) >> 8;
|
||||
final z = v & 0xff;
|
||||
return Version(x, y, z);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(x, y, z);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other is Version && x == other.x && y == other.y && z == other.z;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => '$x.$y.$z';
|
||||
}
|
||||
|
||||
enum Platform {
|
||||
PLATFORM_UNKNOWN(0, 'Unknown'),
|
||||
PLATFORM_ANY(0xFFFFFFFF, 'any'),
|
||||
PLATFORM_MACOS(0x1, 'MacOS'),
|
||||
PLATFORM_IOS(0x2, 'iOS'),
|
||||
PLATFORM_TVOS(0x3, 'TVOS'),
|
||||
PLATFORM_WATCHOS(0x4, 'WatchOS'),
|
||||
PLATFORM_IOSSIMULATOR(0x7, 'iOS Simulator'),
|
||||
PLATFORM_TVOSSIMULATOR(0x8, 'TVOS Simulator'),
|
||||
PLATFORM_WATCHOSSIMULATOR(0x9, 'WatchOS Simulator');
|
||||
|
||||
final int id;
|
||||
final String description;
|
||||
|
||||
const Platform(this.id, this.description);
|
||||
|
||||
static Platform? fromId(int id) {
|
||||
for (final platform in values) {
|
||||
if (platform.id == id) {
|
||||
return platform;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => description;
|
||||
}
|
||||
|
||||
enum BuildTool {
|
||||
TOOL_CLANG(1, 'clang'),
|
||||
TOOL_SWIFT(2, 'Swift'),
|
||||
TOOL_LD(3, 'ld'),
|
||||
TOOL_LLD(4, 'lld');
|
||||
|
||||
final int id;
|
||||
final String description;
|
||||
|
||||
const BuildTool(this.id, this.description);
|
||||
|
||||
static BuildTool? fromId(int id) {
|
||||
for (final tool in values) {
|
||||
if (tool.id == id) {
|
||||
return tool;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => description;
|
||||
}
|
||||
|
||||
class BuildToolVersion {
|
||||
final int _toolId;
|
||||
final BuildTool? tool;
|
||||
final Version version;
|
||||
|
||||
const BuildToolVersion._(this._toolId, this.tool, this.version);
|
||||
|
||||
factory BuildToolVersion.fromReader(Reader reader) {
|
||||
final id = _readMachOUint32(reader);
|
||||
final tool = BuildTool.fromId(id);
|
||||
final version = Version.fromInt(_readMachOUint32(reader));
|
||||
return BuildToolVersion._(id, tool, version);
|
||||
}
|
||||
|
||||
void writeToStringBuffer(StringBuffer buffer) {
|
||||
buffer
|
||||
..write(' - ')
|
||||
..write(tool ?? 'Unknown (0x${paddedHex(_toolId, 4)})')
|
||||
..write(' (')
|
||||
..write(version)
|
||||
..writeln(')');
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final buffer = StringBuffer();
|
||||
writeToStringBuffer(buffer);
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class BuildVersionCommand extends LoadCommand {
|
||||
final int _platformId;
|
||||
final Platform? platform;
|
||||
final Version minOS;
|
||||
final Version sdk;
|
||||
final List<BuildToolVersion> toolVersions;
|
||||
|
||||
BuildVersionCommand._(super.cmd, super.cmdsize, this._platformId,
|
||||
this.platform, this.minOS, this.sdk, this.toolVersions)
|
||||
: super._();
|
||||
|
||||
static BuildVersionCommand fromReader(Reader reader, int cmd, int cmdsize) {
|
||||
final platformId = _readMachOUint32(reader);
|
||||
final platform = Platform.fromId(platformId);
|
||||
final minOS = Version.fromInt(_readMachOUint32(reader));
|
||||
final sdk = Version.fromInt(_readMachOUint32(reader));
|
||||
final toolCount = _readMachOUint32(reader);
|
||||
final toolVersions = <BuildToolVersion>[];
|
||||
for (int i = 0; i < toolCount; i++) {
|
||||
toolVersions.add(BuildToolVersion.fromReader(reader));
|
||||
}
|
||||
return BuildVersionCommand._(
|
||||
cmd, cmdsize, platformId, platform, minOS, sdk, toolVersions);
|
||||
}
|
||||
|
||||
@override
|
||||
void writeToStringBuffer(StringBuffer buffer) {
|
||||
buffer
|
||||
..writeln('Build version:')
|
||||
..write(' Platform: ')
|
||||
..writeln(platform ?? 'Unknown (0x${paddedHex(_platformId, 4)})')
|
||||
..write(' Minimum OS: ')
|
||||
..writeln(minOS)
|
||||
..write(' Minimum SDK: ')
|
||||
..writeln(sdk);
|
||||
if (toolVersions.isNotEmpty) {
|
||||
buffer.writeln(' Tools:');
|
||||
for (final v in toolVersions) {
|
||||
v.writeToStringBuffer(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MachOHeader {
|
||||
final int magic;
|
||||
final int cputype;
|
||||
@@ -529,6 +688,9 @@ class MachO extends DwarfContainer {
|
||||
Reader.fromTypedData(reader.bdata,
|
||||
wordSize: _header.wordSize, endian: _header.endian);
|
||||
|
||||
Iterable<T> commandsWhereType<T extends LoadCommand>() =>
|
||||
_commands.whereType<T>();
|
||||
|
||||
@override
|
||||
String? get architecture => CpuType.fromCode(_header.cputype)?.dartName;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import "dart:io";
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:native_stack_traces/src/dwarf_container.dart';
|
||||
import 'package:native_stack_traces/src/macho.dart' as macho;
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'use_flag_test_helper.dart';
|
||||
@@ -85,11 +86,18 @@ Future<List<String>?> retrieveDebugMap(
|
||||
return await runOutput(dsymutil, ['--dump-debug-map', snapshotPath]);
|
||||
}
|
||||
|
||||
final hasMinOSVersionOption = Platform.isMacOS || Platform.isIOS;
|
||||
final expectedVersion = hasMinOSVersionOption ? macho.Version(1, 2, 3) : null;
|
||||
|
||||
Future<void> checkSnapshotType(
|
||||
String tempDir,
|
||||
String scriptDill,
|
||||
SnapshotType snapshotType,
|
||||
) async {
|
||||
final commonOptions = <String>[];
|
||||
if (hasMinOSVersionOption && snapshotType == SnapshotType.machoDylib) {
|
||||
commonOptions.add('--macho-min-os-version=$expectedVersion');
|
||||
}
|
||||
// Run the AOT compiler without Dwarf stack trace, once without obfuscation,
|
||||
// once with obfuscation, and once with obfuscation and saving debugging
|
||||
// information.
|
||||
@@ -97,8 +105,14 @@ Future<void> checkSnapshotType(
|
||||
tempDir,
|
||||
'unobfuscated-$snapshotType.so',
|
||||
);
|
||||
await createSnapshot(scriptDill, snapshotType, scriptUnobfuscatedSnapshot);
|
||||
await createSnapshot(
|
||||
scriptDill,
|
||||
snapshotType,
|
||||
scriptUnobfuscatedSnapshot,
|
||||
commonOptions,
|
||||
);
|
||||
final unobfuscatedCase = TestCase(
|
||||
snapshotType,
|
||||
scriptUnobfuscatedSnapshot,
|
||||
snapshotType.fromFile(scriptUnobfuscatedSnapshot)!,
|
||||
debugMap: await retrieveDebugMap(snapshotType, scriptUnobfuscatedSnapshot),
|
||||
@@ -109,9 +123,11 @@ Future<void> checkSnapshotType(
|
||||
'obfuscated-only-$snapshotType.so',
|
||||
);
|
||||
await createSnapshot(scriptDill, snapshotType, scriptObfuscatedOnlySnapshot, [
|
||||
...commonOptions,
|
||||
'--obfuscate',
|
||||
]);
|
||||
final obfuscatedOnlyCase = TestCase(
|
||||
snapshotType,
|
||||
scriptObfuscatedOnlySnapshot,
|
||||
snapshotType.fromFile(scriptObfuscatedOnlySnapshot)!,
|
||||
debugMap: await retrieveDebugMap(
|
||||
@@ -135,10 +151,12 @@ Future<void> checkSnapshotType(
|
||||
'obfuscated-debug-$snapshotType.so',
|
||||
);
|
||||
await createSnapshot(scriptDill, snapshotType, scriptObfuscatedSnapshot, [
|
||||
...commonOptions,
|
||||
'--obfuscate',
|
||||
'--save-debugging-info=$scriptDebuggingInfo',
|
||||
]);
|
||||
obfuscatedCase = TestCase(
|
||||
snapshotType,
|
||||
scriptObfuscatedSnapshot,
|
||||
snapshotType.fromFile(scriptObfuscatedSnapshot)!,
|
||||
debuggingInfoContainer: snapshotType.fromFile(scriptDebuggingInfo)!,
|
||||
@@ -154,11 +172,13 @@ Future<void> checkSnapshotType(
|
||||
'obfuscated-separate-debug-$snapshotType.so',
|
||||
);
|
||||
await createSnapshot(scriptDill, snapshotType, scriptStrippedSnapshot, [
|
||||
...commonOptions,
|
||||
'--strip',
|
||||
'--obfuscate',
|
||||
'--save-debugging-info=$scriptSeparateDebuggingInfo',
|
||||
]);
|
||||
strippedCase = TestCase(
|
||||
snapshotType,
|
||||
scriptStrippedSnapshot,
|
||||
/*container=*/ null, // No static symbols in stripped snapshot.
|
||||
debuggingInfoContainer: snapshotType.fromFile(
|
||||
@@ -192,12 +212,14 @@ Future<void> checkAssembly(String tempDir, String scriptDill) async {
|
||||
}
|
||||
|
||||
class TestCase {
|
||||
final SnapshotType type;
|
||||
final String snapshotPath;
|
||||
final DwarfContainer? container;
|
||||
final DwarfContainer? debuggingInfoContainer;
|
||||
final List<String>? debugMap;
|
||||
|
||||
TestCase(
|
||||
this.type,
|
||||
this.snapshotPath,
|
||||
this.container, {
|
||||
this.debuggingInfoContainer,
|
||||
@@ -222,6 +244,7 @@ Future<void> checkCases(
|
||||
unstrippedObfuscateds.map((c) => c.debugMap!).toList(),
|
||||
);
|
||||
}
|
||||
checkMachOSnapshots(unobfuscated, obfuscateds);
|
||||
}
|
||||
|
||||
Future<void> checkTraces(
|
||||
@@ -412,3 +435,31 @@ void checkDebugMaps(List<String> expected, List<List<String>> cases) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks for MachO snapshots (not separate debugging information).
|
||||
void checkMachOSnapshots(TestCase unobfuscated, List<TestCase> obfuscateds) {
|
||||
checkMachOSnapshot(unobfuscated);
|
||||
obfuscateds.forEach(checkMachOSnapshot);
|
||||
}
|
||||
|
||||
void checkMachOSnapshot(TestCase testCase) {
|
||||
// The checks below are only for snapshots, not for debugging information.
|
||||
final snapshot = testCase.container;
|
||||
if (snapshot is! macho.MachO) return;
|
||||
final buildVersion = snapshot
|
||||
.commandsWhereType<macho.BuildVersionCommand>()
|
||||
.singleOrNull;
|
||||
final expectedPlatform = Platform.isMacOS
|
||||
? macho.Platform.PLATFORM_MACOS
|
||||
: Platform.isIOS
|
||||
? macho.Platform.PLATFORM_IOS
|
||||
: null;
|
||||
Expect.equals(expectedPlatform, buildVersion?.platform);
|
||||
if (testCase.type == SnapshotType.machoDylib) {
|
||||
Expect.equals(expectedVersion, buildVersion?.minOS);
|
||||
Expect.equals(expectedVersion, buildVersion?.sdk);
|
||||
if (buildVersion != null) {
|
||||
Expect.isEmpty(buildVersion.toolVersions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@ import "dart:io";
|
||||
|
||||
import 'package:native_stack_traces/native_stack_traces.dart';
|
||||
import 'package:native_stack_traces/src/constants.dart' show rootLoadingUnitId;
|
||||
import 'package:native_stack_traces/src/macho.dart';
|
||||
import 'package:native_stack_traces/src/macho.dart'
|
||||
show emptyMachOForArchitecture, MachO;
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ import "dart:async";
|
||||
import "dart:io";
|
||||
|
||||
import 'package:native_stack_traces/native_stack_traces.dart';
|
||||
import 'package:native_stack_traces/src/macho.dart';
|
||||
import 'package:native_stack_traces/src/macho.dart'
|
||||
show emptyMachOForArchitecture, MachO;
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
||||
+156
-72
@@ -14,6 +14,7 @@
|
||||
#include "vm/compiler/runtime_api.h"
|
||||
#include "vm/dwarf.h"
|
||||
#include "vm/dwarf_so_writer.h"
|
||||
#include "vm/flags.h"
|
||||
#include "vm/hash_map.h"
|
||||
#include "vm/image_snapshot.h"
|
||||
#include "vm/os.h"
|
||||
@@ -22,6 +23,13 @@
|
||||
|
||||
namespace dart {
|
||||
|
||||
#if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
|
||||
DEFINE_FLAG(charp,
|
||||
macho_min_os_version,
|
||||
nullptr,
|
||||
"The minimum OS version required for MacOS/iOS Mach-O snapshots");
|
||||
#endif
|
||||
|
||||
static constexpr intptr_t kLinearInitValue = -1;
|
||||
|
||||
#define DEFINE_LINEAR_FIELD_METHODS(name) \
|
||||
@@ -53,17 +61,24 @@ static constexpr intptr_t kLinearInitValue = -1;
|
||||
return true; \
|
||||
}
|
||||
|
||||
#if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
|
||||
#define FOR_EACH_MACOS_ONLY_CONCRETE_MACHO_CONTENTS_TYPE(V) \
|
||||
V(MachOBuildVersion) \
|
||||
V(MachOLoadDylib)
|
||||
#else
|
||||
#define FOR_EACH_MACOS_ONLY_CONCRETE_MACHO_CONTENTS_TYPE(V)
|
||||
#endif
|
||||
|
||||
// All concrete subclasses of MachOContents should go here:
|
||||
#define FOR_EACH_CONCRETE_MACHO_CONTENTS_TYPE(V) \
|
||||
FOR_EACH_MACOS_ONLY_CONCRETE_MACHO_CONTENTS_TYPE(V) \
|
||||
V(MachOHeader) \
|
||||
V(MachOSegment) \
|
||||
V(MachOSection) \
|
||||
V(MachOSymbolTable) \
|
||||
V(MachODynamicSymbolTable) \
|
||||
V(MachOUuid) \
|
||||
V(MachOBuildVersion) \
|
||||
V(MachOIdDylib) \
|
||||
V(MachOLoadDylib) \
|
||||
V(MachOCodeSignature)
|
||||
|
||||
#define DECLARE_CONTENTS_TYPE_CLASS(Type) class Type;
|
||||
@@ -924,70 +939,6 @@ class MachOUuid : public MachOCommand {
|
||||
#define MACHO_XYZ_VERSION_ENCODING(x, y, z) \
|
||||
static_cast<uint32_t>(((x) << 16) | ((y) << 8) | (z))
|
||||
|
||||
class MachOBuildVersion : public MachOCommand {
|
||||
public:
|
||||
static constexpr uint32_t kCommandCode = mach_o::LC_BUILD_VERSION;
|
||||
|
||||
MachOBuildVersion()
|
||||
: MachOCommand(kCommandCode,
|
||||
/*needs_offset=*/false,
|
||||
/*in_segment=*/false) {}
|
||||
|
||||
uint32_t cmdsize() const override {
|
||||
return sizeof(mach_o::build_version_command);
|
||||
}
|
||||
|
||||
uint32_t platform() const {
|
||||
#if defined(DART_TARGET_OS_MACOS_IOS)
|
||||
return mach_o::PLATFORM_IOS;
|
||||
#elif defined(DART_TARGET_OS_MACOS)
|
||||
return mach_o::PLATFORM_MACOS;
|
||||
#else
|
||||
return mach_o::PLATFORM_UNKNOWN;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t minos() const {
|
||||
#if defined(DART_TARGET_OS_MACOS_IOS)
|
||||
// TODO(sstrickl): No minimum version for iOS currently defined.
|
||||
UNIMPLEMENTED();
|
||||
#elif defined(DART_TARGET_OS_MACOS)
|
||||
return kMinMacOSVersion;
|
||||
#else
|
||||
return 0; // No version for the unknown platform.
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t sdk() const {
|
||||
#if defined(DART_TARGET_OS_MACOS_IOS)
|
||||
// TODO(sstrickl): No SDK version for iOS currently defined.
|
||||
UNIMPLEMENTED();
|
||||
#elif defined(DART_TARGET_OS_MACOS)
|
||||
return kMacOSSdkVersion;
|
||||
#else
|
||||
return 0; // No version for the unknown platform.
|
||||
#endif
|
||||
}
|
||||
|
||||
void WriteLoadCommand(MachOWriteStream* stream) const override {
|
||||
MachOCommand::WriteLoadCommand(stream);
|
||||
stream->Write32(platform());
|
||||
stream->Write32(minos());
|
||||
stream->Write32(sdk());
|
||||
stream->Write32(0); // No tool versions.
|
||||
}
|
||||
|
||||
void Accept(Visitor* visitor) override {
|
||||
visitor->VisitMachOBuildVersion(this);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr auto kMinMacOSVersion = MACHO_XYZ_VERSION_ENCODING(15, 0, 0);
|
||||
static constexpr auto kMacOSSdkVersion = MACHO_XYZ_VERSION_ENCODING(15, 4, 0);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MachOBuildVersion);
|
||||
};
|
||||
|
||||
class MachODylib : public MachOCommand {
|
||||
public:
|
||||
uint32_t cmdsize() const override {
|
||||
@@ -1058,6 +1009,7 @@ class MachOIdDylib : public MachODylib {
|
||||
DISALLOW_COPY_AND_ASSIGN(MachOIdDylib);
|
||||
};
|
||||
|
||||
#if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
|
||||
class MachOLoadDylib : public MachODylib {
|
||||
public:
|
||||
static constexpr uint32_t kCommandCode = mach_o::LC_LOAD_DYLIB;
|
||||
@@ -1089,6 +1041,139 @@ class MachOLoadDylib : public MachODylib {
|
||||
DISALLOW_COPY_AND_ASSIGN(MachOLoadDylib);
|
||||
};
|
||||
|
||||
class Version {
|
||||
public:
|
||||
explicit Version(intptr_t major) : Version(major, 0, 0) {}
|
||||
|
||||
Version(intptr_t major, intptr_t minor) : Version(major, minor, 0) {}
|
||||
|
||||
Version(intptr_t major, intptr_t minor, intptr_t patch)
|
||||
: major_(major), minor_(minor), patch_(patch) {
|
||||
ASSERT(Utils::IsUint(16, major));
|
||||
ASSERT(Utils::IsUint(8, minor));
|
||||
ASSERT(Utils::IsUint(8, patch));
|
||||
}
|
||||
|
||||
Version(const Version& other)
|
||||
: major_(other.major_), minor_(other.minor_), patch_(other.patch_) {}
|
||||
|
||||
static Version FromString(const char* str) {
|
||||
ASSERT(str != nullptr);
|
||||
int64_t major = 0;
|
||||
int64_t minor = 0;
|
||||
int64_t patch = 0;
|
||||
char* current = nullptr;
|
||||
if (!OS::ParseInitialInt64(str, &major, ¤t)) {
|
||||
FATAL("Expected an integer, got %s", str);
|
||||
}
|
||||
if (!Utils::IsUint(16, major)) {
|
||||
FATAL("Major version is too large to represent in 16 bits: %" Pd64,
|
||||
major);
|
||||
}
|
||||
if (*current != '\0' && *current != '.') {
|
||||
FATAL("Unexpected characters when parsing version: %s", current);
|
||||
}
|
||||
if (*current == '.') {
|
||||
if (!OS::ParseInitialInt64(current + 1, &minor, ¤t)) {
|
||||
FATAL("Expected an integer, got %s", str);
|
||||
}
|
||||
if (!Utils::IsUint(8, minor)) {
|
||||
FATAL("Minor version is too large to represent in 8 bits: %" Pd64,
|
||||
minor);
|
||||
}
|
||||
if (*current != '\0' && *current != '.') {
|
||||
FATAL("Unexpected characters when parsing version: %s", current);
|
||||
}
|
||||
if (*current == '.') {
|
||||
if (!OS::ParseInitialInt64(current + 1, &patch, ¤t)) {
|
||||
FATAL("Expected an integer, got %s", str);
|
||||
}
|
||||
if (!Utils::IsUint(8, patch)) {
|
||||
FATAL("Patch version is too large to represent in 8 bits: %" Pd64,
|
||||
patch);
|
||||
}
|
||||
if (*current != '\0') {
|
||||
FATAL("Unexpected characters when parsing version: %s", current);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Version(major, minor, patch);
|
||||
}
|
||||
|
||||
void Write(MachOWriteStream* stream) const {
|
||||
stream->Write32(MACHO_XYZ_VERSION_ENCODING(major_, minor_, patch_));
|
||||
}
|
||||
|
||||
const char* ToCString() const {
|
||||
return OS::SCreate(Thread::Current()->zone(), "%" Pd ".%" Pd ".%" Pd "",
|
||||
major_, minor_, patch_);
|
||||
}
|
||||
|
||||
private:
|
||||
const intptr_t major_;
|
||||
const intptr_t minor_;
|
||||
const intptr_t patch_;
|
||||
DISALLOW_ALLOCATION();
|
||||
void operator=(const Version&) = delete;
|
||||
};
|
||||
|
||||
// These defaults were taken from Flutter at the time of editing, but can be
|
||||
// overridden using the --min-ios-version and --min-macos-version flags.
|
||||
#if defined(DART_TARGET_OS_MACOS_IOS)
|
||||
static const Version kDefaultMinOSVersion(13, 0, 0); // iOS 13
|
||||
#else
|
||||
static const Version kDefaultMinOSVersion(10, 15, 0); // MacOS Catalina (10.15)
|
||||
#endif
|
||||
|
||||
class MachOBuildVersion : public MachOCommand {
|
||||
public:
|
||||
static constexpr uint32_t kCommandCode = mach_o::LC_BUILD_VERSION;
|
||||
|
||||
MachOBuildVersion()
|
||||
: MachOCommand(kCommandCode,
|
||||
/*needs_offset=*/false,
|
||||
/*in_segment=*/false),
|
||||
min_os_(FLAG_macho_min_os_version != nullptr
|
||||
? Version::FromString(FLAG_macho_min_os_version)
|
||||
: kDefaultMinOSVersion) {}
|
||||
|
||||
uint32_t cmdsize() const override {
|
||||
return sizeof(mach_o::build_version_command);
|
||||
}
|
||||
|
||||
uint32_t platform() const {
|
||||
#if defined(DART_TARGET_OS_MACOS_IOS)
|
||||
return mach_o::PLATFORM_IOS;
|
||||
#else
|
||||
return mach_o::PLATFORM_MACOS;
|
||||
#endif
|
||||
}
|
||||
|
||||
const Version& minos() const { return min_os_; }
|
||||
|
||||
const Version& sdk() const {
|
||||
// Just use the minimum version as the targeted version.
|
||||
return minos();
|
||||
}
|
||||
|
||||
void WriteLoadCommand(MachOWriteStream* stream) const override {
|
||||
MachOCommand::WriteLoadCommand(stream);
|
||||
stream->Write32(platform());
|
||||
minos().Write(stream);
|
||||
sdk().Write(stream);
|
||||
stream->Write32(0); // No tool versions.
|
||||
}
|
||||
|
||||
void Accept(Visitor* visitor) override {
|
||||
visitor->VisitMachOBuildVersion(this);
|
||||
}
|
||||
|
||||
private:
|
||||
const Version min_os_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MachOBuildVersion);
|
||||
};
|
||||
#endif
|
||||
#undef MACHO_XYZ_VERSION_ENCODING
|
||||
|
||||
class MachOSymbolTable : public MachOCommand {
|
||||
@@ -2310,15 +2395,14 @@ void MachOHeader::GenerateUnwindingInformation() {
|
||||
}
|
||||
|
||||
void MachOHeader::GenerateMiscellaneousCommands() {
|
||||
// Not idempotent;
|
||||
ASSERT(!HasCommand(MachOBuildVersion::kCommandCode));
|
||||
ASSERT(!HasCommand(MachOIdDylib::kCommandCode));
|
||||
ASSERT(!HasCommand(MachOLoadDylib::kCommandCode));
|
||||
|
||||
commands_.Add(new (zone_) MachOBuildVersion());
|
||||
if (type_ == SnapshotType::Snapshot) {
|
||||
// Not idempotent;
|
||||
ASSERT(!HasCommand(MachOIdDylib::kCommandCode));
|
||||
commands_.Add(new (zone_) MachOIdDylib(identifier_));
|
||||
#if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
|
||||
ASSERT(!HasCommand(MachOBuildVersion::kCommandCode));
|
||||
ASSERT(!HasCommand(MachOLoadDylib::kCommandCode));
|
||||
commands_.Add(new (zone_) MachOBuildVersion());
|
||||
commands_.Add(MachOLoadDylib::CreateLoadSystemDylib(zone_));
|
||||
#endif
|
||||
}
|
||||
|
||||
+15
-1
@@ -99,13 +99,27 @@ class OS {
|
||||
PRINTF_ATTRIBUTE(2, 3);
|
||||
static char* VSCreate(Zone* zone, const char* format, va_list args);
|
||||
|
||||
// Converts the initial portion of a C string which represents a valid dart
|
||||
// integer into a 64 bit value.
|
||||
//
|
||||
// Returns false if it is unable to convert the string to a 64 bit value,
|
||||
// the failure could be because of underflow/overflow or invalid characters.
|
||||
//
|
||||
// On success the function returns true, 'value' contains the converted
|
||||
// value, and 'end' contains a pointer to the next character
|
||||
// after the parsed integer.
|
||||
static bool ParseInitialInt64(const char* str, int64_t* value, char** end);
|
||||
|
||||
// Converts a C string which represents a valid dart integer into a 64 bit
|
||||
// value.
|
||||
// Returns false if it is unable to convert the string to a 64 bit value,
|
||||
// the failure could be because of underflow/overflow or invalid characters.
|
||||
// On success the function returns true and 'value' contains the converted
|
||||
// value.
|
||||
static bool StringToInt64(const char* str, int64_t* value);
|
||||
static bool StringToInt64(const char* str, int64_t* value) {
|
||||
char* endptr = nullptr;
|
||||
return ParseInitialInt64(str, value, &endptr) && (*endptr == '\0');
|
||||
}
|
||||
|
||||
// Register code observers relevant to this OS.
|
||||
static void RegisterCodeObservers();
|
||||
|
||||
@@ -303,10 +303,10 @@ char* OS::VSCreate(Zone* zone, const char* format, va_list args) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool OS::StringToInt64(const char* str, int64_t* value) {
|
||||
ASSERT(str != nullptr && strlen(str) > 0 && value != nullptr);
|
||||
bool OS::ParseInitialInt64(const char* str, int64_t* value, char** end) {
|
||||
ASSERT(str != nullptr && strlen(str) > 0 && value != nullptr &&
|
||||
end != nullptr);
|
||||
int32_t base = 10;
|
||||
char* endptr;
|
||||
int i = 0;
|
||||
if (str[0] == '-') {
|
||||
i = 1;
|
||||
@@ -321,11 +321,11 @@ bool OS::StringToInt64(const char* str, int64_t* value) {
|
||||
if (base == 16) {
|
||||
// Unsigned 64-bit hexadecimal integer literals are allowed but
|
||||
// immediately interpreted as signed 64-bit integers.
|
||||
*value = static_cast<int64_t>(strtoull(str, &endptr, base));
|
||||
*value = static_cast<int64_t>(strtoull(str, end, base));
|
||||
} else {
|
||||
*value = strtoll(str, &endptr, base);
|
||||
*value = strtoll(str, end, base);
|
||||
}
|
||||
return ((errno == 0) && (endptr != str) && (*endptr == 0));
|
||||
return (errno == 0) && (*end != str);
|
||||
}
|
||||
|
||||
void OS::RegisterCodeObservers() {
|
||||
|
||||
@@ -560,10 +560,10 @@ char* OS::VSCreate(Zone* zone, const char* format, va_list args) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool OS::StringToInt64(const char* str, int64_t* value) {
|
||||
ASSERT(str != nullptr && strlen(str) > 0 && value != nullptr);
|
||||
bool OS::ParseInitialInt64(const char* str, int64_t* value, char** end) {
|
||||
ASSERT(str != nullptr && strlen(str) > 0 && value != nullptr &&
|
||||
end != nullptr);
|
||||
int32_t base = 10;
|
||||
char* endptr;
|
||||
int i = 0;
|
||||
if (str[0] == '-') {
|
||||
i = 1;
|
||||
@@ -578,11 +578,11 @@ bool OS::StringToInt64(const char* str, int64_t* value) {
|
||||
if (base == 16) {
|
||||
// Unsigned 64-bit hexadecimal integer literals are allowed but
|
||||
// immediately interpreted as signed 64-bit integers.
|
||||
*value = static_cast<int64_t>(strtoull(str, &endptr, base));
|
||||
*value = static_cast<int64_t>(strtoull(str, end, base));
|
||||
} else {
|
||||
*value = strtoll(str, &endptr, base);
|
||||
*value = strtoll(str, end, base);
|
||||
}
|
||||
return ((errno == 0) && (endptr != str) && (*endptr == 0));
|
||||
return (errno == 0) && (*end != str);
|
||||
}
|
||||
|
||||
void OS::RegisterCodeObservers() {
|
||||
|
||||
@@ -616,10 +616,10 @@ char* OS::VSCreate(Zone* zone, const char* format, va_list args) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool OS::StringToInt64(const char* str, int64_t* value) {
|
||||
ASSERT(str != nullptr && strlen(str) > 0 && value != nullptr);
|
||||
bool OS::ParseInitialInt64(const char* str, int64_t* value, char** end) {
|
||||
ASSERT(str != nullptr && strlen(str) > 0 && value != nullptr &&
|
||||
end != nullptr);
|
||||
int32_t base = 10;
|
||||
char* endptr;
|
||||
int i = 0;
|
||||
if (str[0] == '-') {
|
||||
i = 1;
|
||||
@@ -634,11 +634,11 @@ bool OS::StringToInt64(const char* str, int64_t* value) {
|
||||
if (base == 16) {
|
||||
// Unsigned 64-bit hexadecimal integer literals are allowed but
|
||||
// immediately interpreted as signed 64-bit integers.
|
||||
*value = static_cast<int64_t>(strtoull(str, &endptr, base));
|
||||
*value = static_cast<int64_t>(strtoull(str, end, base));
|
||||
} else {
|
||||
*value = strtoll(str, &endptr, base);
|
||||
*value = strtoll(str, end, base);
|
||||
}
|
||||
return ((errno == 0) && (endptr != str) && (*endptr == 0));
|
||||
return (errno == 0) && (*end != str);
|
||||
}
|
||||
|
||||
void OS::RegisterCodeObservers() {
|
||||
|
||||
@@ -223,10 +223,10 @@ char* OS::VSCreate(Zone* zone, const char* format, va_list args) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool OS::StringToInt64(const char* str, int64_t* value) {
|
||||
ASSERT(str != nullptr && strlen(str) > 0 && value != nullptr);
|
||||
bool OS::ParseInitialInt64(const char* str, int64_t* value, char** end) {
|
||||
ASSERT(str != nullptr && strlen(str) > 0 && value != nullptr &&
|
||||
end != nullptr);
|
||||
int32_t base = 10;
|
||||
char* endptr;
|
||||
int i = 0;
|
||||
if (str[0] == '-') {
|
||||
i = 1;
|
||||
@@ -241,11 +241,11 @@ bool OS::StringToInt64(const char* str, int64_t* value) {
|
||||
if (base == 16) {
|
||||
// Unsigned 64-bit hexadecimal integer literals are allowed but
|
||||
// immediately interpreted as signed 64-bit integers.
|
||||
*value = static_cast<int64_t>(strtoull(str, &endptr, base));
|
||||
*value = static_cast<int64_t>(strtoull(str, end, base));
|
||||
} else {
|
||||
*value = strtoll(str, &endptr, base);
|
||||
*value = strtoll(str, end, base);
|
||||
}
|
||||
return ((errno == 0) && (endptr != str) && (*endptr == 0));
|
||||
return (errno == 0) && (*end != str);
|
||||
}
|
||||
|
||||
void OS::RegisterCodeObservers() {}
|
||||
|
||||
@@ -328,10 +328,10 @@ char* OS::VSCreate(Zone* zone, const char* format, va_list args) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool OS::StringToInt64(const char* str, int64_t* value) {
|
||||
ASSERT(str != nullptr && strlen(str) > 0 && value != nullptr);
|
||||
bool OS::ParseInitialInt64(const char* str, int64_t* value, char** end) {
|
||||
ASSERT(str != nullptr && strlen(str) > 0 && value != nullptr &&
|
||||
end != nullptr);
|
||||
int32_t base = 10;
|
||||
char* endptr;
|
||||
int i = 0;
|
||||
if (str[0] == '-') {
|
||||
i = 1;
|
||||
@@ -346,11 +346,11 @@ bool OS::StringToInt64(const char* str, int64_t* value) {
|
||||
if (base == 16) {
|
||||
// Unsigned 64-bit hexadecimal integer literals are allowed but
|
||||
// immediately interpreted as signed 64-bit integers.
|
||||
*value = static_cast<int64_t>(_strtoui64(str, &endptr, base));
|
||||
*value = static_cast<int64_t>(_strtoui64(str, end, base));
|
||||
} else {
|
||||
*value = _strtoi64(str, &endptr, base);
|
||||
*value = _strtoi64(str, end, base);
|
||||
}
|
||||
return ((errno == 0) && (endptr != str) && (*endptr == 0));
|
||||
return (errno == 0) && (*end != str);
|
||||
}
|
||||
|
||||
void OS::RegisterCodeObservers() {}
|
||||
|
||||
Reference in New Issue
Block a user