Commit eeec50d251

Jakub Konka <kubkon@jakubkonka.com>
2024-09-03 21:01:12
elf: misc .eh_frame management fixes
1 parent fca92fd
src/link/Elf/eh_frame.zig
@@ -233,7 +233,10 @@ pub fn calcEhFrameSize(elf_file: *Elf) !usize {
     const comp = elf_file.base.comp;
     const gpa = comp.gpa;
 
-    var offset: usize = 0;
+    var offset: usize = if (elf_file.zigObjectPtr()) |zo| blk: {
+        const sym = zo.symbol(zo.eh_frame_index orelse break :blk 0);
+        break :blk sym.atom(elf_file).?.size;
+    } else 0;
 
     var cies = std.ArrayList(Cie).init(gpa);
     defer cies.deinit();
@@ -423,9 +426,8 @@ pub fn writeEhFrameRelocatable(elf_file: *Elf, writer: anytype) !void {
     }
 }
 
-fn emitReloc(elf_file: *Elf, base_offset: u64, sym: *const Symbol, rel: elf.Elf64_Rela) elf.Elf64_Rela {
+fn emitReloc(elf_file: *Elf, r_offset: u64, sym: *const Symbol, rel: elf.Elf64_Rela) elf.Elf64_Rela {
     const cpu_arch = elf_file.getTarget().cpu.arch;
-    const r_offset = base_offset + rel.r_offset;
     const r_type = rel.r_type();
     var r_addend = rel.r_addend;
     var r_sym: u32 = 0;
@@ -467,7 +469,7 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void {
         for (atom_ptr.relocs(elf_file)) |rel| {
             const ref = zo.resolveSymbol(rel.r_sym(), elf_file);
             const target = elf_file.symbol(ref).?;
-            const out_rel = emitReloc(elf_file, 0, target, rel);
+            const out_rel = emitReloc(elf_file, rel.r_offset, target, rel);
             try writer.writeStruct(out_rel);
         }
     }
@@ -480,8 +482,8 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void {
             for (cie.relocs(elf_file)) |rel| {
                 const ref = object.resolveSymbol(rel.r_sym(), elf_file);
                 const sym = elf_file.symbol(ref).?;
-                const offset = cie.address(elf_file) - cie.offset;
-                const out_rel = emitReloc(elf_file, offset, sym, rel);
+                const r_offset = cie.address(elf_file) + rel.r_offset - cie.offset;
+                const out_rel = emitReloc(elf_file, r_offset, sym, rel);
                 try writer.writeStruct(out_rel);
             }
         }
@@ -491,8 +493,8 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void {
             for (fde.relocs(elf_file)) |rel| {
                 const ref = object.resolveSymbol(rel.r_sym(), elf_file);
                 const sym = elf_file.symbol(ref).?;
-                const offset = fde.address(elf_file) - fde.offset;
-                const out_rel = emitReloc(elf_file, offset, sym, rel);
+                const r_offset = fde.address(elf_file) + rel.r_offset - fde.offset;
+                const out_rel = emitReloc(elf_file, r_offset, sym, rel);
                 try writer.writeStruct(out_rel);
             }
         }
src/link/Elf/Object.zig
@@ -1096,6 +1096,7 @@ pub fn initRelaSections(self: *Object, elf_file: *Elf) !void {
     for (self.atoms_indexes.items) |atom_index| {
         const atom_ptr = self.atom(atom_index) orelse continue;
         if (!atom_ptr.alive) continue;
+        if (atom_ptr.output_section_index == elf_file.eh_frame_section_index) continue;
         const shndx = atom_ptr.relocsShndx() orelse continue;
         const shdr = self.shdrs.items[shndx];
         const out_shndx = try elf_file.initOutputSection(.{
src/link/Elf/relocatable.zig
@@ -302,30 +302,29 @@ fn initSections(elf_file: *Elf) !void {
         try msec.initOutputSection(elf_file);
     }
 
-    const needs_eh_frame = if (elf_file.zigObjectPtr()) |zo|
-        zo.eh_frame_index != null
-    else for (elf_file.objects.items) |index| {
-        if (elf_file.file(index).?.object.cies.items.len > 0) break true;
-    } else false;
+    const needs_eh_frame = blk: {
+        if (elf_file.zigObjectPtr()) |zo|
+            if (zo.eh_frame_index != null) break :blk true;
+        break :blk for (elf_file.objects.items) |index| {
+            if (elf_file.file(index).?.object.cies.items.len > 0) break true;
+        } else false;
+    };
     if (needs_eh_frame) {
         if (elf_file.eh_frame_section_index == null) {
-            elf_file.eh_frame_section_index = blk: {
-                if (elf_file.zigObjectPtr()) |zo| {
-                    if (zo.eh_frame_index) |idx| break :blk zo.symbol(idx).atom(elf_file).?.output_section_index;
-                }
-                break :blk try elf_file.addSection(.{
-                    .name = try elf_file.insertShString(".eh_frame"),
-                    .type = if (elf_file.getTarget().cpu.arch == .x86_64)
-                        elf.SHT_X86_64_UNWIND
-                    else
-                        elf.SHT_PROGBITS,
-                    .flags = elf.SHF_ALLOC,
-                    .addralign = elf_file.ptrWidthBytes(),
-                    .offset = std.math.maxInt(u64),
-                });
-            };
+            elf_file.eh_frame_section_index = elf_file.sectionByName(".eh_frame") orelse
+                try elf_file.addSection(.{
+                .name = try elf_file.insertShString(".eh_frame"),
+                .type = if (elf_file.getTarget().cpu.arch == .x86_64)
+                    elf.SHT_X86_64_UNWIND
+                else
+                    elf.SHT_PROGBITS,
+                .flags = elf.SHF_ALLOC,
+                .addralign = elf_file.ptrWidthBytes(),
+                .offset = std.math.maxInt(u64),
+            });
         }
-        elf_file.eh_frame_rela_section_index = try elf_file.addRelaShdr(
+        elf_file.eh_frame_rela_section_index = elf_file.sectionByName(".rela.eh_frame") orelse
+            try elf_file.addRelaShdr(
             try elf_file.insertShString(".rela.eh_frame"),
             elf_file.eh_frame_section_index.?,
         );
@@ -367,6 +366,7 @@ fn updateSectionSizes(elf_file: *Elf) !void {
     for (slice.items(.shdr), 0..) |*shdr, shndx| {
         const atom_list = slice.items(.atom_list)[shndx];
         if (shdr.sh_type != elf.SHT_RELA) continue;
+        if (@as(u32, @intCast(shndx)) == elf_file.eh_frame_section_index) continue;
         for (atom_list.items) |ref| {
             const atom_ptr = elf_file.atom(ref) orelse continue;
             if (!atom_ptr.alive) continue;
@@ -378,11 +378,7 @@ fn updateSectionSizes(elf_file: *Elf) !void {
     }
 
     if (elf_file.eh_frame_section_index) |index| {
-        slice.items(.shdr)[index].sh_size = existing_size: {
-            const zo = elf_file.zigObjectPtr() orelse break :existing_size 0;
-            const sym = zo.symbol(zo.eh_frame_index orelse break :existing_size 0);
-            break :existing_size sym.atom(elf_file).?.size;
-        } + try eh_frame.calcEhFrameSize(elf_file);
+        slice.items(.shdr)[index].sh_size = try eh_frame.calcEhFrameSize(elf_file);
     }
     if (elf_file.eh_frame_rela_section_index) |index| {
         const shdr = &slice.items(.shdr)[index];
@@ -464,8 +460,8 @@ fn writeSyntheticSections(elf_file: *Elf) !void {
 
     for (slice.items(.shdr), slice.items(.atom_list), 0..) |shdr, atom_list, shndx| {
         if (shdr.sh_type != elf.SHT_RELA) continue;
-        if (@as(u32, @intCast(shndx)) == elf_file.eh_frame_rela_section_index) continue;
         if (atom_list.items.len == 0) continue;
+        if (@as(u32, @intCast(shndx)) == elf_file.eh_frame_section_index) continue;
 
         const num_relocs = math.cast(usize, @divExact(shdr.sh_size, shdr.sh_entsize)) orelse
             return error.Overflow;
src/link/Elf/ZigObject.zig
@@ -111,6 +111,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                 });
                 self.debug_str_section_dirty = true;
                 self.debug_str_index = try self.addSectionSymbol(gpa, ".debug_str", .@"1", osec);
+                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_str_index.?).ref;
             }
 
             if (self.debug_info_index == null) {
@@ -121,6 +122,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                 });
                 self.debug_info_section_dirty = true;
                 self.debug_info_index = try self.addSectionSymbol(gpa, ".debug_info", .@"1", osec);
+                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_info_index.?).ref;
             }
 
             if (self.debug_abbrev_index == null) {
@@ -131,6 +133,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                 });
                 self.debug_abbrev_section_dirty = true;
                 self.debug_abbrev_index = try self.addSectionSymbol(gpa, ".debug_abbrev", .@"1", osec);
+                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_abbrev_index.?).ref;
             }
 
             if (self.debug_aranges_index == null) {
@@ -141,6 +144,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                 });
                 self.debug_aranges_section_dirty = true;
                 self.debug_aranges_index = try self.addSectionSymbol(gpa, ".debug_aranges", .@"16", osec);
+                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_aranges_index.?).ref;
             }
 
             if (self.debug_line_index == null) {
@@ -151,6 +155,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                 });
                 self.debug_line_section_dirty = true;
                 self.debug_line_index = try self.addSectionSymbol(gpa, ".debug_line", .@"1", osec);
