diff --git a/lib/std/macho.zig b/lib/std/macho.zig index 8f60d8fe94..cbbbdc1d06 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -71,6 +71,38 @@ pub const source_version_command = extern struct { version: u64, }; +/// The build_version_command contains the min OS version on which this +/// binary was built to run for its platform. The list of known platforms and +/// tool values following it. +pub const build_version_command = extern struct { + /// LC_BUILD_VERSION + cmd: u32, + + /// sizeof(struct build_version_command) plus + /// ntools * sizeof(struct build_version_command) + cmdsize: u32, + + /// platform + platform: u32, + + /// X.Y.Z is encoded in nibbles xxxx.yy.zz + minos: u32, + + /// X.Y.Z is encoded in nibbles xxxx.yy.zz + sdk: u32, + + /// number of tool entries following this + ntools: u32, +}; + +pub const build_tool_version = extern struct { + /// enum for the tool + tool: u32, + + /// version number of the tool + version: u32, +}; + /// The entry_point_command is a replacement for thread_command. /// It is used for main executables to specify the location (file offset) /// of main(). If -stack_size was used at link time, the stacksize diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index c1c5b0c144..4d7f57a955 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -23,9 +23,23 @@ load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, symtab_cmd_index: ?u16 = null, dysymtab_cmd_index: ?u16 = null, +id_cmd_index: ?u16 = null, + +id: ?Id = null, symbols: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, +pub const Id = struct { + name: []const u8, + timestamp: u32, + current_version: u32, + compatibility_version: u32, + + pub fn deinit(id: *Id, allocator: *Allocator) void { + allocator.free(id.name); + } +}; + pub fn init(allocator: *Allocator) Dylib { return .{ .allocator = allocator }; } @@ -45,6 +59,10 @@ pub fn deinit(self: *Dylib) void { if (self.name) |name| { self.allocator.free(name); } + + if (self.id) |*id| { + id.deinit(self.allocator); + } } pub fn closeFile(self: Dylib) void { @@ -78,6 +96,7 @@ pub fn parse(self: *Dylib) !void { } try self.readLoadCommands(reader); + try self.parseId(); try self.parseSymbols(); } @@ -94,6 +113,9 @@ pub fn readLoadCommands(self: *Dylib, reader: anytype) !void { macho.LC_DYSYMTAB => { self.dysymtab_cmd_index = i; }, + macho.LC_ID_DYLIB => { + self.id_cmd_index = i; + }, else => { log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()}); }, @@ -102,6 +124,32 @@ pub fn readLoadCommands(self: *Dylib, reader: anytype) !void { } } +pub fn parseId(self: *Dylib) !void { + const index = self.id_cmd_index orelse { + log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{}); + self.id = .{ + .name = try self.allocator.dupe(u8, self.name.?), + .timestamp = 2, + .current_version = 0, + .compatibility_version = 0, + }; + return; + }; + const id_cmd = self.load_commands.items[index].Dylib; + const dylib = id_cmd.inner.dylib; + + // TODO should we compare the name from the dylib's id with the user-specified one? + const dylib_name = @ptrCast([*:0]const u8, id_cmd.data[dylib.name - @sizeOf(macho.dylib_command) ..]); + const name = try self.allocator.dupe(u8, mem.spanZ(dylib_name)); + + self.id = .{ + .name = name, + .timestamp = dylib.timestamp, + .current_version = dylib.current_version, + .compatibility_version = dylib.compatibility_version, + }; +} + pub fn parseSymbols(self: *Dylib) !void { const index = self.symtab_cmd_index orelse return; const symtab_cmd = self.load_commands.items[index].Symtab; diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 12f3968d53..4c6ac8600c 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -339,8 +339,14 @@ fn parseDylibs(self: *Zld, shared_libs: []const []const u8) !void { try self.dylibs.append(self.allocator, dylib); // Add LC_LOAD_DYLIB command - // TODO Read the timestamp and versions from the dylib itself. - var dylib_cmd = try createLoadDylibCommand(self.allocator, dylib.name.?, 2, 0, 0); + const dylib_id = dylib.id orelse unreachable; + var dylib_cmd = try createLoadDylibCommand( + self.allocator, + dylib_id.name, + dylib_id.timestamp, + dylib_id.current_version, + dylib_id.compatibility_version, + ); errdefer dylib_cmd.deinit(self.allocator); try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });