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 {...@@ -1616,7 +1616,7 @@ pub const GenericBlob = extern struct {
1616 length: u32,1616 length: u32,
1617};1617};
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
1620/// to point to an array of data_in_code_entry entries. Each entry1620/// to point to an array of data_in_code_entry entries. Each entry
1621/// describes a range of data in a code section.1621/// describes a range of data in a code section.
1622pub const data_in_code_entry = extern struct {1622pub 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...@@ -209,7 +209,7 @@ fn readObject(self: *Archive, arch: std.Target.Cpu.Arch, ar_name: []const u8, re
209209
210 try object.readLoadCommands(reader, .{ .offset = offset });210 try object.readLoadCommands(reader, .{ .offset = offset });
211211
212 if (object.symtab_cmd.index != null) {212 if (object.symtab_cmd_index != null) {
213 try object.readSymtab();213 try object.readSymtab();
214 try object.readStrtab();214 try object.readStrtab();
215 }215 }
...@@ -245,8 +245,11 @@ fn getName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 {...@@ -245,8 +245,11 @@ fn getName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 {
245 name = try allocator.dupe(u8, n);245 name = try allocator.dupe(u8, n);
246 },246 },
247 .Length => |len| {247 .Length => |len| {
248 name = try allocator.alloc(u8, len);248 var n = try allocator.alloc(u8, len);
249 try reader.readNoEof(name);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.?]);
250 },253 },
251 }254 }
252 return name;255 return name;
src/link/MachO/Zld.zig+102-10
...@@ -77,6 +77,7 @@ lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},...@@ -77,6 +77,7 @@ lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
77tlv_bootstrap: ?Import = null,77tlv_bootstrap: ?Import = null,
78threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{},78threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{},
79local_rebases: std.ArrayListUnmanaged(Pointer) = .{},79local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
80nonlazy_pointers: std.StringArrayHashMapUnmanaged(GotEntry) = .{},
8081
81strtab: std.ArrayListUnmanaged(u8) = .{},82strtab: std.ArrayListUnmanaged(u8) = .{},
8283
...@@ -85,6 +86,16 @@ stub_helper_stubs_start_off: ?u64 = null,...@@ -85,6 +86,16 @@ stub_helper_stubs_start_off: ?u64 = null,
85mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},86mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},
86unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{},87unhandled_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
88const MappingKey = struct {99const MappingKey = struct {
89 object_id: u16,100 object_id: u16,
90 source_sect_id: u16,101 source_sect_id: u16,
...@@ -214,6 +225,10 @@ pub fn deinit(self: *Zld) void {...@@ -214,6 +225,10 @@ pub fn deinit(self: *Zld) void {
214 self.allocator.free(entry.key);225 self.allocator.free(entry.key);
215 }226 }
216 self.nonlazy_imports.deinit(self.allocator);227 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);
217 for (self.exports.items()) |*entry| {232 for (self.exports.items()) |*entry| {
218 self.allocator.free(entry.key);233 self.allocator.free(entry.key);
219 }234 }
...@@ -874,7 +889,10 @@ fn allocateDataConstSegment(self: *Zld) !void {...@@ -874,7 +889,10 @@ fn allocateDataConstSegment(self: *Zld) !void {
874889
875 // Set got size890 // Set got size
876 const got = &seg.sections.items[self.got_section_index.?];891 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
879 try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);897 try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);
880}898}
...@@ -1358,13 +1376,65 @@ fn doRelocs(self: *Zld) !void {...@@ -1358,13 +1376,65 @@ fn doRelocs(self: *Zld) !void {
1358 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);1376 const rel_type = @intToEnum(macho.reloc_type_x86_64, rel.r_type);
13591377
1360 switch (rel_type) {1378 switch (rel_type) {
1361 .X86_64_RELOC_BRANCH,1379 .X86_64_RELOC_BRANCH => {
1362 .X86_64_RELOC_GOT_LOAD,1380 assert(rel.r_length == 2);
1363 .X86_64_RELOC_GOT,1381 const inst = code[off..][0..4];
1364 => {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 => {
1365 assert(rel.r_length == 2);1386 assert(rel.r_length == 2);
1366 const inst = code[off..][0..4];1387 const inst = code[off..][0..4];
1367 const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4));1388 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));
1368 mem.writeIntLittle(u32, inst, displacement);1438 mem.writeIntLittle(u32, inst, displacement);
1369 },1439 },
1370 .X86_64_RELOC_TLV => {1440 .X86_64_RELOC_TLV => {
...@@ -2384,6 +2454,23 @@ fn writeRebaseInfoTable(self: *Zld) !void {...@@ -2384,6 +2454,23 @@ fn writeRebaseInfoTable(self: *Zld) !void {
2384 try pointers.ensureCapacity(pointers.items.len + self.local_rebases.items.len);2454 try pointers.ensureCapacity(pointers.items.len + self.local_rebases.items.len);
2385 pointers.appendSliceAssumeCapacity(self.local_rebases.items);2455 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
2387 if (self.la_symbol_ptr_section_index) |idx| {2474 if (self.la_symbol_ptr_section_index) |idx| {
2388 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len);2475 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len);
2389 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;2476 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
...@@ -2851,8 +2938,9 @@ fn writeDynamicSymbolTable(self: *Zld) !void {...@@ -2851,8 +2938,9 @@ fn writeDynamicSymbolTable(self: *Zld) !void {
28512938
2852 const lazy = self.lazy_imports.items();2939 const lazy = self.lazy_imports.items();
2853 const nonlazy = self.nonlazy_imports.items();2940 const nonlazy = self.nonlazy_imports.items();
2941 const got_locals = self.nonlazy_pointers.items();
2854 dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);2942 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);
2856 const needed_size = dysymtab.nindirectsyms * @sizeOf(u32);2944 const needed_size = dysymtab.nindirectsyms * @sizeOf(u32);
2857 seg.inner.filesize += needed_size;2945 seg.inner.filesize += needed_size;
28582946
...@@ -2867,20 +2955,24 @@ fn writeDynamicSymbolTable(self: *Zld) !void {...@@ -2867,20 +2955,24 @@ fn writeDynamicSymbolTable(self: *Zld) !void {
2867 var writer = stream.writer();2955 var writer = stream.writer();
28682956
2869 stubs.reserved1 = 0;2957 stubs.reserved1 = 0;
2870 for (self.lazy_imports.items()) |_, i| {2958 for (lazy) |_, i| {
2871 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);2959 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2872 try writer.writeIntLittle(u32, symtab_idx);2960 try writer.writeIntLittle(u32, symtab_idx);
2873 }2961 }
28742962
2875 const base_id = @intCast(u32, lazy.len);2963 const base_id = @intCast(u32, lazy.len);
2876 got.reserved1 = base_id;2964 got.reserved1 = base_id;
2877 for (self.nonlazy_imports.items()) |_, i| {2965 for (nonlazy) |_, i| {
2878 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);2966 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);
2879 try writer.writeIntLittle(u32, symtab_idx);2967 try writer.writeIntLittle(u32, symtab_idx);
2880 }2968 }
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);2974 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, nonlazy.len) + @intCast(u32, got_locals.len);
2883 for (self.lazy_imports.items()) |_, i| {2975 for (lazy) |_, i| {
2884 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);2976 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2885 try writer.writeIntLittle(u32, symtab_idx);2977 try writer.writeIntLittle(u32, symtab_idx);
2886 }2978 }