authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-03-13 13:16:34+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-03-17 19:59:57+01:00
log1ec620be62e9acdafca8182b2452c3e3a1dc0ea9
tree6d99d52867eaa4aa241892ac78c37d8aeaca79df
parentac0c669473b20a1fdcb818b92381d5ac5d70b64e

zld: fix GOT loads and indirection on x86_64


3 files changed, 109 insertions(+), 14 deletions(-)

lib/std/macho.zig+1-1
......@@ -1616,7 +1616,7 @@ pub const GenericBlob = extern struct {
16161616 length: u32,
16171617};
16181618
1619/// The LC_DATA_IN_CODE load commands uses a linkedit_data_command
1619/// The LC_DATA_IN_CODE load commands uses a linkedit_data_command
16201620/// to point to an array of data_in_code_entry entries. Each entry
16211621/// describes a range of data in a code section.
16221622pub const data_in_code_entry = extern struct {
src/link/MachO/Archive.zig+6-3
......@@ -209,7 +209,7 @@ fn readObject(self: *Archive, arch: std.Target.Cpu.Arch, ar_name: []const u8, re
209209
210210 try object.readLoadCommands(reader, .{ .offset = offset });
211211
212 if (object.symtab_cmd.index != null) {
212 if (object.symtab_cmd_index != null) {
213213 try object.readSymtab();
214214 try object.readStrtab();
215215 }
......@@ -245,8 +245,11 @@ fn getName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 {
245245 name = try allocator.dupe(u8, n);
246246 },
247247 .Length => |len| {
248 name = try allocator.alloc(u8, len);
249 try reader.readNoEof(name);
248 var n = try allocator.alloc(u8, len);
249 defer allocator.free(n);
250 try reader.readNoEof(n);
251 const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0));
252 name = try allocator.dupe(u8, n[0..actual_len.?]);
250253 },
251254 }
252255 return name;
src/link/MachO/Zld.zig+102-10
......@@ -77,6 +77,7 @@ lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
7777tlv_bootstrap: ?Import = null,
7878threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{},
7979local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
80nonlazy_pointers: std.StringArrayHashMapUnmanaged(GotEntry) = .{},
8081
8182strtab: std.ArrayListUnmanaged(u8) = .{},
8283
......@@ -85,6 +86,16 @@ stub_helper_stubs_start_off: ?u64 = null,
8586mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},
8687unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{},
8788
89// TODO this will require scanning the relocations at least one to work out
90// the exact amount of local GOT indirections. For the time being, set some
91// default value.
92const max_local_got_indirections: u16 = 1000;
93
94const GotEntry = struct {
95 index: u32,
96 target_addr: u64,
97};
98
8899const MappingKey = struct {
89100 object_id: u16,
90101 source_sect_id: u16,
......@@ -214,6 +225,10 @@ pub fn deinit(self: *Zld) void {
214225 self.allocator.free(entry.key);
215226 }
216227 self.nonlazy_imports.deinit(self.allocator);
228 for (self.nonlazy_pointers.items()) |*entry| {
229 self.allocator.free(entry.key);
230 }
231 self.nonlazy_pointers.deinit(self.allocator);
217232 for (self.exports.items()) |*entry| {
218233 self.allocator.free(entry.key);
219234 }
......@@ -874,7 +889,10 @@ fn allocateDataConstSegment(self: *Zld) !void {
874889
875890 // Set got size
876891 const got = &seg.sections.items[self.got_section_index.?];
877 got.size += nonlazy * @sizeOf(u64);
892 // TODO this will require scanning the relocations at least one to work out
893 // the exact amount of local GOT indirections. For the time being, set some
894 // default value.
895 got.size += (max_local_got_indirections + nonlazy) * @sizeOf(u64);
878896
879897 try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);
880898}
......@@ -1358,13 +1376,65 @@ fn doRelocs(self: *Zld) !void {
13581376 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
13591377
13601378 switch (rel_type) {
1361 .X86_64_RELOC_BRANCH,
1362 .X86_64_RELOC_GOT_LOAD,
1363 .X86_64_RELOC_GOT,
1364 => {
1379 .X86_64_RELOC_BRANCH => {
1380 assert(rel.r_length == 2);
1381 const inst = code[off..][0..4];
1382 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4));
1383 mem.writeIntLittle(u32, inst, displacement);
1384 },
1385 .X86_64_RELOC_GOT_LOAD => {
13651386 assert(rel.r_length == 2);
13661387 const inst = code[off..][0..4];
13671388 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4));
1389
1390 blk: {
1391 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1392 const got = data_const_seg.sections.items[self.got_section_index.?];
1393 if (got.addr <= target_addr and target_addr < got.addr + got.size) break :blk;
1394 log.debug(" | rewriting to leaq", .{});
1395 code[off - 2] = 0x8d;
1396 }
1397
1398 mem.writeIntLittle(u32, inst, displacement);
1399 },
1400 .X86_64_RELOC_GOT => {
1401 assert(rel.r_length == 2);
1402 // TODO Instead of referring to the target symbol directly, we refer to it
1403 // indirectly via GOT. Getting actual target address should be done in the
1404 // helper relocTargetAddr function rather than here.
1405 const sym = object.symtab.items[rel.r_symbolnum];
1406 const sym_name = try self.allocator.dupe(u8, object.getString(sym.n_strx));
1407 const res = try self.nonlazy_pointers.getOrPut(self.allocator, sym_name);
1408 defer if (res.found_existing) self.allocator.free(sym_name);
1409
1410 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1411 const got = data_const_seg.sections.items[self.got_section_index.?];
1412
1413 if (!res.found_existing) {
1414 const index = @intCast(u32, self.nonlazy_pointers.items().len) - 1;
1415 assert(index < max_local_got_indirections); // TODO This is just a temp solution.
1416 res.entry.value = .{
1417 .index = index,
1418 .target_addr = target_addr,
1419 };
1420 var buf: [@sizeOf(u64)]u8 = undefined;
1421 mem.writeIntLittle(u64, &buf, target_addr);
1422 const got_offset = got.offset + (index + self.nonlazy_imports.items().len) * @sizeOf(u64);
1423
1424 log.debug(" | GOT off 0x{x}", .{got.offset});
1425 log.debug(" | writing GOT entry 0x{x} at 0x{x}", .{ target_addr, got_offset });
1426
1427 try self.file.?.pwriteAll(&buf, got_offset);
1428 }
1429
1430 const index = res.entry.value.index + self.nonlazy_imports.items().len;
1431 const actual_target_addr = got.addr + index * @sizeOf(u64);
1432
1433 log.debug(" | GOT addr 0x{x}", .{got.addr});
1434 log.debug(" | actual target address in GOT 0x{x}", .{actual_target_addr});
1435
1436 const inst = code[off..][0..4];
1437 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, actual_target_addr) - @intCast(i64, this_addr) - 4));
13681438 mem.writeIntLittle(u32, inst, displacement);
13691439 },
13701440 .X86_64_RELOC_TLV => {
......@@ -2384,6 +2454,23 @@ fn writeRebaseInfoTable(self: *Zld) !void {
23842454 try pointers.ensureCapacity(pointers.items.len + self.local_rebases.items.len);
23852455 pointers.appendSliceAssumeCapacity(self.local_rebases.items);
23862456
2457 if (self.got_section_index) |idx| {
2458 // TODO this should be cleaned up!
2459 try pointers.ensureCapacity(pointers.items.len + self.nonlazy_pointers.items().len);
2460 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2461 const sect = seg.sections.items[idx];
2462 const base_offset = sect.addr - seg.inner.vmaddr;
2463 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
2464 const index_offset = @intCast(u32, self.nonlazy_imports.items().len);
2465 for (self.nonlazy_pointers.items()) |entry| {
2466 const index = index_offset + entry.value.index;
2467 pointers.appendAssumeCapacity(.{
2468 .offset = base_offset + index * @sizeOf(u64),
2469 .segment_id = segment_id,
2470 });
2471 }
2472 }
2473
23872474 if (self.la_symbol_ptr_section_index) |idx| {
23882475 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len);
23892476 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
......@@ -2851,8 +2938,9 @@ fn writeDynamicSymbolTable(self: *Zld) !void {
28512938
28522939 const lazy = self.lazy_imports.items();
28532940 const nonlazy = self.nonlazy_imports.items();
2941 const got_locals = self.nonlazy_pointers.items();
28542942 dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
2855 dysymtab.nindirectsyms = @intCast(u32, lazy.len * 2 + nonlazy.len);
2943 dysymtab.nindirectsyms = @intCast(u32, lazy.len * 2 + nonlazy.len + got_locals.len);
28562944 const needed_size = dysymtab.nindirectsyms * @sizeOf(u32);
28572945 seg.inner.filesize += needed_size;
28582946
......@@ -2867,20 +2955,24 @@ fn writeDynamicSymbolTable(self: *Zld) !void {
28672955 var writer = stream.writer();
28682956
28692957 stubs.reserved1 = 0;
2870 for (self.lazy_imports.items()) |_, i| {
2958 for (lazy) |_, i| {
28712959 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
28722960 try writer.writeIntLittle(u32, symtab_idx);
28732961 }
28742962
28752963 const base_id = @intCast(u32, lazy.len);
28762964 got.reserved1 = base_id;
2877 for (self.nonlazy_imports.items()) |_, i| {
2965 for (nonlazy) |_, i| {
28782966 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);
28792967 try writer.writeIntLittle(u32, symtab_idx);
28802968 }
2969 // TODO there should be one common set of GOT entries.
2970 for (got_locals) |_| {
2971 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
2972 }
28812973
2882 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, nonlazy.len);
2883 for (self.lazy_imports.items()) |_, i| {
2974 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, nonlazy.len) + @intCast(u32, got_locals.len);
2975 for (lazy) |_, i| {
28842976 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
28852977 try writer.writeIntLittle(u32, symtab_idx);
28862978 }