diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 95389c2d95..45e7bbe519 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -514,117 +514,83 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { } } -fn resolvePaths( +fn resolveSearchDir( arena: *Allocator, - resolved_paths: *std.ArrayList([]const u8), + dir: []const u8, syslibroot: ?[]const u8, +) !?[]const u8 { + var candidates = std.ArrayList([]const u8).init(arena); + + if (fs.path.isAbsolute(dir)) { + if (syslibroot) |root| { + const full_path = try fs.path.join(arena, &[_][]const u8{ root, dir }); + try candidates.append(full_path); + } + } + + try candidates.append(dir); + + for (candidates.items) |candidate| { + // Verify that search path actually exists + var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => |e| return e, + }; + defer tmp.close(); + + return candidate; + } + + return null; +} + +fn resolveLib( + arena: *Allocator, search_dirs: []const []const u8, - lib_names: []const []const u8, - kind: enum { lib, framework }, -) !void { - var resolved_dirs = std.ArrayList([]const u8).init(arena); + name: []const u8, + ext: []const u8, +) !?[]const u8 { + const search_name = try std.fmt.allocPrint(arena, "lib{s}{s}", .{ name, ext }); + for (search_dirs) |dir| { - if (fs.path.isAbsolute(dir)) { - var candidates = std.ArrayList([]const u8).init(arena); - if (syslibroot) |root| { - const full_path = try fs.path.join(arena, &[_][]const u8{ root, dir }); - try candidates.append(full_path); - } - try candidates.append(dir); + const full_path = try fs.path.join(arena, &[_][]const u8{ dir, search_name }); - var found = false; - for (candidates.items) |candidate| { - // Verify that search path actually exists - var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) { - error.FileNotFound => continue, - else => |e| return e, - }; - defer tmp.close(); + // Check if the file exists. + const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => |e| return e, + }; + defer tmp.close(); - try resolved_dirs.append(candidate); - found = true; - break; - } - - if (!found) { - switch (kind) { - .lib => log.warn("directory not found for '-L{s}'", .{dir}), - .framework => log.warn("directory not found for '-F{s}'", .{dir}), - } - } - } else { - // Verify that search path actually exists - var tmp = fs.cwd().openDir(dir, .{}) catch |err| switch (err) { - error.FileNotFound => { - switch (kind) { - .lib => log.warn("directory not found for '-L{s}'", .{dir}), - .framework => log.warn("directory not found for '-F{s}'", .{dir}), - } - continue; - }, - else => |e| return e, - }; - defer tmp.close(); - - try resolved_dirs.append(dir); - } + return full_path; } - // Assume ld64 default: -search_paths_first - // Look in each directory for a dylib (next, tbd), and then for archive - // TODO implement alternative: -search_dylibs_first - const exts = switch (kind) { - .lib => &[_][]const u8{ "dylib", "tbd", "a" }, - .framework => &[_][]const u8{ "dylib", "tbd" }, - }; + return null; +} - for (lib_names) |lib_name| { - var found = false; +fn resolveFramework( + arena: *Allocator, + search_dirs: []const []const u8, + name: []const u8, + ext: []const u8, +) !?[]const u8 { + const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext }); + const prefix_path = try std.fmt.allocPrint(arena, "{s}.framework", .{name}); - ext: for (exts) |ext| { - const lib_name_ext = blk: { - switch (kind) { - .lib => break :blk try std.fmt.allocPrint(arena, "lib{s}.{s}", .{ lib_name, ext }), - .framework => { - const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{lib_name}); - const nn = try std.fmt.allocPrint(arena, "{s}.{s}", .{ lib_name, ext }); - break :blk try fs.path.join(arena, &[_][]const u8{ prefix, nn }); - }, - } - }; + for (search_dirs) |dir| { + const full_path = try fs.path.join(arena, &[_][]const u8{ dir, prefix_path, search_name }); - for (resolved_dirs.items) |dir| { - const full_path = try fs.path.join(arena, &[_][]const u8{ dir, lib_name_ext }); + // Check if the file exists. + const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => |e| return e, + }; + defer tmp.close(); - // Check if the lib file exists. - const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { - error.FileNotFound => continue, - else => |e| return e, - }; - defer tmp.close(); - - try resolved_paths.append(full_path); - found = true; - break :ext; - } - } - - if (!found) { - switch (kind) { - .lib => { - log.warn("library not found for '-l{s}'", .{lib_name}); - log.warn("Library search paths:", .{}); - }, - .framework => { - log.warn("framework not found for '-f{s}'", .{lib_name}); - log.warn("Framework search paths:", .{}); - }, - } - for (resolved_dirs.items) |dir| { - log.warn(" {s}", .{dir}); - } - } + return full_path; } + + return null; } fn linkWithLLD(self: *MachO, comp: *Compilation) !void { @@ -789,7 +755,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { zld.deinit(); } zld.arch = target.cpu.arch; - zld.syslibroot = self.base.options.sysroot; zld.stack_size = stack_size; // Positional arguments to the linker such as object files and static archives. @@ -829,15 +794,97 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { try search_lib_names.append(link_lib); } + var lib_dirs = std.ArrayList([]const u8).init(arena); + for (self.base.options.lib_dirs) |dir| { + if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| { + try lib_dirs.append(search_dir); + } else { + log.warn("directory not found for '-L{s}'", .{dir}); + } + } + var libs = std.ArrayList([]const u8).init(arena); - try resolvePaths( - arena, - &libs, - self.base.options.sysroot, - self.base.options.lib_dirs, - search_lib_names.items, - .lib, - ); + var lib_not_found = false; + for (search_lib_names.items) |lib_name| { + // Assume ld64 default: -search_paths_first + // Look in each directory for a dylib (stub first), and then for archive + // TODO implement alternative: -search_dylibs_first + for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| { + if (try resolveLib(arena, lib_dirs.items, lib_name, ext)) |full_path| { + try libs.append(full_path); + break; + } + } else { + log.warn("library not found for '-l{s}'", .{lib_name}); + lib_not_found = true; + } + } + + if (lib_not_found) { + log.warn("Library search paths:", .{}); + for (lib_dirs.items) |dir| { + log.warn(" {s}", .{dir}); + } + } + + // If we're compiling native and we can find libSystem.B.{dylib, tbd}, + // we link against that instead of embedded libSystem.B.tbd file. + var native_libsystem_available = false; + if (self.base.options.is_native_os) blk: { + // Try stub file first. If we hit it, then we're done as the stub file + // re-exports every single symbol definition. + if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| { + try libs.append(full_path); + native_libsystem_available = true; + break :blk; + } + // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib + // doesn't export libc.dylib which we'll need to resolve subsequently also. + if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| { + if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| { + try libs.append(libsystem_path); + try libs.append(libc_path); + native_libsystem_available = true; + break :blk; + } + } + } + if (!native_libsystem_available) { + const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "darwin", "libSystem.B.tbd", + }); + try libs.append(full_path); + } + + // frameworks + var framework_dirs = std.ArrayList([]const u8).init(arena); + for (self.base.options.framework_dirs) |dir| { + if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| { + try framework_dirs.append(search_dir); + } else { + log.warn("directory not found for '-F{s}'", .{dir}); + } + } + + var framework_not_found = false; + for (self.base.options.frameworks) |framework| { + for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { + if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| { + try libs.append(full_path); + break; + } + } else { + log.warn("framework not found for '-f{s}'", .{framework}); + framework_not_found = true; + } + } + + if (framework_not_found) { + log.warn("Framework search paths:", .{}); + for (framework_dirs.items) |dir| { + log.warn(" {s}", .{dir}); + } + } // rpaths var rpath_table = std.StringArrayHashMap(void).init(arena); @@ -852,16 +899,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { rpaths.appendAssumeCapacity(key.*); } - // frameworks - try resolvePaths( - arena, - &libs, - self.base.options.sysroot, - self.base.options.framework_dirs, - self.base.options.frameworks, - .framework, - ); - if (self.base.options.verbose_link) { var argv = std.ArrayList([]const u8).init(arena); @@ -883,6 +920,11 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { try argv.append("-o"); try argv.append(full_out_path); + if (native_libsystem_available) { + try argv.append("-lSystem"); + try argv.append("-lc"); + } + for (search_lib_names.items) |l_name| { try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name})); } @@ -895,11 +937,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { } try zld.link(positionals.items, full_out_path, .{ + .syslibroot = self.base.options.sysroot, .libs = libs.items, .rpaths = rpaths.items, - .libc_stub_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ - "libc", "darwin", "libSystem.B.tbd", - }), }); break :outer; diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 2ecd2a20ed..91d4fe5320 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -45,8 +45,8 @@ id: ?Id = null, /// a symbol is referenced by an object file. symbols: std.StringArrayHashMapUnmanaged(void) = .{}, -// TODO add parsing re-exported libs from binary dylibs -dependent_libs: std.StringArrayHashMapUnmanaged(void) = .{}, +/// Array list of all dependent libs of this dylib. +dependent_libs: std.ArrayListUnmanaged(Id) = .{}, pub const Id = struct { name: []const u8, @@ -54,15 +54,28 @@ pub const Id = struct { current_version: u32, compatibility_version: u32, - pub fn default(name: []const u8) Id { - return .{ - .name = name, + pub fn default(allocator: *Allocator, name: []const u8) !Id { + return Id{ + .name = try allocator.dupe(u8, name), .timestamp = 2, .current_version = 0x10000, .compatibility_version = 0x10000, }; } + pub fn fromLoadCommand(allocator: *Allocator, lc: GenericCommandWithData(macho.dylib_command)) !Id { + const dylib = lc.inner.dylib; + const dylib_name = @ptrCast([*:0]const u8, lc.data[dylib.name - @sizeOf(macho.dylib_command) ..]); + const name = try allocator.dupe(u8, mem.spanZ(dylib_name)); + + return Id{ + .name = name, + .timestamp = dylib.timestamp, + .current_version = dylib.current_version, + .compatibility_version = dylib.compatibility_version, + }; + } + pub fn deinit(id: *Id, allocator: *Allocator) void { allocator.free(id.name); } @@ -129,12 +142,12 @@ pub const Error = error{ UnsupportedCpuArchitecture, } || fs.File.OpenError || std.os.PReadError || Id.ParseError; -pub fn createAndParseFromPath( - allocator: *Allocator, - arch: Arch, - path: []const u8, - syslibroot: ?[]const u8, -) Error!?[]*Dylib { +pub const CreateOpts = struct { + syslibroot: ?[]const u8 = null, + id: ?Id = null, +}; + +pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8, opts: CreateOpts) Error!?[]*Dylib { const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return null, else => |e| return e, @@ -152,7 +165,7 @@ pub fn createAndParseFromPath( .arch = arch, .name = name, .file = file, - .syslibroot = syslibroot, + .syslibroot = opts.syslibroot, }; dylib.parse() catch |err| switch (err) { @@ -171,6 +184,20 @@ pub fn createAndParseFromPath( else => |e| return e, }; + if (opts.id) |id| { + if (dylib.id.?.current_version < id.compatibility_version) { + log.warn("found dylib is incompatible with the required minimum version", .{}); + log.warn(" | dylib: {s}", .{id.name}); + log.warn(" | required minimum version: {}", .{id.compatibility_version}); + log.warn(" | dylib version: {}", .{dylib.id.?.current_version}); + + // TODO maybe this should be an error and facilitate auto-cleanup? + dylib.deinit(); + allocator.destroy(dylib); + return null; + } + } + var dylibs = std.ArrayList(*Dylib).init(allocator); defer dylibs.deinit(); @@ -191,8 +218,8 @@ pub fn deinit(self: *Dylib) void { } self.symbols.deinit(self.allocator); - for (self.dependent_libs.keys()) |key| { - self.allocator.free(key); + for (self.dependent_libs.items) |*id| { + id.deinit(self.allocator); } self.dependent_libs.deinit(self.allocator); @@ -287,6 +314,8 @@ fn readFatStruct(reader: anytype, comptime T: type) !T { } fn readLoadCommands(self: *Dylib, reader: anytype) !void { + const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0; + try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds); var i: u16 = 0; @@ -302,6 +331,13 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void { macho.LC_ID_DYLIB => { self.id_cmd_index = i; }, + macho.LC_REEXPORT_DYLIB => { + if (should_lookup_reexports) { + // Parse install_name to dependent dylib. + const id = try Id.fromLoadCommand(self.allocator, cmd.Dylib); + try self.dependent_libs.append(self.allocator, id); + } + }, else => { log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()}); }, @@ -313,22 +349,10 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void { fn parseId(self: *Dylib) !void { const index = self.id_cmd_index orelse { log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{}); - self.id = Id.default(try self.allocator.dupe(u8, self.name.?)); + self.id = try Id.default(self.allocator, self.name.?); return; }; - const id_cmd = self.load_commands.items[index].Dylib; - const dylib = id_cmd.inner.dylib; - - // TODO should we compare the name from the dylib's id with the user-specified one? - const dylib_name = @ptrCast([*:0]const u8, id_cmd.data[dylib.name - @sizeOf(macho.dylib_command) ..]); - const name = try self.allocator.dupe(u8, mem.spanZ(dylib_name)); - - self.id = .{ - .name = name, - .timestamp = dylib.timestamp, - .current_version = dylib.current_version, - .compatibility_version = dylib.compatibility_version, - }; + self.id = try Id.fromLoadCommand(self.allocator, self.load_commands.items[index].Dylib); } fn parseSymbols(self: *Dylib) !void { @@ -345,10 +369,11 @@ fn parseSymbols(self: *Dylib) !void { _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff + self.library_offset); for (slice) |sym| { + const add_to_symtab = Symbol.isExt(sym) and (Symbol.isSect(sym) or Symbol.isIndr(sym)); + + if (!add_to_symtab) continue; + const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx)); - - if (!(Symbol.isSect(sym) and Symbol.isExt(sym))) continue; - const name = try self.allocator.dupe(u8, sym_name); try self.symbols.putNoClobber(self.allocator, name, {}); } @@ -380,7 +405,7 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { const umbrella_lib = lib_stub.inner[0]; - var id = Id.default(try self.allocator.dupe(u8, umbrella_lib.install_name)); + var id = try Id.default(self.allocator, umbrella_lib.install_name); if (umbrella_lib.current_version) |version| { try id.parseCurrentVersion(version); } @@ -470,7 +495,9 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { } log.debug(" | {s}", .{lib}); - try self.dependent_libs.put(self.allocator, try self.allocator.dupe(u8, lib), {}); + + const dep_id = try Id.default(self.allocator, lib); + try self.dependent_libs.append(self.allocator, dep_id); } } } @@ -478,36 +505,40 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { } pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void { - outer: for (self.dependent_libs.keys()) |lib| { - const dirname = fs.path.dirname(lib) orelse { - log.warn("unable to resolve dependency {s}", .{lib}); - continue; + outer: for (self.dependent_libs.items) |id| { + const has_ext = blk: { + const basename = fs.path.basename(id.name); + break :blk mem.lastIndexOfScalar(u8, basename, '.') != null; }; - const filename = fs.path.basename(lib); - const without_ext = if (mem.lastIndexOfScalar(u8, filename, '.')) |index| - filename[0..index] - else - filename; + const extension = if (has_ext) fs.path.extension(id.name) else ""; + const without_ext = if (has_ext) blk: { + const index = mem.lastIndexOfScalar(u8, id.name, '.') orelse unreachable; + break :blk id.name[0..index]; + } else id.name; - for (&[_][]const u8{ "dylib", "tbd" }) |ext| { - const with_ext = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ + for (&[_][]const u8{ extension, ".tbd" }) |ext| { + const with_ext = try std.fmt.allocPrint(self.allocator, "{s}{s}", .{ without_ext, ext, }); defer self.allocator.free(with_ext); - const lib_path = if (self.syslibroot) |syslibroot| - try fs.path.join(self.allocator, &.{ syslibroot, dirname, with_ext }) + const full_path = if (self.syslibroot) |syslibroot| + try fs.path.join(self.allocator, &.{ syslibroot, with_ext }) else - try fs.path.join(self.allocator, &.{ dirname, with_ext }); + with_ext; + defer if (self.syslibroot) |_| self.allocator.free(full_path); - log.debug("trying dependency at fully resolved path {s}", .{lib_path}); + log.debug("trying dependency at fully resolved path {s}", .{full_path}); const dylibs = (try createAndParseFromPath( self.allocator, self.arch.?, - lib_path, - self.syslibroot, + full_path, + .{ + .id = id, + .syslibroot = self.syslibroot, + }, )) orelse { continue; }; @@ -516,7 +547,7 @@ pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void { continue :outer; } else { - log.warn("unable to resolve dependency {s}", .{lib}); + log.warn("unable to resolve dependency {s}", .{id.name}); } } } diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index c58af27672..8da4704909 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -111,7 +111,8 @@ pub const Unresolved = struct { base: Symbol, /// File where this symbol was referenced. - file: *Object, + /// null means synthetic, e.g., dyld_stub_binder. + file: ?*Object = null, pub const base_type: Symbol.Type = .unresolved; }; diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 6cabdfa73e..59c09aacef 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -32,14 +32,12 @@ out_path: ?[]const u8 = null, // TODO these args will become obselete once Zld is coalesced with incremental // linker. -syslibroot: ?[]const u8 = null, stack_size: u64 = 0, objects: std.ArrayListUnmanaged(*Object) = .{}, archives: std.ArrayListUnmanaged(*Archive) = .{}, dylibs: std.ArrayListUnmanaged(*Dylib) = .{}, -libsystem_dylib_index: ?u16 = null, next_dylib_ordinal: u16 = 1, load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, @@ -197,9 +195,9 @@ pub fn closeFiles(self: Zld) void { } const LinkArgs = struct { + syslibroot: ?[]const u8, libs: []const []const u8, rpaths: []const []const u8, - libc_stub_path: []const u8, }; pub 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 }); try self.populateMetadata(); - try self.parseInputFiles(files); - try self.parseLibs(args.libs); - try self.parseLibSystem(args.libc_stub_path); + try self.parseInputFiles(files, args.syslibroot); + try self.parseLibs(args.libs, args.syslibroot); try self.resolveSymbols(); try self.resolveStubsAndGotEntries(); try self.updateMetadata(); @@ -258,7 +255,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L try self.flush(); } -fn parseInputFiles(self: *Zld, files: []const []const u8) !void { +fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u8) !void { for (files) |file_name| { const full_path = full_path: { var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; @@ -280,7 +277,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { self.allocator, self.arch.?, full_path, - self.syslibroot, + .{ .syslibroot = syslibroot }, )) |dylibs| { defer self.allocator.free(dylibs); try self.dylibs.appendSlice(self.allocator, dylibs); @@ -291,13 +288,13 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { } } -fn parseLibs(self: *Zld, libs: []const []const u8) !void { +fn parseLibs(self: *Zld, libs: []const []const u8, syslibroot: ?[]const u8) !void { for (libs) |lib| { if (try Dylib.createAndParseFromPath( self.allocator, self.arch.?, lib, - self.syslibroot, + .{ .syslibroot = syslibroot }, )) |dylibs| { defer self.allocator.free(dylibs); try self.dylibs.appendSlice(self.allocator, dylibs); @@ -313,36 +310,6 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void { } } -fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void { - const dylibs = (try Dylib.createAndParseFromPath( - self.allocator, - self.arch.?, - libc_stub_path, - self.syslibroot, - )) orelse return error.FailedToParseLibSystem; - defer self.allocator.free(dylibs); - - assert(dylibs.len == 1); // More than one dylib output from parsing libSystem! - const dylib = dylibs[0]; - - self.libsystem_dylib_index = @intCast(u16, self.dylibs.items.len); - try self.dylibs.append(self.allocator, dylib); - - // Add LC_LOAD_DYLIB load command. - dylib.ordinal = self.next_dylib_ordinal; - const dylib_id = dylib.id orelse unreachable; - var dylib_cmd = try createLoadDylibCommand( - self.allocator, - dylib_id.name, - dylib_id.timestamp, - dylib_id.current_version, - dylib_id.compatibility_version, - ); - errdefer dylib_cmd.deinit(self.allocator); - try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); - self.next_dylib_ordinal += 1; -} - fn mapAndUpdateSections( self: *Zld, object: *Object, @@ -1656,17 +1623,30 @@ fn resolveSymbols(self: *Zld) !void { } self.unresolved.clearRetainingCapacity(); + // Put dyld_stub_binder as an unresolved special symbol. + { + const name = try self.allocator.dupe(u8, "dyld_stub_binder"); + errdefer self.allocator.free(name); + const undef = try self.allocator.create(Symbol.Unresolved); + errdefer self.allocator.destroy(undef); + undef.* = .{ + .base = .{ + .@"type" = .unresolved, + .name = name, + }, + }; + try unresolved.append(&undef.base); + } + var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator); defer referenced.deinit(); loop: while (unresolved.popOrNull()) |undef| { const proxy = self.imports.get(undef.name) orelse outer: { const proxy = inner: { - for (self.dylibs.items) |dylib, i| { + for (self.dylibs.items) |dylib| { const proxy = (try dylib.createProxy(undef.name)) orelse continue; - if (self.libsystem_dylib_index.? != @intCast(u16, i)) { // LibSystem gets load command seperately. - try referenced.put(dylib, {}); - } + try referenced.put(dylib, {}); break :inner proxy; } if (mem.eql(u8, undef.name, "___dso_handle")) { @@ -1681,7 +1661,6 @@ fn resolveSymbols(self: *Zld) !void { .@"type" = .proxy, .name = name, }, - .file = null, }; break :inner &proxy.base; } @@ -1717,21 +1696,13 @@ fn resolveSymbols(self: *Zld) !void { if (self.unresolved.count() > 0) { for (self.unresolved.values()) |undef| { log.err("undefined reference to symbol '{s}'", .{undef.name}); - log.err(" | referenced in {s}", .{ - undef.cast(Symbol.Unresolved).?.file.name.?, - }); + if (undef.cast(Symbol.Unresolved).?.file) |file| { + log.err(" | referenced in {s}", .{file.name.?}); + } } return error.UndefinedSymbolReference; } - - // Finally put dyld_stub_binder as an Import - const libsystem_dylib = self.dylibs.items[self.libsystem_dylib_index.?]; - const proxy = (try libsystem_dylib.createProxy("dyld_stub_binder")) orelse { - log.err("undefined reference to symbol 'dyld_stub_binder'", .{}); - return error.UndefinedSymbolReference; - }; - try self.imports.putNoClobber(self.allocator, proxy.name, proxy); } fn resolveStubsAndGotEntries(self: *Zld) !void { @@ -3173,33 +3144,3 @@ pub fn parseName(name: *const [16]u8) []const u8 { const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len; return name[0..len]; } - -fn printSymbols(self: *Zld) void { - log.debug("globals", .{}); - for (self.globals.values()) |value| { - const sym = value.cast(Symbol.Regular) orelse unreachable; - log.debug(" | {s} @ {*}", .{ sym.base.name, value }); - log.debug(" => alias of {*}", .{sym.base.alias}); - log.debug(" => linkage {s}", .{sym.linkage}); - log.debug(" => defined in {s}", .{sym.file.name.?}); - } - for (self.objects.items) |object| { - log.debug("locals in {s}", .{object.name.?}); - for (object.symbols.items) |sym| { - log.debug(" | {s} @ {*}", .{ sym.name, sym }); - log.debug(" => alias of {*}", .{sym.alias}); - if (sym.cast(Symbol.Regular)) |reg| { - log.debug(" => linkage {s}", .{reg.linkage}); - } else { - log.debug(" => unresolved", .{}); - } - } - } - log.debug("proxies", .{}); - for (self.imports.values()) |value| { - const sym = value.cast(Symbol.Proxy) orelse unreachable; - log.debug(" | {s} @ {*}", .{ sym.base.name, value }); - log.debug(" => alias of {*}", .{sym.base.alias}); - log.debug(" => defined in libSystem.B.dylib", .{}); - } -}