Commit 9745e7b512

Jakub Konka <kubkon@jakubkonka.com>
2020-08-24 09:48:00
Clean up draft for merging into upstream
Signed-off-by: Jakub Konka <kubkon@jakubkonka.com>
1 parent 1698e6d
Changed files (3)
lib
src-self-hosted
lib/std/macho.zig
@@ -119,6 +119,11 @@ pub const dylinker_command = extern struct {
     name: u32,
 };
 
+/// A dynamically linked shared library (filetype == MH_DYLIB in the mach header)
+/// contains a dylib_command (cmd == LC_ID_DYLIB) to identify the library.
+/// An object that uses a dynamically linked shared library also contains a
+/// dylib_command (cmd == LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, or
+/// LC_REEXPORT_DYLIB) for each library it uses.
 pub const dylib_command = extern struct {
     /// LC_ID_DYLIB, LC_LOAD_WEAK_DYLIB, LC_LOAD_DYLIB, LC_REEXPORT_DYLIB
     cmd: u32,
src-self-hosted/link/MachO.zig
@@ -16,6 +16,8 @@ const Module = @import("../Module.zig");
 const link = @import("../link.zig");
 const File = link.File;
 
+const is_darwin = std.Target.current.os.tag.isDarwin();
+
 pub const base_tag: File.Tag = File.Tag.macho;
 
 base: File,
@@ -42,16 +44,27 @@ seg_table_dirty: bool = false,
 
 error_flags: File.ErrorFlags = File.ErrorFlags{},
 
+/// TODO ultimately this will be propagated down from main() and set (in this form or another)
+/// when user links against system lib.
+link_against_system: bool = false,
+
 /// `alloc_num / alloc_den` is the factor of padding when allocating.
 const alloc_num = 4;
 const alloc_den = 3;
 
 /// Default path to dyld
+/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
+/// instead but this will do for now.
 const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
 
-/// We always have to link against libSystem since macOS Catalina (TODO link)
+/// Default lib search path
+/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
+/// instead but this will do for now.
+const DEFAULT_LIB_SEARCH_PATH: []const u8 = "/usr/lib";
+
 const LIB_SYSTEM_NAME: [*:0]const u8 = "System";
-const LIB_SYSTEM_PATH: [*:0]const u8 = "/usr/lib/libSystem.B.dylib";
+/// TODO we should search for libSystem and fail if it doesn't exist, instead of hardcoding it
+const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
 
 pub const TextBlock = struct {
     pub const empty = TextBlock{};
@@ -212,74 +225,81 @@ pub fn flush(self: *MachO, module: *Module) !void {
 
     switch (self.base.options.output_mode) {
         .Exe => {
-            {
-                // We need to add LC_LOAD_DYLINKER and LC_LOAD_DYLIB since we always
-                // have to link against libSystem.dylib
-                const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH)));
-                const load_dylinker = [1]macho.dylinker_command{
-                    .{
-                        .cmd = macho.LC_LOAD_DYLINKER,
-                        .cmdsize = cmdsize,
-                        .name = @sizeOf(macho.dylinker_command),
-                    },
-                };
-                try self.commands.append(self.base.allocator, .{
-                    .cmd = macho.LC_LOAD_DYLINKER,
-                    .cmdsize = cmdsize,
-                });
-
-                try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), self.command_file_offset.?);
-
-                const file_offset = self.command_file_offset.? + @sizeOf(macho.dylinker_command);
-                try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset);
-
-                try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset);
-                self.command_file_offset.? += cmdsize;
-            }
-
-            {
-                // Link against libSystem
-                const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH)));
-                const version = std.c.NSVersionOfRunTimeLibrary(LIB_SYSTEM_NAME);
-                const dylib = .{
-                    .name = @sizeOf(macho.dylib_command),
-                    .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
-                    .current_version = version,
-                    .compatibility_version = 0x10000, // not sure why this either; value from reverse engineering
-                };
-                const load_dylib = [1]macho.dylib_command{
-                    .{
-                        .cmd = macho.LC_LOAD_DYLIB,
-                        .cmdsize = cmdsize,
-                        .dylib = dylib,
-                    },
-                };
-                try self.commands.append(self.base.allocator, .{
-                    .cmd = macho.LC_LOAD_DYLIB,
-                    .cmdsize = cmdsize,
-                });
-
-                try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), self.command_file_offset.?);
-
-                const file_offset = self.command_file_offset.? + @sizeOf(macho.dylib_command);
-                try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset);
-
-                try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset);
-                self.command_file_offset.? += cmdsize;
+            if (self.link_against_system) {
+                if (is_darwin) {
+                    {
+                        // Specify path to dynamic linker dyld
+                        const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH)));
+                        const load_dylinker = [1]macho.dylinker_command{
+                            .{
+                                .cmd = macho.LC_LOAD_DYLINKER,
+                                .cmdsize = cmdsize,
+                                .name = @sizeOf(macho.dylinker_command),
+                            },
+                        };
+                        try self.commands.append(self.base.allocator, .{
+                            .cmd = macho.LC_LOAD_DYLINKER,
+                            .cmdsize = cmdsize,
+                        });
+
+                        try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), self.command_file_offset.?);
+
+                        const file_offset = self.command_file_offset.? + @sizeOf(macho.dylinker_command);
+                        try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset);
+
+                        try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset);
+                        self.command_file_offset.? += cmdsize;
+                    }
+
+                    {
+                        // Link against libSystem
+                        const cmdsize = commandSize(@intCast(u32, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH)));
+                        // According to Apple's manual, we should obtain current libSystem version using libc call
+                        // NSVersionOfRunTimeLibrary.
+                        const version = std.c.NSVersionOfRunTimeLibrary(LIB_SYSTEM_NAME);
+                        const dylib = .{
+                            .name = @sizeOf(macho.dylib_command),
+                            .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
+                            .current_version = version,
+                            .compatibility_version = 0x10000, // not sure why this either; value from reverse engineering
+                        };
+                        const load_dylib = [1]macho.dylib_command{
+                            .{
+                                .cmd = macho.LC_LOAD_DYLIB,
+                                .cmdsize = cmdsize,
+                                .dylib = dylib,
+                            },
+                        };
+                        try self.commands.append(self.base.allocator, .{
+                            .cmd = macho.LC_LOAD_DYLIB,
+                            .cmdsize = cmdsize,
+                        });
+
+                        try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), self.command_file_offset.?);
+
+                        const file_offset = self.command_file_offset.? + @sizeOf(macho.dylib_command);
+                        try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset);
+
+                        try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset);
+                        self.command_file_offset.? += cmdsize;
+                    }
+                } else {
+                    @panic("linking against libSystem on non-native target is unsupported");
+                }
             }
         },
         .Obj => return error.TODOImplementWritingObjFiles,
         .Lib => return error.TODOImplementWritingLibFiles,
     }
 
