[vm] Additional Mach-O related command line options for gen_snapshot.

Adds the following command line options to gen_snapshot:

* --macho-install-name: The name to use as the install name for the
  dynamic library (used in the LC_ID_DYLIB and LC_CODE_SIGNATURE
  load commands). If not provided, the output filename is used.

* --[no-]macho-linker-signature: Whether or not gen_snapshot should
  generate an ad-hoc linker-signed signature. Defaults to true.

The following command line option is macOS/iOS-specific:

* --macho-rpath: Comma-delimited run paths that should be added at
  runtime to the current run path used for finding @rpath-prefixed
  dynamic libraries.

Adds support to parsing rpath and dylib load commands to
pkg/native_stack_traces's Mach-O parser in order to test the
command line options appropriately.

TEST=vm/dart/use_macho_options

Issue: https://github.com/dart-lang/sdk/issues/60307
Change-Id: I17618578a7bff7851a88cdaf27f299f058f98dd3
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,vm-aot-win-release-arm64-try,vm-aot-win-debug-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/441920
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
This commit is contained in:
Tess Strickland
2025-07-29 06:01:14 -07:00
committed by Commit Queue
parent 20f773724a
commit 83e9bd72ea
6 changed files with 545 additions and 164 deletions
@@ -121,10 +121,15 @@ class LoadCommand {
LoadCommand._(this.cmd, this.cmdsize);
static const LC_REQ_DYLD = 0x80000000;
static const LC_SEGMENT = 0x1;
static const LC_SYMTAB = 0x2;
static const LC_ID_DYLIB = 0xd;
static const LC_SEGMENT_64 = 0x19;
static const LC_UUID = 0x1b;
static const LC_RPATH = 0x1c | LC_REQ_DYLD;
static const LC_CODE_SIGNATURE = 0x1d; // Only used in vm/dart tests.
static const LC_BUILD_VERSION = 0x32;
static LoadCommand fromReader(Reader reader) {
@@ -147,6 +152,10 @@ class LoadCommand {
case LC_BUILD_VERSION:
command = BuildVersionCommand.fromReader(reader, cmd, cmdsize);
break;
case LC_ID_DYLIB:
command = DylibCommand.fromReader(reader, cmd, cmdsize);
case LC_RPATH:
command = RunPathCommand.fromReader(reader, cmd, cmdsize);
default:
break;
}
@@ -355,6 +364,86 @@ class UuidCommand extends LoadCommand {
}
}
class DylibInfo {
final String name;
final int timestamp;
final int currentVersion;
final int compatibilityVersion;
const DylibInfo._(this.name, this.timestamp, this.currentVersion,
this.compatibilityVersion);
static DylibInfo fromReader(Reader reader, int cmdsize) {
final start = reader.offset - 8; // cmd + cmdsize
final offset = _readMachOUint32(reader);
final timestamp = _readMachOUint32(reader);
final currentVersion = _readMachOUint32(reader);
final compatibilityVersion = _readMachOUint32(reader);
reader.seek(start + offset, absolute: true);
final name = reader.readNullTerminatedString(maxSize: cmdsize - offset);
return DylibInfo._(name, timestamp, currentVersion, compatibilityVersion);
}
void writeToStringBuffer(StringBuffer buffer) {
buffer
..write(' Name: ')
..writeln(name)
..write(' Timestamp: ')
..writeln(timestamp)
..write(' Current version: ')
..writeln(currentVersion)
..write(' Compatibility version: ')
..writeln(compatibilityVersion);
}
@override
String toString() {
final buffer = StringBuffer();
writeToStringBuffer(buffer);
return buffer.toString();
}
}
class DylibCommand extends LoadCommand {
final DylibInfo info;
DylibCommand._(super.cmd, super.cmdsize, this.info) : super._();
static DylibCommand fromReader(Reader reader, int cmd, int cmdsize) =>
DylibCommand._(cmd, cmdsize, DylibInfo.fromReader(reader, cmdsize));
@override
void writeToStringBuffer(StringBuffer buffer) {
if (cmd == LoadCommand.LC_ID_DYLIB) {
buffer.writeln('LC_ID_DYLIB:');
info.writeToStringBuffer(buffer);
} else {
throw StateError("Unexpected command code $cmd");
}
}
}
class RunPathCommand extends LoadCommand {
String path;
RunPathCommand._(super.cmd, super.cmdsize, this.path) : super._();
static RunPathCommand fromReader(Reader reader, int cmd, int cmdsize) {
final start = reader.offset - 8; // cmd + cmdsize
final offset = _readMachOUint32(reader);
reader.seek(start + offset, absolute: true);
final path = reader.readNullTerminatedString(maxSize: cmdsize - offset);
return RunPathCommand._(cmd, cmdsize, path);
}
@override
void writeToStringBuffer(StringBuffer buffer) {
buffer
..write('Run path: ')
..write(path);
}
}
class Version {
final int x;
final int y;
@@ -688,6 +777,7 @@ class MachO extends DwarfContainer {
Reader.fromTypedData(reader.bdata,
wordSize: _header.wordSize, endian: _header.endian);
Iterable<LoadCommand> get commands => _commands;
Iterable<T> commandsWhereType<T extends LoadCommand>() =>
_commands.whereType<T>();