Commit de8e612455
src/link/MachO/Dylib.zig
@@ -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});
- }
- }
-}
src/link/MachO.zig
@@ -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,
src/main.zig
@@ -1613,6 +1613,12 @@ fn buildOutputType(
) catch |err| {
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
};
+ } else if (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 {
warn("unsupported linker arg: {s}", .{arg});
}