Commit b667fe2c62

Jakub Konka <kubkon@jakubkonka.com>
2021-04-07 13:58:12
zld: resolve stubs and GOT entries
1 parent 46cc214
Changed files (3)
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);
 
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);
     }
 };
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 });
+    }
 }