authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-04-18 17:12:05+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-04-21 22:44:27+02:00
log711bc2cf3919a2010503610574e7dfcad8a17929
tree4ad1f7023fb7a7652079728c789f44304fbaf57d
parent8e3100ae02fef20ce08a679f6212b3d673a5c8f0

macho: use generic TableSection for GOT mgmt


3 files changed, 105 insertions(+), 69 deletions(-)

src/link/MachO.zig+77-48
...@@ -41,6 +41,7 @@ const Md5 = std.crypto.hash.Md5;...@@ -41,6 +41,7 @@ const Md5 = std.crypto.hash.Md5;
41const Module = @import("../Module.zig");41const Module = @import("../Module.zig");
42const Relocation = @import("MachO/Relocation.zig");42const Relocation = @import("MachO/Relocation.zig");
43const StringTable = @import("strtab.zig").StringTable;43const StringTable = @import("strtab.zig").StringTable;
44const TableSection = @import("table_section.zig").TableSection;
44const Trie = @import("MachO/Trie.zig");45const Trie = @import("MachO/Trie.zig");
45const Type = @import("../type.zig").Type;46const Type = @import("../type.zig").Type;
46const TypedValue = @import("../TypedValue.zig");47const TypedValue = @import("../TypedValue.zig");
...@@ -154,13 +155,14 @@ stub_helper_preamble_atom_index: ?Atom.Index = null,...@@ -154,13 +155,14 @@ stub_helper_preamble_atom_index: ?Atom.Index = null,
154155
155strtab: StringTable(.strtab) = .{},156strtab: StringTable(.strtab) = .{},
156157
157got_table: SectionTable = .{},158got_table: TableSection(SymbolWithLoc) = .{},
158stubs_table: SectionTable = .{},159stubs_table: SectionTable = .{},
159tlv_table: SectionTable = .{},160tlv_table: SectionTable = .{},
160161
161error_flags: File.ErrorFlags = File.ErrorFlags{},162error_flags: File.ErrorFlags = File.ErrorFlags{},
162163
163segment_table_dirty: bool = false,164segment_table_dirty: bool = false,
165got_table_count_dirty: bool = false,
164166
165/// A helper var to indicate if we are at the start of the incremental updates, or167/// A helper var to indicate if we are at the start of the incremental updates, or
166/// already somewhere further along the update-and-run chain.168/// already somewhere further along the update-and-run chain.
...@@ -1270,6 +1272,30 @@ fn updateAtomInMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index:...@@ -1270,6 +1272,30 @@ fn updateAtomInMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index:
1270 if (nwritten != code.len) return error.InputOutput;1272 if (nwritten != code.len) return error.InputOutput;
1271}1273}
12721274
1275fn writeOffsetTableEntry(self: *MachO, index: @TypeOf(self.got_table).Index) !void {
1276 const sect_id = self.got_section_index.?;
1277
1278 if (self.got_table_count_dirty) {
1279 const needed_size = self.got_table.entries.items.len * @sizeOf(u64);
1280 try self.growSection(sect_id, needed_size);
1281 self.got_table_count_dirty = false;
1282 }
1283
1284 const header = &self.sections.items(.header)[sect_id];
1285 const entry = self.got_table.entries.items[index];
1286 const entry_value = self.getSymbol(entry).n_value;
1287 const entry_offset = index * @sizeOf(u64);
1288 const file_offset = header.offset + entry_offset;
1289 const vmaddr = header.addr + entry_offset;
1290 _ = vmaddr;
1291
1292 var buf: [8]u8 = undefined;
1293 mem.writeIntLittle(u64, &buf, entry_value);
1294 try self.base.file.?.pwriteAll(&buf, file_offset);
1295
1296 // TODO write in memory
1297}
1298
1273fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void {1299fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void {
1274 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);1300 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
1275 try self.writeAtom(atom_index, &buffer);1301 try self.writeAtom(atom_index, &buffer);
...@@ -1290,10 +1316,9 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {...@@ -1290,10 +1316,9 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {
1290 log.debug("marking relocs dirty by address: {x}", .{addr});1316 log.debug("marking relocs dirty by address: {x}", .{addr});
1291 for (self.relocs.values()) |*relocs| {1317 for (self.relocs.values()) |*relocs| {
1292 for (relocs.items) |*reloc| {1318 for (relocs.items) |*reloc| {
1293 const target_atom_index = reloc.getTargetAtomIndex(self) orelse continue;1319 const target_addr = reloc.getTargetBaseAddress(self) orelse continue;
1294 const target_atom = self.getAtom(target_atom_index);1320 if (target_addr == 0) continue;
1295 const target_sym = target_atom.getSymbol(self);1321 if (target_addr < addr) continue;
1296 if (target_sym.n_value < addr) continue;
1297 reloc.dirty = true;1322 reloc.dirty = true;
1298 }1323 }
1299 }1324 }
...@@ -1335,40 +1360,6 @@ pub fn createAtom(self: *MachO) !Atom.Index {...@@ -1335,40 +1360,6 @@ pub fn createAtom(self: *MachO) !Atom.Index {
1335 return atom_index;1360 return atom_index;
1336}1361}
13371362
1338pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index {
1339 const atom_index = try self.createAtom();
1340 self.getAtomPtr(atom_index).size = @sizeOf(u64);
1341
1342 const sym = self.getAtom(atom_index).getSymbolPtr(self);
1343 sym.n_type = macho.N_SECT;
1344 sym.n_sect = self.got_section_index.? + 1;
1345 sym.n_value = try self.allocateAtom(atom_index, @sizeOf(u64), @alignOf(u64));
1346
1347 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});
1348
1349 try Atom.addRelocation(self, atom_index, .{
1350 .type = .unsigned,
1351 .target = target,
1352 .offset = 0,
1353 .addend = 0,
1354 .pcrel = false,
1355 .length = 3,
1356 });
1357
1358 const target_sym = self.getSymbol(target);
1359 if (target_sym.undf()) {
1360 try Atom.addBinding(self, atom_index, .{
1361 .target = self.getGlobal(self.getSymbolName(target)).?,
1362 .offset = 0,
1363 });
1364 } else {
1365 try Atom.addRebase(self, atom_index, 0);
1366 }
1367 try self.writePtrWidthAtom(atom_index);
1368
1369 return atom_index;
1370}
1371
1372fn createDyldPrivateAtom(self: *MachO) !void {1363fn createDyldPrivateAtom(self: *MachO) !void {
1373 if (self.dyld_private_atom_index != null) return;1364 if (self.dyld_private_atom_index != null) return;
13741365
...@@ -2104,9 +2095,7 @@ fn allocateGlobal(self: *MachO) !u32 {...@@ -2104,9 +2095,7 @@ fn allocateGlobal(self: *MachO) !u32 {
2104fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {2095fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {
2105 if (self.got_table.lookup.contains(target)) return;2096 if (self.got_table.lookup.contains(target)) return;
2106 const got_index = try self.got_table.allocateEntry(self.base.allocator, target);2097 const got_index = try self.got_table.allocateEntry(self.base.allocator, target);
2107 const got_atom_index = try self.createGotAtom(target);2098 try self.writeOffsetTableEntry(got_index);
2108 const got_atom = self.getAtom(got_atom_index);
2109 self.got_table.entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
2110 self.markRelocsDirtyByTarget(target);2099 self.markRelocsDirtyByTarget(target);
2111}2100}
21122101
...@@ -2532,8 +2521,8 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64...@@ -2532,8 +2521,8 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []u8) !u64
2532 } else .{ .sym_index = sym_index };2521 } else .{ .sym_index = sym_index };
2533 self.markRelocsDirtyByTarget(target);2522 self.markRelocsDirtyByTarget(target);
2534 log.debug(" (updating GOT entry)", .{});2523 log.debug(" (updating GOT entry)", .{});
2535 const got_atom_index = self.got_table.getAtomIndex(self, target).?;2524 const got_atom_index = self.got_table.lookup.get(target).?;
2536 try self.writePtrWidthAtom(got_atom_index);2525 try self.writeOffsetTableEntry(got_atom_index);
2537 }2526 }
2538 } else if (code_len < atom.size) {2527 } else if (code_len < atom.size) {
2539 self.shrinkAtom(atom_index, code_len);2528 self.shrinkAtom(atom_index, code_len);
...@@ -3276,6 +3265,20 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {...@@ -3276,6 +3265,20 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
3276 }3265 }
3277 }3266 }
32783267
3268 // Gather GOT pointers
3269 const segment_index = self.sections.items(.segment_index)[self.got_section_index.?];
3270 for (self.got_table.entries.items, 0..) |entry, i| {
3271 if (!self.got_table.lookup.contains(entry)) continue;
3272 const sym = self.getSymbol(entry);
3273 if (sym.undf()) continue;
3274 const offset = i * @sizeOf(u64);
3275 log.debug(" | rebase at {x}", .{offset});
3276 rebase.entries.appendAssumeCapacity(.{
3277 .offset = offset,
3278 .segment_id = segment_index,
3279 });
3280 }
3281
3279 try rebase.finalize(gpa);3282 try rebase.finalize(gpa);
3280}3283}
32813284
...@@ -3320,6 +3323,32 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {...@@ -3320,6 +3323,32 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
3320 }3323 }
3321 }3324 }
33223325
3326 // Gather GOT pointers
3327 const segment_index = self.sections.items(.segment_index)[self.got_section_index.?];
3328 for (self.got_table.entries.items, 0..) |entry, i| {
3329 if (!self.got_table.lookup.contains(entry)) continue;
3330 const sym = self.getSymbol(entry);
3331 if (!sym.undf()) continue;
3332 const offset = i * @sizeOf(u64);
3333 const bind_sym = self.getSymbol(entry);
3334 const bind_sym_name = self.getSymbolName(entry);
3335 const dylib_ordinal = @divTrunc(
3336 @bitCast(i16, bind_sym.n_desc),
3337 macho.N_SYMBOL_RESOLVER,
3338 );
3339 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
3340 offset,
3341 bind_sym_name,
3342 dylib_ordinal,
3343 });
3344 bind.entries.appendAssumeCapacity(.{
3345 .target = entry,
3346 .offset = offset,
3347 .segment_id = segment_index,
3348 .addend = 0,
3349 });
3350 }
3351
3323 try bind.finalize(gpa, self);3352 try bind.finalize(gpa, self);
3324}3353}
33253354
...@@ -3620,10 +3649,10 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {...@@ -3620,10 +3649,10 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
3620 const got = &self.sections.items(.header)[sect_id];3649 const got = &self.sections.items(.header)[sect_id];
3621 got.reserved1 = nstubs;3650 got.reserved1 = nstubs;
3622 for (self.got_table.entries.items) |entry| {3651 for (self.got_table.entries.items) |entry| {
3623 if (entry.sym_index == 0) continue;3652 if (!self.got_table.lookup.contains(entry)) continue;
3624 const target_sym = self.getSymbol(entry.target);3653 const target_sym = self.getSymbol(entry);
3625 if (target_sym.undf()) {3654 if (target_sym.undf()) {
3626 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);3655 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
3627 } else {3656 } else {
3628 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);3657 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
3629 }3658 }
...@@ -4321,7 +4350,7 @@ pub fn logSymtab(self: *MachO) void {...@@ -4321,7 +4350,7 @@ pub fn logSymtab(self: *MachO) void {
4321 }4350 }
43224351
4323 log.debug("GOT entries:", .{});4352 log.debug("GOT entries:", .{});
4324 log.debug("{}", .{self.got_table.fmtDebug(self)});4353 log.debug("{}", .{self.got_table});
43254354
4326 log.debug("stubs entries:", .{});4355 log.debug("stubs entries:", .{});
4327 log.debug("{}", .{self.stubs_table.fmtDebug(self)});4356 log.debug("{}", .{self.stubs_table.fmtDebug(self)});
src/link/MachO/DebugSymbols.zig+2-2
...@@ -230,7 +230,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {...@@ -230,7 +230,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
230 .got_load => blk: {230 .got_load => blk: {
231 const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?;231 const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?;
232 const got_entry = macho_file.got_table.entries.items[got_index];232 const got_entry = macho_file.got_table.entries.items[got_index];
233 break :blk got_entry.getSymbol(macho_file);233 break :blk macho_file.getSymbol(got_entry);
234 },234 },
235 };235 };
236 if (sym.n_value == reloc.prev_vaddr) continue;236 if (sym.n_value == reloc.prev_vaddr) continue;
...@@ -240,7 +240,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {...@@ -240,7 +240,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
240 .got_load => blk: {240 .got_load => blk: {
241 const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?;241 const got_index = macho_file.got_table.lookup.get(.{ .sym_index = reloc.target }).?;
242 const got_entry = macho_file.got_table.entries.items[got_index];242 const got_entry = macho_file.got_table.entries.items[got_index];
243 break :blk got_entry.getName(macho_file);243 break :blk macho_file.getSymbolName(got_entry);
244 },244 },
245 };245 };
246 const sect = &self.sections.items[self.debug_info_section_index.?];246 const sect = &self.sections.items[self.debug_info_section_index.?];
src/link/MachO/Relocation.zig+26-19
...@@ -39,25 +39,35 @@ pub const Type = enum {...@@ -39,25 +39,35 @@ pub const Type = enum {
3939
40/// Returns true if and only if the reloc is dirty AND the target address is available.40/// Returns true if and only if the reloc is dirty AND the target address is available.
41pub fn isResolvable(self: Relocation, macho_file: *MachO) bool {41pub fn isResolvable(self: Relocation, macho_file: *MachO) bool {
42 _ = self.getTargetAtomIndex(macho_file) orelse return false;42 const addr = self.getTargetBaseAddress(macho_file) orelse return false;
43 if (addr == 0) return false;
43 return self.dirty;44 return self.dirty;
44}45}
4546
46pub fn getTargetAtomIndex(self: Relocation, macho_file: *MachO) ?Atom.Index {47pub fn getTargetBaseAddress(self: Relocation, macho_file: *MachO) ?u64 {
47 return switch (self.type) {48 switch (self.type) {
48 .got, .got_page, .got_pageoff => macho_file.got_table.getAtomIndex(macho_file, self.target),49 .got, .got_page, .got_pageoff => {
50 const got_index = macho_file.got_table.lookup.get(self.target) orelse return null;
51 const header = macho_file.sections.items(.header)[macho_file.got_section_index.?];
52 return header.addr + got_index * @sizeOf(u64);
53 },
49 .tlv => {54 .tlv => {
50 const thunk_atom_index = macho_file.tlv_table.getAtomIndex(macho_file, self.target) orelse55 const thunk_atom_index = macho_file.tlv_table.getAtomIndex(macho_file, self.target) orelse return null;
51 return null;
52 const thunk_atom = macho_file.getAtom(thunk_atom_index);56 const thunk_atom = macho_file.getAtom(thunk_atom_index);
53 return macho_file.got_table.getAtomIndex(macho_file, thunk_atom.getSymbolWithLoc());57 const got_index = macho_file.got_table.lookup.get(thunk_atom.getSymbolWithLoc()) orelse return null;
58 const header = macho_file.sections.items(.header)[macho_file.got_section_index.?];
59 return header.addr + got_index * @sizeOf(u64);
54 },60 },
55 .branch => if (macho_file.stubs_table.getAtomIndex(macho_file, self.target)) |index|61 .branch => {
56 index62 const atom_index = blk: {
57 else63 if (macho_file.stubs_table.getAtomIndex(macho_file, self.target)) |index| break :blk index;
58 macho_file.getAtomIndexForSymbol(self.target),64 break :blk macho_file.getAtomIndexForSymbol(self.target) orelse return null;
59 else => macho_file.getAtomIndexForSymbol(self.target),65 };
60 };66 const atom = macho_file.getAtom(atom_index);
67 return atom.getSymbol(macho_file).n_value;
68 },
69 else => return macho_file.getSymbol(self.target).n_value,
70 }
61}71}
6272
63pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, code: []u8) void {73pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, code: []u8) void {
...@@ -66,17 +76,14 @@ pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, cod...@@ -66,17 +76,14 @@ pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, cod
66 const source_sym = atom.getSymbol(macho_file);76 const source_sym = atom.getSymbol(macho_file);
67 const source_addr = source_sym.n_value + self.offset;77 const source_addr = source_sym.n_value + self.offset;
6878
69 const target_atom_index = self.getTargetAtomIndex(macho_file).?; // Oops, you didn't check if the relocation can be resolved with isResolvable().79 const target_base_addr = self.getTargetBaseAddress(macho_file).?; // Oops, you didn't check if the relocation can be resolved with isResolvable().
70 const target_atom = macho_file.getAtom(target_atom_index);
71
72 const target_addr: i64 = switch (self.type) {80 const target_addr: i64 = switch (self.type) {
73 .tlv_initializer => blk: {81 .tlv_initializer => blk: {
74 assert(self.addend == 0); // Addend here makes no sense.82 assert(self.addend == 0); // Addend here makes no sense.
75 const header = macho_file.sections.items(.header)[macho_file.thread_data_section_index.?];83 const header = macho_file.sections.items(.header)[macho_file.thread_data_section_index.?];
76 const target_sym = target_atom.getSymbol(macho_file);84 break :blk @intCast(i64, target_base_addr - header.addr);
77 break :blk @intCast(i64, target_sym.n_value - header.addr);
78 },85 },
79 else => @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend,86 else => @intCast(i64, target_base_addr) + self.addend,
80 };87 };
8188
82 log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{89 log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{