Commit ea95c74948

Jakub Konka <kubkon@jakubkonka.com>
2023-10-30 20:36:34
elf: encapsulate ZigObject-specific logic in flushModule hook
1 parent b1a735a
Changed files (3)
src/link/Elf/Atom.zig
@@ -166,15 +166,16 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
         try elf_file.growAllocSection(self.outputShndx().?, needed_size);
         last_atom_index.* = self.atom_index;
 
-        if (elf_file.zigObjectPtr().?.dwarf) |_| {
+        const zig_object = elf_file.zigObjectPtr().?;
+        if (zig_object.dwarf) |_| {
             // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
             // range of the compilation unit. When we expand the text section, this range changes,
             // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty.
-            elf_file.debug_info_header_dirty = true;
+            zig_object.debug_info_header_dirty = true;
             // This becomes dirty for the same reason. We could potentially make this more
             // fine-grained with the addition of support for more compilation units. It is planned to
             // model each package as a different compilation unit.
-            elf_file.debug_aranges_section_dirty = true;
+            zig_object.debug_aranges_section_dirty = true;
         }
     }
     shdr.sh_addralign = @max(shdr.sh_addralign, self.alignment.toByteUnitsOptional().?);
src/link/Elf/ZigObject.zig
@@ -52,6 +52,20 @@ unnamed_consts: UnnamedConstTable = .{},
 /// Table of tracked AnonDecls.
 anon_decls: AnonDeclTable = .{},
 
+debug_strtab_dirty: bool = false,
+debug_abbrev_section_dirty: bool = false,
+debug_aranges_section_dirty: bool = false,
+debug_info_header_dirty: bool = false,
+debug_line_header_dirty: bool = false,
+
+/// Size contribution of Zig's metadata to each debug section.
+/// Used to track start of metadata from input object files.
+debug_info_section_zig_size: u64 = 0,
+debug_abbrev_section_zig_size: u64 = 0,
+debug_str_section_zig_size: u64 = 0,
+debug_aranges_section_zig_size: u64 = 0,
+debug_line_section_zig_size: u64 = 0,
+
 pub const global_symbol_bit: u32 = 0x80000000;
 pub const symbol_mask: u32 = 0x7fffffff;
 pub const SHN_ATOM: u16 = 0x100;
@@ -123,6 +137,103 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
     }
 }
 
