Commit 4ec6d174ad

Jakub Konka <kubkon@jakubkonka.com>
2021-04-23 10:33:32
zld: new approach at handling static inits
1 parent b00d08b
Changed files (2)
src
src/link/MachO/Object.zig
@@ -14,7 +14,6 @@ const Allocator = mem.Allocator;
 const Relocation = reloc.Relocation;
 const Symbol = @import("Symbol.zig");
 const parseName = @import("Zld.zig").parseName;
-const CppStatic = @import("Zld.zig").CppStatic;
 
 usingnamespace @import("commands.zig");
 
@@ -33,7 +32,9 @@ symtab_cmd_index: ?u16 = null,
 dysymtab_cmd_index: ?u16 = null,
 build_version_cmd_index: ?u16 = null,
 data_in_code_cmd_index: ?u16 = null,
+
 text_section_index: ?u16 = null,
+mod_init_func_section_index: ?u16 = null,
 
 // __DWARF segment sections
 dwarf_debug_info_index: ?u16 = null,
@@ -50,6 +51,7 @@ stabs: std.ArrayListUnmanaged(Stab) = .{},
 tu_path: ?[]const u8 = null,
 tu_mtime: ?u64 = null,
 
+initializers: std.ArrayListUnmanaged(CppStatic) = .{},
 data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
 
 pub const Section = struct {
@@ -69,6 +71,11 @@ pub const Section = struct {
     }
 };
 
+const CppStatic = struct {
+    symbol: u32,
+    target_addr: u64,
+};
+
 const Stab = struct {
     tag: Tag,
     symbol: u32,
@@ -171,6 +178,7 @@ pub fn deinit(self: *Object) void {
     self.strtab.deinit(self.allocator);
     self.stabs.deinit(self.allocator);
     self.data_in_code_entries.deinit(self.allocator);
+    self.initializers.deinit(self.allocator);
 
     if (self.name) |n| {
         self.allocator.free(n);
@@ -252,6 +260,10 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void {
                         if (mem.eql(u8, sectname, "__text")) {
                             self.text_section_index = index;
                         }
+                    } else if (mem.eql(u8, segname, "__DATA")) {
+                        if (mem.eql(u8, sectname, "__mod_init_func")) {
+                            self.mod_init_func_section_index = index;
+                        }
                     }
 
                     sect.offset += offset;
@@ -323,16 +335,27 @@ pub fn parseSections(self: *Object) !void {
 }
 
 pub fn parseInitializers(self: *Object) !void {
-    for (self.sections.items) |section| {
-        if (section.inner.flags != macho.S_MOD_INIT_FUNC_POINTERS) continue;
-        log.warn("parsing initializers in {s}", .{self.name.?});
-        // Parse C++ initializers
-        const relocs = section.relocs orelse unreachable;
-        for (relocs) |rel| {
-            const sym = self.symtab.items[rel.target.symbol];
-            const sym_name = self.getString(sym.n_strx);
-            log.warn("    | {s}", .{sym_name});
-        }
+    const index = self.mod_init_func_section_index orelse return;
+    const section = self.sections.items[index];
+
+    log.debug("parsing initializers in {s}", .{self.name.?});
+
+    // Parse C++ initializers
+    const relocs = section.relocs orelse unreachable;
+    try self.initializers.ensureCapacity(self.allocator, relocs.len);
+    for (relocs) |rel| {
+        self.initializers.appendAssumeCapacity(.{
+            .symbol = rel.target.symbol,
+            .target_addr = undefined,
+        });
+    }
+
+    mem.reverse(CppStatic, self.initializers.items);
+
+    for (self.initializers.items) |initializer| {
+        const sym = self.symtab.items[initializer.symbol];
+        const sym_name = self.getString(sym.n_strx);
+        log.debug("    | {s}", .{sym_name});
     }
 }
 
src/link/MachO/Zld.zig
@@ -82,20 +82,12 @@ 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) = .{},
 
-pub const CppStatic = struct {
-    index: u32,
-    target_addr: u64,
-    file: u16,
-};
-
 const GotEntry = struct {
     tag: enum {
         local,
@@ -143,16 +135,6 @@ 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);
     }
@@ -243,6 +225,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {
     self.allocateLinkeditSegment();
     try self.allocateSymbols();
     try self.allocateStubsAndGotEntries();
+    try self.allocateCppStatics();
     try self.writeStubHelperCommon();
     try self.resolveRelocsAndWriteSections();
     try self.flush();
@@ -1007,37 +990,20 @@ 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;
-        };
+fn allocateCppStatics(self: *Zld) !void {
+    for (self.objects.items) |*object| {
+        for (object.initializers.items) |*initializer| {
+            const sym = object.symtab.items[initializer.symbol];
+            const sym_name = object.getString(sym.n_strx);
+            initializer.target_addr = object.locals.get(sym_name).?.address;
 
-        log.debug("resolving C++ finalizer '{s}' at 0x{x}", .{
-            entry.key,
-            entry.value.target_addr,
-        });
+            log.debug("resolving C++ initializer '{s}' at 0x{x}", .{
+                sym_name,
+                initializer.target_addr,
+            });
+        }
     }
 }
 
@@ -1453,34 +1419,7 @@ fn resolveStubsAndGotEntries(self: *Zld) !void {
             const relocs = sect.relocs orelse continue;
             for (relocs) |rel| {
                 switch (rel.@"type") {
-                    .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;
-                    },
+                    .unsigned => 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);
@@ -2194,36 +2133,18 @@ fn flush(self: *Zld) !void {
         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();
+        var initializers = std.ArrayList(u64).init(self.allocator);
+        defer initializers.deinit();
 
-        for (self.cpp_finalizers.items()) |entry| {
-            try writer.writeIntLittle(u64, entry.value.target_addr);
+        // TODO sort the initializers globally
+        for (self.objects.items) |object| {
+            for (object.initializers.items) |initializer| {
+                try initializers.append(initializer.target_addr);
+            }
         }
 
-        _ = try self.file.?.pwriteAll(buffer, sect.offset);
-        sect.size = @intCast(u32, buffer.len);
+        _ = try self.file.?.pwriteAll(mem.sliceAsBytes(initializers.items), sect.offset);
+        sect.size = @intCast(u32, initializers.items.len * @sizeOf(u64));
     }
 
     try self.writeGotEntries();
@@ -2328,26 +2249,15 @@ fn writeRebaseInfoTable(self: *Zld) !void {
         const base_offset = sect.addr - seg.inner.vmaddr;
         const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
 
-        for (self.cpp_initializers.items()) |entry| {
-            try pointers.append(.{
-                .offset = base_offset + entry.value.index * @sizeOf(u64),
-                .segment_id = segment_id,
-            });
-        }
-    }
-
-    if (self.mod_term_func_section_index) |idx| {
-        // 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 base_offset = sect.addr - seg.inner.vmaddr;
-        const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
-
-        for (self.cpp_finalizers.items()) |entry| {
-            try pointers.append(.{
-                .offset = base_offset + entry.value.index * @sizeOf(u64),
-                .segment_id = segment_id,
-            });
+        var index: u64 = 0;
+        for (self.objects.items) |object| {
+            for (object.initializers.items) |_| {
+                try pointers.append(.{
+                    .offset = base_offset + index * @sizeOf(u64),
+                    .segment_id = segment_id,
+                });
+                index += 1;
+            }
         }
     }