Commit 8943a0aaaa

Jakub Konka <kubkon@jakubkonka.com>
2021-04-14 15:19:42
zld: dedup initializers and finalizers
1 parent 3e73a3c
Changed files (2)
src
link
MachO
src/link/MachO/reloc/aarch64.zig
@@ -226,7 +226,9 @@ pub const Parser = struct {
                     try parser.parseTlvpLoadPageOff(rel);
                 },
                 .ARM64_RELOC_POINTER_TO_GOT => {
-                    return error.ToDoRelocPointerToGot;
+                    // TODO Handle pointer to GOT. This reloc seems to appear in
+                    // __LD,__compact_unwind section which we currently don't handle.
+                    log.debug("Unhandled relocation ARM64_RELOC_POINTER_TO_GOT", .{});
                 },
             }
         }
src/link/MachO/Zld.zig
@@ -81,12 +81,20 @@ threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{},
 local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
 stubs: std.StringArrayHashMapUnmanaged(u32) = .{},
 got_entries: std.StringArrayHashMapUnmanaged(GotEntry) = .{},
+cpp_initializers: std.StringArrayHashMapUnmanaged(CppStatic) = .{},
+cpp_finalizers: std.StringArrayHashMapUnmanaged(CppStatic) = .{},
 
 stub_helper_stubs_start_off: ?u64 = null,
 
 mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},
 unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{},
 
+const CppStatic = struct {
+    index: u32,
+    target_addr: u64,
+    file: u16,
+};
+
 const GotEntry = struct {
     tag: enum {
         local,
@@ -134,6 +142,16 @@ pub fn deinit(self: *Zld) void {
     }
     self.got_entries.deinit(self.allocator);
 
+    for (self.cpp_initializers.items()) |entry| {
+        self.allocator.free(entry.key);
+    }
+    self.cpp_initializers.deinit(self.allocator);
+
+    for (self.cpp_finalizers.items()) |entry| {
+        self.allocator.free(entry.key);
+    }
+    self.cpp_finalizers.deinit(self.allocator);
+
     for (self.load_commands.items) |*lc| {
         lc.deinit(self.allocator);
     }
@@ -957,6 +975,38 @@ fn allocateStubsAndGotEntries(self: *Zld) !void {
             entry.value.target_addr,
         });
     }
