authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-05-22 13:08:00+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-05-22 16:12:45+02:00
log2b0d322ea003907dc2111864cea259e5e0043328
tree8707c105b098f8e1aa504cc3adcaeaaf4cfdf244
parentf4101c1153980b887e9aa8850ac0a9dd88192140

zld: permit system static libs

This commits permits passing in static archives using the system lib flag `-la`. With this commit, `zig ld` will now look firstly for a dynamic library (which always takes precedence), and will fall back on `liba.a` if the dylib is not found. The static archive is searched for in the system lib search dirs like the dylibs.

10 files changed, 138 insertions(+), 78 deletions(-)

src/link/MachO.zig+23-19
......@@ -698,8 +698,8 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
698698 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
699699 }
700700
701 // Shared libraries.
702 var shared_libs = std.ArrayList([]const u8).init(arena);
701 // Shared and static libraries passed via `-l` flag.
702 var libs = std.ArrayList([]const u8).init(arena);
703703 var search_lib_names = std.ArrayList([]const u8).init(arena);
704704
705705 const system_libs = self.base.options.system_libs.items();
......@@ -708,9 +708,8 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
708708 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
709709 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
710710 // case we want to avoid prepending "-l".
711 // TODO I think they should go as an input file instead of via shared_libs.
712711 if (Compilation.classifyFileExt(link_lib) == .shared_library) {
713 try shared_libs.append(link_lib);
712 try positionals.append(link_lib);
714713 continue;
715714 }
716715
......@@ -760,24 +759,29 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
760759 }
761760 }
762761
763 for (search_lib_names.items) |l_name| {
764 // TODO text-based API, or .tbd files.
765 const l_name_ext = try std.fmt.allocPrint(arena, "lib{s}.dylib", .{l_name});
762 // TODO text-based API, or .tbd files.
763 const exts = &[_][]const u8{ "dylib", "a" };
766764
765 for (search_lib_names.items) |l_name| {
767766 var found = false;
768 for (search_lib_dirs.items) |lib_dir| {
769 const full_path = try fs.path.join(arena, &[_][]const u8{ lib_dir, l_name_ext });
770767
771 // Check if the dylib file exists.
772 const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
773 error.FileNotFound => continue,
774 else => |e| return e,
775 };
776 defer tmp.close();
768 for (exts) |ext| ext: {
769 const l_name_ext = try std.fmt.allocPrint(arena, "lib{s}.{s}", .{ l_name, ext });
770
771 for (search_lib_dirs.items) |lib_dir| {
772 const full_path = try fs.path.join(arena, &[_][]const u8{ lib_dir, l_name_ext });
777773
778 try shared_libs.append(full_path);
779 found = true;
780 break;
774 // Check if the dylib file exists.
775 const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
776 error.FileNotFound => continue,
777 else => |e| return e,
778 };
779 defer tmp.close();
780
781 try libs.append(full_path);
782 found = true;
783 break :ext;
784 }
781785 }
782786
783787 if (!found) {
......@@ -835,7 +839,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
835839 }
836840
837841 try zld.link(positionals.items, full_out_path, .{
838 .shared_libs = shared_libs.items,
842 .libs = libs.items,
839843 .rpaths = rpaths.items,
840844 });
841845
src/link/MachO/Archive.zig+12-6
......@@ -27,14 +27,14 @@ toc: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(u32)) = .{},
2727// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
2828// member indicates, for each member file.
2929/// String that begins an archive file.
30pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
30const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
3131/// Size of that string.
32pub const SARMAG: u4 = 8;
32const SARMAG: u4 = 8;
3333
3434/// String in ar_fmag at the end of each header.
35pub const ARFMAG: *const [2:0]u8 = "`\n";
35const ARFMAG: *const [2:0]u8 = "`\n";
3636
37pub const ar_hdr = extern struct {
37const ar_hdr = extern struct {
3838 /// Member file name, sometimes / terminated.
3939 ar_name: [16]u8,
4040
......@@ -60,7 +60,7 @@ pub const ar_hdr = extern struct {
6060 Name: []const u8,
6161 Length: u64,
6262 };
63 pub fn nameOrLength(self: ar_hdr) !NameOrLength {
63 fn nameOrLength(self: ar_hdr) !NameOrLength {
6464 const value = getValue(&self.ar_name);
6565 const slash_index = mem.indexOf(u8, value, "/") orelse return error.MalformedArchive;
6666 const len = value.len;
......@@ -75,7 +75,7 @@ pub const ar_hdr = extern struct {
7575 }
7676 }
7777
78 pub fn size(self: ar_hdr) !u64 {
78 fn size(self: ar_hdr) !u64 {
7979 const value = getValue(&self.ar_size);
8080 return std.fmt.parseInt(u64, value, 10);
8181 }
......@@ -231,3 +231,9 @@ pub fn parseObject(self: Archive, offset: u32) !*Object {
231231
232232 return object;
233233}
234
235pub fn isArchive(file: fs.File) !bool {
236 const magic = try file.reader().readBytesNoEof(Archive.SARMAG);
237 try file.seekTo(0);
238 return mem.eql(u8, &magic, Archive.ARMAG);
239}
src/link/MachO/Dylib.zig+6
......@@ -183,3 +183,9 @@ pub fn parseSymbols(self: *Dylib) !void {
183183 try self.symbols.putNoClobber(self.allocator, name, &proxy.base);
184184 }
185185}
186
187pub fn isDylib(file: fs.File) !bool {
188 const header = try file.reader().readStruct(macho.mach_header_64);
189 try file.seekTo(0);
190 return header.filetype == macho.MH_DYLIB;
191}
src/link/MachO/Object.zig+6
......@@ -485,3 +485,9 @@ pub fn parseDataInCode(self: *Object) !void {
485485 try self.data_in_code_entries.append(self.allocator, dice);
486486 }
487487}
488
489pub fn isObject(file: fs.File) !bool {
490 const header = try file.reader().readStruct(macho.mach_header_64);
491 try file.seekTo(0);
492 return header.filetype == macho.MH_OBJECT;
493}
src/link/MachO/Zld.zig+53-53
......@@ -187,7 +187,7 @@ pub fn closeFiles(self: Zld) void {
187187}
188188
189189const LinkArgs = struct {
190 shared_libs: []const []const u8,
190 libs: []const []const u8,
191191 rpaths: []const []const u8,
192192};
193193
......@@ -229,7 +229,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L
229229 try self.populateMetadata();
230230 try self.addRpaths(args.rpaths);
231231 try self.parseInputFiles(files);
232 try self.parseDylibs(args.shared_libs);
232 try self.parseLibs(args.libs);
233233 try self.resolveSymbols();
234234 try self.resolveStubsAndGotEntries();
235235 try self.updateMetadata();
......@@ -265,13 +265,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
265265 };
266266
267267 try_object: {
268 const header = try file.reader().readStruct(macho.mach_header_64);
269 if (header.filetype != macho.MH_OBJECT) {
270 try file.seekTo(0);
271 break :try_object;
272 }
273
274 try file.seekTo(0);
268 if (!(try Object.isObject(file))) break :try_object;
275269 try classified.append(.{
276270 .kind = .object,
277271 .file = file,
......@@ -281,13 +275,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
281275 }
282276
283277 try_archive: {
284 const magic = try file.reader().readBytesNoEof(Archive.SARMAG);
285 if (!mem.eql(u8, &magic, Archive.ARMAG)) {
286 try file.seekTo(0);
287 break :try_archive;
288 }
289
290 try file.seekTo(0);
278 if (!(try Archive.isArchive(file))) break :try_archive;
291279 try classified.append(.{
292280 .kind = .archive,
293281 .file = file,
......@@ -297,13 +285,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
297285 }
298286
299287 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);
288 if (!(try Dylib.isDylib(file))) break :try_dylib;
307289 try classified.append(.{
308290 .kind = .dylib,
309291 .file = file,
......@@ -312,7 +294,8 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
312294 continue;
313295 }
314296
315 log.debug("unexpected input file of unknown type '{s}'", .{file_name});
297 file.close();
298 log.warn("unknown filetype for positional input file: '{s}'", .{file_name});
316299 }
317300
318301 // Based on our classification, proceed with parsing.
......@@ -373,35 +356,52 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
373356 }
374357}
375358
376fn parseDylibs(self: *Zld, shared_libs: []const []const u8) !void {
377 for (shared_libs) |lib| {
378 const dylib = try self.allocator.create(Dylib);
379 errdefer self.allocator.destroy(dylib);
380
381 dylib.* = Dylib.init(self.allocator);
382 dylib.arch = self.arch.?;
383 dylib.name = try self.allocator.dupe(u8, lib);
384 dylib.file = try fs.cwd().openFile(lib, .{});
385
386 const ordinal = @intCast(u16, self.dylibs.items.len);
387 dylib.ordinal = ordinal + 2; // TODO +2 since 1 is reserved for libSystem
388
389 // TODO Defer parsing of the dylibs until they are actually needed
390 try dylib.parse();
391 try self.dylibs.append(self.allocator, dylib);
392
393 // Add LC_LOAD_DYLIB command
394 const dylib_id = dylib.id orelse unreachable;
395 var dylib_cmd = try createLoadDylibCommand(
396 self.allocator,
397 dylib_id.name,
398 dylib_id.timestamp,
399 dylib_id.current_version,
400 dylib_id.compatibility_version,
401 );
402 errdefer dylib_cmd.deinit(self.allocator);
403
404 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
359fn parseLibs(self: *Zld, libs: []const []const u8) !void {
360 for (libs) |lib| {
361 const file = try fs.cwd().openFile(lib, .{});
362
363 if (try Dylib.isDylib(file)) {
364 const dylib = try self.allocator.create(Dylib);
365 errdefer self.allocator.destroy(dylib);
366
367 dylib.* = Dylib.init(self.allocator);
368 dylib.arch = self.arch.?;
369 dylib.name = try self.allocator.dupe(u8, lib);
370 dylib.file = file;
371
372 const ordinal = @intCast(u16, self.dylibs.items.len);
373 dylib.ordinal = ordinal + 2; // TODO +2 since 1 is reserved for libSystem
374
375 // TODO Defer parsing of the dylibs until they are actually needed
376 try dylib.parse();
377 try self.dylibs.append(self.allocator, dylib);
378
379 // Add LC_LOAD_DYLIB command
380 const dylib_id = dylib.id orelse unreachable;
381 var dylib_cmd = try createLoadDylibCommand(
382 self.allocator,
383 dylib_id.name,
384 dylib_id.timestamp,
385 dylib_id.current_version,
386 dylib_id.compatibility_version,
387 );
388 errdefer dylib_cmd.deinit(self.allocator);
389
390 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
391 } else if (try Archive.isArchive(file)) {
392 const archive = try self.allocator.create(Archive);
393 errdefer self.allocator.destroy(archive);
394
395 archive.* = Archive.init(self.allocator);
396 archive.arch = self.arch.?;
397 archive.name = try self.allocator.dupe(u8, lib);
398 archive.file = file;
399 try archive.parse();
400 try self.archives.append(self.allocator, archive);
401 } else {
402 file.close();
403 log.warn("unknown filetype for a library: '{s}'", .{lib});
404 }
405405 }
406406}
407407
test/standalone.zig+1
......@@ -14,6 +14,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
1414 cases.addBuildFile("test/standalone/global_linkage/build.zig");
1515 cases.addBuildFile("test/standalone/static_c_lib/build.zig");
1616 cases.addBuildFile("test/standalone/link_interdependent_static_c_libs/build.zig");
17 cases.addBuildFile("test/standalone/link_static_lib_as_system_lib/build.zig");
1718 cases.addBuildFile("test/standalone/issue_339/build.zig");
1819 cases.addBuildFile("test/standalone/issue_794/build.zig");
1920 cases.addBuildFile("test/standalone/issue_5825/build.zig");
test/standalone/link_static_lib_as_system_lib/a.c created+4
......@@ -0,0 +1,4 @@
1#include "a.h"
2int32_t add(int32_t a, int32_t b) {
3 return a + b;
4}
test/standalone/link_static_lib_as_system_lib/a.h created+2
......@@ -0,0 +1,2 @@
1#include <stdint.h>
2int32_t add(int32_t a, int32_t b);
test/standalone/link_static_lib_as_system_lib/build.zig created+23
......@@ -0,0 +1,23 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
6
7 const lib_a = b.addStaticLibrary("a", null);
8 lib_a.addCSourceFile("a.c", &[_][]const u8{});
9 lib_a.setBuildMode(mode);
10 lib_a.addIncludeDir(".");
11 lib_a.install();
12
13 const test_exe = b.addTest("main.zig");
14 test_exe.setBuildMode(mode);
15 test_exe.linkSystemLibrary("a"); // force linking liba.a as -la
16 test_exe.addSystemIncludeDir(".");
17 const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch unreachable;
18 test_exe.addLibPath(search_path);
19
20 const test_step = b.step("test", "Test it");
21 test_step.dependOn(b.getInstallStep());
22 test_step.dependOn(&test_exe.step);
23}
test/standalone/link_static_lib_as_system_lib/main.zig created+8
......@@ -0,0 +1,8 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const c = @cImport(@cInclude("a.h"));
4
5test "import C add" {
6 const result = c.add(2, 1);
7 try expect(result == 3);
8}