authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-29 02:50:40+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-01-29 02:50:40+01:00
logabb8e7478d365088301a9390c12f86b2ad381ce9
tree800c02a60af03be8dd4136d86d8168491e4f6673
parent96a5f7c8edac4bb2f50bdfe31c1287207d90d29b
parent5b315f8a3a6c925bdcbbc182d5bdc14f5f726146
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #18714 from ziglang/macho-mem

macho: reduce heap allocations

11 files changed, 405 insertions(+), 296 deletions(-)

build.zig+1-1
......@@ -623,7 +623,7 @@ fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.St
623623 .root_source_file = .{ .path = "src/main.zig" },
624624 .target = options.target,
625625 .optimize = options.optimize,
626 .max_rss = 8_000_000_000,
626 .max_rss = 7_000_000_000,
627627 .strip = options.strip,
628628 .sanitize_thread = options.sanitize_thread,
629629 .single_threaded = options.single_threaded,
src/link/MachO.zig+17-40
......@@ -610,7 +610,10 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
610610 if (mem.indexOf(u8, sect.segName(), "ZIG") == null) continue; // Non-Zig sections are handled separately
611611 // TODO: we will resolve and write ZigObject's TLS data twice:
612612 // once here, and once in writeAtoms
613 const code = zo.getAtomDataAlloc(self, gpa, atom.*) catch |err| switch (err) {
613 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
614 const code = try gpa.alloc(u8, atom_size);
615 defer gpa.free(code);
616 atom.getData(self, code) catch |err| switch (err) {
614617 error.InputOutput => {
615618 try self.reportUnexpectedError("fetching code for '{s}' failed", .{
616619 atom.getName(self),
......@@ -625,7 +628,6 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node
625628 return error.FlushFailure;
626629 },
627630 };
628 defer gpa.free(code);
629631 const file_offset = sect.offset + atom.value - sect.addr;
630632 atom.resolveRelocs(self, code) catch |err| switch (err) {
631633 error.ResolveFailed => has_resolve_error = true,
......@@ -974,17 +976,15 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void {
974976
975977 const gpa = self.base.comp.gpa;
976978 const file = try std.fs.cwd().openFile(path, .{});
977 defer file.close();
978979 const mtime: u64 = mtime: {
979980 const stat = file.stat() catch break :mtime 0;
980981 break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
981982 };
982 const data = try file.readToEndAlloc(gpa, std.math.maxInt(u32));
983983 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
984984 self.files.set(index, .{ .object = .{
985985 .path = try gpa.dupe(u8, path),
986 .file = file,
986987 .mtime = mtime,
987 .data = data,
988988 .index = index,
989989 } });
990990 try self.objects.append(gpa, index);
......@@ -1013,17 +1013,9 @@ fn parseArchive(self: *MachO, lib: SystemLib, must_link: bool, fat_arch: ?fat.Ar
10131013 const file = try std.fs.cwd().openFile(lib.path, .{});
10141014 defer file.close();
10151015
1016 const data = if (fat_arch) |arch| blk: {
1017 try file.seekTo(arch.offset);
1018 const data = try gpa.alloc(u8, arch.size);
1019 const nread = try file.readAll(data);
1020 if (nread != arch.size) return error.InputOutput;
1021 break :blk data;
1022 } else try file.readToEndAlloc(gpa, std.math.maxInt(u32));
1023
1024 var archive = Archive{ .path = try gpa.dupe(u8, lib.path), .data = data };
1016 var archive = Archive{};
10251017 defer archive.deinit(gpa);
1026 try archive.parse(self);
1018 try archive.parse(self, lib.path, file, fat_arch);
10271019
10281020 var has_parse_error = false;
10291021 for (archive.objects.items) |extracted| {
......@@ -1058,18 +1050,9 @@ fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch)
10581050 const file = try std.fs.cwd().openFile(lib.path, .{});
10591051 defer file.close();
10601052
1061 const data = if (fat_arch) |arch| blk: {
1062 try file.seekTo(arch.offset);
1063 const data = try gpa.alloc(u8, arch.size);
1064 const nread = try file.readAll(data);
1065 if (nread != arch.size) return error.InputOutput;
1066 break :blk data;
1067 } else try file.readToEndAlloc(gpa, std.math.maxInt(u32));
1068
10691053 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
10701054 self.files.set(index, .{ .dylib = .{
10711055 .path = try gpa.dupe(u8, lib.path),
1072 .data = data,
10731056 .index = index,
10741057 .needed = lib.needed,
10751058 .weak = lib.weak,
......@@ -1077,7 +1060,7 @@ fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch)
10771060 .explicit = explicit,
10781061 } });
10791062 const dylib = &self.files.items(.data)[index].dylib;
1080 try dylib.parse(self);
1063 try dylib.parse(self, file, fat_arch);
10811064
10821065 try self.dylibs.append(gpa, index);
10831066
......@@ -1098,7 +1081,6 @@ fn parseTbd(self: *MachO, lib: SystemLib, explicit: bool) ParseError!File.Index
10981081 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
10991082 self.files.set(index, .{ .dylib = .{
11001083 .path = try gpa.dupe(u8, lib.path),
1101 .data = &[0]u8{},
11021084 .index = index,
11031085 .needed = lib.needed,
11041086 .weak = lib.weak,
......@@ -1404,6 +1386,8 @@ pub fn resolveSymbols(self: *MachO) !void {
14041386 const index = self.objects.items[i];
14051387 if (!self.getFile(index).?.object.alive) {
14061388 _ = self.objects.orderedRemove(i);
1389 self.files.items(.data)[index].object.deinit(self.base.comp.gpa);
1390 self.files.set(index, .null);
14071391 } else i += 1;
14081392 }
14091393
......@@ -1511,18 +1495,13 @@ fn createObjcSections(self: *MachO) !void {
15111495 }
15121496
15131497 for (objc_msgsend_syms.keys()) |sym_index| {
1498 const internal = self.getInternalObject().?;
15141499 const sym = self.getSymbol(sym_index);
1515 sym.value = 0;
1516 sym.atom = 0;
1517 sym.nlist_idx = 0;
1518 sym.file = self.internal_object.?;
1519 sym.flags = .{};
1500 _ = try internal.addSymbol(sym.getName(self), self);
15201501 sym.visibility = .hidden;
1521 const object = self.getInternalObject().?;
15221502 const name = eatPrefix(sym.getName(self), "_objc_msgSend$").?;
1523 const selrefs_index = try object.addObjcMsgsendSections(name, self);
1503 const selrefs_index = try internal.addObjcMsgsendSections(name, self);
15241504 try sym.addExtra(.{ .objc_selrefs = selrefs_index }, self);
1525 try object.symbols.append(gpa, sym_index);
15261505 }
15271506}
15281507
......@@ -1659,6 +1638,8 @@ fn deadStripDylibs(self: *MachO) void {
16591638 const index = self.dylibs.items[i];
16601639 if (!self.getFile(index).?.dylib.isAlive(self)) {
16611640 _ = self.dylibs.orderedRemove(i);
1641 self.files.items(.data)[index].dylib.deinit(self.base.comp.gpa);
1642 self.files.set(index, .null);
16621643 } else i += 1;
16631644 }
16641645}
......@@ -2609,13 +2590,8 @@ fn writeAtoms(self: *MachO) !void {
26092590 const atom = self.getAtom(atom_index).?;
26102591 assert(atom.flags.alive);
26112592 const off = math.cast(usize, atom.value - header.addr) orelse return error.Overflow;
2612 const data = switch (atom.getFile(self)) {
2613 .object => |x| try x.getAtomData(atom.*),
2614 .zig_object => |x| try x.getAtomDataAlloc(self, arena.allocator(), atom.*),
2615 else => unreachable,
2616 };
26172593 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
2618 @memcpy(buffer[off..][0..atom_size], data);
2594 try atom.getData(self, buffer[off..][0..atom_size]);
26192595 atom.resolveRelocs(self, buffer[off..][0..atom_size]) catch |err| switch (err) {
26202596 error.ResolveFailed => has_resolve_error = true,
26212597 else => |e| return e,
......@@ -3734,6 +3710,7 @@ pub fn getOrCreateGlobal(self: *MachO, off: u32) !GetOrCreateGlobalResult {
37343710 const index = try self.addSymbol();
37353711 const global = self.getSymbol(index);
37363712 global.name = off;
3713 global.flags.global = true;
37373714 gop.value_ptr.* = index;
37383715 }
37393716 return .{
src/link/MachO/Archive.zig+26-18
......@@ -1,6 +1,3 @@
1path: []const u8,
2data: []const u8,
3
41objects: std.ArrayListUnmanaged(Object) = .{},
52
63// Archive files start with the ARMAG identifying string. Then follows a
......@@ -73,62 +70,73 @@ pub fn isArchive(path: []const u8, fat_arch: ?fat.Arch) !bool {
7370}
7471
7572pub fn deinit(self: *Archive, allocator: Allocator) void {
76 allocator.free(self.data);
77 allocator.free(self.path);
7873 self.objects.deinit(allocator);
7974}
8075
81pub fn parse(self: *Archive, macho_file: *MachO) !void {
76pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, file: std.fs.File, fat_arch: ?fat.Arch) !void {
8277 const gpa = macho_file.base.comp.gpa;
8378
8479 var arena = std.heap.ArenaAllocator.init(gpa);
8580 defer arena.deinit();
8681
87 var stream = std.io.fixedBufferStream(self.data);
88 const reader = stream.reader();
89 _ = try reader.readBytesNoEof(SARMAG);
82 const offset = if (fat_arch) |ar| ar.offset else 0;
83 const size = if (fat_arch) |ar| ar.size else (try file.stat()).size;
84 try file.seekTo(offset);
85
86 const reader = file.reader();
87 _ = try reader.readBytesNoEof(Archive.SARMAG);
9088
89 var pos: usize = Archive.SARMAG;
9190 while (true) {
92 if (stream.pos >= self.data.len) break;
93 if (!mem.isAligned(stream.pos, 2)) stream.pos += 1;
91 if (pos >= size) break;
92 if (!mem.isAligned(pos, 2)) {
93 try file.seekBy(1);
94 pos += 1;
95 }
9496
9597 const hdr = try reader.readStruct(ar_hdr);
98 pos += @sizeOf(ar_hdr);
9699
97100 if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) {
98 try macho_file.reportParseError(self.path, "invalid header delimiter: expected '{s}', found '{s}'", .{
101 try macho_file.reportParseError(path, "invalid header delimiter: expected '{s}', found '{s}'", .{
99102 std.fmt.fmtSliceEscapeLower(ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag),
100103 });
101104 return error.MalformedArchive;
102105 }
103106
104 var size = try hdr.size();
107 var hdr_size = try hdr.size();
105108 const name = name: {
106109 if (hdr.name()) |n| break :name n;
107110 if (try hdr.nameLength()) |len| {
108 size -= len;
111 hdr_size -= len;
109112 const buf = try arena.allocator().alloc(u8, len);
110113 try reader.readNoEof(buf);
114 pos += len;
111115 const actual_len = mem.indexOfScalar(u8, buf, @as(u8, 0)) orelse len;
112116 break :name buf[0..actual_len];
113117 }
114118 unreachable;
115119 };
116120 defer {
117 _ = stream.seekBy(size) catch {};
121 _ = file.seekBy(hdr_size) catch {};
122 pos += hdr_size;
118123 }
119124
120125 if (mem.eql(u8, name, "__.SYMDEF") or mem.eql(u8, name, "__.SYMDEF SORTED")) continue;
121126
122127 const object = Object{
123 .archive = try gpa.dupe(u8, self.path),
128 .archive = .{
129 .path = try gpa.dupe(u8, path),
130 .offset = offset + pos,
131 },
124132 .path = try gpa.dupe(u8, name),
125 .data = try gpa.dupe(u8, self.data[stream.pos..][0..size]),
133 .file = try std.fs.cwd().openFile(path, .{}),
126134 .index = undefined,
127135 .alive = false,
128136 .mtime = hdr.date() catch 0,
129137 };
130138
131 log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, self.path });
139 log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, path });
132140
133141 try self.objects.append(gpa, object);
134142 }
src/link/MachO/Atom.zig+19-5
......@@ -43,26 +43,40 @@ prev_index: Index = 0,
4343next_index: Index = 0,
4444
4545pub fn getName(self: Atom, macho_file: *MachO) [:0]const u8 {
46 return macho_file.strings.getAssumeExists(self.name);
46 return switch (self.getFile(macho_file)) {
47 .dylib => unreachable,
48 .zig_object => |x| x.strtab.getAssumeExists(self.name),
49 inline else => |x| x.getString(self.name),
50 };
4751}
4852
4953pub fn getFile(self: Atom, macho_file: *MachO) File {
5054 return macho_file.getFile(self.file).?;
5155}
5256
57pub fn getData(self: Atom, macho_file: *MachO, buffer: []u8) !void {
58 assert(buffer.len == self.size);
59 switch (self.getFile(macho_file)) {
60 .internal => |x| try x.getAtomData(self, buffer),
61 .object => |x| try x.getAtomData(self, buffer),
62 .zig_object => |x| try x.getAtomData(macho_file, self, buffer),
63 else => unreachable,
64 }
65}
66
5367pub fn getRelocs(self: Atom, macho_file: *MachO) []const Relocation {
5468 return switch (self.getFile(macho_file)) {
55 .zig_object => |x| x.getAtomRelocs(self),
56 .object => |x| x.getAtomRelocs(self),
57 else => unreachable,
69 .dylib => unreachable,
70 inline else => |x| x.getAtomRelocs(self),
5871 };
5972}
6073
6174pub fn getInputSection(self: Atom, macho_file: *MachO) macho.section_64 {
6275 return switch (self.getFile(macho_file)) {
76 .dylib => unreachable,
6377 .zig_object => |x| x.getInputSection(self, macho_file),
6478 .object => |x| x.sections.items(.header)[self.n_sect],
65 else => unreachable,
79 .internal => |x| x.sections.items(.header)[self.n_sect],
6680 };
6781}
6882
src/link/MachO/DwarfInfo.zig+39-20
......@@ -1,15 +1,17 @@
1debug_info: []const u8,
2debug_abbrev: []const u8,
3debug_str: []const u8,
4
51/// Abbreviation table indexed by offset in the .debug_abbrev bytestream
62abbrev_tables: std.AutoArrayHashMapUnmanaged(u64, AbbrevTable) = .{},
73/// List of compile units as they appear in the .debug_info bytestream
84compile_units: std.ArrayListUnmanaged(CompileUnit) = .{},
9
10pub fn init(dw: *DwarfInfo, allocator: Allocator) !void {
11 try dw.parseAbbrevTables(allocator);
12 try dw.parseCompileUnits(allocator);
5/// Debug info string table
6strtab: std.ArrayListUnmanaged(u8) = .{},
7/// Debug info data
8di_data: std.ArrayListUnmanaged(u8) = .{},
9
10pub fn init(dw: *DwarfInfo, allocator: Allocator, di: DebugInfo) !void {
11 try dw.strtab.ensureTotalCapacityPrecise(allocator, di.debug_str.len);
12 dw.strtab.appendSliceAssumeCapacity(di.debug_str);
13 try dw.parseAbbrevTables(allocator, di);
14 try dw.parseCompileUnits(allocator, di);
1315}
1416
1517pub fn deinit(dw: *DwarfInfo, allocator: Allocator) void {
......@@ -18,18 +20,27 @@ pub fn deinit(dw: *DwarfInfo, allocator: Allocator) void {
1820 cu.deinit(allocator);
1921 }
2022 dw.compile_units.deinit(allocator);
23 dw.strtab.deinit(allocator);
24 dw.di_data.deinit(allocator);
25}
26
27fn appendDiData(dw: *DwarfInfo, allocator: Allocator, values: []const u8) error{OutOfMemory}!u32 {
28 const index: u32 = @intCast(dw.di_data.items.len);
29 try dw.di_data.ensureUnusedCapacity(allocator, values.len);
30 dw.di_data.appendSliceAssumeCapacity(values);
31 return index;
2132}
2233
2334fn getString(dw: DwarfInfo, off: usize) [:0]const u8 {
24 assert(off < dw.debug_str.len);
25 return mem.sliceTo(@as([*:0]const u8, @ptrCast(dw.debug_str.ptr + off)), 0);
35 assert(off < dw.strtab.items.len);
36 return mem.sliceTo(@as([*:0]const u8, @ptrCast(dw.strtab.items.ptr + off)), 0);
2637}
2738
28fn parseAbbrevTables(dw: *DwarfInfo, allocator: Allocator) !void {
39fn parseAbbrevTables(dw: *DwarfInfo, allocator: Allocator, di: DebugInfo) !void {
2940 const tracy = trace(@src());
3041 defer tracy.end();
3142
32 const debug_abbrev = dw.debug_abbrev;
43 const debug_abbrev = di.debug_abbrev;
3344 var stream = std.io.fixedBufferStream(debug_abbrev);
3445 var creader = std.io.countingReader(stream.reader());
3546 const reader = creader.reader();
......@@ -77,11 +88,11 @@ fn parseAbbrevTables(dw: *DwarfInfo, allocator: Allocator) !void {
7788 }
7889}
7990
80fn parseCompileUnits(dw: *DwarfInfo, allocator: Allocator) !void {
91fn parseCompileUnits(dw: *DwarfInfo, allocator: Allocator, di: DebugInfo) !void {
8192 const tracy = trace(@src());
8293 defer tracy.end();
8394
84 const debug_info = dw.debug_info;
95 const debug_info = di.debug_info;
8596 var stream = std.io.fixedBufferStream(debug_info);
8697 var creader = std.io.countingReader(stream.reader());
8798 const reader = creader.reader();
......@@ -107,7 +118,7 @@ fn parseCompileUnits(dw: *DwarfInfo, allocator: Allocator) !void {
107118 cu.header.address_size = try reader.readInt(u8, .little);
108119
109120 const table = dw.abbrev_tables.get(cu.header.debug_abbrev_offset).?;
110 try dw.parseDie(allocator, cu, table, null, &creader);
121 try dw.parseDie(allocator, cu, table, di, null, &creader);
111122 }
112123}
113124
......@@ -116,6 +127,7 @@ fn parseDie(
116127 allocator: Allocator,
117128 cu: *CompileUnit,
118129 table: AbbrevTable,
130 di: DebugInfo,
119131 parent: ?u32,
120132 creader: anytype,
121133) anyerror!void {
......@@ -140,19 +152,20 @@ fn parseDie(
140152 }
141153
142154 const decl = table.decls.get(code) orelse return error.MalformedDwarf; // TODO better errors
143 const data = dw.debug_info;
155 const data = di.debug_info;
144156 try cu.diePtr(die).values.ensureTotalCapacityPrecise(allocator, decl.attrs.values().len);
145157
146158 for (decl.attrs.values()) |attr| {
147159 const start = std.math.cast(usize, creader.bytes_read) orelse return error.Overflow;
148160 try advanceByFormSize(cu, attr.form, creader);
149161 const end = std.math.cast(usize, creader.bytes_read) orelse return error.Overflow;
150 cu.diePtr(die).values.appendAssumeCapacity(data[start..end]);
162 const index = try dw.appendDiData(allocator, data[start..end]);
163 cu.diePtr(die).values.appendAssumeCapacity(.{ .index = index, .len = @intCast(end - start) });
151164 }
152165
153166 if (decl.children) {
154167 // Open scope
155 try dw.parseDie(allocator, cu, table, die, creader);
168 try dw.parseDie(allocator, cu, table, di, die, creader);
156169 }
157170 }
158171}
......@@ -340,7 +353,7 @@ pub const CompileUnit = struct {
340353
341354pub const Die = struct {
342355 code: Code,
343 values: std.ArrayListUnmanaged([]const u8) = .{},
356 values: std.ArrayListUnmanaged(struct { index: u32, len: u32 }) = .{},
344357 children: std.ArrayListUnmanaged(Die.Index) = .{},
345358
346359 pub fn deinit(die: *Die, gpa: Allocator) void {
......@@ -354,7 +367,7 @@ pub const Die = struct {
354367 const index = decl.attrs.getIndex(at) orelse return null;
355368 const attr = decl.attrs.values()[index];
356369 const value = die.values.items[index];
357 return .{ .attr = attr, .bytes = value };
370 return .{ .attr = attr, .bytes = ctx.di_data.items[value.index..][0..value.len] };
358371 }
359372
360373 pub const Index = u32;
......@@ -458,6 +471,12 @@ pub const Format = enum {
458471 dwarf64,
459472};
460473
474const DebugInfo = struct {
475 debug_info: []const u8,
476 debug_abbrev: []const u8,
477 debug_str: []const u8,
478};
479
461480const assert = std.debug.assert;
462481const dwarf = std.dwarf;
463482const leb = std.leb;
src/link/MachO/Dylib.zig+49-50
......@@ -1,8 +1,6 @@
11path: []const u8,
2data: []const u8,
32index: File.Index,
43
5header: ?macho.mach_header_64 = null,
64exports: std.MultiArrayList(Export) = .{},
75strtab: std.ArrayListUnmanaged(u8) = .{},
86id: ?Id = null,
......@@ -34,7 +32,6 @@ pub fn isDylib(path: []const u8, fat_arch: ?fat.Arch) !bool {
3432}
3533
3634pub fn deinit(self: *Dylib, allocator: Allocator) void {
37 allocator.free(self.data);
3835 allocator.free(self.path);
3936 self.exports.deinit(allocator);
4037 self.strtab.deinit(allocator);
......@@ -44,22 +41,29 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
4441 id.deinit(allocator);
4542 }
4643 self.dependents.deinit(allocator);
44 for (self.rpaths.keys()) |rpath| {
45 allocator.free(rpath);
46 }
4747 self.rpaths.deinit(allocator);
4848}
4949
50pub fn parse(self: *Dylib, macho_file: *MachO) !void {
50pub fn parse(self: *Dylib, macho_file: *MachO, file: std.fs.File, fat_arch: ?fat.Arch) !void {
5151 const tracy = trace(@src());
5252 defer tracy.end();
5353
5454 const gpa = macho_file.base.comp.gpa;
55 var stream = std.io.fixedBufferStream(self.data);
56 const reader = stream.reader();
55 const offset = if (fat_arch) |ar| ar.offset else 0;
5756
5857 log.debug("parsing dylib from binary", .{});
5958
60 self.header = try reader.readStruct(macho.mach_header_64);
59 var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
60 {
61 const amt = try file.preadAll(&header_buffer, offset);
62 if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput;
63 }
64 const header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*;
6165
62 const this_cpu_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) {
66 const this_cpu_arch: std.Target.Cpu.Arch = switch (header.cputype) {
6367 macho.CPU_TYPE_ARM64 => .aarch64,
6468 macho.CPU_TYPE_X86_64 => .x86_64,
6569 else => |x| {
......@@ -72,39 +76,60 @@ pub fn parse(self: *Dylib, macho_file: *MachO) !void {
7276 return error.InvalidCpuArch;
7377 }
7478
75 const lc_id = self.getLoadCommand(.ID_DYLIB) orelse {
76 try macho_file.reportParseError2(self.index, "missing LC_ID_DYLIB load command", .{});
77 return error.MalformedDylib;
78 };
79 self.id = try Id.fromLoadCommand(gpa, lc_id.cast(macho.dylib_command).?, lc_id.getDylibPathName());
79 const lc_buffer = try gpa.alloc(u8, header.sizeofcmds);
80 defer gpa.free(lc_buffer);
81 {
82 const amt = try file.preadAll(lc_buffer, offset + @sizeOf(macho.mach_header_64));
83 if (amt != lc_buffer.len) return error.InputOutput;
84 }
8085
8186 var it = LoadCommandIterator{
82 .ncmds = self.header.?.ncmds,
83 .buffer = self.data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds],
87 .ncmds = header.ncmds,
88 .buffer = lc_buffer,
8489 };
8590 while (it.next()) |cmd| switch (cmd.cmd()) {
86 .REEXPORT_DYLIB => if (self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0) {
91 .ID_DYLIB => {
92 self.id = try Id.fromLoadCommand(gpa, cmd.cast(macho.dylib_command).?, cmd.getDylibPathName());
93 },
94 .REEXPORT_DYLIB => if (header.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0) {
8795 const id = try Id.fromLoadCommand(gpa, cmd.cast(macho.dylib_command).?, cmd.getDylibPathName());
8896 try self.dependents.append(gpa, id);
8997 },
9098 .DYLD_INFO_ONLY => {
9199 const dyld_cmd = cmd.cast(macho.dyld_info_command).?;
92 const data = self.data[dyld_cmd.export_off..][0..dyld_cmd.export_size];
100 const data = try gpa.alloc(u8, dyld_cmd.export_size);
101 defer gpa.free(data);
102 const amt = try file.preadAll(data, dyld_cmd.export_off + offset);
103 if (amt != data.len) return error.InputOutput;
93104 try self.parseTrie(data, macho_file);
94105 },
95106 .DYLD_EXPORTS_TRIE => {
96107 const ld_cmd = cmd.cast(macho.linkedit_data_command).?;
97 const data = self.data[ld_cmd.dataoff..][0..ld_cmd.datasize];
108 const data = try gpa.alloc(u8, ld_cmd.datasize);
109 defer gpa.free(data);
110 const amt = try file.preadAll(data, ld_cmd.dataoff + offset);
111 if (amt != data.len) return error.InputOutput;
98112 try self.parseTrie(data, macho_file);
99113 },
100114 .RPATH => {
101115 const path = cmd.getRpathPathName();
102 try self.rpaths.put(gpa, path, {});
116 try self.rpaths.put(gpa, try gpa.dupe(u8, path), {});
117 },
118 .BUILD_VERSION,
119 .VERSION_MIN_MACOSX,
120 .VERSION_MIN_IPHONEOS,
121 .VERSION_MIN_TVOS,
122 .VERSION_MIN_WATCHOS,
123 => {
124 self.platform = MachO.Platform.fromLoadCommand(cmd);
103125 },
104126 else => {},
105127 };
106128
107 self.initPlatform();
129 if (self.id == null) {
130 try macho_file.reportParseError2(self.index, "missing LC_ID_DYLIB load command", .{});
131 return error.MalformedDylib;
132 }
108133
109134 if (self.platform) |platform| {
110135 if (!macho_file.platform.eqlTarget(platform)) {
......@@ -168,7 +193,7 @@ const TrieIterator = struct {
168193
169194pub fn addExport(self: *Dylib, allocator: Allocator, name: []const u8, flags: Export.Flags) !void {
170195 try self.exports.append(allocator, .{
171 .name = try self.insertString(allocator, name),
196 .name = try self.addString(allocator, name),
172197 .flags = flags,
173198 });
174199}
......@@ -479,24 +504,6 @@ pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void {
479504 }
480505}
481506
482fn initPlatform(self: *Dylib) void {
483 var it = LoadCommandIterator{
484 .ncmds = self.header.?.ncmds,
485 .buffer = self.data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds],
486 };
487 self.platform = while (it.next()) |cmd| {
488 switch (cmd.cmd()) {
489 .BUILD_VERSION,
490 .VERSION_MIN_MACOSX,
491 .VERSION_MIN_IPHONEOS,
492 .VERSION_MIN_TVOS,
493 .VERSION_MIN_WATCHOS,
494 => break MachO.Platform.fromLoadCommand(cmd),
495 else => {},
496 }
497 } else null;
498}
499
500507pub fn resolveSymbols(self: *Dylib, macho_file: *MachO) void {
501508 const tracy = trace(@src());
502509 defer tracy.end();
......@@ -526,8 +533,10 @@ pub fn resetGlobals(self: *Dylib, macho_file: *MachO) void {
526533 for (self.symbols.items) |sym_index| {
527534 const sym = macho_file.getSymbol(sym_index);
528535 const name = sym.name;
536 const global = sym.flags.global;
529537 sym.* = .{};
530538 sym.name = name;
539 sym.flags.global = global;
531540 }
532541}
533542
......@@ -589,17 +598,7 @@ pub inline fn getUmbrella(self: Dylib, macho_file: *MachO) *Dylib {
589598 return macho_file.getFile(self.umbrella).?.dylib;
590599}
591600
592fn getLoadCommand(self: Dylib, lc: macho.LC) ?LoadCommandIterator.LoadCommand {
593 var it = LoadCommandIterator{
594 .ncmds = self.header.?.ncmds,
595 .buffer = self.data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds],
596 };
597 while (it.next()) |cmd| {
598 if (cmd.cmd() == lc) return cmd;
599 } else return null;
600}
601
602fn insertString(self: *Dylib, allocator: Allocator, name: []const u8) !u32 {
601fn addString(self: *Dylib, allocator: Allocator, name: []const u8) !u32 {
603602 const off = @as(u32, @intCast(self.strtab.items.len));
604603 try self.strtab.writer(allocator).print("{s}\x00", .{name});
605604 return off;
src/link/MachO/InternalObject.zig+41-12
......@@ -3,6 +3,7 @@ index: File.Index,
33sections: std.MultiArrayList(Section) = .{},
44atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
55symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
6strtab: std.ArrayListUnmanaged(u8) = .{},
67
78objc_methnames: std.ArrayListUnmanaged(u8) = .{},
89objc_selrefs: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64),
......@@ -16,6 +17,7 @@ pub fn deinit(self: *InternalObject, allocator: Allocator) void {
1617 self.sections.deinit(allocator);
1718 self.atoms.deinit(allocator);
1819 self.symbols.deinit(allocator);
20 self.strtab.deinit(allocator);
1921 self.objc_methnames.deinit(allocator);
2022}
2123
......@@ -26,7 +28,11 @@ pub fn addSymbol(self: *InternalObject, name: [:0]const u8, macho_file: *MachO)
2628 const gop = try macho_file.getOrCreateGlobal(off);
2729 self.symbols.addOneAssumeCapacity().* = gop.index;
2830 const sym = macho_file.getSymbol(gop.index);
29 sym.* = .{ .name = off, .file = self.index };
31 sym.file = self.index;
32 sym.value = 0;
33 sym.atom = 0;
34 sym.nlist_idx = 0;
35 sym.flags = .{ .global = true };
3036 return gop.index;
3137}
3238
......@@ -45,7 +51,7 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil
4551 defer gpa.free(name);
4652 const atom = macho_file.getAtom(atom_index).?;
4753 atom.atom_index = atom_index;
48 atom.name = try macho_file.strings.insert(gpa, name);
54 atom.name = try self.addString(gpa, name);
4955 atom.file = self.index;
5056 atom.size = methname.len + 1;
5157 atom.alignment = .@"1";
......@@ -79,7 +85,7 @@ fn addObjcSelrefsSection(
7985 defer gpa.free(name);
8086 const atom = macho_file.getAtom(atom_index).?;
8187 atom.atom_index = atom_index;
82 atom.name = try macho_file.strings.insert(gpa, name);
88 atom.name = try self.addString(gpa, name);
8389 atom.file = self.index;
8490 atom.size = @sizeOf(u64);
8591 atom.alignment = .@"8";
......@@ -158,16 +164,39 @@ fn addSection(self: *InternalObject, allocator: Allocator, segname: []const u8,
158164 return n_sect;
159165}
160166
161pub fn getSectionData(self: *const InternalObject, index: u32) []const u8 {
167pub fn getAtomData(self: *const InternalObject, atom: Atom, buffer: []u8) !void {
168 assert(buffer.len == atom.size);
162169 const slice = self.sections.slice();
163 assert(index < slice.items(.header).len);
164 const sect = slice.items(.header)[index];
165 const extra = slice.items(.extra)[index];
166 if (extra.is_objc_methname) {
167 return self.objc_methnames.items[sect.offset..][0..sect.size];
168 } else if (extra.is_objc_selref) {
169 return &self.objc_selrefs;
170 } else @panic("ref to non-existent section");
170 const sect = slice.items(.header)[atom.n_sect];
171 const extra = slice.items(.extra)[atom.n_sect];
172 const data = if (extra.is_objc_methname) blk: {
173 const size = std.math.cast(usize, sect.size) orelse return error.Overflow;
174 break :blk self.objc_methnames.items[sect.offset..][0..size];
175 } else if (extra.is_objc_selref)
176 &self.objc_selrefs
177 else
178 @panic("ref to non-existent section");
179 const off = std.math.cast(usize, atom.off) orelse return error.Overflow;
180 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
181 @memcpy(buffer, data[off..][0..size]);
182}
183
184pub fn getAtomRelocs(self: *const InternalObject, atom: Atom) []const Relocation {
185 const relocs = self.sections.items(.relocs)[atom.n_sect];
186 return relocs.items[atom.relocs.pos..][0..atom.relocs.len];
187}
188
189fn addString(self: *InternalObject, allocator: Allocator, name: [:0]const u8) error{OutOfMemory}!u32 {
190 const off: u32 = @intCast(self.strtab.items.len);
191 try self.strtab.ensureUnusedCapacity(allocator, name.len + 1);
192 self.strtab.appendSliceAssumeCapacity(name);
193 self.strtab.appendAssumeCapacity(0);
194 return off;
195}
196
197pub fn getString(self: InternalObject, off: u32) [:0]const u8 {
198 assert(off < self.strtab.items.len);
199 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
171200}
172201
173202pub fn asFile(self: *InternalObject) File {
src/link/MachO/Object.zig+178-117
......@@ -1,13 +1,13 @@
1archive: ?[]const u8 = null,
1archive: ?Archive = null,
22path: []const u8,
3file: std.fs.File,
34mtime: u64,
4data: []const u8,
55index: File.Index,
66
77header: ?macho.mach_header_64 = null,
88sections: std.MultiArrayList(Section) = .{},
99symtab: std.MultiArrayList(Nlist) = .{},
10strtab: []const u8 = &[0]u8{},
10strtab: std.ArrayListUnmanaged(u8) = .{},
1111
1212symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
1313atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
......@@ -22,6 +22,7 @@ cies: std.ArrayListUnmanaged(Cie) = .{},
2222fdes: std.ArrayListUnmanaged(Fde) = .{},
2323eh_frame_data: std.ArrayListUnmanaged(u8) = .{},
2424unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record.Index) = .{},
25data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
2526
2627alive: bool = true,
2728hidden: bool = false,
......@@ -29,6 +30,11 @@ hidden: bool = false,
2930dynamic_relocs: MachO.DynamicRelocs = .{},
3031output_symtab_ctx: MachO.SymtabCtx = .{},
3132
33const Archive = struct {
34 path: []const u8,
35 offset: u64,
36};
37
3238pub fn isObject(path: []const u8) !bool {
3339 const file = try std.fs.cwd().openFile(path, .{});
3440 defer file.close();
......@@ -37,12 +43,16 @@ pub fn isObject(path: []const u8) !bool {
3743}
3844
3945pub fn deinit(self: *Object, allocator: Allocator) void {
46 self.file.close();
47 if (self.archive) |*ar| allocator.free(ar.path);
48 allocator.free(self.path);
4049 for (self.sections.items(.relocs), self.sections.items(.subsections)) |*relocs, *sub| {
4150 relocs.deinit(allocator);
4251 sub.deinit(allocator);
4352 }
4453 self.sections.deinit(allocator);
4554 self.symtab.deinit(allocator);
55 self.strtab.deinit(allocator);
4656 self.symbols.deinit(allocator);
4757 self.atoms.deinit(allocator);
4858 self.cies.deinit(allocator);
......@@ -54,7 +64,7 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
5464 sf.stabs.deinit(allocator);
5565 }
5666 self.stab_files.deinit(allocator);
57 allocator.free(self.data);
67 self.data_in_code.deinit(allocator);
5868}
5969
6070pub fn parse(self: *Object, macho_file: *MachO) !void {
......@@ -62,10 +72,14 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
6272 defer tracy.end();
6373
6474 const gpa = macho_file.base.comp.gpa;
65 var stream = std.io.fixedBufferStream(self.data);
66 const reader = stream.reader();
75 const offset = if (self.archive) |ar| ar.offset else 0;
6776
68 self.header = try reader.readStruct(macho.mach_header_64);
77 var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
78 {
79 const amt = try self.file.preadAll(&header_buffer, offset);
80 if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput;
81 }
82 self.header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*;
6983
7084 const this_cpu_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) {
7185 macho.CPU_TYPE_ARM64 => .aarch64,
......@@ -80,35 +94,79 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
8094 return error.InvalidCpuArch;
8195 }
8296
83 if (self.getLoadCommand(.SEGMENT_64)) |lc| {
84 const sections = lc.getSections();
85 try self.sections.ensureUnusedCapacity(gpa, sections.len);
86 for (sections) |sect| {
87 const index = try self.sections.addOne(gpa);
88 self.sections.set(index, .{ .header = sect });
89
90 if (mem.eql(u8, sect.sectName(), "__eh_frame")) {
91 self.eh_frame_sect_index = @intCast(index);
92 } else if (mem.eql(u8, sect.sectName(), "__compact_unwind")) {
93 self.compact_unwind_sect_index = @intCast(index);
94 }
95 }
96 }
97 if (self.getLoadCommand(.SYMTAB)) |lc| {
98 const cmd = lc.cast(macho.symtab_command).?;
99 self.strtab = self.data[cmd.stroff..][0..cmd.strsize];
100
101 const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(self.data.ptr + cmd.symoff))[0..cmd.nsyms];
102 try self.symtab.ensureUnusedCapacity(gpa, symtab.len);
103 for (symtab) |nlist| {
104 self.symtab.appendAssumeCapacity(.{
105 .nlist = nlist,
106 .atom = 0,
107 .size = 0,
108 });
109 }
97 const lc_buffer = try gpa.alloc(u8, self.header.?.sizeofcmds);
98 defer gpa.free(lc_buffer);
99 {
100 const amt = try self.file.preadAll(lc_buffer, offset + @sizeOf(macho.mach_header_64));
101 if (amt != self.header.?.sizeofcmds) return error.InputOutput;
110102 }
111103
104 var it = LoadCommandIterator{
105 .ncmds = self.header.?.ncmds,
106 .buffer = lc_buffer,
107 };
108 while (it.next()) |lc| switch (lc.cmd()) {
109 .SEGMENT_64 => {
110 const sections = lc.getSections();
111 try self.sections.ensureUnusedCapacity(gpa, sections.len);
112 for (sections) |sect| {
113 const index = try self.sections.addOne(gpa);
114 self.sections.set(index, .{ .header = sect });
115
116 if (mem.eql(u8, sect.sectName(), "__eh_frame")) {
117 self.eh_frame_sect_index = @intCast(index);
118 } else if (mem.eql(u8, sect.sectName(), "__compact_unwind")) {
119 self.compact_unwind_sect_index = @intCast(index);
120 }
121 }
122 },
123 .SYMTAB => {
124 const cmd = lc.cast(macho.symtab_command).?;
125 try self.strtab.resize(gpa, cmd.strsize);
126 {
127 const amt = try self.file.preadAll(self.strtab.items, cmd.stroff + offset);
128 if (amt != self.strtab.items.len) return error.InputOutput;
129 }
130
131 const symtab_buffer = try gpa.alloc(u8, cmd.nsyms * @sizeOf(macho.nlist_64));
132 defer gpa.free(symtab_buffer);
133 {
134 const amt = try self.file.preadAll(symtab_buffer, cmd.symoff + offset);
135 if (amt != symtab_buffer.len) return error.InputOutput;
136 }
137 const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(symtab_buffer.ptr))[0..cmd.nsyms];
138 try self.symtab.ensureUnusedCapacity(gpa, symtab.len);
139 for (symtab) |nlist| {
140 self.symtab.appendAssumeCapacity(.{
141 .nlist = nlist,
142 .atom = 0,
143 .size = 0,
144 });
145 }
146 },
147 .DATA_IN_CODE => {
148 const cmd = lc.cast(macho.linkedit_data_command).?;
149 const buffer = try gpa.alloc(u8, cmd.datasize);
150 defer gpa.free(buffer);
151 {
152 const amt = try self.file.preadAll(buffer, offset + cmd.dataoff);
153 if (amt != buffer.len) return error.InputOutput;
154 }
155 const ndice = @divExact(cmd.datasize, @sizeOf(macho.data_in_code_entry));
156 const dice = @as([*]align(1) const macho.data_in_code_entry, @ptrCast(buffer.ptr))[0..ndice];
157 try self.data_in_code.appendUnalignedSlice(gpa, dice);
158 },
159 .BUILD_VERSION,
160 .VERSION_MIN_MACOSX,
161 .VERSION_MIN_IPHONEOS,
162 .VERSION_MIN_TVOS,
163 .VERSION_MIN_WATCHOS,
164 => if (self.platform == null) {
165 self.platform = MachO.Platform.fromLoadCommand(lc);
166 },
167 else => {},
168 };
169
112170 const NlistIdx = struct {
113171 nlist: macho.nlist_64,
114172 idx: usize,
......@@ -170,8 +228,6 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
170228 try self.parseUnwindRecords(macho_file);
171229 }
172230
173 self.initPlatform();
174
175231 if (self.platform) |platform| {
176232 if (!macho_file.platform.eqlTarget(platform)) {
177233 try macho_file.reportParseError2(self.index, "invalid platform: {}", .{
......@@ -237,7 +293,7 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
237293 defer gpa.free(name);
238294 const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr;
239295 const atom_index = try self.addAtom(.{
240 .name = name,
296 .name = try self.addString(gpa, name),
241297 .n_sect = @intCast(n_sect),
242298 .off = 0,
243299 .size = size,
......@@ -267,7 +323,7 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
267323 else
268324 sect.@"align";
269325 const atom_index = try self.addAtom(.{
270 .name = self.getString(nlist.nlist.n_strx),
326 .name = nlist.nlist.n_strx,
271327 .n_sect = @intCast(n_sect),
272328 .off = nlist.nlist.n_value - sect.addr,
273329 .size = size,
......@@ -300,7 +356,7 @@ fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
300356 defer gpa.free(name);
301357
302358 const atom_index = try self.addAtom(.{
303 .name = name,
359 .name = try self.addString(gpa, name),
304360 .n_sect = @intCast(n_sect),
305361 .off = 0,
306362 .size = sect.size,
......@@ -336,7 +392,7 @@ fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
336392}
337393
338394const AddAtomArgs = struct {
339 name: [:0]const u8,
395 name: u32,
340396 n_sect: u8,
341397 off: u64,
342398 size: u64,
......@@ -349,7 +405,7 @@ fn addAtom(self: *Object, args: AddAtomArgs, macho_file: *MachO) !Atom.Index {
349405 const atom = macho_file.getAtom(atom_index).?;
350406 atom.file = self.index;
351407 atom.atom_index = atom_index;
352 atom.name = try macho_file.strings.insert(gpa, args.name);
408 atom.name = args.name;
353409 atom.n_sect = args.n_sect;
354410 atom.size = args.size;
355411 atom.alignment = Atom.Alignment.fromLog2Units(args.alignment);
......@@ -376,7 +432,7 @@ fn initLiteralSections(self: *Object, macho_file: *MachO) !void {
376432 defer gpa.free(name);
377433
378434 const atom_index = try self.addAtom(.{
379 .name = name,
435 .name = try self.addString(gpa, name),
380436 .n_sect = @intCast(n_sect),
381437 .off = 0,
382438 .size = sect.size,
......@@ -475,10 +531,9 @@ fn initSymbols(self: *Object, macho_file: *MachO) !void {
475531 const index = try macho_file.addSymbol();
476532 self.symbols.appendAssumeCapacity(index);
477533 const symbol = macho_file.getSymbol(index);
478 const name = self.getString(nlist.n_strx);
479534 symbol.* = .{
480535 .value = nlist.n_value,
481 .name = try macho_file.strings.insert(gpa, name),
536 .name = nlist.n_strx,
482537 .nlist_idx = @intCast(i),
483538 .atom = 0,
484539 .file = self.index,
......@@ -638,7 +693,10 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
638693 const sect = slice.items(.header)[sect_id];
639694 const relocs = slice.items(.relocs)[sect_id];
640695
641 const data = try self.getSectionData(sect_id);
696 // TODO: read into buffer directly
697 const data = try self.getSectionData(gpa, sect_id);
698 defer gpa.free(data);
699
642700 try self.eh_frame_data.ensureTotalCapacityPrecise(gpa, data.len);
643701 self.eh_frame_data.appendSliceAssumeCapacity(data);
644702
......@@ -739,7 +797,8 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
739797 };
740798
741799 const gpa = macho_file.base.comp.gpa;
742 const data = try self.getSectionData(sect_id);
800 const data = try self.getSectionData(gpa, sect_id);
801 defer gpa.free(data);
743802 const nrecs = @divExact(data.len, @sizeOf(macho.compact_unwind_entry));
744803 const recs = @as([*]align(1) const macho.compact_unwind_entry, @ptrCast(data.ptr))[0..nrecs];
745804 const sym_lookup = SymbolLookup{ .ctx = self };
......@@ -934,24 +993,6 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
934993 }
935994}
936995
937fn initPlatform(self: *Object) void {
938 var it = LoadCommandIterator{
939 .ncmds = self.header.?.ncmds,
940 .buffer = self.data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds],
941 };
942 self.platform = while (it.next()) |cmd| {
943 switch (cmd.cmd()) {
944 .BUILD_VERSION,
945 .VERSION_MIN_MACOSX,
946 .VERSION_MIN_IPHONEOS,
947 .VERSION_MIN_TVOS,
948 .VERSION_MIN_WATCHOS,
949 => break MachO.Platform.fromLoadCommand(cmd),
950 else => {},
951 }
952 } else null;
953}
954
955996/// Currently, we only check if a compile unit for this input object file exists
956997/// and record that so that we can emit symbol stabs.
957998/// TODO in the future, we want parse debug info and debug line sections so that
......@@ -975,12 +1016,20 @@ fn initDwarfInfo(self: *Object, macho_file: *MachO) !void {
9751016
9761017 if (debug_info_index == null or debug_abbrev_index == null) return;
9771018
978 var dwarf_info = DwarfInfo{
979 .debug_info = try self.getSectionData(@intCast(debug_info_index.?)),
980 .debug_abbrev = try self.getSectionData(@intCast(debug_abbrev_index.?)),
981 .debug_str = if (debug_str_index) |index| try self.getSectionData(@intCast(index)) else "",
982 };
983 dwarf_info.init(gpa) catch {
1019 const debug_info = try self.getSectionData(gpa, @intCast(debug_info_index.?));
1020 defer gpa.free(debug_info);
1021 const debug_abbrev = try self.getSectionData(gpa, @intCast(debug_abbrev_index.?));
1022 defer gpa.free(debug_abbrev);
1023 const debug_str = if (debug_str_index) |index| try self.getSectionData(gpa, @intCast(index)) else &[0]u8{};
1024 defer gpa.free(debug_str);
1025
1026 var dwarf_info = DwarfInfo{};
1027 errdefer dwarf_info.deinit(gpa);
1028 dwarf_info.init(gpa, .{
1029 .debug_info = debug_info,
1030 .debug_abbrev = debug_abbrev,
1031 .debug_str = debug_str,
1032 }) catch {
9841033 try macho_file.reportParseError2(self.index, "invalid __DWARF info found", .{});
9851034 return error.MalformedObject;
9861035 };
......@@ -1049,8 +1098,10 @@ pub fn resetGlobals(self: *Object, macho_file: *MachO) void {
10491098 if (!self.symtab.items(.nlist)[nlist_idx].ext()) continue;
10501099 const sym = macho_file.getSymbol(sym_index);
10511100 const name = sym.name;
1101 const global = sym.flags.global;
10521102 sym.* = .{};
10531103 sym.name = name;
1104 sym.flags.global = global;
10541105 }
10551106}
10561107
......@@ -1137,7 +1188,7 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
11371188 defer gpa.free(name);
11381189 const atom = macho_file.getAtom(atom_index).?;
11391190 atom.atom_index = atom_index;
1140 atom.name = try macho_file.strings.insert(gpa, name);
1191 atom.name = try self.addString(gpa, name);
11411192 atom.file = self.index;
11421193 atom.size = nlist.n_value;
11431194 atom.alignment = Atom.Alignment.fromLog2Units((nlist.n_desc >> 8) & 0x0f);
......@@ -1151,6 +1202,7 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
11511202
11521203 sym.value = 0;
11531204 sym.atom = atom_index;
1205 sym.flags.global = true;
11541206 sym.flags.weak = false;
11551207 sym.flags.weak_ref = false;
11561208 sym.flags.tentative = false;
......@@ -1219,8 +1271,8 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
12191271 self.output_symtab_ctx.strsize += @as(u32, @intCast(comp_dir.len + 1)); // comp_dir
12201272 self.output_symtab_ctx.strsize += @as(u32, @intCast(tu_name.len + 1)); // tu_name
12211273
1222 if (self.archive) |path| {
1223 self.output_symtab_ctx.strsize += @as(u32, @intCast(path.len + 1 + self.path.len + 1 + 1));
1274 if (self.archive) |ar| {
1275 self.output_symtab_ctx.strsize += @as(u32, @intCast(ar.path.len + 1 + self.path.len + 1 + 1));
12241276 } else {
12251277 self.output_symtab_ctx.strsize += @as(u32, @intCast(self.path.len + 1));
12261278 }
......@@ -1365,8 +1417,8 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO) error{Overflow}!void
13651417 index += 1;
13661418 // N_OSO path
13671419 n_strx = @as(u32, @intCast(macho_file.strtab.items.len));
1368 if (self.archive) |path| {
1369 macho_file.strtab.appendSliceAssumeCapacity(path);
1420 if (self.archive) |ar| {
1421 macho_file.strtab.appendSliceAssumeCapacity(ar.path);
13701422 macho_file.strtab.appendAssumeCapacity('(');
13711423 macho_file.strtab.appendSliceAssumeCapacity(self.path);
13721424 macho_file.strtab.appendAssumeCapacity(')');
......@@ -1532,30 +1584,26 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO) error{Overflow}!void
15321584 }
15331585}
15341586
1535fn getLoadCommand(self: Object, lc: macho.LC) ?LoadCommandIterator.LoadCommand {
1536 var it = LoadCommandIterator{
1537 .ncmds = self.header.?.ncmds,
1538 .buffer = self.data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds],
1539 };
1540 while (it.next()) |cmd| {
1541 if (cmd.cmd() == lc) return cmd;
1542 } else return null;
1543}
1544
1545pub fn getSectionData(self: *const Object, index: u32) error{Overflow}![]const u8 {
1587fn getSectionData(self: *const Object, allocator: Allocator, index: u32) ![]u8 {
15461588 const slice = self.sections.slice();
15471589 assert(index < slice.items(.header).len);
15481590 const sect = slice.items(.header)[index];
1549 const off = math.cast(usize, sect.offset) orelse return error.Overflow;
1591 const offset = if (self.archive) |ar| ar.offset else 0;
15501592 const size = math.cast(usize, sect.size) orelse return error.Overflow;
1551 return self.data[off..][0..size];
1593 const buffer = try allocator.alloc(u8, size);
1594 errdefer allocator.free(buffer);
1595 const amt = try self.file.preadAll(buffer, sect.offset + offset);
1596 if (amt != buffer.len) return error.InputOutput;
1597 return buffer;
15521598}
15531599
1554pub fn getAtomData(self: *const Object, atom: Atom) error{Overflow}![]const u8 {
1555 const data = try self.getSectionData(atom.n_sect);
1556 const off = math.cast(usize, atom.off) orelse return error.Overflow;
1557 const size = math.cast(usize, atom.size) orelse return error.Overflow;
1558 return data[off..][0..size];
1600pub fn getAtomData(self: *const Object, atom: Atom, buffer: []u8) !void {
1601 assert(buffer.len == atom.size);
1602 const slice = self.sections.slice();
1603 const offset = if (self.archive) |ar| ar.offset else 0;
1604 const sect = slice.items(.header)[atom.n_sect];
1605 const amt = try self.file.preadAll(buffer, sect.offset + offset + atom.off);
1606 if (amt != buffer.len) return error.InputOutput;
15591607}
15601608
15611609pub fn getAtomRelocs(self: *const Object, atom: Atom) []const Relocation {
......@@ -1563,9 +1611,17 @@ pub fn getAtomRelocs(self: *const Object, atom: Atom) []const Relocation {
15631611 return relocs.items[atom.relocs.pos..][0..atom.relocs.len];
15641612}
15651613
1566fn getString(self: Object, off: u32) [:0]const u8 {
1567 assert(off < self.strtab.len);
1568 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0);
1614fn addString(self: *Object, allocator: Allocator, name: [:0]const u8) error{OutOfMemory}!u32 {
1615 const off: u32 = @intCast(self.strtab.items.len);
1616 try self.strtab.ensureUnusedCapacity(allocator, name.len + 1);
1617 self.strtab.appendSliceAssumeCapacity(name);
1618 self.strtab.appendAssumeCapacity(0);
1619 return off;
1620}
1621
1622pub fn getString(self: Object, off: u32) [:0]const u8 {
1623 assert(off < self.strtab.items.len);
1624 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
15691625}
15701626
15711627pub fn hasUnwindRecords(self: Object) bool {
......@@ -1600,15 +1656,8 @@ pub fn hasObjc(self: Object) bool {
16001656 return false;
16011657}
16021658
1603pub fn getDataInCode(self: Object) []align(1) const macho.data_in_code_entry {
1604 const lc = self.getLoadCommand(.DATA_IN_CODE) orelse return &[0]macho.data_in_code_entry{};
1605 const cmd = lc.cast(macho.linkedit_data_command).?;
1606 const ndice = @divExact(cmd.datasize, @sizeOf(macho.data_in_code_entry));
1607 const dice = @as(
1608 [*]align(1) const macho.data_in_code_entry,
1609 @ptrCast(self.data.ptr + cmd.dataoff),
1610 )[0..ndice];
1611 return dice;
1659pub fn getDataInCode(self: Object) []const macho.data_in_code_entry {
1660 return self.data_in_code.items;
16121661}
16131662
16141663pub inline fn hasSubsections(self: Object) bool {
......@@ -1762,8 +1811,8 @@ fn formatPath(
17621811) !void {
17631812 _ = unused_fmt_string;
17641813 _ = options;
1765 if (object.archive) |path| {
1766 try writer.writeAll(path);
1814 if (object.archive) |ar| {
1815 try writer.writeAll(ar.path);
17671816 try writer.writeByte('(');
17681817 try writer.writeAll(object.path);
17691818 try writer.writeByte(')');
......@@ -1831,11 +1880,17 @@ const x86_64 = struct {
18311880 ) !void {
18321881 const gpa = macho_file.base.comp.gpa;
18331882
1834 const relocs = @as(
1835 [*]align(1) const macho.relocation_info,
1836 @ptrCast(self.data.ptr + sect.reloff),
1837 )[0..sect.nreloc];
1838 const code = try self.getSectionData(@intCast(n_sect));
1883 const offset = if (self.archive) |ar| ar.offset else 0;
1884 const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));
1885 defer gpa.free(relocs_buffer);
1886 {
1887 const amt = try self.file.preadAll(relocs_buffer, sect.reloff + offset);
1888 if (amt != relocs_buffer.len) return error.InputOutput;
1889 }
1890 const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc];
1891
1892 const code = try self.getSectionData(gpa, @intCast(n_sect));
1893 defer gpa.free(code);
18391894
18401895 try out.ensureTotalCapacityPrecise(gpa, relocs.len);
18411896
......@@ -1987,11 +2042,17 @@ const aarch64 = struct {
19872042 ) !void {
19882043 const gpa = macho_file.base.comp.gpa;
19892044
1990 const relocs = @as(
1991 [*]align(1) const macho.relocation_info,
1992 @ptrCast(self.data.ptr + sect.reloff),
1993 )[0..sect.nreloc];
1994 const code = try self.getSectionData(@intCast(n_sect));
2045 const offset = if (self.archive) |ar| ar.offset else 0;
2046 const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));
2047 defer gpa.free(relocs_buffer);
2048 {
2049 const amt = try self.file.preadAll(relocs_buffer, sect.reloff + offset);
2050 if (amt != relocs_buffer.len) return error.InputOutput;
2051 }
2052 const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc];
2053
2054 const code = try self.getSectionData(gpa, @intCast(n_sect));
2055 defer gpa.free(code);
19952056
19962057 try out.ensureTotalCapacityPrecise(gpa, relocs.len);
19972058
src/link/MachO/Symbol.zig+11-1
......@@ -55,7 +55,12 @@ pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool {
5555}
5656
5757pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 {
58 return macho_file.strings.getAssumeExists(symbol.name);
58 if (symbol.flags.global) return macho_file.strings.getAssumeExists(symbol.name);
59 return switch (symbol.getFile(macho_file).?) {
60 .dylib => unreachable, // There are no local symbols for dylibs
61 .zig_object => |x| x.strtab.getAssumeExists(symbol.name),
62 inline else => |x| x.getString(symbol.name),
63 };
5964}
6065
6166pub fn getAtom(symbol: Symbol, macho_file: *MachO) ?*Atom {
......@@ -341,6 +346,11 @@ pub const Flags = packed struct {
341346 /// Whether the symbol is exported at runtime.
342347 @"export": bool = false,
343348
349 /// Whether the symbol is effectively an extern and takes part in global
350 /// symbol resolution. Then, its name will be saved in global string interning
351 /// table.
352 global: bool = false,
353
344354 /// Whether this symbol is weak.
345355 weak: bool = false,
346356
src/link/MachO/ZigObject.zig+23-30
......@@ -3,6 +3,7 @@ path: []const u8,
33index: File.Index,
44
55symtab: std.MultiArrayList(Nlist) = .{},
6strtab: StringTable = .{},
67
78symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
89atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
......@@ -52,10 +53,12 @@ pub fn init(self: *ZigObject, macho_file: *MachO) !void {
5253 const gpa = comp.gpa;
5354
5455 try self.atoms.append(gpa, 0); // null input section
56 try self.strtab.buffer.append(gpa, 0);
5557}
5658
5759pub fn deinit(self: *ZigObject, allocator: Allocator) void {
5860 self.symtab.deinit(allocator);
61 self.strtab.deinit(allocator);
5962 self.symbols.deinit(allocator);
6063 self.atoms.deinit(allocator);
6164 self.globals_lookup.deinit(allocator);
......@@ -136,37 +139,24 @@ pub fn addAtom(self: *ZigObject, macho_file: *MachO) !Symbol.Index {
136139 return symbol_index;
137140}
138141
139/// Caller owns the memory.
140pub fn getAtomDataAlloc(
141 self: ZigObject,
142 macho_file: *MachO,
143 allocator: Allocator,
144 atom: Atom,
145) ![]u8 {
142pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8) !void {
146143 assert(atom.file == self.index);
144 assert(atom.size == buffer.len);
147145 const sect = macho_file.sections.items(.header)[atom.out_n_sect];
148146 assert(!sect.isZerofill());
149147
150148 switch (sect.type()) {
151149 macho.S_THREAD_LOCAL_REGULAR => {
152150 const tlv = self.tlv_initializers.get(atom.atom_index).?;
153 const data = try allocator.dupe(u8, tlv.data);
154 return data;
151 @memcpy(buffer, tlv.data);
155152 },
156153 macho.S_THREAD_LOCAL_VARIABLES => {
157 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
158 const data = try allocator.alloc(u8, size);
159 @memset(data, 0);
160 return data;
154 @memset(buffer, 0);
161155 },
162156 else => {
163157 const file_offset = sect.offset + atom.value - sect.addr;
164 const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
165 const data = try allocator.alloc(u8, size);
166 errdefer allocator.free(data);
167 const amt = try macho_file.base.file.?.preadAll(data, file_offset);
168 if (amt != data.len) return error.InputOutput;
169 return data;
158 const amt = try macho_file.base.file.?.preadAll(buffer, file_offset);
159 if (amt != buffer.len) return error.InputOutput;
170160 },
171161 }
172162}
......@@ -242,8 +232,10 @@ pub fn resetGlobals(self: *ZigObject, macho_file: *MachO) void {
242232 if (!self.symtab.items(.nlist)[nlist_idx].ext()) continue;
243233 const sym = macho_file.getSymbol(sym_index);
244234 const name = sym.name;
235 const global = sym.flags.global;
245236 sym.* = .{};
246237 sym.name = name;
238 sym.flags.global = global;
247239 }
248240}
249241
......@@ -686,7 +678,7 @@ fn updateDeclCode(
686678 sym.out_n_sect = sect_index;
687679 atom.out_n_sect = sect_index;
688680
689 sym.name = try macho_file.strings.insert(gpa, decl_name);
681 sym.name = try self.strtab.insert(gpa, decl_name);
690682 atom.flags.alive = true;
691683 atom.name = sym.name;
692684 nlist.n_strx = sym.name;
......@@ -796,7 +788,7 @@ fn createTlvInitializer(
796788 atom.out_n_sect = sect_index;
797789
798790 sym.value = 0;
799 sym.name = try macho_file.strings.insert(gpa, sym_name);
791 sym.name = try self.strtab.insert(gpa, sym_name);
800792 atom.flags.alive = true;
801793 atom.name = sym.name;
802794 nlist.n_strx = sym.name;
......@@ -849,7 +841,7 @@ fn createTlvDescriptor(
849841 atom.out_n_sect = sect_index;
850842
851843 sym.value = 0;
852 sym.name = try macho_file.strings.insert(gpa, name);
844 sym.name = try self.strtab.insert(gpa, name);
853845 atom.flags.alive = true;
854846 atom.name = sym.name;
855847 nlist.n_strx = sym.name;
......@@ -1019,7 +1011,7 @@ fn lowerConst(
10191011 };
10201012
10211013 const sym = macho_file.getSymbol(sym_index);
1022 const name_str_index = try macho_file.strings.insert(gpa, name);
1014 const name_str_index = try self.strtab.insert(gpa, name);
10231015 sym.name = name_str_index;
10241016 sym.out_n_sect = output_section_index;
10251017
......@@ -1110,7 +1102,7 @@ pub fn updateExports(
11101102 }
11111103
11121104 const exp_name = mod.intern_pool.stringToSlice(exp.opts.name);
1113 const global_nlist_index = if (metadata.@"export"(self, macho_file, exp_name)) |exp_index|
1105 const global_nlist_index = if (metadata.@"export"(self, exp_name)) |exp_index|
11141106 exp_index.*
11151107 else blk: {
11161108 const global_nlist_index = try self.getGlobalSymbol(macho_file, exp_name, null);
......@@ -1159,7 +1151,7 @@ fn updateLazySymbol(
11591151 lazy_sym.ty.fmt(mod),
11601152 });
11611153 defer gpa.free(name);
1162 break :blk try macho_file.strings.insert(gpa, name);
1154 break :blk try self.strtab.insert(gpa, name);
11631155 };
11641156
11651157 const src = if (lazy_sym.ty.getOwnerDeclOrNull(mod)) |owner_decl|
......@@ -1247,7 +1239,7 @@ pub fn deleteDeclExport(
12471239
12481240 const mod = macho_file.base.comp.module.?;
12491241 const exp_name = mod.intern_pool.stringToSlice(name);
1250 const nlist_index = metadata.@"export"(self, macho_file, exp_name) orelse return;
1242 const nlist_index = metadata.@"export"(self, exp_name) orelse return;
12511243
12521244 log.debug("deleting export '{s}'", .{exp_name});
12531245
......@@ -1268,7 +1260,7 @@ pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, l
12681260 const gpa = macho_file.base.comp.gpa;
12691261 const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
12701262 defer gpa.free(sym_name);
1271 const off = try macho_file.strings.insert(gpa, sym_name);
1263 const off = try self.strtab.insert(gpa, sym_name);
12721264 const lookup_gop = try self.globals_lookup.getOrPut(gpa, off);
12731265 if (!lookup_gop.found_existing) {
12741266 const nlist_index = try self.addNlist(gpa);
......@@ -1276,7 +1268,8 @@ pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, l
12761268 nlist.n_strx = off;
12771269 nlist.n_type = macho.N_EXT;
12781270 lookup_gop.value_ptr.* = nlist_index;
1279 const gop = try macho_file.getOrCreateGlobal(off);
1271 const global_name_off = try macho_file.strings.insert(gpa, sym_name);
1272 const gop = try macho_file.getOrCreateGlobal(global_name_off);
12801273 try self.symbols.append(gpa, gop.index);
12811274 }
12821275 return lookup_gop.value_ptr.*;
......@@ -1406,10 +1399,10 @@ const DeclMetadata = struct {
14061399 /// A list of all exports aliases of this Decl.
14071400 exports: std.ArrayListUnmanaged(Symbol.Index) = .{},
14081401
1409 fn @"export"(m: DeclMetadata, zig_object: *ZigObject, macho_file: *MachO, name: []const u8) ?*u32 {
1402 fn @"export"(m: DeclMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 {
14101403 for (m.exports.items) |*exp| {
14111404 const nlist = zig_object.symtab.items(.nlist)[exp.*];
1412 const exp_name = macho_file.strings.getAssumeExists(nlist.n_strx);
1405 const exp_name = zig_object.strtab.getAssumeExists(nlist.n_strx);
14131406 if (mem.eql(u8, name, exp_name)) return exp;
14141407 }
14151408 return null;
src/link/MachO/relocatable.zig+1-2
......@@ -290,8 +290,7 @@ fn writeAtoms(macho_file: *MachO) !void {
290290 assert(atom.flags.alive);
291291 const off = math.cast(usize, atom.value - header.addr) orelse return error.Overflow;
292292 const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
293 const atom_data = try atom.getFile(macho_file).object.getAtomData(atom.*);
294 @memcpy(code[off..][0..atom_size], atom_data);
293 try atom.getFile(macho_file).object.getAtomData(atom.*, code[off..][0..atom_size]);
295294 try atom.writeRelocs(macho_file, code[off..][0..atom_size], &relocs);
296295 }
297296