-    // if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
-    //     log.debug("flushing. no_entry_point_found = true\n", .{});
-    //     self.error_flags.no_entry_point_found = true;
-    // } else {
-    log.debug("flushing. no_entry_point_found = false\n", .{});
-    self.error_flags.no_entry_point_found = false;
-    try self.writeMachOHeader();
-    // }
+    if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
+        log.debug("flushing. no_entry_point_found = true\n", .{});
+        self.error_flags.no_entry_point_found = true;
+    } else {
+        log.debug("flushing. no_entry_point_found = false\n", .{});
+        self.error_flags.no_entry_point_found = false;
+        try self.writeMachOHeader();
+    }
 }
 
 pub fn deinit(self: *MachO) void {
@@ -290,51 +310,7 @@ pub fn deinit(self: *MachO) void {
 
 pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {}
 
-pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
-    // const tracy = trace(@src());
-    // defer tracy.end();
-
-    // var code_buffer = std.ArrayList(u8).init(self.base.allocator);
-    // defer code_buffer.deinit();
-
-    // var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
-    // defer dbg_line_buffer.deinit();
-
-    // var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
-    // defer dbg_info_buffer.deinit();
-
-    // var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};
-    // defer {
-    //     for (dbg_info_type_relocs.items()) |*entry| {
-    //         entry.value.relocs.deinit(self.base.allocator);
-    //     }
-    //     dbg_info_type_relocs.deinit(self.base.allocator);
-    // }
-
-    // const typed_value = decl.typed_value.most_recent.typed_value;
-    // log.debug("typed_value = {}", .{typed_value});
-
-    // const res = try codegen.generateSymbol(
-    //     &self.base,
-    //     decl.src(),
-    //     typed_value,
-    //     &code_buffer,
-    //     &dbg_line_buffer,
-    //     &dbg_info_buffer,
-    //     &dbg_info_type_relocs,
-    // );
-    // log.debug("res = {}", .{res});
-
-    // const code = switch (res) {
-    //     .externally_managed => |x| x,
-    //     .appended => code_buffer.items,
-    //     .fail => |em| {
-    //         decl.analysis = .codegen_failure;
-    //         try module.failed_decls.put(module.gpa, decl, em);
-    //         return;
-    //     },
-    // };
-}
+pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {}
 
 pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}
 
