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 {
514514 }
515515}
516516
517fn resolvePaths(
517fn resolveSearchDir(
518518 arena: *Allocator,
519 resolved_paths: *std.ArrayList([]const u8),
519 dir: []const u8,
520520 syslibroot: ?[]const u8,
521 search_dirs: []const []const u8,
522 lib_names: []const []const u8,
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();
521) !?[]const u8 {
522 var candidates = std.ArrayList([]const u8).init(arena);
543523
544 try resolved_dirs.append(candidate);
545 found = true;
546 break;
547 }
524 if (fs.path.isAbsolute(dir)) {
525 if (syslibroot) |root| {
526 const full_path = try fs.path.join(arena, &[_][]const u8{ root, dir });
527 try candidates.append(full_path);
528 }
529 }
548530
549 if (!found) {
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();
531 try candidates.append(dir);
568532
569 try resolved_dirs.append(dir);
570 }
533 for (candidates.items) |candidate| {
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;
571542 }
572543
573 // Assume ld64 default: -search_paths_first
574 // Look in each directory for a dylib (next, tbd), and then for archive
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 };
544 return null;
545}
580546
581 for (lib_names) |lib_name| {
582 var found = false;
583
584 ext: for (exts) |ext| {
585 const lib_name_ext = blk: {
586 switch (kind) {
587 .lib => break :blk try std.fmt.allocPrint(arena, "lib{s}.{s}", .{ lib_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 };
547fn resolveLib(
548 arena: *Allocator,
549 search_dirs: []const []const u8,
550 name: []const u8,
551 ext: []const u8,
552) !?[]const u8 {
553 const search_name = try std.fmt.allocPrint(arena, "lib{s}{s}", .{ name, ext });
595554
596 for (resolved_dirs.items) |dir| {
597 const full_path = try fs.path.join(arena, &[_][]const u8{ dir, lib_name_ext });
555 for (search_dirs) |dir| {
556 const full_path = try fs.path.join(arena, &[_][]const u8{ dir, search_name });
598557
599 // Check if the lib file exists.
600 const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
601 error.FileNotFound => continue,
602 else => |e| return e,
603 };
604 defer tmp.close();
558 // Check if the file exists.
559 const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
560 error.FileNotFound => continue,
561 else => |e| return e,
562 };
563 defer tmp.close();
605564
606 try resolved_paths.append(full_path);
607 found = true;
608 break :ext;
609 }
610 }
565 return full_path;
566 }
611567
612 if (!found) {
613 switch (kind) {
614 .lib => {
615 log.warn("library not found for '-l{s}'", .{lib_name});
616 log.warn("Library search paths:", .{});
617 },
618 .framework => {
619 log.warn("framework not found for '-f{s}'", .{lib_name});
620 log.warn("Framework search paths:", .{});
621 },
622 }
623 for (resolved_dirs.items) |dir| {
624 log.warn(" {s}", .{dir});
625 }
626 }
568 return null;
569}
570
571fn resolveFramework(
572 arena: *Allocator,
573 search_dirs: []const []const u8,
574 name: []const u8,
575 ext: []const u8,
576) !?[]const u8 {
577 const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext });
578 const prefix_path = try std.fmt.allocPrint(arena, "{s}.framework", .{name});
579
580 for (search_dirs) |dir| {
581 const full_path = try fs.path.join(arena, &[_][]const u8{ dir, prefix_path, search_name });
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;
627591 }
592
593 return null;
628594}
629595
630596fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
......@@ -789,7 +755,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
789755 zld.deinit();
790756 }
791757 zld.arch = target.cpu.arch;
792 zld.syslibroot = self.base.options.sysroot;
793758 zld.stack_size = stack_size;
794759
795760 // Positional arguments to the linker such as object files and static archives.
......@@ -829,15 +794,97 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
829794 try search_lib_names.append(link_lib);
830795 }
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
832806 var libs = std.ArrayList([]const u8).init(arena);
833 try resolvePaths(
834 arena,
835 &libs,
836 self.base.options.sysroot,
837 self.base.options.lib_dirs,
838 search_lib_names.items,
839 .lib,
840 );
807 var lib_not_found = false;
808 for (search_lib_names.items) |lib_name| {
809 // Assume ld64 default: -search_paths_first
810 // Look in each directory for a dylib (stub first), and then for archive
811 // TODO implement alternative: -search_dylibs_first
812 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
813 if (try resolveLib(arena, lib_dirs.items, lib_name, ext)) |full_path| {
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
842889 // rpaths
843890 var rpath_table = std.StringArrayHashMap(void).init(arena);
......@@ -852,16 +899,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
852899 rpaths.appendAssumeCapacity(key.*);
853900 }
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
865902 if (self.base.options.verbose_link) {
866903 var argv = std.ArrayList([]const u8).init(arena);
867904
......@@ -883,6 +920,11 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
883920 try argv.append("-o");
884921 try argv.append(full_out_path);
885922
923 if (native_libsystem_available) {
924 try argv.append("-lSystem");
925 try argv.append("-lc");
926 }
927
886928 for (search_lib_names.items) |l_name| {
887929 try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));
888930 }
......@@ -895,11 +937,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
895937 }
896938
897939 try zld.link(positionals.items, full_out_path, .{
940 .syslibroot = self.base.options.sysroot,
898941 .libs = libs.items,
899942 .rpaths = rpaths.items,
900 .libc_stub_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
901 "libc", "darwin", "libSystem.B.tbd",
902 }),
903943 });
904944
905945 break :outer;
src/link/MachO/Dylib.zig+82-51
......@@ -45,8 +45,8 @@ id: ?Id = null,
4545/// a symbol is referenced by an object file.
4646symbols: std.StringArrayHashMapUnmanaged(void) = .{},
4747
48// TODO add parsing re-exported libs from binary dylibs
49dependent_libs: std.StringArrayHashMapUnmanaged(void) = .{},
48/// Array list of all dependent libs of this dylib.
49dependent_libs: std.ArrayListUnmanaged(Id) = .{},
5050
5151pub const Id = struct {
5252 name: []const u8,
......@@ -54,15 +54,28 @@ pub const Id = struct {
5454 current_version: u32,
5555 compatibility_version: u32,
5656
57 pub fn default(name: []const u8) Id {
58 return .{
59 .name = name,
57 pub fn default(allocator: *Allocator, name: []const u8) !Id {
58 return Id{
59 .name = try allocator.dupe(u8, name),
6060 .timestamp = 2,
6161 .current_version = 0x10000,
6262 .compatibility_version = 0x10000,
6363 };
6464 }
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
6679 pub fn deinit(id: *Id, allocator: *Allocator) void {
6780 allocator.free(id.name);
6881 }
......@@ -129,12 +142,12 @@ pub const Error = error{
129142 UnsupportedCpuArchitecture,
130143} || fs.File.OpenError || std.os.PReadError || Id.ParseError;
131144
132pub fn createAndParseFromPath(
133 allocator: *Allocator,
134 arch: Arch,
135 path: []const u8,
136 syslibroot: ?[]const u8,
137) Error!?[]*Dylib {
145pub const CreateOpts = struct {
146 syslibroot: ?[]const u8 = null,
147 id: ?Id = null,
148};
149
150pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8, opts: CreateOpts) Error!?[]*Dylib {
138151 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
139152 error.FileNotFound => return null,
140153 else => |e| return e,
......@@ -152,7 +165,7 @@ pub fn createAndParseFromPath(
152165 .arch = arch,
153166 .name = name,
154167 .file = file,
155 .syslibroot = syslibroot,
168 .syslibroot = opts.syslibroot,
156169 };
157170
158171 dylib.parse() catch |err| switch (err) {
......@@ -171,6 +184,20 @@ pub fn createAndParseFromPath(
171184 else => |e| return e,
172185 };
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
174201 var dylibs = std.ArrayList(*Dylib).init(allocator);
175202 defer dylibs.deinit();
176203
......@@ -191,8 +218,8 @@ pub fn deinit(self: *Dylib) void {
191218 }
192219 self.symbols.deinit(self.allocator);
193220
194 for (self.dependent_libs.keys()) |key| {
195 self.allocator.free(key);
221 for (self.dependent_libs.items) |*id| {
222 id.deinit(self.allocator);
196223 }
197224 self.dependent_libs.deinit(self.allocator);
198225
......@@ -287,6 +314,8 @@ fn readFatStruct(reader: anytype, comptime T: type) !T {
287314}
288315
289316fn readLoadCommands(self: *Dylib, reader: anytype) !void {
317 const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
318
290319 try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds);
291320
292321 var i: u16 = 0;
......@@ -302,6 +331,13 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void {
302331 macho.LC_ID_DYLIB => {
303332 self.id_cmd_index = i;
304333 },
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 },
305341 else => {
306342 log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
307343 },
......@@ -313,22 +349,10 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void {
313349fn parseId(self: *Dylib) !void {
314350 const index = self.id_cmd_index orelse {
315351 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.?);
317353 return;
318354 };
319 const id_cmd = 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 };
355 self.id = try Id.fromLoadCommand(self.allocator, self.load_commands.items[index].Dylib);
332356}
333357
334358fn parseSymbols(self: *Dylib) !void {
......@@ -345,10 +369,11 @@ fn parseSymbols(self: *Dylib) !void {
345369 _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff + self.library_offset);
346370
347371 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));
352377 const name = try self.allocator.dupe(u8, sym_name);
353378 try self.symbols.putNoClobber(self.allocator, name, {});
354379 }
......@@ -380,7 +405,7 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {
380405
381406 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);
384409 if (umbrella_lib.current_version) |version| {
385410 try id.parseCurrentVersion(version);
386411 }
......@@ -470,7 +495,9 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {
470495 }
471496
472497 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);
474501 }
475502 }
476503 }
......@@ -478,36 +505,40 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {
478505}
479506
480507pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void {
481 outer: for (self.dependent_libs.keys()) |lib| {
482 const dirname = fs.path.dirname(lib) orelse {
483 log.warn("unable to resolve dependency {s}", .{lib});
484 continue;
508 outer: for (self.dependent_libs.items) |id| {
509 const has_ext = blk: {
510 const basename = fs.path.basename(id.name);
511 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
485512 };
486 const filename = fs.path.basename(lib);
487 const without_ext = if (mem.lastIndexOfScalar(u8, filename, '.')) |index|
488 filename[0..index]
489 else
490 filename;
491
492 for (&[_][]const u8{ "dylib", "tbd" }) |ext| {
493 const with_ext = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{
513 const extension = if (has_ext) fs.path.extension(id.name) else "";
514 const without_ext = if (has_ext) blk: {
515 const index = mem.lastIndexOfScalar(u8, id.name, '.') orelse unreachable;
516 break :blk id.name[0..index];
517 } else id.name;
518
519 for (&[_][]const u8{ extension, ".tbd" }) |ext| {
520 const with_ext = try std.fmt.allocPrint(self.allocator, "{s}{s}", .{
494521 without_ext,
495522 ext,
496523 });
497524 defer self.allocator.free(with_ext);
498525
499 const lib_path = if (self.syslibroot) |syslibroot|
500 try fs.path.join(self.allocator, &.{ syslibroot, dirname, with_ext })
526 const full_path = if (self.syslibroot) |syslibroot|
527 try fs.path.join(self.allocator, &.{ syslibroot, with_ext })
501528 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
506534 const dylibs = (try createAndParseFromPath(
507535 self.allocator,
508536 self.arch.?,
509 lib_path,
510 self.syslibroot,
537 full_path,
538 .{
539 .id = id,
540 .syslibroot = self.syslibroot,
541 },
511542 )) orelse {
512543 continue;
513544 };
......@@ -516,7 +547,7 @@ pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void {
516547
517548 continue :outer;
518549 } else {
519 log.warn("unable to resolve dependency {s}", .{lib});
550 log.warn("unable to resolve dependency {s}", .{id.name});
520551 }
521552 }
522553}
src/link/MachO/Symbol.zig+2-1
......@@ -111,7 +111,8 @@ pub const Unresolved = struct {
111111 base: Symbol,
112112
113113 /// File where this symbol was referenced.
114 file: *Object,
114 /// null means synthetic, e.g., dyld_stub_binder.
115 file: ?*Object = null,
115116
116117 pub const base_type: Symbol.Type = .unresolved;
117118};
src/link/MachO/Zld.zig+27-86
......@@ -32,14 +32,12 @@ out_path: ?[]const u8 = null,
3232
3333// TODO these args will become obselete once Zld is coalesced with incremental
3434// linker.
35syslibroot: ?[]const u8 = null,
3635stack_size: u64 = 0,
3736
3837objects: std.ArrayListUnmanaged(*Object) = .{},
3938archives: std.ArrayListUnmanaged(*Archive) = .{},
4039dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
4140
42libsystem_dylib_index: ?u16 = null,
4341next_dylib_ordinal: u16 = 1,
4442
4543load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
......@@ -197,9 +195,9 @@ pub fn closeFiles(self: Zld) void {
197195}
198196
199197const LinkArgs = struct {
198 syslibroot: ?[]const u8,
200199 libs: []const []const u8,
201200 rpaths: []const []const u8,
202 libc_stub_path: []const u8,
203201};
204202
205203pub 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
238236 });
239237
240238 try self.populateMetadata();
241 try self.parseInputFiles(files);
242 try self.parseLibs(args.libs);
243 try self.parseLibSystem(args.libc_stub_path);
239 try self.parseInputFiles(files, args.syslibroot);
240 try self.parseLibs(args.libs, args.syslibroot);
244241 try self.resolveSymbols();
245242 try self.resolveStubsAndGotEntries();
246243 try self.updateMetadata();
......@@ -258,7 +255,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L
258255 try self.flush();
259256}
260257
261fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
258fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u8) !void {
262259 for (files) |file_name| {
263260 const full_path = full_path: {
264261 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
......@@ -280,7 +277,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
280277 self.allocator,
281278 self.arch.?,
282279 full_path,
283 self.syslibroot,
280 .{ .syslibroot = syslibroot },
284281 )) |dylibs| {
285282 defer self.allocator.free(dylibs);
286283 try self.dylibs.appendSlice(self.allocator, dylibs);
......@@ -291,13 +288,13 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
291288 }
292289}
293290
294fn parseLibs(self: *Zld, libs: []const []const u8) !void {
291fn parseLibs(self: *Zld, libs: []const []const u8, syslibroot: ?[]const u8) !void {
295292 for (libs) |lib| {
296293 if (try Dylib.createAndParseFromPath(
297294 self.allocator,
298295 self.arch.?,
299296 lib,
300 self.syslibroot,
297 .{ .syslibroot = syslibroot },
301298 )) |dylibs| {
302299 defer self.allocator.free(dylibs);
303300 try self.dylibs.appendSlice(self.allocator, dylibs);
......@@ -313,36 +310,6 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
313310 }
314311}
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
346313fn mapAndUpdateSections(
347314 self: *Zld,
348315 object: *Object,
......@@ -1656,17 +1623,30 @@ fn resolveSymbols(self: *Zld) !void {
16561623 }
16571624 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
16591641 var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator);
16601642 defer referenced.deinit();
16611643
16621644 loop: while (unresolved.popOrNull()) |undef| {
16631645 const proxy = self.imports.get(undef.name) orelse outer: {
16641646 const proxy = inner: {
1665 for (self.dylibs.items) |dylib, i| {
1647 for (self.dylibs.items) |dylib| {
16661648 const proxy = (try dylib.createProxy(undef.name)) orelse continue;
1667 if (self.libsystem_dylib_index.? != @intCast(u16, i)) { // LibSystem gets load command seperately.
1668 try referenced.put(dylib, {});
1669 }
1649 try referenced.put(dylib, {});
16701650 break :inner proxy;
16711651 }
16721652 if (mem.eql(u8, undef.name, "___dso_handle")) {
......@@ -1681,7 +1661,6 @@ fn resolveSymbols(self: *Zld) !void {
16811661 .@"type" = .proxy,
16821662 .name = name,
16831663 },
1684 .file = null,
16851664 };
16861665 break :inner &proxy.base;
16871666 }
......@@ -1717,21 +1696,13 @@ fn resolveSymbols(self: *Zld) !void {
17171696 if (self.unresolved.count() > 0) {
17181697 for (self.unresolved.values()) |undef| {
17191698 log.err("undefined reference to symbol '{s}'", .{undef.name});
1720 log.err(" | referenced in {s}", .{
1721 undef.cast(Symbol.Unresolved).?.file.name.?,
1722 });
1699 if (undef.cast(Symbol.Unresolved).?.file) |file| {
1700 log.err(" | referenced in {s}", .{file.name.?});
1701 }
17231702 }
17241703
17251704 return error.UndefinedSymbolReference;
17261705 }
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);
17351706}
17361707
17371708fn resolveStubsAndGotEntries(self: *Zld) !void {
......@@ -3173,33 +3144,3 @@ pub fn parseName(name: *const [16]u8) []const u8 {
31733144 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
31743145 return name[0..len];
31753146}
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}