mirror of
https://github.com/ziglang/zig.git
synced 2025-12-28 09:03:21 +00:00
Merge pull request #10208 from ziglang/zld-frameworks
zld: resolve frameworks in BFS order and handle additional macOS flags
This commit is contained in:
commit
8e6c038dd8
@ -758,6 +758,7 @@ pub const InitOptions = struct {
|
||||
image_base_override: ?u64 = null,
|
||||
self_exe_path: ?[]const u8 = null,
|
||||
version: ?std.builtin.Version = null,
|
||||
compatibility_version: ?std.builtin.Version = null,
|
||||
libc_installation: ?*const LibCInstallation = null,
|
||||
machine_code_model: std.builtin.CodeModel = .default,
|
||||
clang_preprocessor_mode: ClangPreprocessorMode = .no,
|
||||
@ -1439,6 +1440,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
.extra_lld_args = options.lld_argv,
|
||||
.soname = options.soname,
|
||||
.version = options.version,
|
||||
.compatibility_version = options.compatibility_version,
|
||||
.libc_installation = libc_dirs.libc_installation,
|
||||
.pic = pic,
|
||||
.pie = pie,
|
||||
|
||||
@ -143,6 +143,7 @@ pub const Options = struct {
|
||||
rpath_list: []const []const u8,
|
||||
|
||||
version: ?std.builtin.Version,
|
||||
compatibility_version: ?std.builtin.Version,
|
||||
libc_installation: ?*const LibCInstallation,
|
||||
|
||||
/// WASI-only. Type of WASI execution model ("command" or "reactor").
|
||||
|
||||
@ -842,8 +842,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
|
||||
Compilation.dump_argv(argv.items);
|
||||
}
|
||||
|
||||
try self.parseInputFiles(positionals.items, self.base.options.sysroot);
|
||||
try self.parseLibs(libs.items, self.base.options.sysroot);
|
||||
var dependent_libs = std.fifo.LinearFifo(Dylib.Id, .Dynamic).init(self.base.allocator);
|
||||
defer dependent_libs.deinit();
|
||||
try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
|
||||
try self.parseLibs(libs.items, self.base.options.sysroot, &dependent_libs);
|
||||
try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
|
||||
}
|
||||
|
||||
if (self.bss_section_index) |idx| {
|
||||
@ -1161,7 +1164,8 @@ const ParseDylibError = error{
|
||||
} || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;
|
||||
|
||||
const DylibCreateOpts = struct {
|
||||
syslibroot: ?[]const u8 = null,
|
||||
syslibroot: ?[]const u8,
|
||||
dependent_libs: *std.fifo.LinearFifo(Dylib.Id, .Dynamic),
|
||||
id: ?Dylib.Id = null,
|
||||
is_dependent: bool = false,
|
||||
};
|
||||
@ -1181,7 +1185,7 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
|
||||
.file = file,
|
||||
};
|
||||
|
||||
dylib.parse(self.base.allocator, self.base.options.target) catch |err| switch (err) {
|
||||
dylib.parse(self.base.allocator, self.base.options.target, opts.dependent_libs) catch |err| switch (err) {
|
||||
error.EndOfStream, error.NotDylib => {
|
||||
try file.seekTo(0);
|
||||
|
||||
@ -1191,7 +1195,7 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
|
||||
};
|
||||
defer lib_stub.deinit();
|
||||
|
||||
try dylib.parseFromStub(self.base.allocator, self.base.options.target, lib_stub);
|
||||
try dylib.parseFromStub(self.base.allocator, self.base.options.target, lib_stub, opts.dependent_libs);
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
@ -1218,14 +1222,10 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
|
||||
try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
|
||||
}
|
||||
|
||||
// TODO this should not be performed if the user specifies `-flat_namespace` flag.
|
||||
// See ld64 manpages.
|
||||
try dylib.parseDependentLibs(self, opts.syslibroot);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8) !void {
|
||||
fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
|
||||
for (files) |file_name| {
|
||||
const full_path = full_path: {
|
||||
var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
@ -1239,17 +1239,19 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
|
||||
if (try self.parseArchive(full_path)) continue;
|
||||
if (try self.parseDylib(full_path, .{
|
||||
.syslibroot = syslibroot,
|
||||
.dependent_libs = dependent_libs,
|
||||
})) continue;
|
||||
|
||||
log.warn("unknown filetype for positional input file: '{s}'", .{file_name});
|
||||
}
|
||||
}
|
||||
|
||||
fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !void {
|
||||
fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
|
||||
for (libs) |lib| {
|
||||
log.debug("parsing lib path '{s}'", .{lib});
|
||||
if (try self.parseDylib(lib, .{
|
||||
.syslibroot = syslibroot,
|
||||
.dependent_libs = dependent_libs,
|
||||
})) continue;
|
||||
if (try self.parseArchive(lib)) continue;
|
||||
|
||||
@ -1257,6 +1259,50 @@ fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !v
|
||||
}
|
||||
}
|
||||
|
||||
fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
|
||||
// At this point, we can now parse dependents of dylibs preserving the inclusion order of:
|
||||
// 1) anything on the linker line is parsed first
|
||||
// 2) afterwards, we parse dependents of the included dylibs
|
||||
// TODO this should not be performed if the user specifies `-flat_namespace` flag.
|
||||
// See ld64 manpages.
|
||||
var arena_alloc = std.heap.ArenaAllocator.init(self.base.allocator);
|
||||
const arena = &arena_alloc.allocator;
|
||||
defer arena_alloc.deinit();
|
||||
|
||||
while (dependent_libs.readItem()) |*id| {
|
||||
defer id.deinit(self.base.allocator);
|
||||
|
||||
if (self.dylibs_map.contains(id.name)) continue;
|
||||
|
||||
const has_ext = blk: {
|
||||
const basename = fs.path.basename(id.name);
|
||||
break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
|
||||
};
|
||||
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(arena, "{s}{s}", .{ without_ext, ext });
|
||||
const full_path = if (syslibroot) |root| try fs.path.join(arena, &.{ root, with_ext }) else with_ext;
|
||||
|
||||
log.debug("trying dependency at fully resolved path {s}", .{full_path});
|
||||
|
||||
const did_parse_successfully = try self.parseDylib(full_path, .{
|
||||
.id = id.*,
|
||||
.syslibroot = syslibroot,
|
||||
.is_dependent = true,
|
||||
.dependent_libs = dependent_libs,
|
||||
});
|
||||
if (did_parse_successfully) break;
|
||||
} else {
|
||||
log.warn("unable to resolve dependency {s}", .{id.name});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const MatchingSection = struct {
|
||||
seg: u16,
|
||||
sect: u16,
|
||||
@ -3992,12 +4038,16 @@ pub fn populateMissingMetadata(self: *MachO) !void {
|
||||
self.base.options.emit.?.sub_path,
|
||||
});
|
||||
defer self.base.allocator.free(install_name);
|
||||
const current_version = self.base.options.version orelse
|
||||
std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
|
||||
const compat_version = self.base.options.compatibility_version orelse
|
||||
std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
|
||||
var dylib_cmd = try commands.createLoadDylibCommand(
|
||||
self.base.allocator,
|
||||
install_name,
|
||||
2,
|
||||
0x10000, // TODO forward user-provided versions
|
||||
0x10000,
|
||||
current_version.major << 16 | current_version.minor << 8 | current_version.patch,
|
||||
compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch,
|
||||
);
|
||||
errdefer dylib_cmd.deinit(self.base.allocator);
|
||||
dylib_cmd.inner.cmd = macho.LC_ID_DYLIB;
|
||||
|
||||
@ -38,9 +38,6 @@ id: ?Id = null,
|
||||
/// a symbol is referenced by an object file.
|
||||
symbols: std.StringArrayHashMapUnmanaged(void) = .{},
|
||||
|
||||
/// Array list of all dependent libs of this dylib.
|
||||
dependent_libs: std.ArrayListUnmanaged(Id) = .{},
|
||||
|
||||
pub const Id = struct {
|
||||
name: []const u8,
|
||||
timestamp: u32,
|
||||
@ -139,10 +136,6 @@ pub fn deinit(self: *Dylib, allocator: *Allocator) void {
|
||||
}
|
||||
self.symbols.deinit(allocator);
|
||||
|
||||
for (self.dependent_libs.items) |*id| {
|
||||
id.deinit(allocator);
|
||||
}
|
||||
self.dependent_libs.deinit(allocator);
|
||||
allocator.free(self.name);
|
||||
|
||||
if (self.id) |*id| {
|
||||
@ -150,7 +143,7 @@ pub fn deinit(self: *Dylib, allocator: *Allocator) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target) !void {
|
||||
pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target, dependent_libs: anytype) !void {
|
||||
log.debug("parsing shared library '{s}'", .{self.name});
|
||||
|
||||
self.library_offset = try fat.getLibraryOffset(self.file.reader(), target);
|
||||
@ -172,12 +165,12 @@ pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target) !void {
|
||||
return error.MismatchedCpuArchitecture;
|
||||
}
|
||||
|
||||
try self.readLoadCommands(allocator, reader);
|
||||
try self.readLoadCommands(allocator, reader, dependent_libs);
|
||||
try self.parseId(allocator);
|
||||
try self.parseSymbols(allocator);
|
||||
}
|
||||
|
||||
fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype) !void {
|
||||
fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype, dependent_libs: anytype) !void {
|
||||
const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
|
||||
|
||||
try self.load_commands.ensureUnusedCapacity(allocator, self.header.?.ncmds);
|
||||
@ -198,8 +191,8 @@ fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype) !void
|
||||
macho.LC_REEXPORT_DYLIB => {
|
||||
if (should_lookup_reexports) {
|
||||
// Parse install_name to dependent dylib.
|
||||
const id = try Id.fromLoadCommand(allocator, cmd.Dylib);
|
||||
try self.dependent_libs.append(allocator, id);
|
||||
var id = try Id.fromLoadCommand(allocator, cmd.Dylib);
|
||||
try dependent_libs.writeItem(id);
|
||||
}
|
||||
},
|
||||
else => {
|
||||
@ -341,7 +334,13 @@ const TargetMatcher = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, lib_stub: LibStub) !void {
|
||||
pub fn parseFromStub(
|
||||
self: *Dylib,
|
||||
allocator: *Allocator,
|
||||
target: std.Target,
|
||||
lib_stub: LibStub,
|
||||
dependent_libs: anytype,
|
||||
) !void {
|
||||
if (lib_stub.inner.len == 0) return error.EmptyStubFile;
|
||||
|
||||
log.debug("parsing shared library from stub '{s}'", .{self.name});
|
||||
@ -416,8 +415,8 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, li
|
||||
|
||||
log.debug(" (found re-export '{s}')", .{lib});
|
||||
|
||||
const dep_id = try Id.default(allocator, lib);
|
||||
try self.dependent_libs.append(allocator, dep_id);
|
||||
var dep_id = try Id.default(allocator, lib);
|
||||
try dependent_libs.writeItem(dep_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -521,55 +520,10 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, li
|
||||
|
||||
log.debug(" (found re-export '{s}')", .{lib});
|
||||
|
||||
const dep_id = try Id.default(allocator, lib);
|
||||
try self.dependent_libs.append(allocator, dep_id);
|
||||
var dep_id = try Id.default(allocator, lib);
|
||||
try dependent_libs.writeItem(dep_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parseDependentLibs(
|
||||
self: *Dylib,
|
||||
macho_file: *MachO,
|
||||
syslibroot: ?[]const u8,
|
||||
) !void {
|
||||
outer: for (self.dependent_libs.items) |id| {
|
||||
if (macho_file.dylibs_map.contains(id.name)) continue :outer;
|
||||
|
||||
const has_ext = blk: {
|
||||
const basename = fs.path.basename(id.name);
|
||||
break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
|
||||
};
|
||||
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(macho_file.base.allocator, "{s}{s}", .{
|
||||
without_ext,
|
||||
ext,
|
||||
});
|
||||
defer macho_file.base.allocator.free(with_ext);
|
||||
|
||||
const full_path = if (syslibroot) |root|
|
||||
try fs.path.join(macho_file.base.allocator, &.{ root, with_ext })
|
||||
else
|
||||
with_ext;
|
||||
defer if (syslibroot) |_| macho_file.base.allocator.free(full_path);
|
||||
|
||||
log.debug("trying dependency at fully resolved path {s}", .{full_path});
|
||||
|
||||
const did_parse_successfully = try macho_file.parseDylib(full_path, .{
|
||||
.id = id,
|
||||
.syslibroot = syslibroot,
|
||||
.is_dependent = true,
|
||||
});
|
||||
if (!did_parse_successfully) continue;
|
||||
} else {
|
||||
log.debug("unable to resolve dependency {s}", .{id.name});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
24
src/main.zig
24
src/main.zig
@ -564,6 +564,7 @@ fn buildOutputType(
|
||||
var root_src_file: ?[]const u8 = null;
|
||||
var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };
|
||||
var have_version = false;
|
||||
var compatibility_version: ?std.builtin.Version = null;
|
||||
var strip = false;
|
||||
var single_threaded = false;
|
||||
var function_sections = false;
|
||||
@ -1613,6 +1614,29 @@ fn buildOutputType(
|
||||
) catch |err| {
|
||||
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
|
||||
};
|
||||
} else if (mem.eql(u8, arg, "-framework") or mem.eql(u8, arg, "-weak_framework")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
try frameworks.append(linker_args.items[i]);
|
||||
} else if (mem.eql(u8, arg, "-compatibility_version")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
compatibility_version = std.builtin.Version.parse(linker_args.items[i]) catch |err| {
|
||||
fatal("unable to parse -compatibility_version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
|
||||
};
|
||||
} else if (mem.eql(u8, arg, "-current_version")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.items.len) {
|
||||
fatal("expected linker arg after '{s}'", .{arg});
|
||||
}
|
||||
version = std.builtin.Version.parse(linker_args.items[i]) catch |err| {
|
||||
fatal("unable to parse -current_version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
|
||||
};
|
||||
have_version = true;
|
||||
} else {
|
||||
warn("unsupported linker arg: {s}", .{arg});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user