Commit 2b0d322ea0
Changed files (10)
src
test
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);
+}
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;
+}
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;
+}
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});
+ }
}
}
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,
});
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;
+}
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);
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);
+}
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);
+}
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");