+pub fn flushModule(self: *ZigObject, elf_file: *Elf) !void {
+    // Handle any lazy symbols that were emitted by incremental compilation.
+    if (self.lazy_syms.getPtr(.none)) |metadata| {
+        const module = elf_file.base.options.module.?;
+
+        // Most lazy symbols can be updated on first use, but
+        // anyerror needs to wait for everything to be flushed.
+        if (metadata.text_state != .unused) self.updateLazySymbol(
+            elf_file,
+            link.File.LazySymbol.initDecl(.code, null, module),
+            metadata.text_symbol_index,
+        ) catch |err| return switch (err) {
+            error.CodegenFail => error.FlushFailure,
+            else => |e| e,
+        };
+        if (metadata.rodata_state != .unused) self.updateLazySymbol(
+            elf_file,
+            link.File.LazySymbol.initDecl(.const_data, null, module),
+            metadata.rodata_symbol_index,
+        ) catch |err| return switch (err) {
+            error.CodegenFail => error.FlushFailure,
+            else => |e| e,
+        };
+    }
+    for (self.lazy_syms.values()) |*metadata| {
+        if (metadata.text_state != .unused) metadata.text_state = .flushed;
+        if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed;
+    }
+
+    if (self.dwarf) |*dw| {
+        try dw.flushModule(elf_file.base.options.module.?);
+
+        // TODO I need to re-think how to handle ZigObject's debug sections AND debug sections
+        // extracted from input object files correctly.
+        if (self.debug_abbrev_section_dirty) {
+            try dw.writeDbgAbbrev();
+            self.debug_abbrev_section_dirty = false;
+        }
+
+        if (self.debug_info_header_dirty) {
+            const text_phdr = &elf_file.phdrs.items[elf_file.phdr_zig_load_re_index.?];
+            const low_pc = text_phdr.p_vaddr;
+            const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
+            try dw.writeDbgInfoHeader(elf_file.base.options.module.?, low_pc, high_pc);
+            self.debug_info_header_dirty = false;
+        }
+
+        if (self.debug_aranges_section_dirty) {
+            const text_phdr = &elf_file.phdrs.items[elf_file.phdr_zig_load_re_index.?];
+            try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz);
+            self.debug_aranges_section_dirty = false;
+        }
+
+        if (self.debug_line_header_dirty) {
+            try dw.writeDbgLineHeader();
+            self.debug_line_header_dirty = false;
+        }
+
+        if (elf_file.debug_str_section_index) |shndx| {
+            if (self.debug_strtab_dirty or dw.strtab.buffer.items.len != elf_file.shdrs.items[shndx].sh_size) {
+                try elf_file.growNonAllocSection(shndx, dw.strtab.buffer.items.len, 1, false);
+                const shdr = elf_file.shdrs.items[shndx];
+                try elf_file.base.file.?.pwriteAll(dw.strtab.buffer.items, shdr.sh_offset);
+                self.debug_strtab_dirty = false;
+            }
+        }
+
+        self.saveDebugSectionsSizes(elf_file);
+    }
+
+    // The point of flushModule() is to commit changes, so in theory, nothing should
+    // be dirty after this. However, it is possible for some things to remain
+    // dirty because they fail to be written in the event of compile errors,
+    // such as debug_line_header_dirty and debug_info_header_dirty.
+    assert(!self.debug_abbrev_section_dirty);
+    assert(!self.debug_aranges_section_dirty);
+    assert(!self.debug_strtab_dirty);
+}
+
+fn saveDebugSectionsSizes(self: *ZigObject, elf_file: *Elf) void {
+    if (elf_file.debug_info_section_index) |shndx| {
+        self.debug_info_section_zig_size = elf_file.shdrs.items[shndx].sh_size;
+    }
+    if (elf_file.debug_abbrev_section_index) |shndx| {
+        self.debug_abbrev_section_zig_size = elf_file.shdrs.items[shndx].sh_size;
+    }
+    if (elf_file.debug_str_section_index) |shndx| {
+        self.debug_str_section_zig_size = elf_file.shdrs.items[shndx].sh_size;
+    }
+    if (elf_file.debug_aranges_section_index) |shndx| {
+        self.debug_aranges_section_zig_size = elf_file.shdrs.items[shndx].sh_size;
+    }
+    if (elf_file.debug_line_section_index) |shndx| {
+        self.debug_line_section_zig_size = elf_file.shdrs.items[shndx].sh_size;
+    }
+}
+
 pub fn addLocalEsym(self: *ZigObject, allocator: Allocator) !Symbol.Index {
     try self.local_esyms.ensureUnusedCapacity(allocator, 1);
     const index = @as(Symbol.Index, @intCast(self.local_esyms.addOneAssumeCapacity()));
@@ -837,7 +948,7 @@ pub fn updateDecl(
     return self.updateExports(elf_file, mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
 }
 
-pub fn updateLazySymbol(
+fn updateLazySymbol(
     self: *ZigObject,
     elf_file: *Elf,
     sym: link.File.LazySymbol,
src/link/Elf.zig
@@ -113,14 +113,6 @@ debug_str_section_index: ?u16 = null,
 debug_aranges_section_index: ?u16 = null,
 debug_line_section_index: ?u16 = null,
 
-/// Size contribution of Zig's metadata to each debug section.
-/// Used to track start of metadata from input object files.
-debug_info_section_zig_size: u64 = 0,
-debug_abbrev_section_zig_size: u64 = 0,
-debug_str_section_zig_size: u64 = 0,
-debug_aranges_section_zig_size: u64 = 0,
-debug_line_section_zig_size: u64 = 0,
-
 copy_rel_section_index: ?u16 = null,
 dynamic_section_index: ?u16 = null,
 dynstrtab_section_index: ?u16 = null,
@@ -170,12 +162,6 @@ symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
 has_text_reloc: bool = false,
 num_ifunc_dynrelocs: usize = 0,
 
-debug_strtab_dirty: bool = false,
-debug_abbrev_section_dirty: bool = false,
-debug_aranges_section_dirty: bool = false,
-debug_info_header_dirty: bool = false,
-debug_line_header_dirty: bool = false,
-
 error_flags: link.File.ErrorFlags = link.File.ErrorFlags{},
 misc_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{},
 
@@ -696,7 +682,8 @@ pub fn initMetadata(self: *Elf) !void {
         try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_bss_section_index.?, .{});
     }
 
-    if (self.zigObjectPtr().?.dwarf) |*dw| {
+    const zig_object = self.zigObjectPtr().?;
+    if (zig_object.dwarf) |*dw| {
         if (self.debug_str_section_index == null) {
             assert(dw.strtab.buffer.items.len == 0);
             try dw.strtab.buffer.append(gpa, 0);
@@ -706,7 +693,7 @@ pub fn initMetadata(self: *Elf) !void {
                 .flags = elf.SHF_MERGE | elf.SHF_STRINGS,
                 .entsize = 1,
             });
-            self.debug_strtab_dirty = true;
+            zig_object.debug_strtab_dirty = true;
         }
 
         if (self.debug_info_section_index == null) {
@@ -715,7 +702,7 @@ pub fn initMetadata(self: *Elf) !void {
                 .size = 200,
                 .alignment = 1,
             });
-            self.debug_info_header_dirty = true;
+            zig_object.debug_info_header_dirty = true;
         }
 
         if (self.debug_abbrev_section_index == null) {
@@ -724,7 +711,7 @@ pub fn initMetadata(self: *Elf) !void {
                 .size = 128,
                 .alignment = 1,
             });
-            self.debug_abbrev_section_dirty = true;
+            zig_object.debug_abbrev_section_dirty = true;
         }
 
         if (self.debug_aranges_section_index == null) {
@@ -733,7 +720,7 @@ pub fn initMetadata(self: *Elf) !void {
                 .size = 160,
                 .alignment = 16,
             });
-            self.debug_aranges_section_dirty = true;
+            zig_object.debug_aranges_section_dirty = true;
         }
 
         if (self.debug_line_section_index == null) {
@@ -742,7 +729,7 @@ pub fn initMetadata(self: *Elf) !void {
                 .size = 250,
                 .alignment = 1,
             });
-            self.debug_line_header_dirty = true;
+            zig_object.debug_line_header_dirty = true;
         }
     }
 }
