Commit eb528a9cbc

Jakub Konka <kubkon@jakubkonka.com>
2020-12-05 10:57:05
lld+macho: add missing LC_LOAD_DYLIB cmd
1 parent 6dfe9cc
Changed files (1)
src
src/link/MachO.zig
@@ -752,44 +752,81 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
 
             // At this stage, LLD has done its job. It is time to patch the resultant
             // binaries up!
-            // This is currently needed only for aarch64 targets.
-            if (target.cpu.arch == .aarch64) {
-                const out_file = try directory.handle.openFile(self.base.options.emit.?.sub_path, .{ .write = true });
-                try self.parseFromFile(out_file);
-                if (self.code_signature_cmd_index == null) {
-                    const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
-                    const text_section = text_segment.sections.items[self.text_section_index.?];
-                    const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
-                    const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den;
-
-                    if (needed_size + after_last_cmd_offset > text_section.offset) {
-                        std.log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
-                        std.log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size});
-                        std.log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{});
-                        return error.NotEnoughPadding;
-                    }
+            const out_file = try directory.handle.openFile(self.base.options.emit.?.sub_path, .{ .write = true });
+            try self.parseFromFile(out_file);
+            if (self.libsystem_cmd_index == null) {
+                const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+                const text_section = text_segment.sections.items[self.text_section_index.?];
+                const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
+                const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den;
+
+                if (needed_size + after_last_cmd_offset > text_section.offset) {
+                    std.log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
+                    std.log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size});
+                    std.log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{});
+                    return error.NotEnoughPadding;
+                }
 
-                    const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
-                    // TODO This is clunky.
-                    self.linkedit_segment_next_offset = @intCast(u32, mem.alignForwardGeneric(u64, linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize, @sizeOf(u64)));
-                    // Add code signature load command
-                    self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
-                    try self.load_commands.append(self.base.allocator, .{
-                        .LinkeditData = .{
-                            .cmd = macho.LC_CODE_SIGNATURE,
-                            .cmdsize = @sizeOf(macho.linkedit_data_command),
-                            .dataoff = 0,
-                            .datasize = 0,
-                        },
-                    });
-                    // Pad out space for code signature
-                    try self.writeCodeSignaturePadding();
-                    // Write updated load commands and the header
-                    try self.writeLoadCommands();
-                    try self.writeHeader();
-                    // Generate adhoc code signature
-                    try self.writeCodeSignature();
+                // Add load dylib load command
+                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 0.0.0.
+                const min_version = 0x0;
+                var dylib_cmd = emptyGenericCommandWithData(macho.dylib_command{
+                    .cmd = macho.LC_LOAD_DYLIB,
+                    .cmdsize = @intCast(u32, cmdsize),
+                    .dylib = .{
+                        .name = @sizeOf(macho.dylib_command),
+                        .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
+                        .current_version = min_version,
+                        .compatibility_version = min_version,
+                    },
+                });
+                dylib_cmd.data = try self.base.allocator.alloc(u8, cmdsize - dylib_cmd.inner.dylib.name);
+                mem.set(u8, dylib_cmd.data, 0);
+                mem.copy(u8, dylib_cmd.data, mem.spanZ(LIB_SYSTEM_PATH));
+                try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd });
+                // TODO Fixup linkedit data
+                // Write updated load commands and the header
+                try self.writeLoadCommands();
+                try self.writeHeader();
+            }
+            if (self.code_signature_cmd_index == null) {
+                if (target.cpu.arch != .aarch64) return; // This is currently needed only for aarch64 targets.
+                const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+                const text_section = text_segment.sections.items[self.text_section_index.?];
+                const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
+                const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den;
+
+                if (needed_size + after_last_cmd_offset > text_section.offset) {
+                    std.log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
+                    std.log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size});
+                    std.log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{});
+                    return error.NotEnoughPadding;
                 }
+
+                const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
+                // TODO This is clunky.
+                self.linkedit_segment_next_offset = @intCast(u32, mem.alignForwardGeneric(u64, linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize, @sizeOf(u64)));
+                // Add code signature load command
+                self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
+                try self.load_commands.append(self.base.allocator, .{
+                    .LinkeditData = .{
+                        .cmd = macho.LC_CODE_SIGNATURE,
+                        .cmdsize = @sizeOf(macho.linkedit_data_command),
+                        .dataoff = 0,
+                        .datasize = 0,
+                    },
+                });
+
+                // Pad out space for code signature
+                try self.writeCodeSignaturePadding();
+                // Write updated load commands and the header
+                try self.writeLoadCommands();
+                try self.writeHeader();
+                // Generate adhoc code signature
+                try self.writeCodeSignature();
             }
         }
     }
@@ -1925,18 +1962,18 @@ fn parseFromFile(self: *MachO, file: fs.File) !void {
         switch (cmd.cmd()) {
             macho.LC_SEGMENT_64 => {
                 const x = cmd.Segment;
-                if (isSegmentOrSection(&x.inner.segname, "__PAGEZERO")) {
+                if (parseAndCmpName(x.inner.segname[0..], "__PAGEZERO")) {
                     self.pagezero_segment_cmd_index = i;
-                } else if (isSegmentOrSection(&x.inner.segname, "__LINKEDIT")) {
+                } else if (parseAndCmpName(x.inner.segname[0..], "__LINKEDIT")) {
                     self.linkedit_segment_cmd_index = i;
-                } else if (isSegmentOrSection(&x.inner.segname, "__TEXT")) {
+                } else if (parseAndCmpName(x.inner.segname[0..], "__TEXT")) {
                     self.text_segment_cmd_index = i;
                     for (x.sections.items) |sect, j| {
-                        if (isSegmentOrSection(&sect.sectname, "__text")) {
+                        if (parseAndCmpName(sect.sectname[0..], "__text")) {
                             self.text_section_index = @intCast(u16, j);
                         }
                     }
-                } else if (isSegmentOrSection(&x.inner.segname, "__DATA")) {
+                } else if (parseAndCmpName(x.inner.segname[0..], "__DATA")) {
                     self.data_segment_cmd_index = i;
                 }
             },
@@ -1962,7 +1999,10 @@ fn parseFromFile(self: *MachO, file: fs.File) !void {
                 self.main_cmd_index = i;
             },
             macho.LC_LOAD_DYLIB => {
-                self.libsystem_cmd_index = i; // TODO This is incorrect, but we'll fixup later.
+                const x = cmd.Dylib;
+                if (parseAndCmpName(x.data, mem.spanZ(LIB_SYSTEM_PATH))) {
+                    self.libsystem_cmd_index = i;
+                }
             },
             macho.LC_FUNCTION_STARTS => {
                 self.function_starts_cmd_index = i;
@@ -1983,9 +2023,10 @@ fn parseFromFile(self: *MachO, file: fs.File) !void {
     }
     self.header = header;
 
-    // TODO parse memory mapped segments
+    // TODO Should we parse memory mapped segments here or as needed?
 }
 
-fn isSegmentOrSection(name: *const [16]u8, needle: []const u8) bool {
-    return mem.eql(u8, mem.trimRight(u8, name.*[0..], &[_]u8{0}), needle);
+fn parseAndCmpName(name: []const u8, needle: []const u8) bool {
+    const len = mem.indexOfScalar(u8, name[0..], @as(u8, 0)) orelse name.len;
+    return mem.eql(u8, name[0..len], needle);
 }