authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-29 17:47:58+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-29 18:55:58+01:00
loga7a95ce9c4e11e8b3ab887481a7f0e7e8294a87c
tree3b4f7d71604291b606974c61ad53ee7d7da493de
parenteaca72534c6d57acf85b7f20e76fd31302785a5e

macho: implement exporting anon decls


2 files changed, 73 insertions(+), 53 deletions(-)

src/link/MachO.zig+67-51
...@@ -130,7 +130,7 @@ bindings: BindingTable = .{},...@@ -130,7 +130,7 @@ bindings: BindingTable = .{},
130lazy_syms: LazySymbolTable = .{},130lazy_syms: LazySymbolTable = .{},
131131
132/// Table of tracked Decls.132/// Table of tracked Decls.
133decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},133decls: DeclTable = .{},
134134
135/// Table of threadlocal variables descriptors.135/// Table of threadlocal variables descriptors.
136/// They are emitted in the `__thread_vars` section.136/// They are emitted in the `__thread_vars` section.
...@@ -1904,6 +1904,7 @@ pub fn deinit(self: *MachO) void {...@@ -1904,6 +1904,7 @@ pub fn deinit(self: *MachO) void {
1904 m.exports.deinit(gpa);1904 m.exports.deinit(gpa);
1905 }1905 }
1906 self.decls.deinit(gpa);1906 self.decls.deinit(gpa);
1907
1907 self.lazy_syms.deinit(gpa);1908 self.lazy_syms.deinit(gpa);
1908 self.tlv_table.deinit(gpa);1909 self.tlv_table.deinit(gpa);
19091910
...@@ -1911,7 +1912,14 @@ pub fn deinit(self: *MachO) void {...@@ -1911,7 +1912,14 @@ pub fn deinit(self: *MachO) void {
1911 atoms.deinit(gpa);1912 atoms.deinit(gpa);
1912 }1913 }
1913 self.unnamed_const_atoms.deinit(gpa);1914 self.unnamed_const_atoms.deinit(gpa);
1914 self.anon_decls.deinit(gpa);1915
1916 {
1917 var it = self.anon_decls.iterator();
1918 while (it.next()) |entry| {
1919 entry.value_ptr.exports.deinit(gpa);
1920 }
1921 self.anon_decls.deinit(gpa);
1922 }
19151923
1916 self.atom_by_index_table.deinit(gpa);1924 self.atom_by_index_table.deinit(gpa);
19171925
...@@ -2689,18 +2697,30 @@ pub fn updateExports(...@@ -2689,18 +2697,30 @@ pub fn updateExports(
26892697
2690 const gpa = self.base.allocator;2698 const gpa = self.base.allocator;
26912699
2692 const decl_index = switch (exported) {2700 const metadata = switch (exported) {
2693 .decl_index => |i| i,2701 .decl_index => |decl_index| blk: {
2694 .value => |val| {2702 _ = try self.getOrCreateAtomForDecl(decl_index);
2695 _ = val;2703 break :blk self.decls.getPtr(decl_index).?;
2696 @panic("TODO: implement MachO linker code for exporting a constant value");2704 },
2705 .value => |value| self.anon_decls.getPtr(value) orelse blk: {
2706 const first_exp = exports[0];
2707 const res = try self.lowerAnonDecl(value, .none, first_exp.getSrcLoc(mod));
2708 switch (res) {
2709 .ok => {},
2710 .fail => |em| {
2711 // TODO maybe it's enough to return an error here and let Module.processExportsInner
2712 // handle the error?
2713 try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);
2714 mod.failed_exports.putAssumeCapacityNoClobber(first_exp, em);
2715 return;
2716 },
2717 }
2718 break :blk self.anon_decls.getPtr(value).?;
2697 },2719 },
2698 };2720 };
2699 const decl = mod.declPtr(decl_index);2721 const atom_index = metadata.atom;
2700 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2701 const atom = self.getAtom(atom_index);2722 const atom = self.getAtom(atom_index);
2702 const decl_sym = atom.getSymbol(self);2723 const sym = atom.getSymbol(self);
2703 const decl_metadata = self.decls.getPtr(decl_index).?;
27042724
2705 for (exports) |exp| {2725 for (exports) |exp| {
2706 const exp_name = try std.fmt.allocPrint(gpa, "_{}", .{2726 const exp_name = try std.fmt.allocPrint(gpa, "_{}", .{
...@@ -2712,73 +2732,65 @@ pub fn updateExports(...@@ -2712,73 +2732,65 @@ pub fn updateExports(
27122732
2713 if (exp.opts.section.unwrap()) |section_name| {2733 if (exp.opts.section.unwrap()) |section_name| {
2714 if (!mod.intern_pool.stringEqlSlice(section_name, "__text")) {2734 if (!mod.intern_pool.stringEqlSlice(section_name, "__text")) {
2715 try mod.failed_exports.putNoClobber(2735 try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create(
2716 mod.gpa,2736 gpa,
2717 exp,2737 exp.getSrcLoc(mod),
2718 try Module.ErrorMsg.create(2738 "Unimplemented: ExportOptions.section",
2719 gpa,2739 .{},
2720 decl.srcLoc(mod),2740 ));
2721 "Unimplemented: ExportOptions.section",
2722 .{},
2723 ),
2724 );
2725 continue;2741 continue;
2726 }2742 }
2727 }2743 }
27282744
2729 if (exp.opts.linkage == .LinkOnce) {2745 if (exp.opts.linkage == .LinkOnce) {
2730 try mod.failed_exports.putNoClobber(2746 try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create(
2731 mod.gpa,2747 gpa,
2732 exp,2748 exp.getSrcLoc(mod),
2733 try Module.ErrorMsg.create(2749 "Unimplemented: GlobalLinkage.LinkOnce",
2734 gpa,2750 .{},
2735 decl.srcLoc(mod),2751 ));
2736 "Unimplemented: GlobalLinkage.LinkOnce",
2737 .{},
2738 ),
2739 );
2740 continue;2752 continue;
2741 }2753 }
27422754
2743 const sym_index = decl_metadata.getExport(self, exp_name) orelse blk: {2755 const global_sym_index = metadata.getExport(self, exp_name) orelse blk: {
2744 const sym_index = try self.allocateSymbol();2756 const global_sym_index = try self.allocateSymbol();
2745 try decl_metadata.exports.append(gpa, sym_index);2757 try metadata.exports.append(gpa, global_sym_index);
2746 break :blk sym_index;2758 break :blk global_sym_index;
2747 };2759 };
2748 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };2760 const global_sym_loc = SymbolWithLoc{ .sym_index = global_sym_index };
2749 const sym = self.getSymbolPtr(sym_loc);2761 const global_sym = self.getSymbolPtr(global_sym_loc);
2750 sym.* = .{2762 global_sym.* = .{
2751 .n_strx = try self.strtab.insert(gpa, exp_name),2763 .n_strx = try self.strtab.insert(gpa, exp_name),
2752 .n_type = macho.N_SECT | macho.N_EXT,2764 .n_type = macho.N_SECT | macho.N_EXT,
2753 .n_sect = self.text_section_index.? + 1, // TODO what if we export a variable?2765 .n_sect = metadata.section + 1,
2754 .n_desc = 0,2766 .n_desc = 0,
2755 .n_value = decl_sym.n_value,2767 .n_value = sym.n_value,
2756 };2768 };
27572769
2758 switch (exp.opts.linkage) {2770 switch (exp.opts.linkage) {
2759 .Internal => {2771 .Internal => {
2760 // Symbol should be hidden, or in MachO lingo, private extern.2772 // Symbol should be hidden, or in MachO lingo, private extern.
2761 // We should also mark the symbol as Weak: n_desc == N_WEAK_DEF.2773 // We should also mark the symbol as Weak: n_desc == N_WEAK_DEF.
2762 sym.n_type |= macho.N_PEXT;2774 global_sym.n_type |= macho.N_PEXT;
2763 sym.n_desc |= macho.N_WEAK_DEF;2775 global_sym.n_desc |= macho.N_WEAK_DEF;
2764 },2776 },
2765 .Strong => {},2777 .Strong => {},
2766 .Weak => {2778 .Weak => {
2767 // Weak linkage is specified as part of n_desc field.2779 // Weak linkage is specified as part of n_desc field.
2768 // Symbol's n_type is like for a symbol with strong linkage.2780 // Symbol's n_type is like for a symbol with strong linkage.
2769 sym.n_desc |= macho.N_WEAK_DEF;2781 global_sym.n_desc |= macho.N_WEAK_DEF;
2770 },2782 },
2771 else => unreachable,2783 else => unreachable,
2772 }2784 }
27732785
2774 self.resolveGlobalSymbol(sym_loc) catch |err| switch (err) {2786 self.resolveGlobalSymbol(global_sym_loc) catch |err| switch (err) {
2775 error.MultipleSymbolDefinitions => {2787 error.MultipleSymbolDefinitions => {
2776 // TODO: this needs rethinking2788 // TODO: this needs rethinking
2777 const global = self.getGlobal(exp_name).?;2789 const global = self.getGlobal(exp_name).?;
2778 if (sym_loc.sym_index != global.sym_index and global.getFile() != null) {2790 if (global_sym_loc.sym_index != global.sym_index and global.getFile() != null) {
2779 _ = try mod.failed_exports.put(mod.gpa, exp, try Module.ErrorMsg.create(2791 _ = try mod.failed_exports.put(mod.gpa, exp, try Module.ErrorMsg.create(
2780 gpa,2792 gpa,
2781 decl.srcLoc(mod),2793 exp.getSrcLoc(mod),
2782 \\LinkError: symbol '{s}' defined multiple times2794 \\LinkError: symbol '{s}' defined multiple times
2783 ,2795 ,
2784 .{exp_name},2796 .{exp_name},
...@@ -2886,8 +2898,8 @@ pub fn lowerAnonDecl(...@@ -2886,8 +2898,8 @@ pub fn lowerAnonDecl(
2886 .none => ty.abiAlignment(mod),2898 .none => ty.abiAlignment(mod),
2887 else => explicit_alignment,2899 else => explicit_alignment,
2888 };2900 };
2889 if (self.anon_decls.get(decl_val)) |atom_index| {2901 if (self.anon_decls.get(decl_val)) |metadata| {
2890 const existing_addr = self.getAtom(atom_index).getSymbol(self).n_value;2902 const existing_addr = self.getAtom(metadata.atom).getSymbol(self).n_value;
2891 if (decl_alignment.check(existing_addr))2903 if (decl_alignment.check(existing_addr))
2892 return .ok;2904 return .ok;
2893 }2905 }
...@@ -2917,14 +2929,17 @@ pub fn lowerAnonDecl(...@@ -2917,14 +2929,17 @@ pub fn lowerAnonDecl(
2917 .ok => |atom_index| atom_index,2929 .ok => |atom_index| atom_index,
2918 .fail => |em| return .{ .fail = em },2930 .fail => |em| return .{ .fail = em },
2919 };2931 };
2920 try self.anon_decls.put(gpa, decl_val, atom_index);2932 try self.anon_decls.put(gpa, decl_val, .{
2933 .atom = atom_index,
2934 .section = self.data_const_section_index.?,
2935 });
2921 return .ok;2936 return .ok;
2922}2937}
29232938
2924pub fn getAnonDeclVAddr(self: *MachO, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {2939pub fn getAnonDeclVAddr(self: *MachO, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
2925 assert(self.llvm_object == null);2940 assert(self.llvm_object == null);
29262941
2927 const this_atom_index = self.anon_decls.get(decl_val).?;2942 const this_atom_index = self.anon_decls.get(decl_val).?.atom;
2928 const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?;2943 const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?;
2929 const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index }).?;2944 const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index }).?;
2930 try Atom.addRelocation(self, atom_index, .{2945 try Atom.addRelocation(self, atom_index, .{
...@@ -5489,7 +5504,8 @@ const DeclMetadata = struct {...@@ -5489,7 +5504,8 @@ const DeclMetadata = struct {
5489 }5504 }
5490};5505};
54915506
5492const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, Atom.Index);5507const DeclTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata);
5508const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata);
5493const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding));5509const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding));
5494const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));5510const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
5495const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));5511const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));
test/behavior/export_builtin.zig+6-2
...@@ -54,7 +54,9 @@ test "exporting using field access" {...@@ -54,7 +54,9 @@ test "exporting using field access" {
54test "exporting comptime-known value" {54test "exporting comptime-known value" {
55 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;55 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
56 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;56 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
57 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;57 if (builtin.zig_backend == .stage2_x86_64 and
58 (builtin.target.ofmt != .elf and
59 builtin.target.ofmt != .macho)) return error.SkipZigTest;
58 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;60 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
59 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;61 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
60 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;62 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
...@@ -70,7 +72,9 @@ test "exporting comptime-known value" {...@@ -70,7 +72,9 @@ test "exporting comptime-known value" {
70test "exporting comptime var" {72test "exporting comptime var" {
71 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;73 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
72 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;74 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
73 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;75 if (builtin.zig_backend == .stage2_x86_64 and
76 (builtin.target.ofmt != .elf and
77 builtin.target.ofmt != .macho)) return error.SkipZigTest;
74 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;78 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
75 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;79 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
76 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;80 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;