@@ -833,17 +820,18 @@ pub fn growNonAllocSection(
 }
 
 pub fn markDirty(self: *Elf, shdr_index: u16) void {
-    if (self.zigObjectPtr().?.dwarf) |_| {
+    const zig_object = self.zigObjectPtr().?;
+    if (zig_object.dwarf) |_| {
         if (self.debug_info_section_index.? == shdr_index) {
-            self.debug_info_header_dirty = true;
+            zig_object.debug_info_header_dirty = true;
         } else if (self.debug_line_section_index.? == shdr_index) {
-            self.debug_line_header_dirty = true;
+            zig_object.debug_line_header_dirty = true;
         } else if (self.debug_abbrev_section_index.? == shdr_index) {
-            self.debug_abbrev_section_dirty = true;
+            zig_object.debug_abbrev_section_dirty = true;
         } else if (self.debug_str_section_index.? == shdr_index) {
-            self.debug_strtab_dirty = true;
+            zig_object.debug_strtab_dirty = true;
         } else if (self.debug_aranges_section_index.? == shdr_index) {
-            self.debug_aranges_section_dirty = true;
+            zig_object.debug_aranges_section_dirty = true;
         }
     }
 }
@@ -1343,39 +1331,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
             try self.handleAndReportParseError(obj.path, err, &parse_ctx);
     }
 
