Commit b3277c8936

Jakub Konka <kubkon@jakubkonka.com>
2023-02-01 11:12:53
link: make Plan9 atoms fully owned by the linker
1 parent 9fdc32c
Changed files (8)
src/arch/aarch64/CodeGen.zig
@@ -4307,7 +4307,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
     if (self.air.value(callee)) |func_value| {
         if (func_value.castTag(.function)) |func_payload| {
             const func = func_payload.data;
-            const fn_owner_decl = mod.declPtr(func.owner_decl);
 
             if (self.bin_file.cast(link.File.Elf)) |elf_file| {
                 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
@@ -4333,11 +4332,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
                     },
                 });
             } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
-                try p9.seeDecl(func.owner_decl);
+                const decl_block_index = try p9.seeDecl(func.owner_decl);
+                const decl_block = p9.getDeclBlock(decl_block_index);
                 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
                 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
                 const got_addr = p9.bases.data;
-                const got_index = fn_owner_decl.link.plan9.got_index.?;
+                const got_index = decl_block.got_index.?;
                 const fn_got_addr = got_addr + got_index * ptr_bytes;
                 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });
             } else unreachable;
@@ -6166,8 +6166,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
             .sym_index = sym_index,
         } };
     } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
-        try p9.seeDecl(decl_index);
-        const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
+        const decl_block_index = try p9.seeDecl(decl_index);
+        const decl_block = p9.getDeclBlock(decl_block_index);
+        const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
         return MCValue{ .memory = got_addr };
     } else {
         return self.fail("TODO codegen non-ELF const Decl pointer", .{});
src/arch/arm/CodeGen.zig
@@ -6091,8 +6091,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
     } else if (self.bin_file.cast(link.File.Coff)) |_| {
         return self.fail("TODO codegen COFF const Decl pointer", .{});
     } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
-        try p9.seeDecl(decl_index);
-        const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
+        const decl_block_index = try p9.seeDecl(decl_index);
+        const decl_block = p9.getDeclBlock(decl_block_index);
+        const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
         return MCValue{ .memory = got_addr };
     } else {
         return self.fail("TODO codegen non-ELF const Decl pointer", .{});
src/arch/riscv64/CodeGen.zig
@@ -2558,8 +2558,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
     } else if (self.bin_file.cast(link.File.Coff)) |_| {
         return self.fail("TODO codegen COFF const Decl pointer", .{});
     } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
-        try p9.seeDecl(decl_index);
-        const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
+        const decl_block_index = try p9.seeDecl(decl_index);
+        const decl_block = p9.getDeclBlock(decl_block_index);
+        const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
         return MCValue{ .memory = got_addr };
     } else {
         return self.fail("TODO codegen non-ELF const Decl pointer", .{});
src/arch/x86_64/CodeGen.zig
@@ -3996,7 +3996,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
     if (self.air.value(callee)) |func_value| {
         if (func_value.castTag(.function)) |func_payload| {
             const func = func_payload.data;
-            const fn_owner_decl = mod.declPtr(func.owner_decl);
 
             if (self.bin_file.cast(link.File.Elf)) |elf_file| {
                 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
@@ -4042,11 +4041,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
                     .data = undefined,
                 });
             } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
-                try p9.seeDecl(func.owner_decl);
+                const decl_block_index = try p9.seeDecl(func.owner_decl);
+                const decl_block = p9.getDeclBlock(decl_block_index);
                 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
                 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
                 const got_addr = p9.bases.data;
-                const got_index = fn_owner_decl.link.plan9.got_index.?;
+                const got_index = decl_block.got_index.?;
                 const fn_got_addr = got_addr + got_index * ptr_bytes;
                 _ = try self.addInst(.{
                     .tag = .call,
@@ -6739,8 +6739,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
             .sym_index = sym_index,
         } };
     } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
