aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2021-06-25 07:51:21 +0200
committerGitHub <noreply@github.com>2021-06-25 07:51:21 +0200
commit350ead9cb2ce87485569fbf630f2906864f35a6b (patch)
tree8c0f8fe31061a60db637ffc9120f800a0379beb1 /src
parent8216ce67895f5605d1720df4e5e6636395f2fc92 (diff)
parentddd2cd73307c06906a8d120b41fd5ab8864797a1 (diff)
downloadzig-350ead9cb2ce87485569fbf630f2906864f35a6b.tar.gz
zig-350ead9cb2ce87485569fbf630f2906864f35a6b.zip
Merge pull request #9229 from ziglang/zld-objc-frameworks
zig ld: link Obj-C, link frameworks, improve linker's implementation
Diffstat (limited to 'src')
-rw-r--r--src/Compilation.zig12
-rw-r--r--src/link/MachO.zig318
-rw-r--r--src/link/MachO/Archive.zig63
-rw-r--r--src/link/MachO/DebugSymbols.zig66
-rw-r--r--src/link/MachO/Dylib.zig326
-rw-r--r--src/link/MachO/Object.zig52
-rw-r--r--src/link/MachO/Stub.zig130
-rw-r--r--src/link/MachO/Symbol.zig30
-rw-r--r--src/link/MachO/Zld.zig1267
-rw-r--r--src/link/MachO/commands.zig73
-rw-r--r--src/link/tapi.zig11
-rw-r--r--src/link/tapi/parse.zig7
-rw-r--r--src/main.zig5
13 files changed, 1105 insertions, 1255 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig
index ea878056ae..eea4d930c4 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -2857,7 +2857,7 @@ pub fn addCCArgs(
try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });
switch (ext) {
- .c, .cpp, .h => {
+ .c, .cpp, .m, .h => {
try argv.appendSlice(&[_][]const u8{
"-nostdinc",
"-fno-spell-checking",
@@ -3148,6 +3148,7 @@ pub const FileExt = enum {
c,
cpp,
h,
+ m,
ll,
bc,
assembly,
@@ -3159,7 +3160,7 @@ pub const FileExt = enum {
pub fn clangSupportsDepFile(ext: FileExt) bool {
return switch (ext) {
- .c, .cpp, .h => true,
+ .c, .cpp, .h, .m => true,
.ll,
.bc,
@@ -3193,6 +3194,10 @@ pub fn hasCppExt(filename: []const u8) bool {
mem.endsWith(u8, filename, ".cxx");
}
+pub fn hasObjCExt(filename: []const u8) bool {
+ return mem.endsWith(u8, filename, ".m");
+}
+
pub fn hasAsmExt(filename: []const u8) bool {
return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S");
}
@@ -3229,6 +3234,8 @@ pub fn classifyFileExt(filename: []const u8) FileExt {
return .c;
} else if (hasCppExt(filename)) {
return .cpp;
+ } else if (hasObjCExt(filename)) {
+ return .m;
} else if (mem.endsWith(u8, filename, ".ll")) {
return .ll;
} else if (mem.endsWith(u8, filename, ".bc")) {
@@ -3252,6 +3259,7 @@ pub fn classifyFileExt(filename: []const u8) FileExt {
test "classifyFileExt" {
try std.testing.expectEqual(FileExt.cpp, classifyFileExt("foo.cc"));
+ try std.testing.expectEqual(FileExt.m, classifyFileExt("foo.m"));
try std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.nim"));
try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so"));
try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1"));
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index b7696f6a7c..4ede9c516e 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -514,6 +514,119 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
}
}
+fn resolvePaths(
+ arena: *Allocator,
+ resolved_paths: *std.ArrayList([]const u8),
+ syslibroot: ?[]const u8,
+ search_dirs: []const []const u8,
+ lib_names: []const []const u8,
+ kind: enum { lib, framework },
+) !void {
+ var resolved_dirs = std.ArrayList([]const u8).init(arena);
+ for (search_dirs) |dir| {
+ if (fs.path.isAbsolute(dir)) {
+ var candidates = std.ArrayList([]const u8).init(arena);
+ if (syslibroot) |root| {
+ const full_path = try fs.path.join(arena, &[_][]const u8{ root, dir });
+ try candidates.append(full_path);
+ }
+ try candidates.append(dir);
+
+ var found = false;
+ for (candidates.items) |candidate| {
+ // Verify that search path actually exists
+ var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) {
+ error.FileNotFound => continue,
+ else => |e| return e,
+ };
+ defer tmp.close();
+
+ try resolved_dirs.append(candidate);
+ found = true;
+ break;
+ }
+
+ if (!found) {
+ switch (kind) {
+ .lib => log.warn("directory not found for '-L{s}'", .{dir}),
+ .framework => log.warn("directory not found for '-F{s}'", .{dir}),
+ }
+ }
+ } else {
+ // Verify that search path actually exists
+ var tmp = fs.cwd().openDir(dir, .{}) catch |err| switch (err) {
+ error.FileNotFound => {
+ switch (kind) {
+ .lib => log.warn("directory not found for '-L{s}'", .{dir}),
+ .framework => log.warn("directory not found for '-F{s}'", .{dir}),
+ }
+ continue;
+ },
+ else => |e| return e,
+ };
+ defer tmp.close();
+
+ try resolved_dirs.append(dir);
+ }
+ }
+
+ // Assume ld64 default: -search_paths_first
+ // Look in each directory for a dylib (next, tbd), and then for archive
+ // TODO implement alternative: -search_dylibs_first
+ const exts = switch (kind) {
+ .lib => &[_][]const u8{ "dylib", "tbd", "a" },
+ .framework => &[_][]const u8{ "dylib", "tbd" },
+ };
+
+ for (lib_names) |lib_name| {
+ var found = false;
+
+ ext: for (exts) |ext| {
+ const lib_name_ext = blk: {
+ switch (kind) {
+ .lib => break :blk try std.fmt.allocPrint(arena, "lib{s}.{s}", .{ lib_name, ext }),
+ .framework => {
+ const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{lib_name});
+ const nn = try std.fmt.allocPrint(arena, "{s}.{s}", .{ lib_name, ext });
+ break :blk try fs.path.join(arena, &[_][]const u8{ prefix, nn });
+ },
+ }
+ };
+
+ for (resolved_dirs.items) |dir| {
+ const full_path = try fs.path.join(arena, &[_][]const u8{ dir, lib_name_ext });
+
+ // Check if the lib file exists.
+ const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
+ error.FileNotFound => continue,
+ else => |e| return e,
+ };
+ defer tmp.close();
+
+ try resolved_paths.append(full_path);
+ found = true;
+ break :ext;
+ }
+ }
+
+ if (!found) {
+ switch (kind) {
+ .lib => {
+ log.warn("library not found for '-l{s}'", .{lib_name});
+ log.warn("Library search paths:", .{});
+ },
+ .framework => {
+ log.warn("framework not found for '-f{s}'", .{lib_name});
+ log.warn("Framework search paths:", .{});
+ },
+ }
+ for (resolved_dirs.items) |dir| {
+ log.warn(" {s}", .{dir});
+ }
+ }
+ }
+}
+
fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -676,6 +789,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
zld.deinit();
}
zld.arch = target.cpu.arch;
+ zld.syslibroot = self.base.options.syslibroot;
zld.stack_size = stack_size;
// Positional arguments to the linker such as object files and static archives.
@@ -700,7 +814,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
}
// 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.keys();
@@ -716,84 +829,15 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
try search_lib_names.append(link_lib);
}
- var search_lib_dirs = std.ArrayList([]const u8).init(arena);
-
- for (self.base.options.lib_dirs) |path| {
- if (fs.path.isAbsolute(path)) {
- var candidates = std.ArrayList([]const u8).init(arena);
- if (self.base.options.syslibroot) |syslibroot| {
- const full_path = try fs.path.join(arena, &[_][]const u8{ syslibroot, path });
- try candidates.append(full_path);
- }
- try candidates.append(path);
-
- var found = false;
- for (candidates.items) |candidate| {
- // Verify that search path actually exists
- var tmp = fs.cwd().openDir(candidate, .{}) catch |err| switch (err) {
- error.FileNotFound => continue,
- else => |e| return e,
- };
- defer tmp.close();
-
- try search_lib_dirs.append(candidate);
- found = true;
- break;
- }
-
- if (!found) {
- log.warn("directory not found for '-L{s}'", .{path});
- }
- } else {
- // Verify that search path actually exists
- var tmp = fs.cwd().openDir(path, .{}) catch |err| switch (err) {
- error.FileNotFound => {
- log.warn("directory not found for '-L{s}'", .{path});
- continue;
- },
- else => |e| return e,
- };
- defer tmp.close();
-
- try search_lib_dirs.append(path);
- }
- }
-
- // Assume ld64 default: -search_paths_first
- // Look in each directory for a dylib (next, tbd), and then for archive
- // TODO implement alternative: -search_dylibs_first
- const exts = &[_][]const u8{ "dylib", "tbd", "a" };
-
- for (search_lib_names.items) |l_name| {
- var found = false;
-
- ext: for (exts) |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 });
-
- // Check if the lib 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) {
- log.warn("library not found for '-l{s}'", .{l_name});
- log.warn("Library search paths:", .{});
- for (search_lib_dirs.items) |lib_dir| {
- log.warn(" {s}", .{lib_dir});
- }
- }
- }
+ var libs = std.ArrayList([]const u8).init(arena);
+ try resolvePaths(
+ arena,
+ &libs,
+ self.base.options.syslibroot,
+ self.base.options.lib_dirs,
+ search_lib_names.items,
+ .lib,
+ );
// rpaths
var rpath_table = std.StringArrayHashMap(void).init(arena);
@@ -809,9 +853,14 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
}
// frameworks
- for (self.base.options.frameworks) |framework| {
- log.warn("frameworks not yet supported for '-framework {s}'", .{framework});
- }
+ try resolvePaths(
+ arena,
+ &libs,
+ self.base.options.syslibroot,
+ self.base.options.framework_dirs,
+ self.base.options.frameworks,
+ .framework,
+ );
if (self.base.options.verbose_link) {
var argv = std.ArrayList([]const u8).init(arena);
@@ -1731,18 +1780,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
if (self.pagezero_segment_cmd_index == null) {
self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.base.allocator, .{
- .Segment = SegmentCommand.empty(.{
- .cmd = macho.LC_SEGMENT_64,
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString("__PAGEZERO"),
- .vmaddr = 0,
+ .Segment = SegmentCommand.empty("__PAGEZERO", .{
.vmsize = 0x100000000, // size always set to 4GB
- .fileoff = 0,
- .filesize = 0,
- .maxprot = 0,
- .initprot = 0,
- .nsects = 0,
- .flags = 0,
}),
});
self.header_dirty = true;
@@ -1761,18 +1800,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
try self.load_commands.append(self.base.allocator, .{
- .Segment = SegmentCommand.empty(.{
- .cmd = macho.LC_SEGMENT_64,
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString("__TEXT"),
+ .Segment = SegmentCommand.empty("__TEXT", .{
.vmaddr = 0x100000000, // always starts at 4GB
.vmsize = needed_size,
- .fileoff = 0,
.filesize = needed_size,
.maxprot = maxprot,
.initprot = initprot,
- .nsects = 0,
- .flags = 0,
}),
});
self.header_dirty = true;
@@ -1793,19 +1826,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
log.debug("found __text section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
- try text_segment.addSection(self.base.allocator, .{
- .sectname = makeStaticString("__text"),
- .segname = makeStaticString("__TEXT"),
+ try text_segment.addSection(self.base.allocator, "__text", .{
.addr = text_segment.inner.vmaddr + off,
.size = @intCast(u32, needed_size),
.offset = @intCast(u32, off),
.@"align" = alignment,
- .reloff = 0,
- .nreloc = 0,
.flags = flags,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
self.header_dirty = true;
self.load_commands_dirty = true;
@@ -1831,19 +1857,13 @@ pub fn populateMissingMetadata(self: *MachO) !void {
log.debug("found __stubs section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
- try text_segment.addSection(self.base.allocator, .{
- .sectname = makeStaticString("__stubs"),
- .segname = makeStaticString("__TEXT"),
+ try text_segment.addSection(self.base.allocator, "__stubs", .{
.addr = text_segment.inner.vmaddr + off,
.size = needed_size,
.offset = @intCast(u32, off),
.@"align" = alignment,
- .reloff = 0,
- .nreloc = 0,
.flags = flags,
- .reserved1 = 0,
.reserved2 = stub_size,
- .reserved3 = 0,
});
self.header_dirty = true;
self.load_commands_dirty = true;
@@ -1864,19 +1884,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
log.debug("found __stub_helper section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
- try text_segment.addSection(self.base.allocator, .{
- .sectname = makeStaticString("__stub_helper"),
- .segname = makeStaticString("__TEXT"),
+ try text_segment.addSection(self.base.allocator, "__stub_helper", .{
.addr = text_segment.inner.vmaddr + off,
.size = needed_size,
.offset = @intCast(u32, off),
.@"align" = alignment,
- .reloff = 0,
- .nreloc = 0,
.flags = flags,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
self.header_dirty = true;
self.load_commands_dirty = true;
@@ -1893,18 +1906,13 @@ pub fn populateMissingMetadata(self: *MachO) !void {
log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size });
try self.load_commands.append(self.base.allocator, .{
- .Segment = SegmentCommand.empty(.{
- .cmd = macho.LC_SEGMENT_64,
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString("__DATA_CONST"),
+ .Segment = SegmentCommand.empty("__DATA_CONST", .{
.vmaddr = address_and_offset.address,
.vmsize = needed_size,
.fileoff = address_and_offset.offset,
.filesize = needed_size,
.maxprot = maxprot,
.initprot = initprot,
- .nsects = 0,
- .flags = 0,
}),
});
self.header_dirty = true;
@@ -1921,19 +1929,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
log.debug("found __got section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
- try dc_segment.addSection(self.base.allocator, .{
- .sectname = makeStaticString("__got"),
- .segname = makeStaticString("__DATA_CONST"),
+ try dc_segment.addSection(self.base.allocator, "__got", .{
.addr = dc_segment.inner.vmaddr + off - dc_segment.inner.fileoff,
.size = needed_size,
.offset = @intCast(u32, off),
.@"align" = 3, // 2^3 = @sizeOf(u64)
- .reloff = 0,
- .nreloc = 0,
.flags = flags,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
self.header_dirty = true;
self.load_commands_dirty = true;
@@ -1950,18 +1951,13 @@ pub fn populateMissingMetadata(self: *MachO) !void {
log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size });
try self.load_commands.append(self.base.allocator, .{
- .Segment = SegmentCommand.empty(.{
- .cmd = macho.LC_SEGMENT_64,
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString("__DATA"),
+ .Segment = SegmentCommand.empty("__DATA", .{
.vmaddr = address_and_offset.address,
.vmsize = needed_size,
.fileoff = address_and_offset.offset,
.filesize = needed_size,
.maxprot = maxprot,
.initprot = initprot,
- .nsects = 0,
- .flags = 0,
}),
});
self.header_dirty = true;
@@ -1978,19 +1974,12 @@ pub fn populateMissingMetadata(self: *MachO) !void {
log.debug("found __la_symbol_ptr section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
- try data_segment.addSection(self.base.allocator, .{
- .sectname = makeStaticString("__la_symbol_ptr"),
- .segname = makeStaticString("__DATA"),
+ try data_segment.addSection(self.base.allocator, "__la_symbol_ptr", .{
.addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff,
.size = needed_size,
.offset = @intCast(u32, off),
.@"align" = 3, // 2^3 = @sizeOf(u64)
- .reloff = 0,
- .nreloc = 0,
.flags = flags,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
self.header_dirty = true;
self.load_commands_dirty = true;
@@ -1999,26 +1988,17 @@ pub fn populateMissingMetadata(self: *MachO) !void {
const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
self.data_section_index = @intCast(u16, data_segment.sections.items.len);
- const flags = macho.S_REGULAR;
const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
const off = data_segment.findFreeSpace(needed_size, @alignOf(u64), null);
assert(off + needed_size <= data_segment.inner.fileoff + data_segment.inner.filesize); // TODO Must expand __DATA segment.
log.debug("found __data section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
- try data_segment.addSection(self.base.allocator, .{
- .sectname = makeStaticString("__data"),
- .segname = makeStaticString("__DATA"),
+ try data_segment.addSection(self.base.allocator, "__data", .{
.addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff,
.size = needed_size,
.offset = @intCast(u32, off),
.@"align" = 3, // 2^3 = @sizeOf(u64)
- .reloff = 0,
- .nreloc = 0,
- .flags = flags,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
self.header_dirty = true;
self.load_commands_dirty = true;
@@ -2033,18 +2013,11 @@ pub fn populateMissingMetadata(self: *MachO) !void {
log.debug("found __LINKEDIT segment free space at 0x{x}", .{address_and_offset.offset});
try self.load_commands.append(self.base.allocator, .{
- .Segment = SegmentCommand.empty(.{
- .cmd = macho.LC_SEGMENT_64,
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString("__LINKEDIT"),
+ .Segment = SegmentCommand.empty("__LINKEDIT", .{
.vmaddr = address_and_offset.address,
- .vmsize = 0,
.fileoff = address_and_offset.offset,
- .filesize = 0,
.maxprot = maxprot,
.initprot = initprot,
- .nsects = 0,
- .flags = 0,
}),
});
self.header_dirty = true;
@@ -2402,13 +2375,6 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
return vaddr;
}
-pub fn makeStaticString(comptime bytes: []const u8) [16]u8 {
- var buf = [_]u8{0} ** 16;
- if (bytes.len > buf.len) @compileError("string too long; max 16 bytes");
- mem.copy(u8, &buf, bytes);
- return buf;
-}
-
fn makeString(self: *MachO, bytes: []const u8) !u32 {
if (self.string_table_directory.get(bytes)) |offset| {
log.debug("reusing '{s}' from string table at offset 0x{x}", .{ bytes, offset });
diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig
index 48bc33f0fa..22439d300a 100644
--- a/src/link/MachO/Archive.zig
+++ b/src/link/MachO/Archive.zig
@@ -8,12 +8,13 @@ const macho = std.macho;
const mem = std.mem;
const Allocator = mem.Allocator;
+const Arch = std.Target.Cpu.Arch;
const Object = @import("Object.zig");
usingnamespace @import("commands.zig");
allocator: *Allocator,
-arch: ?std.Target.Cpu.Arch = null,
+arch: ?Arch = null,
file: ?fs.File = null,
header: ?ar_hdr = null,
name: ?[]const u8 = null,
@@ -85,10 +86,36 @@ const ar_hdr = extern struct {
}
};
-pub fn init(allocator: *Allocator) Archive {
- return .{
+pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?*Archive {
+ const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
+ error.FileNotFound => return null,
+ else => |e| return e,
+ };
+ errdefer file.close();
+
+ const archive = try allocator.create(Archive);
+ errdefer allocator.destroy(archive);
+
+ const name = try allocator.dupe(u8, path);
+ errdefer allocator.free(name);
+
+ archive.* = .{
.allocator = allocator,
+ .arch = arch,
+ .name = name,
+ .file = file,
+ };
+
+ archive.parse() catch |err| switch (err) {
+ error.EndOfStream, error.NotArchive => {
+ archive.deinit();
+ allocator.destroy(archive);
+ return null;
+ },
+ else => |e| return e,
};
+
+ return archive;
}
pub fn deinit(self: *Archive) void {
@@ -116,15 +143,15 @@ pub fn parse(self: *Archive) !void {
const magic = try reader.readBytesNoEof(SARMAG);
if (!mem.eql(u8, &magic, ARMAG)) {
- log.err("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic });
- return error.MalformedArchive;
+ log.debug("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic });
+ return error.NotArchive;
}
self.header = try reader.readStruct(ar_hdr);
if (!mem.eql(u8, &self.header.?.ar_fmag, ARFMAG)) {
- log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.?.ar_fmag });
- return error.MalformedArchive;
+ log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.?.ar_fmag });
+ return error.NotArchive;
}
var embedded_name = try parseName(self.allocator, self.header.?, reader);
@@ -222,23 +249,15 @@ pub fn parseObject(self: Archive, offset: u32) !*Object {
var object = try self.allocator.create(Object);
errdefer self.allocator.destroy(object);
- object.* = Object.init(self.allocator);
- object.arch = self.arch.?;
- object.file = try fs.cwd().openFile(self.name.?, .{});
- object.name = name;
- object.file_offset = @intCast(u32, try reader.context.getPos());
+ object.* = .{
+ .allocator = self.allocator,
+ .arch = self.arch.?,
+ .file = try fs.cwd().openFile(self.name.?, .{}),
+ .name = name,
+ .file_offset = @intCast(u32, try reader.context.getPos()),
+ };
try object.parse();
-
try reader.context.seekTo(0);
return object;
}
-
-pub fn isArchive(file: fs.File) !bool {
- const magic = file.reader().readBytesNoEof(Archive.SARMAG) catch |err| switch (err) {
- error.EndOfStream => return false,
- else => |e| return e,
- };
- try file.seekTo(0);
- return mem.eql(u8, &magic, Archive.ARMAG);
-}
diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig
index 2b10f3307b..684861ebf5 100644
--- a/src/link/MachO/DebugSymbols.zig
+++ b/src/link/MachO/DebugSymbols.zig
@@ -19,7 +19,6 @@ const MachO = @import("../MachO.zig");
const SrcFn = MachO.SrcFn;
const TextBlock = MachO.TextBlock;
const padToIdeal = MachO.padToIdeal;
-const makeStaticString = MachO.makeStaticString;
usingnamespace @import("commands.zig");
@@ -212,18 +211,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
log.debug("found dSym __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });
try self.load_commands.append(allocator, .{
- .Segment = SegmentCommand.empty(.{
- .cmd = macho.LC_SEGMENT_64,
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString("__DWARF"),
+ .Segment = SegmentCommand.empty("__DWARF", .{
.vmaddr = vmaddr,
.vmsize = needed_size,
.fileoff = off,
.filesize = needed_size,
- .maxprot = 0,
- .initprot = 0,
- .nsects = 0,
- .flags = 0,
}),
});
self.header_dirty = true;
@@ -234,19 +226,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
self.debug_str_section_index = @intCast(u16, dwarf_segment.sections.items.len);
assert(self.debug_string_table.items.len == 0);
- try dwarf_segment.addSection(allocator, .{
- .sectname = makeStaticString("__debug_str"),
- .segname = makeStaticString("__DWARF"),
+ try dwarf_segment.addSection(allocator, "__debug_str", .{
.addr = dwarf_segment.inner.vmaddr,
.size = @intCast(u32, self.debug_string_table.items.len),
.offset = @intCast(u32, dwarf_segment.inner.fileoff),
.@"align" = 1,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
self.header_dirty = true;
self.load_commands_dirty = true;
@@ -262,19 +246,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
log.debug("found dSym __debug_info free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
- try dwarf_segment.addSection(allocator, .{
- .sectname = makeStaticString("__debug_info"),
- .segname = makeStaticString("__DWARF"),
+ try dwarf_segment.addSection(allocator, "__debug_info", .{
.addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
.size = file_size_hint,
.offset = @intCast(u32, off),
.@"align" = p_align,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
self.header_dirty = true;
self.load_commands_dirty = true;
@@ -290,19 +266,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
log.debug("found dSym __debug_abbrev free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
- try dwarf_segment.addSection(allocator, .{
- .sectname = makeStaticString("__debug_abbrev"),
- .segname = makeStaticString("__DWARF"),
+ try dwarf_segment.addSection(allocator, "__debug_abbrev", .{
.addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
.size = file_size_hint,
.offset = @intCast(u32, off),
.@"align" = p_align,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
self.header_dirty = true;
self.load_commands_dirty = true;
@@ -318,19 +286,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
log.debug("found dSym __debug_aranges free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
- try dwarf_segment.addSection(allocator, .{
- .sectname = makeStaticString("__debug_aranges"),
- .segname = makeStaticString("__DWARF"),
+ try dwarf_segment.addSection(allocator, "__debug_aranges", .{
.addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
.size = file_size_hint,
.offset = @intCast(u32, off),
.@"align" = p_align,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
self.header_dirty = true;
self.load_commands_dirty = true;
@@ -346,19 +306,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
log.debug("found dSym __debug_line free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
- try dwarf_segment.addSection(allocator, .{
- .sectname = makeStaticString("__debug_line"),
- .segname = makeStaticString("__DWARF"),
+ try dwarf_segment.addSection(allocator, "__debug_line", .{
.addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
.size = file_size_hint,
.offset = @intCast(u32, off),
.@"align" = p_align,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
self.header_dirty = true;
self.load_commands_dirty = true;
@@ -692,14 +644,10 @@ pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void {
}
fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: SegmentCommand) !SegmentCommand {
- var cmd = SegmentCommand.empty(.{
- .cmd = macho.LC_SEGMENT_64,
+ var cmd = SegmentCommand.empty("", .{
.cmdsize = base_cmd.inner.cmdsize,
- .segname = undefined,
.vmaddr = base_cmd.inner.vmaddr,
.vmsize = base_cmd.inner.vmsize,
- .fileoff = 0,
- .filesize = 0,
.maxprot = base_cmd.inner.maxprot,
.initprot = base_cmd.inner.initprot,
.nsects = base_cmd.inner.nsects,
diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig
index bcf2433dd7..e91ff30ad2 100644
--- a/src/link/MachO/Dylib.zig
+++ b/src/link/MachO/Dylib.zig
@@ -3,20 +3,26 @@ const Dylib = @This();
const std = @import("std");
const assert = std.debug.assert;
const fs = std.fs;
+const fmt = std.fmt;
const log = std.log.scoped(.dylib);
const macho = std.macho;
+const math = std.math;
const mem = std.mem;
const Allocator = mem.Allocator;
+const Arch = std.Target.Cpu.Arch;
const Symbol = @import("Symbol.zig");
+const LibStub = @import("../tapi.zig").LibStub;
usingnamespace @import("commands.zig");
allocator: *Allocator,
-arch: ?std.Target.Cpu.Arch = null,
+
+arch: ?Arch = null,
header: ?macho.mach_header_64 = null,
file: ?fs.File = null,
name: ?[]const u8 = null,
+syslibroot: ?[]const u8 = null,
ordinal: ?u16 = null,
@@ -33,19 +39,139 @@ id: ?Id = null,
/// a symbol is referenced by an object file.
symbols: std.StringArrayHashMapUnmanaged(void) = .{},
+// TODO add parsing re-exported libs from binary dylibs
+dependent_libs: std.StringArrayHashMapUnmanaged(void) = .{},
+
pub const Id = struct {
name: []const u8,
timestamp: u32,
current_version: u32,
compatibility_version: u32,
+ pub fn default(name: []const u8) Id {
+ return .{
+ .name = name,
+ .timestamp = 2,
+ .current_version = 0x10000,
+ .compatibility_version = 0x10000,
+ };
+ }
+
pub fn deinit(id: *Id, allocator: *Allocator) void {
allocator.free(id.name);
}
+
+ const ParseError = fmt.ParseIntError || fmt.BufPrintError;
+
+ pub fn parseCurrentVersion(id: *Id, version: anytype) ParseError!void {
+ id.current_version = try parseVersion(version);
+ }
+
+ pub fn parseCompatibilityVersion(id: *Id, version: anytype) ParseError!void {
+ id.compatibility_version = try parseVersion(version);
+ }
+
+ fn parseVersion(version: anytype) ParseError!u32 {
+ const string = blk: {
+ switch (version) {
+ .int => |int| {
+ var out: u32 = 0;
+ const major = try math.cast(u16, int);
+ out += @intCast(u32, major) << 16;
+ return out;
+ },
+ .float => |float| {
+ var buf: [256]u8 = undefined;
+ break :blk try fmt.bufPrint(&buf, "{d:.2}", .{float});
+ },
+ .string => |string| {
+ break :blk string;
+ },
+ }
+ };
+
+ var out: u32 = 0;
+ var values: [3][]const u8 = undefined;
+
+ var split = mem.split(string, ".");
+ var count: u4 = 0;
+ while (split.next()) |value| {
+ if (count > 2) {
+ log.warn("malformed version field: {s}", .{string});
+ return 0x10000;
+ }
+ values[count] = value;
+ count += 1;
+ }
+
+ if (count > 2) {
+ out += try fmt.parseInt(u8, values[2], 10);
+ }
+ if (count > 1) {
+ out += @intCast(u32, try fmt.parseInt(u8, values[1], 10)) << 8;
+ }
+ out += @intCast(u32, try fmt.parseInt(u16, values[0], 10)) << 16;
+
+ return out;
+ }
};
-pub fn init(allocator: *Allocator) Dylib {
- return .{ .allocator = allocator };
+pub const Error = error{
+ OutOfMemory,
+ EmptyStubFile,
+ MismatchedCpuArchitecture,
+ UnsupportedCpuArchitecture,
+} || fs.File.OpenError || std.os.PReadError || Id.ParseError;
+
+pub fn createAndParseFromPath(
+ allocator: *Allocator,
+ arch: Arch,
+ path: []const u8,
+ syslibroot: ?[]const u8,
+) Error!?[]*Dylib {
+ const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
+ error.FileNotFound => return null,
+ else => |e| return e,
+ };
+ errdefer file.close();
+
+ const dylib = try allocator.create(Dylib);
+ errdefer allocator.destroy(dylib);
+
+ const name = try allocator.dupe(u8, path);
+ errdefer allocator.free(name);
+
+ dylib.* = .{
+ .allocator = allocator,
+ .arch = arch,
+ .name = name,
+ .file = file,
+ .syslibroot = syslibroot,
+ };
+
+ dylib.parse() catch |err| switch (err) {
+ error.EndOfStream, error.NotDylib => {
+ try file.seekTo(0);
+
+ var lib_stub = LibStub.loadFromFile(allocator, file) catch {
+ dylib.deinit();
+ allocator.destroy(dylib);
+ return null;
+ };
+ defer lib_stub.deinit();
+
+ try dylib.parseFromStub(lib_stub);
+ },
+ else => |e| return e,
+ };
+
+ var dylibs = std.ArrayList(*Dylib).init(allocator);
+ defer dylibs.deinit();
+
+ try dylibs.append(dylib);
+ try dylib.parseDependentLibs(&dylibs);
+
+ return dylibs.toOwnedSlice();
}
pub fn deinit(self: *Dylib) void {
@@ -59,6 +185,11 @@ pub fn deinit(self: *Dylib) void {
}
self.symbols.deinit(self.allocator);
+ for (self.dependent_libs.keys()) |key| {
+ self.allocator.free(key);
+ }
+ self.dependent_libs.deinit(self.allocator);
+
if (self.name) |name| {
self.allocator.free(name);
}
@@ -81,8 +212,8 @@ pub fn parse(self: *Dylib) !void {
self.header = try reader.readStruct(macho.mach_header_64);
if (self.header.?.filetype != macho.MH_DYLIB) {
- log.err("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, self.header.?.filetype });
- return error.MalformedDylib;
+ log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, self.header.?.filetype });
+ return error.NotDylib;
}
const this_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) {
@@ -103,7 +234,7 @@ pub fn parse(self: *Dylib) !void {
try self.parseSymbols();
}
-pub fn readLoadCommands(self: *Dylib, reader: anytype) !void {
+fn readLoadCommands(self: *Dylib, reader: anytype) !void {
try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds);
var i: u16 = 0;
@@ -127,15 +258,10 @@ pub fn readLoadCommands(self: *Dylib, reader: anytype) !void {
}
}
-pub fn parseId(self: *Dylib) !void {
+fn parseId(self: *Dylib) !void {
const index = self.id_cmd_index orelse {
log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{});
- self.id = .{
- .name = try self.allocator.dupe(u8, self.name.?),
- .timestamp = 2,
- .current_version = 0,
- .compatibility_version = 0,
- };
+ self.id = Id.default(try self.allocator.dupe(u8, self.name.?));
return;
};
const id_cmd = self.load_commands.items[index].Dylib;
@@ -153,7 +279,7 @@ pub fn parseId(self: *Dylib) !void {
};
}
-pub fn parseSymbols(self: *Dylib) !void {
+fn parseSymbols(self: *Dylib) !void {
const index = self.symtab_cmd_index orelse return;
const symtab_cmd = self.load_commands.items[index].Symtab;
@@ -176,13 +302,171 @@ pub fn parseSymbols(self: *Dylib) !void {
}
}
-pub fn isDylib(file: fs.File) !bool {
- const header = file.reader().readStruct(macho.mach_header_64) catch |err| switch (err) {
- error.EndOfStream => return false,
- else => |e| return e,
+fn hasTarget(targets: []const []const u8, target: []const u8) bool {
+ for (targets) |t| {
+ if (mem.eql(u8, t, target)) return true;
+ }
+ return false;
+}
+
+fn addObjCClassSymbols(self: *Dylib, sym_name: []const u8) !void {
+ const expanded = &[_][]const u8{
+ try std.fmt.allocPrint(self.allocator, "_OBJC_CLASS_$_{s}", .{sym_name}),
+ try std.fmt.allocPrint(self.allocator, "_OBJC_METACLASS_$_{s}", .{sym_name}),
+ };
+
+ for (expanded) |sym| {
+ if (self.symbols.contains(sym)) continue;
+ try self.symbols.putNoClobber(self.allocator, sym, .{});
+ }
+}
+
+pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {
+ if (lib_stub.inner.len == 0) return error.EmptyStubFile;
+
+ log.debug("parsing shared library from stub '{s}'", .{self.name.?});
+
+ const umbrella_lib = lib_stub.inner[0];
+
+ var id = Id.default(try self.allocator.dupe(u8, umbrella_lib.install_name));
+ if (umbrella_lib.current_version) |version| {
+ try id.parseCurrentVersion(version);
+ }
+ if (umbrella_lib.compatibility_version) |version| {
+ try id.parseCompatibilityVersion(version);
+ }
+ self.id = id;
+
+ const target_string: []const u8 = switch (self.arch.?) {
+ .aarch64 => "arm64-macos",
+ .x86_64 => "x86_64-macos",
+ else => unreachable,
};
- try file.seekTo(0);
- return header.filetype == macho.MH_DYLIB;
+
+ var umbrella_libs = std.StringHashMap(void).init(self.allocator);
+ defer umbrella_libs.deinit();
+
+ for (lib_stub.inner) |stub, stub_index| {
+ if (!hasTarget(stub.targets, target_string)) continue;
+
+ if (stub_index > 0) {
+ // TODO I thought that we could switch on presence of `parent-umbrella` map;
+ // however, turns out `libsystem_notify.dylib` is fully reexported by `libSystem.dylib`
+ // BUT does not feature a `parent-umbrella` map as the only sublib. Apple's bug perhaps?
+ try umbrella_libs.put(stub.install_name, .{});
+ }
+
+ if (stub.exports) |exports| {
+ for (exports) |exp| {
+ if (!hasTarget(exp.targets, target_string)) continue;
+
+ if (exp.symbols) |symbols| {
+ for (symbols) |sym_name| {
+ if (self.symbols.contains(sym_name)) continue;
+ try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {});
+ }
+ }
+
+ if (exp.objc_classes) |classes| {
+ for (classes) |sym_name| {
+ try self.addObjCClassSymbols(sym_name);
+ }
+ }
+ }
+ }
+
+ if (stub.reexports) |reexports| {
+ for (reexports) |reexp| {
+ if (!hasTarget(reexp.targets, target_string)) continue;
+
+ if (reexp.symbols) |symbols| {
+ for (symbols) |sym_name| {
+ if (self.symbols.contains(sym_name)) continue;
+ try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {});
+ }
+ }
+
+ if (reexp.objc_classes) |classes| {
+ for (classes) |sym_name| {
+ try self.addObjCClassSymbols(sym_name);
+ }
+ }
+ }
+ }
+
+ if (stub.objc_classes) |classes| {
+ for (classes) |sym_name| {
+ try self.addObjCClassSymbols(sym_name);
+ }
+ }
+ }
+
+ log.debug("{s}", .{umbrella_lib.install_name});
+
+ // TODO track which libs were already parsed in different steps
+ for (lib_stub.inner) |stub| {
+ if (!hasTarget(stub.targets, target_string)) continue;
+
+ if (stub.reexported_libraries) |reexports| {
+ for (reexports) |reexp| {
+ if (!hasTarget(reexp.targets, target_string)) continue;
+
+ for (reexp.libraries) |lib| {
+ if (umbrella_libs.contains(lib)) {
+ log.debug(" | {s} <= {s}", .{ lib, umbrella_lib.install_name });
+ continue;
+ }
+
+ log.debug(" | {s}", .{lib});
+ try self.dependent_libs.put(self.allocator, try self.allocator.dupe(u8, lib), {});
+ }
+ }
+ }
+ }
+}
+
+pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void {
+ outer: for (self.dependent_libs.keys()) |lib| {
+ const dirname = fs.path.dirname(lib) orelse {
+ log.warn("unable to resolve dependency {s}", .{lib});
+ continue;
+ };
+ const filename = fs.path.basename(lib);
+ const without_ext = if (mem.lastIndexOfScalar(u8, filename, '.')) |index|
+ filename[0..index]
+ else
+ filename;
+
+ for (&[_][]const u8{ "dylib", "tbd" }) |ext| {
+ const with_ext = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{
+ without_ext,
+ ext,
+ });
+ defer self.allocator.free(with_ext);
+
+ const lib_path = if (self.syslibroot) |syslibroot|
+ try fs.path.join(self.allocator, &.{ syslibroot, dirname, with_ext })
+ else
+ try fs.path.join(self.allocator, &.{ dirname, with_ext });
+
+ log.debug("trying dependency at fully resolved path {s}", .{lib_path});
+
+ const dylibs = (try createAndParseFromPath(
+ self.allocator,
+ self.arch.?,
+ lib_path,
+ self.syslibroot,
+ )) orelse {
+ continue;
+ };
+
+ try out.appendSlice(dylibs);
+
+ continue :outer;
+ } else {
+ log.warn("unable to resolve dependency {s}", .{lib});
+ }
+ }
}
pub fn createProxy(self: *Dylib, sym_name: []const u8) !?*Symbol {
@@ -197,7 +481,7 @@ pub fn createProxy(self: *Dylib, sym_name: []const u8) !?*Symbol {
.@"type" = .proxy,
.name = name,
},
- .file = .{ .dylib = self },
+ .file = self,
};
return &proxy.base;
diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig
index 64db2fe091..747adaab87 100644
--- a/src/link/MachO/Object.zig
+++ b/src/link/MachO/Object.zig
@@ -11,6 +11,7 @@ const mem = std.mem;
const reloc = @import("reloc.zig");
const Allocator = mem.Allocator;
+const Arch = std.Target.Cpu.Arch;
const Relocation = reloc.Relocation;
const Symbol = @import("Symbol.zig");
const parseName = @import("Zld.zig").parseName;
@@ -18,7 +19,7 @@ const parseName = @import("Zld.zig").parseName;
usingnamespace @import("commands.zig");
allocator: *Allocator,
-arch: ?std.Target.Cpu.Arch = null,
+arch: ?Arch = null,
header: ?macho.mach_header_64 = null,
file: ?fs.File = null,
file_offset: ?u32 = null,
@@ -173,10 +174,36 @@ const DebugInfo = struct {
}
};
-pub fn init(allocator: *Allocator) Object {
- return .{
+pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8) !?*Object {
+ const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
+ error.FileNotFound => return null,
+ else => |e| return e,
+ };
+ errdefer file.close();
+
+ const object = try allocator.create(Object);
+ errdefer allocator.destroy(object);
+
+ const name = try allocator.dupe(u8, path);
+ errdefer allocator.free(name);
+
+ object.* = .{
.allocator = allocator,
+ .arch = arch,
+ .name = name,
+ .file = file,
};
+
+ object.parse() catch |err| switch (err) {
+ error.EndOfStream, error.NotObject => {
+ object.deinit();
+ allocator.destroy(object);
+ return null;
+ },
+ else => |e| return e,
+ };
+
+ return object;
}
pub fn deinit(self: *Object) void {
@@ -223,11 +250,15 @@ pub fn parse(self: *Object) !void {
self.header = try reader.readStruct(macho.mach_header_64);
if (self.header.?.filetype != macho.MH_OBJECT) {
- log.err("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_OBJECT, self.header.?.filetype });
- return error.MalformedObject;
+ log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{
+ macho.MH_OBJECT,
+ self.header.?.filetype,
+ });
+
+ return error.NotObject;
}
- const this_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) {
+ const this_arch: Arch = switch (self.header.?.cputype) {
macho.CPU_TYPE_ARM64 => .aarch64,
macho.CPU_TYPE_X86_64 => .x86_64,
else => |value| {
@@ -533,12 +564,3 @@ 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 = file.reader().readStruct(macho.mach_header_64) catch |err| switch (err) {
- error.EndOfStream => return false,
- else => |e| return e,
- };
- try file.seekTo(0);
- return header.filetype == macho.MH_OBJECT;
-}
diff --git a/src/link/MachO/Stub.zig b/src/link/MachO/Stub.zig
deleted file mode 100644
index 3e1474539d..0000000000
--- a/src/link/MachO/Stub.zig
+++ /dev/null
@@ -1,130 +0,0 @@
-const Stub = @This();
-
-const std = @import("std");
-const assert = std.debug.assert;
-const fs = std.fs;
-const log = std.log.scoped(.stub);
-const macho = std.macho;
-const mem = std.mem;
-
-const Allocator = mem.Allocator;
-const Symbol = @import("Symbol.zig");
-pub const LibStub = @import("../tapi.zig").LibStub;
-
-allocator: *Allocator,
-arch: ?std.Target.Cpu.Arch = null,
-lib_stub: ?LibStub = null,
-name: ?[]const u8 = null,
-
-ordinal: ?u16 = null,
-
-id: ?Id = null,
-
-/// Parsed symbol table represented as hash map of symbols'
-/// names. We can and should defer creating *Symbols until
-/// a symbol is referenced by an object file.
-symbols: std.StringArrayHashMapUnmanaged(void) = .{},
-
-pub const Id = struct {
- name: []const u8,
- timestamp: u32,
- current_version: u32,
- compatibility_version: u32,
-
- pub fn deinit(id: *Id, allocator: *Allocator) void {
- allocator.free(id.name);
- }
-};
-
-pub fn init(allocator: *Allocator) Stub {
- return .{ .allocator = allocator };
-}
-
-pub fn deinit(self: *Stub) void {
- self.symbols.deinit(self.allocator);
-
- if (self.lib_stub) |*lib_stub| {
- lib_stub.deinit();
- }
-
- if (self.name) |name| {
- self.allocator.free(name);
- }
-
- if (self.id) |*id| {
- id.deinit(self.allocator);
- }
-}
-
-pub fn parse(self: *Stub) !void {
- const lib_stub = self.lib_stub orelse return error.EmptyStubFile;
- if (lib_stub.inner.len == 0) return error.EmptyStubFile;
-
- log.debug("parsing shared library from stub '{s}'", .{self.name.?});
-
- const umbrella_lib = lib_stub.inner[0];
- self.id = .{
- .name = try self.allocator.dupe(u8, umbrella_lib.install_name),
- // TODO parse from the stub
- .timestamp = 2,
- .current_version = 0,
- .compatibility_version = 0,
- };
-
- const target_string: []const u8 = switch (self.arch.?) {
- .aarch64 => "arm64-macos",
- .x86_64 => "x86_64-macos",
- else => unreachable,
- };
-
- for (lib_stub.inner) |stub| {
- if (!hasTarget(stub.targets, target_string)) continue;
-
- if (stub.exports) |exports| {
- for (exports) |exp| {
- if (!hasTarget(exp.targets, target_string)) continue;
-
- for (exp.symbols) |sym_name| {
- if (self.symbols.contains(sym_name)) continue;
- try self.symbols.putNoClobber(self.allocator, sym_name, {});
- }
- }
- }
-
- if (stub.reexports) |reexports| {
- for (reexports) |reexp| {
- if (!hasTarget(reexp.targets, target_string)) continue;
-
- for (reexp.symbols) |sym_name| {
- if (self.symbols.contains(sym_name)) continue;
- try self.symbols.putNoClobber(self.allocator, sym_name, {});
- }
- }
- }
- }
-}
-
-fn hasTarget(targets: []const []const u8, target: []const u8) bool {
- for (targets) |t| {
- if (mem.eql(u8, t, target)) return true;
- }
- return false;
-}
-
-pub fn createProxy(self: *Stub, sym_name: []const u8) !?*Symbol {
- if (!self.symbols.contains(sym_name)) return null;
-
- const name = try self.allocator.dupe(u8, sym_name);
- const proxy = try self.allocator.create(Symbol.Proxy);
- errdefer self.allocator.destroy(proxy);
-
- proxy.* = .{
- .base = .{
- .@"type" = .proxy,
- .name = name,
- },
- .file = .{ .stub = self },
- };
-
- return &proxy.base;
-}
diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig
index 2286b1ea93..c58af27672 100644
--- a/src/link/MachO/Symbol.zig
+++ b/src/link/MachO/Symbol.zig
@@ -7,7 +7,6 @@ const mem = std.mem;
const Allocator = mem.Allocator;
const Dylib = @import("Dylib.zig");
const Object = @import("Object.zig");
-const Stub = @import("Stub.zig");
pub const Type = enum {
regular,
@@ -85,21 +84,26 @@ pub const Regular = struct {
pub const Proxy = struct {
base: Symbol,
- /// Dylib or stub where to locate this symbol.
+ /// Dynamic binding info - spots within the final
+ /// executable where this proxy is referenced from.
+ bind_info: std.ArrayListUnmanaged(struct {
+ segment_id: u16,
+ address: u64,
+ }) = .{},
+
+ /// Dylib where to locate this symbol.
/// null means self-reference.
- file: ?union(enum) {
- dylib: *Dylib,
- stub: *Stub,
- } = null,
+ file: ?*Dylib = null,
pub const base_type: Symbol.Type = .proxy;
+ pub fn deinit(proxy: *Proxy, allocator: *Allocator) void {
+ proxy.bind_info.deinit(allocator);
+ }
+
pub fn dylibOrdinal(proxy: *Proxy) u16 {
- const file = proxy.file orelse return 0;
- return switch (file) {
- .dylib => |dylib| dylib.ordinal.?,
- .stub => |stub| stub.ordinal.?,
- };
+ const dylib = proxy.file orelse return 0;
+ return dylib.ordinal.?;
}
};
@@ -129,6 +133,10 @@ pub const Tentative = struct {
pub fn deinit(base: *Symbol, allocator: *Allocator) void {
allocator.free(base.name);
+ switch (base.@"type") {
+ .proxy => @fieldParentPtr(Proxy, "base", base).deinit(allocator),
+ else => {},
+ }
}
pub fn cast(base: *Symbol, comptime T: type) ?*T {
diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig
index 0a9d209c61..f391899159 100644
--- a/src/link/MachO/Zld.zig
+++ b/src/link/MachO/Zld.zig
@@ -17,7 +17,6 @@ const Archive = @import("Archive.zig");
const CodeSignature = @import("CodeSignature.zig");
const Dylib = @import("Dylib.zig");
const Object = @import("Object.zig");
-const Stub = @import("Stub.zig");
const Symbol = @import("Symbol.zig");
const Trie = @import("Trie.zig");
@@ -33,14 +32,14 @@ out_path: ?[]const u8 = null,
// TODO these args will become obselete once Zld is coalesced with incremental
// linker.
+syslibroot: ?[]const u8 = null,
stack_size: u64 = 0,
objects: std.ArrayListUnmanaged(*Object) = .{},
archives: std.ArrayListUnmanaged(*Archive) = .{},
dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
-lib_stubs: std.ArrayListUnmanaged(*Stub) = .{},
-libsystem_stub_index: ?u16 = null,
+libsystem_dylib_index: ?u16 = null,
next_dylib_ordinal: u16 = 1,
load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
@@ -73,12 +72,21 @@ gcc_except_tab_section_index: ?u16 = null,
unwind_info_section_index: ?u16 = null,
eh_frame_section_index: ?u16 = null,
+objc_methlist_section_index: ?u16 = null,
+objc_methname_section_index: ?u16 = null,
+objc_methtype_section_index: ?u16 = null,
+objc_classname_section_index: ?u16 = null,
+
// __DATA_CONST segment sections
got_section_index: ?u16 = null,
mod_init_func_section_index: ?u16 = null,
mod_term_func_section_index: ?u16 = null,
data_const_section_index: ?u16 = null,
+objc_cfstring_section_index: ?u16 = null,
+objc_classlist_section_index: ?u16 = null,
+objc_imageinfo_section_index: ?u16 = null,
+
// __DATA segment sections
tlv_section_index: ?u16 = null,
tlv_data_section_index: ?u16 = null,
@@ -88,6 +96,11 @@ data_section_index: ?u16 = null,
bss_section_index: ?u16 = null,
common_section_index: ?u16 = null,
+objc_const_section_index: ?u16 = null,
+objc_selrefs_section_index: ?u16 = null,
+objc_classrefs_section_index: ?u16 = null,
+objc_data_section_index: ?u16 = null,
+
globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
imports: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
@@ -120,10 +133,6 @@ const TlvOffset = struct {
/// Default path to dyld
const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
-const LIB_SYSTEM_NAME: [*:0]const u8 = "System";
-/// TODO this should be inferred from included libSystem.tbd or similar.
-const LIB_SYSTEM_PATH: [*:0]const u8 = "/usr/lib/libSystem.B.dylib";
-
pub fn init(allocator: *Allocator) Zld {
return .{ .allocator = allocator };
}
@@ -157,12 +166,6 @@ pub fn deinit(self: *Zld) void {
}
self.dylibs.deinit(self.allocator);
- for (self.lib_stubs.items) |stub| {
- stub.deinit();
- self.allocator.destroy(stub);
- }
- self.lib_stubs.deinit(self.allocator);
-
for (self.imports.values()) |proxy| {
proxy.deinit(self.allocator);
self.allocator.destroy(proxy);
@@ -235,7 +238,6 @@ 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.parseLibs(args.libs);
try self.parseLibSystem(args.libc_stub_path);
@@ -243,217 +245,92 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L
try self.resolveStubsAndGotEntries();
try self.updateMetadata();
try self.sortSections();
+ try self.addRpaths(args.rpaths);
+ try self.addDataInCodeLC();
+ try self.addCodeSignatureLC();
try self.allocateTextSegment();
try self.allocateDataConstSegment();
try self.allocateDataSegment();
self.allocateLinkeditSegment();
try self.allocateSymbols();
try self.allocateTentativeSymbols();
+ try self.allocateProxyBindAddresses();
try self.flush();
}
fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
- const Input = struct {
- kind: enum {
- object,
- archive,
- dylib,
- stub,
- },
- origin: union {
- file: fs.File,
- stub: Stub.LibStub,
- },
- name: []const u8,
- };
- var classified = std.ArrayList(Input).init(self.allocator);
- defer classified.deinit();
-
- // First, classify input files: object, archive, dylib or stub (tbd).
for (files) |file_name| {
- const file = try fs.cwd().openFile(file_name, .{});
const full_path = full_path: {
var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
const path = try std.fs.realpath(file_name, &buffer);
break :full_path try self.allocator.dupe(u8, path);
};
- try_object: {
- if (!(try Object.isObject(file))) break :try_object;
- try classified.append(.{
- .kind = .object,
- .origin = .{ .file = file },
- .name = full_path,
- });
- continue;
- }
-
- try_archive: {
- if (!(try Archive.isArchive(file))) break :try_archive;
- try classified.append(.{
- .kind = .archive,
- .origin = .{ .file = file },
- .name = full_path,
- });
+ if (try Object.createAndParseFromPath(self.allocator, self.arch.?, full_path)) |object| {
+ try self.objects.append(self.allocator, object);
continue;
}
- try_dylib: {
- if (!(try Dylib.isDylib(file))) break :try_dylib;
- try classified.append(.{
- .kind = .dylib,
- .origin = .{ .file = file },
- .name = full_path,
- });
+ if (try Archive.createAndParseFromPath(self.allocator, self.arch.?, full_path)) |archive| {
+ try self.archives.append(self.allocator, archive);
continue;
}
- try_stub: {
- var lib_stub = Stub.LibStub.loadFromFile(self.allocator, file) catch {
- break :try_stub;
- };
- try classified.append(.{
- .kind = .stub,
- .origin = .{ .stub = lib_stub },
- .name = full_path,
- });
- file.close();
+ if (try Dylib.createAndParseFromPath(
+ self.allocator,
+ self.arch.?,
+ full_path,
+ self.syslibroot,
+ )) |dylibs| {
+ defer self.allocator.free(dylibs);
+ try self.dylibs.appendSlice(self.allocator, dylibs);
continue;
}
- file.close();
log.warn("unknown filetype for positional input file: '{s}'", .{file_name});
}
-
- // Based on our classification, proceed with parsing.
- for (classified.items) |input| {
- switch (input.kind) {
- .object => {
- const object = try self.allocator.create(Object);
- errdefer self.allocator.destroy(object);
-
- object.* = Object.init(self.allocator);
- object.arch = self.arch.?;
- object.name = input.name;
- object.file = input.origin.file;
-
- try object.parse();
- try self.objects.append(self.allocator, object);
- },
- .archive => {
- const archive = try self.allocator.create(Archive);
- errdefer self.allocator.destroy(archive);
-
- archive.* = Archive.init(self.allocator);
- archive.arch = self.arch.?;
- archive.name = input.name;
- archive.file = input.origin.file;
-
- 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.origin.file;
-
- try dylib.parse();
- try self.dylibs.append(self.allocator, dylib);
- },
- .stub => {
- const stub = try self.allocator.create(Stub);
- errdefer self.allocator.destroy(stub);
-
- stub.* = Stub.init(self.allocator);
- stub.arch = self.arch.?;
- stub.name = input.name;
- stub.lib_stub = input.origin.stub;
-
- try stub.parse();
- try self.lib_stubs.append(self.allocator, stub);
- },
- }
- }
}
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;
+ if (try Dylib.createAndParseFromPath(
+ self.allocator,
+ self.arch.?,
+ lib,
+ self.syslibroot,
+ )) |dylibs| {
+ defer self.allocator.free(dylibs);
+ try self.dylibs.appendSlice(self.allocator, dylibs);
+ continue;
+ }
- try dylib.parse();
- try self.dylibs.append(self.allocator, dylib);
- } else {
- // Try tbd stub file next.
- if (Stub.LibStub.loadFromFile(self.allocator, file)) |lib_stub| {
- const stub = try self.allocator.create(Stub);
- errdefer self.allocator.destroy(stub);
-
- stub.* = Stub.init(self.allocator);
- stub.arch = self.arch.?;
- stub.name = try self.allocator.dupe(u8, lib);
- stub.lib_stub = lib_stub;
-
- try stub.parse();
- try self.lib_stubs.append(self.allocator, stub);
- } else |_| {
- // TODO this entire logic has to be cleaned up.
- try file.seekTo(0);
-
- 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});
- }
- }
+ if (try Archive.createAndParseFromPath(self.allocator, self.arch.?, lib)) |archive| {
+ try self.archives.append(self.allocator, archive);
+ continue;
}
+
+ log.warn("unknown filetype for a library: '{s}'", .{lib});
}
}
fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void {
- const file = try fs.cwd().openFile(libc_stub_path, .{});
- defer file.close();
-
- var lib_stub = try Stub.LibStub.loadFromFile(self.allocator, file);
-
- const stub = try self.allocator.create(Stub);
- errdefer self.allocator.destroy(stub);
-
- stub.* = Stub.init(self.allocator);
- stub.arch = self.arch.?;
- stub.name = try self.allocator.dupe(u8, libc_stub_path);
- stub.lib_stub = lib_stub;
+ const dylibs = (try Dylib.createAndParseFromPath(
+ self.allocator,
+ self.arch.?,
+ libc_stub_path,
+ self.syslibroot,
+ )) orelse return error.FailedToParseLibSystem;
+ defer self.allocator.free(dylibs);
- try stub.parse();
+ assert(dylibs.len == 1); // More than one dylib output from parsing libSystem!
+ const dylib = dylibs[0];
- self.libsystem_stub_index = @intCast(u16, self.lib_stubs.items.len);
- try self.lib_stubs.append(self.allocator, stub);
+ self.libsystem_dylib_index = @intCast(u16, self.dylibs.items.len);
+ try self.dylibs.append(self.allocator, dylib);
// Add LC_LOAD_DYLIB load command.
- stub.ordinal = self.next_dylib_ordinal;
- const dylib_id = stub.id orelse unreachable;
+ dylib.ordinal = self.next_dylib_ordinal;
+ const dylib_id = dylib.id orelse unreachable;
var dylib_cmd = try createLoadDylibCommand(
self.allocator,
dylib_id.name,
@@ -501,428 +378,22 @@ fn mapAndUpdateSections(
fn updateMetadata(self: *Zld) !void {
for (self.objects.items) |object| {
- const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
- const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
- const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
-
- // Create missing metadata
- for (object.sections.items) |sect| {
- const segname = sect.segname();
- const sectname = sect.sectname();
-
- switch (sect.sectionType()) {
- macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS, macho.S_LITERAL_POINTERS => {
- if (self.text_const_section_index != null) continue;
-
- self.text_const_section_index = @intCast(u16, text_seg.sections.items.len);
- try text_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__const"),
- .segname = makeStaticString("__TEXT"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- continue;
- },
- macho.S_CSTRING_LITERALS => {
- if (self.cstring_section_index != null) continue;
-
- self.cstring_section_index = @intCast(u16, text_seg.sections.items.len);
- try text_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__cstring"),
- .segname = makeStaticString("__TEXT"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_CSTRING_LITERALS,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- continue;
- },
- macho.S_MOD_INIT_FUNC_POINTERS => {
- if (self.mod_init_func_section_index != null) continue;
-
- self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len);
- try data_const_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__mod_init_func"),
- .segname = makeStaticString("__DATA_CONST"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_MOD_INIT_FUNC_POINTERS,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- continue;
- },
- macho.S_MOD_TERM_FUNC_POINTERS => {
- if (self.mod_term_func_section_index != null) continue;
-
- self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len);
- try data_const_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__mod_term_func"),
- .segname = makeStaticString("__DATA_CONST"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_MOD_TERM_FUNC_POINTERS,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- continue;
- },
- macho.S_ZEROFILL => {
- if (mem.eql(u8, sectname, "__common")) {
- if (self.common_section_index != null) continue;
-
- self.common_section_index = @intCast(u16, data_seg.sections.items.len);
- try data_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__common"),
- .segname = makeStaticString("__DATA"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_ZEROFILL,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- } else {
- if (self.bss_section_index != null) continue;
-
- self.bss_section_index = @intCast(u16, data_seg.sections.items.len);
- try data_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__bss"),
- .segname = makeStaticString("__DATA"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_ZEROFILL,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- }
- continue;
- },
- macho.S_THREAD_LOCAL_VARIABLES => {
- if (self.tlv_section_index != null) continue;
-
- self.tlv_section_index = @intCast(u16, data_seg.sections.items.len);
- try data_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__thread_vars"),
- .segname = makeStaticString("__DATA"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_THREAD_LOCAL_VARIABLES,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- continue;
- },
- macho.S_THREAD_LOCAL_REGULAR => {
- if (self.tlv_data_section_index != null) continue;
-
- self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len);
- try data_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__thread_data"),
- .segname = makeStaticString("__DATA"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_THREAD_LOCAL_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- continue;
- },
- macho.S_THREAD_LOCAL_ZEROFILL => {
- if (self.tlv_bss_section_index != null) continue;
-
- self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len);
- try data_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__thread_bss"),
- .segname = makeStaticString("__DATA"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_THREAD_LOCAL_ZEROFILL,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- continue;
- },
- macho.S_COALESCED => {
- if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) {
- // TODO I believe __eh_frame is currently part of __unwind_info section
- // in the latest ld64 output.
- if (self.eh_frame_section_index != null) continue;
-
- self.eh_frame_section_index = @intCast(u16, text_seg.sections.items.len);
- try text_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__eh_frame"),
- .segname = makeStaticString("__TEXT"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- continue;
- }
-
- // TODO audit this: is this the right mapping?
- if (self.data_const_section_index != null) continue;
-
- self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
- try data_const_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__const"),
- .segname = makeStaticString("__DATA_CONST"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- continue;
- },
- macho.S_REGULAR => {
- if (sect.isCode()) {
- if (self.text_section_index != null) continue;
-
- self.text_section_index = @intCast(u16, text_seg.sections.items.len);
- try text_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__text"),
- .segname = makeStaticString("__TEXT"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- continue;
- }
-
- if (sect.isDebug()) {
- if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
- log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
- sect.flags(), segname, sectname,
- });
- }
- continue;
- }
-
- if (mem.eql(u8, segname, "__TEXT")) {
- if (mem.eql(u8, sectname, "__ustring")) {
- if (self.ustring_section_index != null) continue;
-
- self.ustring_section_index = @intCast(u16, text_seg.sections.items.len);
- try text_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__ustring"),
- .segname = makeStaticString("__TEXT"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- } else if (mem.eql(u8, sectname, "__gcc_except_tab")) {
- if (self.gcc_except_tab_section_index != null) continue;
-
- self.gcc_except_tab_section_index = @intCast(u16, text_seg.sections.items.len);
- try text_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__gcc_except_tab"),
- .segname = makeStaticString("__TEXT"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- } else {
- if (self.text_const_section_index != null) continue;
-
- self.text_const_section_index = @intCast(u16, text_seg.sections.items.len);
- try text_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__const"),
- .segname = makeStaticString("__TEXT"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- }
- continue;
- }
-
- if (mem.eql(u8, segname, "__DATA_CONST")) {
- if (self.data_const_section_index != null) continue;
-
- self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
- try data_const_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__const"),
- .segname = makeStaticString("__DATA_CONST"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- continue;
- }
-
- if (mem.eql(u8, segname, "__DATA")) {
- if (mem.eql(u8, sectname, "__const")) {
- if (self.data_const_section_index != null) continue;
-
- self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
- try data_const_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__const"),
- .segname = makeStaticString("__DATA_CONST"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- } else {
- if (self.data_section_index != null) continue;
-
- self.data_section_index = @intCast(u16, data_seg.sections.items.len);
- try data_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__data"),
- .segname = makeStaticString("__DATA"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
- }
-
- continue;
- }
-
- if (mem.eql(u8, "__LLVM", segname) and mem.eql(u8, "__asm", sectname)) {
- log.debug("TODO LLVM asm section: type 0x{x}, name '{s},{s}'", .{
- sect.flags(), segname, sectname,
- });
- continue;
- }
- },
- else => {},
- }
-
- log.err("{s}: unhandled section type 0x{x} for '{s},{s}'", .{
- object.name.?,
- sect.flags(),
- segname,
- sectname,
- });
- return error.UnhandledSection;
- }
-
- // Find ideal section alignment.
- for (object.sections.items) |sect| {
- if (self.getMatchingSection(sect)) |res| {
- const target_seg = &self.load_commands.items[res.seg].Segment;
- const target_sect = &target_seg.sections.items[res.sect];
- target_sect.@"align" = math.max(target_sect.@"align", sect.inner.@"align");
- }
- }
-
- // Update section mappings
+ // Find ideal section alignment and update section mappings
for (object.sections.items) |sect, sect_id| {
- if (self.getMatchingSection(sect)) |res| {
- try self.mapAndUpdateSections(object, @intCast(u16, sect_id), res.seg, res.sect);
+ const match = (try self.getMatchingSection(sect)) orelse {
+ log.debug("{s}: unhandled section type 0x{x} for '{s},{s}'", .{
+ object.name.?,
+ sect.flags(),
+ sect.segname(),
+ sect.sectname(),
+ });
continue;
- }
- log.debug("section '{s},{s}' will be unmapped", .{ sect.segname(), sect.sectname() });
+ };
+ const target_seg = &self.load_commands.items[match.seg].Segment;
+ const target_sect = &target_seg.sections.items[match.sect];
+ target_sect.@"align" = math.max(target_sect.@"align", sect.inner.@"align");
+
+ try self.mapAndUpdateSections(object, @intCast(u16, sect_id), match.seg, match.sect);
}
}
@@ -932,19 +403,8 @@ fn updateMetadata(self: *Zld) !void {
const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
const common_section_index = self.common_section_index orelse ind: {
self.common_section_index = @intCast(u16, data_seg.sections.items.len);
- try data_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__common"),
- .segname = makeStaticString("__DATA"),
- .addr = 0,
- .size = 0,
- .offset = 0,
- .@"align" = 0,
- .reloff = 0,
- .nreloc = 0,
+ try data_seg.addSection(self.allocator, "__common", .{
.flags = macho.S_ZEROFILL,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
break :ind self.common_section_index.?;
};
@@ -1018,31 +478,116 @@ const MatchingSection = struct {
sect: u16,
};
-fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection {
+fn getMatchingSection(self: *Zld, sect: Object.Section) !?MatchingSection {
+ const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+ const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
+ const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
const segname = sect.segname();
const sectname = sect.sectname();
const res: ?MatchingSection = blk: {
switch (sect.sectionType()) {
- macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS, macho.S_LITERAL_POINTERS => {
+ macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => {
+ if (self.text_const_section_index == null) {
+ self.text_const_section_index = @intCast(u16, text_seg.sections.items.len);
+ try text_seg.addSection(self.allocator, "__const", .{});
+ }
+
break :blk .{
.seg = self.text_segment_cmd_index.?,
.sect = self.text_const_section_index.?,
};
},
macho.S_CSTRING_LITERALS => {
+ if (mem.eql(u8, sectname, "__objc_methname")) {
+ // TODO it seems the common values within the sections in objects are deduplicated/merged
+ // on merging the sections' contents.
+ if (self.objc_methname_section_index == null) {
+ self.objc_methname_section_index = @intCast(u16, text_seg.sections.items.len);
+ try text_seg.addSection(self.allocator, "__objc_methname", .{
+ .flags = macho.S_CSTRING_LITERALS,
+ });
+ }
+
+ break :blk .{
+ .seg = self.text_segment_cmd_index.?,
+ .sect = self.objc_methname_section_index.?,
+ };
+ } else if (mem.eql(u8, sectname, "__objc_methtype")) {
+ if (self.objc_methtype_section_index == null) {
+ self.objc_methtype_section_index = @intCast(u16, text_seg.sections.items.len);
+ try text_seg.addSection(self.allocator, "__objc_methtype", .{
+ .flags = macho.S_CSTRING_LITERALS,
+ });
+ }
+
+ break :blk .{
+ .seg = self.text_segment_cmd_index.?,
+ .sect = self.objc_methtype_section_index.?,
+ };
+ } else if (mem.eql(u8, sectname, "__objc_classname")) {
+ if (self.objc_classname_section_index == null) {
+ self.objc_classname_section_index = @intCast(u16, text_seg.sections.items.len);
+ try text_seg.addSection(self.allocator, "__objc_classname", .{});
+ }
+
+ break :blk .{
+ .seg = self.text_segment_cmd_index.?,
+ .sect = self.objc_classname_section_index.?,
+ };
+ }
+
+ if (self.cstring_section_index == null) {
+ self.cstring_section_index = @intCast(u16, text_seg.sections.items.len);
+ try text_seg.addSection(self.allocator, "__cstring", .{
+ .flags = macho.S_CSTRING_LITERALS,
+ });
+ }
+
break :blk .{
.seg = self.text_segment_cmd_index.?,
.sect = self.cstring_section_index.?,
};
},
+ macho.S_LITERAL_POINTERS => {
+ if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__objc_selrefs")) {
+ if (self.objc_selrefs_section_index == null) {
+ self.objc_selrefs_section_index = @intCast(u16, data_seg.sections.items.len);
+ try data_seg.addSection(self.allocator, "__objc_selrefs", .{
+ .flags = macho.S_LITERAL_POINTERS,
+ });
+ }
+
+ break :blk .{
+ .seg = self.data_segment_cmd_index.?,
+ .sect = self.objc_selrefs_section_index.?,
+ };
+ }
+
+ // TODO investigate
+ break :blk null;
+ },
macho.S_MOD_INIT_FUNC_POINTERS => {
+ if (self.mod_init_func_section_index == null) {
+ self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len);
+ try data_const_seg.addSection(self.allocator, "__mod_init_func", .{
+ .flags = macho.S_MOD_INIT_FUNC_POINTERS,
+ });
+ }
+
break :blk .{
.seg = self.data_const_segment_cmd_index.?,
.sect = self.mod_init_func_section_index.?,
};
},
macho.S_MOD_TERM_FUNC_POINTERS => {
+ if (self.mod_term_func_section_index == null) {
+ self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len);
+ try data_const_seg.addSection(self.allocator, "__mod_term_func", .{
+ .flags = macho.S_MOD_TERM_FUNC_POINTERS,
+ });
+ }
+
break :blk .{
.seg = self.data_const_segment_cmd_index.?,
.sect = self.mod_term_func_section_index.?,
@@ -1050,11 +595,25 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection {
},
macho.S_ZEROFILL => {
if (mem.eql(u8, sectname, "__common")) {
+ if (self.common_section_index == null) {
+ self.common_section_index = @intCast(u16, data_seg.sections.items.len);
+ try data_seg.addSection(self.allocator, "__common", .{
+ .flags = macho.S_ZEROFILL,
+ });
+ }
+
break :blk .{
.seg = self.data_segment_cmd_index.?,
.sect = self.common_section_index.?,
};
} else {
+ if (self.bss_section_index == null) {
+ self.bss_section_index = @intCast(u16, data_seg.sections.items.len);
+ try data_seg.addSection(self.allocator, "__bss", .{
+ .flags = macho.S_ZEROFILL,
+ });
+ }
+
break :blk .{
.seg = self.data_segment_cmd_index.?,
.sect = self.bss_section_index.?,
@@ -1062,18 +621,39 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection {
}
},
macho.S_THREAD_LOCAL_VARIABLES => {
+ if (self.tlv_section_index == null) {
+ self.tlv_section_index = @intCast(u16, data_seg.sections.items.len);
+ try data_seg.addSection(self.allocator, "__thread_vars", .{
+ .flags = macho.S_THREAD_LOCAL_VARIABLES,
+ });
+ }
+
break :blk .{
.seg = self.data_segment_cmd_index.?,
.sect = self.tlv_section_index.?,
};
},
macho.S_THREAD_LOCAL_REGULAR => {
+ if (self.tlv_data_section_index == null) {
+ self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len);
+ try data_seg.addSection(self.allocator, "__thread_data", .{
+ .flags = macho.S_THREAD_LOCAL_REGULAR,
+ });
+ }
+
break :blk .{
.seg = self.data_segment_cmd_index.?,
.sect = self.tlv_data_section_index.?,
};
},
macho.S_THREAD_LOCAL_ZEROFILL => {
+ if (self.tlv_bss_section_index == null) {
+ self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len);
+ try data_seg.addSection(self.allocator, "__thread_bss", .{
+ .flags = macho.S_THREAD_LOCAL_ZEROFILL,
+ });
+ }
+
break :blk .{
.seg = self.data_segment_cmd_index.?,
.sect = self.tlv_bss_section_index.?,
@@ -1081,12 +661,25 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection {
},
macho.S_COALESCED => {
if (mem.eql(u8, "__TEXT", segname) and mem.eql(u8, "__eh_frame", sectname)) {
+ // TODO I believe __eh_frame is currently part of __unwind_info section
+ // in the latest ld64 output.
+ if (self.eh_frame_section_index == null) {
+ self.eh_frame_section_index = @intCast(u16, text_seg.sections.items.len);
+ try text_seg.addSection(self.allocator, "__eh_frame", .{});
+ }
+
break :blk .{
.seg = self.text_segment_cmd_index.?,
.sect = self.eh_frame_section_index.?,
};
}
+ // TODO audit this: is this the right mapping?
+ if (self.data_const_section_index == null) {
+ self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
+ try data_const_seg.addSection(self.allocator, "__const", .{});
+ }
+
break :blk .{
.seg = self.data_const_segment_cmd_index.?,
.sect = self.data_const_section_index.?,
@@ -1094,6 +687,13 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection {
},
macho.S_REGULAR => {
if (sect.isCode()) {
+ if (self.text_section_index == null) {
+ self.text_section_index = @intCast(u16, text_seg.sections.items.len);
+ try text_seg.addSection(self.allocator, "__text", .{
+ .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
+ });
+ }
+
break :blk .{
.seg = self.text_segment_cmd_index.?,
.sect = self.text_section_index.?,
@@ -1101,21 +701,51 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection {
}
if (sect.isDebug()) {
// TODO debug attributes
+ if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
+ log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
+ sect.flags(), segname, sectname,
+ });
+ }
break :blk null;
}
if (mem.eql(u8, segname, "__TEXT")) {
if (mem.eql(u8, sectname, "__ustring")) {
+ if (self.ustring_section_index == null) {
+ self.ustring_section_index = @intCast(u16, text_seg.sections.items.len);
+ try text_seg.addSection(self.allocator, "__ustring", .{});
+ }
+
break :blk .{
.seg = self.text_segment_cmd_index.?,
.sect = self.ustring_section_index.?,
};
} else if (mem.eql(u8, sectname, "__gcc_except_tab")) {
+ if (self.gcc_except_tab_section_index == null) {
+ self.gcc_except_tab_section_index = @intCast(u16, text_seg.sections.items.len);
+ try text_seg.addSection(self.allocator, "__gcc_except_tab", .{});
+ }
+
break :blk .{
.seg = self.text_segment_cmd_index.?,
.sect = self.gcc_except_tab_section_index.?,
};
+ } else if (mem.eql(u8, sectname, "__objc_methlist")) {
+ if (self.objc_methlist_section_index == null) {
+ self.objc_methlist_section_index = @intCast(u16, text_seg.sections.items.len);
+ try text_seg.addSection(self.allocator, "__objc_methlist", .{});
+ }
+
+ break :blk .{
+ .seg = self.text_segment_cmd_index.?,
+ .sect = self.objc_methlist_section_index.?,
+ };
} else {
+ if (self.text_const_section_index == null) {
+ self.text_const_section_index = @intCast(u16, text_seg.sections.items.len);
+ try text_seg.addSection(self.allocator, "__const", .{});
+ }
+
break :blk .{
.seg = self.text_segment_cmd_index.?,
.sect = self.text_const_section_index.?,
@@ -1124,6 +754,11 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection {
}
if (mem.eql(u8, segname, "__DATA_CONST")) {
+ if (self.data_const_section_index == null) {
+ self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
+ try data_const_seg.addSection(self.allocator, "__const", .{});
+ }
+
break :blk .{
.seg = self.data_const_segment_cmd_index.?,
.sect = self.data_const_section_index.?,
@@ -1132,15 +767,92 @@ fn getMatchingSection(self: *Zld, sect: Object.Section) ?MatchingSection {
if (mem.eql(u8, segname, "__DATA")) {
if (mem.eql(u8, sectname, "__const")) {
+ if (self.data_const_section_index == null) {
+ self.data_const_section_index = @intCast(u16, data_const_seg.sections.items.len);
+ try data_const_seg.addSection(self.allocator, "__const", .{});
+ }
+
break :blk .{
.seg = self.data_const_segment_cmd_index.?,
.sect = self.data_const_section_index.?,
};
+ } else if (mem.eql(u8, sectname, "__cfstring")) {
+ if (self.objc_cfstring_section_index == null) {
+ self.objc_cfstring_section_index = @intCast(u16, data_const_seg.sections.items.len);
+ try data_const_seg.addSection(self.allocator, "__cfstring", .{});
+ }
+
+ break :blk .{
+ .seg = self.data_const_segment_cmd_index.?,
+ .sect = self.objc_cfstring_section_index.?,
+ };
+ } else if (mem.eql(u8, sectname, "__objc_classlist")) {
+ if (self.objc_classlist_section_index == null) {
+ self.objc_classlist_section_index = @intCast(u16, data_const_seg.sections.items.len);
+ try data_const_seg.addSection(self.allocator, "__objc_classlist", .{});
+ }
+
+ break :blk .{
+ .seg = self.data_const_segment_cmd_index.?,
+ .sect = self.objc_classlist_section_index.?,
+ };
+ } else if (mem.eql(u8, sectname, "__objc_imageinfo")) {
+ if (self.objc_imageinfo_section_index == null) {
+ self.objc_imageinfo_section_index = @intCast(u16, data_const_seg.sections.items.len);
+ try data_const_seg.addSection(self.allocator, "__objc_imageinfo", .{});
+ }
+
+ break :blk .{
+ .seg = self.data_const_segment_cmd_index.?,
+ .sect = self.objc_imageinfo_section_index.?,
+ };
+ } else if (mem.eql(u8, sectname, "__objc_const")) {
+ if (self.objc_const_section_index == null) {
+ self.objc_const_section_index = @intCast(u16, data_seg.sections.items.len);
+ try data_seg.addSection(self.allocator, "__objc_const", .{});
+ }
+
+ break :blk .{
+ .seg = self.data_segment_cmd_index.?,
+ .sect = self.objc_const_section_index.?,
+ };
+ } else if (mem.eql(u8, sectname, "__objc_classrefs")) {
+ if (self.objc_classrefs_section_index == null) {
+ self.objc_classrefs_section_index = @intCast(u16, data_seg.sections.items.len);
+ try data_seg.addSection(self.allocator, "__objc_classrefs", .{});
+ }
+
+ break :blk .{
+ .seg = self.data_segment_cmd_index.?,
+ .sect = self.objc_classrefs_section_index.?,
+ };
+ } else if (mem.eql(u8, sectname, "__objc_data")) {
+ if (self.objc_data_section_index == null) {
+ self.objc_data_section_index = @intCast(u16, data_seg.sections.items.len);
+ try data_seg.addSection(self.allocator, "__objc_data", .{});
+ }
+
+ break :blk .{
+ .seg = self.data_segment_cmd_index.?,
+ .sect = self.objc_data_section_index.?,
+ };
+ } else {
+ if (self.data_section_index == null) {
+ self.data_section_index = @intCast(u16, data_seg.sections.items.len);
+ try data_seg.addSection(self.allocator, "__data", .{});
+ }
+
+ break :blk .{
+ .seg = self.data_segment_cmd_index.?,
+ .sect = self.data_section_index.?,
+ };
}
- break :blk .{
- .seg = self.data_segment_cmd_index.?,
- .sect = self.data_section_index.?,
- };
+ }
+
+ if (mem.eql(u8, "__LLVM", segname) and mem.eql(u8, "__asm", sectname)) {
+ log.debug("TODO LLVM asm section: type 0x{x}, name '{s},{s}'", .{
+ sect.flags(), segname, sectname,
+ });
}
break :blk null;
@@ -1175,6 +887,9 @@ fn sortSections(self: *Zld) !void {
&self.cstring_section_index,
&self.ustring_section_index,
&self.text_const_section_index,
+ &self.objc_methname_section_index,
+ &self.objc_methtype_section_index,
+ &self.objc_classname_section_index,
&self.eh_frame_section_index,
};
for (indices) |maybe_index| {
@@ -1200,6 +915,9 @@ fn sortSections(self: *Zld) !void {
&self.mod_init_func_section_index,
&self.mod_term_func_section_index,
&self.data_const_section_index,
+ &self.objc_cfstring_section_index,
+ &self.objc_classlist_section_index,
+ &self.objc_imageinfo_section_index,
};
for (indices) |maybe_index| {
const new_index: u16 = if (maybe_index.*) |index| blk: {
@@ -1222,6 +940,10 @@ fn sortSections(self: *Zld) !void {
// __DATA segment
const indices = &[_]*?u16{
&self.la_symbol_ptr_section_index,
+ &self.objc_const_section_index,
+ &self.objc_selrefs_section_index,
+ &self.objc_classrefs_section_index,
+ &self.objc_data_section_index,
&self.data_section_index,
&self.tlv_section_index,
&self.tlv_data_section_index,
@@ -1487,6 +1209,31 @@ fn allocateTentativeSymbols(self: *Zld) !void {
}
}
+fn allocateProxyBindAddresses(self: *Zld) !void {
+ for (self.objects.items) |object| {
+ for (object.sections.items) |sect| {
+ const relocs = sect.relocs orelse continue;
+
+ for (relocs) |rel| {
+ if (rel.@"type" != .unsigned) continue; // GOT is currently special-cased
+ if (rel.target != .symbol) continue;
+
+ const sym = rel.target.symbol.getTopmostAlias();
+ if (sym.cast(Symbol.Proxy)) |proxy| {
+ const target_map = sect.target_map orelse continue;
+ const target_seg = self.load_commands.items[target_map.segment_id].Segment;
+ const target_sect = target_seg.sections.items[target_map.section_id];
+
+ try proxy.bind_info.append(self.allocator, .{
+ .segment_id = target_map.segment_id,
+ .address = target_sect.addr + target_map.offset + rel.offset,
+ });
+ }
+ }
+ }
+ }
+}
+
fn writeStubHelperCommon(self: *Zld) !void {
const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
@@ -1893,25 +1640,16 @@ fn resolveSymbols(self: *Zld) !void {
}
self.unresolved.clearRetainingCapacity();
- var referenced = std.AutoHashMap(union(enum) {
- dylib: *Dylib,
- stub: *Stub,
- }, void).init(self.allocator);
+ var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator);
defer referenced.deinit();
loop: while (unresolved.popOrNull()) |undef| {
const proxy = self.imports.get(undef.name) orelse outer: {
const proxy = inner: {
- for (self.dylibs.items) |dylib| {
+ for (self.dylibs.items) |dylib, i| {
const proxy = (try dylib.createProxy(undef.name)) orelse continue;
- try referenced.put(.{ .dylib = dylib }, {});
- break :inner proxy;
- }
- for (self.lib_stubs.items) |stub, i| {
- const proxy = (try stub.createProxy(undef.name)) orelse continue;
- if (self.libsystem_stub_index.? != @intCast(u16, i)) {
- // LibSystem gets its load command separately.
- try referenced.put(.{ .stub = stub }, {});
+ if (self.libsystem_dylib_index.? != @intCast(u16, i)) { // LibSystem gets load command seperately.
+ try referenced.put(dylib, {});
}
break :inner proxy;
}
@@ -1944,33 +1682,17 @@ fn resolveSymbols(self: *Zld) !void {
// Add LC_LOAD_DYLIB load command for each referenced dylib/stub.
var it = referenced.iterator();
- while (it.next()) |key| {
- var dylib_cmd = blk: {
- switch (key.key_ptr.*) {
- .dylib => |dylib| {
- dylib.ordinal = self.next_dylib_ordinal;
- const dylib_id = dylib.id orelse unreachable;
- break :blk try createLoadDylibCommand(
- self.allocator,
- dylib_id.name,
- dylib_id.timestamp,
- dylib_id.current_version,
- dylib_id.compatibility_version,
- );
- },
- .stub => |stub| {
- stub.ordinal = self.next_dylib_ordinal;
- const dylib_id = stub.id orelse unreachable;
- break :blk try createLoadDylibCommand(
- self.allocator,
- dylib_id.name,
- dylib_id.timestamp,
- dylib_id.current_version,
- dylib_id.compatibility_version,
- );
- },
- }
- };
+ while (it.next()) |entry| {
+ const dylib = entry.key_ptr.*;
+ dylib.ordinal = self.next_dylib_ordinal;
+ 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 });
self.next_dylib_ordinal += 1;
@@ -1988,8 +1710,8 @@ fn resolveSymbols(self: *Zld) !void {
}
// Finally put dyld_stub_binder as an Import
- const libsystem_stub = self.lib_stubs.items[self.libsystem_stub_index.?];
- const proxy = (try libsystem_stub.createProxy("dyld_stub_binder")) orelse {
+ const libsystem_dylib = self.dylibs.items[self.libsystem_dylib_index.?];
+ const proxy = (try libsystem_dylib.createProxy("dyld_stub_binder")) orelse {
log.err("undefined reference to symbol 'dyld_stub_binder'", .{});
return error.UndefinedSymbolReference;
};
@@ -2004,6 +1726,7 @@ fn resolveStubsAndGotEntries(self: *Zld) !void {
const relocs = sect.relocs orelse continue;
for (relocs) |rel| {
switch (rel.@"type") {
+ .unsigned => continue,
.got_page, .got_page_off, .got_load, .got, .pointer_to_got => {
const sym = rel.target.symbol.getTopmostAlias();
if (sym.got_index != null) continue;
@@ -2089,29 +1812,52 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
args.source_target_sect_addr = source_sect.inner.addr;
}
- rebases: {
- var hit: bool = false;
- if (target_map.segment_id == self.data_segment_cmd_index.?) {
- if (self.data_section_index) |index| {
- if (index == target_map.section_id) hit = true;
+ const flags = @truncate(u8, target_sect.flags & 0xff);
+ const should_rebase = rebase: {
+ if (!unsigned.is_64bit) break :rebase false;
+
+ // TODO actually, a check similar to what dyld is doing, that is, verifying
+ // that the segment is writable should be enough here.
+ const is_right_segment = blk: {
+ if (self.data_segment_cmd_index) |idx| {
+ if (target_map.segment_id == idx) {
+ break :blk true;
+ }
+ }
+ if (self.data_const_segment_cmd_index) |idx| {
+ if (target_map.segment_id == idx) {
+ break :blk true;
+ }
}
+ break :blk false;
+ };
+
+ if (!is_right_segment) break :rebase false;
+ if (flags != macho.S_LITERAL_POINTERS and
+ flags != macho.S_REGULAR)
+ {
+ break :rebase false;
}
- if (target_map.segment_id == self.data_const_segment_cmd_index.?) {
- if (self.data_const_section_index) |index| {
- if (index == target_map.section_id) hit = true;
+ if (rel.target == .symbol) {
+ const final = rel.target.symbol.getTopmostAlias();
+ if (final.cast(Symbol.Proxy)) |_| {
+ break :rebase false;
}
}
- if (!hit) break :rebases;
+ break :rebase true;
+ };
+ if (should_rebase) {
try self.local_rebases.append(self.allocator, .{
.offset = source_addr - target_seg.inner.vmaddr,
.segment_id = target_map.segment_id,
});
}
+
// TLV is handled via a separate offset mechanism.
// Calculate the offset to the initializer.
- if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
+ if (flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
// TODO we don't want to save offset to tlv_bootstrap
if (mem.eql(u8, rel.target.symbol.name, "__tlv_bootstrap")) break :tlv;
@@ -2208,7 +1954,13 @@ fn relocTargetAddr(self: *Zld, object: *const Object, target: reloc.Relocation.T
const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
const stubs = segment.sections.items[self.stubs_section_index.?];
const stubs_index = proxy.base.stubs_index orelse {
- log.err("expected stubs index when relocating symbol '{s}'", .{final.name});
+ if (proxy.bind_info.items.len > 0) {
+ break :blk 0; // Dynamically bound by dyld.
+ }
+ log.err(
+ "expected stubs index or dynamic bind address when relocating symbol '{s}'",
+ .{final.name},
+ );
log.err("this is an internal linker error", .{});
return error.FailedToResolveRelocationTarget;
};
@@ -2240,18 +1992,8 @@ fn populateMetadata(self: *Zld) !void {
if (self.pagezero_segment_cmd_index == null) {
self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.allocator, .{
- .Segment = SegmentCommand.empty(.{
- .cmd = macho.LC_SEGMENT_64,
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString("__PAGEZERO"),
- .vmaddr = 0,
+ .Segment = SegmentCommand.empty("__PAGEZERO", .{
.vmsize = 0x100000000, // size always set to 4GB
- .fileoff = 0,
- .filesize = 0,
- .maxprot = 0,
- .initprot = 0,
- .nsects = 0,
- .flags = 0,
}),
});
}
@@ -2259,18 +2001,10 @@ fn populateMetadata(self: *Zld) !void {
if (self.text_segment_cmd_index == null) {
self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.allocator, .{
- .Segment = SegmentCommand.empty(.{
- .cmd = macho.LC_SEGMENT_64,
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString("__TEXT"),
+ .Segment = SegmentCommand.empty("__TEXT", .{
.vmaddr = 0x100000000, // always starts at 4GB
- .vmsize = 0,
- .fileoff = 0,
- .filesize = 0,
.maxprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
.initprot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE,
- .nsects = 0,
- .flags = 0,
}),
});
}
@@ -2283,19 +2017,9 @@ fn populateMetadata(self: *Zld) !void {
.aarch64 => 2,
else => unreachable, // unhandled architecture type
};
- try text_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__text"),
- .segname = makeStaticString("__TEXT"),
- .addr = 0,
- .size = 0,
- .offset = 0,
+ try text_seg.addSection(self.allocator, "__text", .{
.@"align" = alignment,
- .reloff = 0,
- .nreloc = 0,
.flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
}
@@ -2312,19 +2036,10 @@ fn populateMetadata(self: *Zld) !void {
.aarch64 => 3 * @sizeOf(u32),
else => unreachable, // unhandled architecture type
};
- try text_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__stubs"),
- .segname = makeStaticString("__TEXT"),
- .addr = 0,
- .size = 0,
- .offset = 0,
+ try text_seg.addSection(self.allocator, "__stubs", .{
.@"align" = alignment,
- .reloff = 0,
- .nreloc = 0,
.flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
- .reserved1 = 0,
.reserved2 = stub_size,
- .reserved3 = 0,
});
}
@@ -2341,37 +2056,19 @@ fn populateMetadata(self: *Zld) !void {
.aarch64 => 6 * @sizeOf(u32),
else => unreachable,
};
- try text_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__stub_helper"),
- .segname = makeStaticString("__TEXT"),
- .addr = 0,
+ try text_seg.addSection(self.allocator, "__stub_helper", .{
.size = stub_helper_size,
- .offset = 0,
.@"align" = alignment,
- .reloff = 0,
- .nreloc = 0,
.flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
}
if (self.data_const_segment_cmd_index == null) {
self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.allocator, .{
- .Segment = SegmentCommand.empty(.{
- .cmd = macho.LC_SEGMENT_64,
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString("__DATA_CONST"),
- .vmaddr = 0,
- .vmsize = 0,
- .fileoff = 0,
- .filesize = 0,
+ .Segment = SegmentCommand.empty("__DATA_CONST", .{
.maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
.initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
- .nsects = 0,
- .flags = 0,
}),
});
}
@@ -2379,37 +2076,18 @@ fn populateMetadata(self: *Zld) !void {
if (self.got_section_index == null) {
const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
self.got_section_index = @intCast(u16, data_const_seg.sections.items.len);
- try data_const_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__got"),
- .segname = makeStaticString("__DATA_CONST"),
- .addr = 0,
- .size = 0,
- .offset = 0,
+ try data_const_seg.addSection(self.allocator, "__got", .{
.@"align" = 3, // 2^3 = @sizeOf(u64)
- .reloff = 0,
- .nreloc = 0,
.flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
}
if (self.data_segment_cmd_index == null) {
self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.allocator, .{
- .Segment = SegmentCommand.empty(.{
- .cmd = macho.LC_SEGMENT_64,
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString("__DATA"),
- .vmaddr = 0,
- .vmsize = 0,
- .fileoff = 0,
- .filesize = 0,
+ .Segment = SegmentCommand.empty("__DATA", .{
.maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
.initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE,
- .nsects = 0,
- .flags = 0,
}),
});
}
@@ -2417,56 +2095,26 @@ fn populateMetadata(self: *Zld) !void {
if (self.la_symbol_ptr_section_index == null) {
const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
self.la_symbol_ptr_section_index = @intCast(u16, data_seg.sections.items.len);
- try data_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__la_symbol_ptr"),
- .segname = makeStaticString("__DATA"),
- .addr = 0,
- .size = 0,
- .offset = 0,
+ try data_seg.addSection(self.allocator, "__la_symbol_ptr", .{
.@"align" = 3, // 2^3 = @sizeOf(u64)
- .reloff = 0,
- .nreloc = 0,
.flags = macho.S_LAZY_SYMBOL_POINTERS,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
}
if (self.data_section_index == null) {
const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
self.data_section_index = @intCast(u16, data_seg.sections.items.len);
- try data_seg.addSection(self.allocator, .{
- .sectname = makeStaticString("__data"),
- .segname = makeStaticString("__DATA"),
- .addr = 0,
- .size = 0,
- .offset = 0,
+ try data_seg.addSection(self.allocator, "__data", .{
.@"align" = 3, // 2^3 = @sizeOf(u64)
- .reloff = 0,
- .nreloc = 0,
- .flags = macho.S_REGULAR,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
});
}
if (self.linkedit_segment_cmd_index == null) {
self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.allocator, .{
- .Segment = SegmentCommand.empty(.{
- .cmd = macho.LC_SEGMENT_64,
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString("__LINKEDIT"),
- .vmaddr = 0,
- .vmsize = 0,
- .fileoff = 0,
- .filesize = 0,
+ .Segment = SegmentCommand.empty("__LINKEDIT", .{
.maxprot = macho.VM_PROT_READ,
.initprot = macho.VM_PROT_READ,
- .nsects = 0,
- .flags = 0,
}),
});
}
@@ -2585,24 +2233,28 @@ fn populateMetadata(self: *Zld) !void {
std.crypto.random.bytes(&uuid_cmd.uuid);
try self.load_commands.append(self.allocator, .{ .Uuid = uuid_cmd });
}
+}
- if (self.code_signature_cmd_index == null and self.arch.? == .aarch64) {
- self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
+fn addDataInCodeLC(self: *Zld) !void {
+ if (self.data_in_code_cmd_index == null) {
+ self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.allocator, .{
.LinkeditData = .{
- .cmd = macho.LC_CODE_SIGNATURE,
+ .cmd = macho.LC_DATA_IN_CODE,
.cmdsize = @sizeOf(macho.linkedit_data_command),
.dataoff = 0,
.datasize = 0,
},
});
}
+}
- if (self.data_in_code_cmd_index == null and self.arch.? == .x86_64) {
- self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
+fn addCodeSignatureLC(self: *Zld) !void {
+ if (self.code_signature_cmd_index == null and self.arch.? == .aarch64) {
+ self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.allocator, .{
.LinkeditData = .{
- .cmd = macho.LC_DATA_IN_CODE,
+ .cmd = macho.LC_CODE_SIGNATURE,
.cmdsize = @sizeOf(macho.linkedit_data_command),
.dataoff = 0,
.datasize = 0,
@@ -2698,9 +2350,7 @@ fn flush(self: *Zld) !void {
try self.writeBindInfoTable();
try self.writeLazyBindInfoTable();
try self.writeExportInfo();
- if (self.arch.? == .x86_64) {
- try self.writeDataInCode();
- }
+ try self.writeDataInCode();
{
const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
@@ -2862,6 +2512,20 @@ fn writeBindInfoTable(self: *Zld) !void {
}
}
+ for (self.imports.values()) |sym| {
+ if (sym.cast(Symbol.Proxy)) |proxy| {
+ for (proxy.bind_info.items) |info| {
+ const seg = self.load_commands.items[info.segment_id].Segment;
+ try pointers.append(.{
+ .offset = info.address - seg.inner.vmaddr,
+ .segment_id = info.segment_id,
+ .dylib_ordinal = proxy.dylibOrdinal(),
+ .name = proxy.base.name,
+ });
+ }
+ }
+ }
+
if (self.tlv_section_index) |idx| {
const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
const sect = seg.sections.items[idx];
@@ -3469,13 +3133,6 @@ fn writeHeader(self: *Zld) !void {
try self.file.?.pwriteAll(mem.asBytes(&header), 0);
}
-pub fn makeStaticString(bytes: []const u8) [16]u8 {
- var buf = [_]u8{0} ** 16;
- assert(bytes.len <= buf.len);
- mem.copy(u8, &buf, bytes);
- return buf;
-}
-
fn makeString(self: *Zld, bytes: []const u8) !u32 {
if (self.strtab_dir.get(bytes)) |offset| {
log.debug("reusing '{s}' from string table at offset 0x{x}", .{ bytes, offset });
diff --git a/src/link/MachO/commands.zig b/src/link/MachO/commands.zig
index 8bceb64f1e..6958b8d1e6 100644
--- a/src/link/MachO/commands.zig
+++ b/src/link/MachO/commands.zig
@@ -9,7 +9,6 @@ const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const MachO = @import("../MachO.zig");
-const makeStaticString = MachO.makeStaticString;
const padToIdeal = MachO.padToIdeal;
pub const LoadCommand = union(enum) {
@@ -187,11 +186,70 @@ pub const SegmentCommand = struct {
inner: macho.segment_command_64,
sections: std.ArrayListUnmanaged(macho.section_64) = .{},
- pub fn empty(inner: macho.segment_command_64) SegmentCommand {
- return .{ .inner = inner };
+ const SegmentOptions = struct {
+ cmdsize: u32 = @sizeOf(macho.segment_command_64),
+ vmaddr: u64 = 0,
+ vmsize: u64 = 0,
+ fileoff: u64 = 0,
+ filesize: u64 = 0,
+ maxprot: macho.vm_prot_t = macho.VM_PROT_NONE,
+ initprot: macho.vm_prot_t = macho.VM_PROT_NONE,
+ nsects: u32 = 0,
+ flags: u32 = 0,
+ };
+
+ pub fn empty(comptime segname: []const u8, opts: SegmentOptions) SegmentCommand {
+ return .{
+ .inner = .{
+ .cmd = macho.LC_SEGMENT_64,
+ .cmdsize = opts.cmdsize,
+ .segname = makeStaticString(segname),
+ .vmaddr = opts.vmaddr,
+ .vmsize = opts.vmsize,
+ .fileoff = opts.fileoff,
+ .filesize = opts.filesize,
+ .maxprot = opts.maxprot,
+ .initprot = opts.initprot,
+ .nsects = opts.nsects,
+ .flags = opts.flags,
+ },
+ };
}
- pub fn addSection(self: *SegmentCommand, alloc: *Allocator, section: macho.section_64) !void {
+ const SectionOptions = struct {
+ addr: u64 = 0,
+ size: u64 = 0,
+ offset: u32 = 0,
+ @"align": u32 = 0,
+ reloff: u32 = 0,
+ nreloc: u32 = 0,
+ flags: u32 = macho.S_REGULAR,
+ reserved1: u32 = 0,
+ reserved2: u32 = 0,
+ reserved3: u32 = 0,
+ };
+
+ pub fn addSection(
+ self: *SegmentCommand,
+ alloc: *Allocator,
+ comptime sectname: []const u8,
+ opts: SectionOptions,
+ ) !void {
+ var section = macho.section_64{
+ .sectname = makeStaticString(sectname),
+ .segname = undefined,
+ .addr = opts.addr,
+ .size = opts.size,
+ .offset = opts.offset,
+ .@"align" = opts.@"align",
+ .reloff = opts.reloff,
+ .nreloc = opts.nreloc,
+ .flags = opts.flags,
+ .reserved1 = opts.reserved1,
+ .reserved2 = opts.reserved2,
+ .reserved3 = opts.reserved3,
+ };
+ mem.copy(u8, &section.segname, &self.inner.segname);
try self.sections.append(alloc, section);
self.inner.cmdsize += @sizeOf(macho.section_64);
self.inner.nsects += 1;
@@ -338,6 +396,13 @@ pub fn createLoadDylibCommand(
return dylib_cmd;
}
+fn makeStaticString(bytes: []const u8) [16]u8 {
+ var buf = [_]u8{0} ** 16;
+ assert(bytes.len <= buf.len);
+ mem.copy(u8, &buf, bytes);
+ return buf;
+}
+
fn testRead(allocator: *Allocator, buffer: []const u8, expected: anytype) !void {
var stream = io.fixedBufferStream(buffer);
var given = try LoadCommand.read(allocator, stream.reader());
diff --git a/src/link/tapi.zig b/src/link/tapi.zig
index efa7227def..35193b0eec 100644
--- a/src/link/tapi.zig
+++ b/src/link/tapi.zig
@@ -26,6 +26,11 @@ pub const LibStub = struct {
float: f64,
int: u64,
},
+ compatibility_version: ?union(enum) {
+ string: []const u8,
+ float: f64,
+ int: u64,
+ },
reexported_libraries: ?[]const struct {
targets: []const []const u8,
libraries: []const []const u8,
@@ -36,11 +41,13 @@ pub const LibStub = struct {
},
exports: ?[]const struct {
targets: []const []const u8,
- symbols: []const []const u8,
+ symbols: ?[]const []const u8,
+ objc_classes: ?[]const []const u8,
},
reexports: ?[]const struct {
targets: []const []const u8,
- symbols: []const []const u8,
+ symbols: ?[]const []const u8,
+ objc_classes: ?[]const []const u8,
},
allowable_clients: ?[]const struct {
targets: []const []const u8,
diff --git a/src/link/tapi/parse.zig b/src/link/tapi/parse.zig
index 1e40ac63dc..0c923f961b 100644
--- a/src/link/tapi/parse.zig
+++ b/src/link/tapi/parse.zig
@@ -42,7 +42,7 @@ pub const Node = struct {
.doc => @fieldParentPtr(Node.Doc, "base", self).deinit(allocator),
.map => @fieldParentPtr(Node.Map, "base", self).deinit(allocator),
.list => @fieldParentPtr(Node.List, "base", self).deinit(allocator),
- .value => @fieldParentPtr(Node.Value, "base", self).deinit(allocator),
+ .value => {},
}
}
@@ -180,11 +180,6 @@ pub const Node = struct {
pub const base_tag: Node.Tag = .value;
- pub fn deinit(self: *Value, allocator: *Allocator) void {
- _ = self;
- _ = allocator;
- }
-
pub fn format(
self: *const Value,
comptime fmt: []const u8,
diff --git a/src/main.zig b/src/main.zig
index f2d00c47bb..1cf62c011b 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -290,6 +290,7 @@ const usage_build_generic =
\\ .c C source code (requires LLVM extensions)
\\ .cpp C++ source code (requires LLVM extensions)
\\ Other C++ extensions: .C .cc .cxx
+ \\ .m Objective-C source code (requires LLVM extensions)
\\
\\General Options:
\\ -h, --help Print this help and exit
@@ -1072,7 +1073,7 @@ fn buildOutputType(
.object, .static_library, .shared_library => {
try link_objects.append(arg);
},
- .assembly, .c, .cpp, .h, .ll, .bc => {
+ .assembly, .c, .cpp, .h, .ll, .bc, .m => {
try c_source_files.append(.{
.src_path = arg,
.extra_flags = try arena.dupe([]const u8, extra_cflags.items),
@@ -1135,7 +1136,7 @@ fn buildOutputType(
.positional => {
const file_ext = Compilation.classifyFileExt(mem.spanZ(it.only_arg));
switch (file_ext) {
- .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(.{ .src_path = it.only_arg }),
+ .assembly, .c, .cpp, .ll, .bc, .h, .m => try c_source_files.append(.{ .src_path = it.only_arg }),
.unknown, .shared_library, .object, .static_library => {
try link_objects.append(it.only_arg);
},