Commit 0b2133d441

Jakub Konka <kubkon@jakubkonka.com>
2024-01-17 14:55:40
macho: init metadata and partially implement updateDecl
1 parent bd9d8bd
Changed files (2)
src
src/link/MachO/ZigObject.zig
@@ -295,7 +295,8 @@ pub fn updateDecl(
             return;
         },
     };
-    _ = code;
+    const sect_index = try self.getDeclOutputSection(macho_file, decl, code);
+    _ = sect_index;
     // const addr = try self.updateDeclCode(decl_index, code);
 
     // if (decl_state) |*ds| {
@@ -313,6 +314,59 @@ pub fn updateDecl(
     // try self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
 }
 
+fn getDeclOutputSection(
+    self: *ZigObject,
+    macho_file: *MachO,
+    decl: *const Module.Decl,
+    code: []const u8,
+) error{OutOfMemory}!u8 {
+    _ = self;
+    const mod = macho_file.base.comp.module.?;
+    const any_non_single_threaded = macho_file.base.comp.config.any_non_single_threaded;
+    const sect_id: u8 = switch (decl.ty.zigTypeTag(mod)) {
+        .Fn => macho_file.zig_text_section_index.?,
+        else => blk: {
+            if (decl.getOwnedVariable(mod)) |variable| {
+                if (variable.is_threadlocal and any_non_single_threaded) {
+                    const is_all_zeroes = for (code) |byte| {
+                        if (byte != 0) break false;
+                    } else true;
+                    if (is_all_zeroes) break :blk macho_file.getSectionByName("__DATA", "__thread_bss") orelse try macho_file.addSection(
+                        "__DATA",
+                        "__thread_bss",
+                        .{ .flags = macho.S_THREAD_LOCAL_ZEROFILL },
+                    );
+                    break :blk macho_file.getSectionByName("__DATA", "__thread_data") orelse try macho_file.addSection(
+                        "__DATA",
+                        "__thread_data",
+                        .{ .flags = macho.S_THREAD_LOCAL_REGULAR },
+                    );
+                }
+
+                if (variable.is_const) break :blk macho_file.zig_data_const_section_index.?;
+                if (Value.fromInterned(variable.init).isUndefDeep(mod)) {
+                    // TODO: get the optimize_mode from the Module that owns the decl instead
+                    // of using the root module here.
+                    break :blk switch (macho_file.base.comp.root_mod.optimize_mode) {
+                        .Debug, .ReleaseSafe => macho_file.zig_data_section_index.?,
+                        .ReleaseFast, .ReleaseSmall => macho_file.zig_bss_section_index.?,
+                    };
+                }
+
+                // TODO I blatantly copied the logic from the Wasm linker, but is there a less
+                // intrusive check for all zeroes than this?
+                const is_all_zeroes = for (code) |byte| {
+                    if (byte != 0) break false;
+                } else true;
+                if (is_all_zeroes) break :blk macho_file.zig_bss_section_index.?;
+                break :blk macho_file.zig_data_section_index.?;
+            }
+            break :blk macho_file.zig_data_const_section_index.?;
+        },
+    };
+    return sect_id;
+}
+
 pub fn lowerUnnamedConst(
     self: *ZigObject,
     macho_file: *MachO,
@@ -386,7 +440,7 @@ pub fn getOrCreateMetadataForDecl(
         const sym_index = try self.addAtom(macho_file);
         const mod = macho_file.base.comp.module.?;
         const decl = mod.declPtr(decl_index);
-        const sym = macho_file.getSymbol(self.symbols.items[sym_index]);
+        const sym = macho_file.getSymbol(sym_index);
         if (decl.getOwnedVariable(mod)) |variable| {
             if (variable.is_threadlocal and any_non_single_threaded) {
                 sym.flags.tlv = true;
src/link/MachO.zig
@@ -82,6 +82,18 @@ lazy_bind: LazyBindSection = .{},
 export_trie: ExportTrieSection = .{},
 unwind_info: UnwindInfo = .{},
 
+/// Tracked loadable segments during incremental linking.
+zig_text_seg_index: ?u8 = null,
+zig_data_const_seg_index: ?u8 = null,
+zig_data_seg_index: ?u8 = null,
+
+/// Tracked section headers with incremental updates to Zig object.
+zig_text_section_index: ?u8 = null,
+zig_data_const_section_index: ?u8 = null,
+zig_data_section_index: ?u8 = null,
+zig_bss_section_index: ?u8 = null,
+zig_got_section_index: ?u8 = null,
+
 has_tlv: bool = false,
 binds_to_weak: bool = false,
 weak_defines: bool = false,
@@ -234,8 +246,11 @@ pub fn createEmpty(
             } });
             self.zig_object = index;
             try self.getZigObject().?.init(self);
+            try self.initMetadata(.{
+                .symbol_count_hint = options.symbol_count_hint,
+                .program_code_size_hint = options.program_code_size_hint,
+            });
 
-            // TODO init metadata
             // TODO init dwarf
 
             // if (comp.config.debug_format != .strip) {
@@ -3103,6 +3118,45 @@ fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
     return start;
 }
 
+const InitMetadataOptions = struct {
+    symbol_count_hint: u64,
+    program_code_size_hint: u64,
+};
+
+// TODO: move to ZigObject
+// TODO: bring back pre-alloc of segments/sections
+fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
+    _ = options;
+
+    if (!self.base.isRelocatable()) {
+        // TODO: If we are not emitting a relocatable object file, init segments.
+    }
+
+    if (self.zig_text_section_index == null) {
+        self.zig_text_section_index = try self.addSection("__TEXT", "__text", .{
+            .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
+        });
+    }
+
+    if (self.zig_got_section_index == null and !self.base.isRelocatable()) {
+        self.zig_got_section_index = try self.addSection("__DATA_CONST", "__got_zig", .{});
+    }
+
+    if (self.zig_data_const_section_index == null) {
+        self.zig_data_const_section_index = try self.addSection("__DATA_CONST", "__const", .{});
+    }
+
+    if (self.zig_data_section_index == null) {
+        self.zig_data_section_index = try self.addSection("__DATA", "__data", .{});
+    }
+
+    if (self.zig_bss_section_index == null) {
+        self.zig_bss_section_index = try self.addSection("__DATA", "_bss", .{
+            .flags = macho.S_ZEROFILL,
+        });
+    }
+}
+
 pub fn getTarget(self: MachO) std.Target {
     return self.base.comp.root_mod.resolved_target.result;
 }
@@ -3148,6 +3202,29 @@ inline fn requiresThunks(self: MachO) bool {
     return self.getTarget().cpu.arch == .aarch64;
 }
 
+pub fn addSegment(self: *MachO, name: []const u8, opts: struct {
+    vmaddr: u64 = 0,
+    vmsize: u64 = 0,
+    fileoff: u64 = 0,
+    filesize: u64 = 0,
+    prot: macho.vm_prot_t = macho.PROT.NONE,
+}) error{OutOfMemory}!u8 {
+    const gpa = self.base.comp.gpa;
+    const index = @as(u8, @intCast(self.segments.items.len));
+    try self.segments.append(gpa, .{
+        .segname = makeStaticString(name),
+        .vmaddr = opts.vmaddr,
+        .vmsize = opts.vmsize,
+        .fileoff = opts.fileoff,
+        .filesize = opts.filesize,
+        .maxprot = opts.prot,
+        .initprot = opts.prot,
+        .nsects = 0,
+        .cmdsize = @sizeOf(macho.segment_command_64),
+    });
+    return index;
+}
+
 const AddSectionOpts = struct {
     flags: u32 = macho.S_REGULAR,
     reserved1: u32 = 0,