-        try p9.seeDecl(decl_index);
-        const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
+        const decl_block_index = try p9.seeDecl(decl_index);
+        const decl_block = p9.getDeclBlock(decl_block_index);
+        const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
         return MCValue{ .memory = got_addr };
     } else {
         return self.fail("TODO codegen non-ELF const Decl pointer", .{});
src/link/Plan9.zig
@@ -21,14 +21,7 @@ const Allocator = std.mem.Allocator;
 const log = std.log.scoped(.link);
 const assert = std.debug.assert;
 
-const FnDeclOutput = struct {
-    /// this code is modified when relocated so it is mutable
-    code: []u8,
-    /// this might have to be modified in the linker, so thats why its mutable
-    lineinfo: []u8,
-    start_line: u32,
-    end_line: u32,
-};
+pub const base_tag = .plan9;
 
 base: link.File,
 sixtyfour_bit: bool,
@@ -101,6 +94,9 @@ got_index_free_list: std.ArrayListUnmanaged(usize) = .{},
 
 syms_index_free_list: std.ArrayListUnmanaged(usize) = .{},
 
+decl_blocks: std.ArrayListUnmanaged(DeclBlock) = .{},
+decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
+
 const Reloc = struct {
     target: Module.Decl.Index,
     offset: u64,
@@ -115,6 +111,42 @@ const Bases = struct {
 
 const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(struct { info: DeclBlock, code: []const u8 }));
 
+pub const PtrWidth = enum { p32, p64 };
+
+pub const DeclBlock = struct {
+    type: aout.Sym.Type,
+    /// offset in the text or data sects
+    offset: ?u64,
+    /// offset into syms
+    sym_index: ?usize,
+    /// offset into got
+    got_index: ?usize,
+
+    pub const Index = u32;
+};
+
+const DeclMetadata = struct {
+    index: DeclBlock.Index,
+    exports: std.ArrayListUnmanaged(usize) = .{},
+
+    fn getExport(m: DeclMetadata, p9: *const Plan9, name: []const u8) ?usize {
+        for (m.exports.items) |exp| {
+            const sym = p9.syms.items[exp];
+            if (mem.eql(u8, name, sym.name)) return exp;
+        }
+        return null;
+    }
+};
+
+const FnDeclOutput = struct {
+    /// this code is modified when relocated so it is mutable
+    code: []u8,
+    /// this might have to be modified in the linker, so thats why its mutable
+    lineinfo: []u8,
+    start_line: u32,
+    end_line: u32,
+};
+
 fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {
     return addr + switch (t) {
         .T, .t, .l, .L => self.bases.text,
@@ -127,22 +159,6 @@ fn getSymAddr(self: Plan9, s: aout.Sym) u64 {
     return self.getAddr(s.value, s.type);
 }
 
-pub const DeclBlock = struct {
-    type: aout.Sym.Type,
-    /// offset in the text or data sects
-    offset: ?u64,
-    /// offset into syms
-    sym_index: ?usize,
-    /// offset into got
-    got_index: ?usize,
-    pub const empty = DeclBlock{
-        .type = .t,
-        .offset = null,
-        .sym_index = null,
-        .got_index = null,
-    };
-};
-
 pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases {
     return switch (arch) {
         .x86_64 => .{
@@ -164,8 +180,6 @@ pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases {
     };
 }
 
-pub const PtrWidth = enum { p32, p64 };
-
 pub fn createEmpty(gpa: Allocator, options: link.Options) !*Plan9 {
     if (options.use_llvm)
         return error.LLVMBackendDoesNotSupportPlan9;
@@ -271,7 +285,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
     const decl = module.declPtr(decl_index);
     self.freeUnnamedConsts(decl_index);
 
-    try self.seeDecl(decl_index);
+    _ = try self.seeDecl(decl_index);
     log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
 
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
@@ -313,11 +327,11 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
         .end_line = end_line,
     };
     try self.putFn(decl_index, out);
-    return self.updateFinish(decl);
+    return self.updateFinish(decl_index);
 }
 
 pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {
-    try self.seeDecl(decl_index);
+    _ = try self.seeDecl(decl_index);
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
     defer code_buffer.deinit();
 
@@ -387,7 +401,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)
         }
     }
 
-    try self.seeDecl(decl_index);
+    _ = try self.seeDecl(decl_index);
 
     log.debug("codegen decl {*} ({s}) ({d})", .{ decl, decl.name, decl_index });
 
@@ -414,28 +428,31 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)
     if (self.data_decl_table.fetchPutAssumeCapacity(decl_index, duped_code)) |old_entry| {
         self.base.allocator.free(old_entry.value);
     }
-    return self.updateFinish(decl);
+    return self.updateFinish(decl_index);
 }
 /// called at the end of update{Decl,Func}
-fn updateFinish(self: *Plan9, decl: *Module.Decl) !void {
+fn updateFinish(self: *Plan9, decl_index: Module.Decl.Index) !void {
+    const decl = self.base.options.module.?.declPtr(decl_index);
     const is_fn = (decl.ty.zigTypeTag() == .Fn);
     log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name });
     const sym_t: aout.Sym.Type = if (is_fn) .t else .d;
+
+    const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index);
     // write the internal linker metadata
