authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-05-17 17:59:38+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-05-18 09:28:00+02:00
log1dac5f5214aa9e35ad0fbc2ba561a894bd193ae3
tree5badfc9bca79b7f59780648cd504203e66097c0d
parentca772735c3d01c58551639d8bcfe355bc7ac9785

zld: parse dylibs as positionals

* add preliminary rpath support * enable shared_library test on x86_64 macOS

5 files changed, 119 insertions(+), 8 deletions(-)

lib/std/macho.zig+13
...@@ -516,6 +516,19 @@ pub const dylib = extern struct {...@@ -516,6 +516,19 @@ pub const dylib = extern struct {
516 compatibility_version: u32,516 compatibility_version: u32,
517};517};
518518
519/// The rpath_command contains a path which at runtime should be added to the current
520/// run path used to find @rpath prefixed dylibs.
521pub const rpath_command = extern struct {
522 /// LC_RPATH
523 cmd: u32,
524
525 /// includes string
526 cmdsize: u32,
527
528 /// path to add to run path
529 path: u32,
530};
531
519/// The segment load command indicates that a part of this file is to be532/// The segment load command indicates that a part of this file is to be
520/// mapped into the task's address space. The size of this segment in memory,533/// mapped into the task's address space. The size of this segment in memory,
521/// vmsize, maybe equal to or larger than the amount to map from this file,534/// vmsize, maybe equal to or larger than the amount to map from this file,
src/link/MachO.zig+22-1
...@@ -756,6 +756,19 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -756,6 +756,19 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
756 }756 }
757 }757 }
758758
759 // rpaths
760 var rpath_table = std.StringArrayHashMap(void).init(arena);
761 for (self.base.options.rpath_list) |rpath| {
762 if (rpath_table.contains(rpath)) continue;
763 try rpath_table.putNoClobber(rpath, {});
764 }
765
766 var rpaths = std.ArrayList([]const u8) .init(arena);
767 try rpaths.ensureCapacity(rpath_table.count());
768 for (rpath_table.items()) |entry| {
769 rpaths.appendAssumeCapacity(entry.key);
770 }
771
759 if (self.base.options.verbose_link) {772 if (self.base.options.verbose_link) {
760 var argv = std.ArrayList([]const u8).init(arena);773 var argv = std.ArrayList([]const u8).init(arena);
761774
...@@ -767,6 +780,11 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -767,6 +780,11 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
767 try argv.append(syslibroot);780 try argv.append(syslibroot);
768 }781 }
769782
783 for (rpaths.items) |rpath| {
784 try argv.append("-rpath");
785 try argv.append(rpath);
786 }
787
770 try argv.appendSlice(positionals.items);788 try argv.appendSlice(positionals.items);
771789
772 try argv.append("-o");790 try argv.append("-o");
...@@ -783,7 +801,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -783,7 +801,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
783 Compilation.dump_argv(argv.items);801 Compilation.dump_argv(argv.items);
784 }802 }
785803
786 try zld.link(positionals.items, shared_libs.items, full_out_path);804 try zld.link(positionals.items, full_out_path, .{
805 .shared_libs = shared_libs.items,
806 .rpaths = rpaths.items,
807 });
787808
788 break :outer;809 break :outer;
789 }810 }
src/link/MachO/Zld.zig+74-3
...@@ -186,7 +186,12 @@ pub fn closeFiles(self: Zld) void {...@@ -186,7 +186,12 @@ pub fn closeFiles(self: Zld) void {
186 if (self.file) |f| f.close();186 if (self.file) |f| f.close();
187}187}
188188
189pub fn link(self: *Zld, files: []const []const u8, shared_libs: []const []const u8, out_path: []const u8) !void {189const LinkArgs = struct {
190 shared_libs: []const []const u8,
191 rpaths: []const []const u8,
192};
193
194pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void {
190 if (files.len == 0) return error.NoInputFiles;195 if (files.len == 0) return error.NoInputFiles;
191 if (out_path.len == 0) return error.EmptyOutputPath;196 if (out_path.len == 0) return error.EmptyOutputPath;
192197
...@@ -222,8 +227,9 @@ pub fn link(self: *Zld, files: []const []const u8, shared_libs: []const []const...@@ -222,8 +227,9 @@ pub fn link(self: *Zld, files: []const []const u8, shared_libs: []const []const
222 });227 });
223228
224 try self.populateMetadata();229 try self.populateMetadata();
230 try self.addRpaths(args.rpaths);
225 try self.parseInputFiles(files);231 try self.parseInputFiles(files);
226 try self.parseDylibs(shared_libs);232 try self.parseDylibs(args.shared_libs);
227 try self.resolveSymbols();233 try self.resolveSymbols();
228 try self.resolveStubsAndGotEntries();234 try self.resolveStubsAndGotEntries();
229 try self.updateMetadata();235 try self.updateMetadata();
...@@ -241,6 +247,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -241,6 +247,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
241 kind: enum {247 kind: enum {
242 object,248 object,
243 archive,249 archive,
250 dylib,
244 },251 },
245 file: fs.File,252 file: fs.File,
246 name: []const u8,253 name: []const u8,
...@@ -248,7 +255,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -248,7 +255,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
248 var classified = std.ArrayList(Input).init(self.allocator);255 var classified = std.ArrayList(Input).init(self.allocator);
249 defer classified.deinit();256 defer classified.deinit();
250257
251 // First, classify input files as either object or archive.258 // First, classify input files: object, archive or dylib.
252 for (files) |file_name| {259 for (files) |file_name| {
253 const file = try fs.cwd().openFile(file_name, .{});260 const file = try fs.cwd().openFile(file_name, .{});
254 const full_path = full_path: {261 const full_path = full_path: {
...@@ -289,6 +296,22 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -289,6 +296,22 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
289 continue;296 continue;
290 }297 }
291298
299 try_dylib: {
300 const header = try file.reader().readStruct(macho.mach_header_64);
301 if (header.filetype != macho.MH_DYLIB) {
302 try file.seekTo(0);
303 break :try_dylib;
304 }
305
306 try file.seekTo(0);
307 try classified.append(.{
308 .kind = .dylib,
309 .file = file,
310 .name = full_path,
311 });
312 continue;
313 }
314
292 log.debug("unexpected input file of unknown type '{s}'", .{file_name});315 log.debug("unexpected input file of unknown type '{s}'", .{file_name});
293 }316 }
294317
...@@ -317,6 +340,35 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -317,6 +340,35 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
317 try archive.parse();340 try archive.parse();
318 try self.archives.append(self.allocator, archive);341 try self.archives.append(self.allocator, archive);
319 },342 },
343 .dylib => {
344 const dylib = try self.allocator.create(Dylib);
345 errdefer self.allocator.destroy(dylib);
346
347 dylib.* = Dylib.init(self.allocator);
348 dylib.arch = self.arch.?;
349 dylib.name = input.name;
350 dylib.file = input.file;
351
352 const ordinal = @intCast(u16, self.dylibs.items.len);
353 dylib.ordinal = ordinal + 2; // TODO +2 since 1 is reserved for libSystem
354
355 // TODO Defer parsing of the dylibs until they are actually needed
356 try dylib.parse();
357 try self.dylibs.append(self.allocator, dylib);
358
359 // Add LC_LOAD_DYLIB command
360 const dylib_id = dylib.id orelse unreachable;
361 var dylib_cmd = try createLoadDylibCommand(
362 self.allocator,
363 dylib_id.name,
364 dylib_id.timestamp,
365 dylib_id.current_version,
366 dylib_id.compatibility_version,
367 );
368 errdefer dylib_cmd.deinit(self.allocator);
369
370 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
371 },
320 }372 }
321 }373 }
322}374}
...@@ -2117,6 +2169,25 @@ fn populateMetadata(self: *Zld) !void {...@@ -2117,6 +2169,25 @@ fn populateMetadata(self: *Zld) !void {
2117 }2169 }
2118}2170}
21192171
2172fn addRpaths(self: *Zld, rpaths: []const []const u8) !void {
2173 for (rpaths) |rpath| {
2174 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2175 u64,
2176 @sizeOf(macho.rpath_command) + rpath.len,
2177 @sizeOf(u64),
2178 ));
2179 var rpath_cmd = emptyGenericCommandWithData(macho.rpath_command{
2180 .cmd = macho.LC_RPATH,
2181 .cmdsize = cmdsize,
2182 .path = @sizeOf(macho.rpath_command),
2183 });
2184 rpath_cmd.data = try self.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path);
2185 mem.set(u8, rpath_cmd.data, 0);
2186 mem.copy(u8, rpath_cmd.data, rpath);
2187 try self.load_commands.append(self.allocator, .{ .Rpath = rpath_cmd });
2188 }
2189}
2190
2120fn flush(self: *Zld) !void {2191fn flush(self: *Zld) !void {
2121 try self.writeStubHelperCommon();2192 try self.writeStubHelperCommon();
2122 try self.resolveRelocsAndWriteSections();2193 try self.resolveRelocsAndWriteSections();
src/link/MachO/commands.zig+9
...@@ -24,6 +24,7 @@ pub const LoadCommand = union(enum) {...@@ -24,6 +24,7 @@ pub const LoadCommand = union(enum) {
24 SourceVersion: macho.source_version_command,24 SourceVersion: macho.source_version_command,
25 Uuid: macho.uuid_command,25 Uuid: macho.uuid_command,
26 LinkeditData: macho.linkedit_data_command,26 LinkeditData: macho.linkedit_data_command,
27 Rpath: GenericCommandWithData(macho.rpath_command),
27 Unknown: GenericCommandWithData(macho.load_command),28 Unknown: GenericCommandWithData(macho.load_command),
2829
29 pub fn read(allocator: *Allocator, reader: anytype) !LoadCommand {30 pub fn read(allocator: *Allocator, reader: anytype) !LoadCommand {
...@@ -84,6 +85,9 @@ pub const LoadCommand = union(enum) {...@@ -84,6 +85,9 @@ pub const LoadCommand = union(enum) {
84 => LoadCommand{85 => LoadCommand{
85 .LinkeditData = try stream.reader().readStruct(macho.linkedit_data_command),86 .LinkeditData = try stream.reader().readStruct(macho.linkedit_data_command),
86 },87 },
88 macho.LC_RPATH => LoadCommand{
89 .Rpath = try GenericCommandWithData(macho.rpath_command).read(allocator, stream.reader()),
90 },
87 else => LoadCommand{91 else => LoadCommand{
88 .Unknown = try GenericCommandWithData(macho.load_command).read(allocator, stream.reader()),92 .Unknown = try GenericCommandWithData(macho.load_command).read(allocator, stream.reader()),
89 },93 },
...@@ -103,6 +107,7 @@ pub const LoadCommand = union(enum) {...@@ -103,6 +107,7 @@ pub const LoadCommand = union(enum) {
103 .Segment => |x| x.write(writer),107 .Segment => |x| x.write(writer),
104 .Dylinker => |x| x.write(writer),108 .Dylinker => |x| x.write(writer),
105 .Dylib => |x| x.write(writer),109 .Dylib => |x| x.write(writer),
110 .Rpath => |x| x.write(writer),
106 .Unknown => |x| x.write(writer),111 .Unknown => |x| x.write(writer),
107 };112 };
108 }113 }
...@@ -120,6 +125,7 @@ pub const LoadCommand = union(enum) {...@@ -120,6 +125,7 @@ pub const LoadCommand = union(enum) {
120 .Segment => |x| x.inner.cmd,125 .Segment => |x| x.inner.cmd,
121 .Dylinker => |x| x.inner.cmd,126 .Dylinker => |x| x.inner.cmd,
122 .Dylib => |x| x.inner.cmd,127 .Dylib => |x| x.inner.cmd,
128 .Rpath => |x| x.inner.cmd,
123 .Unknown => |x| x.inner.cmd,129 .Unknown => |x| x.inner.cmd,
124 };130 };
125 }131 }
...@@ -137,6 +143,7 @@ pub const LoadCommand = union(enum) {...@@ -137,6 +143,7 @@ pub const LoadCommand = union(enum) {
137 .Segment => |x| x.inner.cmdsize,143 .Segment => |x| x.inner.cmdsize,
138 .Dylinker => |x| x.inner.cmdsize,144 .Dylinker => |x| x.inner.cmdsize,
139 .Dylib => |x| x.inner.cmdsize,145 .Dylib => |x| x.inner.cmdsize,
146 .Rpath => |x| x.inner.cmdsize,
140 .Unknown => |x| x.inner.cmdsize,147 .Unknown => |x| x.inner.cmdsize,
141 };148 };
142 }149 }
...@@ -146,6 +153,7 @@ pub const LoadCommand = union(enum) {...@@ -146,6 +153,7 @@ pub const LoadCommand = union(enum) {
146 .Segment => |*x| x.deinit(allocator),153 .Segment => |*x| x.deinit(allocator),
147 .Dylinker => |*x| x.deinit(allocator),154 .Dylinker => |*x| x.deinit(allocator),
148 .Dylib => |*x| x.deinit(allocator),155 .Dylib => |*x| x.deinit(allocator),
156 .Rpath => |*x| x.deinit(allocator),
149 .Unknown => |*x| x.deinit(allocator),157 .Unknown => |*x| x.deinit(allocator),
150 else => {},158 else => {},
151 };159 };
...@@ -169,6 +177,7 @@ pub const LoadCommand = union(enum) {...@@ -169,6 +177,7 @@ pub const LoadCommand = union(enum) {
169 .Segment => |x| x.eql(other.Segment),177 .Segment => |x| x.eql(other.Segment),
170 .Dylinker => |x| x.eql(other.Dylinker),178 .Dylinker => |x| x.eql(other.Dylinker),
171 .Dylib => |x| x.eql(other.Dylib),179 .Dylib => |x| x.eql(other.Dylib),
180 .Rpath => |x| x.eql(other.Rpath),
172 .Unknown => |x| x.eql(other.Unknown),181 .Unknown => |x| x.eql(other.Unknown),
173 };182 };
174 }183 }
test/standalone.zig+1-4
...@@ -9,10 +9,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -9,10 +9,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
9 cases.add("test/standalone/main_return_error/error_u8.zig");9 cases.add("test/standalone/main_return_error/error_u8.zig");
10 cases.add("test/standalone/main_return_error/error_u8_non_zero.zig");10 cases.add("test/standalone/main_return_error/error_u8_non_zero.zig");
11 cases.addBuildFile("test/standalone/main_pkg_path/build.zig");11 cases.addBuildFile("test/standalone/main_pkg_path/build.zig");
12 if (std.Target.current.os.tag != .macos) {12 cases.addBuildFile("test/standalone/shared_library/build.zig");
13 // TODO zld cannot link shared libraries yet.
14 cases.addBuildFile("test/standalone/shared_library/build.zig");
15 }
16 cases.addBuildFile("test/standalone/mix_o_files/build.zig");13 cases.addBuildFile("test/standalone/mix_o_files/build.zig");
17 cases.addBuildFile("test/standalone/global_linkage/build.zig");14 cases.addBuildFile("test/standalone/global_linkage/build.zig");
18 cases.addBuildFile("test/standalone/static_c_lib/build.zig");15 cases.addBuildFile("test/standalone/static_c_lib/build.zig");