+
+    for (self.cpp_initializers.items()) |*entry| {
+        const object = self.objects.items[entry.value.file];
+        entry.value.target_addr = target_addr: {
+            if (object.locals.get(entry.key)) |local| {
+                break :target_addr local.address;
+            }
+            const global = self.symtab.get(entry.key) orelse unreachable;
+            break :target_addr global.address;
+        };
+
+        log.debug("resolving C++ initializer '{s}' at 0x{x}", .{
+            entry.key,
+            entry.value.target_addr,
+        });
+    }
+
+    for (self.cpp_finalizers.items()) |*entry| {
+        const object = self.objects.items[entry.value.file];
+        entry.value.target_addr = target_addr: {
+            if (object.locals.get(entry.key)) |local| {
+                break :target_addr local.address;
+            }
+            const global = self.symtab.get(entry.key) orelse unreachable;
+            break :target_addr global.address;
+        };
+
+        log.debug("resolving C++ finalizer '{s}' at 0x{x}", .{
+            entry.key,
+            entry.value.target_addr,
+        });
+    }
 }
 
 fn writeStubHelperCommon(self: *Zld) !void {
@@ -1257,8 +1307,7 @@ fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void {
                 },
                 .strong => {
                     if (!is_weak) {
-                        log.err("symbol '{s}' defined multiple times", .{sym_name});
-                        return error.MultipleSymbolDefinitions;
+                        log.debug("strong symbol '{s}' defined multiple times", .{sym_name});
                     }
                     continue;
                 },
@@ -1348,14 +1397,14 @@ fn resolveSymbols(self: *Zld) !void {
     });
 
     {
-        log.warn("symtab", .{});
+        log.debug("symtab", .{});
         for (self.symtab.items()) |sym| {
             switch (sym.value.tag) {
                 .weak, .strong => {
-                    log.warn("    | {s} => {s}", .{ sym.key, self.objects.items[sym.value.file.?].name.? });
+                    log.debug("    | {s} => {s}", .{ sym.key, self.objects.items[sym.value.file.?].name.? });
                 },
                 .import => {
-                    log.warn("    | {s} => libSystem.B.dylib", .{sym.key});
+                    log.debug("    | {s} => libSystem.B.dylib", .{sym.key});
                 },
                 else => unreachable,
             }
@@ -1371,7 +1420,34 @@ fn resolveStubsAndGotEntries(self: *Zld) !void {
             const relocs = sect.relocs orelse continue;
             for (relocs) |rel| {
                 switch (rel.@"type") {
-                    .unsigned => continue,
+                    .unsigned => {
+                        if (rel.target != .symbol) continue;
+
+                        const sym = object.symtab.items[rel.target.symbol];
+                        const sym_name = object.getString(sym.n_strx);
+
+                        if (sect.inner.flags == macho.S_MOD_INIT_FUNC_POINTERS) {
+                            if (self.cpp_initializers.contains(sym_name)) continue;
+
+                            var name = try self.allocator.dupe(u8, sym_name);
+                            const index = @intCast(u32, self.cpp_initializers.items().len);
+                            try self.cpp_initializers.putNoClobber(self.allocator, name, .{
+                                .index = index,
+                                .target_addr = 0,
+                                .file = @intCast(u16, object_id),
+                            });
+                        } else if (sect.inner.flags == macho.S_MOD_TERM_FUNC_POINTERS) {
+                            if (self.cpp_finalizers.contains(sym_name)) continue;
+
+                            var name = try self.allocator.dupe(u8, sym_name);
+                            const index = @intCast(u32, self.cpp_finalizers.items().len);
+                            try self.cpp_finalizers.putNoClobber(self.allocator, name, .{
+                                .index = index,
+                                .target_addr = 0,
+                                .file = @intCast(u16, object_id),
+                            });
+                        } else continue;
+                    },
                     .got_page, .got_page_off, .got_load, .got => {
                         const sym = object.symtab.items[rel.target.symbol];
                         const sym_name = object.getString(sym.n_strx);
@@ -1433,9 +1509,14 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
         log.debug("relocating object {s}", .{object.name});
 
         for (object.sections.items) |sect, source_sect_id| {
+            if (sect.inner.flags == macho.S_MOD_INIT_FUNC_POINTERS or
+                sect.inner.flags == macho.S_MOD_TERM_FUNC_POINTERS) continue;
+
             const segname = parseName(&sect.inner.segname);
             const sectname = parseName(&sect.inner.sectname);
 
+            log.debug("relocating section '{s},{s}'", .{ segname, sectname });
+
             // Get mapping
             const target_mapping = self.mappings.get(.{
                 .object_id = @intCast(u16, object_id),
@@ -2061,6 +2142,42 @@ fn flush(self: *Zld) !void {
         try self.file.?.pwriteAll(buffer, sect.offset);
     }
 
+    if (self.mod_init_func_section_index) |index| {
+        const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
+        const sect = &seg.sections.items[index];
+
+        var buffer = try self.allocator.alloc(u8, self.cpp_initializers.items().len * @sizeOf(u64));
+        defer self.allocator.free(buffer);
+        
+        var stream = std.io.fixedBufferStream(buffer);
+        var writer = stream.writer();
+
+        for (self.cpp_initializers.items()) |entry| {
+            try writer.writeIntLittle(u64, entry.value.target_addr);
+        }
+
+        _ = try self.file.?.pwriteAll(buffer, sect.offset);
+        sect.size = @intCast(u32, buffer.len);
+    }
+
+    if (self.mod_term_func_section_index) |index| {
+        const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
+        const sect = &seg.sections.items[index];
+
+        var buffer = try self.allocator.alloc(u8, self.cpp_finalizers.items().len * @sizeOf(u64));
+        defer self.allocator.free(buffer);
+        
+        var stream = std.io.fixedBufferStream(buffer);
+        var writer = stream.writer();
+
+        for (self.cpp_finalizers.items()) |entry| {
+            try writer.writeIntLittle(u64, entry.value.target_addr);
+        }
+
+        _ = try self.file.?.pwriteAll(buffer, sect.offset);
+        sect.size = @intCast(u32, buffer.len);
+    }
+
     try self.writeGotEntries();
     try self.setEntryPoint();
     try self.writeRebaseInfoTable();
@@ -2160,15 +2277,12 @@ fn writeRebaseInfoTable(self: *Zld) !void {
         // TODO audit and investigate this.
         const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
         const sect = seg.sections.items[idx];
-        const npointers = sect.size * @sizeOf(u64);
         const base_offset = sect.addr - seg.inner.vmaddr;
         const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
 
-        try pointers.ensureCapacity(pointers.items.len + npointers);
-        var i: usize = 0;
-        while (i < npointers) : (i += 1) {
-            pointers.appendAssumeCapacity(.{
-                .offset = base_offset + i * @sizeOf(u64),
+        for (self.cpp_initializers.items()) |entry| {
+            try pointers.append(.{
+                .offset = base_offset + entry.value.index * @sizeOf(u64),
                 .segment_id = segment_id,
             });
         }
@@ -2178,15 +2292,12 @@ fn writeRebaseInfoTable(self: *Zld) !void {
         // TODO audit and investigate this.
         const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
         const sect = seg.sections.items[idx];
-        const npointers = sect.size * @sizeOf(u64);
         const base_offset = sect.addr - seg.inner.vmaddr;
         const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
 
-        try pointers.ensureCapacity(pointers.items.len + npointers);
-        var i: usize = 0;
-        while (i < npointers) : (i += 1) {
-            pointers.appendAssumeCapacity(.{
-                .offset = base_offset + i * @sizeOf(u64),
+        for (self.cpp_finalizers.items()) |entry| {
+            try pointers.append(.{
+                .offset = base_offset + entry.value.index * @sizeOf(u64),
                 .segment_id = segment_id,
             });
         }