mirror of
https://github.com/ziglang/zig.git
synced 2025-12-13 01:33:09 +00:00
zld: recurse dylibs reexports when defined and desired
This commit is contained in:
parent
5aff1d5d6f
commit
eca12b74b8
@ -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;
|
||||
|
||||
@ -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;
|
||||
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 +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});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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", .{});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user