authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-04-22 00:51:14+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-04-22 12:21:37+02:00
loga7e4d1722616ba9fabfae8b830902e3651c645e4
tree8e43482601ef089420de83484f2d43f33e5e2ced
parent3c5e840732053318f5e722d6cc16f65d51cdc297

link/macho: introduce Atom extras for out-of-band data


6 files changed, 157 insertions(+), 41 deletions(-)

src/link/MachO.zig+47
...@@ -65,6 +65,7 @@ entry_index: ?Symbol.Index = null,...@@ -65,6 +65,7 @@ entry_index: ?Symbol.Index = null,
6565
66/// List of atoms that are either synthetic or map directly to the Zig source program.66/// List of atoms that are either synthetic or map directly to the Zig source program.
67atoms: std.ArrayListUnmanaged(Atom) = .{},67atoms: std.ArrayListUnmanaged(Atom) = .{},
68atoms_extra: std.ArrayListUnmanaged(u32) = .{},
68thunks: std.ArrayListUnmanaged(Thunk) = .{},69thunks: std.ArrayListUnmanaged(Thunk) = .{},
69unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record) = .{},70unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record) = .{},
7071
...@@ -246,6 +247,7 @@ pub fn createEmpty(...@@ -246,6 +247,7 @@ pub fn createEmpty(
246 try self.files.append(gpa, .null);247 try self.files.append(gpa, .null);
247 // Atom at index 0 is reserved as null atom248 // Atom at index 0 is reserved as null atom
248 try self.atoms.append(gpa, .{});249 try self.atoms.append(gpa, .{});
250 try self.atoms_extra.append(gpa, 0);
249 // Append empty string to string tables251 // Append empty string to string tables
250 try self.strings.buffer.append(gpa, 0);252 try self.strings.buffer.append(gpa, 0);
251 try self.strtab.append(gpa, 0);253 try self.strtab.append(gpa, 0);
...@@ -350,6 +352,7 @@ pub fn deinit(self: *MachO) void {...@@ -350,6 +352,7 @@ pub fn deinit(self: *MachO) void {
350 self.unwind_info.deinit(gpa);352 self.unwind_info.deinit(gpa);
351353
352 self.atoms.deinit(gpa);354 self.atoms.deinit(gpa);
355 self.atoms_extra.deinit(gpa);
353 for (self.thunks.items) |*thunk| {356 for (self.thunks.items) |*thunk| {
354 thunk.deinit(gpa);357 thunk.deinit(gpa);
355 }358 }
...@@ -3865,6 +3868,50 @@ pub fn getAtom(self: *MachO, index: Atom.Index) ?*Atom {...@@ -3865,6 +3868,50 @@ pub fn getAtom(self: *MachO, index: Atom.Index) ?*Atom {
3865 return &self.atoms.items[index];3868 return &self.atoms.items[index];
3866}3869}
38673870
3871pub fn addAtomExtra(self: *MachO, extra: Atom.Extra) !u32 {
3872 const fields = @typeInfo(Atom.Extra).Struct.fields;
3873 try self.atoms_extra.ensureUnusedCapacity(self.base.comp.gpa, fields.len);
3874 return self.addAtomExtraAssumeCapacity(extra);
3875}
3876
3877pub fn addAtomExtraAssumeCapacity(self: *MachO, extra: Atom.Extra) u32 {
3878 const index = @as(u32, @intCast(self.atoms_extra.items.len));
3879 const fields = @typeInfo(Atom.Extra).Struct.fields;
3880 inline for (fields) |field| {
3881 self.atoms_extra.appendAssumeCapacity(switch (field.type) {
3882 u32 => @field(extra, field.name),
3883 else => @compileError("bad field type"),
3884 });
3885 }
3886 return index;
3887}
3888
3889pub fn getAtomExtra(self: *MachO, index: u32) ?Atom.Extra {
3890 if (index == 0) return null;
3891 const fields = @typeInfo(Atom.Extra).Struct.fields;
3892 var i: usize = index;
3893 var result: Atom.Extra = undefined;
3894 inline for (fields) |field| {
3895 @field(result, field.name) = switch (field.type) {
3896 u32 => self.atoms_extra.items[i],
3897 else => @compileError("bad field type"),
3898 };
3899 i += 1;
3900 }
3901 return result;
3902}
3903
3904pub fn setAtomExtra(self: *MachO, index: u32, extra: Atom.Extra) void {
3905 assert(index > 0);
3906 const fields = @typeInfo(Atom.Extra).Struct.fields;
3907 inline for (fields, 0..) |field, i| {
3908 self.atoms_extra.items[index + i] = switch (field.type) {
3909 u32 => @field(extra, field.name),
3910 else => @compileError("bad field type"),
3911 };
3912 }
3913}
3914
3868pub fn addSymbol(self: *MachO) !Symbol.Index {3915pub fn addSymbol(self: *MachO) !Symbol.Index {
3869 const index = @as(Symbol.Index, @intCast(self.symbols.items.len));3916 const index = @as(Symbol.Index, @intCast(self.symbols.items.len));
3870 const symbol = try self.symbols.addOne(self.base.comp.gpa);3917 const symbol = try self.symbols.addOne(self.base.comp.gpa);
src/link/MachO/Atom.zig+80-26
...@@ -23,18 +23,9 @@ out_n_sect: u8 = 0,...@@ -23,18 +23,9 @@ out_n_sect: u8 = 0,
23/// off + size <= parent section size.23/// off + size <= parent section size.
24off: u64 = 0,24off: u64 = 0,
2525
26/// Relocations of this atom.
27relocs: Loc = .{},
28
29/// Index of this atom in the linker's atoms table.26/// Index of this atom in the linker's atoms table.
30atom_index: Index = 0,27atom_index: Index = 0,
3128
32/// Index of the thunk for this atom.
33thunk_index: Thunk.Index = 0,
34
35/// Unwind records associated with this atom.
36unwind_records: Loc = .{},
37
38flags: Flags = .{},29flags: Flags = .{},
3930
40/// Points to the previous and next neighbors, based on the `text_offset`.31/// Points to the previous and next neighbors, based on the `text_offset`.
...@@ -42,6 +33,8 @@ flags: Flags = .{},...@@ -42,6 +33,8 @@ flags: Flags = .{},
42prev_index: Index = 0,33prev_index: Index = 0,
43next_index: Index = 0,34next_index: Index = 0,
4435
36extra: u32 = 0,
37
45pub fn getName(self: Atom, macho_file: *MachO) [:0]const u8 {38pub fn getName(self: Atom, macho_file: *MachO) [:0]const u8 {
46 return switch (self.getFile(macho_file)) {39 return switch (self.getFile(macho_file)) {
47 .dylib => unreachable,40 .dylib => unreachable,
...@@ -67,7 +60,7 @@ pub fn getData(self: Atom, macho_file: *MachO, buffer: []u8) !void {...@@ -67,7 +60,7 @@ pub fn getData(self: Atom, macho_file: *MachO, buffer: []u8) !void {
67pub fn getRelocs(self: Atom, macho_file: *MachO) []const Relocation {60pub fn getRelocs(self: Atom, macho_file: *MachO) []const Relocation {
68 return switch (self.getFile(macho_file)) {61 return switch (self.getFile(macho_file)) {
69 .dylib => unreachable,62 .dylib => unreachable,
70 inline else => |x| x.getAtomRelocs(self),63 inline else => |x| x.getAtomRelocs(self, macho_file),
71 };64 };
72}65}
7366
...@@ -95,10 +88,11 @@ pub fn getPriority(self: Atom, macho_file: *MachO) u64 {...@@ -95,10 +88,11 @@ pub fn getPriority(self: Atom, macho_file: *MachO) u64 {
95}88}
9689
97pub fn getUnwindRecords(self: Atom, macho_file: *MachO) []const UnwindInfo.Record.Index {90pub fn getUnwindRecords(self: Atom, macho_file: *MachO) []const UnwindInfo.Record.Index {
91 if (!self.flags.unwind) return &[0]UnwindInfo.Record.Index{};
92 const extra = self.getExtra(macho_file).?;
98 return switch (self.getFile(macho_file)) {93 return switch (self.getFile(macho_file)) {
99 .dylib => unreachable,94 .dylib, .zig_object, .internal => unreachable,
100 .zig_object, .internal => &[0]UnwindInfo.Record.Index{},95 .object => |x| x.unwind_records.items[extra.unwind_index..][0..extra.unwind_count],
101 .object => |x| x.unwind_records.items[self.unwind_records.pos..][0..self.unwind_records.len],
102 };96 };
103}97}
10498
...@@ -114,7 +108,38 @@ pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {...@@ -114,7 +108,38 @@ pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {
114}108}
115109
116pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {110pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {
117 return macho_file.getThunk(self.thunk_index);111 assert(self.flags.thunk);
112 const extra = self.getExtra(macho_file).?;
113 return macho_file.getThunk(extra.thunk);
114}
115
116const AddExtraOpts = struct {
117 thunk: ?u32 = null,
118 rel_index: ?u32 = null,
119 rel_count: ?u32 = null,
120 unwind_index: ?u32 = null,
121 unwind_count: ?u32 = null,
122};
123
124pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) !void {
125 if (atom.getExtra(macho_file) == null) {
126 atom.extra = try macho_file.addAtomExtra(.{});
127 }
128 var extra = atom.getExtra(macho_file).?;
129 inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {
130 if (@field(opts, field.name)) |x| {
131 @field(extra, field.name) = x;
132 }
133 }
134 atom.setExtra(extra, macho_file);
135}
136
137pub inline fn getExtra(atom: Atom, macho_file: *MachO) ?Extra {
138 return macho_file.getAtomExtra(atom.extra);
139}
140
141pub inline fn setExtra(atom: Atom, extra: Extra, macho_file: *MachO) void {
142 macho_file.setAtomExtra(atom.extra, extra);
118}143}
119144
120pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {145pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
...@@ -403,14 +428,20 @@ pub fn addReloc(self: *Atom, macho_file: *MachO, reloc: Relocation) !void {...@@ -403,14 +428,20 @@ pub fn addReloc(self: *Atom, macho_file: *MachO, reloc: Relocation) !void {
403 const gpa = macho_file.base.comp.gpa;428 const gpa = macho_file.base.comp.gpa;
404 const file = self.getFile(macho_file);429 const file = self.getFile(macho_file);
405 assert(file == .zig_object);430 assert(file == .zig_object);
406 const rels = &file.zig_object.relocs.items[self.relocs.pos];431 assert(self.flags.relocs);
432 var extra = self.getExtra(macho_file).?;
433 const rels = &file.zig_object.relocs.items[extra.rel_index];
407 try rels.append(gpa, reloc);434 try rels.append(gpa, reloc);
408 self.relocs.len += 1;435 extra.rel_count += 1;
436 self.setExtra(extra, macho_file);
409}437}
410438
411pub fn freeRelocs(self: *Atom, macho_file: *MachO) void {439pub fn freeRelocs(self: *Atom, macho_file: *MachO) void {
412 self.getFile(macho_file).zig_object.freeAtomRelocs(self.*);440 if (!self.flags.relocs) return;
413 self.relocs.len = 0;441 self.getFile(macho_file).zig_object.freeAtomRelocs(self.*, macho_file);
442 var extra = self.getExtra(macho_file).?;
443 extra.rel_count = 0;
444 self.setExtra(extra, macho_file);
414}445}
415446
416pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {447pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
...@@ -1117,19 +1148,21 @@ fn format2(...@@ -1117,19 +1148,21 @@ fn format2(
1117 _ = unused_fmt_string;1148 _ = unused_fmt_string;
1118 const atom = ctx.atom;1149 const atom = ctx.atom;
1119 const macho_file = ctx.macho_file;1150 const macho_file = ctx.macho_file;
1120 try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{1151 try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d})", .{
1121 atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),1152 atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
1122 atom.out_n_sect, atom.alignment, atom.size,1153 atom.out_n_sect, atom.alignment, atom.size,
1123 atom.getRelocs(macho_file).len, atom.thunk_index,1154 atom.getRelocs(macho_file).len,
1124 });1155 });
1156 if (atom.flags.thunk) try writer.print(" : thunk({d})", .{atom.getExtra(macho_file).?.thunk});
1125 if (!atom.flags.alive) try writer.writeAll(" : [*]");1157 if (!atom.flags.alive) try writer.writeAll(" : [*]");
1126 if (atom.unwind_records.len > 0) {1158 if (atom.flags.unwind) {
1127 try writer.writeAll(" : unwind{ ");1159 try writer.writeAll(" : unwind{ ");
1128 for (atom.getUnwindRecords(macho_file), atom.unwind_records.pos..) |index, i| {1160 const extra = atom.getExtra(macho_file).?;
1161 for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| {
1129 const rec = macho_file.getUnwindRecord(index);1162 const rec = macho_file.getUnwindRecord(index);
1130 try writer.print("{d}", .{index});1163 try writer.print("{d}", .{index});
1131 if (!rec.alive) try writer.writeAll("([*])");1164 if (!rec.alive) try writer.writeAll("([*])");
1132 if (i < atom.unwind_records.pos + atom.unwind_records.len - 1) try writer.writeAll(", ");1165 if (i < extra.unwind_index + extra.unwind_count - 1) try writer.writeAll(", ");
1133 }1166 }
1134 try writer.writeAll(" }");1167 try writer.writeAll(" }");
1135 }1168 }
...@@ -1143,11 +1176,32 @@ pub const Flags = packed struct {...@@ -1143,11 +1176,32 @@ pub const Flags = packed struct {
11431176
1144 /// Specifies if the atom has been visited during garbage collection.1177 /// Specifies if the atom has been visited during garbage collection.
1145 visited: bool = false,1178 visited: bool = false,
1179
1180 /// Whether this atom has a range extension thunk.
1181 thunk: bool = false,
1182
1183 /// Whether this atom has any relocations.
1184 relocs: bool = false,
1185
1186 /// Whether this atom has any unwind records.
1187 unwind: bool = false,
1146};1188};
11471189
1148pub const Loc = struct {1190pub const Extra = struct {
1149 pos: u32 = 0,1191 /// Index of the range extension thunk of this atom.
1150 len: u32 = 0,1192 thunk: u32 = 0,
1193
1194 /// Start index of relocations belonging to this atom.
1195 rel_index: u32 = 0,
1196
1197 /// Count of relocations belonging to this atom.
1198 rel_count: u32 = 0,
1199
1200 /// Start index of relocations belonging to this atom.
1201 unwind_index: u32 = 0,
1202
1203 /// Count of relocations belonging to this atom.
1204 unwind_count: u32 = 0,
1151};1205};
11521206
1153pub const Alignment = @import("../../InternPool.zig").Alignment;1207pub const Alignment = @import("../../InternPool.zig").Alignment;
src/link/MachO/InternalObject.zig+6-3
...@@ -115,7 +115,8 @@ fn addObjcSelrefsSection(...@@ -115,7 +115,8 @@ fn addObjcSelrefsSection(
115 .has_subtractor = false,115 .has_subtractor = false,
116 },116 },
117 });117 });
118 atom.relocs = .{ .pos = 0, .len = 1 };118 try atom.addExtra(.{ .rel_index = 0, .rel_count = 1 }, macho_file);
119 atom.flags.relocs = true;
119 self.num_rebase_relocs += 1;120 self.num_rebase_relocs += 1;
120121
121 return atom_index;122 return atom_index;
...@@ -183,9 +184,11 @@ pub fn getAtomData(self: *const InternalObject, atom: Atom, buffer: []u8) !void...@@ -183,9 +184,11 @@ pub fn getAtomData(self: *const InternalObject, atom: Atom, buffer: []u8) !void
183 @memcpy(buffer, data[off..][0..size]);184 @memcpy(buffer, data[off..][0..size]);
184}185}
185186
186pub fn getAtomRelocs(self: *const InternalObject, atom: Atom) []const Relocation {187pub fn getAtomRelocs(self: *const InternalObject, atom: Atom, macho_file: *MachO) []const Relocation {
188 if (!atom.flags.relocs) return &[0]Relocation{};
189 const extra = atom.getExtra(macho_file).?;
187 const relocs = self.sections.items(.relocs)[atom.n_sect];190 const relocs = self.sections.items(.relocs)[atom.n_sect];
188 return relocs.items[atom.relocs.pos..][0..atom.relocs.len];191 return relocs.items[extra.rel_index..][0..extra.rel_count];
189}192}
190193
191fn addString(self: *InternalObject, allocator: Allocator, name: [:0]const u8) error{OutOfMemory}!u32 {194fn addString(self: *InternalObject, allocator: Allocator, name: [:0]const u8) error{OutOfMemory}!u32 {
src/link/MachO/Object.zig+10-5
...@@ -690,11 +690,13 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {...@@ -690,11 +690,13 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
690 if (!atom.flags.alive) continue;690 if (!atom.flags.alive) continue;
691 if (next_reloc >= relocs.items.len) break;691 if (next_reloc >= relocs.items.len) break;
692 const end_addr = atom.off + atom.size;692 const end_addr = atom.off + atom.size;
693 atom.relocs.pos = next_reloc;693 const rel_index = next_reloc;
694694
695 while (next_reloc < relocs.items.len and relocs.items[next_reloc].offset < end_addr) : (next_reloc += 1) {}695 while (next_reloc < relocs.items.len and relocs.items[next_reloc].offset < end_addr) : (next_reloc += 1) {}
696696
697 atom.relocs.len = next_reloc - atom.relocs.pos;697 const rel_count = next_reloc - rel_index;
698 try atom.addExtra(.{ .rel_index = rel_index, .rel_count = rel_count }, macho_file);
699 atom.flags.relocs = true;
698 }700 }
699 }701 }
700}702}
...@@ -1004,7 +1006,8 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {...@@ -1004,7 +1006,8 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
1004 {}1006 {}
10051007
1006 const atom = rec.getAtom(macho_file);1008 const atom = rec.getAtom(macho_file);
1007 atom.unwind_records = .{ .pos = start, .len = next_cu - start };1009 try atom.addExtra(.{ .unwind_index = start, .unwind_count = next_cu - start }, macho_file);
1010 atom.flags.unwind = true;
1008 }1011 }
1009}1012}
10101013
...@@ -1724,9 +1727,11 @@ pub fn getAtomData(self: *const Object, macho_file: *MachO, atom: Atom, buffer:...@@ -1724,9 +1727,11 @@ pub fn getAtomData(self: *const Object, macho_file: *MachO, atom: Atom, buffer:
1724 if (amt != buffer.len) return error.InputOutput;1727 if (amt != buffer.len) return error.InputOutput;
1725}1728}
17261729
1727pub fn getAtomRelocs(self: *const Object, atom: Atom) []const Relocation {1730pub fn getAtomRelocs(self: *const Object, atom: Atom, macho_file: *MachO) []const Relocation {
1731 if (!atom.flags.relocs) return &[0]Relocation{};
1732 const extra = atom.getExtra(macho_file).?;
1728 const relocs = self.sections.items(.relocs)[atom.n_sect];1733 const relocs = self.sections.items(.relocs)[atom.n_sect];
1729 return relocs.items[atom.relocs.pos..][0..atom.relocs.len];1734 return relocs.items[extra.rel_index..][0..extra.rel_count];
1730}1735}
17311736
1732fn addString(self: *Object, allocator: Allocator, name: [:0]const u8) error{OutOfMemory}!u32 {1737fn addString(self: *Object, allocator: Allocator, name: [:0]const u8) error{OutOfMemory}!u32 {
src/link/MachO/ZigObject.zig+12-6
...@@ -163,7 +163,8 @@ pub fn addAtom(self: *ZigObject, macho_file: *MachO) !Symbol.Index {...@@ -163,7 +163,8 @@ pub fn addAtom(self: *ZigObject, macho_file: *MachO) !Symbol.Index {
163 const relocs_index = @as(u32, @intCast(self.relocs.items.len));163 const relocs_index = @as(u32, @intCast(self.relocs.items.len));
164 const relocs = try self.relocs.addOne(gpa);164 const relocs = try self.relocs.addOne(gpa);
165 relocs.* = .{};165 relocs.* = .{};
166 atom.relocs = .{ .pos = relocs_index, .len = 0 };166 try atom.addExtra(.{ .rel_index = relocs_index, .rel_count = 0 }, macho_file);
167 atom.flags.relocs = true;
167168
168 return symbol_index;169 return symbol_index;
169}170}
...@@ -190,13 +191,18 @@ pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8...@@ -190,13 +191,18 @@ pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8
190 }191 }
191}192}
192193
193pub fn getAtomRelocs(self: *ZigObject, atom: Atom) []const Relocation {194pub fn getAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) []const Relocation {
194 const relocs = self.relocs.items[atom.relocs.pos];195 if (!atom.flags.relocs) return &[0]Relocation{};
195 return relocs.items[0..atom.relocs.len];196 const extra = atom.getExtra(macho_file).?;
197 const relocs = self.relocs.items[extra.rel_index];
198 return relocs.items[0..extra.rel_count];
196}199}
197200
198pub fn freeAtomRelocs(self: *ZigObject, atom: Atom) void {201pub fn freeAtomRelocs(self: *ZigObject, atom: Atom, macho_file: *MachO) void {
199 self.relocs.items[atom.relocs.pos].clearRetainingCapacity();202 if (atom.flags.relocs) {
203 const extra = atom.getExtra(macho_file).?;
204 self.relocs.items[extra.rel_index].clearRetainingCapacity();
205 }
200}206}
201207
202pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) void {208pub fn resolveSymbols(self: *ZigObject, macho_file: *MachO) void {
src/link/MachO/thunks.zig+2-1
...@@ -43,7 +43,8 @@ pub fn createThunks(sect_id: u8, macho_file: *MachO) !void {...@@ -43,7 +43,8 @@ pub fn createThunks(sect_id: u8, macho_file: *MachO) !void {
43 if (isReachable(atom, rel, macho_file)) continue;43 if (isReachable(atom, rel, macho_file)) continue;
44 try thunk.symbols.put(gpa, rel.target, {});44 try thunk.symbols.put(gpa, rel.target, {});
45 }45 }
46 atom.thunk_index = thunk_index;46 try atom.addExtra(.{ .thunk = thunk_index }, macho_file);
47 atom.flags.thunk = true;
47 }48 }
4849
49 thunk.value = try advance(header, thunk.size(), .@"4");50 thunk.value = try advance(header, thunk.size(), .@"4");