+                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_line_index.?).ref;
             }
 
             if (self.debug_line_str_index == null) {
@@ -163,6 +168,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                 });
                 self.debug_line_str_section_dirty = true;
                 self.debug_line_str_index = try self.addSectionSymbol(gpa, ".debug_line_str", .@"1", osec);
+                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_line_str_index.?).ref;
             }
 
             if (self.debug_loclists_index == null) {
@@ -173,6 +179,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                 });
                 self.debug_loclists_section_dirty = true;
                 self.debug_loclists_index = try self.addSectionSymbol(gpa, ".debug_loclists", .@"1", osec);
+                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_loclists_index.?).ref;
             }
 
             if (self.debug_rnglists_index == null) {
@@ -183,6 +190,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                 });
                 self.debug_rnglists_section_dirty = true;
                 self.debug_rnglists_index = try self.addSectionSymbol(gpa, ".debug_rnglists", .@"1", osec);
+                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.debug_rnglists_index.?).ref;
             }
 
             if (self.eh_frame_index == null) {
@@ -197,6 +205,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
                 });
                 self.eh_frame_section_dirty = true;
                 self.eh_frame_index = try self.addSectionSymbol(gpa, ".eh_frame", Atom.Alignment.fromNonzeroByteUnits(ptr_size), osec);