-    if (self.zigObjectPtr()) |zig_object| {
-        // Handle any lazy symbols that were emitted by incremental compilation.
-        if (zig_object.lazy_syms.getPtr(.none)) |metadata| {
-            const module = self.base.options.module.?;
-
-            // Most lazy symbols can be updated on first use, but
-            // anyerror needs to wait for everything to be flushed.
-            if (metadata.text_state != .unused) zig_object.updateLazySymbol(
-                self,
-                link.File.LazySymbol.initDecl(.code, null, module),
-                metadata.text_symbol_index,
-            ) catch |err| return switch (err) {
-                error.CodegenFail => error.FlushFailure,
-                else => |e| e,
-            };
-            if (metadata.rodata_state != .unused) zig_object.updateLazySymbol(
-                self,
-                link.File.LazySymbol.initDecl(.const_data, null, module),
-                metadata.rodata_symbol_index,
-            ) catch |err| return switch (err) {
-                error.CodegenFail => error.FlushFailure,
-                else => |e| e,
-            };
-        }
-        for (zig_object.lazy_syms.values()) |*metadata| {
-            if (metadata.text_state != .unused) metadata.text_state = .flushed;
-            if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed;
-        }
-
-        if (zig_object.dwarf) |*dw| {
-            try dw.flushModule(self.base.options.module.?);
-        }
-    }
+    if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
 
     // Dedup shared objects
     {
@@ -1437,47 +1393,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
     // Scan and create missing synthetic entries such as GOT indirection.
     try self.scanRelocs();
 
-    if (self.zigObjectPtr()) |zig_object| {
-        // TODO I need to re-think how to handle ZigObject's debug sections AND debug sections
-        // extracted from input object files correctly.
-        if (zig_object.dwarf) |*dw| {
-            if (self.debug_abbrev_section_dirty) {
-                try dw.writeDbgAbbrev();
-                self.debug_abbrev_section_dirty = false;
-            }
-
-            if (self.debug_info_header_dirty) {
-                const text_phdr = &self.phdrs.items[self.phdr_zig_load_re_index.?];
-                const low_pc = text_phdr.p_vaddr;
-                const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
-                try dw.writeDbgInfoHeader(self.base.options.module.?, low_pc, high_pc);
-                self.debug_info_header_dirty = false;
-            }
-
-            if (self.debug_aranges_section_dirty) {
-                const text_phdr = &self.phdrs.items[self.phdr_zig_load_re_index.?];
-                try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz);
-                self.debug_aranges_section_dirty = false;
-            }
-
-            if (self.debug_line_header_dirty) {
-                try dw.writeDbgLineHeader();
-                self.debug_line_header_dirty = false;
-            }
-
-            if (self.debug_str_section_index) |shndx| {
-                if (self.debug_strtab_dirty or dw.strtab.buffer.items.len != self.shdrs.items[shndx].sh_size) {
-                    try self.growNonAllocSection(shndx, dw.strtab.buffer.items.len, 1, false);
-                    const shdr = self.shdrs.items[shndx];
-                    try self.base.file.?.pwriteAll(dw.strtab.buffer.items, shdr.sh_offset);
-                    self.debug_strtab_dirty = false;
-                }
-            }
-
-            self.saveDebugSectionsSizes();
-        }
-    }
-
     // Generate and emit non-incremental sections.
     try self.initSections();
     try self.initSpecialPhdrs();
@@ -1542,14 +1457,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
         self.error_flags.no_entry_point_found = false;
         try self.writeHeader();
     }
-
-    // The point of flush() is to commit changes, so in theory, nothing should
-    // be dirty after this. However, it is possible for some things to remain
-    // dirty because they fail to be written in the event of compile errors,
-    // such as debug_line_header_dirty and debug_info_header_dirty.
-    assert(!self.debug_abbrev_section_dirty);
-    assert(!self.debug_aranges_section_dirty);
-    assert(!self.debug_strtab_dirty);
 }
 
 const ParseError = error{
@@ -3815,24 +3722,6 @@ fn sortShdrs(self: *Elf) !void {
     }
 }
 
