aboutsummaryrefslogtreecommitdiff
path: root/src/link
diff options
context:
space:
mode:
Diffstat (limited to 'src/link')
-rw-r--r--src/link/MachO.zig23
-rw-r--r--src/link/MachO/Dylib.zig128
-rw-r--r--src/link/MachO/Symbol.zig3
-rw-r--r--src/link/MachO/Zld.zig101
4 files changed, 116 insertions, 139 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index 81a63b6f9e..cd842fcf31 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -871,17 +871,19 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
// If we're compiling native and we can find libSystem.B.{dylib, tbd},
// we link against that instead of embedded libSystem.B.tbd file.
- const libc_stub_path = blk: {
- if (self.base.options.is_native_os) {
- if (try resolveLib(arena, lib_dirs.items, "System", .lib)) |full_path| {
- break :blk full_path;
- }
+ var link_native_libsystem = false;
+ if (self.base.options.is_native_os) {
+ if (try resolveLib(arena, lib_dirs.items, "System", .lib)) |full_path| {
+ try libs.append(full_path);
+ link_native_libsystem = true;
}
-
- break :blk try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ }
+ if (!link_native_libsystem) {
+ const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
"libc", "darwin", "libSystem.B.tbd",
});
- };
+ try positionals.append(full_path);
+ }
// frameworks
var framework_dirs = std.ArrayList([]const u8).init(arena);
@@ -935,6 +937,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
try argv.append("-o");
try argv.append(full_out_path);
+ if (link_native_libsystem) {
+ try argv.append("-lSystem");
+ }
+
for (search_lib_names.items) |l_name| {
try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));
}
@@ -950,7 +956,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
.syslibroot = self.base.options.sysroot,
.libs = libs.items,
.rpaths = rpaths.items,
- .libc_stub_path = libc_stub_path,
});
break :outer;
diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig
index 2ecd2a20ed..a60a45c6ed 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 {
@@ -380,7 +404,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 +494,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 +504,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 +546,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 56462c1140..87dcdcd35d 100644
--- a/src/link/MachO/Zld.zig
+++ b/src/link/MachO/Zld.zig
@@ -38,7 +38,6 @@ 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) = .{},
@@ -199,7 +198,6 @@ 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 {
@@ -240,7 +238,6 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L
try self.populateMetadata();
try self.parseInputFiles(files, args.syslibroot);
try self.parseLibs(args.libs, args.syslibroot);
- try self.parseLibSystem(args.libc_stub_path, args.syslibroot);
try self.resolveSymbols();
try self.resolveStubsAndGotEntries();
try self.updateMetadata();
@@ -280,7 +277,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u
self.allocator,
self.arch.?,
full_path,
- syslibroot,
+ .{ .syslibroot = syslibroot },
)) |dylibs| {
defer self.allocator.free(dylibs);
try self.dylibs.appendSlice(self.allocator, dylibs);
@@ -297,7 +294,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8, syslibroot: ?[]const u8) !voi
self.allocator,
self.arch.?,
lib,
- 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, syslibroot: ?[]const u8) !voi
}
}
-fn parseLibSystem(self: *Zld, libc_stub_path: []const u8, syslibroot: ?[]const u8) !void {
- const dylibs = (try Dylib.createAndParseFromPath(
- self.allocator,
- self.arch.?,
- libc_stub_path,
- 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,6 +1623,21 @@ 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();
@@ -1664,9 +1646,7 @@ fn resolveSymbols(self: *Zld) !void {
const proxy = inner: {
for (self.dylibs.items) |dylib, i| {
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", .{});
- }
-}