authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-11 00:26:50+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-12 00:32:03+01:00
logdbfcebf8d88b91c5a85366da09a4ce69c0bb5dc6
treee8f255a5277c428a2f598a6352a1d3b04f6c19ea
parentefdb94486b78fa143f8d684609cb4c5d3de10124

macho: allow undefined symbols in dylibs

We now respect both `-fallow-shlib-undefined` and `-Wl,"-undefined=dynamic_lookup"` flags. This is the first step towards solving issues #8180 and #3000. We currently do not expose any other ld64 equivalent flag for `-undefined` flag - we basically throw an error should the user specify a different flag. Support for those is conditional on closing #8180. As a result of this change, it is now possible to generate a valid native Node.js addon with Zig for macOS.

2 files changed, 28 insertions(+), 2 deletions(-)

src/link/MachO.zig+18-2
...@@ -444,6 +444,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -444,6 +444,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
444 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;444 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
445 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;445 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
446 const stack_size = self.base.options.stack_size_override orelse 0;446 const stack_size = self.base.options.stack_size_override orelse 0;
447 const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false);
447448
448 const id_symlink_basename = "zld.id";449 const id_symlink_basename = "zld.id";
449450
...@@ -847,6 +848,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -847,6 +848,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
847 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));848 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
848 }849 }
849850
851 if (allow_undef) {
852 try argv.append("-undefined");
853 try argv.append("dynamic_lookup");
854 }
855
850 Compilation.dump_argv(argv.items);856 Compilation.dump_argv(argv.items);
851 }857 }
852858
...@@ -899,6 +905,16 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -899,6 +905,16 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
899 };905 };
900 _ = self.unresolved.swapRemove(resolv.where_index);906 _ = self.unresolved.swapRemove(resolv.where_index);
901 continue;907 continue;
908 } else if (allow_undef) {
909 const n_desc = @bitCast(
910 u16,
911 macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @intCast(i16, macho.N_SYMBOL_RESOLVER),
912 );
913 // TODO allow_shlib_undefined is an ELF flag so figure out macOS specific flags too.
914 sym.n_type = macho.N_EXT;
915 sym.n_desc = n_desc;
916 _ = self.unresolved.swapRemove(resolv.where_index);
917 continue;
902 }918 }
903919
904 log.err("undefined reference to symbol '{s}'", .{sym_name});920 log.err("undefined reference to symbol '{s}'", .{sym_name});
...@@ -5111,7 +5127,7 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5111,7 +5127,7 @@ fn writeDyldInfoData(self: *MachO) !void {
5111 try bind_pointers.append(.{5127 try bind_pointers.append(.{
5112 .offset = binding.offset + base_offset,5128 .offset = binding.offset + base_offset,
5113 .segment_id = match.seg,5129 .segment_id = match.seg,
5114 .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),5130 .dylib_ordinal = @divExact(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
5115 .name = self.getString(bind_sym.n_strx),5131 .name = self.getString(bind_sym.n_strx),
5116 });5132 });
5117 },5133 },
...@@ -5133,7 +5149,7 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5133,7 +5149,7 @@ fn writeDyldInfoData(self: *MachO) !void {
5133 try lazy_bind_pointers.append(.{5149 try lazy_bind_pointers.append(.{
5134 .offset = binding.offset + base_offset,5150 .offset = binding.offset + base_offset,
5135 .segment_id = match.seg,5151 .segment_id = match.seg,
5136 .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),5152 .dylib_ordinal = @divExact(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
5137 .name = self.getString(bind_sym.n_strx),5153 .name = self.getString(bind_sym.n_strx),
5138 });5154 });
5139 },5155 },
src/main.zig+10
...@@ -1703,6 +1703,16 @@ fn buildOutputType(...@@ -1703,6 +1703,16 @@ fn buildOutputType(
1703 }1703 }
1704 emit_implib = .{ .yes = linker_args.items[i] };1704 emit_implib = .{ .yes = linker_args.items[i] };
1705 emit_implib_arg_provided = true;1705 emit_implib_arg_provided = true;
1706 } else if (mem.eql(u8, arg, "-undefined")) {
1707 i += 1;
1708 if (i >= linker_args.items.len) {
1709 fatal("expected linker arg after '{s}'", .{arg});
1710 }
1711 if (mem.eql(u8, "dynamic_lookup", linker_args.items[i])) {
1712 linker_allow_shlib_undefined = true;
1713 } else {
1714 fatal("unsupported -undefined option '{s}'", .{linker_args.items[i]});
1715 }
1706 } else {1716 } else {
1707 warn("unsupported linker arg: {s}", .{arg});1717 warn("unsupported linker arg: {s}", .{arg});
1708 }1718 }