[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>
diff --git a/pkg/native_stack_traces/lib/src/macho.dart b/pkg/native_stack_traces/lib/src/macho.dart
index 15b65f4..adeddfa 100644
--- a/pkg/native_stack_traces/lib/src/macho.dart
+++ b/pkg/native_stack_traces/lib/src/macho.dart
@@ -125,6 +125,7 @@
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 @@
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 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 @@
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;
diff --git a/runtime/tests/vm/dart/unobfuscated_static_symbols_test.dart b/runtime/tests/vm/dart/unobfuscated_static_symbols_test.dart
index 51d4674..efaab5b 100644
--- a/runtime/tests/vm/dart/unobfuscated_static_symbols_test.dart
+++ b/runtime/tests/vm/dart/unobfuscated_static_symbols_test.dart
@@ -13,6 +13,7 @@
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 @@
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 @@
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 @@
'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 @@
'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 @@
'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 @@
}
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 @@
unstrippedObfuscateds.map((c) => c.debugMap!).toList(),
);
}
+ checkMachOSnapshots(unobfuscated, obfuscateds);
}
Future<void> checkTraces(
@@ -412,3 +435,31 @@
}
}
}
+
+// 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);
+ }
+ }
+}
diff --git a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_deferred_test.dart b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_deferred_test.dart
index b49e314..298665b 100644
--- a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_deferred_test.dart
+++ b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_deferred_test.dart
@@ -16,7 +16,8 @@
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';
diff --git a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart
index 4a50290..9ec3370 100644
--- a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart
+++ b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart
@@ -10,7 +10,8 @@
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';
diff --git a/runtime/vm/mach_o.cc b/runtime/vm/mach_o.cc
index 58c88d2..8fbb5c2 100644
--- a/runtime/vm/mach_o.cc
+++ b/runtime/vm/mach_o.cc
@@ -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 @@
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 @@
#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 @@
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 @@
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::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
}
diff --git a/runtime/vm/os.h b/runtime/vm/os.h
index 29875c4..f5a9199 100644
--- a/runtime/vm/os.h
+++ b/runtime/vm/os.h
@@ -99,13 +99,27 @@
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();
diff --git a/runtime/vm/os_android.cc b/runtime/vm/os_android.cc
index 64a5d53..5547201 100644
--- a/runtime/vm/os_android.cc
+++ b/runtime/vm/os_android.cc
@@ -303,10 +303,10 @@
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 @@
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() {
diff --git a/runtime/vm/os_fuchsia.cc b/runtime/vm/os_fuchsia.cc
index eb3860e..4f6b563 100644
--- a/runtime/vm/os_fuchsia.cc
+++ b/runtime/vm/os_fuchsia.cc
@@ -560,10 +560,10 @@
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 @@
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() {
diff --git a/runtime/vm/os_linux.cc b/runtime/vm/os_linux.cc
index f8c7bc5..a09fbfd 100644
--- a/runtime/vm/os_linux.cc
+++ b/runtime/vm/os_linux.cc
@@ -616,10 +616,10 @@
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 @@
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() {
diff --git a/runtime/vm/os_macos.cc b/runtime/vm/os_macos.cc
index 4a84c7c..b11ef12 100644
--- a/runtime/vm/os_macos.cc
+++ b/runtime/vm/os_macos.cc
@@ -223,10 +223,10 @@
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 @@
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() {}
diff --git a/runtime/vm/os_win.cc b/runtime/vm/os_win.cc
index b0472b8..1c510d2 100644
--- a/runtime/vm/os_win.cc
+++ b/runtime/vm/os_win.cc
@@ -328,10 +328,10 @@
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 @@
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() {}