Commit 6c731be3a1

Jakub Konka <kubkon@jakubkonka.com>
2024-08-20 14:34:31
elf: allow for concatenating atoms to merge sections
1 parent b2fad5c
src/link/Elf/merge_section.zig
@@ -1,4 +1,8 @@
 pub const MergeSection = struct {
+    value: u64 = 0,
+    size: u64 = 0,
+    alignment: Atom.Alignment = .@"1",
+    entsize: u32 = 0,
     name_offset: u32 = 0,
     type: u32 = 0,
     flags: u64 = 0,
@@ -26,7 +30,7 @@ pub const MergeSection = struct {
 
     pub fn address(msec: MergeSection, elf_file: *Elf) i64 {
         const shdr = elf_file.shdrs.items[msec.output_section_index];
-        return @intCast(shdr.sh_addr);
+        return @intCast(shdr.sh_addr + msec.value);
     }
 
     const InsertResult = struct {
@@ -90,6 +94,29 @@ pub const MergeSection = struct {
         std.mem.sort(MergeSubsection.Index, msec.finalized_subsections.items, msec, sortFn);
     }
 
+    pub fn updateSize(msec: *MergeSection) void {
+        for (msec.finalized_subsections.items) |msub_index| {
+            const msub = msec.mergeSubsection(msub_index);
+            assert(msub.alive);
+            const offset = msub.alignment.forward(msec.size);
+            const padding = offset - msec.size;
+            msub.value = @intCast(offset);
+            msec.size += padding + msub.size;
+            msec.alignment = msec.alignment.max(msub.alignment);
+            msec.entsize = if (msec.entsize == 0) msub.entsize else @min(msec.entsize, msub.entsize);
+        }
+    }
+
+    pub fn initOutputSection(msec: *MergeSection, elf_file: *Elf) !void {
+        const shndx = elf_file.sectionByName(msec.name(elf_file)) orelse try elf_file.addSection(.{
+            .name = msec.name_offset,
+            .type = msec.type,
+            .flags = msec.flags,
+        });
+        try elf_file.output_sections.put(elf_file.base.comp.gpa, shndx, .{});
+        msec.output_section_index = shndx;
+    }
+
     pub fn addMergeSubsection(msec: *MergeSection, allocator: Allocator) !MergeSubsection.Index {
         const index: MergeSubsection.Index = @intCast(msec.subsections.items.len);
         const msub = try msec.subsections.addOne(allocator);
@@ -163,9 +190,12 @@ pub const MergeSection = struct {
         _ = unused_fmt_string;
         const msec = ctx.msec;
         const elf_file = ctx.elf_file;
-        try writer.print("{s} : @{x} : type({x}) : flags({x})\n", .{
+        try writer.print("{s} : @{x} : size({x}) : align({x}) : entsize({x}) : type({x}) : flags({x})\n", .{
             msec.name(elf_file),
             msec.address(elf_file),
+            msec.size,
+            msec.alignment.toByteUnits() orelse 0,
+            msec.entsize,
             msec.type,
             msec.flags,
         });
src/link/Elf/relocatable.zig
@@ -42,7 +42,11 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
         try elf_file.finalizeMergeSections();
         zig_object.claimUnresolvedObject(elf_file);
 
-        try elf_file.initMergeSections();
+        for (elf_file.merge_sections.items) |*msec| {
+            if (msec.finalized_subsections.items.len == 0) continue;
+            try msec.initOutputSection(elf_file);
+        }
+
         try elf_file.initSymtab();
         try elf_file.initShStrtab();
         try elf_file.sortShdrs();
@@ -198,7 +202,6 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
     claimUnresolved(elf_file);
 
     try initSections(elf_file);
-    try elf_file.initMergeSections();
     try elf_file.sortShdrs();
     if (elf_file.zigObjectPtr()) |zig_object| {
         try zig_object.addAtomsToRelaSections(elf_file);
@@ -294,6 +297,11 @@ fn initSections(elf_file: *Elf) !void {
         try object.initRelaSections(elf_file);
     }
 
+    for (elf_file.merge_sections.items) |*msec| {
+        if (msec.finalized_subsections.items.len == 0) continue;
+        try msec.initOutputSection(elf_file);
+    }
+
     const needs_eh_frame = for (elf_file.objects.items) |index| {
         if (elf_file.file(index).?.object.cies.items.len > 0) break true;
     } else false;
src/link/Elf/relocation.zig
@@ -91,6 +91,44 @@ pub fn encode(comptime kind: Kind, cpu_arch: std.Target.Cpu.Arch) u32 {
     };
 }
 
+pub const dwarf = struct {
+    pub fn crossSectionRelocType(format: DW.Format, cpu_arch: std.Target.Cpu.Arch) u32 {
+        return switch (cpu_arch) {
+            .x86_64 => @intFromEnum(switch (format) {
+                .@"32" => elf.R_X86_64.@"32",
+                .@"64" => .@"64",
+            }),
+            .riscv64 => @intFromEnum(switch (format) {
+                .@"32" => elf.R_RISCV.@"32",
+                .@"64" => .@"64",
+            }),
+            else => @panic("TODO unhandled cpu arch"),
+        };
+    }
+
+    pub fn externalRelocType(
+        target: Symbol,
+        address_size: Dwarf.AddressSize,
+        cpu_arch: std.Target.Cpu.Arch,
+    ) u32 {
+        return switch (cpu_arch) {
+            .x86_64 => @intFromEnum(switch (address_size) {
+                .@"32" => if (target.flags.is_tls) elf.R_X86_64.DTPOFF32 else .@"32",
+                .@"64" => if (target.flags.is_tls) elf.R_X86_64.DTPOFF64 else .@"64",
+                else => unreachable,
+            }),
+            .riscv64 => @intFromEnum(switch (address_size) {
+                .@"32" => elf.R_RISCV.@"32",
+                .@"64" => elf.R_RISCV.@"64",
+                else => unreachable,
+            }),
+            else => @panic("TODO unhandled cpu arch"),
+        };
+    }
+
+    const DW = std.dwarf;
+};
+
 const FormatRelocTypeCtx = struct {
     r_type: u32,
     cpu_arch: std.Target.Cpu.Arch,
@@ -124,4 +162,6 @@ const assert = std.debug.assert;
 const elf = std.elf;
 const std = @import("std");
 
+const Dwarf = @import("../Dwarf.zig");
 const Elf = @import("../Elf.zig");
+const Symbol = @import("Symbol.zig");
src/link/Elf/ZigObject.zig
@@ -183,6 +183,7 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !voi
         try dwarf.resolveRelocs();
 
         const gpa = elf_file.base.comp.gpa;
+        const cpu_arch = elf_file.getTarget().cpu.arch;
 
         // TODO invert this logic so that we manage the output section with the atom, not the
         // other way around
@@ -242,20 +243,17 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !voi
                         target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(unit, sect, dwarf).off
                     else
                         0));
-                    const r_type: elf.R_X86_64 = switch (dwarf.format) {
-                        .@"32" => .@"32",
-                        .@"64" => .@"64",
-                    };
+                    const r_type = relocation.dwarf.crossSectionRelocType(dwarf.format, cpu_arch);
                     log.debug("  {s} <- r_off={x}, r_add={x}, r_type={}", .{
                         self.symbol(target_sym_index).name(elf_file),
                         r_offset,
                         r_addend,
-                        relocation.fmtRelocType(@intFromEnum(r_type), elf_file.getTarget().cpu.arch),
+                        relocation.fmtRelocType(r_type, cpu_arch),
                     });
                     atom_ptr.addRelocAssumeCapacity(.{
                         .r_offset = r_offset,
                         .r_addend = r_addend,
-                        .r_info = (@as(u64, @intCast(target_sym_index)) << 32) | @intFromEnum(r_type),
+                        .r_info = (@as(u64, @intCast(target_sym_index)) << 32) | r_type,
                     }, self);
                 }
 
@@ -264,21 +262,17 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !voi
                     const target_sym = self.symbol(reloc.target_sym);
                     const r_offset = unit.off + unit.header_len + unit.getEntry(reloc.source_entry).off + reloc.source_off;
                     const r_addend: i64 = @intCast(reloc.target_off);
-                    const r_type: elf.R_X86_64 = switch (dwarf.address_size) {
-                        .@"32" => if (target_sym.flags.is_tls) .DTPOFF32 else .@"32",
-                        .@"64" => if (target_sym.flags.is_tls) .DTPOFF64 else .@"64",
-                        else => unreachable,
-                    };
+                    const r_type = relocation.dwarf.externalRelocType(target_sym.*, dwarf.address_size, cpu_arch);
                     log.debug("  {s} <- r_off={x}, r_add={x}, r_type={}", .{
                         target_sym.name(elf_file),
                         r_offset,
                         r_addend,
-                        relocation.fmtRelocType(@intFromEnum(r_type), elf_file.getTarget().cpu.arch),
+                        relocation.fmtRelocType(r_type, cpu_arch),
                     });
                     atom_ptr.addRelocAssumeCapacity(.{
                         .r_offset = r_offset,
                         .r_addend = r_addend,
-                        .r_info = (@as(u64, @intCast(reloc.target_sym)) << 32) | @intFromEnum(r_type),
+                        .r_info = (@as(u64, @intCast(reloc.target_sym)) << 32) | r_type,
                     }, self);
                 }
             }
src/link/Elf.zig
@@ -1282,7 +1282,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
     try self.addCommentString();
     try self.finalizeMergeSections();
     try self.initOutputSections();
-    try self.initMergeSections();
     if (self.linkerDefinedPtr()) |obj| {
         try obj.initStartStopSymbols(self);
     }
@@ -3048,17 +3047,17 @@ pub fn finalizeMergeSections(self: *Elf) !void {
 }
 
 pub fn updateMergeSectionSizes(self: *Elf) !void {
+    for (self.merge_sections.items) |*msec| {
+        msec.updateSize();
+    }
     for (self.merge_sections.items) |*msec| {
         const shdr = &self.shdrs.items[msec.output_section_index];
-        for (msec.finalized_subsections.items) |msub_index| {
-            const msub = msec.mergeSubsection(msub_index);
-            assert(msub.alive);
-            const offset = msub.alignment.forward(shdr.sh_size);
-            const padding = offset - shdr.sh_size;
-            msub.value = @intCast(offset);
-            shdr.sh_size += padding + msub.size;
-            shdr.sh_addralign = @max(shdr.sh_addralign, msub.alignment.toByteUnits() orelse 1);
-        }
+        const offset = msec.alignment.forward(shdr.sh_size);
+        const padding = offset - shdr.sh_size;
+        msec.value = @intCast(offset);
+        shdr.sh_size += padding + msec.size;
+        shdr.sh_addralign = @max(shdr.sh_addralign, msec.alignment.toByteUnits() orelse 1);
+        shdr.sh_entsize = if (shdr.sh_entsize == 0) msec.entsize else @min(shdr.sh_entsize, msec.entsize);
     }
 }
 
@@ -3069,7 +3068,8 @@ pub fn writeMergeSections(self: *Elf) !void {
 
     for (self.merge_sections.items) |*msec| {
         const shdr = self.shdrs.items[msec.output_section_index];
-        const size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
+        const fileoff = math.cast(usize, msec.value + shdr.sh_offset) orelse return error.Overflow;
+        const size = math.cast(usize, msec.size) orelse return error.Overflow;
         try buffer.ensureTotalCapacity(size);
         buffer.appendNTimesAssumeCapacity(0, size);
 
@@ -3081,7 +3081,7 @@ pub fn writeMergeSections(self: *Elf) !void {
             @memcpy(buffer.items[off..][0..string.len], string);
         }
 
-        try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
+        try self.base.file.?.pwriteAll(buffer.items, fileoff);
         buffer.clearRetainingCapacity();
     }
 }
@@ -3090,26 +3090,9 @@ fn initOutputSections(self: *Elf) !void {
     for (self.objects.items) |index| {
         try self.file(index).?.object.initOutputSections(self);
     }
-}
-
-pub fn initMergeSections(self: *Elf) !void {
     for (self.merge_sections.items) |*msec| {
         if (msec.finalized_subsections.items.len == 0) continue;
-        const name = msec.name(self);
-        const shndx = self.sectionByName(name) orelse try self.addSection(.{
-            .name = msec.name_offset,
-            .type = msec.type,
-            .flags = msec.flags,
-        });
-        msec.output_section_index = shndx;
-
-        var entsize = msec.mergeSubsection(msec.finalized_subsections.items[0]).entsize;
-        for (msec.finalized_subsections.items) |msub_index| {
-            const msub = msec.mergeSubsection(msub_index);
-            entsize = @min(entsize, msub.entsize);
-        }
-        const shdr = &self.shdrs.items[shndx];
-        shdr.sh_entsize = entsize;
+        try msec.initOutputSection(self);
     }
 }
 
@@ -4427,7 +4410,6 @@ pub fn updateSymtabSize(self: *Elf) !void {
     if (self.eh_frame_section_index) |_| {
         nlocals += 1;
     }
-    nlocals += @intCast(self.merge_sections.items.len);
 
     if (self.requiresThunks()) for (self.thunks.items) |*th| {
         th.output_symtab_ctx.ilocal = nlocals + 1;
@@ -4751,30 +4733,12 @@ fn writeSectionSymbols(self: *Elf) void {
         };
         ilocal += 1;
     }
-
-    for (self.merge_sections.items) |msec| {
-        const shdr = self.shdrs.items[msec.output_section_index];
-        const out_sym = &self.symtab.items[ilocal];
-        out_sym.* = .{
-            .st_name = 0,
-            .st_value = shdr.sh_addr,
-            .st_info = elf.STT_SECTION,
-            .st_shndx = @intCast(msec.output_section_index),
-            .st_size = 0,
-            .st_other = 0,
-        };
-        ilocal += 1;
-    }
 }
 
 pub fn sectionSymbolOutputSymtabIndex(self: Elf, shndx: u32) u32 {
     if (self.eh_frame_section_index) |index| {
         if (index == shndx) return @intCast(self.output_sections.keys().len + 1);
     }
-    const base: usize = if (self.eh_frame_section_index == null) 0 else 1;
-    for (self.merge_sections.items, 0..) |msec, index| {
-        if (msec.output_section_index == shndx) return @intCast(self.output_sections.keys().len + 1 + index + base);
-    }
     return @intCast(self.output_sections.getIndex(shndx).? + 1);
 }
 
@@ -5537,10 +5501,11 @@ fn formatShdr(
     _ = options;
     _ = unused_fmt_string;
     const shdr = ctx.shdr;
-    try writer.print("{s} : @{x} ({x}) : align({x}) : size({x}) : flags({})", .{
+    try writer.print("{s} : @{x} ({x}) : align({x}) : size({x}) : entsize({x}) : flags({})", .{
         ctx.elf_file.getShString(shdr.sh_name), shdr.sh_offset,
         shdr.sh_addr,                           shdr.sh_addralign,
-        shdr.sh_size,                           fmtShdrFlags(shdr.sh_flags),
+        shdr.sh_size,                           shdr.sh_entsize,
+        fmtShdrFlags(shdr.sh_flags),
     });
 }