[pkg/dart2native] Handle cases where linker-signed is not an option.
Instead of assuming that linker-signed is a valid option flag for codesign, only attempt to use it on macOS 11.0 or later, where signatures on linker-signed binaries need not be force overwritten. Otherwise, fall back to a regular signature that must be force overwritten. Bug: https://github.com/dart-lang/sdk/issues/49010 Cq-Include-Trybots: luci.dart.try:pkg-mac-release-arm64-try,pkg-mac-release-try Change-Id: Ia2bff2f501dbe37b3269a2a2f16ce9a29026664e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/245000 Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Tess Strickland <sstrickl@google.com>
This commit is contained in:
committed by
Commit Bot
parent
6ef2b237e0
commit
4e520ec7a7
@@ -265,6 +265,32 @@ Future<int> pipeStream(RandomAccessFile from, RandomAccessFile to,
|
||||
return numWritten;
|
||||
}
|
||||
|
||||
class _MacOSVersion {
|
||||
final int? _major;
|
||||
final int? _minor;
|
||||
|
||||
static final _regexp = RegExp(r'Version (?<major>\d+).(?<minor>\d+)');
|
||||
static const _parseFailure = 'Could not determine macOS version';
|
||||
|
||||
const _MacOSVersion._internal(this._major, this._minor);
|
||||
|
||||
static const _unknown = _MacOSVersion._internal(null, null);
|
||||
|
||||
factory _MacOSVersion() {
|
||||
if (!Platform.isMacOS) return _unknown;
|
||||
final match =
|
||||
_regexp.matchAsPrefix(Platform.operatingSystemVersion) as RegExpMatch?;
|
||||
if (match == null) return _unknown;
|
||||
final minor = int.tryParse(match.namedGroup('minor')!);
|
||||
final major = int.tryParse(match.namedGroup('major')!);
|
||||
return _MacOSVersion._internal(major, minor);
|
||||
}
|
||||
|
||||
bool get isValid => _major != null;
|
||||
int get major => _major ?? (throw _parseFailure);
|
||||
int get minor => _minor ?? (throw _parseFailure);
|
||||
}
|
||||
|
||||
// Writes an "appended" dart runtime + script snapshot file in a format
|
||||
// compatible with MachO executables.
|
||||
Future writeAppendedMachOExecutable(
|
||||
@@ -312,17 +338,39 @@ Future writeAppendedMachOExecutable(
|
||||
await stream.close();
|
||||
|
||||
if (machOFile.hasCodeSignature) {
|
||||
if (!Platform.isMacOS) {
|
||||
throw 'Cannot sign MachO binary on non-macOS platform';
|
||||
}
|
||||
|
||||
// After writing the modified file, we perform ad-hoc signing (no identity)
|
||||
// similar to the linker (the linker-signed option flag) to ensure that any
|
||||
// LC_CODE_SIGNATURE block has the correct CD hashes. This is necessary for
|
||||
// platforms where signature verification is always on (e.g., OS X on M1).
|
||||
// to ensure that any LC_CODE_SIGNATURE block has the correct CD hashes.
|
||||
// This is necessary for platforms where signature verification is always on
|
||||
// (e.g., OS X on M1).
|
||||
//
|
||||
// We use the `-f` flag to force signature overwriting as the official
|
||||
// Dart binaries (including dartaotruntime) are fully signed.
|
||||
final signingProcess = await Process.run(
|
||||
'codesign', ['-f', '-o', 'linker-signed', '-s', '-', outputPath]);
|
||||
final args = ['-f', '-s', '-', outputPath];
|
||||
|
||||
// If running on macOS >=11.0, then the linker-signed option flag can be
|
||||
// used to create a signature that does not need to be force overridden.
|
||||
final version = _MacOSVersion();
|
||||
if (version.isValid && version.major >= 11) {
|
||||
final signingProcess =
|
||||
await Process.run('codesign', ['-o', 'linker-signed', ...args]);
|
||||
if (signingProcess.exitCode == 0) {
|
||||
return;
|
||||
}
|
||||
print('Failed to add a linker signed signature, '
|
||||
'adding a regular signature instead.');
|
||||
}
|
||||
|
||||
// If that fails or we're running on an older or undetermined version of
|
||||
// macOS, we fall back to signing without the linker-signed option flag.
|
||||
// Thus, to sign the binary, the developer must force signature overwriting.
|
||||
final signingProcess = await Process.run('codesign', args);
|
||||
if (signingProcess.exitCode != 0) {
|
||||
print('Subcommand terminated with exit code ${signingProcess.exitCode}.');
|
||||
print('Failed to replace the dartaotruntime signature, ');
|
||||
print('subcommand terminated with exit code ${signingProcess.exitCode}.');
|
||||
if (signingProcess.stdout.isNotEmpty) {
|
||||
print('Subcommand stdout:');
|
||||
print(signingProcess.stdout);
|
||||
|
||||
@@ -187,7 +187,7 @@ void defineCompileTests() {
|
||||
expect(result.stdout, contains('2: foo'));
|
||||
});
|
||||
|
||||
test('Compile and run executable', () async {
|
||||
Future<void> basicCompileTest() async {
|
||||
final p = project(mainSrc: 'void main() { print("I love executables"); }');
|
||||
final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
|
||||
final outFile = path.canonicalize(path.join(p.dirPath, 'lib', 'main.exe'));
|
||||
@@ -213,7 +213,9 @@ void defineCompileTests() {
|
||||
expect(result.stderr, isEmpty);
|
||||
expect(result.exitCode, 0);
|
||||
expect(result.stdout, contains('I love executables'));
|
||||
}, skip: isRunningOnIA32);
|
||||
}
|
||||
|
||||
test('Compile and run executable', basicCompileTest, skip: isRunningOnIA32);
|
||||
|
||||
test('Compile to executable disabled on IA32', () async {
|
||||
final p = project(mainSrc: 'void main() { print("I love executables"); }');
|
||||
@@ -1112,4 +1114,33 @@ void main() {
|
||||
expect(result.stderr, contains('Warning:'));
|
||||
expect(result.exitCode, 0);
|
||||
});
|
||||
|
||||
if (Platform.isMacOS) {
|
||||
test('Compile and run executable from signed dartaotruntime', () async {
|
||||
// Either the locally built dartaotruntime is already linker signed
|
||||
// (on M1) or it is unsigned (on X64). For this test, sign the
|
||||
// dartaotruntime executable with a non-linker signed adhoc signature,
|
||||
// which won't cause issues with any other tests that use it. This
|
||||
// ensures the code signing path in dart2native is exercised on X64
|
||||
// (macOS <11.0), and also mimics the case for end users that are using
|
||||
// the published Dart SDK (which is fully signed, not linker signed).
|
||||
final Directory binDir = File(Platform.resolvedExecutable).parent;
|
||||
final String originalRuntimePath =
|
||||
path.join(binDir.path, 'dartaotruntime');
|
||||
final codeSigningProcess = await Process.start('codesign', [
|
||||
'-o',
|
||||
'runtime',
|
||||
'-s',
|
||||
'-',
|
||||
originalRuntimePath,
|
||||
]);
|
||||
|
||||
final signingResult = await codeSigningProcess.exitCode;
|
||||
expect(signingResult, 0);
|
||||
|
||||
// Now perform the same basic compile and run test with the signed
|
||||
// dartaotruntime.
|
||||
await basicCompileTest();
|
||||
}, skip: isRunningOnIA32);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user