-    decl.link.plan9.type = sym_t;
+    decl_block.type = sym_t;
     // write the symbol
     // we already have the got index
     const sym: aout.Sym = .{
         .value = undefined, // the value of stuff gets filled in in flushModule
-        .type = decl.link.plan9.type,
+        .type = decl_block.type,
         .name = mem.span(decl.name),
     };
 
-    if (decl.link.plan9.sym_index) |s| {
+    if (decl_block.sym_index) |s| {
         self.syms.items[s] = sym;
     } else {
         const s = try self.allocateSymbolIndex();
-        decl.link.plan9.sym_index = s;
+        decl_block.sym_index = s;
         self.syms.items[s] = sym;
     }
 }
@@ -550,6 +567,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
             while (it.next()) |entry| {
                 const decl_index = entry.key_ptr.*;
                 const decl = mod.declPtr(decl_index);
+                const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index);
                 const out = entry.value_ptr.*;
                 log.debug("write text decl {*} ({s}), lines {d} to {d}", .{ decl, decl.name, out.start_line + 1, out.end_line });
                 {
@@ -568,16 +586,16 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
                 iovecs_i += 1;
                 const off = self.getAddr(text_i, .t);
                 text_i += out.code.len;
-                decl.link.plan9.offset = off;
+                decl_block.offset = off;
                 if (!self.sixtyfour_bit) {
-                    mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off));
-                    mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
+                    mem.writeIntNative(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off));
+                    mem.writeInt(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
                 } else {
-                    mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
+                    mem.writeInt(u64, got_table[decl_block.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
                 }
-                self.syms.items[decl.link.plan9.sym_index.?].value = off;
+                self.syms.items[decl_block.sym_index.?].value = off;
                 if (mod.decl_exports.get(decl_index)) |exports| {
-                    try self.addDeclExports(mod, decl, exports.items);
+                    try self.addDeclExports(mod, decl_index, exports.items);
                 }
             }
         }
@@ -598,6 +616,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
         while (it.next()) |entry| {
             const decl_index = entry.key_ptr.*;
             const decl = mod.declPtr(decl_index);
+            const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index);
             const code = entry.value_ptr.*;
             log.debug("write data decl {*} ({s})", .{ decl, decl.name });
 
@@ -606,15 +625,15 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
             iovecs_i += 1;
             const off = self.getAddr(data_i, .d);
             data_i += code.len;
-            decl.link.plan9.offset = off;
+            decl_block.offset = off;
             if (!self.sixtyfour_bit) {
-                mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
+                mem.writeInt(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
             } else {
-                mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
+                mem.writeInt(u64, got_table[decl_block.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
             }
-            self.syms.items[decl.link.plan9.sym_index.?].value = off;
+            self.syms.items[decl_block.sym_index.?].value = off;
             if (mod.decl_exports.get(decl_index)) |exports| {
-                try self.addDeclExports(mod, decl, exports.items);
+                try self.addDeclExports(mod, decl_index, exports.items);
             }
         }
         // write the unnamed constants after the other data decls
@@ -676,7 +695,8 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
             for (kv.value_ptr.items) |reloc| {
                 const target_decl_index = reloc.target;
                 const target_decl = mod.declPtr(target_decl_index);
-                const target_decl_offset = target_decl.link.plan9.offset.?;
+                const target_decl_block = self.getDeclBlock(self.decls.get(target_decl_index).?.index);
+                const target_decl_offset = target_decl_block.offset.?;
 
                 const offset = reloc.offset;
                 const addend = reloc.addend;
@@ -709,28 +729,36 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
 fn addDeclExports(
     self: *Plan9,
     module: *Module,
-    decl: *Module.Decl,
+    decl_index: Module.Decl.Index,
     exports: []const *Module.Export,
 ) !void {
+    const metadata = self.decls.getPtr(decl_index).?;
+    const decl_block = self.getDeclBlock(metadata.index);
+
     for (exports) |exp| {
         // plan9 does not support custom sections
         if (exp.options.section) |section_name| {
             if (!mem.eql(u8, section_name, ".text") or !mem.eql(u8, section_name, ".data")) {
-                try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "plan9 does not support extra sections", .{}));
+                try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(
+                    self.base.allocator,
+                    module.declPtr(decl_index).srcLoc(),
+                    "plan9 does not support extra sections",
+                    .{},
+                ));
                 break;
             }
         }
         const sym = .{
-            .value = decl.link.plan9.offset.?,
-            .type = decl.link.plan9.type.toGlobal(),
+            .value = decl_block.offset.?,
+            .type = decl_block.type.toGlobal(),
             .name = exp.options.name,
         };
 
-        if (exp.link.plan9) |i| {
+        if (metadata.getExport(self, exp.options.name)) |i| {
             self.syms.items[i] = sym;
         } else {
             try self.syms.append(self.base.allocator, sym);
-            exp.link.plan9 = self.syms.items.len - 1;
+            try metadata.exports.append(self.base.allocator, self.syms.items.len - 1);
         }
     }
 }
@@ -760,13 +788,18 @@ pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void {
             self.base.allocator.free(removed_entry.value);
         }
     }
