diff --git a/src/Compilation.zig b/src/Compilation.zig index 4ad4d3edfa..7a3279445e 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -699,6 +699,7 @@ pub const InitOptions = struct { rpath_list: []const []const u8 = &[0][]const u8{}, c_source_files: []const CSourceFile = &[0]CSourceFile{}, link_objects: []const []const u8 = &[0][]const u8{}, + must_link_objects: []const []const u8 = &[0][]const u8{}, framework_dirs: []const []const u8 = &[0][]const u8{}, frameworks: []const []const u8 = &[0][]const u8{}, system_lib_names: []const []const u8 = &.{}, @@ -1535,6 +1536,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .link_libcpp = link_libcpp, .link_libunwind = link_libunwind, .objects = options.link_objects, + .must_link_objects = options.must_link_objects, .frameworks = options.frameworks, .framework_dirs = options.framework_dirs, .system_libs = system_libs, diff --git a/src/link.zig b/src/link.zig index a4b990cf6b..ea12292d22 100644 --- a/src/link.zig +++ b/src/link.zig @@ -156,6 +156,7 @@ pub const Options = struct { llvm_cpu_features: ?[*:0]const u8, objects: []const []const u8, + must_link_objects: []const []const u8, framework_dirs: []const []const u8, frameworks: []const []const u8, system_libs: std.StringArrayHashMapUnmanaged(SystemLib), diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 746fe07e0e..8ba2ac973c 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -679,10 +679,20 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { // the relocatable object files. self.invalidate_relocs = true; + // Unpack must-link archives + var must_link_archives = std.StringArrayHashMap(void).init(arena); + try must_link_archives.ensureTotalCapacity(@intCast(u32, self.base.options.must_link_objects.len)); + for (self.base.options.must_link_objects) |lib| { + _ = must_link_archives.getOrPutAssumeCapacity(lib); + } + // Positional arguments to the linker such as object files and static archives. var positionals = std.ArrayList([]const u8).init(arena); - - try positionals.appendSlice(self.base.options.objects); + try positionals.ensureUnusedCapacity(self.base.options.objects.len); + for (self.base.options.objects) |obj| { + if (must_link_archives.contains(obj)) continue; + _ = positionals.appendAssumeCapacity(obj); + } for (comp.c_object_table.keys()) |key| { try positionals.append(key.status.success.object_path); @@ -889,12 +899,17 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { try argv.append("dynamic_lookup"); } + for (self.base.options.must_link_objects) |lib| { + try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib})); + } + Compilation.dump_argv(argv.items); } 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.parseAndForceLoadStaticArchives(must_link_archives.keys()); try self.parseLibs(libs.items, self.base.options.sysroot, &dependent_libs); try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs); } @@ -1203,7 +1218,7 @@ fn parseObject(self: *MachO, path: []const u8) !bool { return true; } -fn parseArchive(self: *MachO, path: []const u8) !bool { +fn parseArchive(self: *MachO, path: []const u8, force_load: bool) !bool { const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return false, else => |e| return e, @@ -1226,7 +1241,23 @@ fn parseArchive(self: *MachO, path: []const u8) !bool { else => |e| return e, }; - try self.archives.append(self.base.allocator, archive); + if (force_load) { + defer archive.deinit(self.base.allocator); + // Get all offsets from the ToC + var offsets = std.AutoArrayHashMap(u32, void).init(self.base.allocator); + defer offsets.deinit(); + for (archive.toc.values()) |offs| { + for (offs.items) |off| { + _ = try offsets.getOrPut(off); + } + } + for (offsets.keys()) |off| { + const object = try self.objects.addOne(self.base.allocator); + object.* = try archive.parseObject(self.base.allocator, self.base.options.target, off); + } + } else { + try self.archives.append(self.base.allocator, archive); + } return true; } @@ -1311,7 +1342,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const log.debug("parsing input file path '{s}'", .{full_path}); if (try self.parseObject(full_path)) continue; - if (try self.parseArchive(full_path)) continue; + if (try self.parseArchive(full_path, false)) continue; if (try self.parseDylib(full_path, .{ .syslibroot = syslibroot, .dependent_libs = dependent_libs, @@ -1321,6 +1352,21 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const } } +fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !void { + for (files) |file_name| { + const full_path = full_path: { + var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; + const path = try fs.realpath(file_name, &buffer); + break :full_path try self.base.allocator.dupe(u8, path); + }; + defer self.base.allocator.free(full_path); + log.debug("parsing and force loading static archive '{s}'", .{full_path}); + + if (try self.parseArchive(full_path, true)) continue; + log.warn("unknown filetype: expected static archive: '{s}'", .{file_name}); + } +} + 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}); @@ -1328,7 +1374,7 @@ fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8, de .syslibroot = syslibroot, .dependent_libs = dependent_libs, })) continue; - if (try self.parseArchive(lib)) continue; + if (try self.parseArchive(lib, false)) continue; log.warn("unknown filetype for a library: '{s}'", .{lib}); } diff --git a/src/main.zig b/src/main.zig index 7756f48e26..335ca21142 100644 --- a/src/main.zig +++ b/src/main.zig @@ -708,6 +708,9 @@ fn buildOutputType( var link_objects = std.ArrayList([]const u8).init(gpa); defer link_objects.deinit(); + var must_link_objects = std.ArrayList([]const u8).init(gpa); + defer must_link_objects.deinit(); + var framework_dirs = std.ArrayList([]const u8).init(gpa); defer framework_dirs.deinit(); @@ -1748,6 +1751,12 @@ fn buildOutputType( fatal("expected linker arg after '{s}'", .{arg}); } install_name = linker_args.items[i]; + } else if (mem.eql(u8, arg, "-force_load")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{s}'", .{arg}); + } + try must_link_objects.append(linker_args.items[i]); } else { warn("unsupported linker arg: {s}", .{arg}); } @@ -2438,6 +2447,7 @@ fn buildOutputType( .rpath_list = rpath_list.items, .c_source_files = c_source_files.items, .link_objects = link_objects.items, + .must_link_objects = must_link_objects.items, .framework_dirs = framework_dirs.items, .frameworks = frameworks.items, .system_lib_names = system_libs.keys(),