From 4404c4d20094bb5021aac4a047cd33b6c24b9a9b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 31 Jan 2023 17:54:12 +0100 Subject: [PATCH] link: make Elf atoms fully owned by the linker --- src/Module.zig | 6 +- src/Sema.zig | 2 +- src/arch/aarch64/CodeGen.zig | 13 +- src/arch/arm/CodeGen.zig | 15 +- src/arch/riscv64/CodeGen.zig | 14 +- src/arch/sparc64/CodeGen.zig | 12 +- src/arch/x86_64/CodeGen.zig | 13 +- src/link.zig | 4 +- src/link/Dwarf.zig | 26 +- src/link/Elf.zig | 1525 ++++++++++++++++++---------------- src/link/Elf/Atom.zig | 61 +- src/link/MachO.zig | 4 +- 12 files changed, 886 insertions(+), 809 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 83015054922ca49241c558d22c47436b84e6defc..0695a2e98aace36593ce627ccc538bceee08e725 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -5275,7 +5275,7 @@ pub fn clearDecl( // and allow it to be variably sized. decl.link = switch (mod.comp.bin_file.tag) { .coff => .{ .coff = link.File.Coff.Atom.empty }, - .elf => .{ .elf = link.File.Elf.TextBlock.empty }, + .elf => .{ .elf = {} }, .macho => .{ .macho = {} }, .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty }, .c => .{ .c = {} }, @@ -5381,7 +5381,7 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void } } if (mod.comp.bin_file.cast(link.File.Elf)) |elf| { - elf.deleteExport(exp.link.elf); + elf.deleteDeclExport(decl_index, exp.options.name); } if (mod.comp.bin_file.cast(link.File.MachO)) |macho| { try macho.deleteDeclExport(decl_index, exp.options.name); @@ -5695,7 +5695,7 @@ pub fn allocateNewDecl( .src_scope = src_scope, .link = switch (mod.comp.bin_file.tag) { .coff => .{ .coff = link.File.Coff.Atom.empty }, - .elf => .{ .elf = link.File.Elf.TextBlock.empty }, + .elf => .{ .elf = {} }, .macho => .{ .macho = {} }, .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty }, .c => .{ .c = {} }, diff --git a/src/Sema.zig b/src/Sema.zig index 9083cc92abee98b1461d0f25319c6effb7a7fc4b..28d559f730f01dcd17340f017d386383e81e222c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5566,7 +5566,7 @@ pub fn analyzeExport( .src = src, .link = switch (mod.comp.bin_file.tag) { .coff => .{ .coff = .{} }, - .elf => .{ .elf = .{} }, + .elf => .{ .elf = {} }, .macho => .{ .macho = {} }, .plan9 => .{ .plan9 = null }, .c => .{ .c = {} }, diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index edbe7905a213ffe715b2e66f40135d947fc15ed7..67197c35f890b06936bde181daa4a2e2de72bbd3 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4308,8 +4308,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const fn_owner_decl = mod.declPtr(func.owner_decl); if (self.bin_file.cast(link.File.Elf)) |elf_file| { - try fn_owner_decl.link.elf.ensureInitialized(elf_file); - const got_addr = @intCast(u32, fn_owner_decl.link.elf.getOffsetTableAddress(elf_file)); + const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); + const atom = elf_file.getAtom(atom_index); + const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr }); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl); @@ -6138,8 +6139,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne mod.markDeclAlive(decl); if (self.bin_file.cast(link.File.Elf)) |elf_file| { - try decl.link.elf.ensureInitialized(elf_file); - return MCValue{ .memory = decl.link.elf.getOffsetTableAddress(elf_file) }; + const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); + const atom = elf_file.getAtom(atom_index); + return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { const atom = try macho_file.getOrCreateAtomForDecl(decl_index); const sym_index = macho_file.getAtom(atom).getSymbolIndex().?; @@ -6168,8 +6170,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); }; if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const vaddr = elf_file.local_symbols.items[local_sym_index].st_value; - return MCValue{ .memory = vaddr }; + return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value }; } else if (self.bin_file.cast(link.File.MachO)) |_| { return MCValue{ .linker_load = .{ .type = .direct, diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 49f979624d0a78ed7489763f219ceb9da3f12881..c6ee960e510163f433cb6d35bcc66cbae90497e5 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -4256,12 +4256,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (self.air.value(callee)) |func_value| { if (func_value.castTag(.function)) |func_payload| { const func = func_payload.data; - const mod = self.bin_file.options.module.?; - const fn_owner_decl = mod.declPtr(func.owner_decl); if (self.bin_file.cast(link.File.Elf)) |elf_file| { - try fn_owner_decl.link.elf.ensureInitialized(elf_file); - const got_addr = @intCast(u32, fn_owner_decl.link.elf.getOffsetTableAddress(elf_file)); + const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); + const atom = elf_file.getAtom(atom_index); + const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr }); } else if (self.bin_file.cast(link.File.MachO)) |_| { unreachable; // unsupported architecture for MachO @@ -6084,8 +6083,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne mod.markDeclAlive(decl); if (self.bin_file.cast(link.File.Elf)) |elf_file| { - try decl.link.elf.ensureInitialized(elf_file); - return MCValue{ .memory = decl.link.elf.getOffsetTableAddress(elf_file) }; + const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); + const atom = elf_file.getAtom(atom_index); + return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; } else if (self.bin_file.cast(link.File.MachO)) |_| { unreachable; // unsupported architecture for MachO } else if (self.bin_file.cast(link.File.Coff)) |_| { @@ -6106,8 +6106,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); }; if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const vaddr = elf_file.local_symbols.items[local_sym_index].st_value; - return MCValue{ .memory = vaddr }; + return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value }; } else if (self.bin_file.cast(link.File.MachO)) |_| { unreachable; } else if (self.bin_file.cast(link.File.Coff)) |_| { diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 07a8dcd8582a935fcce13c899ec6120d8f74631b..a0af1b3ccec40a4257898c67f8402cadff040c50 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1721,12 +1721,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (self.air.value(callee)) |func_value| { if (func_value.castTag(.function)) |func_payload| { const func = func_payload.data; - - const mod = self.bin_file.options.module.?; - const fn_owner_decl = mod.declPtr(func.owner_decl); - try fn_owner_decl.link.elf.ensureInitialized(elf_file); - const got_addr = @intCast(u32, fn_owner_decl.link.elf.getOffsetTableAddress(elf_file)); - + const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); + const atom = elf_file.getAtom(atom_index); + const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr }); _ = try self.addInst(.{ .tag = .jalr, @@ -2553,8 +2550,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne const decl = mod.declPtr(decl_index); mod.markDeclAlive(decl); if (self.bin_file.cast(link.File.Elf)) |elf_file| { - try decl.link.elf.ensureInitialized(elf_file); - return MCValue{ .memory = decl.link.elf.getOffsetTableAddress(elf_file) }; + const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); + const atom = elf_file.getAtom(atom_index); + return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; } else if (self.bin_file.cast(link.File.MachO)) |_| { unreachable; } else if (self.bin_file.cast(link.File.Coff)) |_| { diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 5e9326d23b3f1c66b5992fbe9697c7d1c5e424b7..e67244167ef25351157c701e35add5749f7388b2 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1216,11 +1216,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (self.bin_file.tag == link.File.Elf.base_tag) { if (func_value.castTag(.function)) |func_payload| { const func = func_payload.data; - const mod = self.bin_file.options.module.?; - const fn_owner_decl = mod.declPtr(func.owner_decl); const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { - try fn_owner_decl.link.elf.ensureInitialized(elf_file); - break :blk @intCast(u32, fn_owner_decl.link.elf.getOffsetTableAddress(elf_file)); + const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); + const atom = elf_file.getAtom(atom_index); + break :blk @intCast(u32, atom.getOffsetTableAddress(elf_file)); } else unreachable; try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr }); @@ -4205,8 +4204,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne mod.markDeclAlive(decl); if (self.bin_file.cast(link.File.Elf)) |elf_file| { - try decl.link.elf.ensureInitialized(elf_file); - return MCValue{ .memory = decl.link.elf.getOffsetTableAddress(elf_file) }; + const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); + const atom = elf_file.getAtom(atom_index); + return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; } else { return self.fail("TODO codegen non-ELF const Decl pointer", .{}); } diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index fc244e3130afa595af98853ef09d2210dd5a087d..23d3ca5514c86eebdd2e4c78e14d20ce5ba1e1c6 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -4000,8 +4000,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const fn_owner_decl = mod.declPtr(func.owner_decl); if (self.bin_file.cast(link.File.Elf)) |elf_file| { - try fn_owner_decl.link.elf.ensureInitialized(elf_file); - const got_addr = @intCast(u32, fn_owner_decl.link.elf.getOffsetTableAddress(elf_file)); + const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); + const atom = elf_file.getAtom(atom_index); + const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); _ = try self.addInst(.{ .tag = .call, .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), @@ -6721,8 +6722,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne module.markDeclAlive(decl); if (self.bin_file.cast(link.File.Elf)) |elf_file| { - try decl.link.elf.ensureInitialized(elf_file); - return MCValue{ .memory = decl.link.elf.getOffsetTableAddress(elf_file) }; + const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); + const atom = elf_file.getAtom(atom_index); + return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; @@ -6751,8 +6753,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); }; if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const vaddr = elf_file.local_symbols.items[local_sym_index].st_value; - return MCValue{ .memory = vaddr }; + return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value }; } else if (self.bin_file.cast(link.File.MachO)) |_| { return MCValue{ .linker_load = .{ .type = .direct, diff --git a/src/link.zig b/src/link.zig index 2a96efe89da6b5f69020846bd683e9e184d81743..09804add531c1e5bc5d3c2f915053b43b01556ff 100644 --- a/src/link.zig +++ b/src/link.zig @@ -262,7 +262,7 @@ pub const File = struct { lock: ?Cache.Lock = null, pub const LinkBlock = union { - elf: Elf.TextBlock, + elf: void, coff: Coff.Atom, macho: void, plan9: Plan9.DeclBlock, @@ -284,7 +284,7 @@ pub const File = struct { }; pub const Export = union { - elf: Elf.Export, + elf: void, coff: Coff.Export, macho: void, plan9: Plan9.Export, diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 2595cd8ba56acc7796a8c62161ea5dedacd54c41..8278377095edfc5cd29a17aae7dc533fc4cbfd52 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1099,7 +1099,7 @@ pub fn commitDeclState( switch (self.bin_file.tag) { .elf => { const elf_file = self.bin_file.cast(File.Elf).?; - const debug_line_sect = &elf_file.sections.items[elf_file.debug_line_section_index.?]; + const debug_line_sect = &elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?]; const file_pos = debug_line_sect.sh_offset + src_fn.off; try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len); }, @@ -1152,7 +1152,7 @@ pub fn commitDeclState( const elf_file = self.bin_file.cast(File.Elf).?; const shdr_index = elf_file.debug_line_section_index.?; try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true); - const debug_line_sect = elf_file.sections.items[shdr_index]; + const debug_line_sect = elf_file.sections.items(.shdr)[shdr_index]; const file_pos = debug_line_sect.sh_offset + src_fn.off; try pwriteDbgLineNops( elf_file.base.file.?, @@ -1332,7 +1332,7 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void { switch (self.bin_file.tag) { .elf => { const elf_file = self.bin_file.cast(File.Elf).?; - const debug_info_sect = &elf_file.sections.items[elf_file.debug_info_section_index.?]; + const debug_info_sect = &elf_file.sections.items(.shdr)[elf_file.debug_info_section_index.?]; const file_pos = debug_info_sect.sh_offset + atom.off; try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false); }, @@ -1399,7 +1399,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void const elf_file = self.bin_file.cast(File.Elf).?; const shdr_index = elf_file.debug_info_section_index.?; try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true); - const debug_info_sect = elf_file.sections.items[shdr_index]; + const debug_info_sect = elf_file.sections.items(.shdr)[shdr_index]; const file_pos = debug_info_sect.sh_offset + atom.off; try pwriteDbgInfoNops( elf_file.base.file.?, @@ -1475,7 +1475,7 @@ pub fn updateDeclLineNumber(self: *Dwarf, decl: *const Module.Decl) !void { switch (self.bin_file.tag) { .elf => { const elf_file = self.bin_file.cast(File.Elf).?; - const shdr = elf_file.sections.items[elf_file.debug_line_section_index.?]; + const shdr = elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?]; const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff(); try elf_file.base.file.?.pwriteAll(&data, file_pos); }, @@ -1690,7 +1690,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void { const elf_file = self.bin_file.cast(File.Elf).?; const shdr_index = elf_file.debug_abbrev_section_index.?; try elf_file.growNonAllocSection(shdr_index, needed_size, 1, false); - const debug_abbrev_sect = elf_file.sections.items[shdr_index]; + const debug_abbrev_sect = elf_file.sections.items(.shdr)[shdr_index]; const file_pos = debug_abbrev_sect.sh_offset + abbrev_offset; try elf_file.base.file.?.pwriteAll(&abbrev_buf, file_pos); }, @@ -1805,7 +1805,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u switch (self.bin_file.tag) { .elf => { const elf_file = self.bin_file.cast(File.Elf).?; - const debug_info_sect = elf_file.sections.items[elf_file.debug_info_section_index.?]; + const debug_info_sect = elf_file.sections.items(.shdr)[elf_file.debug_info_section_index.?]; const file_pos = debug_info_sect.sh_offset; try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false); }, @@ -2124,7 +2124,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void { const elf_file = self.bin_file.cast(File.Elf).?; const shdr_index = elf_file.debug_aranges_section_index.?; try elf_file.growNonAllocSection(shdr_index, needed_size, 16, false); - const debug_aranges_sect = elf_file.sections.items[shdr_index]; + const debug_aranges_sect = elf_file.sections.items(.shdr)[shdr_index]; const file_pos = debug_aranges_sect.sh_offset; try elf_file.base.file.?.pwriteAll(di_buf.items, file_pos); }, @@ -2285,9 +2285,9 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { .elf => { const elf_file = self.bin_file.cast(File.Elf).?; const shdr_index = elf_file.debug_line_section_index.?; - const needed_size = elf_file.sections.items[shdr_index].sh_size + delta; + const needed_size = elf_file.sections.items(.shdr)[shdr_index].sh_size + delta; try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true); - const file_pos = elf_file.sections.items[shdr_index].sh_offset + src_fn.off; + const file_pos = elf_file.sections.items(.shdr)[shdr_index].sh_offset + src_fn.off; const amt = try elf_file.base.file.?.preadAll(buffer, file_pos); if (amt != buffer.len) return error.InputOutput; @@ -2346,7 +2346,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { switch (self.bin_file.tag) { .elf => { const elf_file = self.bin_file.cast(File.Elf).?; - const debug_line_sect = elf_file.sections.items[elf_file.debug_line_section_index.?]; + const debug_line_sect = elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?]; const file_pos = debug_line_sect.sh_offset; try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt); }, @@ -2487,7 +2487,7 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void { switch (self.bin_file.tag) { .elf => { const elf_file = self.bin_file.cast(File.Elf).?; - const debug_info_sect = &elf_file.sections.items[elf_file.debug_info_section_index.?]; + const debug_info_sect = &elf_file.sections.items(.shdr)[elf_file.debug_info_section_index.?]; break :blk debug_info_sect.sh_offset; }, .macho => { @@ -2638,7 +2638,7 @@ fn addDbgInfoErrorSet( fn getDbgInfoAtom(tag: File.Tag, mod: *Module, decl_index: Module.Decl.Index) *Atom { const decl = mod.declPtr(decl_index); return switch (tag) { - .elf => &decl.link.elf.dbg_info_atom, + .elf => unreachable, .macho => unreachable, .wasm => &decl.link.wasm.dbg_info_atom, else => unreachable, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 2c55e55f83226ecadd43ef7e0aee606c6f6bf25d..0b8128aa3308413498246533904835027be6c125 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1,43 +1,89 @@ const Elf = @This(); const std = @import("std"); +const build_options = @import("build_options"); const builtin = @import("builtin"); -const math = std.math; -const mem = std.mem; const assert = std.debug.assert; -const Allocator = std.mem.Allocator; -const fs = std.fs; const elf = std.elf; +const fs = std.fs; const log = std.log.scoped(.link); +const math = std.math; +const mem = std.mem; -const Atom = @import("Elf/Atom.zig"); -const Module = @import("../Module.zig"); -const Compilation = @import("../Compilation.zig"); -const Dwarf = @import("Dwarf.zig"); const codegen = @import("../codegen.zig"); -const lldMain = @import("../main.zig").lldMain; -const trace = @import("../tracy.zig").trace; -const Package = @import("../Package.zig"); -const Value = @import("../value.zig").Value; -const Type = @import("../type.zig").Type; -const TypedValue = @import("../TypedValue.zig"); -const link = @import("../link.zig"); -const File = link.File; -const build_options = @import("build_options"); -const target_util = @import("../target.zig"); const glibc = @import("../glibc.zig"); +const link = @import("../link.zig"); +const lldMain = @import("../main.zig").lldMain; const musl = @import("../musl.zig"); -const Cache = @import("../Cache.zig"); +const target_util = @import("../target.zig"); +const trace = @import("../tracy.zig").trace; + const Air = @import("../Air.zig"); +const Allocator = std.mem.Allocator; +pub const Atom = @import("Elf/Atom.zig"); +const Cache = @import("../Cache.zig"); +const Compilation = @import("../Compilation.zig"); +const Dwarf = @import("Dwarf.zig"); +const File = link.File; const Liveness = @import("../Liveness.zig"); const LlvmObject = @import("../codegen/llvm.zig").Object; - -pub const TextBlock = Atom; +const Module = @import("../Module.zig"); +const Package = @import("../Package.zig"); +const StringTable = @import("strtab.zig").StringTable; +const Type = @import("../type.zig").Type; +const TypedValue = @import("../TypedValue.zig"); +const Value = @import("../value.zig").Value; const default_entry_addr = 0x8000000; pub const base_tag: File.Tag = .elf; +const Section = struct { + shdr: elf.Elf64_Shdr, + phdr_index: u16, + + /// Index of the last allocated atom in this section. + last_atom_index: ?Atom.Index = null, + + /// A list of atoms that have surplus capacity. This list can have false + /// positives, as functions grow and shrink over time, only sometimes being added + /// or removed from the freelist. + /// + /// An atom has surplus capacity when its overcapacity value is greater than + /// padToIdeal(minimum_atom_size). That is, when it has so + /// much extra capacity, that we could fit a small new symbol in it, itself with + /// ideal_capacity or more. + /// + /// Ideal capacity is defined by size + (size / ideal_factor) + /// + /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that + /// overcapacity can be negative. A simple way to have negative overcapacity is to + /// allocate a fresh text block, which will have ideal capacity, and then grow it + /// by 1 byte. It will then have -1 overcapacity. + free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, +}; + +const DeclMetadata = struct { + atom: Atom.Index, + shdr: u16, + /// A list of all exports aliases of this Decl. + exports: std.ArrayListUnmanaged(u32) = .{}, + + fn getExport(m: DeclMetadata, elf_file: *const Elf, name: []const u8) ?u32 { + for (m.exports.items) |exp| { + if (mem.eql(u8, name, elf_file.getSymbolName(exp))) return exp; + } + return null; + } + + fn getExportPtr(m: *DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 { + for (m.exports.items) |*exp| { + if (mem.eql(u8, name, elf_file.getSymbolName(exp.*))) return exp; + } + return null; + } +}; + base: File, dwarf: ?Dwarf = null, @@ -48,12 +94,12 @@ llvm_object: ?*LlvmObject = null, /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. /// Same order as in the file. -sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){}, +sections: std.MultiArrayList(Section) = .{}, shdr_table_offset: ?u64 = null, /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. /// Same order as in the file. -program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){}, +program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{}, phdr_table_offset: ?u64 = null, /// The index into the program headers of a PT_LOAD program header with Read and Execute flags phdr_load_re_index: ?u16 = null, @@ -65,12 +111,10 @@ phdr_load_ro_index: ?u16 = null, /// The index into the program headers of a PT_LOAD program header with Write flag phdr_load_rw_index: ?u16 = null, -phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{}, - entry_addr: ?u64 = null, page_size: u32, -shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, +shstrtab: StringTable(.strtab) = .{}, shstrtab_index: ?u16 = null, symtab_section_index: ?u16 = null, @@ -113,39 +157,14 @@ debug_line_header_dirty: bool = false, error_flags: File.ErrorFlags = File.ErrorFlags{}, -/// Pointer to the last allocated atom -atoms: std.AutoHashMapUnmanaged(u16, *TextBlock) = .{}, - -/// A list of text blocks that have surplus capacity. This list can have false -/// positives, as functions grow and shrink over time, only sometimes being added -/// or removed from the freelist. -/// -/// A text block has surplus capacity when its overcapacity value is greater than -/// padToIdeal(minimum_text_block_size). That is, when it has so -/// much extra capacity, that we could fit a small new symbol in it, itself with -/// ideal_capacity or more. -/// -/// Ideal capacity is defined by size + (size / ideal_factor) -/// -/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that -/// overcapacity can be negative. A simple way to have negative overcapacity is to -/// allocate a fresh text block, which will have ideal capacity, and then grow it -/// by 1 byte. It will then have -1 overcapacity. -atom_free_lists: std.AutoHashMapUnmanaged(u16, std.ArrayListUnmanaged(*TextBlock)) = .{}, - -/// Table of Decls that are currently alive. -/// We store them here so that we can properly dispose of any allocated -/// memory within the atom in the incremental linker. -/// TODO consolidate this. -decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{}, +/// Table of tracked Decls. +decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, /// List of atoms that are owned directly by the linker. -/// Currently these are only atoms that are the result of linking -/// object files. Atoms which take part in incremental linking are -/// at present owned by Module.Decl. -/// TODO consolidate this. -managed_atoms: std.ArrayListUnmanaged(*TextBlock) = .{}, -atom_by_index_table: std.AutoHashMapUnmanaged(u32, *TextBlock) = .{}, +atoms: std.ArrayListUnmanaged(Atom) = .{}, + +/// Table of atoms indexed by the symbol index. +atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{}, /// Table of unnamed constants associated with a parent `Decl`. /// We store them here so that we can free the constants whenever the `Decl` @@ -173,15 +192,8 @@ unnamed_const_atoms: UnnamedConstTable = .{}, /// this will be a table indexed by index into the list of Atoms. relocs: RelocTable = .{}, -const Reloc = struct { - target: u32, - offset: u64, - addend: u32, - prev_vaddr: u64, -}; - -const RelocTable = std.AutoHashMapUnmanaged(*TextBlock, std.ArrayListUnmanaged(Reloc)); -const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*TextBlock)); +const RelocTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Reloc)); +const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); /// When allocating, the ideal_capacity is calculated by /// actual_capacity + (actual_capacity / ideal_factor) @@ -190,15 +202,11 @@ const ideal_factor = 3; /// In order for a slice of bytes to be considered eligible to keep metadata pointing at /// it as a possible place to put new symbols, it must have enough room for this many bytes /// (plus extra for reserved capacity). -const minimum_text_block_size = 64; -pub const min_text_capacity = padToIdeal(minimum_text_block_size); +const minimum_atom_size = 64; +pub const min_text_capacity = padToIdeal(minimum_atom_size); pub const PtrWidth = enum { p32, p64 }; -pub const Export = struct { - sym_index: ?u32 = null, -}; - pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf { assert(options.target.ofmt == .elf); @@ -230,16 +238,19 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option // There must always be a null section in index 0 try self.sections.append(allocator, .{ - .sh_name = 0, - .sh_type = elf.SHT_NULL, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = 0, - .sh_size = 0, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = 0, - .sh_entsize = 0, + .shdr = .{ + .sh_name = 0, + .sh_type = elf.SHT_NULL, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = 0, + .sh_size = 0, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = 0, + .sh_entsize = 0, + }, + .phdr_index = undefined, }); try self.populateMissingMetadata(); @@ -286,75 +297,67 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { } pub fn deinit(self: *Elf) void { + const gpa = self.base.allocator; + if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator); + if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa); } - self.sections.deinit(self.base.allocator); - self.program_headers.deinit(self.base.allocator); - self.shstrtab.deinit(self.base.allocator); - self.local_symbols.deinit(self.base.allocator); - self.global_symbols.deinit(self.base.allocator); - self.global_symbol_free_list.deinit(self.base.allocator); - self.local_symbol_free_list.deinit(self.base.allocator); - self.offset_table_free_list.deinit(self.base.allocator); - self.offset_table.deinit(self.base.allocator); - self.phdr_shdr_table.deinit(self.base.allocator); - self.decls.deinit(self.base.allocator); + for (self.sections.items(.free_list)) |*free_list| { + free_list.deinit(gpa); + } + self.sections.deinit(gpa); + + self.program_headers.deinit(gpa); + self.shstrtab.deinit(gpa); + self.local_symbols.deinit(gpa); + self.global_symbols.deinit(gpa); + self.global_symbol_free_list.deinit(gpa); + self.local_symbol_free_list.deinit(gpa); + self.offset_table_free_list.deinit(gpa); + self.offset_table.deinit(gpa); - self.atoms.deinit(self.base.allocator); { - var it = self.atom_free_lists.valueIterator(); - while (it.next()) |free_list| { - free_list.deinit(self.base.allocator); + var it = self.decls.iterator(); + while (it.next()) |entry| { + entry.value_ptr.exports.deinit(gpa); } - self.atom_free_lists.deinit(self.base.allocator); + self.decls.deinit(gpa); } - for (self.managed_atoms.items) |atom| { - self.base.allocator.destroy(atom); - } - self.managed_atoms.deinit(self.base.allocator); + self.atoms.deinit(gpa); + self.atom_by_index_table.deinit(gpa); { var it = self.unnamed_const_atoms.valueIterator(); while (it.next()) |atoms| { - atoms.deinit(self.base.allocator); + atoms.deinit(gpa); } - self.unnamed_const_atoms.deinit(self.base.allocator); + self.unnamed_const_atoms.deinit(gpa); } { var it = self.relocs.valueIterator(); while (it.next()) |relocs| { - relocs.deinit(self.base.allocator); + relocs.deinit(gpa); } - self.relocs.deinit(self.base.allocator); + self.relocs.deinit(gpa); } - self.atom_by_index_table.deinit(self.base.allocator); - - if (self.dwarf) |*dw| { - dw.deinit(); - } + // if (self.dwarf) |*dw| { + // dw.deinit(); + // } } pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 { - const mod = self.base.options.module.?; - const decl = mod.declPtr(decl_index); - assert(self.llvm_object == null); - try decl.link.elf.ensureInitialized(self); - const target = decl.link.elf.getSymbolIndex().?; - - const vaddr = self.local_symbols.items[target].st_value; - const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?; - const gop = try self.relocs.getOrPut(self.base.allocator, atom); - if (!gop.found_existing) { - gop.value_ptr.* = .{}; - } - try gop.value_ptr.append(self.base.allocator, .{ + const this_atom_index = try self.getOrCreateAtomForDecl(decl_index); + const this_atom = self.getAtom(this_atom_index); + const target = this_atom.getSymbolIndex().?; + const vaddr = this_atom.getSymbol(self).st_value; + const atom_index = self.getAtomIndexForSymbol(reloc_info.parent_atom_index).?; + try Atom.addRelocation(self, atom_index, .{ .target = target, .offset = reloc_info.offset, .addend = reloc_info.addend, @@ -375,7 +378,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { if (self.shdr_table_offset) |off| { const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr); - const tight_size = self.sections.items.len * shdr_size; + const tight_size = self.sections.slice().len * shdr_size; const increased_size = padToIdeal(tight_size); const test_end = off + increased_size; if (end > off and start < test_end) { @@ -385,7 +388,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { if (self.phdr_table_offset) |off| { const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr); - const tight_size = self.sections.items.len * phdr_size; + const tight_size = self.sections.slice().len * phdr_size; const increased_size = padToIdeal(tight_size); const test_end = off + increased_size; if (end > off and start < test_end) { @@ -393,7 +396,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { } } - for (self.sections.items) |section| { + for (self.sections.items(.shdr)) |section| { const increased_size = padToIdeal(section.sh_size); const test_end = section.sh_offset + increased_size; if (end > section.sh_offset and start < test_end) { @@ -420,7 +423,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 { if (self.phdr_table_offset) |off| { if (off > start and off < min_pos) min_pos = off; } - for (self.sections.items) |section| { + for (self.sections.items(.shdr)) |section| { if (section.sh_offset <= start) continue; if (section.sh_offset < min_pos) min_pos = section.sh_offset; } @@ -439,31 +442,10 @@ pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 { return start; } -/// TODO Improve this to use a table. -fn makeString(self: *Elf, bytes: []const u8) !u32 { - try self.shstrtab.ensureUnusedCapacity(self.base.allocator, bytes.len + 1); - const result = self.shstrtab.items.len; - self.shstrtab.appendSliceAssumeCapacity(bytes); - self.shstrtab.appendAssumeCapacity(0); - return @intCast(u32, result); -} - -pub fn getString(self: Elf, str_off: u32) []const u8 { - assert(str_off < self.shstrtab.items.len); - return mem.sliceTo(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off), 0); -} - -fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 { - const existing_name = self.getString(old_str_off); - if (mem.eql(u8, existing_name, new_name)) { - return old_str_off; - } - return self.makeString(new_name); -} - pub fn populateMissingMetadata(self: *Elf) !void { assert(self.llvm_object == null); + const gpa = self.base.allocator; const small_ptr = switch (self.ptr_width) { .p32 => true, .p64 => false, @@ -477,7 +459,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { const off = self.findFreeSpace(file_size, p_align); log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size }); const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr; - try self.program_headers.append(self.base.allocator, .{ + try self.program_headers.append(gpa, .{ .p_type = elf.PT_LOAD, .p_offset = off, .p_filesz = file_size, @@ -487,7 +469,6 @@ pub fn populateMissingMetadata(self: *Elf) !void { .p_align = p_align, .p_flags = elf.PF_X | elf.PF_R, }); - try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_re_index.?, .{}); self.entry_addr = null; self.phdr_table_dirty = true; } @@ -504,7 +485,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { // we'll need to re-use that function anyway, in case the GOT grows and overlaps something // else in virtual memory. const got_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x4000000 else 0x8000; - try self.program_headers.append(self.base.allocator, .{ + try self.program_headers.append(gpa, .{ .p_type = elf.PT_LOAD, .p_offset = off, .p_filesz = file_size, @@ -527,7 +508,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}", .{ off, off + file_size }); // TODO Same as for GOT const rodata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0xc000000 else 0xa000; - try self.program_headers.append(self.base.allocator, .{ + try self.program_headers.append(gpa, .{ .p_type = elf.PT_LOAD, .p_offset = off, .p_filesz = file_size, @@ -537,7 +518,6 @@ pub fn populateMissingMetadata(self: *Elf) !void { .p_align = p_align, .p_flags = elf.PF_R, }); - try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_ro_index.?, .{}); self.phdr_table_dirty = true; } @@ -551,7 +531,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}", .{ off, off + file_size }); // TODO Same as for GOT const rwdata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x10000000 else 0xc000; - try self.program_headers.append(self.base.allocator, .{ + try self.program_headers.append(gpa, .{ .p_type = elf.PT_LOAD, .p_offset = off, .p_filesz = file_size, @@ -561,278 +541,290 @@ pub fn populateMissingMetadata(self: *Elf) !void { .p_align = p_align, .p_flags = elf.PF_R | elf.PF_W, }); - try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_rw_index.?, .{}); self.phdr_table_dirty = true; } if (self.shstrtab_index == null) { - self.shstrtab_index = @intCast(u16, self.sections.items.len); - assert(self.shstrtab.items.len == 0); - try self.shstrtab.append(self.base.allocator, 0); // need a 0 at position 0 - const off = self.findFreeSpace(self.shstrtab.items.len, 1); - log.debug("found shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.items.len }); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".shstrtab"), - .sh_type = elf.SHT_STRTAB, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = self.shstrtab.items.len, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = 1, - .sh_entsize = 0, + self.shstrtab_index = @intCast(u16, self.sections.slice().len); + assert(self.shstrtab.buffer.items.len == 0); + try self.shstrtab.buffer.append(gpa, 0); // need a 0 at position 0 + const off = self.findFreeSpace(self.shstrtab.buffer.items.len, 1); + log.debug("found shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.buffer.items.len }); + try self.sections.append(gpa, .{ + .shdr = .{ + .sh_name = try self.shstrtab.insert(gpa, ".shstrtab"), + .sh_type = elf.SHT_STRTAB, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = self.shstrtab.buffer.items.len, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = 1, + .sh_entsize = 0, + }, + .phdr_index = undefined, }); self.shstrtab_dirty = true; self.shdr_table_dirty = true; } if (self.text_section_index == null) { - self.text_section_index = @intCast(u16, self.sections.items.len); + self.text_section_index = @intCast(u16, self.sections.slice().len); const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".text"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, - .sh_addr = phdr.p_vaddr, - .sh_offset = phdr.p_offset, - .sh_size = phdr.p_filesz, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = 1, - .sh_entsize = 0, + try self.sections.append(gpa, .{ + .shdr = .{ + .sh_name = try self.shstrtab.insert(gpa, ".text"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, + .sh_addr = phdr.p_vaddr, + .sh_offset = phdr.p_offset, + .sh_size = phdr.p_filesz, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = 1, + .sh_entsize = 0, + }, + .phdr_index = self.phdr_load_re_index.?, }); - try self.phdr_shdr_table.putNoClobber( - self.base.allocator, - self.phdr_load_re_index.?, - self.text_section_index.?, - ); self.shdr_table_dirty = true; } if (self.got_section_index == null) { - self.got_section_index = @intCast(u16, self.sections.items.len); + self.got_section_index = @intCast(u16, self.sections.slice().len); const phdr = &self.program_headers.items[self.phdr_got_index.?]; - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".got"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = elf.SHF_ALLOC, - .sh_addr = phdr.p_vaddr, - .sh_offset = phdr.p_offset, - .sh_size = phdr.p_filesz, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = @as(u16, ptr_size), - .sh_entsize = 0, + try self.sections.append(gpa, .{ + .shdr = .{ + .sh_name = try self.shstrtab.insert(gpa, ".got"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = elf.SHF_ALLOC, + .sh_addr = phdr.p_vaddr, + .sh_offset = phdr.p_offset, + .sh_size = phdr.p_filesz, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = @as(u16, ptr_size), + .sh_entsize = 0, + }, + .phdr_index = self.phdr_got_index.?, }); - try self.phdr_shdr_table.putNoClobber( - self.base.allocator, - self.phdr_got_index.?, - self.got_section_index.?, - ); self.shdr_table_dirty = true; } if (self.rodata_section_index == null) { - self.rodata_section_index = @intCast(u16, self.sections.items.len); + self.rodata_section_index = @intCast(u16, self.sections.slice().len); const phdr = &self.program_headers.items[self.phdr_load_ro_index.?]; - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".rodata"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = elf.SHF_ALLOC, - .sh_addr = phdr.p_vaddr, - .sh_offset = phdr.p_offset, - .sh_size = phdr.p_filesz, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = 1, - .sh_entsize = 0, + try self.sections.append(gpa, .{ + .shdr = .{ + .sh_name = try self.shstrtab.insert(gpa, ".rodata"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = elf.SHF_ALLOC, + .sh_addr = phdr.p_vaddr, + .sh_offset = phdr.p_offset, + .sh_size = phdr.p_filesz, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = 1, + .sh_entsize = 0, + }, + .phdr_index = self.phdr_load_ro_index.?, }); - try self.phdr_shdr_table.putNoClobber( - self.base.allocator, - self.phdr_load_ro_index.?, - self.rodata_section_index.?, - ); self.shdr_table_dirty = true; } if (self.data_section_index == null) { - self.data_section_index = @intCast(u16, self.sections.items.len); + self.data_section_index = @intCast(u16, self.sections.slice().len); const phdr = &self.program_headers.items[self.phdr_load_rw_index.?]; - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".data"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC, - .sh_addr = phdr.p_vaddr, - .sh_offset = phdr.p_offset, - .sh_size = phdr.p_filesz, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = @as(u16, ptr_size), - .sh_entsize = 0, + try self.sections.append(gpa, .{ + .shdr = .{ + .sh_name = try self.shstrtab.insert(gpa, ".data"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC, + .sh_addr = phdr.p_vaddr, + .sh_offset = phdr.p_offset, + .sh_size = phdr.p_filesz, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = @as(u16, ptr_size), + .sh_entsize = 0, + }, + .phdr_index = self.phdr_load_rw_index.?, }); - try self.phdr_shdr_table.putNoClobber( - self.base.allocator, - self.phdr_load_rw_index.?, - self.data_section_index.?, - ); self.shdr_table_dirty = true; } if (self.symtab_section_index == null) { - self.symtab_section_index = @intCast(u16, self.sections.items.len); + self.symtab_section_index = @intCast(u16, self.sections.slice().len); const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); const file_size = self.base.options.symbol_count_hint * each_size; const off = self.findFreeSpace(file_size, min_align); log.debug("found symtab free space 0x{x} to 0x{x}", .{ off, off + file_size }); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".symtab"), - .sh_type = elf.SHT_SYMTAB, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size, - // The section header index of the associated string table. - .sh_link = self.shstrtab_index.?, - .sh_info = @intCast(u32, self.local_symbols.items.len), - .sh_addralign = min_align, - .sh_entsize = each_size, + try self.sections.append(gpa, .{ + .shdr = .{ + .sh_name = try self.shstrtab.insert(gpa, ".symtab"), + .sh_type = elf.SHT_SYMTAB, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = file_size, + // The section header index of the associated string table. + .sh_link = self.shstrtab_index.?, + .sh_info = @intCast(u32, self.local_symbols.items.len), + .sh_addralign = min_align, + .sh_entsize = each_size, + }, + .phdr_index = undefined, }); self.shdr_table_dirty = true; try self.writeSymbol(0); } - if (self.dwarf) |*dw| { - if (self.debug_str_section_index == null) { - self.debug_str_section_index = @intCast(u16, self.sections.items.len); - assert(dw.strtab.items.len == 0); - try dw.strtab.append(self.base.allocator, 0); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".debug_str"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, - .sh_addr = 0, - .sh_offset = 0, - .sh_size = 0, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = 1, - .sh_entsize = 1, - }); - self.debug_strtab_dirty = true; - self.shdr_table_dirty = true; - } + // if (self.dwarf) |*dw| { + // if (self.debug_str_section_index == null) { + // self.debug_str_section_index = @intCast(u16, self.sections.slice().len); + // assert(dw.strtab.items.len == 0); + // try dw.strtab.append(gpa, 0); + // try self.sections.append(gpa, .{ + // .shdr = .{ + // .sh_name = try self.shstrtab.insert(gpa, ".debug_str"), + // .sh_type = elf.SHT_PROGBITS, + // .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, + // .sh_addr = 0, + // .sh_offset = 0, + // .sh_size = 0, + // .sh_link = 0, + // .sh_info = 0, + // .sh_addralign = 1, + // .sh_entsize = 1, + // }, + // .phdr_index = undefined, + // }); + // self.debug_strtab_dirty = true; + // self.shdr_table_dirty = true; + // } - if (self.debug_info_section_index == null) { - self.debug_info_section_index = @intCast(u16, self.sections.items.len); + // if (self.debug_info_section_index == null) { + // self.debug_info_section_index = @intCast(u16, self.sections.slice().len); - const file_size_hint = 200; - const p_align = 1; - const off = self.findFreeSpace(file_size_hint, p_align); - log.debug("found .debug_info free space 0x{x} to 0x{x}", .{ - off, - off + file_size_hint, - }); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".debug_info"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }); - self.shdr_table_dirty = true; - self.debug_info_header_dirty = true; - } + // const file_size_hint = 200; + // const p_align = 1; + // const off = self.findFreeSpace(file_size_hint, p_align); + // log.debug("found .debug_info free space 0x{x} to 0x{x}", .{ + // off, + // off + file_size_hint, + // }); + // try self.sections.append(gpa, .{ + // .shdr = .{ + // .sh_name = try self.shstrtab.insert(gpa, ".debug_info"), + // .sh_type = elf.SHT_PROGBITS, + // .sh_flags = 0, + // .sh_addr = 0, + // .sh_offset = off, + // .sh_size = file_size_hint, + // .sh_link = 0, + // .sh_info = 0, + // .sh_addralign = p_align, + // .sh_entsize = 0, + // }, + // .phdr_index = undefined, + // }); + // self.shdr_table_dirty = true; + // self.debug_info_header_dirty = true; + // } - if (self.debug_abbrev_section_index == null) { - self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len); + // if (self.debug_abbrev_section_index == null) { + // self.debug_abbrev_section_index = @intCast(u16, self.sections.slice().len); - const file_size_hint = 128; - const p_align = 1; - const off = self.findFreeSpace(file_size_hint, p_align); - log.debug("found .debug_abbrev free space 0x{x} to 0x{x}", .{ - off, - off + file_size_hint, - }); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".debug_abbrev"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }); - self.shdr_table_dirty = true; - self.debug_abbrev_section_dirty = true; - } + // const file_size_hint = 128; + // const p_align = 1; + // const off = self.findFreeSpace(file_size_hint, p_align); + // log.debug("found .debug_abbrev free space 0x{x} to 0x{x}", .{ + // off, + // off + file_size_hint, + // }); + // try self.sections.append(gpa, .{ + // .shdr = .{ + // .sh_name = try self.shstrtab.insert(gpa, ".debug_abbrev"), + // .sh_type = elf.SHT_PROGBITS, + // .sh_flags = 0, + // .sh_addr = 0, + // .sh_offset = off, + // .sh_size = file_size_hint, + // .sh_link = 0, + // .sh_info = 0, + // .sh_addralign = p_align, + // .sh_entsize = 0, + // }, + // .phdr_index = undefined, + // }); + // self.shdr_table_dirty = true; + // self.debug_abbrev_section_dirty = true; + // } - if (self.debug_aranges_section_index == null) { - self.debug_aranges_section_index = @intCast(u16, self.sections.items.len); + // if (self.debug_aranges_section_index == null) { + // self.debug_aranges_section_index = @intCast(u16, self.sections.slice().len); - const file_size_hint = 160; - const p_align = 16; - const off = self.findFreeSpace(file_size_hint, p_align); - log.debug("found .debug_aranges free space 0x{x} to 0x{x}", .{ - off, - off + file_size_hint, - }); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".debug_aranges"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }); - self.shdr_table_dirty = true; - self.debug_aranges_section_dirty = true; - } + // const file_size_hint = 160; + // const p_align = 16; + // const off = self.findFreeSpace(file_size_hint, p_align); + // log.debug("found .debug_aranges free space 0x{x} to 0x{x}", .{ + // off, + // off + file_size_hint, + // }); + // try self.sections.append(gpa, .{ + // .shdr = .{ + // .sh_name = try self.shstrtab.insert(gpa, ".debug_aranges"), + // .sh_type = elf.SHT_PROGBITS, + // .sh_flags = 0, + // .sh_addr = 0, + // .sh_offset = off, + // .sh_size = file_size_hint, + // .sh_link = 0, + // .sh_info = 0, + // .sh_addralign = p_align, + // .sh_entsize = 0, + // }, + // .phdr_index = undefined, + // }); + // self.shdr_table_dirty = true; + // self.debug_aranges_section_dirty = true; + // } - if (self.debug_line_section_index == null) { - self.debug_line_section_index = @intCast(u16, self.sections.items.len); + // if (self.debug_line_section_index == null) { + // self.debug_line_section_index = @intCast(u16, self.sections.slice().len); - const file_size_hint = 250; - const p_align = 1; - const off = self.findFreeSpace(file_size_hint, p_align); - log.debug("found .debug_line free space 0x{x} to 0x{x}", .{ - off, - off + file_size_hint, - }); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".debug_line"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }); - self.shdr_table_dirty = true; - self.debug_line_header_dirty = true; - } - } + // const file_size_hint = 250; + // const p_align = 1; + // const off = self.findFreeSpace(file_size_hint, p_align); + // log.debug("found .debug_line free space 0x{x} to 0x{x}", .{ + // off, + // off + file_size_hint, + // }); + // try self.sections.append(gpa, .{ + // .shdr = .{ + // .sh_name = try self.shstrtab.insert(gpa, ".debug_line"), + // .sh_type = elf.SHT_PROGBITS, + // .sh_flags = 0, + // .sh_addr = 0, + // .sh_offset = off, + // .sh_size = file_size_hint, + // .sh_link = 0, + // .sh_info = 0, + // .sh_addralign = p_align, + // .sh_entsize = 0, + // }, + // .phdr_index = undefined, + // }); + // self.shdr_table_dirty = true; + // self.debug_line_header_dirty = true; + // } + // } const shsize: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Shdr), @@ -843,7 +835,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { .p64 => @alignOf(elf.Elf64_Shdr), }; if (self.shdr_table_offset == null) { - self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign); + self.shdr_table_offset = self.findFreeSpace(self.sections.slice().len * shsize, shalign); self.shdr_table_dirty = true; } @@ -874,7 +866,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { // offset + it's filesize. var max_file_offset: u64 = 0; - for (self.sections.items) |shdr| { + for (self.sections.items(.shdr)) |shdr| { if (shdr.sh_offset + shdr.sh_size > max_file_offset) { max_file_offset = shdr.sh_offset + shdr.sh_size; } @@ -884,15 +876,18 @@ pub fn populateMissingMetadata(self: *Elf) !void { } } -fn growAllocSection(self: *Elf, shdr_index: u16, phdr_index: u16, needed_size: u64) !void { +fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { // TODO Also detect virtual address collisions. - const shdr = &self.sections.items[shdr_index]; + const shdr = &self.sections.items(.shdr)[shdr_index]; + const phdr_index = self.sections.items(.phdr_index)[shdr_index]; const phdr = &self.program_headers.items[phdr_index]; + const maybe_last_atom_index = self.sections.items(.last_atom_index)[shdr_index]; if (needed_size > self.allocatedSize(shdr.sh_offset)) { // Must move the entire section. const new_offset = self.findFreeSpace(needed_size, self.page_size); - const existing_size = if (self.atoms.get(phdr_index)) |last| blk: { + const existing_size = if (maybe_last_atom_index) |last_atom_index| blk: { + const last = self.getAtom(last_atom_index); const sym = last.getSymbol(self); break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr; } else if (shdr_index == self.got_section_index.?) blk: { @@ -900,8 +895,8 @@ fn growAllocSection(self: *Elf, shdr_index: u16, phdr_index: u16, needed_size: u } else 0; shdr.sh_size = 0; - log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{ - self.getString(shdr.sh_name), + log.debug("new '{?s}' file offset 0x{x} to 0x{x}", .{ + self.shstrtab.get(shdr.sh_name), new_offset, new_offset + existing_size, }); @@ -927,7 +922,7 @@ pub fn growNonAllocSection( min_alignment: u32, requires_file_copy: bool, ) !void { - const shdr = &self.sections.items[shdr_index]; + const shdr = &self.sections.items(.shdr)[shdr_index]; if (needed_size > self.allocatedSize(shdr.sh_offset)) { const existing_size = if (self.symtab_section_index.? == shdr_index) blk: { @@ -940,7 +935,7 @@ pub fn growNonAllocSection( shdr.sh_size = 0; // Move all the symbols to a new file location. const new_offset = self.findFreeSpace(needed_size, min_alignment); - log.debug("moving '{s}' from 0x{x} to 0x{x}", .{ self.getString(shdr.sh_name), shdr.sh_offset, new_offset }); + log.debug("moving '{?s}' from 0x{x} to 0x{x}", .{ self.shstrtab.get(shdr.sh_name), shdr.sh_offset, new_offset }); if (requires_file_copy) { const amt = try self.base.file.?.copyRangeAll( @@ -961,25 +956,26 @@ pub fn growNonAllocSection( } pub fn markDirty(self: *Elf, shdr_index: u16, phdr_index: ?u16) void { + _ = shdr_index; self.shdr_table_dirty = true; // TODO look into only writing one section if (phdr_index) |_| { self.phdr_table_dirty = true; // TODO look into making only the one program header dirty } - if (self.dwarf) |_| { - if (self.debug_info_section_index.? == shdr_index) { - self.debug_info_header_dirty = true; - } else if (self.debug_line_section_index.? == shdr_index) { - self.debug_line_header_dirty = true; - } else if (self.debug_abbrev_section_index.? == shdr_index) { - self.debug_abbrev_section_dirty = true; - } else if (self.debug_str_section_index.? == shdr_index) { - self.debug_strtab_dirty = true; - } else if (self.debug_aranges_section_index.? == shdr_index) { - self.debug_aranges_section_dirty = true; - } - } + // if (self.dwarf) |_| { + // if (self.debug_info_section_index.? == shdr_index) { + // self.debug_info_header_dirty = true; + // } else if (self.debug_line_section_index.? == shdr_index) { + // self.debug_line_header_dirty = true; + // } else if (self.debug_abbrev_section_index.? == shdr_index) { + // self.debug_abbrev_section_dirty = true; + // } else if (self.debug_str_section_index.? == shdr_index) { + // self.debug_strtab_dirty = true; + // } else if (self.debug_aranges_section_index.? == shdr_index) { + // self.debug_aranges_section_dirty = true; + // } + // } } pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { @@ -1011,6 +1007,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } } + const gpa = self.base.allocator; var sub_prog_node = prog_node.start("ELF Flush", 0); sub_prog_node.activate(); defer sub_prog_node.end(); @@ -1018,23 +1015,25 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // TODO This linker code currently assumes there is only 1 compilation unit and it // corresponds to the Zig source code. const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + _ = module; const target_endian = self.base.options.target.cpu.arch.endian(); const foreign_endian = target_endian != builtin.cpu.arch.endian(); - if (self.dwarf) |*dw| { - try dw.flushModule(module); - } + // if (self.dwarf) |*dw| { + // try dw.flushModule(module); + // } { var it = self.relocs.iterator(); while (it.next()) |entry| { - const atom = entry.key_ptr.*; + const atom_index = entry.key_ptr.*; const relocs = entry.value_ptr.*; + const atom = self.getAtom(atom_index); const source_sym = atom.getSymbol(self); - const source_shdr = self.sections.items[source_sym.st_shndx]; + const source_shdr = self.sections.items(.shdr)[source_sym.st_shndx]; - log.debug("relocating '{s}'", .{self.getString(source_sym.st_name)}); + log.debug("relocating '{?s}'", .{self.shstrtab.get(source_sym.st_name)}); for (relocs.items) |*reloc| { const target_sym = self.local_symbols.items[reloc.target]; @@ -1045,10 +1044,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr; const file_offset = source_shdr.sh_offset + section_offset; - log.debug(" ({x}: [() => 0x{x}] ({s}))", .{ + log.debug(" ({x}: [() => 0x{x}] ({?s}))", .{ reloc.offset, target_vaddr, - self.getString(target_sym.st_name), + self.shstrtab.get(target_sym.st_name), }); switch (self.ptr_width) { @@ -1069,43 +1068,43 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node self.logSymtab(); } - if (self.dwarf) |*dw| { - if (self.debug_abbrev_section_dirty) { - try dw.writeDbgAbbrev(); - if (!self.shdr_table_dirty) { - // Then it won't get written with the others and we need to do it. - try self.writeSectHeader(self.debug_abbrev_section_index.?); - } - self.debug_abbrev_section_dirty = false; - } + // if (self.dwarf) |*dw| { + // if (self.debug_abbrev_section_dirty) { + // try dw.writeDbgAbbrev(); + // if (!self.shdr_table_dirty) { + // // Then it won't get written with the others and we need to do it. + // try self.writeSectHeader(self.debug_abbrev_section_index.?); + // } + // self.debug_abbrev_section_dirty = false; + // } - if (self.debug_info_header_dirty) { - // Currently only one compilation unit is supported, so the address range is simply - // identical to the main program header virtual address and memory size. - const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; - const low_pc = text_phdr.p_vaddr; - const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; - try dw.writeDbgInfoHeader(module, low_pc, high_pc); - self.debug_info_header_dirty = false; - } + // if (self.debug_info_header_dirty) { + // // Currently only one compilation unit is supported, so the address range is simply + // // identical to the main program header virtual address and memory size. + // const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; + // const low_pc = text_phdr.p_vaddr; + // const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; + // try dw.writeDbgInfoHeader(module, low_pc, high_pc); + // self.debug_info_header_dirty = false; + // } - if (self.debug_aranges_section_dirty) { - // Currently only one compilation unit is supported, so the address range is simply - // identical to the main program header virtual address and memory size. - const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; - try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz); - if (!self.shdr_table_dirty) { - // Then it won't get written with the others and we need to do it. - try self.writeSectHeader(self.debug_aranges_section_index.?); - } - self.debug_aranges_section_dirty = false; - } + // if (self.debug_aranges_section_dirty) { + // // Currently only one compilation unit is supported, so the address range is simply + // // identical to the main program header virtual address and memory size. + // const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; + // try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz); + // if (!self.shdr_table_dirty) { + // // Then it won't get written with the others and we need to do it. + // try self.writeSectHeader(self.debug_aranges_section_index.?); + // } + // self.debug_aranges_section_dirty = false; + // } - if (self.debug_line_header_dirty) { - try dw.writeDbgLineHeader(); - self.debug_line_header_dirty = false; - } - } + // if (self.debug_line_header_dirty) { + // try dw.writeDbgLineHeader(); + // self.debug_line_header_dirty = false; + // } + // } if (self.phdr_table_dirty) { const phsize: u64 = switch (self.ptr_width) { @@ -1126,8 +1125,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node switch (self.ptr_width) { .p32 => { - const buf = try self.base.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len); - defer self.base.allocator.free(buf); + const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len); + defer gpa.free(buf); for (buf) |*phdr, i| { phdr.* = progHeaderTo32(self.program_headers.items[i]); @@ -1138,8 +1137,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); }, .p64 => { - const buf = try self.base.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len); - defer self.base.allocator.free(buf); + const buf = try gpa.alloc(elf.Elf64_Phdr, self.program_headers.items.len); + defer gpa.free(buf); for (buf) |*phdr, i| { phdr.* = self.program_headers.items[i]; @@ -1155,23 +1154,23 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node { const shdr_index = self.shstrtab_index.?; - if (self.shstrtab_dirty or self.shstrtab.items.len != self.sections.items[shdr_index].sh_size) { - try self.growNonAllocSection(shdr_index, self.shstrtab.items.len, 1, false); - const shstrtab_sect = self.sections.items[shdr_index]; - try self.base.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset); + if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { + try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false); + const shstrtab_sect = self.sections.items(.shdr)[shdr_index]; + try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shstrtab_sect.sh_offset); self.shstrtab_dirty = false; } } - if (self.dwarf) |dwarf| { - const shdr_index = self.debug_str_section_index.?; - if (self.debug_strtab_dirty or dwarf.strtab.items.len != self.sections.items[shdr_index].sh_size) { - try self.growNonAllocSection(shdr_index, dwarf.strtab.items.len, 1, false); - const debug_strtab_sect = self.sections.items[shdr_index]; - try self.base.file.?.pwriteAll(dwarf.strtab.items, debug_strtab_sect.sh_offset); - self.debug_strtab_dirty = false; - } - } + // if (self.dwarf) |dwarf| { + // const shdr_index = self.debug_str_section_index.?; + // if (self.debug_strtab_dirty or dwarf.strtab.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { + // try self.growNonAllocSection(shdr_index, dwarf.strtab.items.len, 1, false); + // const debug_strtab_sect = self.sections.items(.shdr)[shdr_index]; + // try self.base.file.?.pwriteAll(dwarf.strtab.items, debug_strtab_sect.sh_offset); + // self.debug_strtab_dirty = false; + // } + // } if (self.shdr_table_dirty) { const shsize: u64 = switch (self.ptr_width) { @@ -1183,7 +1182,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node .p64 => @alignOf(elf.Elf64_Shdr), }; const allocated_size = self.allocatedSize(self.shdr_table_offset.?); - const needed_size = self.sections.items.len * shsize; + const needed_size = self.sections.slice().len * shsize; if (needed_size > allocated_size) { self.shdr_table_offset = null; // free the space @@ -1192,12 +1191,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node switch (self.ptr_width) { .p32 => { - const buf = try self.base.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len); - defer self.base.allocator.free(buf); + const slice = self.sections.slice(); + const buf = try gpa.alloc(elf.Elf32_Shdr, slice.len); + defer gpa.free(buf); for (buf) |*shdr, i| { - shdr.* = sectHeaderTo32(self.sections.items[i]); - log.debug("writing section {s}: {}", .{ self.getString(shdr.sh_name), shdr.* }); + shdr.* = sectHeaderTo32(slice.items(.shdr)[i]); + log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Shdr, shdr); } @@ -1205,12 +1205,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); }, .p64 => { - const buf = try self.base.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len); - defer self.base.allocator.free(buf); + const slice = self.sections.slice(); + const buf = try gpa.alloc(elf.Elf64_Shdr, slice.len); + defer gpa.free(buf); for (buf) |*shdr, i| { - shdr.* = self.sections.items[i]; - log.debug("writing section {s}: {}", .{ self.getString(shdr.sh_name), shdr.* }); + shdr.* = slice.items(.shdr)[i]; + log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf64_Shdr, shdr); } @@ -2021,7 +2022,7 @@ fn writeElfHeader(self: *Elf) !void { mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian); index += 2; - const e_shnum = @intCast(u16, self.sections.items.len); + const e_shnum = @intCast(u16, self.sections.slice().len); mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian); index += 2; @@ -2033,124 +2034,150 @@ fn writeElfHeader(self: *Elf) !void { try self.base.file.?.pwriteAll(hdr_buf[0..index], 0); } -fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void { - const local_sym = text_block.getSymbol(self); - const name_str_index = local_sym.st_name; - const name = self.getString(name_str_index); - log.debug("freeTextBlock {*} ({s})", .{ text_block, name }); +fn freeAtom(self: *Elf, atom_index: Atom.Index) void { + const atom = self.getAtom(atom_index); + log.debug("freeAtom {d} ({s})", .{ atom_index, atom.getName(self) }); - self.freeRelocationsForTextBlock(text_block); + Atom.freeRelocations(self, atom_index); - const free_list = self.atom_free_lists.getPtr(phdr_index).?; + const gpa = self.base.allocator; + const shndx = atom.getSymbol(self).st_shndx; + const free_list = &self.sections.items(.free_list)[shndx]; var already_have_free_list_node = false; { var i: usize = 0; // TODO turn free_list into a hash map while (i < free_list.items.len) { - if (free_list.items[i] == text_block) { + if (free_list.items[i] == atom_index) { _ = free_list.swapRemove(i); continue; } - if (free_list.items[i] == text_block.prev) { + if (free_list.items[i] == atom.prev_index) { already_have_free_list_node = true; } i += 1; } } - if (self.atoms.getPtr(phdr_index)) |last_block| { - if (last_block.* == text_block) { - if (text_block.prev) |prev| { + const maybe_last_atom_index = &self.sections.items(.last_atom_index)[shndx]; + if (maybe_last_atom_index.*) |last_atom_index| { + if (last_atom_index == atom_index) { + if (atom.prev_index) |prev_index| { // TODO shrink the section size here - last_block.* = prev; + maybe_last_atom_index.* = prev_index; } else { - _ = self.atoms.fetchRemove(phdr_index); + maybe_last_atom_index.* = null; } } } - if (text_block.prev) |prev| { - prev.next = text_block.next; + if (atom.prev_index) |prev_index| { + const prev = self.getAtomPtr(prev_index); + prev.next_index = atom.next_index; - if (!already_have_free_list_node and prev.freeListEligible(self)) { + if (!already_have_free_list_node and prev.*.freeListEligible(self)) { // The free list is heuristics, it doesn't have to be perfect, so we can // ignore the OOM here. - free_list.append(self.base.allocator, prev) catch {}; + free_list.append(gpa, prev_index) catch {}; } } else { - text_block.prev = null; + self.getAtomPtr(atom_index).prev_index = null; } - if (text_block.next) |next| { - next.prev = text_block.prev; + if (atom.next_index) |next_index| { + self.getAtomPtr(next_index).prev_index = atom.prev_index; } else { - text_block.next = null; + self.getAtomPtr(atom_index).next_index = null; } // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. - const local_sym_index = text_block.getSymbolIndex().?; - self.local_symbol_free_list.append(self.base.allocator, local_sym_index) catch {}; + const local_sym_index = atom.getSymbolIndex().?; + + self.local_symbol_free_list.append(gpa, local_sym_index) catch {}; self.local_symbols.items[local_sym_index].st_info = 0; + self.local_symbols.items[local_sym_index].st_shndx = 0; _ = self.atom_by_index_table.remove(local_sym_index); - text_block.local_sym_index = 0; + self.getAtomPtr(atom_index).local_sym_index = 0; - self.offset_table_free_list.append(self.base.allocator, text_block.offset_table_index) catch {}; + self.offset_table_free_list.append(self.base.allocator, atom.offset_table_index) catch {}; - if (self.dwarf) |*dw| { - dw.freeAtom(&text_block.dbg_info_atom); - } + // if (self.dwarf) |*dw| { + // dw.freeAtom(&atom.dbg_info_atom); + // } } -fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, phdr_index: u16) void { +fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void { _ = self; - _ = text_block; + _ = atom_index; _ = new_block_size; - _ = phdr_index; } -fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64, phdr_index: u16) !u64 { - const sym = text_block.getSymbol(self); +fn growAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 { + const atom = self.getAtom(atom_index); + const sym = atom.getSymbol(self); const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value; - const need_realloc = !align_ok or new_block_size > text_block.capacity(self); + const need_realloc = !align_ok or new_block_size > atom.capacity(self); if (!need_realloc) return sym.st_value; - return self.allocateTextBlock(text_block, new_block_size, alignment, phdr_index); + return self.allocateAtom(atom_index, new_block_size, alignment); } -fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64, phdr_index: u16) !u64 { - const shdr_index = self.phdr_shdr_table.get(phdr_index).?; +pub fn createAtom(self: *Elf) !Atom.Index { + const gpa = self.base.allocator; + const atom_index = @intCast(Atom.Index, self.atoms.items.len); + const atom = try self.atoms.addOne(gpa); + const local_sym_index = try self.allocateLocalSymbol(); + const offset_table_index = try self.allocateGotOffset(); + try self.atom_by_index_table.putNoClobber(gpa, local_sym_index, atom_index); + atom.* = .{ + .local_sym_index = local_sym_index, + .offset_table_index = offset_table_index, + .prev_index = null, + .next_index = null, + .dbg_info_atom = undefined, + }; + log.debug("creating ATOM(%{d}) at index {d}", .{ local_sym_index, atom_index }); + return atom_index; +} + +fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 { + const atom = self.getAtom(atom_index); + const sym = atom.getSymbol(self); + const phdr_index = self.sections.items(.phdr_index)[sym.st_shndx]; const phdr = &self.program_headers.items[phdr_index]; - const shdr = &self.sections.items[shdr_index]; - const new_block_ideal_capacity = padToIdeal(new_block_size); + const shdr = &self.sections.items(.shdr)[sym.st_shndx]; + const free_list = &self.sections.items(.free_list)[sym.st_shndx]; + const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sym.st_shndx]; + const new_atom_ideal_capacity = padToIdeal(new_block_size); - // We use these to indicate our intention to update metadata, placing the new block, + // We use these to indicate our intention to update metadata, placing the new atom, // and possibly removing a free list node. // It would be simpler to do it inside the for loop below, but that would cause a // problem if an error was returned later in the function. So this action // is actually carried out at the end of the function, when errors are no longer possible. - var block_placement: ?*TextBlock = null; + var atom_placement: ?Atom.Index = null; var free_list_removal: ?usize = null; - var free_list = self.atom_free_lists.get(phdr_index).?; // First we look for an appropriately sized free list node. // The list is unordered. We'll just take the first thing that works. const vaddr = blk: { var i: usize = 0; while (i < free_list.items.len) { - const big_block = free_list.items[i]; - // We now have a pointer to a live text block that has too much capacity. - // Is it enough that we could fit this new text block? - const sym = big_block.getSymbol(self); - const capacity = big_block.capacity(self); + const big_atom_index = free_list.items[i]; + const big_atom = self.getAtom(big_atom_index); + // We now have a pointer to a live atom that has too much capacity. + // Is it enough that we could fit this new atom? + const big_atom_sym = big_atom.getSymbol(self); + const capacity = big_atom.capacity(self); const ideal_capacity = padToIdeal(capacity); - const ideal_capacity_end_vaddr = std.math.add(u64, sym.st_value, ideal_capacity) catch ideal_capacity; - const capacity_end_vaddr = sym.st_value + capacity; - const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity; + const ideal_capacity_end_vaddr = std.math.add(u64, big_atom_sym.st_value, ideal_capacity) catch ideal_capacity; + const capacity_end_vaddr = big_atom_sym.st_value + capacity; + const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity; const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment); if (new_start_vaddr < ideal_capacity_end_vaddr) { // Additional bookkeeping here to notice if this free list node // should be deleted because the block that it points to has grown to take up // more of the extra capacity. - if (!big_block.freeListEligible(self)) { + if (!big_atom.freeListEligible(self)) { _ = free_list.swapRemove(i); } else { i += 1; @@ -2164,60 +2191,69 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al const keep_free_list_node = remaining_capacity >= min_text_capacity; // Set up the metadata to be updated, after errors are no longer possible. - block_placement = big_block; + atom_placement = big_atom_index; if (!keep_free_list_node) { free_list_removal = i; } break :blk new_start_vaddr; - } else if (self.atoms.get(phdr_index)) |last| { - const sym = last.getSymbol(self); - const ideal_capacity = padToIdeal(sym.st_size); - const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity; + } else if (maybe_last_atom_index.*) |last_index| { + const last = self.getAtom(last_index); + const last_sym = last.getSymbol(self); + const ideal_capacity = padToIdeal(last_sym.st_size); + const ideal_capacity_end_vaddr = last_sym.st_value + ideal_capacity; const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment); // Set up the metadata to be updated, after errors are no longer possible. - block_placement = last; + atom_placement = last_index; break :blk new_start_vaddr; } else { break :blk phdr.p_vaddr; } }; - const expand_text_section = block_placement == null or block_placement.?.next == null; - if (expand_text_section) { + const expand_section = if (atom_placement) |placement_index| + self.getAtom(placement_index).next_index == null + else + true; + if (expand_section) { const needed_size = (vaddr + new_block_size) - phdr.p_vaddr; - try self.growAllocSection(shdr_index, phdr_index, needed_size); - _ = try self.atoms.put(self.base.allocator, phdr_index, text_block); + try self.growAllocSection(sym.st_shndx, needed_size); + maybe_last_atom_index.* = atom_index; - if (self.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. - self.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. - self.debug_aranges_section_dirty = true; - } + // if (self.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. + // self.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. + // self.debug_aranges_section_dirty = true; + // } } shdr.sh_addralign = math.max(shdr.sh_addralign, alignment); - // This function can also reallocate a text block. + // This function can also reallocate an atom. // In this case we need to "unplug" it from its previous location before // plugging it in to its new location. - if (text_block.prev) |prev| { - prev.next = text_block.next; + if (atom.prev_index) |prev_index| { + const prev = self.getAtomPtr(prev_index); + prev.next_index = atom.next_index; } - if (text_block.next) |next| { - next.prev = text_block.prev; + if (atom.next_index) |next_index| { + const next = self.getAtomPtr(next_index); + next.prev_index = atom.prev_index; } - if (block_placement) |big_block| { - text_block.prev = big_block; - text_block.next = big_block.next; - big_block.next = text_block; + if (atom_placement) |big_atom_index| { + const big_atom = self.getAtomPtr(big_atom_index); + const atom_ptr = self.getAtomPtr(atom_index); + atom_ptr.prev_index = big_atom_index; + atom_ptr.next_index = big_atom.next_index; + big_atom.next_index = atom_index; } else { - text_block.prev = null; - text_block.next = null; + const atom_ptr = self.getAtomPtr(atom_index); + atom_ptr.prev_index = null; + atom_ptr.next_index = null; } if (free_list_removal) |i| { _ = free_list.swapRemove(i); @@ -2272,15 +2308,10 @@ pub fn allocateGotOffset(self: *Elf) !u32 { return index; } -fn freeRelocationsForTextBlock(self: *Elf, text_block: *TextBlock) void { - var removed_relocs = self.relocs.fetchRemove(text_block); - if (removed_relocs) |*relocs| relocs.value.deinit(self.base.allocator); -} - fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void { const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; for (unnamed_consts.items) |atom| { - self.freeTextBlock(atom, self.phdr_load_ro_index.?); + self.freeAtom(atom); } unnamed_consts.clearAndFree(self.base.allocator); } @@ -2295,43 +2326,57 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { log.debug("freeDecl {*}", .{decl}); - if (self.decls.fetchRemove(decl_index)) |kv| { - if (kv.value) |index| { - self.freeTextBlock(&decl.link.elf, index); - self.freeUnnamedConsts(decl_index); - } + if (self.decls.fetchRemove(decl_index)) |const_kv| { + var kv = const_kv; + self.freeAtom(kv.value.atom); + self.freeUnnamedConsts(decl_index); + kv.value.exports.deinit(self.base.allocator); } - if (self.dwarf) |*dw| { - dw.freeDecl(decl); + // if (self.dwarf) |*dw| { + // dw.freeDecl(decl); + // } +} + +pub fn getOrCreateAtomForDecl(self: *Elf, decl_index: Module.Decl.Index) !Atom.Index { + const gop = try self.decls.getOrPut(self.base.allocator, decl_index); + if (!gop.found_existing) { + gop.value_ptr.* = .{ + .atom = try self.createAtom(), + .shdr = self.getDeclShdrIndex(decl_index), + .exports = .{}, + }; } + return gop.value_ptr.atom; } -fn getDeclPhdrIndex(self: *Elf, decl: *Module.Decl) !u16 { +fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index) u16 { + const decl = self.base.options.module.?.declPtr(decl_index); const ty = decl.ty; const zig_ty = ty.zigTypeTag(); const val = decl.val; - const phdr_index: u16 = blk: { + const shdr_index: u16 = blk: { if (val.isUndefDeep()) { // TODO in release-fast and release-small, we should put undef in .bss - break :blk self.phdr_load_rw_index.?; + break :blk self.data_section_index.?; } switch (zig_ty) { // TODO: what if this is a function pointer? - .Fn => break :blk self.phdr_load_re_index.?, + .Fn => break :blk self.text_section_index.?, else => { if (val.castTag(.variable)) |_| { - break :blk self.phdr_load_rw_index.?; + break :blk self.data_section_index.?; } - break :blk self.phdr_load_ro_index.?; + break :blk self.rodata_section_index.?; }, } }; - return phdr_index; + return shdr_index; } fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym { + const gpa = self.base.allocator; const mod = self.base.options.module.?; const decl = mod.declPtr(decl_index); @@ -2341,60 +2386,65 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s log.debug("updateDeclCode {s}{*}", .{ decl_name, decl }); const required_alignment = decl.getAlignment(self.base.options.target); - const decl_ptr = self.decls.getPtr(decl_index).?; - if (decl_ptr.* == null) { - decl_ptr.* = try self.getDeclPhdrIndex(decl); - } - const phdr_index = decl_ptr.*.?; - const shdr_index = self.phdr_shdr_table.get(phdr_index).?; + const decl_metadata = self.decls.get(decl_index).?; + const atom_index = decl_metadata.atom; + const atom = self.getAtom(atom_index); - const local_sym = decl.link.elf.getSymbolPtr(self); - if (local_sym.st_size != 0) { - const capacity = decl.link.elf.capacity(self); + const shdr_index = decl_metadata.shdr; + if (atom.getSymbol(self).st_size != 0) { + const local_sym = atom.getSymbolPtr(self); + local_sym.st_name = try self.shstrtab.insert(gpa, decl_name); + local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; + local_sym.st_other = 0; + local_sym.st_shndx = shdr_index; + + const capacity = atom.capacity(self); const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); + if (need_realloc) { - const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment, phdr_index); + const vaddr = try self.growAtom(atom_index, code.len, required_alignment); log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, local_sym.st_value, vaddr }); if (vaddr != local_sym.st_value) { local_sym.st_value = vaddr; log.debug(" (writing new offset table entry)", .{}); - self.offset_table.items[decl.link.elf.offset_table_index] = vaddr; - try self.writeOffsetTableEntry(decl.link.elf.offset_table_index); + self.offset_table.items[atom.offset_table_index] = vaddr; + try self.writeOffsetTableEntry(atom.offset_table_index); } } else if (code.len < local_sym.st_size) { - self.shrinkTextBlock(&decl.link.elf, code.len, phdr_index); + self.shrinkAtom(atom_index, code.len); } local_sym.st_size = code.len; - local_sym.st_name = try self.updateString(local_sym.st_name, decl_name); - local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; - local_sym.st_other = 0; - local_sym.st_shndx = shdr_index; + // TODO this write could be avoided if no fields of the symbol were changed. - try self.writeSymbol(decl.link.elf.getSymbolIndex().?); + try self.writeSymbol(atom.getSymbolIndex().?); } else { - const name_str_index = try self.makeString(decl_name); - const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment, phdr_index); - errdefer self.freeTextBlock(&decl.link.elf, phdr_index); - log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr }); - + const local_sym = atom.getSymbolPtr(self); local_sym.* = .{ - .st_name = name_str_index, + .st_name = try self.shstrtab.insert(gpa, decl_name), .st_info = (elf.STB_LOCAL << 4) | stt_bits, .st_other = 0, .st_shndx = shdr_index, - .st_value = vaddr, - .st_size = code.len, + .st_value = 0, + .st_size = 0, }; - self.offset_table.items[decl.link.elf.offset_table_index] = vaddr; + const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment); + errdefer self.freeAtom(atom_index); + log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr }); - try self.writeSymbol(decl.link.elf.getSymbolIndex().?); - try self.writeOffsetTableEntry(decl.link.elf.offset_table_index); + self.offset_table.items[atom.offset_table_index] = vaddr; + local_sym.st_value = vaddr; + local_sym.st_size = code.len; + + try self.writeSymbol(atom.getSymbolIndex().?); + try self.writeOffsetTableEntry(atom.offset_table_index); } + const local_sym = atom.getSymbolPtr(self); + const phdr_index = self.sections.items(.phdr_index)[shdr_index]; const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; - const file_offset = self.sections.items[shdr_index].sh_offset + section_offset; + const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; try self.base.file.?.pwriteAll(code, file_offset); return local_sym; @@ -2413,28 +2463,23 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven const decl_index = func.owner_decl; const decl = module.declPtr(decl_index); - const atom = &decl.link.elf; - try atom.ensureInitialized(self); - const gop = try self.decls.getOrPut(self.base.allocator, decl_index); - if (gop.found_existing) { - self.freeUnnamedConsts(decl_index); - self.freeRelocationsForTextBlock(atom); - } else { - gop.value_ptr.* = null; - } + + const atom_index = try self.getOrCreateAtomForDecl(decl_index); + self.freeUnnamedConsts(decl_index); + Atom.freeRelocations(self, atom_index); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null; - defer if (decl_state) |*ds| ds.deinit(); + // var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null; + // defer if (decl_state) |*ds| ds.deinit(); - const res = if (decl_state) |*ds| - try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ - .dwarf = ds, - }) - else - try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); + // const res = if (decl_state) |*ds| + // try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ + // .dwarf = ds, + // }) + // else + const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); const code = switch (res) { .ok => code_buffer.items, @@ -2445,15 +2490,16 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven }, }; const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_FUNC); - if (decl_state) |*ds| { - try self.dwarf.?.commitDeclState( - module, - decl_index, - local_sym.st_value, - local_sym.st_size, - ds, - ); - } + _ = local_sym; + // if (decl_state) |*ds| { + // try self.dwarf.?.commitDeclState( + // module, + // decl_index, + // local_sym.st_value, + // local_sym.st_size, + // ds, + // ); + // } // Since we updated the vaddr and the size, each corresponding export // symbol also needs to be updated. @@ -2483,41 +2529,34 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v } } - assert(!self.unnamed_const_atoms.contains(decl_index)); - - const atom = &decl.link.elf; - try atom.ensureInitialized(self); - const gop = try self.decls.getOrPut(self.base.allocator, decl_index); - if (gop.found_existing) { - self.freeRelocationsForTextBlock(atom); - } else { - gop.value_ptr.* = null; - } + const atom_index = try self.getOrCreateAtomForDecl(decl_index); + Atom.freeRelocations(self, atom_index); + const atom = self.getAtom(atom_index); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null; - defer if (decl_state) |*ds| ds.deinit(); + // var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null; + // defer if (decl_state) |*ds| ds.deinit(); // TODO implement .debug_info for global variables const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; - const res = if (decl_state) |*ds| - try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ - .ty = decl.ty, - .val = decl_val, - }, &code_buffer, .{ - .dwarf = ds, - }, .{ - .parent_atom_index = decl.link.elf.getSymbolIndex().?, - }) - else - try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ - .ty = decl.ty, - .val = decl_val, - }, &code_buffer, .none, .{ - .parent_atom_index = decl.link.elf.getSymbolIndex().?, - }); + // const res = if (decl_state) |*ds| + // try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ + // .ty = decl.ty, + // .val = decl_val, + // }, &code_buffer, .{ + // .dwarf = ds, + // }, .{ + // .parent_atom_index = atom.getSymbolIndex().?, + // }) + // else + const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ + .ty = decl.ty, + .val = decl_val, + }, &code_buffer, .none, .{ + .parent_atom_index = atom.getSymbolIndex().?, + }); const code = switch (res) { .ok => code_buffer.items, @@ -2529,15 +2568,16 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v }; const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_OBJECT); - if (decl_state) |*ds| { - try self.dwarf.?.commitDeclState( - module, - decl_index, - local_sym.st_value, - local_sym.st_size, - ds, - ); - } + _ = local_sym; + // if (decl_state) |*ds| { + // try self.dwarf.?.commitDeclState( + // module, + // decl_index, + // local_sym.st_value, + // local_sym.st_size, + // ds, + // ); + // } // Since we updated the vaddr and the size, each corresponding export // symbol also needs to be updated. @@ -2545,36 +2585,31 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v } pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 { - var code_buffer = std.ArrayList(u8).init(self.base.allocator); + const gpa = self.base.allocator; + + var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); const mod = self.base.options.module.?; - const decl = mod.declPtr(decl_index); - - const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl_index); + const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index); if (!gop.found_existing) { gop.value_ptr.* = .{}; } const unnamed_consts = gop.value_ptr; - const atom = try self.base.allocator.create(TextBlock); - errdefer self.base.allocator.destroy(atom); - atom.* = TextBlock.empty; - // TODO for unnamed consts we don't need GOT offset/entry allocated - try atom.ensureInitialized(self); - try self.managed_atoms.append(self.base.allocator, atom); - + const decl = mod.declPtr(decl_index); const name_str_index = blk: { const decl_name = try decl.getFullyQualifiedName(mod); - defer self.base.allocator.free(decl_name); - + defer gpa.free(decl_name); const index = unnamed_consts.items.len; - const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl_name, index }); - defer self.base.allocator.free(name); - - break :blk try self.makeString(name); + const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index }); + defer gpa.free(name); + break :blk try self.shstrtab.insert(gpa, name); }; - const name = self.getString(name_str_index); + const name = self.shstrtab.get(name_str_index).?; + + const atom_index = try self.createAtom(); + const atom = self.getAtomPtr(atom_index); const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{ .none = {}, @@ -2592,28 +2627,24 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module }; const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); - const phdr_index = self.phdr_load_ro_index.?; - const shdr_index = self.phdr_shdr_table.get(phdr_index).?; - const vaddr = try self.allocateTextBlock(atom, code.len, required_alignment, phdr_index); - errdefer self.freeTextBlock(atom, phdr_index); - - log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr }); - + const shdr_index = self.rodata_section_index.?; + const phdr_index = self.sections.items(.phdr_index)[shdr_index]; const local_sym = atom.getSymbolPtr(self); - local_sym.* = .{ - .st_name = name_str_index, - .st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT, - .st_other = 0, - .st_shndx = shdr_index, - .st_value = vaddr, - .st_size = code.len, - }; + local_sym.st_name = name_str_index; + local_sym.st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT; + local_sym.st_other = 0; + local_sym.st_shndx = shdr_index; + local_sym.st_size = code.len; + local_sym.st_value = try self.allocateAtom(atom_index, code.len, required_alignment); + errdefer self.freeAtom(atom_index); + + log.debug("allocated text block for {s} at 0x{x}", .{ name, local_sym.st_value }); try self.writeSymbol(atom.getSymbolIndex().?); - try unnamed_consts.append(self.base.allocator, atom); + try unnamed_consts.append(gpa, atom_index); const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; - const file_offset = self.sections.items[shdr_index].sh_offset + section_offset; + const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; try self.base.file.?.pwriteAll(code, file_offset); return atom.getSymbolIndex().?; @@ -2635,20 +2666,16 @@ pub fn updateDeclExports( const tracy = trace(@src()); defer tracy.end(); + const gpa = self.base.allocator; + const decl = module.declPtr(decl_index); - const atom = &decl.link.elf; - - if (atom.getSymbolIndex() == null) return; - + const atom_index = try self.getOrCreateAtomForDecl(decl_index); + const atom = self.getAtom(atom_index); const decl_sym = atom.getSymbol(self); - try self.global_symbols.ensureUnusedCapacity(self.base.allocator, exports.len); + const decl_metadata = self.decls.getPtr(decl_index).?; + const shdr_index = decl_metadata.shdr; - const gop = try self.decls.getOrPut(self.base.allocator, decl_index); - if (!gop.found_existing) { - gop.value_ptr.* = try self.getDeclPhdrIndex(decl); - } - const phdr_index = gop.value_ptr.*.?; - const shdr_index = self.phdr_shdr_table.get(phdr_index).?; + try self.global_symbols.ensureUnusedCapacity(gpa, exports.len); for (exports) |exp| { if (exp.options.section) |section_name| { @@ -2681,10 +2708,10 @@ pub fn updateDeclExports( }, }; const stt_bits: u8 = @truncate(u4, decl_sym.st_info); - if (exp.link.elf.sym_index) |i| { + if (decl_metadata.getExport(self, exp.options.name)) |i| { const sym = &self.global_symbols.items[i]; sym.* = .{ - .st_name = try self.updateString(sym.st_name, exp.options.name), + .st_name = try self.shstrtab.insert(gpa, exp.options.name), .st_info = (stb_bits << 4) | stt_bits, .st_other = 0, .st_shndx = shdr_index, @@ -2692,21 +2719,19 @@ pub fn updateDeclExports( .st_size = decl_sym.st_size, }; } else { - const name = try self.makeString(exp.options.name); const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: { _ = self.global_symbols.addOneAssumeCapacity(); break :blk self.global_symbols.items.len - 1; }; + try decl_metadata.exports.append(gpa, @intCast(u32, i)); self.global_symbols.items[i] = .{ - .st_name = name, + .st_name = try self.shstrtab.insert(gpa, exp.options.name), .st_info = (stb_bits << 4) | stt_bits, .st_other = 0, .st_shndx = shdr_index, .st_value = decl_sym.st_value, .st_size = decl_sym.st_size, }; - - exp.link.elf.sym_index = @intCast(u32, i); } } } @@ -2722,17 +2747,19 @@ pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl) log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl }); if (self.llvm_object) |_| return; - if (self.dwarf) |*dw| { - try dw.updateDeclLineNumber(decl); - } + // if (self.dwarf) |*dw| { + // try dw.updateDeclLineNumber(decl); + // } } -pub fn deleteExport(self: *Elf, exp: Export) void { +pub fn deleteDeclExport(self: *Elf, decl_index: Module.Decl.Index, name: []const u8) void { if (self.llvm_object) |_| return; - - const sym_index = exp.sym_index orelse return; - self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {}; - self.global_symbols.items[sym_index].st_info = 0; + const metadata = self.decls.getPtr(decl_index) orelse return; + const sym_index = metadata.getExportPtr(self, name) orelse return; + log.debug("deleting export '{s}'", .{name}); + self.global_symbol_free_list.append(self.base.allocator, sym_index.*) catch {}; + self.global_symbols.items[sym_index.*].st_info = 0; + sym_index.* = 0; } fn writeProgHeader(self: *Elf, index: usize) !void { @@ -2761,7 +2788,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void { switch (self.ptr_width) { .p32 => { var shdr: [1]elf.Elf32_Shdr = undefined; - shdr[0] = sectHeaderTo32(self.sections.items[index]); + shdr[0] = sectHeaderTo32(self.sections.items(.shdr)[index]); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]); } @@ -2769,7 +2796,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void { return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); }, .p64 => { - var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]}; + var shdr = [1]elf.Elf64_Shdr{self.sections.items(.shdr)[index]}; if (foreign_endian) { mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]); } @@ -2783,11 +2810,11 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { const entry_size: u16 = self.archPtrWidthBytes(); if (self.offset_table_count_dirty) { const needed_size = self.offset_table.items.len * entry_size; - try self.growAllocSection(self.got_section_index.?, self.phdr_got_index.?, needed_size); + try self.growAllocSection(self.got_section_index.?, needed_size); self.offset_table_count_dirty = false; } const endian = self.base.options.target.cpu.arch.endian(); - const shdr = &self.sections.items[self.got_section_index.?]; + const shdr = &self.sections.items(.shdr)[self.got_section_index.?]; const off = shdr.sh_offset + @as(u64, entry_size) * index; switch (entry_size) { 2 => { @@ -2813,7 +2840,7 @@ fn writeSymbol(self: *Elf, index: usize) !void { const tracy = trace(@src()); defer tracy.end(); - const syms_sect = &self.sections.items[self.symtab_section_index.?]; + const syms_sect = &self.sections.items(.shdr)[self.symtab_section_index.?]; // Make sure we are not pointlessly writing symbol data that will have to get relocated // due to running out of space. if (self.local_symbols.items.len != syms_sect.sh_info) { @@ -2835,7 +2862,7 @@ fn writeSymbol(self: *Elf, index: usize) !void { .p64 => syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index, }; const local = self.local_symbols.items[index]; - log.debug("writing symbol {d}, '{s}' at 0x{x}", .{ index, self.getString(local.st_name), off }); + log.debug("writing symbol {d}, '{?s}' at 0x{x}", .{ index, self.shstrtab.get(local.st_name), off }); log.debug(" ({})", .{local}); switch (self.ptr_width) { .p32 => { @@ -2865,7 +2892,7 @@ fn writeSymbol(self: *Elf, index: usize) !void { } fn writeAllGlobalSymbols(self: *Elf) !void { - const syms_sect = &self.sections.items[self.symtab_section_index.?]; + const syms_sect = &self.sections.items(.shdr)[self.symtab_section_index.?]; const sym_size: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Sym), .p64 => @sizeOf(elf.Elf64_Sym), @@ -3215,10 +3242,52 @@ const CsuObjects = struct { fn logSymtab(self: Elf) void { log.debug("locals:", .{}); for (self.local_symbols.items) |sym, id| { - log.debug(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.st_name), sym.st_value, sym.st_shndx }); + log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.shstrtab.get(sym.st_name), sym.st_value, sym.st_shndx }); } log.debug("globals:", .{}); for (self.global_symbols.items) |sym, id| { - log.debug(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.st_name), sym.st_value, sym.st_shndx }); + log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.shstrtab.get(sym.st_name), sym.st_value, sym.st_shndx }); } } + +pub fn getProgramHeader(self: *const Elf, shdr_index: u16) elf.Elf64_Phdr { + const index = self.sections.items(.phdr_index)[shdr_index]; + return self.program_headers.items[index]; +} + +pub fn getProgramHeaderPtr(self: *Elf, shdr_index: u16) *elf.Elf64_Phdr { + const index = self.sections.items(.phdr_index)[shdr_index]; + return &self.program_headers.items[index]; +} + +/// Returns pointer-to-symbol described at sym_index. +pub fn getSymbolPtr(self: *Elf, sym_index: u32) *elf.Elf64_Sym { + return &self.local_symbols.items[sym_index]; +} + +/// Returns symbol at sym_index. +pub fn getSymbol(self: *const Elf, sym_index: u32) elf.Elf64_Sym { + return self.local_symbols.items[sym_index]; +} + +/// Returns name of the symbol at sym_index. +pub fn getSymbolName(self: *const Elf, sym_index: u32) []const u8 { + const sym = self.local_symbols.items[sym_index]; + return self.shstrtab.get(sym.st_name).?; +} + +pub fn getAtom(self: *const Elf, atom_index: Atom.Index) Atom { + assert(atom_index < self.atoms.items.len); + return self.atoms.items[atom_index]; +} + +pub fn getAtomPtr(self: *Elf, atom_index: Atom.Index) *Atom { + assert(atom_index < self.atoms.items.len); + return &self.atoms.items[atom_index]; +} + +/// Returns atom if there is an atom referenced by the symbol. +/// Returns null on failure. +pub fn getAtomIndexForSymbol(self: *Elf, sym_index: u32) ?Atom.Index { + return self.atom_by_index_table.get(sym_index); +} diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index caeb3bfbc58d3caf207f32e317e063aa6a4fd589..79b699636fc3f532e14dba6946a1cf742e41c7cf 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -20,44 +20,35 @@ offset_table_index: u32, /// Points to the previous and next neighbors, based on the `text_offset`. /// This can be used to find, for example, the capacity of this `TextBlock`. -prev: ?*Atom, -next: ?*Atom, +prev_index: ?Atom.Index, +next_index: ?Atom.Index, dbg_info_atom: Dwarf.Atom, -pub const empty = Atom{ - .local_sym_index = 0, - .offset_table_index = undefined, - .prev = null, - .next = null, - .dbg_info_atom = undefined, +pub const Index = u32; + +pub const Reloc = struct { + target: u32, + offset: u64, + addend: u32, + prev_vaddr: u64, }; -pub fn ensureInitialized(self: *Atom, elf_file: *Elf) !void { - if (self.getSymbolIndex() != null) return; // Already initialized - self.local_sym_index = try elf_file.allocateLocalSymbol(); - self.offset_table_index = try elf_file.allocateGotOffset(); - try elf_file.atom_by_index_table.putNoClobber(elf_file.base.allocator, self.local_sym_index, self); -} - pub fn getSymbolIndex(self: Atom) ?u32 { if (self.local_sym_index == 0) return null; return self.local_sym_index; } -pub fn getSymbol(self: Atom, elf_file: *Elf) elf.Elf64_Sym { - const sym_index = self.getSymbolIndex().?; - return elf_file.local_symbols.items[sym_index]; +pub fn getSymbol(self: Atom, elf_file: *const Elf) elf.Elf64_Sym { + return elf_file.getSymbol(self.getSymbolIndex().?); } pub fn getSymbolPtr(self: Atom, elf_file: *Elf) *elf.Elf64_Sym { - const sym_index = self.getSymbolIndex().?; - return &elf_file.local_symbols.items[sym_index]; + return elf_file.getSymbolPtr(self.getSymbolIndex().?); } -pub fn getName(self: Atom, elf_file: *Elf) []const u8 { - const sym = self.getSymbol(); - return elf_file.getString(sym.st_name); +pub fn getName(self: Atom, elf_file: *const Elf) []const u8 { + return elf_file.getSymbolName(self.getSymbolIndex().?); } pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 { @@ -72,9 +63,10 @@ pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 { /// Returns how much room there is to grow in virtual address space. /// File offset relocation happens transparently, so it is not included in /// this calculation. -pub fn capacity(self: Atom, elf_file: *Elf) u64 { +pub fn capacity(self: Atom, elf_file: *const Elf) u64 { const self_sym = self.getSymbol(elf_file); - if (self.next) |next| { + if (self.next_index) |next_index| { + const next = elf_file.getAtom(next_index); const next_sym = next.getSymbol(elf_file); return next_sym.st_value - self_sym.st_value; } else { @@ -83,9 +75,10 @@ pub fn capacity(self: Atom, elf_file: *Elf) u64 { } } -pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { +pub fn freeListEligible(self: Atom, elf_file: *const Elf) bool { // No need to keep a free list node for the last block. - const next = self.next orelse return false; + const next_index = self.next_index orelse return false; + const next = elf_file.getAtom(next_index); const self_sym = self.getSymbol(elf_file); const next_sym = next.getSymbol(elf_file); const cap = next_sym.st_value - self_sym.st_value; @@ -94,3 +87,17 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { const surplus = cap - ideal_cap; return surplus >= Elf.min_text_capacity; } + +pub fn addRelocation(elf_file: *Elf, atom_index: Index, reloc: Reloc) !void { + const gpa = elf_file.base.allocator; + const gop = try elf_file.relocs.getOrPut(gpa, atom_index); + if (!gop.found_existing) { + gop.value_ptr.* = .{}; + } + try gop.value_ptr.append(gpa, reloc); +} + +pub fn freeRelocations(elf_file: *Elf, atom_index: Index) void { + var removed_relocs = elf_file.relocs.fetchRemove(atom_index); + if (removed_relocs) |*relocs| relocs.value.deinit(elf_file.base.allocator); +} diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 29aed25b3108b52080eed45f72d3d5e9b3f29872..11a1119449dd640959b9f4673f6ecf7d4c2bb73e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2604,9 +2604,11 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void { log.debug("freeDecl {*}", .{decl}); - if (self.decls.fetchSwapRemove(decl_index)) |kv| { + if (self.decls.fetchSwapRemove(decl_index)) |const_kv| { + var kv = const_kv; self.freeAtom(kv.value.atom); self.freeUnnamedConsts(decl_index); + kv.value.exports.deinit(self.base.allocator); } // if (self.d_sym) |*d_sym| { -- 2.54.0