+                elf_file.sections.items(.last_atom)[osec] = self.symbol(self.eh_frame_index.?).ref;
             }
 
             try dwarf.initMetadata();
@@ -1116,6 +1125,9 @@ pub fn getOrCreateMetadataForNav(
     return gop.value_ptr.symbol_index;
 }
 
+// FIXME: we always create an atom to basically store size and alignment, however, this is only true for
+// sections that have a single atom like the debug sections. It would be a better solution to decouple this
+// concept from the atom, maybe.
 fn addSectionSymbol(
     self: *ZigObject,
     allocator: Allocator,
src/link/Elf.zig
@@ -621,7 +621,6 @@ pub fn growNonAllocSection(
         try self.base.file.?.setEndPos(shdr.sh_offset + needed_size);
     }
     shdr.sh_size = needed_size;
-
     self.markDirty(shdr_index);
 }
 
@@ -2895,28 +2894,25 @@ fn initSyntheticSections(self: *Elf) !void {
     const target = self.getTarget();
     const ptr_size = self.ptrWidthBytes();
 
-    const needs_eh_frame = if (self.zigObjectPtr()) |zo|
-        zo.eh_frame_index != null
-    else for (self.objects.items) |index| {
-        if (self.file(index).?.object.cies.items.len > 0) break true;
-    } else false;
+    const needs_eh_frame = blk: {
+        if (self.zigObjectPtr()) |zo|
+            if (zo.eh_frame_index != null) break :blk true;
+        break :blk for (self.objects.items) |index| {
+            if (self.file(index).?.object.cies.items.len > 0) break true;
+        } else false;
+    };
     if (needs_eh_frame) {
         if (self.eh_frame_section_index == null) {
-            self.eh_frame_section_index = blk: {
-                if (self.zigObjectPtr()) |zo| {
-                    if (zo.eh_frame_index) |idx| break :blk zo.symbol(idx).atom(self).?.output_section_index;
-                }
-                break :blk try self.addSection(.{
-                    .name = try self.insertShString(".eh_frame"),
-                    .type = if (target.cpu.arch == .x86_64)
-                        elf.SHT_X86_64_UNWIND
-                    else
-                        elf.SHT_PROGBITS,
-                    .flags = elf.SHF_ALLOC,
-                    .addralign = ptr_size,
-                    .offset = std.math.maxInt(u64),
-                });
-            };
+            self.eh_frame_section_index = self.sectionByName(".eh_frame") orelse try self.addSection(.{
+                .name = try self.insertShString(".eh_frame"),
+                .type = if (target.cpu.arch == .x86_64)
+                    elf.SHT_X86_64_UNWIND
+                else
+                    elf.SHT_PROGBITS,
+                .flags = elf.SHF_ALLOC,
+                .addralign = ptr_size,
+                .offset = std.math.maxInt(u64),
+            });
         }
         if (comp.link_eh_frame_hdr) {
             self.eh_frame_hdr_section_index = try self.addSection(.{
@@ -3591,11 +3587,7 @@ fn updateSectionSizes(self: *Elf) !void {
 
     const shdrs = slice.items(.shdr);
     if (self.eh_frame_section_index) |index| {
-        shdrs[index].sh_size = existing_size: {
-            const zo = self.zigObjectPtr() orelse break :existing_size 0;
-            const sym = zo.symbol(zo.eh_frame_index orelse break :existing_size 0);
-            break :existing_size sym.atom(self).?.size;
-        } + try eh_frame.calcEhFrameSize(self);
+        shdrs[index].sh_size = try eh_frame.calcEhFrameSize(self);
     }
 
     if (self.eh_frame_hdr_section_index) |index| {