Commit 73c015b956

Jakub Konka <kubkon@jakubkonka.com>
2021-05-17 14:28:49
zld: parse dylib id
1 parent 17b2588
Changed files (3)
lib
src
link
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
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;
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 });