From b667fe2c62044ec56e05edd74f8ab3f080f813b1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 7 Apr 2021 13:58:12 +0200 Subject: [PATCH] zld: resolve stubs and GOT entries --- src/link/MachO/Object.zig | 2 +- src/link/MachO/Zld.zig | 138 ++++++++++++++++++++++++++++++++++++-- src/link/MachO/reloc.zig | 28 ++++---- 3 files changed, 149 insertions(+), 19 deletions(-) diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index e8c619e86e..e7189d1f1b 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -207,7 +207,7 @@ pub fn parseSections(self: *Object) !void { }; // Parse relocations - var relocs: ?[]*Relocation = if (sect.nreloc > 0) relocs: { + section.relocs = if (sect.nreloc > 0) relocs: { var raw_relocs = try self.allocator.alloc(u8, @sizeOf(macho.relocation_info) * sect.nreloc); defer self.allocator.free(raw_relocs); diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 16f5fbf693..62d88cd489 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -87,8 +87,14 @@ mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{}, unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{}, const GotEntry = struct { + tag: enum { + local, + import, + }, index: u32, target_addr: u64, + file: u16, + local_index: u32, }; const MappingKey = struct { @@ -273,7 +279,8 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { try self.allocateDataSegment(); self.allocateLinkeditSegment(); try self.allocateSymbols(); - self.printSymtab(); + try self.allocateStubsAndGotEntries(); + self.printDebug(); // try self.writeStubHelperCommon(); // try self.resolveRelocsAndWriteSections(); // try self.flush(); @@ -957,7 +964,7 @@ fn allocateSymbols(self: *Zld) !void { const target_addr = target_sect.addr + target_mapping.offset; const n_value = source_sym.inner.n_value - source_sect.addr + target_addr; - log.warn("resolving '{s}' symbol at 0x{x}", .{ entry.key, n_value }); + log.debug("resolving '{s}' symbol at 0x{x}", .{ entry.key, n_value }); // TODO there might be a more generic way of doing this. var n_sect: u8 = 0; @@ -979,6 +986,41 @@ fn allocateSymbols(self: *Zld) !void { } } +fn allocateStubsAndGotEntries(self: *Zld) !void { + for (self.got_entries.items()) |*entry| { + if (entry.value.tag == .import) continue; + + const object = self.objects.items[entry.value.file]; + const sym = object.symtab.items[entry.value.local_index]; + const sym_name = object.getString(sym.inner.n_strx); + assert(mem.eql(u8, sym_name, entry.key)); + + // TODO clean this up + entry.value.target_addr = target_addr: { + if (sym.tag != .Local) { + const glob = self.symtab.get(sym_name) orelse unreachable; + break :target_addr glob.inner.n_value; + } + + const target_mapping = self.mappings.get(.{ + .object_id = entry.value.file, + .source_sect_id = sym.inner.n_sect - 1, + }) orelse unreachable; + const source_sect = object.sections.items[target_mapping.source_sect_id]; + const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment; + const target_sect = target_seg.sections.items[target_mapping.target_sect_id]; + const target_sect_addr = target_sect.addr + target_mapping.offset; + + break :target_addr target_sect_addr + sym.inner.n_value - source_sect.inner.addr; + }; + + log.warn("resolving GOT entry '{s}' at 0x{x}", .{ + entry.key, + entry.value.target_addr, + }); + } +} + fn writeStubHelperCommon(self: *Zld) !void { const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?]; @@ -1385,9 +1427,85 @@ fn resolveSymbols(self: *Zld) !void { if (has_unresolved) { return error.UndefinedSymbolReference; } + + // Finally put dyld_stub_binder as an Import + var name = try self.allocator.dupe(u8, "dyld_stub_binder"); + try self.symtab.putNoClobber(self.allocator, name, .{ + .tag = .Import, + .inner = .{ + .n_strx = 0, // This will be populated once we write the string table. + .n_type = macho.N_UNDF | macho.N_EXT, + .n_sect = 0, + .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER, + .n_value = 0, + }, + .file = 0, + }); } -fn resolveStubsAndGotEntries(self: *Zld) !void {} +fn resolveStubsAndGotEntries(self: *Zld) !void { + for (self.objects.items) |object, object_id| { + log.debug("\nresolving stubs and got entries from {s}", .{object.name}); + + for (object.sections.items) |sect| { + const relocs = sect.relocs orelse continue; + for (relocs) |reloc| { + switch (reloc.@"type") { + .unsigned => continue, + .got_page, .got_page_off => { + const sym = object.symtab.items[reloc.target.symbol]; + const sym_name = object.getString(sym.inner.n_strx); + + if (self.got_entries.contains(sym_name)) continue; + + const is_import = self.symtab.get(sym_name).?.tag == .Import; + var name = try self.allocator.dupe(u8, sym_name); + const index = @intCast(u32, self.got_entries.items().len); + try self.got_entries.putNoClobber(self.allocator, name, .{ + .tag = if (is_import) .import else .local, + .index = index, + .target_addr = 0, + .file = if (is_import) 0 else @intCast(u16, object_id), + .local_index = if (is_import) 0 else reloc.target.symbol, + }); + + log.debug(" | found GOT entry {s}: {}", .{ sym_name, self.got_entries.get(sym_name) }); + }, + else => { + const sym = object.symtab.items[reloc.target.symbol]; + const sym_name = object.getString(sym.inner.n_strx); + + if (sym.tag != .Undef) continue; + + const in_globals = self.symtab.get(sym_name) orelse unreachable; + + if (in_globals.tag != .Import) continue; + if (self.stubs.contains(sym_name)) continue; + + var name = try self.allocator.dupe(u8, sym_name); + const index = @intCast(u32, self.stubs.items().len); + try self.stubs.putNoClobber(self.allocator, name, index); + + log.debug(" | found stub {s}: {}", .{ sym_name, self.stubs.get(sym_name) }); + }, + } + } + } + } + + // Finally, put dyld_stub_binder as the final GOT entry + var name = try self.allocator.dupe(u8, "dyld_stub_binder"); + const index = @intCast(u32, self.got_entries.items().len); + try self.got_entries.putNoClobber(self.allocator, name, .{ + .tag = .import, + .index = index, + .target_addr = 0, + .file = 0, + .local_index = 0, + }); + + log.debug(" | found GOT entry dyld_stub_binder: {}", .{self.got_entries.get("dyld_stub_binder")}); +} fn resolveRelocsAndWriteSections(self: *Zld) !void { for (self.objects.items) |object, object_id| { @@ -2871,9 +2989,21 @@ fn aarch64IsArithmetic(inst: *const [4]u8) callconv(.Inline) bool { return ((group_decode >> 2) == 4); } -fn printSymtab(self: Zld) void { +fn printDebug(self: Zld) void { log.warn("symtab", .{}); for (self.symtab.items()) |entry| { log.warn(" | {s} => {any}", .{ entry.key, entry.value }); } + + log.warn("\n", .{}); + log.warn("GOT entries", .{}); + for (self.got_entries.items()) |entry| { + log.warn(" | {s} => {any}", .{ entry.key, entry.value }); + } + + log.warn("\n", .{}); + log.warn("stubs", .{}); + for (self.stubs.items()) |entry| { + log.warn(" | {s} => {any}", .{ entry.key, entry.value }); + } } diff --git a/src/link/MachO/reloc.zig b/src/link/MachO/reloc.zig index 8dadcb2b1b..187cdfa4ce 100644 --- a/src/link/MachO/reloc.zig +++ b/src/link/MachO/reloc.zig @@ -292,12 +292,12 @@ const RelocIterator = struct { self.index += 1; if (self.index < self.buffer.len) { const reloc = self.buffer[@intCast(u64, self.index)]; - log.warn("{s}", .{@intToEnum(macho.reloc_type_arm64, reloc.r_type)}); - log.warn(" | offset = {}", .{reloc.r_address}); - log.warn(" | PC = {}", .{reloc.r_pcrel == 1}); - log.warn(" | length = {}", .{reloc.r_length}); - log.warn(" | symbolnum = {}", .{reloc.r_symbolnum}); - log.warn(" | extern = {}", .{reloc.r_extern == 1}); + log.debug("{s}", .{@intToEnum(macho.reloc_type_arm64, reloc.r_type)}); + log.debug(" | offset = {}", .{reloc.r_address}); + log.debug(" | PC = {}", .{reloc.r_pcrel == 1}); + log.debug(" | length = {}", .{reloc.r_length}); + log.debug(" | symbolnum = {}", .{reloc.r_symbolnum}); + log.debug(" | extern = {}", .{reloc.r_extern == 1}); return reloc; } return null; @@ -419,7 +419,7 @@ const Parser = struct { .inst = parsed_inst, }; - log.warn(" | emitting {}", .{branch}); + log.debug(" | emitting {}", .{branch}); try parser.parsed.append(&branch.base); } @@ -458,7 +458,7 @@ const Parser = struct { .inst = parsed_inst, }; - log.warn(" | emitting {}", .{page}); + log.debug(" | emitting {}", .{page}); break :ptr &page.base; }, @@ -476,7 +476,7 @@ const Parser = struct { .inst = parsed_inst, }; - log.warn(" | emitting {}", .{page}); + log.debug(" | emitting {}", .{page}); break :ptr &page.base; }, @@ -494,7 +494,7 @@ const Parser = struct { .inst = parsed_inst, }; - log.warn(" | emitting {}", .{page}); + log.debug(" | emitting {}", .{page}); break :ptr &page.base; }, @@ -551,7 +551,7 @@ const Parser = struct { .addend = parser.addend, }; - log.warn(" | emitting {}", .{page_off}); + log.debug(" | emitting {}", .{page_off}); try parser.parsed.append(&page_off.base); } @@ -588,7 +588,7 @@ const Parser = struct { }, }; - log.warn(" | emitting {}", .{page_off}); + log.debug(" | emitting {}", .{page_off}); try parser.parsed.append(&page_off.base); } @@ -655,7 +655,7 @@ const Parser = struct { }, }; - log.warn(" | emitting {}", .{page_off}); + log.debug(" | emitting {}", .{page_off}); try parser.parsed.append(&page_off.base); } @@ -716,7 +716,7 @@ const Parser = struct { .addend = addend, }; - log.warn(" | emitting {}", .{unsigned}); + log.debug(" | emitting {}", .{unsigned}); try parser.parsed.append(&unsigned.base); } };