authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-06-28 21:06:12+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-06-28 21:06:12+02:00
log2c385e58f96ea080bd6c732de422f84c6c38c2a2
tree34bd5fd2b65f02be3e822dd220557d0c36c1e1a5
parent6d47b4f39e8cdd95ead71b1efdbf67a737174e97
parent9b5a463111534a892177f731397cbab1f586e437
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9242 from ziglang/zld-link-system-libsystem-when-native

zld: link against system libSystem when available

4 files changed, 271 insertions(+), 258 deletions(-)

src/link/MachO.zig+160-120
...@@ -514,117 +514,83 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -514,117 +514,83 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
514 }514 }
515}515}
516516
517fn resolvePaths(517fn resolveSearchDir(
518 arena: *Allocator,518 arena: *Allocator,
519 resolved_paths: *std.ArrayList([]const u8),519 dir: []const u8,
520 syslibroot: ?[]const u8,520 syslibroot: ?[]const u8,
521 search_dirs: []const []const u8,521) !?[]const u8 {
522 lib_names: []const []const u8,522 var candidates = std.ArrayList([]const u8).init(arena);
523 kind: enum { lib, framework },
524) !void {
525 var resolved_dirs = std.ArrayList([]const u8).init(arena);
526 for (search_dirs) |dir| {
527 if (fs.path.isAbsolute(dir)) {
528 var candidates = std.ArrayList([]const u8).init(arena);
529 if (syslibroot) |root| {
530 const full_path = try fs.path.join(arena, &[_][]const u8{ root, dir });
531 try candidates.append(full_path);
532 }
533 try candidates.append(dir);
534
535 var found = false;
536 for (candidates.items) |candidate| {
537 // Verify that search path actually exists
538 var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) {
539 error.FileNotFound => continue,
540 else => |e| return e,
541 };
542 defer tmp.close();
543523
544 try resolved_dirs.append(candidate);524 if (fs.path.isAbsolute(dir)) {
545 found = true;525 if (syslibroot) |root| {
546 break;526 const full_path = try fs.path.join(arena, &[_][]const u8{ root, dir });
547 }527 try candidates.append(full_path);
528 }
529 }
548530
549 if (!found) {531 try candidates.append(dir);
550 switch (kind) {
551 .lib => log.warn("directory not found for '-L{s}'", .{dir}),
552 .framework => log.warn("directory not found for '-F{s}'", .{dir}),
553 }
554 }
555 } else {
556 // Verify that search path actually exists
557 var tmp = fs.cwd().openDir(dir, .{}) catch |err| switch (err) {
558 error.FileNotFound => {
559 switch (kind) {
560 .lib => log.warn("directory not found for '-L{s}'", .{dir}),
561 .framework => log.warn("directory not found for '-F{s}'", .{dir}),
562 }
563 continue;
564 },
565 else => |e| return e,
566 };
567 defer tmp.close();
568532
569 try resolved_dirs.append(dir);533 for (candidates.items) |candidate| {
570 }534 // Verify that search path actually exists
535 var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) {
536 error.FileNotFound => continue,
537 else => |e| return e,
538 };
539 defer tmp.close();
540
541 return candidate;
571 }542 }
572543
573 // Assume ld64 default: -search_paths_first544 return null;
574 // Look in each directory for a dylib (next, tbd), and then for archive545}
575 // TODO implement alternative: -search_dylibs_first
576 const exts = switch (kind) {
577 .lib => &[_][]const u8{ "dylib", "tbd", "a" },
578 .framework => &[_][]const u8{ "dylib", "tbd" },
579 };
580546
581 for (lib_names) |lib_name| {547fn resolveLib(
582 var found = false;548 arena: *Allocator,
583549 search_dirs: []const []const u8,
584 ext: for (exts) |ext| {550 name: []const u8,
585 const lib_name_ext = blk: {551 ext: []const u8,
586 switch (kind) {552) !?[]const u8 {
587 .lib => break :blk try std.fmt.allocPrint(arena, "lib{s}.{s}", .{ lib_name, ext }),553 const search_name = try std.fmt.allocPrint(arena, "lib{s}{s}", .{ name, ext });
588 .framework => {
589 const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{lib_name});
590 const nn = try std.fmt.allocPrint(arena, "{s}.{s}", .{ lib_name, ext });
591 break :blk try fs.path.join(arena, &[_][]const u8{ prefix, nn });
592 },
593 }
594 };
595554
596 for (resolved_dirs.items) |dir| {555 for (search_dirs) |dir| {
597 const full_path = try fs.path.join(arena, &[_][]const u8{ dir, lib_name_ext });556 const full_path = try fs.path.join(arena, &[_][]const u8{ dir, search_name });
598557
599 // Check if the lib file exists.558 // Check if the file exists.
600 const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {559 const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
601 error.FileNotFound => continue,560 error.FileNotFound => continue,
602 else => |e| return e,561 else => |e| return e,
603 };562 };
604 defer tmp.close();563 defer tmp.close();
605564
606 try resolved_paths.append(full_path);565 return full_path;
607 found = true;566 }
608 break :ext;
609 }
610 }
611567
612 if (!found) {568 return null;
613 switch (kind) {569}
614 .lib => {570
615 log.warn("library not found for '-l{s}'", .{lib_name});571fn resolveFramework(
616 log.warn("Library search paths:", .{});572 arena: *Allocator,
617 },573 search_dirs: []const []const u8,
618 .framework => {574 name: []const u8,
619 log.warn("framework not found for '-f{s}'", .{lib_name});575 ext: []const u8,
620 log.warn("Framework search paths:", .{});576) !?[]const u8 {
621 },577 const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext });
622 }578 const prefix_path = try std.fmt.allocPrint(arena, "{s}.framework", .{name});
623 for (resolved_dirs.items) |dir| {579
624 log.warn(" {s}", .{dir});580 for (search_dirs) |dir| {
625 }581 const full_path = try fs.path.join(arena, &[_][]const u8{ dir, prefix_path, search_name });
626 }582
583 // Check if the file exists.
584 const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
585 error.FileNotFound => continue,
586 else => |e| return e,
587 };
588 defer tmp.close();
589
590 return full_path;
627 }591 }
592
593 return null;
628}594}
629595
630fn linkWithLLD(self: *MachO, comp: *Compilation) !void {596fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
...@@ -789,7 +755,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -789,7 +755,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
789 zld.deinit();755 zld.deinit();
790 }756 }
791 zld.arch = target.cpu.arch;757 zld.arch = target.cpu.arch;
792 zld.syslibroot = self.base.options.sysroot;
793 zld.stack_size = stack_size;758 zld.stack_size = stack_size;
794759
795 // Positional arguments to the linker such as object files and static archives.760 // Positional arguments to the linker such as object files and static archives.
...@@ -829,15 +794,97 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -829,15 +794,97 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
829 try search_lib_names.append(link_lib);794 try search_lib_names.append(link_lib);
830 }795 }
831796
797 var lib_dirs = std.ArrayList([]const u8).init(arena);
798 for (self.base.options.lib_dirs) |dir| {
799 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
800 try lib_dirs.append(search_dir);
801 } else {
802 log.warn("directory not found for '-L{s}'", .{dir});
803 }
804 }
805
832 var libs = std.ArrayList([]const u8).init(arena);806 var libs = std.ArrayList([]const u8).init(arena);
833 try resolvePaths(807 var lib_not_found = false;
834 arena,808 for (search_lib_names.items) |lib_name| {
835 &libs,809 // Assume ld64 default: -search_paths_first
836 self.base.options.sysroot,810 // Look in each directory for a dylib (stub first), and then for archive
837 self.base.options.lib_dirs,811 // TODO implement alternative: -search_dylibs_first
838 search_lib_names.items,812 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
839 .lib,813 if (try resolveLib(arena, lib_dirs.items, lib_name, ext)) |full_path| {
840 );814 try libs.append(full_path);
815 break;
816 }
817 } else {
818 log.warn("library not found for '-l{s}'", .{lib_name});
819 lib_not_found = true;
820 }
821 }
822
823 if (lib_not_found) {
824 log.warn("Library search paths:", .{});
825 for (lib_dirs.items) |dir| {
826 log.warn(" {s}", .{dir});
827 }
828 }
829
830 // If we're compiling native and we can find libSystem.B.{dylib, tbd},
831 // we link against that instead of embedded libSystem.B.tbd file.
832 var native_libsystem_available = false;
833 if (self.base.options.is_native_os) blk: {
834 // Try stub file first. If we hit it, then we're done as the stub file
835 // re-exports every single symbol definition.
836 if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| {
837 try libs.append(full_path);
838 native_libsystem_available = true;
839 break :blk;
840 }
841 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
842 // doesn't export libc.dylib which we'll need to resolve subsequently also.
843 if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| {
844 if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| {
845 try libs.append(libsystem_path);
846 try libs.append(libc_path);
847 native_libsystem_available = true;
848 break :blk;
849 }
850 }
851 }
852 if (!native_libsystem_available) {
853 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
854 "libc", "darwin", "libSystem.B.tbd",
855 });
856 try libs.append(full_path);
857 }
858
859 // frameworks
860 var framework_dirs = std.ArrayList([]const u8).init(arena);
861 for (self.base.options.framework_dirs) |dir| {
862 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
863 try framework_dirs.append(search_dir);
864 } else {
865 log.warn("directory not found for '-F{s}'", .{dir});
866 }
867 }
868
869 var framework_not_found = false;
870 for (self.base.options.frameworks) |framework| {
871 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
872 if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| {
873 try libs.append(full_path);
874 break;
875 }
876 } else {
877 log.warn("framework not found for '-f{s}'", .{framework});
878 framework_not_found = true;
879 }
880 }
881
882 if (framework_not_found) {
883 log.warn("Framework search paths:", .{});
884 for (framework_dirs.items) |dir| {
885 log.warn(" {s}", .{dir});
886 }
887 }
841888
842 // rpaths889 // rpaths
843 var rpath_table = std.StringArrayHashMap(void).init(arena);890 var rpath_table = std.StringArrayHashMap(void).init(arena);
...@@ -852,16 +899,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -852,16 +899,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
852 rpaths.appendAssumeCapacity(key.*);899 rpaths.appendAssumeCapacity(key.*);
853 }900 }
854901
855 // frameworks
856 try resolvePaths(
857 arena,
858 &libs,
859 self.base.options.sysroot,
860 self.base.options.framework_dirs,
861 self.base.options.frameworks,
862 .framework,
863 );
864
865 if (self.base.options.verbose_link) {902 if (self.base.options.verbose_link) {
866 var argv = std.ArrayList([]const u8).init(arena);903 var argv = std.ArrayList([]const u8).init(arena);
867904
...@@ -883,6 +920,11 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -883,6 +920,11 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
883 try argv.append("-o");920 try argv.append("-o");
884 try argv.append(full_out_path);921 try argv.append(full_out_path);
885922
923 if (native_libsystem_available) {
924 try argv.append("-lSystem");
925 try argv.append("-lc");
926 }
927
886 for (search_lib_names.items) |l_name| {928 for (search_lib_names.items) |l_name| {
887 try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));929 try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));
888 }930 }
...@@ -895,11 +937,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -895,11 +937,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
895 }937 }
896938
897 try zld.link(positionals.items, full_out_path, .{939 try zld.link(positionals.items, full_out_path, .{
940 .syslibroot = self.base.options.sysroot,
898 .libs = libs.items,941 .libs = libs.items,
899 .rpaths = rpaths.items,942 .rpaths = rpaths.items,
900 .libc_stub_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
901 "libc", "darwin", "libSystem.B.tbd",
902 }),
903 });943 });
904944
905 break :outer;945 break :outer;
src/link/MachO/Dylib.zig+82-51
...@@ -45,8 +45,8 @@ id: ?Id = null,...@@ -45,8 +45,8 @@ id: ?Id = null,
45/// a symbol is referenced by an object file.45/// a symbol is referenced by an object file.
46symbols: std.StringArrayHashMapUnmanaged(void) = .{},46symbols: std.StringArrayHashMapUnmanaged(void) = .{},
4747
48// TODO add parsing re-exported libs from binary dylibs48/// Array list of all dependent libs of this dylib.
49dependent_libs: std.StringArrayHashMapUnmanaged(void) = .{},49dependent_libs: std.ArrayListUnmanaged(Id) = .{},
5050
51pub const Id = struct {51pub const Id = struct {
52 name: []const u8,52 name: []const u8,
...@@ -54,15 +54,28 @@ pub const Id = struct {...@@ -54,15 +54,28 @@ pub const Id = struct {
54 current_version: u32,54 current_version: u32,
55 compatibility_version: u32,55 compatibility_version: u32,
5656
57 pub fn default(name: []const u8) Id {57 pub fn default(allocator: *Allocator, name: []const u8) !Id {
58 return .{58 return Id{
59 .name = name,59 .name = try allocator.dupe(u8, name),
60 .timestamp = 2,60 .timestamp = 2,
61 .current_version = 0x10000,61 .current_version = 0x10000,
62 .compatibility_version = 0x10000,62 .compatibility_version = 0x10000,
63 };63 };
64 }64 }
6565
66 pub fn fromLoadCommand(allocator: *Allocator, lc: GenericCommandWithData(macho.dylib_command)) !Id {
67 const dylib = lc.inner.dylib;
68 const dylib_name = @ptrCast([*:0]const u8, lc.data[dylib.name - @sizeOf(macho.dylib_command) ..]);
69 const name = try allocator.dupe(u8, mem.spanZ(dylib_name));
70
71 return Id{
72 .name = name,
73 .timestamp = dylib.timestamp,
74 .current_version = dylib.current_version,
75 .compatibility_version = dylib.compatibility_version,
76 };
77 }
78
66 pub fn deinit(id: *Id, allocator: *Allocator) void {79 pub fn deinit(id: *Id, allocator: *Allocator) void {
67 allocator.free(id.name);80 allocator.free(id.name);
68 }81 }
...@@ -129,12 +142,12 @@ pub const Error = error{...@@ -129,12 +142,12 @@ pub const Error = error{
129 UnsupportedCpuArchitecture,142 UnsupportedCpuArchitecture,
130} || fs.File.OpenError || std.os.PReadError || Id.ParseError;143} || fs.File.OpenError || std.os.PReadError || Id.ParseError;
131144
132pub fn createAndParseFromPath(145pub const CreateOpts = struct {
133 allocator: *Allocator,146 syslibroot: ?[]const u8 = null,
134 arch: Arch,147 id: ?Id = null,
135 path: []const u8,148};
136 syslibroot: ?[]const u8,149
137) Error!?[]*Dylib {150pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8, opts: CreateOpts) Error!?[]*Dylib {
138 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {151 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
139 error.FileNotFound => return null,152 error.FileNotFound => return null,
140 else => |e| return e,153 else => |e| return e,
...@@ -152,7 +165,7 @@ pub fn createAndParseFromPath(...@@ -152,7 +165,7 @@ pub fn createAndParseFromPath(
152 .arch = arch,165 .arch = arch,
153 .name = name,166 .name = name,
154 .file = file,167 .file = file,
155 .syslibroot = syslibroot,168 .syslibroot = opts.syslibroot,
156 };169 };
157170
158 dylib.parse() catch |err| switch (err) {171 dylib.parse() catch |err| switch (err) {
...@@ -171,6 +184,20 @@ pub fn createAndParseFromPath(...@@ -171,6 +184,20 @@ pub fn createAndParseFromPath(
171 else => |e| return e,184 else => |e| return e,
172 };185 };
173186
187 if (opts.id) |id| {
188 if (dylib.id.?.current_version < id.compatibility_version) {
189 log.warn("found dylib is incompatible with the required minimum version", .{});
190 log.warn(" | dylib: {s}", .{id.name});
191 log.warn(" | required minimum version: {}", .{id.compatibility_version});
192 log.warn(" | dylib version: {}", .{dylib.id.?.current_version});
193
194 // TODO maybe this should be an error and facilitate auto-cleanup?
195 dylib.deinit();
196 allocator.destroy(dylib);
197 return null;
198 }
199 }
200
174 var dylibs = std.ArrayList(*Dylib).init(allocator);201 var dylibs = std.ArrayList(*Dylib).init(allocator);
175 defer dylibs.deinit();202 defer dylibs.deinit();
176203
...@@ -191,8 +218,8 @@ pub fn deinit(self: *Dylib) void {...@@ -191,8 +218,8 @@ pub fn deinit(self: *Dylib) void {
191 }218 }
192 self.symbols.deinit(self.allocator);219 self.symbols.deinit(self.allocator);
193220
194 for (self.dependent_libs.keys()) |key| {221 for (self.dependent_libs.items) |*id| {
195 self.allocator.free(key);222 id.deinit(self.allocator);
196 }223 }
197 self.dependent_libs.deinit(self.allocator);224 self.dependent_libs.deinit(self.allocator);
198225
...@@ -287,6 +314,8 @@ fn readFatStruct(reader: anytype, comptime T: type) !T {...@@ -287,6 +314,8 @@ fn readFatStruct(reader: anytype, comptime T: type) !T {
287}314}
288315
289fn readLoadCommands(self: *Dylib, reader: anytype) !void {316fn readLoadCommands(self: *Dylib, reader: anytype) !void {
317 const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
318
290 try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds);319 try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds);
291320
292 var i: u16 = 0;321 var i: u16 = 0;
...@@ -302,6 +331,13 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void {...@@ -302,6 +331,13 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void {
302 macho.LC_ID_DYLIB => {331 macho.LC_ID_DYLIB => {
303 self.id_cmd_index = i;332 self.id_cmd_index = i;
304 },333 },
334 macho.LC_REEXPORT_DYLIB => {
335 if (should_lookup_reexports) {
336 // Parse install_name to dependent dylib.
337 const id = try Id.fromLoadCommand(self.allocator, cmd.Dylib);
338 try self.dependent_libs.append(self.allocator, id);
339 }
340 },
305 else => {341 else => {
306 log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});342 log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
307 },343 },
...@@ -313,22 +349,10 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void {...@@ -313,22 +349,10 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void {
313fn parseId(self: *Dylib) !void {349fn parseId(self: *Dylib) !void {
314 const index = self.id_cmd_index orelse {350 const index = self.id_cmd_index orelse {
315 log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{});351 log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{});
316 self.id = Id.default(try self.allocator.dupe(u8, self.name.?));352 self.id = try Id.default(self.allocator, self.name.?);
317 return;353 return;
318 };354 };
319 const id_cmd = self.load_commands.items[index].Dylib;355 self.id = try Id.fromLoadCommand(self.allocator, self.load_commands.items[index].Dylib);
320 const dylib = id_cmd.inner.dylib;
321
322 // TODO should we compare the name from the dylib's id with the user-specified one?
323 const dylib_name = @ptrCast([*:0]const u8, id_cmd.data[dylib.name - @sizeOf(macho.dylib_command) ..]);
324 const name = try self.allocator.dupe(u8, mem.spanZ(dylib_name));
325
326 self.id = .{
327 .name = name,
328 .timestamp = dylib.timestamp,
329 .current_version = dylib.current_version,
330 .compatibility_version = dylib.compatibility_version,
331 };
332}356}
333357
334fn parseSymbols(self: *Dylib) !void {358fn parseSymbols(self: *Dylib) !void {
...@@ -345,10 +369,11 @@ fn parseSymbols(self: *Dylib) !void {...@@ -345,10 +369,11 @@ fn parseSymbols(self: *Dylib) !void {
345 _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff + self.library_offset);369 _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff + self.library_offset);
346370
347 for (slice) |sym| {371 for (slice) |sym| {
348 const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx));372 const add_to_symtab = Symbol.isExt(sym) and (Symbol.isSect(sym) or Symbol.isIndr(sym));
349373
350 if (!(Symbol.isSect(sym) and Symbol.isExt(sym))) continue;374 if (!add_to_symtab) continue;
351375
376 const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx));
352 const name = try self.allocator.dupe(u8, sym_name);377 const name = try self.allocator.dupe(u8, sym_name);
353 try self.symbols.putNoClobber(self.allocator, name, {});378 try self.symbols.putNoClobber(self.allocator, name, {});
354 }379 }
...@@ -380,7 +405,7 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {...@@ -380,7 +405,7 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {
380405
381 const umbrella_lib = lib_stub.inner[0];406 const umbrella_lib = lib_stub.inner[0];
382407
383 var id = Id.default(try self.allocator.dupe(u8, umbrella_lib.install_name));408 var id = try Id.default(self.allocator, umbrella_lib.install_name);
384 if (umbrella_lib.current_version) |version| {409 if (umbrella_lib.current_version) |version| {
385 try id.parseCurrentVersion(version);410 try id.parseCurrentVersion(version);
386 }411 }
...@@ -470,7 +495,9 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {...@@ -470,7 +495,9 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {
470 }495 }
471496
472 log.debug(" | {s}", .{lib});497 log.debug(" | {s}", .{lib});
473 try self.dependent_libs.put(self.allocator, try self.allocator.dupe(u8, lib), {});498
499 const dep_id = try Id.default(self.allocator, lib);
500 try self.dependent_libs.append(self.allocator, dep_id);
474 }501 }
475 }502 }
476 }503 }
...@@ -478,36 +505,40 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {...@@ -478,36 +505,40 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {
478}505}
479506
480pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void {507pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void {
481 outer: for (self.dependent_libs.keys()) |lib| {508 outer: for (self.dependent_libs.items) |id| {
482 const dirname = fs.path.dirname(lib) orelse {509 const has_ext = blk: {
483 log.warn("unable to resolve dependency {s}", .{lib});510 const basename = fs.path.basename(id.name);
484 continue;511 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
485 };512 };
486 const filename = fs.path.basename(lib);513 const extension = if (has_ext) fs.path.extension(id.name) else "";
487 const without_ext = if (mem.lastIndexOfScalar(u8, filename, '.')) |index|514 const without_ext = if (has_ext) blk: {
488 filename[0..index]515 const index = mem.lastIndexOfScalar(u8, id.name, '.') orelse unreachable;
489 else516 break :blk id.name[0..index];
490 filename;517 } else id.name;
491518
492 for (&[_][]const u8{ "dylib", "tbd" }) |ext| {519 for (&[_][]const u8{ extension, ".tbd" }) |ext| {
493 const with_ext = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{520 const with_ext = try std.fmt.allocPrint(self.allocator, "{s}{s}", .{
494 without_ext,521 without_ext,
495 ext,522 ext,
496 });523 });
497 defer self.allocator.free(with_ext);524 defer self.allocator.free(with_ext);
498525
499 const lib_path = if (self.syslibroot) |syslibroot|526 const full_path = if (self.syslibroot) |syslibroot|
500 try fs.path.join(self.allocator, &.{ syslibroot, dirname, with_ext })527 try fs.path.join(self.allocator, &.{ syslibroot, with_ext })
501 else528 else
502 try fs.path.join(self.allocator, &.{ dirname, with_ext });529 with_ext;
530 defer if (self.syslibroot) |_| self.allocator.free(full_path);
503531
504 log.debug("trying dependency at fully resolved path {s}", .{lib_path});532 log.debug("trying dependency at fully resolved path {s}", .{full_path});
505533
506 const dylibs = (try createAndParseFromPath(534 const dylibs = (try createAndParseFromPath(
507 self.allocator,535 self.allocator,
508 self.arch.?,536 self.arch.?,
509 lib_path,537 full_path,
510 self.syslibroot,538 .{
539 .id = id,
540 .syslibroot = self.syslibroot,
541 },
511 )) orelse {542 )) orelse {
512 continue;543 continue;
513 };544 };
...@@ -516,7 +547,7 @@ pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void {...@@ -516,7 +547,7 @@ pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void {
516547
517 continue :outer;548 continue :outer;
518 } else {549 } else {
519 log.warn("unable to resolve dependency {s}", .{lib});550 log.warn("unable to resolve dependency {s}", .{id.name});
520 }551 }
521 }552 }
522}553}
src/link/MachO/Symbol.zig+2-1
...@@ -111,7 +111,8 @@ pub const Unresolved = struct {...@@ -111,7 +111,8 @@ pub const Unresolved = struct {
111 base: Symbol,111 base: Symbol,
112112
113 /// File where this symbol was referenced.113 /// File where this symbol was referenced.
114 file: *Object,114 /// null means synthetic, e.g., dyld_stub_binder.
115 file: ?*Object = null,
115116
116 pub const base_type: Symbol.Type = .unresolved;117 pub const base_type: Symbol.Type = .unresolved;
117};118};
src/link/MachO/Zld.zig+27-86
...@@ -32,14 +32,12 @@ out_path: ?[]const u8 = null,...@@ -32,14 +32,12 @@ out_path: ?[]const u8 = null,
3232
33// TODO these args will become obselete once Zld is coalesced with incremental33// TODO these args will become obselete once Zld is coalesced with incremental
34// linker.34// linker.
35syslibroot: ?[]const u8 = null,
36stack_size: u64 = 0,35stack_size: u64 = 0,
3736
38objects: std.ArrayListUnmanaged(*Object) = .{},37objects: std.ArrayListUnmanaged(*Object) = .{},
39archives: std.ArrayListUnmanaged(*Archive) = .{},38archives: std.ArrayListUnmanaged(*Archive) = .{},
40dylibs: std.ArrayListUnmanaged(*Dylib) = .{},39dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
4140
42libsystem_dylib_index: ?u16 = null,
43next_dylib_ordinal: u16 = 1,41next_dylib_ordinal: u16 = 1,
4442
45load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},43load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
...@@ -197,9 +195,9 @@ pub fn closeFiles(self: Zld) void {...@@ -197,9 +195,9 @@ pub fn closeFiles(self: Zld) void {
197}195}
198196
199const LinkArgs = struct {197const LinkArgs = struct {
198 syslibroot: ?[]const u8,
200 libs: []const []const u8,199 libs: []const []const u8,
201 rpaths: []const []const u8,200 rpaths: []const []const u8,
202 libc_stub_path: []const u8,
203};201};
204202
205pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void {203pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void {
...@@ -238,9 +236,8 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L...@@ -238,9 +236,8 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L
238 });236 });
239237
240 try self.populateMetadata();238 try self.populateMetadata();
241 try self.parseInputFiles(files);239 try self.parseInputFiles(files, args.syslibroot);
242 try self.parseLibs(args.libs);240 try self.parseLibs(args.libs, args.syslibroot);
243 try self.parseLibSystem(args.libc_stub_path);
244 try self.resolveSymbols();241 try self.resolveSymbols();
245 try self.resolveStubsAndGotEntries();242 try self.resolveStubsAndGotEntries();
246 try self.updateMetadata();243 try self.updateMetadata();
...@@ -258,7 +255,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L...@@ -258,7 +255,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L
258 try self.flush();255 try self.flush();
259}256}
260257
261fn parseInputFiles(self: *Zld, files: []const []const u8) !void {258fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u8) !void {
262 for (files) |file_name| {259 for (files) |file_name| {
263 const full_path = full_path: {260 const full_path = full_path: {
264 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;261 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
...@@ -280,7 +277,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -280,7 +277,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
280 self.allocator,277 self.allocator,
281 self.arch.?,278 self.arch.?,
282 full_path,279 full_path,
283 self.syslibroot,280 .{ .syslibroot = syslibroot },
284 )) |dylibs| {281 )) |dylibs| {
285 defer self.allocator.free(dylibs);282 defer self.allocator.free(dylibs);
286 try self.dylibs.appendSlice(self.allocator, dylibs);283 try self.dylibs.appendSlice(self.allocator, dylibs);
...@@ -291,13 +288,13 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -291,13 +288,13 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
291 }288 }
292}289}
293290
294fn parseLibs(self: *Zld, libs: []const []const u8) !void {291fn parseLibs(self: *Zld, libs: []const []const u8, syslibroot: ?[]const u8) !void {
295 for (libs) |lib| {292 for (libs) |lib| {
296 if (try Dylib.createAndParseFromPath(293 if (try Dylib.createAndParseFromPath(
297 self.allocator,294 self.allocator,
298 self.arch.?,295 self.arch.?,
299 lib,296 lib,
300 self.syslibroot,297 .{ .syslibroot = syslibroot },
301 )) |dylibs| {298 )) |dylibs| {
302 defer self.allocator.free(dylibs);299 defer self.allocator.free(dylibs);
303 try self.dylibs.appendSlice(self.allocator, dylibs);300 try self.dylibs.appendSlice(self.allocator, dylibs);
...@@ -313,36 +310,6 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {...@@ -313,36 +310,6 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
313 }310 }
314}311}
315312
316fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void {
317 const dylibs = (try Dylib.createAndParseFromPath(
318 self.allocator,
319 self.arch.?,
320 libc_stub_path,
321 self.syslibroot,
322 )) orelse return error.FailedToParseLibSystem;
323 defer self.allocator.free(dylibs);
324
325 assert(dylibs.len == 1); // More than one dylib output from parsing libSystem!
326 const dylib = dylibs[0];
327
328 self.libsystem_dylib_index = @intCast(u16, self.dylibs.items.len);
329 try self.dylibs.append(self.allocator, dylib);
330
331 // Add LC_LOAD_DYLIB load command.
332 dylib.ordinal = self.next_dylib_ordinal;
333 const dylib_id = dylib.id orelse unreachable;
334 var dylib_cmd = try createLoadDylibCommand(
335 self.allocator,
336 dylib_id.name,
337 dylib_id.timestamp,
338 dylib_id.current_version,
339 dylib_id.compatibility_version,
340 );
341 errdefer dylib_cmd.deinit(self.allocator);
342 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
343 self.next_dylib_ordinal += 1;
344}
345
346fn mapAndUpdateSections(313fn mapAndUpdateSections(
347 self: *Zld,314 self: *Zld,
348 object: *Object,315 object: *Object,
...@@ -1656,17 +1623,30 @@ fn resolveSymbols(self: *Zld) !void {...@@ -1656,17 +1623,30 @@ fn resolveSymbols(self: *Zld) !void {
1656 }1623 }
1657 self.unresolved.clearRetainingCapacity();1624 self.unresolved.clearRetainingCapacity();
16581625
1626 // Put dyld_stub_binder as an unresolved special symbol.
1627 {
1628 const name = try self.allocator.dupe(u8, "dyld_stub_binder");
1629 errdefer self.allocator.free(name);
1630 const undef = try self.allocator.create(Symbol.Unresolved);
1631 errdefer self.allocator.destroy(undef);
1632 undef.* = .{
1633 .base = .{
1634 .@"type" = .unresolved,
1635 .name = name,
1636 },
1637 };
1638 try unresolved.append(&undef.base);
1639 }
1640
1659 var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator);1641 var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator);
1660 defer referenced.deinit();1642 defer referenced.deinit();
16611643
1662 loop: while (unresolved.popOrNull()) |undef| {1644 loop: while (unresolved.popOrNull()) |undef| {
1663 const proxy = self.imports.get(undef.name) orelse outer: {1645 const proxy = self.imports.get(undef.name) orelse outer: {
1664 const proxy = inner: {1646 const proxy = inner: {
1665 for (self.dylibs.items) |dylib, i| {1647 for (self.dylibs.items) |dylib| {
1666 const proxy = (try dylib.createProxy(undef.name)) orelse continue;1648 const proxy = (try dylib.createProxy(undef.name)) orelse continue;
1667 if (self.libsystem_dylib_index.? != @intCast(u16, i)) { // LibSystem gets load command seperately.1649 try referenced.put(dylib, {});
1668 try referenced.put(dylib, {});
1669 }
1670 break :inner proxy;1650 break :inner proxy;
1671 }1651 }
1672 if (mem.eql(u8, undef.name, "___dso_handle")) {1652 if (mem.eql(u8, undef.name, "___dso_handle")) {
...@@ -1681,7 +1661,6 @@ fn resolveSymbols(self: *Zld) !void {...@@ -1681,7 +1661,6 @@ fn resolveSymbols(self: *Zld) !void {
1681 .@"type" = .proxy,1661 .@"type" = .proxy,
1682 .name = name,1662 .name = name,
1683 },1663 },
1684 .file = null,
1685 };1664 };
1686 break :inner &proxy.base;1665 break :inner &proxy.base;
1687 }1666 }
...@@ -1717,21 +1696,13 @@ fn resolveSymbols(self: *Zld) !void {...@@ -1717,21 +1696,13 @@ fn resolveSymbols(self: *Zld) !void {
1717 if (self.unresolved.count() > 0) {1696 if (self.unresolved.count() > 0) {
1718 for (self.unresolved.values()) |undef| {1697 for (self.unresolved.values()) |undef| {
1719 log.err("undefined reference to symbol '{s}'", .{undef.name});1698 log.err("undefined reference to symbol '{s}'", .{undef.name});
1720 log.err(" | referenced in {s}", .{1699 if (undef.cast(Symbol.Unresolved).?.file) |file| {
1721 undef.cast(Symbol.Unresolved).?.file.name.?,1700 log.err(" | referenced in {s}", .{file.name.?});
1722 });1701 }
1723 }1702 }
17241703
1725 return error.UndefinedSymbolReference;1704 return error.UndefinedSymbolReference;
1726 }1705 }
1727
1728 // Finally put dyld_stub_binder as an Import
1729 const libsystem_dylib = self.dylibs.items[self.libsystem_dylib_index.?];
1730 const proxy = (try libsystem_dylib.createProxy("dyld_stub_binder")) orelse {
1731 log.err("undefined reference to symbol 'dyld_stub_binder'", .{});
1732 return error.UndefinedSymbolReference;
1733 };
1734 try self.imports.putNoClobber(self.allocator, proxy.name, proxy);
1735}1706}
17361707
1737fn resolveStubsAndGotEntries(self: *Zld) !void {1708fn resolveStubsAndGotEntries(self: *Zld) !void {
...@@ -3173,33 +3144,3 @@ pub fn parseName(name: *const [16]u8) []const u8 {...@@ -3173,33 +3144,3 @@ pub fn parseName(name: *const [16]u8) []const u8 {
3173 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;3144 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
3174 return name[0..len];3145 return name[0..len];
3175}3146}
3176
3177fn printSymbols(self: *Zld) void {
3178 log.debug("globals", .{});
3179 for (self.globals.values()) |value| {
3180 const sym = value.cast(Symbol.Regular) orelse unreachable;
3181 log.debug(" | {s} @ {*}", .{ sym.base.name, value });
3182 log.debug(" => alias of {*}", .{sym.base.alias});
3183 log.debug(" => linkage {s}", .{sym.linkage});
3184 log.debug(" => defined in {s}", .{sym.file.name.?});
3185 }
3186 for (self.objects.items) |object| {
3187 log.debug("locals in {s}", .{object.name.?});
3188 for (object.symbols.items) |sym| {
3189 log.debug(" | {s} @ {*}", .{ sym.name, sym });
3190 log.debug(" => alias of {*}", .{sym.alias});
3191 if (sym.cast(Symbol.Regular)) |reg| {
3192 log.debug(" => linkage {s}", .{reg.linkage});
3193 } else {
3194 log.debug(" => unresolved", .{});
3195 }
3196 }
3197 }
3198 log.debug("proxies", .{});
3199 for (self.imports.values()) |value| {
3200 const sym = value.cast(Symbol.Proxy) orelse unreachable;
3201 log.debug(" | {s} @ {*}", .{ sym.base.name, value });
3202 log.debug(" => alias of {*}", .{sym.base.alias});
3203 log.debug(" => defined in libSystem.B.dylib", .{});
3204 }
3205}