-fn saveDebugSectionsSizes(self: *Elf) void {
-    if (self.debug_info_section_index) |shndx| {
-        self.debug_info_section_zig_size = self.shdrs.items[shndx].sh_size;
-    }
-    if (self.debug_abbrev_section_index) |shndx| {
-        self.debug_abbrev_section_zig_size = self.shdrs.items[shndx].sh_size;
-    }
-    if (self.debug_str_section_index) |shndx| {
-        self.debug_str_section_zig_size = self.shdrs.items[shndx].sh_size;
-    }
-    if (self.debug_aranges_section_index) |shndx| {
-        self.debug_aranges_section_zig_size = self.shdrs.items[shndx].sh_size;
-    }
-    if (self.debug_line_section_index) |shndx| {
-        self.debug_line_section_zig_size = self.shdrs.items[shndx].sh_size;
-    }
-}
-
 fn updateSectionSizes(self: *Elf) !void {
     for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| {
         if (atom_list.items.len == 0) continue;
@@ -4189,12 +4078,18 @@ fn allocateNonAllocSections(self: *Elf) !void {
                     shdr.sh_offset,
                     new_offset,
                 });
+                const zig_object = self.zigObjectPtr().?;
                 const existing_size = blk: {
-                    if (shndx == self.debug_info_section_index.?) break :blk self.debug_info_section_zig_size;
-                    if (shndx == self.debug_abbrev_section_index.?) break :blk self.debug_abbrev_section_zig_size;
-                    if (shndx == self.debug_str_section_index.?) break :blk self.debug_str_section_zig_size;
-                    if (shndx == self.debug_aranges_section_index.?) break :blk self.debug_aranges_section_zig_size;
-                    if (shndx == self.debug_line_section_index.?) break :blk self.debug_line_section_zig_size;
+                    if (shndx == self.debug_info_section_index.?)
+                        break :blk zig_object.debug_info_section_zig_size;
+                    if (shndx == self.debug_abbrev_section_index.?)
+                        break :blk zig_object.debug_abbrev_section_zig_size;
+                    if (shndx == self.debug_str_section_index.?)
+                        break :blk zig_object.debug_str_section_zig_size;
+                    if (shndx == self.debug_aranges_section_index.?)
+                        break :blk zig_object.debug_aranges_section_zig_size;
+                    if (shndx == self.debug_line_section_index.?)
+                        break :blk zig_object.debug_line_section_zig_size;
                     unreachable;
                 };
                 const amt = try self.base.file.?.copyRangeAll(
@@ -4296,11 +4191,17 @@ fn writeAtoms(self: *Elf) !void {
 
         // TODO really, really handle debug section separately
         const base_offset = if (self.isDebugSection(@intCast(shndx))) blk: {
-            if (shndx == self.debug_info_section_index.?) break :blk self.debug_info_section_zig_size;
-            if (shndx == self.debug_abbrev_section_index.?) break :blk self.debug_abbrev_section_zig_size;
-            if (shndx == self.debug_str_section_index.?) break :blk self.debug_str_section_zig_size;
-            if (shndx == self.debug_aranges_section_index.?) break :blk self.debug_aranges_section_zig_size;
-            if (shndx == self.debug_line_section_index.?) break :blk self.debug_line_section_zig_size;
+            const zig_object = self.zigObjectPtr().?;
+            if (shndx == self.debug_info_section_index.?)
+                break :blk zig_object.debug_info_section_zig_size;
+            if (shndx == self.debug_abbrev_section_index.?)
+                break :blk zig_object.debug_abbrev_section_zig_size;
+            if (shndx == self.debug_str_section_index.?)
+                break :blk zig_object.debug_str_section_zig_size;
+            if (shndx == self.debug_aranges_section_index.?)
+                break :blk zig_object.debug_aranges_section_zig_size;
+            if (shndx == self.debug_line_section_index.?)
+                break :blk zig_object.debug_line_section_zig_size;
             unreachable;
         } else 0;
         const sh_offset = shdr.sh_offset + base_offset;