Commit 72310db1da

Jakub Konka <kubkon@jakubkonka.com>
2020-11-12 21:07:06
stage2 MachO: add min OS version load cmd
1 parent 58365c4
Changed files (2)
lib
src
lib/std/macho.zig
@@ -705,6 +705,23 @@ pub const relocation_info = packed struct {
     r_type: u4,
 };
 
+/// The version_min_command contains the min OS version on which this 
+/// binary was built to run.
+pub const version_min_command = extern struct {
+    /// LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS or LC_VERSION_MIN_WATCHOS
+    /// or LC_VERSION_MIN_TVOS
+    cmd: u32,
+
+    /// sizeof(struct min_version_command)
+    cmdsize: u32,
+
+    /// X.Y.Z is encoded in nibbles xxxx.yy.zz
+    version: u32,
+
+    /// X.Y.Z is encoded in nibbles xxxx.yy.zz
+    sdk: u32,
+};
+
 /// After MacOS X 10.1 when a new load command is added that is required to be
 /// understood by the dynamic linker for the image to execute properly the
 /// LC_REQ_DYLD bit will be or'ed into the load command constant.  If the dynamic
src/link/MachO.zig
@@ -33,6 +33,7 @@ const LoadCommand = union(enum) {
     Dylinker: macho.dylinker_command,
     Dylib: macho.dylib_command,
     EntryPoint: macho.entry_point_command,
+    MinVersion: macho.version_min_command,
 
     pub fn cmdsize(self: LoadCommand) u32 {
         return switch (self) {
@@ -44,6 +45,7 @@ const LoadCommand = union(enum) {
             .Dylinker => |x| x.cmdsize,
             .Dylib => |x| x.cmdsize,
             .EntryPoint => |x| x.cmdsize,
+            .MinVersion => |x| x.cmdsize,
         };
     }
 
@@ -57,6 +59,7 @@ const LoadCommand = union(enum) {
             .Dylinker => |cmd| writeGeneric(cmd, file, offset),
             .Dylib => |cmd| writeGeneric(cmd, file, offset),
             .EntryPoint => |cmd| writeGeneric(cmd, file, offset),
+            .MinVersion => |cmd| writeGeneric(cmd, file, offset),
         };
     }
 
@@ -96,6 +99,8 @@ function_starts_cmd_index: ?u16 = null,
 /// Specifies offset wrt __TEXT segment start address to the main entry point
 /// of the binary.
 main_cmd_index: ?u16 = null,
+/// Minimum OS version
+version_min_cmd_index: ?u16 = null,
 
 /// Table of all sections
 sections: std.ArrayListUnmanaged(macho.section_64) = .{},
@@ -317,8 +322,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
     try self.writeAllGlobalSymbols();
     try self.writeAllUndefSymbols();
 
-    try self.writeStringTable();
-
     switch (self.base.options.output_mode) {
         .Exe => {
             // Write export trie.
@@ -379,8 +382,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
         const nundefs = @intCast(u32, self.undef_symbols.items.len);
         const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
         symtab.nsyms = nlocals + nglobals + nundefs;
+        symtab.stroff = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64);
     }
 
+    try self.writeStringTable();
+
     if (self.cmd_table_dirty) {
         try self.writeCmdHeaders();
         try self.writeMachOHeader();
@@ -1329,8 +1335,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
         self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
         const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), @sizeOf(u64));
         // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
-        // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0.
-        const min_version = 0x10000;
+        // In the meantime, we're gonna hardcode to the minimum compatibility version of 0.0.0.
+        const min_version = 0x0;
         const dylib = .{
             .name = @sizeOf(macho.dylib_command),
             .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
@@ -1359,6 +1365,18 @@ pub fn populateMissingMetadata(self: *MachO) !void {
         });
         self.cmd_table_dirty = true;
     }
+    if (self.version_min_cmd_index == null) {
+        self.version_min_cmd_index = @intCast(u16, self.load_commands.items.len);
+        try self.load_commands.append(self.base.allocator, .{
+            // TODO allow for different targets and different versions
+            .MinVersion = .{
+                .cmd = macho.LC_VERSION_MIN_MACOSX,
+                .cmdsize = @sizeOf(macho.version_min_command),
+                .version = 0xB0001, // 11.0.1 BigSur
+                .sdk = 0xB0001, // 11.0.1 BigSur
+            },
+        });
+    }
     {
         const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
         const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo;