From 1dac5f5214aa9e35ad0fbc2ba561a894bd193ae3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 17 May 2021 17:59:38 +0200 Subject: zld: parse dylibs as positionals * add preliminary rpath support * enable shared_library test on x86_64 macOS --- src/link/MachO.zig | 23 +++++++++++++- src/link/MachO/Zld.zig | 77 +++++++++++++++++++++++++++++++++++++++++++-- src/link/MachO/commands.zig | 9 ++++++ 3 files changed, 105 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/link/MachO.zig b/src/link/MachO.zig index c9b6197ddf..598c41330d 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -756,6 +756,19 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { } } + // rpaths + var rpath_table = std.StringArrayHashMap(void).init(arena); + for (self.base.options.rpath_list) |rpath| { + if (rpath_table.contains(rpath)) continue; + try rpath_table.putNoClobber(rpath, {}); + } + + var rpaths = std.ArrayList([]const u8) .init(arena); + try rpaths.ensureCapacity(rpath_table.count()); + for (rpath_table.items()) |entry| { + rpaths.appendAssumeCapacity(entry.key); + } + if (self.base.options.verbose_link) { var argv = std.ArrayList([]const u8).init(arena); @@ -767,6 +780,11 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { try argv.append(syslibroot); } + for (rpaths.items) |rpath| { + try argv.append("-rpath"); + try argv.append(rpath); + } + try argv.appendSlice(positionals.items); try argv.append("-o"); @@ -783,7 +801,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { Compilation.dump_argv(argv.items); } - try zld.link(positionals.items, shared_libs.items, full_out_path); + try zld.link(positionals.items, full_out_path, .{ + .shared_libs = shared_libs.items, + .rpaths = rpaths.items, + }); break :outer; } diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 4c6ac8600c..cc39ce1047 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -186,7 +186,12 @@ pub fn closeFiles(self: Zld) void { if (self.file) |f| f.close(); } -pub fn link(self: *Zld, files: []const []const u8, shared_libs: []const []const u8, out_path: []const u8) !void { +const LinkArgs = struct { + shared_libs: []const []const u8, + rpaths: []const []const u8, +}; + +pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void { if (files.len == 0) return error.NoInputFiles; if (out_path.len == 0) return error.EmptyOutputPath; @@ -222,8 +227,9 @@ pub fn link(self: *Zld, files: []const []const u8, shared_libs: []const []const }); try self.populateMetadata(); + try self.addRpaths(args.rpaths); try self.parseInputFiles(files); - try self.parseDylibs(shared_libs); + try self.parseDylibs(args.shared_libs); try self.resolveSymbols(); try self.resolveStubsAndGotEntries(); try self.updateMetadata(); @@ -241,6 +247,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { kind: enum { object, archive, + dylib, }, file: fs.File, name: []const u8, @@ -248,7 +255,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { var classified = std.ArrayList(Input).init(self.allocator); defer classified.deinit(); - // First, classify input files as either object or archive. + // First, classify input files: object, archive or dylib. for (files) |file_name| { const file = try fs.cwd().openFile(file_name, .{}); const full_path = full_path: { @@ -289,6 +296,22 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { continue; } + try_dylib: { + const header = try file.reader().readStruct(macho.mach_header_64); + if (header.filetype != macho.MH_DYLIB) { + try file.seekTo(0); + break :try_dylib; + } + + try file.seekTo(0); + try classified.append(.{ + .kind = .dylib, + .file = file, + .name = full_path, + }); + continue; + } + log.debug("unexpected input file of unknown type '{s}'", .{file_name}); } @@ -317,6 +340,35 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { try archive.parse(); try self.archives.append(self.allocator, archive); }, + .dylib => { + const dylib = try self.allocator.create(Dylib); + errdefer self.allocator.destroy(dylib); + + dylib.* = Dylib.init(self.allocator); + dylib.arch = self.arch.?; + dylib.name = input.name; + dylib.file = input.file; + + const ordinal = @intCast(u16, self.dylibs.items.len); + dylib.ordinal = ordinal + 2; // TODO +2 since 1 is reserved for libSystem + + // TODO Defer parsing of the dylibs until they are actually needed + try dylib.parse(); + try self.dylibs.append(self.allocator, dylib); + + // Add LC_LOAD_DYLIB command + const dylib_id = dylib.id orelse unreachable; + var dylib_cmd = try createLoadDylibCommand( + self.allocator, + dylib_id.name, + dylib_id.timestamp, + dylib_id.current_version, + dylib_id.compatibility_version, + ); + errdefer dylib_cmd.deinit(self.allocator); + + try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd }); + }, } } } @@ -2117,6 +2169,25 @@ fn populateMetadata(self: *Zld) !void { } } +fn addRpaths(self: *Zld, rpaths: []const []const u8) !void { + for (rpaths) |rpath| { + const cmdsize = @intCast(u32, mem.alignForwardGeneric( + u64, + @sizeOf(macho.rpath_command) + rpath.len, + @sizeOf(u64), + )); + var rpath_cmd = emptyGenericCommandWithData(macho.rpath_command{ + .cmd = macho.LC_RPATH, + .cmdsize = cmdsize, + .path = @sizeOf(macho.rpath_command), + }); + rpath_cmd.data = try self.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path); + mem.set(u8, rpath_cmd.data, 0); + mem.copy(u8, rpath_cmd.data, rpath); + try self.load_commands.append(self.allocator, .{ .Rpath = rpath_cmd }); + } +} + fn flush(self: *Zld) !void { try self.writeStubHelperCommon(); try self.resolveRelocsAndWriteSections(); diff --git a/src/link/MachO/commands.zig b/src/link/MachO/commands.zig index 6370d89770..93e6890a31 100644 --- a/src/link/MachO/commands.zig +++ b/src/link/MachO/commands.zig @@ -24,6 +24,7 @@ pub const LoadCommand = union(enum) { SourceVersion: macho.source_version_command, Uuid: macho.uuid_command, LinkeditData: macho.linkedit_data_command, + Rpath: GenericCommandWithData(macho.rpath_command), Unknown: GenericCommandWithData(macho.load_command), pub fn read(allocator: *Allocator, reader: anytype) !LoadCommand { @@ -84,6 +85,9 @@ pub const LoadCommand = union(enum) { => LoadCommand{ .LinkeditData = try stream.reader().readStruct(macho.linkedit_data_command), }, + macho.LC_RPATH => LoadCommand{ + .Rpath = try GenericCommandWithData(macho.rpath_command).read(allocator, stream.reader()), + }, else => LoadCommand{ .Unknown = try GenericCommandWithData(macho.load_command).read(allocator, stream.reader()), }, @@ -103,6 +107,7 @@ pub const LoadCommand = union(enum) { .Segment => |x| x.write(writer), .Dylinker => |x| x.write(writer), .Dylib => |x| x.write(writer), + .Rpath => |x| x.write(writer), .Unknown => |x| x.write(writer), }; } @@ -120,6 +125,7 @@ pub const LoadCommand = union(enum) { .Segment => |x| x.inner.cmd, .Dylinker => |x| x.inner.cmd, .Dylib => |x| x.inner.cmd, + .Rpath => |x| x.inner.cmd, .Unknown => |x| x.inner.cmd, }; } @@ -137,6 +143,7 @@ pub const LoadCommand = union(enum) { .Segment => |x| x.inner.cmdsize, .Dylinker => |x| x.inner.cmdsize, .Dylib => |x| x.inner.cmdsize, + .Rpath => |x| x.inner.cmdsize, .Unknown => |x| x.inner.cmdsize, }; } @@ -146,6 +153,7 @@ pub const LoadCommand = union(enum) { .Segment => |*x| x.deinit(allocator), .Dylinker => |*x| x.deinit(allocator), .Dylib => |*x| x.deinit(allocator), + .Rpath => |*x| x.deinit(allocator), .Unknown => |*x| x.deinit(allocator), else => {}, }; @@ -169,6 +177,7 @@ pub const LoadCommand = union(enum) { .Segment => |x| x.eql(other.Segment), .Dylinker => |x| x.eql(other.Dylinker), .Dylib => |x| x.eql(other.Dylib), + .Rpath => |x| x.eql(other.Rpath), .Unknown => |x| x.eql(other.Unknown), }; } -- cgit v1.2.3