-    if (decl.link.plan9.got_index) |i| {
-        // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length
-        self.got_index_free_list.append(self.base.allocator, i) catch {};
-    }
-    if (decl.link.plan9.sym_index) |i| {
-        self.syms_index_free_list.append(self.base.allocator, i) catch {};
-        self.syms.items[i] = aout.Sym.undefined_symbol;
+    if (self.decls.fetchRemove(decl_index)) |const_kv| {
+        var kv = const_kv;
+        const decl_block = self.getDeclBlock(kv.value.index);
+        if (decl_block.got_index) |i| {
+            // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length
+            self.got_index_free_list.append(self.base.allocator, i) catch {};
+        }
+        if (decl_block.sym_index) |i| {
+            self.syms_index_free_list.append(self.base.allocator, i) catch {};
+            self.syms.items[i] = aout.Sym.undefined_symbol;
+        }
+        kv.value.exports.deinit(self.base.allocator);
     }
     self.freeUnnamedConsts(decl_index);
     {
@@ -786,12 +819,30 @@ fn freeUnnamedConsts(self: *Plan9, decl_index: Module.Decl.Index) void {
     unnamed_consts.clearAndFree(self.base.allocator);
 }
 
-pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !void {
-    const mod = self.base.options.module.?;
-    const decl = mod.declPtr(decl_index);
-    if (decl.link.plan9.got_index == null) {
-        decl.link.plan9.got_index = self.allocateGotIndex();
+fn createDeclBlock(self: *Plan9) !DeclBlock.Index {
+    const gpa = self.base.allocator;
+    const index = @intCast(DeclBlock.Index, self.decl_blocks.items.len);
+    const decl_block = try self.decl_blocks.addOne(gpa);
+    decl_block.* = .{
+        .type = .t,
+        .offset = null,
+        .sym_index = null,
+        .got_index = null,
+    };
+    return index;
+}
+
+pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !DeclBlock.Index {
+    const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
+    if (!gop.found_existing) {
+        const index = try self.createDeclBlock();
+        self.getDeclBlockPtr(index).got_index = self.allocateGotIndex();
+        gop.value_ptr.* = .{
+            .index = index,
+            .exports = .{},
+        };
     }
+    return gop.value_ptr.index;
 }
 
 pub fn updateDeclExports(
@@ -800,7 +851,7 @@ pub fn updateDeclExports(
     decl_index: Module.Decl.Index,
     exports: []const *Module.Export,
 ) !void {
-    try self.seeDecl(decl_index);
+    _ = try self.seeDecl(decl_index);
     // we do all the things in flush
     _ = module;
     _ = exports;
@@ -842,10 +893,17 @@ pub fn deinit(self: *Plan9) void {
     self.syms_index_free_list.deinit(gpa);
     self.file_segments.deinit(gpa);
     self.path_arena.deinit();
+    self.decl_blocks.deinit(gpa);
+
+    {
+        var it = self.decls.iterator();
+        while (it.next()) |entry| {
+            entry.value_ptr.exports.deinit(gpa);
+        }
+        self.decls.deinit(gpa);
+    }
 }
 
-pub const Export = ?usize;
-pub const base_tag = .plan9;
 pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Plan9 {
     if (options.use_llvm)
         return error.LLVMBackendDoesNotSupportPlan9;
@@ -911,20 +969,19 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
         }
     }
 
-    const mod = self.base.options.module.?;
-
     // write the data symbols
     {
         var it = self.data_decl_table.iterator();
         while (it.next()) |entry| {
             const decl_index = entry.key_ptr.*;
-            const decl = mod.declPtr(decl_index);
-            const sym = self.syms.items[decl.link.plan9.sym_index.?];
+            const decl_metadata = self.decls.get(decl_index).?;
+            const decl_block = self.getDeclBlock(decl_metadata.index);
+            const sym = self.syms.items[decl_block.sym_index.?];
             try self.writeSym(writer, sym);
             if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {
-                for (exports.items) |e| {
-                    try self.writeSym(writer, self.syms.items[e.link.plan9.?]);
-                }
+                for (exports.items) |e| if (decl_metadata.getExport(self, e.options.name)) |exp_i| {
+                    try self.writeSym(writer, self.syms.items[exp_i]);
+                };
             }
         }
     }
@@ -943,16 +1000,17 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
             var submap_it = symidx_and_submap.functions.iterator();
             while (submap_it.next()) |entry| {
                 const decl_index = entry.key_ptr.*;
-                const decl = mod.declPtr(decl_index);
-                const sym = self.syms.items[decl.link.plan9.sym_index.?];
+                const decl_metadata = self.decls.get(decl_index).?;
+                const decl_block = self.getDeclBlock(decl_metadata.index);
+                const sym = self.syms.items[decl_block.sym_index.?];
                 try self.writeSym(writer, sym);
                 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {
-                    for (exports.items) |e| {
-                        const s = self.syms.items[e.link.plan9.?];
+                    for (exports.items) |e| if (decl_metadata.getExport(self, e.options.name)) |exp_i| {
+                        const s = self.syms.items[exp_i];
                         if (mem.eql(u8, s.name, "_start"))
                             self.entry_val = s.value;
                         try self.writeSym(writer, s);
-                    }
+                    };
                 }
             }
         }
@@ -1004,3 +1062,11 @@ pub fn getDeclVAddr(
     });
     return undefined;
 }
+
+pub fn getDeclBlock(self: *const Plan9, index: DeclBlock.Index) DeclBlock {
+    return self.decl_blocks.items[index];
+}
+
+fn getDeclBlockPtr(self: *Plan9, index: DeclBlock.Index) *DeclBlock {
+    return &self.decl_blocks.items[index];
+}
src/link.zig
@@ -265,7 +265,7 @@ pub const File = struct {
         elf: void,
         coff: void,
         macho: void,
-        plan9: Plan9.DeclBlock,
+        plan9: void,
         c: void,
         wasm: Wasm.DeclBlock,
         spirv: void,
@@ -287,7 +287,7 @@ pub const File = struct {
         elf: void,
         coff: void,
         macho: void,
-        plan9: Plan9.Export,
+        plan9: void,
         c: void,
         wasm: Wasm.Export,
         spirv: void,
src/Module.zig
@@ -5277,7 +5277,7 @@ pub fn clearDecl(
                 .coff => .{ .coff = {} },
                 .elf => .{ .elf = {} },
                 .macho => .{ .macho = {} },
-                .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
+                .plan9 => .{ .plan9 = {} },
                 .c => .{ .c = {} },
                 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
                 .spirv => .{ .spirv = {} },
@@ -5697,7 +5697,7 @@ pub fn allocateNewDecl(
             .coff => .{ .coff = {} },
             .elf => .{ .elf = {} },
             .macho => .{ .macho = {} },
-            .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
+            .plan9 => .{ .plan9 = {} },
             .c => .{ .c = {} },
             .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
             .spirv => .{ .spirv = {} },
src/Sema.zig
@@ -5568,7 +5568,7 @@ pub fn analyzeExport(
             .coff => .{ .coff = {} },
             .elf => .{ .elf = {} },
             .macho => .{ .macho = {} },
-            .plan9 => .{ .plan9 = null },
+            .plan9 => .{ .plan9 = {} },
             .c => .{ .c = {} },
             .wasm => .{ .wasm = .{} },
             .spirv => .{ .spirv = {} },