From a9df098cd2dd04e2c363b439233ff2e14e198413 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 5 Sep 2023 23:10:04 +0200 Subject: [PATCH] elf: make everything upside down - track by Symbol.Index rather than Atom.Index --- src/arch/aarch64/CodeGen.zig | 8 +- src/arch/arm/CodeGen.zig | 8 +- src/arch/riscv64/CodeGen.zig | 8 +- src/arch/sparc64/CodeGen.zig | 8 +- src/arch/x86_64/CodeGen.zig | 16 +- src/codegen.zig | 10 +- src/link/Elf.zig | 684 ++++++++++++--------------------- src/link/Elf/Atom.zig | 249 +++++++++--- src/link/Elf/LinkerDefined.zig | 24 +- src/link/Elf/Symbol.zig | 102 ++--- src/link/Elf/ZigModule.zig | 101 ++++- src/link/Elf/file.zig | 7 + src/link/strtab.zig | 4 +- 13 files changed, 624 insertions(+), 605 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 15f7397721..35daa4a02a 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4314,10 +4314,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (try self.air.value(callee, mod)) |func_value| { if (func_value.getFunction(mod)) |func| { if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); - const atom = elf_file.atom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = @as(u32, @intCast(atom.getOffsetTableAddress(elf_file))); + const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); + const sym = elf_file.symbol(sym_index); + _ = try sym.getOrCreateOffsetTableEntry(elf_file); + const got_addr = @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); try self.genSetReg(Type.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); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 234a281623..f32e104c88 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -4294,10 +4294,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (try self.air.value(callee, mod)) |func_value| { if (func_value.getFunction(mod)) |func| { if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); - const atom = elf_file.atom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = @as(u32, @intCast(atom.getOffsetTableAddress(elf_file))); + const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); + const sym = elf_file.symbol(sym_index); + _ = try sym.getOrCreateOffsetTableEntry(elf_file); + const got_addr = @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr }); } else if (self.bin_file.cast(link.File.MachO)) |_| { unreachable; // unsupported architecture for MachO diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index bead5c6d40..5ed60dc220 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1747,10 +1747,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (try self.air.value(callee, mod)) |func_value| { switch (mod.intern_pool.indexToKey(func_value.ip_index)) { .func => |func| { - const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); - const atom = elf_file.atom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = @as(u32, @intCast(atom.getOffsetTableAddress(elf_file))); + const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); + const sym = elf_file.symbol(sym_index); + _ = try sym.getOrCreateOffsetTableEntry(elf_file); + const got_addr = @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr }); _ = try self.addInst(.{ .tag = .jalr, diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 3a3f69ce37..b0a3573ff9 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1349,10 +1349,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier switch (mod.intern_pool.indexToKey(func_value.ip_index)) { .func => |func| { const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { - const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); - const atom = elf_file.atom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - break :blk @as(u32, @intCast(atom.getOffsetTableAddress(elf_file))); + const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); + const sym = elf_file.symbol(sym_index); + _ = try sym.getOrCreateOffsetTableEntry(elf_file); + break :blk @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); } else unreachable; try self.genSetReg(Type.usize, .o7, .{ .memory = got_addr }); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 187db5f20f..eaed79ef5e 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -8149,10 +8149,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier else => null, }) |owner_decl| { if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForDecl(owner_decl); - const atom = elf_file.atom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = atom.getOffsetTableAddress(elf_file); + const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl); + const sym = elf_file.symbol(sym_index); + _ = try sym.getOrCreateOffsetTableEntry(elf_file); + const got_addr = sym.getOffsetTableAddress(elf_file); try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(got_addr), @@ -10215,11 +10215,11 @@ fn genLazySymbolRef( lazy_sym: link.File.LazySymbol, ) InnerError!void { if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = elf_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + const sym_index = elf_file.getOrCreateMetadataForLazySymbol(lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const atom = elf_file.atom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = atom.getOffsetTableAddress(elf_file); + const sym = elf_file.symbol(sym_index); + _ = try sym.getOrCreateOffsetTableEntry(elf_file); + const got_addr = sym.getOffsetTableAddress(elf_file); const got_mem = Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(got_addr) }); switch (tag) { diff --git a/src/codegen.zig b/src/codegen.zig index 75f42ff876..d3f44bb2f5 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -854,10 +854,10 @@ fn genDeclRef( const is_threadlocal = tv.val.isPtrToThreadLocal(mod) and !bin_file.options.single_threaded; if (bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); - const atom = elf_file.atom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(elf_file) }); + const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index); + const sym = elf_file.symbol(sym_index); + _ = try sym.getOrCreateOffsetTableEntry(elf_file); + return GenResult.mcv(.{ .memory = sym.getOffsetTableAddress(elf_file) }); } else if (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().?; @@ -892,7 +892,7 @@ fn genUnnamedConst( return GenResult.fail(bin_file.allocator, src_loc, "lowering unnamed constant failed: {s}", .{@errorName(err)}); }; if (bin_file.cast(link.File.Elf)) |elf_file| { - return GenResult.mcv(.{ .memory = elf_file.symbol(local_sym_index).st_value }); + return GenResult.mcv(.{ .memory = elf_file.symbol(local_sym_index).value }); } else if (bin_file.cast(link.File.MachO)) |_| { return GenResult.mcv(.{ .load_direct = local_sym_index }); } else if (bin_file.cast(link.File.Coff)) |_| { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 14bfa58748..6539946ca1 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1,4 +1,4 @@ -base: File, +base: link.File, dwarf: ?Dwarf = null, ptr_width: PtrWidth, @@ -6,7 +6,7 @@ ptr_width: PtrWidth, /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. llvm_object: ?*LlvmObject = null, -files: std.MutliArrayList(File.Entry) = .{}, +files: std.MultiArrayList(File.Entry) = .{}, zig_module_index: ?File.Index = null, linker_defined_index: ?File.Index = null, @@ -56,14 +56,12 @@ shstrtab_section_index: ?u16 = null, strtab_section_index: ?u16 = null, symbols: std.ArrayListUnmanaged(Symbol) = .{}, -globals: std.ArrayListUnmanaged(Symbol.Index) = .{}, -resolver: std.StringHashMapUnmanaged(u32) = .{}, +resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, -symbols_free_list: std.ArrayListUnmanaged(u32) = .{}, -globals_free_list: std.ArrayListUnmanaged(u32) = .{}, +symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{}, -got_table: TableSection(u32) = .{}, +got_table: TableSection(Symbol.Index) = .{}, phdr_table_dirty: bool = false, shdr_table_dirty: bool = false, @@ -88,9 +86,6 @@ decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, /// List of atoms that are owned directly by the linker. 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` /// needs updating or is freed. @@ -113,12 +108,10 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{}, unnamed_const_atoms: UnnamedConstTable = .{}, /// A table of relocations indexed by the owning them `TextBlock`. -/// Note that once we refactor `TextBlock`'s lifetime and ownership rules, -/// this will be a table indexed by index into the list of Atoms. relocs: RelocTable = .{}, const RelocTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Reloc)); -const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); +const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index)); const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata); /// When allocating, the ideal_capacity is calculated by @@ -143,13 +136,12 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option const self = try createEmpty(allocator, options); errdefer self.base.destroy(); - const file = try options.emit.?.directory.handle.createFile(sub_path, .{ + self.base.file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options), }); - self.base.file = file; self.shdr_table_dirty = true; // Index 0 is always a null symbol. @@ -243,19 +235,10 @@ pub fn deinit(self: *Elf) void { self.strtab.deinit(gpa); self.symbols.deinit(gpa); self.symbols_free_list.deinit(gpa); - self.globals.deinit(gpa); - self.globals_free_list.deinit(gpa); self.got_table.deinit(gpa); + self.resolver.deinit(gpa); self.unresolved.deinit(gpa); - { - var it = self.resolver.keyIterator(); - while (it.next()) |key_ptr| { - gpa.free(key_ptr.*); - } - self.resolver.deinit(gpa); - } - { var it = self.decls.iterator(); while (it.next()) |entry| { @@ -265,7 +248,6 @@ pub fn deinit(self: *Elf) void { } self.atoms.deinit(gpa); - self.atom_by_index_table.deinit(gpa); self.lazy_syms.deinit(gpa); { @@ -292,13 +274,12 @@ pub fn deinit(self: *Elf) void { pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 { assert(self.llvm_object == null); - const this_atom_index = try self.getOrCreateAtomForDecl(decl_index); - const this_atom = self.atom(this_atom_index); - const target = this_atom.symbolIndex().?; - const vaddr = this_atom.symbol(self).st_value; - const atom_index = self.atomIndexForSymbol(reloc_info.parent_atom_index).?; - try Atom.addRelocation(self, atom_index, .{ - .target = target, + const this_sym_index = try self.getOrCreateMetadataForDecl(decl_index); + const this_sym = self.symbol(this_sym_index); + const vaddr = this_sym.value; + const parent_atom_index = self.symbol(reloc_info.parent_atom_index).atom_index; + try Atom.addRelocation(self, parent_atom_index, .{ + .target = this_sym, .offset = reloc_info.offset, .addend = reloc_info.addend, .prev_vaddr = vaddr, @@ -851,7 +832,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { } } -fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { +pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { // TODO Also detect virtual address collisions. const shdr = &self.sections.items(.shdr)[shdr_index]; const phdr_index = self.sections.items(.phdr_index)[shdr_index]; @@ -863,8 +844,7 @@ fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { const new_offset = self.findFreeSpace(needed_size, self.page_size); const existing_size = if (maybe_last_atom_index) |last_atom_index| blk: { const last = self.atom(last_atom_index); - const sym = last.symbol(self); - break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr; + break :blk (last.value + last.size) - phdr.p_vaddr; } else if (shdr_index == self.got_section_index.?) blk: { break :blk shdr.sh_size; } else 0; @@ -985,6 +965,7 @@ 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; self.zig_module_index = blk: { const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); @@ -992,9 +973,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node break :blk index; }; - self.linker_defined = blk: { + self.linker_defined_index = blk: { const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); - self.files.set(index, .{ .linker_defined = .{} }); + self.files.set(index, .{ .linker_defined = .{ .index = index } }); break :blk index; }; @@ -2157,193 +2138,12 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void { const sym_index = atom_ptr.symbolIndex().?; log.debug("adding %{d} to local symbols free list", .{sym_index}); - self.locals_free_list.append(gpa, sym_index) catch {}; - self.locals.items[sym_index] = null_sym; - _ = self.atom_by_index_table.remove(sym_index); + self.symbols_free_list.append(gpa, sym_index) catch {}; + self.symbols.items[sym_index] = .{}; atom_ptr.sym_index = 0; self.got_table.freeEntry(gpa, sym_index); } -fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void { - _ = self; - _ = atom_index; - _ = new_block_size; -} - -fn growAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 { - const atom_ptr = self.atom(atom_index); - const sym = atom_ptr.symbol(self); - const align_ok = mem.alignBackward(u64, sym.st_value, alignment) == sym.st_value; - const need_realloc = !align_ok or new_block_size > atom_ptr.capacity(self); - if (!need_realloc) return sym.st_value; - return self.allocateAtom(atom_index, new_block_size, alignment); -} - -pub fn createAtom(self: *Elf) !Atom.Index { - const gpa = self.base.allocator; - const atom_index = @as(Atom.Index, @intCast(self.atoms.items.len)); - const atom_ptr = try self.atoms.addOne(gpa); - const sym_index = try self.allocateSymbol(); - try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index); - atom_ptr.* = .{ .sym_index = sym_index }; - log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, atom_index }); - return atom_index; -} - -fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 { - const sym = self.atom(atom_index).symbol(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)[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 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 atom_placement: ?Atom.Index = null; - var free_list_removal: ?usize = null; - - // 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 = if (self.base.child_pid == null) 0 else free_list.items.len; - while (i < free_list.items.len) { - const big_atom_index = free_list.items[i]; - const big_atom = self.atom(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.symbol(self); - const capacity = big_atom.capacity(self); - const ideal_capacity = padToIdeal(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.alignBackward(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_atom.freeListEligible(self)) { - _ = free_list.swapRemove(i); - } else { - i += 1; - } - continue; - } - // At this point we know that we will place the new block here. But the - // remaining question is whether there is still yet enough capacity left - // over for there to still be a free list node. - const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr; - const keep_free_list_node = remaining_capacity >= min_text_capacity; - - // Set up the metadata to be updated, after errors are no longer possible. - atom_placement = big_atom_index; - if (!keep_free_list_node) { - free_list_removal = i; - } - break :blk new_start_vaddr; - } else if (maybe_last_atom_index.*) |last_index| { - const last = self.atom(last_index); - const last_sym = last.symbol(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.alignForward(u64, ideal_capacity_end_vaddr, alignment); - // Set up the metadata to be updated, after errors are no longer possible. - atom_placement = last_index; - break :blk new_start_vaddr; - } else { - break :blk phdr.p_vaddr; - } - }; - - const expand_section = if (atom_placement) |placement_index| - self.atom(placement_index).next_index == null - else - true; - if (expand_section) { - const needed_size = (vaddr + new_block_size) - phdr.p_vaddr; - 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; - } - } - shdr.sh_addralign = @max(shdr.sh_addralign, alignment); - - // 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. - const atom_ptr = self.atom(atom_index); - if (atom_ptr.prev_index) |prev_index| { - const prev = self.atom(prev_index); - prev.next_index = atom_ptr.next_index; - } - if (atom_ptr.next_index) |next_index| { - const next = self.atom(next_index); - next.prev_index = atom_ptr.prev_index; - } - - if (atom_placement) |big_atom_index| { - const big_atom = self.atom(big_atom_index); - atom_ptr.prev_index = big_atom_index; - atom_ptr.next_index = big_atom.next_index; - big_atom.next_index = atom_index; - } else { - atom_ptr.prev_index = null; - atom_ptr.next_index = null; - } - if (free_list_removal) |i| { - _ = free_list.swapRemove(i); - } - return vaddr; -} - -pub fn allocateSymbol(self: *Elf) !u32 { - try self.locals.ensureUnusedCapacity(self.base.allocator, 1); - const index = blk: { - if (self.locals_free_list.popOrNull()) |index| { - log.debug(" (reusing symbol index {d})", .{index}); - break :blk index; - } else { - log.debug(" (allocating symbol index {d})", .{self.locals.items.len}); - const index = @as(u32, @intCast(self.locals.items.len)); - _ = self.locals.addOneAssumeCapacity(); - break :blk index; - } - }; - self.locals.items[index] = null_sym; - return index; -} - -fn allocateGlobal(self: *Elf) !u32 { - try self.globals.ensureUnusedCapacity(self.base.allocator, 1); - const index = blk: { - if (self.globals_free_list.popOrNull()) |index| { - log.debug(" (reusing global index {d})", .{index}); - break :blk index; - } else { - log.debug(" (allocating global index {d})", .{self.globals.items.len}); - const index = @as(u32, @intCast(self.globals.items.len)); - _ = self.globals.addOneAssumeCapacity(); - break :blk index; - } - }; - self.globals.items[index] = 0; - return index; -} - 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_index| { @@ -2372,40 +2172,50 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { } } -pub fn getOrCreateAtomForLazySymbol(self: *Elf, sym: link.File.LazySymbol) !Atom.Index { +pub fn getOrCreateMetadataForLazySymbol(self: *Elf, sym: link.File.LazySymbol) !Symbol.Index { const mod = self.base.options.module.?; const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(mod)); errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); if (!gop.found_existing) gop.value_ptr.* = .{}; - const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) { - .code => .{ .atom = &gop.value_ptr.text_atom, .state = &gop.value_ptr.text_state }, - .const_data => .{ .atom = &gop.value_ptr.rodata_atom, .state = &gop.value_ptr.rodata_state }, + const metadata: struct { + symbol_index: *Symbol.Index, + state: *LazySymbolMetadata.State, + } = switch (sym.kind) { + .code => .{ + .symbol_index = &gop.value_ptr.text_symbol_index, + .state = &gop.value_ptr.text_state, + }, + .const_data => .{ + .symbol_index = &gop.value_ptr.rodata_symbol_index, + .state = &gop.value_ptr.rodata_state, + }, }; + const zig_module = self.file(self.zig_module_index.?).?.zig_module; switch (metadata.state.*) { - .unused => metadata.atom.* = try self.createAtom(), + .unused => metadata.symbol_index.* = try zig_module.createAtom(switch (sym.kind) { + .code => self.text_section_index.?, + .const_data => self.rodata_section_index.?, + }, self), .pending_flush => return metadata.atom.*, .flushed => {}, } metadata.state.* = .pending_flush; - const atom_index = metadata.atom.*; + const symbol_index = metadata.symbol_index.*; // anyerror needs to be deferred until flushModule - if (sym.getDecl(mod) != .none) try self.updateLazySymbolAtom(sym, atom_index, switch (sym.kind) { - .code => self.text_section_index.?, - .const_data => self.rodata_section_index.?, - }); - return atom_index; + if (sym.getDecl(mod) != .none) try self.updateLazySymbol(sym, symbol_index); + return symbol_index; } -pub fn getOrCreateAtomForDecl(self: *Elf, decl_index: Module.Decl.Index) !Atom.Index { +pub fn getOrCreateMetadataForDecl(self: *Elf, decl_index: Module.Decl.Index) !Symbol.Index { const gop = try self.decls.getOrPut(self.base.allocator, decl_index); if (!gop.found_existing) { + const zig_module = self.file(self.zig_module_index.?).?.zig_module; gop.value_ptr.* = .{ - .atom = try self.createAtom(), - .shdr = self.getDeclShdrIndex(decl_index), + .symbol_index = try zig_module.createAtom(self.getDeclShdrIndex(decl_index), self), .exports = .{}, }; } - return gop.value_ptr.atom; + return gop.value_ptr.symbol_index; } fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index) u16 { @@ -2434,7 +2244,13 @@ fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index) u16 { return shdr_index; } -fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym { +fn updateDeclCode( + self: *Elf, + decl_index: Module.Decl.Index, + sym_index: Symbol.Index, + code: []const u8, + stt_bits: u8, +) !void { const gpa = self.base.allocator; const mod = self.base.options.module.?; const decl = mod.declPtr(decl_index); @@ -2444,60 +2260,52 @@ 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(mod); - const decl_metadata = self.decls.get(decl_index).?; - const atom_index = decl_metadata.atom; - const atom_ptr = self.atom(atom_index); - const local_sym_index = atom_ptr.symbolIndex().?; - const local_sym = atom_ptr.symbol(self); + const sym = self.symbol(sym_index); + const esym = sym.sourceSymbol(self); + const atom_ptr = sym.atom(self).?; + const shdr_index = sym.output_section_index; - const shdr_index = decl_metadata.shdr; - if (atom_ptr.symbol(self).st_size != 0 and self.base.child_pid == null) { - local_sym.st_name = try self.strtab.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; + sym.name_offset = try self.strtab.insert(gpa, decl_name); + esym.st_name = sym.name_offset; + esym.st_info |= stt_bits; + esym.st_size = code.len; + const old_size = atom_ptr.size; + atom_ptr.alignment = math.log2_int(u64, required_alignment); + atom_ptr.size = code.len; + + if (old_size > 0 and self.base.child_pid == null) { const capacity = atom_ptr.capacity(self); - const need_realloc = code.len > capacity or - !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); - + const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment); if (need_realloc) { - 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; + const vaddr = try atom_ptr.grow(self); + log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, sym.value, vaddr }); + if (vaddr != sym.value) { + sym.value = vaddr; + esym.st_value = vaddr; log.debug(" (writing new offset table entry)", .{}); - const got_entry_index = self.got_table.lookup.get(local_sym_index).?; - self.got_table.entries.items[got_entry_index] = local_sym_index; + const got_entry_index = self.got_table.lookup.get(sym_index).?; + self.got_table.entries.items[got_entry_index] = sym_index; try self.writeOffsetTableEntry(got_entry_index); } - } else if (code.len < local_sym.st_size) { - self.shrinkAtom(atom_index, code.len); + } else if (code.len < old_size) { + atom_ptr.shrink(self); } - local_sym.st_size = code.len; } else { - local_sym.* = .{ - .st_name = try self.strtab.insert(gpa, decl_name), - .st_info = (elf.STB_LOCAL << 4) | stt_bits, - .st_other = 0, - .st_shndx = shdr_index, - .st_value = 0, - .st_size = 0, - }; - const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment); - errdefer self.freeAtom(atom_index); + const vaddr = try atom_ptr.allocate(self); + errdefer self.freeAtom(atom_ptr); log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr }); - local_sym.st_value = vaddr; - local_sym.st_size = code.len; + sym.value = vaddr; + esym.st_value = vaddr; - const got_entry_index = try atom_ptr.getOrCreateOffsetTableEntry(self); + const got_entry_index = try sym.getOrCreateOffsetTableEntry(self); try self.writeOffsetTableEntry(got_entry_index); } 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 section_offset = sym.value - self.program_headers.items[phdr_index].p_vaddr; const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; if (self.base.child_pid) |pid| { @@ -2508,7 +2316,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s .iov_len = code.len, }}; var remote_vec: [1]std.os.iovec_const = .{.{ - .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(local_sym.st_value)))), + .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.value)))), .iov_len = code.len, }}; const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0); @@ -2522,8 +2330,6 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s } try self.base.file.?.pwriteAll(code, file_offset); - - return local_sym; } pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { @@ -2539,9 +2345,9 @@ pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: A const decl_index = func.owner_decl; const decl = mod.declPtr(decl_index); - const atom_index = try self.getOrCreateAtomForDecl(decl_index); + const sym_index = try self.getOrCreateMetadataForDecl(decl_index); self.freeUnnamedConsts(decl_index); - Atom.freeRelocations(self, atom_index); + Atom.freeRelocations(self, self.symbol(sym_index).atom_index); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); @@ -2564,13 +2370,14 @@ pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: A return; }, }; - const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_FUNC); + try self.updateDeclCode(decl_index, sym_index, code, elf.STT_FUNC); if (decl_state) |*ds| { + const sym = self.symbol(sym_index); try self.dwarf.?.commitDeclState( mod, decl_index, - local_sym.st_value, - local_sym.st_size, + sym.value, + sym.atom(self).?.size, ds, ); } @@ -2604,8 +2411,8 @@ pub fn updateDecl( } } - const atom_index = try self.getOrCreateAtomForDecl(decl_index); - Atom.freeRelocations(self, atom_index); + const sym_index = try self.getOrCreateMetadataForDecl(decl_index); + Atom.freeRelocations(self, self.symbol(sym_index).atom_index); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); @@ -2622,14 +2429,14 @@ pub fn updateDecl( }, &code_buffer, .{ .dwarf = ds, }, .{ - .parent_atom_index = self.atom(atom_index).symbolIndex().?, + .parent_atom_index = sym_index, }) else try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .none, .{ - .parent_atom_index = self.atom(atom_index).symbolIndex().?, + .parent_atom_index = sym_index, }); const code = switch (res) { @@ -2641,13 +2448,14 @@ pub fn updateDecl( }, }; - const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_OBJECT); + try self.updateDeclCode(decl_index, sym_index, code, elf.STT_OBJECT); if (decl_state) |*ds| { + const sym = self.symbol(sym_index); try self.dwarf.?.commitDeclState( mod, decl_index, - local_sym.st_value, - local_sym.st_size, + sym.value, + sym.atom(self).?.size, ds, ); } @@ -2657,12 +2465,7 @@ pub fn updateDecl( return self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index)); } -fn updateLazySymbolAtom( - self: *Elf, - sym: link.File.LazySymbol, - atom_index: Atom.Index, - shdr_index: u16, -) !void { +fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.Index) !void { const gpa = self.base.allocator; const mod = self.base.options.module.?; @@ -2680,9 +2483,6 @@ fn updateLazySymbolAtom( }; const name = self.strtab.get(name_str_index).?; - const atom_ptr = self.atom(atom_index); - const local_sym_index = atom_ptr.symbolIndex().?; - const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl| mod.declPtr(owner_decl).srcLoc(mod) else @@ -2698,7 +2498,7 @@ fn updateLazySymbolAtom( &required_alignment, &code_buffer, .none, - .{ .parent_atom_index = local_sym_index }, + .{ .parent_atom_index = symbol_index }, ); const code = switch (res) { .ok => code_buffer.items, @@ -2708,28 +2508,28 @@ fn updateLazySymbolAtom( }, }; - const phdr_index = self.sections.items(.phdr_index)[shdr_index]; - const local_sym = atom_ptr.symbol(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 = 0, - .st_size = 0, - }; - const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment); - errdefer self.freeAtom(atom_index); + const local_sym = self.symbol(symbol_index); + const phdr_index = self.sections.items(.phdr_index)[local_sym.output_section_index]; + local_sym.name_offset = name_str_index; + const local_esym = local_sym.sourceSymbol(self); + local_esym.st_name = name_str_index; + local_esym.st_info |= elf.STT_OBJECT; + local_esym.st_size = code.len; + const atom_ptr = local_sym.atom(self).?; + atom_ptr.alignment = math.log2_int(u64, required_alignment); + atom_ptr.size = code.len; + const vaddr = try atom_ptr.allocate(self); + errdefer self.freeAtom(atom_ptr); log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr }); - local_sym.st_value = vaddr; - local_sym.st_size = code.len; + local_sym.value = vaddr; + local_esym.st_value = vaddr; - const got_entry_index = try atom_ptr.getOrCreateOffsetTableEntry(self); + const got_entry_index = try local_sym.getOrCreateOffsetTableEntry(self); try self.writeOffsetTableEntry(got_entry_index); const section_offset = vaddr - self.program_headers.items[phdr_index].p_vaddr; - const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; + const file_offset = self.sections.items(.shdr)[local_sym.output_section_index].sh_offset + section_offset; try self.base.file.?.pwriteAll(code, file_offset); } @@ -2756,12 +2556,13 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module }; const name = self.strtab.get(name_str_index).?; - const atom_index = try self.createAtom(); + const zig_module = self.file(self.zig_module_index.?).?.zig_module; + const sym_index = try zig_module.createAtom(self.rodata_section_index.?, self); const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), typed_value, &code_buffer, .{ .none = {}, }, .{ - .parent_atom_index = self.atom(atom_index).symbolIndex().?, + .parent_atom_index = sym_index, }); const code = switch (res) { .ok => code_buffer.items, @@ -2776,24 +2577,30 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module const required_alignment = typed_value.ty.abiAlignment(mod); const shdr_index = self.rodata_section_index.?; const phdr_index = self.sections.items(.phdr_index)[shdr_index]; - const local_sym = self.atom(atom_index).symbol(self); - 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); + const local_sym = self.symbol(sym_index); + local_sym.name_offset = name_str_index; + const local_esym = local_sym.sourceSymbol(self); + local_esym.st_name = name_str_index; + local_esym.st_info |= elf.STT_OBJECT; + local_esym.st_size = code.len; + const atom_ptr = local_sym.atom(self).?; + atom_ptr.alignment = math.log2_int(u64, required_alignment); + atom_ptr.size = code.len; + const vaddr = try atom_ptr.allocateAtom(self); + errdefer self.freeAtom(atom_ptr); log.debug("allocated text block for {s} at 0x{x}", .{ name, local_sym.st_value }); - try unnamed_consts.append(gpa, atom_index); + local_sym.value = vaddr; + local_esym.st_value = vaddr; - const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; + try unnamed_consts.append(gpa, atom_ptr.atom_index); + + const section_offset = local_sym.value - self.program_headers.items[phdr_index].p_vaddr; const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; try self.base.file.?.pwriteAll(code, file_offset); - return self.atom(atom_index).symbolIndex().?; + return sym_index; } pub fn updateDeclExports( @@ -2815,11 +2622,10 @@ pub fn updateDeclExports( const gpa = self.base.allocator; const decl = mod.declPtr(decl_index); - const atom_index = try self.getOrCreateAtomForDecl(decl_index); - const atom_ptr = self.atom(atom_index); - const decl_sym = atom_ptr.symbol(self); + const decl_sym_index = try self.getOrCreateMetadataForDecl(decl_index); + const decl_sym = self.symbol(decl_sym_index); + const decl_esym = symbol.sourceSymbol(self); const decl_metadata = self.decls.getPtr(decl_index).?; - const shdr_index = decl_metadata.shdr; for (exports) |exp| { const exp_name = mod.intern_pool.stringToSlice(exp.opts.name); @@ -2838,7 +2644,7 @@ pub fn updateDeclExports( .Strong => blk: { const entry_name = self.base.options.entry orelse "_start"; if (mem.eql(u8, exp_name, entry_name)) { - self.entry_addr = decl_sym.st_value; + self.entry_addr = decl_sym.value; } break :blk elf.STB_GLOBAL; }, @@ -2855,22 +2661,18 @@ pub fn updateDeclExports( const stt_bits: u8 = @as(u4, @truncate(decl_sym.st_info)); const sym_index = if (decl_metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: { - const sym_index = try self.allocateSymbol(); + const zig_module = self.file(self.zig_module_index.?).?.zig_module; + const sym_index = try zig_module.addGlobal(exp_name, self); try decl_metadata.exports.append(gpa, sym_index); break :blk sym_index; }; const sym = self.symbol(sym_index); - sym.* = .{ - .st_name = try self.strtab.insert(gpa, exp_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, - }; - const sym_name = self.symbolName(sym_index); - const gop = try self.getOrPutGlobal(sym_name); - gop.value_ptr.* = sym_index; + sym.value = decl_sym.value; + sym.atom_index = decl_sym.atom_index; + sym.output_section_index = decl_sym.output_section_index; + const esym = sym.sourceSymbol(self); + esym.* = decl_esym.*; + esym.st_info = (stb_bits << 4) | stt_bits; } } @@ -2901,16 +2703,21 @@ pub fn deleteDeclExport( const mod = self.base.options.module.?; const exp_name = mod.intern_pool.stringToSlice(name); const sym_index = metadata.@"export"(self, exp_name) orelse return; - const sym = self.symbol(sym_index.*); log.debug("deleting export '{s}'", .{exp_name}); - sym.* = null_sym; - self.locals_free_list.append(gpa, sym_index.*) catch {}; - - if (self.resolver.fetchRemove(exp_name)) |entry| { - self.globals_free_list.append(gpa, entry.value) catch {}; - self.globals.items[entry.value] = 0; - } - + const sym = self.symbol(sym_index.*); + const esym = sym.sourceSymbol(self); + assert(self.resolver.fetchSwapRemove(sym.name_offset) != null); // TODO don't delete it if it's not dominant + sym.* = .{}; + // TODO free list for esym! + esym.* = .{ + .st_name = 0, + .st_info = 0, + .st_other = 0, + .st_shndx = 0, + .st_value = 0, + .st_size = 0, + }; + self.symbols_free_list.append(gpa, sym_index.*) catch {}; sym_index.* = 0; } @@ -2971,7 +2778,7 @@ fn writeOffsetTableEntry(self: *Elf, index: @TypeOf(self.got_table).Index) !void const phdr = &self.program_headers.items[self.phdr_got_index.?]; const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index; const got_entry = self.got_table.entries.items[index]; - const got_value = self.symbol(got_entry).st_value; + const got_value = self.symbol(got_entry).value; switch (entry_size) { 2 => { var buf: [2]u8 = undefined; @@ -3374,98 +3181,99 @@ const CsuObjects = struct { } }; -fn logSymtab(self: Elf) void { - log.debug("locals:", .{}); - for (self.locals.items, 0..) |sym, id| { - log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.strtab.get(sym.st_name), sym.st_value, sym.st_shndx }); - } - log.debug("globals:", .{}); - for (self.globals.items, 0..) |glob, id| { - const sym = self.symbol(glob); - log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.strtab.get(sym.st_name), sym.st_value, sym.st_shndx }); - } -} - -/// Returns pointer-to-symbol described at sym_index. -pub fn symbol(self: *const Elf, sym_index: u32) *elf.Elf64_Sym { - return &self.locals.items[sym_index]; -} - -/// Returns name of the symbol at sym_index. -pub fn symbolName(self: *const Elf, sym_index: u32) []const u8 { - const sym = self.locals.items[sym_index]; - return self.strtab.get(sym.st_name).?; -} - -/// Returns pointer to the global entry for `name` if one exists. -pub fn global(self: *const Elf, name: []const u8) ?*u32 { - const global_index = self.resolver.get(name) orelse return null; - return &self.globals.items[global_index]; -} - -/// Returns the index of the global entry for `name` if one exists. -pub fn globalIndex(self: *const Elf, name: []const u8) ?u32 { - return self.resolver.get(name); -} - -/// Returns global entry at `index`. -pub fn globalByIndex(self: *const Elf, index: u32) u32 { - assert(index < self.globals.items.len); - return self.globals.items[index]; -} - -const GetOrPutGlobalResult = struct { - found_existing: bool, - value_ptr: *u32, -}; - -/// Return pointer to the global entry for `name` if one exists. -/// Puts a new global entry for `name` if one doesn't exist, and -/// returns a pointer to it. -pub fn getOrPutGlobal(self: *Elf, name: []const u8) !GetOrPutGlobalResult { - if (self.global(name)) |ptr| { - return GetOrPutGlobalResult{ .found_existing = true, .value_ptr = ptr }; - } - const gpa = self.base.allocator; - const global_index = try self.allocateGlobal(); - const global_name = try gpa.dupe(u8, name); - _ = try self.resolver.put(gpa, global_name, global_index); - const ptr = &self.globals.items[global_index]; - return GetOrPutGlobalResult{ .found_existing = false, .value_ptr = ptr }; -} - pub fn atom(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 atomIndexForSymbol(self: *Elf, sym_index: u32) ?Atom.Index { - return self.atom_by_index_table.get(sym_index); +pub fn addAtom(self: *Elf) !Atom.Index { + const index = @as(Atom.Index, @intCast(self.atoms.items.len)); + const atom_ptr = try self.atoms.addOne(self.base.allocator); + atom_ptr.* = .{ .atom_index = index }; + return index; } -fn dumpState(self: *Elf ) std.fmt.Formatter(fmtDumpState) { +pub fn file(self: *Elf, index: File.Index) ?File { + const tag = self.files.items(.tags)[index]; + return switch (tag) { + .null => null, + .linker_defined => .{ .linker_defined = &self.files.items(.data)[index].linker_defined }, + .zig_module => .{ .zig_module = &self.files.items(.data)[index].zig_module }, + }; +} + +/// Returns pointer-to-symbol described at sym_index. +pub fn symbol(self: *Elf, sym_index: Symbol.Index) *Symbol { + return &self.symbols.items[sym_index]; +} + +pub fn addSymbol(self: *Elf) !Symbol.Index { + try self.symbols.ensureUnusedCapacity(self.base.allocator, 1); + const index = blk: { + if (self.symbols_free_list.popOrNull()) |index| { + log.debug(" (reusing symbol index {d})", .{index}); + break :blk index; + } else { + log.debug(" (allocating symbol index {d})", .{self.symbols.items.len}); + const index = @as(Symbol.Index, @intCast(self.symbols.items.len)); + _ = self.symbols.addOneAssumeCapacity(); + break :blk index; + } + }; + self.symbols.items[index] = .{ .symbol_index = index }; + return index; +} + +const GetOrPutGlobalResult = struct { + found_existing: bool, + index: Symbol.Index, +}; + +pub fn getOrPutGlobal(self: *Elf, name_off: u32) !GetOrPutGlobalResult { + const gpa = self.base.allocator; + const gop = try self.resolver.getOrPut(gpa, name_off); + if (!gop.found_existing) { + const index = try self.addSymbol(); + const global = self.symbol(index); + global.name_offset = name_off; + gop.value_ptr.* = index; + } + return .{ + .found_existing = gop.found_existing, + .index = gop.value_ptr.*, + }; +} + +pub fn getGlobalByName(self: *Elf, name: []const u8) ?Symbol.Index { + const name_off = self.strtab.getOffset(name) orelse return null; + return self.resolver.get(name_off); +} + +fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) { return .{ .data = self }; } -fn fmtDumpState(self: *Elf, +fn fmtDumpState( + self: *Elf, comptime unused_fmt_string: []const u8, options: std.fmt.FormatOptions, writer: anytype, ) !void { + _ = unused_fmt_string; + _ = options; + if (self.zig_module_index) |index| { + const zig_module = self.file(index).?.zig_module; + try writer.print("zig_module({d}) : (zig module)\n", .{index}); + try writer.print("{}\n", .{zig_module.fmtSymtab(self)}); + } + if (self.linker_defined_index) |index| { + const linker_defined = self.file(index).?.linker_defined; + try writer.print("linker_defined({d}) : (linker defined)\n", .{index}); + try writer.print("{}\n", .{linker_defined.fmtSymtab(self)}); + } } -pub const null_sym = elf.Elf64_Sym{ - .st_name = 0, - .st_info = 0, - .st_other = 0, - .st_shndx = 0, - .st_value = 0, - .st_size = 0, -}; - const default_entry_addr = 0x8000000; pub const base_tag: link.File.Tag = .elf; @@ -3497,21 +3305,20 @@ const Section = struct { const LazySymbolMetadata = struct { const State = enum { unused, pending_flush, flushed }; - text_atom: Atom.Index = undefined, - rodata_atom: Atom.Index = undefined, + text_symbol_index: Symbol.Index = undefined, + rodata_symbol_index: Symbol.Index = undefined, text_state: State = .unused, rodata_state: State = .unused, }; const DeclMetadata = struct { - atom: Atom.Index, - shdr: u16, + symbol_index: Symbol.Index, /// A list of all exports aliases of this Decl. - exports: std.ArrayListUnmanaged(u32) = .{}, + exports: std.ArrayListUnmanaged(Symbol.Index) = .{}, - fn @"export"(m: DeclMetadata, elf_file: *const Elf, name: []const u8) ?*u32 { + fn @"export"(m: DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 { for (m.exports.items) |*exp| { - if (mem.eql(u8, name, elf_file.symbolName(exp.*))) return exp; + if (mem.eql(u8, name, elf_file.symbol(exp.*).name(elf_file))) return exp; } return null; } @@ -3543,13 +3350,14 @@ pub const Atom = @import("Elf/Atom.zig"); const Cache = std.Build.Cache; const Compilation = @import("../Compilation.zig"); const Dwarf = @import("Dwarf.zig"); -const File = @import("Elf/File.zig"); +const File = @import("Elf/file.zig").File; const LinkerDefined = @import("Elf/LinkerDefined.zig"); const Liveness = @import("../Liveness.zig"); const LlvmObject = @import("../codegen/llvm.zig").Object; const Module = @import("../Module.zig"); const InternPool = @import("../InternPool.zig"); const Package = @import("../Package.zig"); +const Symbol = @import("Elf/Symbol.zig"); const StringTable = @import("strtab.zig").StringTable; const TableSection = @import("table_section.zig").TableSection; const Type = @import("../type.zig").Type; diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index aa1d3920fb..faebd0b6fa 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -1,81 +1,65 @@ -/// Each decl always gets a local symbol with the fully qualified name. -/// The vaddr and size are found here directly. -/// The file offset is found by computing the vaddr offset from the section vaddr -/// the symbol references, and adding that to the file offset of the section. -/// If this field is 0, it means the codegen size = 0 and there is no symbol or -/// offset table entry. -sym_index: u32 = 0, +/// Address allocated for this Atom. +value: u64 = 0, + +/// Name of this Atom. +name_offset: u32 = 0, + +/// Index into linker's input file table. +file_index: File.Index = 0, + +/// Size of this atom +size: u64 = 0, + +/// Alignment of this atom as a power of two. +alignment: u8 = 0, + +/// Index of the input section. +input_section_index: u16 = 0, + +/// Index of the output section. +output_section_index: u16 = 0, + +/// Index of the input section containing this atom's relocs. +relocs_section_index: u16 = 0, + +/// Index of this atom in the linker's atoms table. +atom_index: Index = 0, + +/// Specifies whether this atom is alive or has been garbage collected. +alive: bool = true, + +/// Specifies if the atom has been visited during garbage collection. +visited: bool = false, + +/// Start index of FDEs referencing this atom. +fde_start: u32 = 0, + +/// End index of FDEs referencing this atom. +fde_end: u32 = 0, /// 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_index: ?Index = null, next_index: ?Index = null, -pub const Index = u32; - -pub const Reloc = struct { - target: u32, - offset: u64, - addend: u32, - prev_vaddr: u64, -}; - -pub fn symbolIndex(self: *const Atom) ?u32 { - if (self.sym_index == 0) return null; - return self.sym_index; -} - -pub fn symbol(self: *const Atom, elf_file: *Elf) *elf.Elf64_Sym { - return elf_file.symbol(self.symbolIndex().?); -} - -pub fn name(self: *const Atom, elf_file: *Elf) []const u8 { - return elf_file.symbolName(self.symbolIndex().?); -} - -/// If entry already exists, returns index to it. -/// Otherwise, creates a new entry in the Global Offset Table for this Atom. -pub fn getOrCreateOffsetTableEntry(self: *const Atom, elf_file: *Elf) !u32 { - const sym_index = self.symbolIndex().?; - if (elf_file.got_table.lookup.get(sym_index)) |index| return index; - const index = try elf_file.got_table.allocateEntry(elf_file.base.allocator, sym_index); - elf_file.got_table_count_dirty = true; - return index; -} - -pub fn getOffsetTableAddress(self: *const Atom, elf_file: *Elf) u64 { - const sym_index = self.symbolIndex().?; - const got_entry_index = elf_file.got_table.lookup.get(sym_index).?; - const target = elf_file.base.options.target; - const ptr_bits = target.ptrBitWidth(); - const ptr_bytes: u64 = @divExact(ptr_bits, 8); - const got = elf_file.program_headers.items[elf_file.phdr_got_index.?]; - return got.p_vaddr + got_entry_index * ptr_bytes; +pub fn name(self: Atom, elf_file: *Elf) []const u8 { + return elf_file.strtab.getAssumeExists(self.name_offset); } /// 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: *const Atom, elf_file: *Elf) u64 { - const self_sym = self.symbol(elf_file); - if (self.next_index) |next_index| { - const next = elf_file.atom(next_index); - const next_sym = next.symbol(elf_file); - return next_sym.st_value - self_sym.st_value; - } else { - // We are the last block. The capacity is limited only by virtual address space. - return std.math.maxInt(u32) - self_sym.st_value; - } +pub fn capacity(self: Atom, elf_file: *Elf) u64 { + const next_value = if (self.next_index) |next_index| elf_file.atom(next_index).value else std.math.maxInt(u32); + return next_value - self.value; } -pub fn freeListEligible(self: *const Atom, elf_file: *Elf) bool { +pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { // No need to keep a free list node for the last block. const next_index = self.next_index orelse return false; const next = elf_file.atom(next_index); - const self_sym = self.symbol(elf_file); - const next_sym = next.symbol(elf_file); - const cap = next_sym.st_value - self_sym.st_value; - const ideal_cap = Elf.padToIdeal(self_sym.st_size); + const cap = next.value - self.value; + const ideal_cap = Elf.padToIdeal(self.size); if (cap <= ideal_cap) return false; const surplus = cap - ideal_cap; return surplus >= Elf.min_text_capacity; @@ -95,10 +79,149 @@ pub fn freeRelocations(elf_file: *Elf, atom_index: Index) void { if (removed_relocs) |*relocs| relocs.value.deinit(elf_file.base.allocator); } -const Atom = @This(); +pub fn allocate(self: *Atom, elf_file: *Elf) !u64 { + const phdr_index = elf_file.sections.items(.phdr_index)[self.output_section_index]; + const phdr = &elf_file.program_headers.items[phdr_index]; + const shdr = &elf_file.sections.items(.shdr)[self.output_section_index]; + const free_list = &elf_file.sections.items(.free_list)[self.output_section_index]; + const maybe_last_atom_index = &elf_file.sections.items(.last_atom_index)[self.output_section_index]; + const new_atom_ideal_capacity = Elf.padToIdeal(self.size); + const alignment = try std.math.powi(u64, 2, self.alignment); + + // 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 atom_placement: ?Atom.Index = null; + var free_list_removal: ?usize = null; + + // 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 = if (elf_file.base.child_pid == null) 0 else free_list.items.len; + while (i < free_list.items.len) { + const big_atom_index = free_list.items[i]; + const big_atom = elf_file.atom(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 cap = big_atom.capacity(elf_file); + const ideal_capacity = Elf.padToIdeal(cap); + const ideal_capacity_end_vaddr = std.math.add(u64, big_atom.value, ideal_capacity) catch ideal_capacity; + const capacity_end_vaddr = big_atom.value + cap; + const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity; + const new_start_vaddr = std.mem.alignBackward(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_atom.freeListEligible(elf_file)) { + _ = free_list.swapRemove(i); + } else { + i += 1; + } + continue; + } + // At this point we know that we will place the new block here. But the + // remaining question is whether there is still yet enough capacity left + // over for there to still be a free list node. + const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr; + const keep_free_list_node = remaining_capacity >= Elf.min_text_capacity; + + // Set up the metadata to be updated, after errors are no longer possible. + atom_placement = big_atom_index; + if (!keep_free_list_node) { + free_list_removal = i; + } + break :blk new_start_vaddr; + } else if (maybe_last_atom_index.*) |last_index| { + const last = elf_file.atom(last_index); + const ideal_capacity = Elf.padToIdeal(last.size); + const ideal_capacity_end_vaddr = last.value + ideal_capacity; + const new_start_vaddr = std.mem.alignForward(u64, ideal_capacity_end_vaddr, alignment); + // Set up the metadata to be updated, after errors are no longer possible. + atom_placement = last_index; + break :blk new_start_vaddr; + } else { + break :blk phdr.p_vaddr; + } + }; + + const expand_section = if (atom_placement) |placement_index| + elf_file.atom(placement_index).next_index == null + else + true; + if (expand_section) { + const needed_size = (vaddr + self.size) - phdr.p_vaddr; + try elf_file.growAllocSection(self.output_section_index, needed_size); + maybe_last_atom_index.* = self.atom_index; + + if (elf_file.dwarf) |_| { + // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address + // range of the compilation unit. When we expand the text section, this range changes, + // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty. + elf_file.debug_info_header_dirty = true; + // This becomes dirty for the same reason. We could potentially make this more + // fine-grained with the addition of support for more compilation units. It is planned to + // model each package as a different compilation unit. + elf_file.debug_aranges_section_dirty = true; + } + } + shdr.sh_addralign = @max(shdr.sh_addralign, alignment); + + // 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 (self.prev_index) |prev_index| { + const prev = elf_file.atom(prev_index); + prev.next_index = self.next_index; + } + if (self.next_index) |next_index| { + const next = elf_file.atom(next_index); + next.prev_index = self.prev_index; + } + + if (atom_placement) |big_atom_index| { + const big_atom = elf_file.atom(big_atom_index); + self.prev_index = big_atom_index; + self.next_index = big_atom.next_index; + big_atom.next_index = self.atom_index; + } else { + self.prev_index = null; + self.next_index = null; + } + if (free_list_removal) |i| { + _ = free_list.swapRemove(i); + } + return vaddr; +} + +pub fn shrink(self: *Atom, elf_file: *Elf) void { + _ = self; + _ = elf_file; +} + +pub fn grow(self: *Atom, elf_file: *Elf) !u64 { + const alignment = try std.math.powi(u64, 2, self.alignment); + const align_ok = std.mem.alignBackward(u64, self.value, alignment) == self.value; + const need_realloc = !align_ok or self.size > self.capacity(elf_file); + if (!need_realloc) return self.value; + return self.allocate(elf_file); +} + +pub const Index = u32; + +pub const Reloc = struct { + target: u32, + offset: u64, + addend: u32, + prev_vaddr: u64, +}; const std = @import("std"); const assert = std.debug.assert; const elf = std.elf; +const Atom = @This(); const Elf = @import("../Elf.zig"); +const File = @import("file.zig").File; diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig index e7cb35b769..99125a9eb6 100644 --- a/src/link/Elf/LinkerDefined.zig +++ b/src/link/Elf/LinkerDefined.zig @@ -23,14 +23,14 @@ pub fn addGlobal(self: *LinkerDefined, name: [:0]const u8, elf_file: *Elf) !u32 .st_size = 0, }); const off = try elf_file.internString("{s}", .{name}); - const gop = try elf_file.getOrCreateGlobal(off); + const gop = try elf_file.getOrPutGlobal(off); self.symbols.addOneAssumeCapacity().* = gop.index; return gop.index; } pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void { for (self.symbols.items, 0..) |index, i| { - const sym_idx = @as(u32, @intCast(i)); + const sym_idx = @as(Symbol.Index, @intCast(i)); const this_sym = self.symtab.items[sym_idx]; if (this_sym.st_shndx == elf.SHN_UNDEF) continue; @@ -86,15 +86,19 @@ pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void { // } // } +pub fn sourceSymbol(self: *LinkerDefined, symbol_index: Symbol.Index) *elf.Elf64_Sym { + return &self.symtab.items[symbol_index]; +} + +pub fn globals(self: *LinkerDefined) []const Symbol.Index { + return self.symbols.items; +} + pub fn asFile(self: *LinkerDefined) File { return .{ .linker_defined = self }; } -pub inline fn getGlobals(self: *LinkerDefined) []const u32 { - return self.symbols.items; -} - -pub fn fmtSymtab(self: *InternalObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { +pub fn fmtSymtab(self: *LinkerDefined, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { return .{ .data = .{ .self = self, .elf_file = elf_file, @@ -102,7 +106,7 @@ pub fn fmtSymtab(self: *InternalObject, elf_file: *Elf) std.fmt.Formatter(format } const FormatContext = struct { - self: *InternalObject, + self: *LinkerDefined, elf_file: *Elf, }; @@ -115,8 +119,8 @@ fn formatSymtab( _ = unused_fmt_string; _ = options; try writer.writeAll(" globals\n"); - for (ctx.self.getGlobals()) |index| { - const global = ctx.elf_file.getSymbol(index); + for (ctx.self.globals()) |index| { + const global = ctx.elf_file.symbol(index); try writer.print(" {}\n", .{global.fmt(ctx.elf_file)}); } } diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index ffbe91aab9..79934840c2 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -32,9 +32,8 @@ extra_index: u32 = 0, pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool { const file_ptr = symbol.file(elf_file).?; - if (file_ptr == .shared) return symbol.sourceSymbol(elf_file).st_shndx == elf.SHN_ABS; - return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.shndx == 0 - and file_ptr != .linker_defined and file_ptr != .zig_module; + // if (file_ptr == .shared) return symbol.sourceSymbol(elf_file).st_shndx == elf.SHN_ABS; + return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.output_section_index == 0 and file_ptr != .linker_defined and file_ptr != .zig_module; } pub fn isLocal(symbol: Symbol) bool { @@ -42,34 +41,30 @@ pub fn isLocal(symbol: Symbol) bool { } pub inline fn isIFunc(symbol: Symbol, elf_file: *Elf) bool { - return symbol.@"type"(elf_file) == elf.STT_GNU_IFUNC; + return symbol.type(elf_file) == elf.STT_GNU_IFUNC; } pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 { - const file_ptr = symbol.file(elf_file).?; const s_sym = symbol.sourceSymbol(elf_file); - if (s_sym.st_type() == elf.STT_GNU_IFUNC and file_ptr == .shared) return elf.STT_FUNC; + // const file_ptr = symbol.file(elf_file).?; + // if (s_sym.st_type() == elf.STT_GNU_IFUNC and file_ptr == .shared) return elf.STT_FUNC; return s_sym.st_type(); } pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 { - return elf_file.strtab.getAssumeExists(symbol.name); + return elf_file.strtab.getAssumeExists(symbol.name_offset); } pub fn atom(symbol: Symbol, elf_file: *Elf) ?*Atom { - return elf_file.atom(symbol.atom); + return elf_file.atom(symbol.atom_index); } pub fn file(symbol: Symbol, elf_file: *Elf) ?File { - return elf_file.file(symbol.file); + return elf_file.file(symbol.file_index); } -pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { - const file_ptr = symbol.file(elf_file).?; - return switch (file_ptr) { - .linker_defined, .zig_module => |x| x.symtab.items[symbol.sym_idx], - inline else => |x| x.symtab[symbol.sym_idx], - }; +pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) *elf.Elf64_Sym { + return symbol.file(elf_file).?.sourceSymbol(symbol.symbol_index); } pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { @@ -82,9 +77,29 @@ pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { return file_ptr.symbolRank(sym, in_archive); } +/// If entry already exists, returns index to it. +/// Otherwise, creates a new entry in the Global Offset Table for this Symbol. +pub fn getOrCreateOffsetTableEntry(self: Symbol, elf_file: *Elf) !Symbol.Index { + if (elf_file.got_table.lookup.get(self.symbol_index)) |index| return index; + const index = try elf_file.got_table.allocateEntry(elf_file.base.allocator, self.symbol_index); + elf_file.got_table_count_dirty = true; + return index; +} + +pub fn getOffsetTableAddress(self: Symbol, elf_file: *Elf) u64 { + const got_entry_index = elf_file.got_table.lookup.get(self.symbol_index).?; + const target = elf_file.base.options.target; + const ptr_bits = target.ptrBitWidth(); + const ptr_bytes: u64 = @divExact(ptr_bits, 8); + const got = elf_file.program_headers.items[elf_file.phdr_got_index.?]; + return got.p_vaddr + got_entry_index * ptr_bytes; +} + pub fn address(symbol: Symbol, opts: struct { plt: bool = true, }, elf_file: *Elf) u64 { + _ = elf_file; + _ = opts; // if (symbol.flags.copy_rel) { // return elf_file.sectionAddress(elf_file.copy_rel_sect_index.?) + symbol.value; // } @@ -100,11 +115,11 @@ pub fn address(symbol: Symbol, opts: struct { return symbol.value; } -pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { - if (!symbol.flags.got) return 0; - const extra = symbol.extra(elf_file).?; - return elf_file.gotEntryAddress(extra.got); -} +// pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { +// if (!symbol.flags.got) return 0; +// const extra = symbol.extra(elf_file).?; +// return elf_file.gotEntryAddress(extra.got); +// } // pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 { // if (!symbol.flags.tlsgd) return 0; @@ -136,22 +151,22 @@ pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { // @min(alignment, try std.math.powi(u64, 2, @ctz(s_sym.st_value))); // } -pub fn addExtra(symbol: *Symbol, extra: Extra, elf_file: *Elf) !void { - symbol.extra = try elf_file.addSymbolExtra(extra); +pub fn addExtra(symbol: *Symbol, extras: Extra, elf_file: *Elf) !void { + symbol.extra = try elf_file.addSymbolExtra(extras); } pub fn extra(symbol: Symbol, elf_file: *Elf) ?Extra { - return elf_file.symbolExtra(symbol.extra); + return elf_file.symbolExtra(symbol.extra_index); } -pub fn setExtra(symbol: Symbol, extra: Extra, elf_file: *Elf) void { - elf_file.setSymbolExtra(symbol.extra, extra); +pub fn setExtra(symbol: Symbol, extras: Extra, elf_file: *Elf) void { + elf_file.setSymbolExtra(symbol.extra_index, extras); } pub fn asElfSym(symbol: Symbol, st_name: u32, elf_file: *Elf) elf.Elf64_Sym { const file_ptr = symbol.file(elf_file).?; const s_sym = symbol.sourceSymbol(elf_file); - const st_type = symbol.@"type"(elf_file); + const st_type = symbol.type(elf_file); const st_bind: u8 = blk: { if (symbol.isLocal()) break :blk 0; if (symbol.flags.weak) break :blk elf.STB_WEAK; @@ -161,7 +176,7 @@ pub fn asElfSym(symbol: Symbol, st_name: u32, elf_file: *Elf) elf.Elf64_Sym { const st_shndx = blk: { // if (symbol.flags.copy_rel) break :blk elf_file.copy_rel_sect_index.?; // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; - if (symbol.atom(elf_file) == null and file_ptr != .linker_defined and file_ptr != .zig_module) + if (symbol.atom(elf_file) == null and file_ptr != .linker_defined and file_ptr != .zig_module) break :blk elf.SHN_ABS; break :blk symbol.shndx; }; @@ -221,13 +236,13 @@ fn formatName( _ = unused_fmt_string; const elf_file = ctx.elf_file; const symbol = ctx.symbol; - try writer.writeAll(symbol.getName(elf_file)); - switch (symbol.ver_idx & elf.VERSYM_VERSION) { + try writer.writeAll(symbol.name(elf_file)); + switch (symbol.version_index & elf.VERSYM_VERSION) { elf.VER_NDX_LOCAL, elf.VER_NDX_GLOBAL => {}, else => { unreachable; // const shared = symbol.getFile(elf_file).?.shared; - // try writer.print("@{s}", .{shared.getVersionString(symbol.ver_idx)}); + // try writer.print("@{s}", .{shared.getVersionString(symbol.version_index)}); }, } } @@ -248,29 +263,27 @@ fn format2( _ = options; _ = unused_fmt_string; const symbol = ctx.symbol; - try writer.print("%{d} : {s} : @{x}", .{ symbol.sym_idx, symbol.fmtName(ctx.elf_file), symbol.value }); - if (symbol.getFile(ctx.elf_file)) |file| { + try writer.print("%{d} : {s} : @{x}", .{ symbol.symbol_index, symbol.fmtName(ctx.elf_file), symbol.value }); + if (symbol.file(ctx.elf_file)) |file_ptr| { if (symbol.isAbs(ctx.elf_file)) { - if (symbol.getSourceSymbol(ctx.elf_file).st_shndx == elf.SHN_UNDEF) { + if (symbol.sourceSymbol(ctx.elf_file).st_shndx == elf.SHN_UNDEF) { try writer.writeAll(" : undef"); } else { try writer.writeAll(" : absolute"); } - } else if (symbol.shndx != 0) { - try writer.print(" : sect({d})", .{symbol.shndx}); + } else if (symbol.output_section_index != 0) { + try writer.print(" : sect({d})", .{symbol.output_section_index}); } - if (symbol.getAtom(ctx.elf_file)) |atom| { - try writer.print(" : atom({d})", .{atom.atom_index}); + if (symbol.atom(ctx.elf_file)) |atom_ptr| { + try writer.print(" : atom({d})", .{atom_ptr.atom_index}); } var buf: [2]u8 = .{'_'} ** 2; if (symbol.flags.@"export") buf[0] = 'E'; if (symbol.flags.import) buf[1] = 'I'; try writer.print(" : {s}", .{&buf}); if (symbol.flags.weak) try writer.writeAll(" : weak"); - switch (file) { - .internal => |x| try writer.print(" : internal({d})", .{x.index}), - .object => |x| try writer.print(" : object({d})", .{x.index}), - .shared => |x| try writer.print(" : shared({d})", .{x.index}), + switch (file_ptr) { + inline else => |x| try writer.print(" : {s}({d})", .{ @tagName(file_ptr), x.index }), } } else try writer.writeAll(" : unresolved"); } @@ -331,7 +344,8 @@ const elf = std.elf; const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; -const InternalObject = @import("InternalObject.zig"); -const Object = @import("Object.zig"); -const SharedObject = @import("SharedObject.zig"); +const LinkerDefined = @import("LinkerDefined.zig"); +// const Object = @import("Object.zig"); +// const SharedObject = @import("SharedObject.zig"); const Symbol = @This(); +const ZigModule = @import("ZigModule.zig"); diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index b76990a29b..ebfcb6c9e4 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -1,31 +1,91 @@ index: File.Index, -elf_locals: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, -locals: std.ArrayListUnmanaged(Symbol.Index) = .{}, -elf_globals: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, -globals: std.ArrayListUnmanaged(Symbol.Index) = .{}, + +elf_local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, +local_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, + +elf_global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, +global_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, + +atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, + alive: bool = true, // output_symtab_size: Elf.SymtabSize = .{}, pub fn deinit(self: *ZigModule, allocator: Allocator) void { - self.elf_locals.deinit(allocator); - self.locals.deinit(allocator); - self.elf_globals.deinit(allocator); - self.globals.deinit(allocator); + self.elf_local_symbols.deinit(allocator); + self.local_symbols.deinit(allocator); + self.elf_global_symbols.deinit(allocator); + self.global_symbols.deinit(allocator); + self.atoms.deinit(allocator); +} + +pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !Symbol.Index { + const gpa = elf_file.base.allocator; + const atom_index = try elf_file.addAtom(); + const symbol_index = try elf_file.addSymbol(); + + const atom_ptr = elf_file.atom(atom_index); + atom_ptr.file_index = self.index; + atom_ptr.output_section_index = output_section_index; + + const symbol_ptr = elf_file.symbol(symbol_index); + symbol_ptr.file_index = self.index; + symbol_ptr.atom_index = atom_index; + symbol_ptr.output_section_index = output_section_index; + + const local_esym = try self.elf_local_symbols.addOne(gpa); + local_esym.* = .{ + .st_name = 0, + .st_info = elf.STB_LOCAL << 4, + .st_other = 0, + .st_shndx = output_section_index, + .st_value = 0, + .st_size = 0, + }; + + try self.atoms.append(gpa, atom_index); + try self.local_symbols.putNoClobber(gpa, symbol_index, {}); + + return symbol_index; +} + +pub fn addGlobal(self: *ZigModule, name: [:0]const u8, elf_file: *Elf) !Symbol.Index { + const gpa = elf_file.base.allocator; + try self.elf_global_symbols.ensureUnusedCapacity(gpa, 1); + try self.global_symbols.ensureUnusedCapacity(gpa, 1); + const off = try elf_file.strtab.insert(gpa, name); + self.elf_global_symbols.appendAssumeCapacity(.{ + .st_name = off, + .st_info = elf.STB_GLOBAL << 4, + .st_other = 0, + .st_shndx = 0, + .st_value = 0, + .st_size = 0, + }); + const gop = try elf_file.getOrPutGlobal(off); + self.global_symbols.putAssumeCapacityNoClobber(gop.index, {}); + return gop.index; +} + +pub fn sourceSymbol(self: *ZigModule, symbol_index: Symbol.Index) *elf.Elf64_Sym { + if (self.local_symbols.get(symbol_index)) |_| return &self.elf_local_symbols.items[symbol_index]; + assert(self.global_symbols.get(symbol_index) != null); + return &self.elf_global_symbols.items[symbol_index]; +} + +pub fn locals(self: *ZigModule) []const Symbol.Index { + return self.local_symbols.keys(); +} + +pub fn globals(self: *ZigModule) []const Symbol.Index { + return self.global_symbols.keys(); } pub fn asFile(self: *ZigModule) File { return .{ .zig_module = self }; } -pub fn getLocals(self: *ZigModule) []const Symbol.Index { - return self.locals.items; -} - -pub fn getGlobals(self: *ZigModule) []const Symbol.Index { - return self.globals.items; -} - pub fn fmtSymtab(self: *ZigModule, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { return .{ .data = .{ .self = self, @@ -47,23 +107,26 @@ fn formatSymtab( _ = unused_fmt_string; _ = options; try writer.writeAll(" locals\n"); - for (ctx.self.getLocals()) |index| { + for (ctx.self.locals()) |index| { const local = ctx.elf_file.symbol(index); try writer.print(" {}\n", .{local.fmt(ctx.elf_file)}); } try writer.writeAll(" globals\n"); - for (ctx.self.getGlobals()) |index| { - const global = ctx.elf_file.getSymbol(index); + for (ctx.self.globals()) |index| { + const global = ctx.elf_file.symbol(index); try writer.print(" {}\n", .{global.fmt(ctx.elf_file)}); } } +const assert = std.debug.assert; const std = @import("std"); const elf = std.elf; const Allocator = std.mem.Allocator; +const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; +const Module = @import("../../Module.zig"); const ZigModule = @This(); // const Object = @import("Object.zig"); const Symbol = @import("Symbol.zig"); diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 25624389ed..45a063cda5 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -10,6 +10,12 @@ pub const File = union(enum) { }; } + pub fn sourceSymbol(file: File, symbol_index: Symbol.Index) *elf.Elf64_Sym { + return switch (file) { + inline else => |x| x.sourceSymbol(symbol_index), + }; + } + pub fn fmtPath(file: File) std.fmt.Formatter(formatPath) { return .{ .data = file }; } @@ -107,4 +113,5 @@ const Elf = @import("../Elf.zig"); const LinkerDefined = @import("LinkerDefined.zig"); // const Object = @import("Object.zig"); // const SharedObject = @import("SharedObject.zig"); +const Symbol = @import("Symbol.zig"); const ZigModule = @import("ZigModule.zig"); diff --git a/src/link/strtab.zig b/src/link/strtab.zig index 0d71c9bf83..f854225ef6 100644 --- a/src/link/strtab.zig +++ b/src/link/strtab.zig @@ -100,13 +100,13 @@ pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type { }); } - pub fn get(self: Self, off: u32) ?[]const u8 { + pub fn get(self: Self, off: u32) ?[:0]const u8 { log.debug("getting string at 0x{x}", .{off}); if (off >= self.buffer.items.len) return null; return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.buffer.items.ptr + off)), 0); } - pub fn getAssumeExists(self: Self, off: u32) []const u8 { + pub fn getAssumeExists(self: Self, off: u32) [:0]const u8 { return self.get(off) orelse unreachable; }