diff options
| -rw-r--r-- | src/link/MachO.zig | 42 | ||||
| -rw-r--r-- | src/link/MachO/Archive.zig | 18 | ||||
| -rw-r--r-- | src/link/MachO/Dylib.zig | 6 | ||||
| -rw-r--r-- | src/link/MachO/Object.zig | 6 | ||||
| -rw-r--r-- | src/link/MachO/Zld.zig | 106 | ||||
| -rw-r--r-- | test/standalone.zig | 1 | ||||
| -rw-r--r-- | test/standalone/link_static_lib_as_system_lib/a.c | 4 | ||||
| -rw-r--r-- | test/standalone/link_static_lib_as_system_lib/a.h | 2 | ||||
| -rw-r--r-- | test/standalone/link_static_lib_as_system_lib/build.zig | 23 | ||||
| -rw-r--r-- | test/standalone/link_static_lib_as_system_lib/main.zig | 8 |
10 files changed, 138 insertions, 78 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig index f0f25bea66..e00a4d2c2d 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -698,8 +698,8 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { try positionals.append(comp.libcxx_static_lib.?.full_object_path); } - // Shared libraries. - var shared_libs = std.ArrayList([]const u8).init(arena); + // Shared and static libraries passed via `-l` flag. + var libs = std.ArrayList([]const u8).init(arena); var search_lib_names = std.ArrayList([]const u8).init(arena); const system_libs = self.base.options.system_libs.items(); @@ -708,9 +708,8 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { // By this time, we depend on these libs being dynamically linked libraries and not static libraries // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which // case we want to avoid prepending "-l". - // TODO I think they should go as an input file instead of via shared_libs. if (Compilation.classifyFileExt(link_lib) == .shared_library) { - try shared_libs.append(link_lib); + try positionals.append(link_lib); continue; } @@ -760,24 +759,29 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { } } - for (search_lib_names.items) |l_name| { - // TODO text-based API, or .tbd files. - const l_name_ext = try std.fmt.allocPrint(arena, "lib{s}.dylib", .{l_name}); + // TODO text-based API, or .tbd files. + const exts = &[_][]const u8{ "dylib", "a" }; + for (search_lib_names.items) |l_name| { var found = false; - for (search_lib_dirs.items) |lib_dir| { - const full_path = try fs.path.join(arena, &[_][]const u8{ lib_dir, l_name_ext }); - // Check if the dylib file exists. - const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { - error.FileNotFound => continue, - else => |e| return e, - }; - defer tmp.close(); + for (exts) |ext| ext: { + const l_name_ext = try std.fmt.allocPrint(arena, "lib{s}.{s}", .{ l_name, ext }); + + for (search_lib_dirs.items) |lib_dir| { + const full_path = try fs.path.join(arena, &[_][]const u8{ lib_dir, l_name_ext }); - try shared_libs.append(full_path); - found = true; - break; + // Check if the dylib file exists. + const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => |e| return e, + }; + defer tmp.close(); + + try libs.append(full_path); + found = true; + break :ext; + } } if (!found) { @@ -835,7 +839,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { } try zld.link(positionals.items, full_out_path, .{ - .shared_libs = shared_libs.items, + .libs = libs.items, .rpaths = rpaths.items, }); diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index 702a807a4d..4c40b35938 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -27,14 +27,14 @@ toc: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(u32)) = .{}, // `struct ar_hdr', and as many bytes of member file data as its `ar_size' // member indicates, for each member file. /// String that begins an archive file. -pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n"; +const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n"; /// Size of that string. -pub const SARMAG: u4 = 8; +const SARMAG: u4 = 8; /// String in ar_fmag at the end of each header. -pub const ARFMAG: *const [2:0]u8 = "`\n"; +const ARFMAG: *const [2:0]u8 = "`\n"; -pub const ar_hdr = extern struct { +const ar_hdr = extern struct { /// Member file name, sometimes / terminated. ar_name: [16]u8, @@ -60,7 +60,7 @@ pub const ar_hdr = extern struct { Name: []const u8, Length: u64, }; - pub fn nameOrLength(self: ar_hdr) !NameOrLength { + fn nameOrLength(self: ar_hdr) !NameOrLength { const value = getValue(&self.ar_name); const slash_index = mem.indexOf(u8, value, "/") orelse return error.MalformedArchive; const len = value.len; @@ -75,7 +75,7 @@ pub const ar_hdr = extern struct { } } - pub fn size(self: ar_hdr) !u64 { + fn size(self: ar_hdr) !u64 { const value = getValue(&self.ar_size); return std.fmt.parseInt(u64, value, 10); } @@ -231,3 +231,9 @@ pub fn parseObject(self: Archive, offset: u32) !*Object { return object; } + +pub fn isArchive(file: fs.File) !bool { + const magic = try file.reader().readBytesNoEof(Archive.SARMAG); + try file.seekTo(0); + return mem.eql(u8, &magic, Archive.ARMAG); +} diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 8d864454f8..d7039f7f6c 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -183,3 +183,9 @@ pub fn parseSymbols(self: *Dylib) !void { try self.symbols.putNoClobber(self.allocator, name, &proxy.base); } } + +pub fn isDylib(file: fs.File) !bool { + const header = try file.reader().readStruct(macho.mach_header_64); + try file.seekTo(0); + return header.filetype == macho.MH_DYLIB; +} diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 4d2ade7aad..4160c2d75c 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -485,3 +485,9 @@ pub fn parseDataInCode(self: *Object) !void { try self.data_in_code_entries.append(self.allocator, dice); } } + +pub fn isObject(file: fs.File) !bool { + const header = try file.reader().readStruct(macho.mach_header_64); + try file.seekTo(0); + return header.filetype == macho.MH_OBJECT; +} diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index cc39ce1047..cef6414acb 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -187,7 +187,7 @@ pub fn closeFiles(self: Zld) void { } const LinkArgs = struct { - shared_libs: []const []const u8, + libs: []const []const u8, rpaths: []const []const u8, }; @@ -229,7 +229,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L try self.populateMetadata(); try self.addRpaths(args.rpaths); try self.parseInputFiles(files); - try self.parseDylibs(args.shared_libs); + try self.parseLibs(args.libs); try self.resolveSymbols(); try self.resolveStubsAndGotEntries(); try self.updateMetadata(); @@ -265,13 +265,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { }; try_object: { - const header = try file.reader().readStruct(macho.mach_header_64); - if (header.filetype != macho.MH_OBJECT) { - try file.seekTo(0); - break :try_object; - } - - try file.seekTo(0); + if (!(try Object.isObject(file))) break :try_object; try classified.append(.{ .kind = .object, .file = file, @@ -281,13 +275,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { } try_archive: { - const magic = try file.reader().readBytesNoEof(Archive.SARMAG); - if (!mem.eql(u8, &magic, Archive.ARMAG)) { - try file.seekTo(0); - break :try_archive; - } - - try file.seekTo(0); + if (!(try Archive.isArchive(file))) break :try_archive; try classified.append(.{ .kind = .archive, .file = file, @@ -297,13 +285,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { } 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); + if (!(try Dylib.isDylib(file))) break :try_dylib; try classified.append(.{ .kind = .dylib, .file = file, @@ -312,7 +294,8 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { continue; } - log.debug("unexpected input file of unknown type '{s}'", .{file_name}); + file.close(); + log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); } // Based on our classification, proceed with parsing. @@ -373,35 +356,52 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { } } -fn parseDylibs(self: *Zld, shared_libs: []const []const u8) !void { - for (shared_libs) |lib| { - const dylib = try self.allocator.create(Dylib); - errdefer self.allocator.destroy(dylib); - - dylib.* = Dylib.init(self.allocator); - dylib.arch = self.arch.?; - dylib.name = try self.allocator.dupe(u8, lib); - dylib.file = try fs.cwd().openFile(lib, .{}); - - 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 }); +fn parseLibs(self: *Zld, libs: []const []const u8) !void { + for (libs) |lib| { + const file = try fs.cwd().openFile(lib, .{}); + + if (try Dylib.isDylib(file)) { + const dylib = try self.allocator.create(Dylib); + errdefer self.allocator.destroy(dylib); + + dylib.* = Dylib.init(self.allocator); + dylib.arch = self.arch.?; + dylib.name = try self.allocator.dupe(u8, lib); + dylib.file = 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 }); + } else if (try Archive.isArchive(file)) { + const archive = try self.allocator.create(Archive); + errdefer self.allocator.destroy(archive); + + archive.* = Archive.init(self.allocator); + archive.arch = self.arch.?; + archive.name = try self.allocator.dupe(u8, lib); + archive.file = file; + try archive.parse(); + try self.archives.append(self.allocator, archive); + } else { + file.close(); + log.warn("unknown filetype for a library: '{s}'", .{lib}); + } } } diff --git a/test/standalone.zig b/test/standalone.zig index e1236d448b..9dd849ef88 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -14,6 +14,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { cases.addBuildFile("test/standalone/global_linkage/build.zig"); cases.addBuildFile("test/standalone/static_c_lib/build.zig"); cases.addBuildFile("test/standalone/link_interdependent_static_c_libs/build.zig"); + cases.addBuildFile("test/standalone/link_static_lib_as_system_lib/build.zig"); cases.addBuildFile("test/standalone/issue_339/build.zig"); cases.addBuildFile("test/standalone/issue_794/build.zig"); cases.addBuildFile("test/standalone/issue_5825/build.zig"); diff --git a/test/standalone/link_static_lib_as_system_lib/a.c b/test/standalone/link_static_lib_as_system_lib/a.c new file mode 100644 index 0000000000..ee9da97a3a --- /dev/null +++ b/test/standalone/link_static_lib_as_system_lib/a.c @@ -0,0 +1,4 @@ +#include "a.h" +int32_t add(int32_t a, int32_t b) { + return a + b; +} diff --git a/test/standalone/link_static_lib_as_system_lib/a.h b/test/standalone/link_static_lib_as_system_lib/a.h new file mode 100644 index 0000000000..7b45d54d56 --- /dev/null +++ b/test/standalone/link_static_lib_as_system_lib/a.h @@ -0,0 +1,2 @@ +#include <stdint.h> +int32_t add(int32_t a, int32_t b); diff --git a/test/standalone/link_static_lib_as_system_lib/build.zig b/test/standalone/link_static_lib_as_system_lib/build.zig new file mode 100644 index 0000000000..f951b68a8c --- /dev/null +++ b/test/standalone/link_static_lib_as_system_lib/build.zig @@ -0,0 +1,23 @@ +const std = @import("std"); +const Builder = std.build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const lib_a = b.addStaticLibrary("a", null); + lib_a.addCSourceFile("a.c", &[_][]const u8{}); + lib_a.setBuildMode(mode); + lib_a.addIncludeDir("."); + lib_a.install(); + + const test_exe = b.addTest("main.zig"); + test_exe.setBuildMode(mode); + test_exe.linkSystemLibrary("a"); // force linking liba.a as -la + test_exe.addSystemIncludeDir("."); + const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch unreachable; + test_exe.addLibPath(search_path); + + const test_step = b.step("test", "Test it"); + test_step.dependOn(b.getInstallStep()); + test_step.dependOn(&test_exe.step); +} diff --git a/test/standalone/link_static_lib_as_system_lib/main.zig b/test/standalone/link_static_lib_as_system_lib/main.zig new file mode 100644 index 0000000000..0b9c46217f --- /dev/null +++ b/test/standalone/link_static_lib_as_system_lib/main.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +const expect = std.testing.expect; +const c = @cImport(@cInclude("a.h")); + +test "import C add" { + const result = c.add(2, 1); + try expect(result == 3); +} |
