mirror of
https://github.com/ziglang/zig.git
synced 2025-12-27 08:33:15 +00:00
macho: move parsing logic for Object, Archive and Dylib into MachO
This way, the functionality is better segregated, and we finally do not unnecessarily reparse dynamic libraries that were already visited and parsed.
This commit is contained in:
parent
16bb5c05f1
commit
5d548cc651
@ -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| {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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});
|
||||
}
|
||||
|
||||
@ -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| {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user