commit 2c385e58f96ea080bd6c732de422f84c6c38c2a2 (tree)
parent 6d47b4f39e8cdd95ead71b1efdbf67a737174e97
Author: Jakub Konka <kubkon@jakubkonka.com>
Date: Mon, 28 Jun 2021 21:06:12 +0200
Merge pull request #9242 from ziglang/zld-link-system-libsystem-when-native
zld: link against system libSystem when available
Diffstat:
4 files changed, 271 insertions(+), 258 deletions(-)
diff --git 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,
- search_dirs: []const []const u8,
- lib_names: []const []const u8,
- kind: enum { lib, framework },
-) !void {
- var resolved_dirs = std.ArrayList([]const u8).init(arena);
- 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);
-
- 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();
+) !?[]const u8 {
+ var candidates = std.ArrayList([]const u8).init(arena);
- try resolved_dirs.append(candidate);
- found = true;
- break;
- }
+ 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);
+ }
+ }
- 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 candidates.append(dir);
- try resolved_dirs.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;
}
- // 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;
-
- 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 });
- },
- }
- };
+fn resolveLib(
+ arena: *Allocator,
+ search_dirs: []const []const u8,
+ name: []const u8,
+ ext: []const u8,
+) !?[]const u8 {
+ const search_name = try std.fmt.allocPrint(arena, "lib{s}{s}", .{ name, ext });
- for (resolved_dirs.items) |dir| {
- const full_path = try fs.path.join(arena, &[_][]const u8{ dir, lib_name_ext });
+ for (search_dirs) |dir| {
+ const full_path = try fs.path.join(arena, &[_][]const u8{ dir, search_name });
- // 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();
+ // 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_paths.append(full_path);
- found = true;
- break :ext;
- }
- }
+ return full_path;
+ }
- 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 null;
+}
+
+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});
+
+ for (search_dirs) |dir| {
+ const full_path = try fs.path.join(arena, &[_][]const u8{ dir, prefix_path, search_name });
+
+ // 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();
+
+ 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
@@ -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 sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx));
+ const add_to_symtab = Symbol.isExt(sym) and (Symbol.isSect(sym) or Symbol.isIndr(sym));
- if (!(Symbol.isSect(sym) and Symbol.isExt(sym))) continue;
+ if (!add_to_symtab) continue;
+ const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx));
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;
-
- for (&[_][]const u8{ "dylib", "tbd" }) |ext| {
- const with_ext = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{
+ 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{ 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
@@ -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
@@ -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", .{});
- }
-}