@@ -265,6 +265,32 @@ Future<int> pipeStream(RandomAccessFile from, RandomAccessFile to,
265
265
return numWritten;
266
266
}
267
267
268
+ class _MacOSVersion {
269
+ final int ? _major;
270
+ final int ? _minor;
271
+
272
+ static final _regexp = RegExp (r'Version (?<major>\d+).(?<minor>\d+)' );
273
+ static const _parseFailure = 'Could not determine macOS version' ;
274
+
275
+ const _MacOSVersion ._internal (this ._major, this ._minor);
276
+
277
+ static const _unknown = _MacOSVersion ._internal (null , null );
278
+
279
+ factory _MacOSVersion () {
280
+ if (! Platform .isMacOS) return _unknown;
281
+ final match =
282
+ _regexp.matchAsPrefix (Platform .operatingSystemVersion) as RegExpMatch ? ;
283
+ if (match == null ) return _unknown;
284
+ final minor = int .tryParse (match.namedGroup ('minor' )! );
285
+ final major = int .tryParse (match.namedGroup ('major' )! );
286
+ return _MacOSVersion ._internal (major, minor);
287
+ }
288
+
289
+ bool get isValid => _major != null ;
290
+ int get major => _major ?? (throw _parseFailure);
291
+ int get minor => _minor ?? (throw _parseFailure);
292
+ }
293
+
268
294
// Writes an "appended" dart runtime + script snapshot file in a format
269
295
// compatible with MachO executables.
270
296
Future writeAppendedMachOExecutable (
@@ -312,17 +338,39 @@ Future writeAppendedMachOExecutable(
312
338
await stream.close ();
313
339
314
340
if (machOFile.hasCodeSignature) {
341
+ if (! Platform .isMacOS) {
342
+ throw 'Cannot sign MachO binary on non-macOS platform' ;
343
+ }
344
+
315
345
// After writing the modified file, we perform ad-hoc signing (no identity)
316
- // similar to the linker (the linker-signed option flag) to ensure that any
317
- // LC_CODE_SIGNATURE block has the correct CD hashes. This is necessary for
318
- // platforms where signature verification is always on (e.g., OS X on M1).
346
+ // to ensure that any LC_CODE_SIGNATURE block has the correct CD hashes.
347
+ // This is necessary for platforms where signature verification is always on
348
+ // (e.g., OS X on M1).
319
349
//
320
350
// We use the `-f` flag to force signature overwriting as the official
321
351
// Dart binaries (including dartaotruntime) are fully signed.
322
- final signingProcess = await Process .run (
323
- 'codesign' , ['-f' , '-o' , 'linker-signed' , '-s' , '-' , outputPath]);
352
+ final args = ['-f' , '-s' , '-' , outputPath];
353
+
354
+ // If running on macOS >=11.0, then the linker-signed option flag can be
355
+ // used to create a signature that does not need to be force overridden.
356
+ final version = _MacOSVersion ();
357
+ if (version.isValid && version.major >= 11 ) {
358
+ final signingProcess =
359
+ await Process .run ('codesign' , ['-o' , 'linker-signed' , ...args]);
360
+ if (signingProcess.exitCode == 0 ) {
361
+ return ;
362
+ }
363
+ print ('Failed to add a linker signed signature, '
364
+ 'adding a regular signature instead.' );
365
+ }
366
+
367
+ // If that fails or we're running on an older or undetermined version of
368
+ // macOS, we fall back to signing without the linker-signed option flag.
369
+ // Thus, to sign the binary, the developer must force signature overwriting.
370
+ final signingProcess = await Process .run ('codesign' , args);
324
371
if (signingProcess.exitCode != 0 ) {
325
- print ('Subcommand terminated with exit code ${signingProcess .exitCode }.' );
372
+ print ('Failed to replace the dartaotruntime signature, ' );
373
+ print ('subcommand terminated with exit code ${signingProcess .exitCode }.' );
326
374
if (signingProcess.stdout.isNotEmpty) {
327
375
print ('Subcommand stdout:' );
328
376
print (signingProcess.stdout);
0 commit comments