@@ -351,105 +327,7 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
     @panic("TODO implement getDeclVAddr for MachO");
 }
 
-pub fn populateMissingMetadata(self: *MachO) !void {
-    // if (self.seg_load_re_index == null) {
-    //     self.seg_load_re_index = @intCast(u16, self.segment_cmds.items.len);
-    //     const file_size = self.base.options.program_code_size_hint;
-    //     const p_align = 0x1000;
-    //     const off = self.findFreeSpace(file_size, p_align);
-    //     log.debug("found LC_SEGMENT_64 free space 0x{x} to 0x{x}", .{ off, off + file_size });
-    //     try self.segment_cmds.append(self.base.allocator, .{});
-    //     self.entry_addr = null;
-    //     self.seg_table_dirty = true;
-    // }
-    // if (self.seg_got_index == null) {
-    //     self.seg_got_index = @intCast(u16, self.segment_cmds.items.len);
-    //     const file_size = 8 * self.base.options.symbol_count_hint;
-    //     // Apple recommends to page align for better performance.
-    //     // TODO This is not necessarily true for MH_OBJECT which means we
-    //     // could potentially shave off a couple of bytes when generating
-    //     // only object files.
-    //     const p_align = 0x1000;
-    //     const off = self.findFreeSpace(file_size, p_align);
-    //     log.debug("found LC_SEGMENT_64 free space 0x{x} to 0x{x}", .{ off, off + file_size });
-    //     const default_vmaddr = 0x4000000;
-    //     try self.segment_cmds.append(self.base.allocator, .{
-    //         .cmd = macho.LC_SEGMENT_64,
-    //         .cmdsize = @sizeOf(macho.segment_command_64),
-    //         .segname = self.makeString("__TEXT"),
-    //         .vmaddr = default_vmaddr,
-    //         .vmsize = file_size,
-    //         .fileoff = off,
-    //         .filesize = file_size,
-    //         .maxprot = 0x5,
-    //         .initprot = 0x5,
-    //         .nsects = 0,
-    //         .flags = 0,
-    //     });
-    //     self.seg_table_dirty = true;
-    // }
-}
-
-/// Returns end pos of collision, if any.
-fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
-    const header_size: u64 = @sizeOf(macho.mach_header_64);
-    if (start < header_size)
-        return header_size;
-
-    const end = start + satMul(size, alloc_num) / alloc_den;
-
-    // if (self.sec_table_offset) |off| {
-    //     const section_size: u64 = @sizeOf(macho.section_64);
-    //     const tight_size = self.sections.items.len * section_size;
-    //     const increased_size = satMul(tight_size, alloc_num) / alloc_den;
-    //     const test_end = off + increased_size;
-    //     if (end > off and start < test_end) {
-    //         return test_end;
-    //     }
-    // }
-
-    // if (self.seg_table_offset) |off| {
-    //     const segment_size: u64 = @sizeOf(macho.segment_command_64);
-    //     const tight_size = self.segment_cmds.items.len * segment_size;
-    //     const increased_size = satMul(tight_size, alloc_num) / alloc_den;
-    //     const test_end = off + increased_size;
-    //     if (end > off and start < test_end) {
-    //         return test_end;
-    //     }
-    // }
-
-    // for (self.sections.items) |section| {
-    //     const increased_size = satMul(section.size, alloc_num) / alloc_den;
-    //     const test_end = section.offset + increased_size;
-    //     if (end > section.offset and start < test_end) {
-    //         return test_end;
-    //     }
-    // }
-
-    for (self.segments.items) |segment| {
-        const increased_size = satMul(segment.filesize, alloc_num) / alloc_den;
-        const test_end = segment_cmd.fileoff + increased_size;
-        if (end > segment_cmd.fileoff and start < test_end) {
-            return test_end;
-        }
-    }
-
-    return null;
-}
-
-fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 {
-    var start: u64 = 0;
-    while (self.detectAllocCollision(start, object_size)) |item_end| {
-        start = mem.alignForwardGeneric(u64, item_end, min_alignment);
-    }
-    return start;
-}
-
-/// Saturating multiplication
-fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
-    const T = @TypeOf(a, b);
-    return std.math.mul(T, a, b) catch std.math.maxInt(T);
-}
+pub fn populateMissingMetadata(self: *MachO) !void {}
 
 fn makeString(comptime bytes: []const u8) [16]u8 {
     var buf: [16]u8 = undefined;
src-self-hosted/codegen.zig
@@ -1428,57 +1428,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                 }
             } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
                 switch (arch) {
-                    // .x86_64 => {
-                    //     for (info.args) |mc_arg, arg_i| {
-                    //         const arg = inst.args[arg_i];
-                    //         const arg_mcv = try self.resolveInst(inst.args[arg_i]);
-                    //         // Here we do not use setRegOrMem even though the logic is similar, because
-                    //         // the function call will move the stack pointer, so the offsets are different.
-                    //         switch (mc_arg) {
-                    //             .none => continue,
-                    //             .register => |reg| {
-                    //                 try self.genSetReg(arg.src, reg, arg_mcv);
-                    //                 // TODO interact with the register allocator to mark the instruction as moved.
-                    //             },
-                    //             .stack_offset => {
-                    //                 // Here we need to emit instructions like this:
-                    //                 // mov     qword ptr [rsp + stack_offset], x
-                    //                 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
-                    //             },
-                    //             .ptr_stack_offset => {
-                    //                 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
-                    //             },
-                    //             .ptr_embedded_in_code => {
-                    //                 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
-                    //             },
-                    //             .undef => unreachable,
-                    //             .immediate => unreachable,
-                    //             .unreach => unreachable,
-                    //             .dead => unreachable,
-                    //             .embedded_in_code => unreachable,
-                    //             .memory => unreachable,
-                    //             .compare_flags_signed => unreachable,
-                    //             .compare_flags_unsigned => unreachable,
-                    //         }
-                    //     }
-
-                    //     if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
-                    //         if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
-                    //             const func = func_val.func;
-                    //             const got = &macho_file.segment_cmds.items[macho_file.seg_got_index.?];
-                    //             const ptr_bytes: u64 = 8;
-                    //             const got_addr = @intCast(u32, got.vmaddrs + func.owner_decl.link.macho.offset_table_index * ptr_bytes);
-                    //             // 01 xx xx xx xx    call [addr]
-                    //             try self.code.ensureCapacity(self.code.items.len + 5);
-                    //             self.code.appendSliceAssumeCapacity(&[1]u8{ 0x1 });
-                    //             mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
-                    //         } else {
-                    //             return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
-                    //         }
-                    //     } else {
-                    //         return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
-                    //     }
-                    // },
                     .x86_64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for x86_64 arch", .{}),
                     .aarch64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for aarch64 arch", .{}),
                     else => unreachable,