aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/link/MachO.zig174
-rw-r--r--src/link/MachO/Archive.zig26
-rw-r--r--src/link/MachO/Dylib.zig119
-rw-r--r--src/link/MachO/Object.zig26
4 files changed, 155 insertions, 190 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index d23b696cb8..6fbe3bc150 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -31,6 +31,7 @@ const DebugSymbols = @import("MachO/DebugSymbols.zig");
const Dylib = @import("MachO/Dylib.zig");
const File = link.File;
const Object = @import("MachO/Object.zig");
+const LibStub = @import("tapi.zig").LibStub;
const Liveness = @import("../Liveness.zig");
const LlvmObject = @import("../codegen/llvm.zig").Object;
const LoadCommand = commands.LoadCommand;
@@ -65,6 +66,7 @@ objects: std.ArrayListUnmanaged(Object) = .{},
archives: std.ArrayListUnmanaged(Archive) = .{},
dylibs: std.ArrayListUnmanaged(Dylib) = .{},
+dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
@@ -994,6 +996,133 @@ fn linkWithZld(self: *MachO, comp: *Compilation) !void {
}
}
+fn parseObject(self: *MachO, path: []const u8) !bool {
+ const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
+ error.FileNotFound => return false,
+ else => |e| return e,
+ };
+ errdefer file.close();
+
+ const name = try self.base.allocator.dupe(u8, path);
+ errdefer self.base.allocator.free(name);
+
+ var object = Object{
+ .name = name,
+ .file = file,
+ };
+
+ object.parse(self.base.allocator, self.base.options.target) catch |err| switch (err) {
+ error.EndOfStream, error.NotObject => {
+ object.deinit(self.base.allocator);
+ return false;
+ },
+ else => |e| return e,
+ };
+
+ try self.objects.append(self.base.allocator, object);
+
+ return true;
+}
+
+fn parseArchive(self: *MachO, path: []const u8) !bool {
+ const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
+ error.FileNotFound => return false,
+ else => |e| return e,
+ };
+ errdefer file.close();
+
+ const name = try self.base.allocator.dupe(u8, path);
+ errdefer self.base.allocator.free(name);
+
+ var archive = Archive{
+ .name = name,
+ .file = file,
+ };
+
+ archive.parse(self.base.allocator, self.base.options.target) catch |err| switch (err) {
+ error.EndOfStream, error.NotArchive => {
+ archive.deinit(self.base.allocator);
+ return false;
+ },
+ else => |e| return e,
+ };
+
+ try self.archives.append(self.base.allocator, archive);
+
+ return true;
+}
+
+const ParseDylibError = error{
+ OutOfMemory,
+ EmptyStubFile,
+ MismatchedCpuArchitecture,
+ UnsupportedCpuArchitecture,
+} || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;
+
+const DylibCreateOpts = struct {
+ syslibroot: ?[]const u8 = null,
+ id: ?Dylib.Id = null,
+ is_dependent: bool = false,
+};
+
+pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDylibError!bool {
+ const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
+ error.FileNotFound => return false,
+ else => |e| return e,
+ };
+ errdefer file.close();
+
+ const name = try self.base.allocator.dupe(u8, path);
+ errdefer self.base.allocator.free(name);
+
+ var dylib = Dylib{
+ .name = name,
+ .file = file,
+ };
+
+ dylib.parse(self.base.allocator, self.base.options.target) catch |err| switch (err) {
+ error.EndOfStream, error.NotDylib => {
+ try file.seekTo(0);
+
+ var lib_stub = LibStub.loadFromFile(self.base.allocator, file) catch {
+ dylib.deinit(self.base.allocator);
+ return false;
+ };
+ defer lib_stub.deinit();
+
+ try dylib.parseFromStub(self.base.allocator, self.base.options.target, lib_stub);
+ },
+ 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(self.base.allocator);
+ return false;
+ }
+ }
+
+ const dylib_id = @intCast(u16, self.dylibs.items.len);
+ try self.dylibs.append(self.base.allocator, dylib);
+ try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id);
+
+ if (!(opts.is_dependent or self.referenced_dylibs.contains(dylib_id))) {
+ 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 {
for (files) |file_name| {
const full_path = full_path: {
@@ -1003,28 +1132,11 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
};
defer self.base.allocator.free(full_path);
- if (try Object.createAndParseFromPath(self.base.allocator, self.base.options.target, full_path)) |object| {
- try self.objects.append(self.base.allocator, object);
- continue;
- }
-
- if (try Archive.createAndParseFromPath(self.base.allocator, self.base.options.target, full_path)) |archive| {
- try self.archives.append(self.base.allocator, archive);
- continue;
- }
-
- if (try Dylib.createAndParseFromPath(self.base.allocator, self.base.options.target, full_path, .{
+ if (try self.parseObject(full_path)) continue;
+ if (try self.parseArchive(full_path)) continue;
+ if (try self.parseDylib(full_path, .{
.syslibroot = syslibroot,
- })) |dylibs| {
- defer self.base.allocator.free(dylibs);
- const dylib_id = @intCast(u16, self.dylibs.items.len);
- try self.dylibs.appendSlice(self.base.allocator, dylibs);
- // We always have to add the dylib that was on the linker line.
- if (!self.referenced_dylibs.contains(dylib_id)) {
- try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
- }
- continue;
- }
+ })) continue;
log.warn("unknown filetype for positional input file: '{s}'", .{file_name});
}
@@ -1032,23 +1144,10 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !void {
for (libs) |lib| {
- if (try Dylib.createAndParseFromPath(self.base.allocator, self.base.options.target, lib, .{
+ if (try self.parseDylib(lib, .{
.syslibroot = syslibroot,
- })) |dylibs| {
- defer self.base.allocator.free(dylibs);
- const dylib_id = @intCast(u16, self.dylibs.items.len);
- try self.dylibs.appendSlice(self.base.allocator, dylibs);
- // We always have to add the dylib that was on the linker line.
- if (!self.referenced_dylibs.contains(dylib_id)) {
- try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
- }
- continue;
- }
-
- if (try Archive.createAndParseFromPath(self.base.allocator, self.base.options.target, lib)) |archive| {
- try self.archives.append(self.base.allocator, archive);
- continue;
- }
+ })) continue;
+ if (try self.parseArchive(lib)) continue;
log.warn("unknown filetype for a library: '{s}'", .{lib});
}
@@ -3360,6 +3459,7 @@ pub fn deinit(self: *MachO) void {
dylib.deinit(self.base.allocator);
}
self.dylibs.deinit(self.base.allocator);
+ self.dylibs_map.deinit(self.base.allocator);
self.referenced_dylibs.deinit(self.base.allocator);
for (self.load_commands.items) |*lc| {
diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig
index 3b01233e1f..1f370088f0 100644
--- a/src/link/MachO/Archive.zig
+++ b/src/link/MachO/Archive.zig
@@ -103,32 +103,6 @@ pub fn deinit(self: *Archive, allocator: *Allocator) void {
allocator.free(self.name);
}
-pub fn createAndParseFromPath(allocator: *Allocator, target: std.Target, path: []const u8) !?Archive {
- const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
- error.FileNotFound => return null,
- else => |e| return e,
- };
- errdefer file.close();
-
- const name = try allocator.dupe(u8, path);
- errdefer allocator.free(name);
-
- var archive = Archive{
- .name = name,
- .file = file,
- };
-
- archive.parse(allocator, target) catch |err| switch (err) {
- error.EndOfStream, error.NotArchive => {
- archive.deinit(allocator);
- return null;
- },
- else => |e| return e,
- };
-
- return archive;
-}
-
pub fn parse(self: *Archive, allocator: *Allocator, target: std.Target) !void {
const reader = self.file.reader();
self.library_offset = try fat.getLibraryOffset(reader, target);
diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig
index bdf3cfe6bb..05d44559ce 100644
--- a/src/link/MachO/Dylib.zig
+++ b/src/link/MachO/Dylib.zig
@@ -10,10 +10,9 @@ const math = std.math;
const mem = std.mem;
const fat = @import("fat.zig");
const commands = @import("commands.zig");
-const tapi = @import("../tapi.zig");
const Allocator = mem.Allocator;
-const LibStub = tapi.LibStub;
+const LibStub = @import("../tapi.zig").LibStub;
const LoadCommand = commands.LoadCommand;
const MachO = @import("../MachO.zig");
@@ -74,7 +73,7 @@ pub const Id = struct {
allocator.free(id.name);
}
- const ParseError = fmt.ParseIntError || fmt.BufPrintError;
+ pub const ParseError = fmt.ParseIntError || fmt.BufPrintError;
pub fn parseCurrentVersion(id: *Id, version: anytype) ParseError!void {
id.current_version = try parseVersion(version);
@@ -110,7 +109,7 @@ pub const Id = struct {
var count: u4 = 0;
while (split.next()) |value| {
if (count > 2) {
- log.warn("malformed version field: {s}", .{string});
+ log.debug("malformed version field: {s}", .{string});
return 0x10000;
}
values[count] = value;
@@ -129,78 +128,6 @@ pub const Id = struct {
}
};
-pub const Error = error{
- OutOfMemory,
- EmptyStubFile,
- MismatchedCpuArchitecture,
- UnsupportedCpuArchitecture,
-} || fs.File.OpenError || std.os.PReadError || Id.ParseError;
-
-pub const CreateOpts = struct {
- syslibroot: ?[]const u8 = null,
- id: ?Id = null,
- target: ?std.Target = null,
-};
-
-pub fn createAndParseFromPath(
- allocator: *Allocator,
- target: std.Target,
- 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,
- };
- errdefer file.close();
-
- const name = try allocator.dupe(u8, path);
- errdefer allocator.free(name);
-
- var dylib = Dylib{
- .name = name,
- .file = file,
- };
-
- dylib.parse(allocator, target) catch |err| switch (err) {
- error.EndOfStream, error.NotDylib => {
- try file.seekTo(0);
-
- var lib_stub = LibStub.loadFromFile(allocator, file) catch {
- dylib.deinit(allocator);
- return null;
- };
- defer lib_stub.deinit();
-
- try dylib.parseFromStub(allocator, target, lib_stub);
- },
- 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);
- return null;
- }
- }
-
- var dylibs = std.ArrayList(Dylib).init(allocator);
- defer dylibs.deinit();
-
- try dylibs.append(dylib);
- // TODO this should not be performed if the user specifies `-flat_namespace` flag.
- // See ld64 manpages.
- try dylib.parseDependentLibs(allocator, target, &dylibs, opts.syslibroot);
-
- return dylibs.toOwnedSlice();
-}
-
pub fn deinit(self: *Dylib, allocator: *Allocator) void {
for (self.load_commands.items) |*lc| {
lc.deinit(allocator);
@@ -421,7 +348,7 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, li
var umbrella_libs = std.StringHashMap(void).init(allocator);
defer umbrella_libs.deinit();
- log.debug("found umbrella lib '{s}'", .{umbrella_lib.installName()});
+ log.debug(" (install_name '{s}')", .{umbrella_lib.installName()});
var matcher = try TargetMatcher.init(allocator, target);
defer matcher.deinit();
@@ -520,7 +447,7 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, li
// For V4, we add dependent libs in a separate pass since some stubs such as libSystem include
// re-exports directly in the stub file.
- for (lib_stub.inner) |elem, stub_index| {
+ for (lib_stub.inner) |elem| {
if (elem == .v3) break;
const stub = elem.v4;
@@ -544,12 +471,12 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, li
pub fn parseDependentLibs(
self: *Dylib,
- allocator: *Allocator,
- target: std.Target,
- out: *std.ArrayList(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;
@@ -561,36 +488,26 @@ pub fn parseDependentLibs(
} else id.name;
for (&[_][]const u8{ extension, ".tbd" }) |ext| {
- const with_ext = try std.fmt.allocPrint(allocator, "{s}{s}", .{
+ const with_ext = try std.fmt.allocPrint(macho_file.base.allocator, "{s}{s}", .{
without_ext,
ext,
});
- defer allocator.free(with_ext);
+ defer macho_file.base.allocator.free(with_ext);
const full_path = if (syslibroot) |root|
- try fs.path.join(allocator, &.{ root, with_ext })
+ try fs.path.join(macho_file.base.allocator, &.{ root, with_ext })
else
with_ext;
- defer if (syslibroot) |_| allocator.free(full_path);
+ defer if (syslibroot) |_| macho_file.base.allocator.free(full_path);
log.debug("trying dependency at fully resolved path {s}", .{full_path});
- const dylibs = (try createAndParseFromPath(
- allocator,
- target,
- full_path,
- .{
- .id = id,
- .syslibroot = syslibroot,
- },
- )) orelse {
- continue;
- };
- defer allocator.free(dylibs);
-
- try out.appendSlice(dylibs);
-
- continue :outer;
+ 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});
}
diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig
index c6aa2fb631..06c76b259d 100644
--- a/src/link/MachO/Object.zig
+++ b/src/link/MachO/Object.zig
@@ -153,32 +153,6 @@ pub fn deinit(self: *Object, allocator: *Allocator) void {
}
}
-pub fn createAndParseFromPath(allocator: *Allocator, target: std.Target, path: []const u8) !?Object {
- const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
- error.FileNotFound => return null,
- else => |e| return e,
- };
- errdefer file.close();
-
- const name = try allocator.dupe(u8, path);
- errdefer allocator.free(name);
-
- var object = Object{
- .name = name,
- .file = file,
- };
-
- object.parse(allocator, target) catch |err| switch (err) {
- error.EndOfStream, error.NotObject => {
- object.deinit(allocator);
- return null;
- },
- else => |e| return e,
- };
-
- return object;
-}
-
pub fn parse(self: *Object, allocator: *Allocator, target: std.Target) !void {
const reader = self.file.reader();
if (self.file_offset) |offset| {