From 6c731be3a1710f3c70a955624c30467616f1d1c0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 20 Aug 2024 14:34:31 +0200 Subject: [PATCH] elf: allow for concatenating atoms to merge sections --- src/link/Elf.zig | 67 ++++++++-------------------------- src/link/Elf/ZigObject.zig | 20 ++++------ src/link/Elf/merge_section.zig | 34 ++++++++++++++++- src/link/Elf/relocatable.zig | 12 +++++- src/link/Elf/relocation.zig | 40 ++++++++++++++++++++ 5 files changed, 105 insertions(+), 68 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index de664c56a7e8d3257a4c832846d846a61d425a2b..5c8f0583ac3186a7fc17e491d3f86439e5056bbd 100644 --- a/src/link/Elf.zig +++ b/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), }); } diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index f1fcc26b46f780f14ae6515ca656980979e4e5ed..93736fbce2ee712a11d9ee0ad3f7961178438c1a 100644 --- a/src/link/Elf/ZigObject.zig +++ b/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); } } diff --git a/src/link/Elf/merge_section.zig b/src/link/Elf/merge_section.zig index baec316c3d9f3e2694e790fb32a1a867a7644c1a..7ffb17e9632e33141a196248141e5eac635d1491 100644 --- a/src/link/Elf/merge_section.zig +++ b/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, }); diff --git a/src/link/Elf/relocatable.zig b/src/link/Elf/relocatable.zig index 213b43a95d036b45a86f4c39a8605f12ff4253b9..710c3f85fa281a23330c7fb6819b02697130664d 100644 --- a/src/link/Elf/relocatable.zig +++ b/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; diff --git a/src/link/Elf/relocation.zig b/src/link/Elf/relocation.zig index e1a317f67d466f34ebd31ce40f03a18d5f360d49..d6f8dc5d101c6a4ceae6d7208030991fc1203031 100644 --- a/src/link/Elf/relocation.zig +++ b/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"); -- 2.54.0