diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2023-02-03 12:49:40 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2023-02-03 12:49:40 -0700 |
| commit | fab9b7110ed1fa7bb082aad5e095047441db2b24 (patch) | |
| tree | 81fef60aa45e7980dab8f3e23e5b5e92b40ee0a9 /src/link/Elf.zig | |
| parent | d20d69b59e6b65a99f45cb6a45c14e887034dd18 (diff) | |
| parent | 60935decd318498529a016eeb1379d943a7e830d (diff) | |
| download | zig-fab9b7110ed1fa7bb082aad5e095047441db2b24.tar.gz zig-fab9b7110ed1fa7bb082aad5e095047441db2b24.zip | |
Merge remote-tracking branch 'origin/master' into llvm16
Diffstat (limited to 'src/link/Elf.zig')
| -rw-r--r-- | src/link/Elf.zig | 1298 |
1 files changed, 665 insertions, 633 deletions
diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 48e0320dc6..45952da6c0 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1,40 +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 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; +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.getGlobalName(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.getGlobalName(exp.*))) return exp; + } + return null; + } +}; + base: File, dwarf: ?Dwarf = null, @@ -45,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, @@ -62,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, @@ -110,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` @@ -170,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) @@ -187,67 +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; -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 TextBlock = struct { - /// 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. - local_sym_index: u32, - /// This field is undefined for symbols with size = 0. - 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: ?*TextBlock, - next: ?*TextBlock, - - dbg_info_atom: Dwarf.Atom, - - pub const empty = TextBlock{ - .local_sym_index = 0, - .offset_table_index = undefined, - .prev = null, - .next = null, - .dbg_info_atom = undefined, - }; - - /// 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. - fn capacity(self: TextBlock, elf_file: Elf) u64 { - const self_sym = elf_file.local_symbols.items[self.local_sym_index]; - if (self.next) |next| { - const next_sym = elf_file.local_symbols.items[next.local_sym_index]; - 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; - } - } - - fn freeListEligible(self: TextBlock, elf_file: Elf) bool { - // No need to keep a free list node for the last block. - const next = self.next orelse return false; - const self_sym = elf_file.local_symbols.items[self.local_sym_index]; - const next_sym = elf_file.local_symbols.items[next.local_sym_index]; - const cap = next_sym.st_value - self_sym.st_value; - const ideal_cap = padToIdeal(self_sym.st_size); - if (cap <= ideal_cap) return false; - const surplus = cap - ideal_cap; - return surplus >= min_text_capacity; - } -}; - -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); @@ -279,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(); @@ -335,74 +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); - } - - 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); - - self.atoms.deinit(self.base.allocator); + if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa); + } + + 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); + { - 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(); } } 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); - assert(decl.link.elf.local_sym_index != 0); - const target = decl.link.elf.local_sym_index; - 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, @@ -423,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) { @@ -433,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) { @@ -441,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) { @@ -468,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; } @@ -487,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); -} - -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, @@ -525,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, @@ -535,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; } @@ -552,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, @@ -575,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, @@ -585,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; } @@ -599,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, @@ -609,148 +541,145 @@ 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); @@ -758,27 +687,30 @@ pub fn populateMissingMetadata(self: *Elf) !void { 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_str_section_index = @intCast(u16, self.sections.slice().len); + assert(dw.strtab.buffer.items.len == 0); + try dw.strtab.buffer.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); + self.debug_info_section_index = @intCast(u16, self.sections.slice().len); const file_size_hint = 200; const p_align = 1; @@ -787,24 +719,27 @@ pub fn populateMissingMetadata(self: *Elf) !void { 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, + 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); + self.debug_abbrev_section_index = @intCast(u16, self.sections.slice().len); const file_size_hint = 128; const p_align = 1; @@ -813,24 +748,27 @@ pub fn populateMissingMetadata(self: *Elf) !void { 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, + 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); + self.debug_aranges_section_index = @intCast(u16, self.sections.slice().len); const file_size_hint = 160; const p_align = 16; @@ -839,24 +777,27 @@ pub fn populateMissingMetadata(self: *Elf) !void { 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, + 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); + self.debug_line_section_index = @intCast(u16, self.sections.slice().len); const file_size_hint = 250; const p_align = 1; @@ -865,17 +806,20 @@ pub fn populateMissingMetadata(self: *Elf) !void { 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, + 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; @@ -891,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; } @@ -922,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; } @@ -932,24 +876,27 @@ 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 sym = self.local_symbols.items[last.local_sym_index]; + 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: { break :blk shdr.sh_size; } 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, }); @@ -975,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: { @@ -988,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( @@ -1059,6 +1006,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(); @@ -1077,12 +1025,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node { 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 source_sym = self.local_symbols.items[atom.local_sym_index]; - const source_shdr = self.sections.items[source_sym.st_shndx]; + const atom = self.getAtom(atom_index); + const source_sym = atom.getSymbol(self); + 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]; @@ -1093,10 +1042,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) { @@ -1174,8 +1123,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]); @@ -1186,8 +1135,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]; @@ -1203,20 +1152,20 @@ 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); + if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { + try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false); + const debug_strtab_sect = self.sections.items(.shdr)[shdr_index]; + try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset); self.debug_strtab_dirty = false; } } @@ -1231,7 +1180,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 @@ -1240,12 +1189,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); } @@ -1253,12 +1203,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); } @@ -2069,7 +2020,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; @@ -2081,113 +2032,145 @@ 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 = self.local_symbols.items[text_block.local_sym_index]; - 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) }); - const free_list = self.atom_free_lists.getPtr(phdr_index).?; + Atom.freeRelocations(self, atom_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; } - if (self.dwarf) |*dw| { - dw.freeAtom(&text_block.dbg_info_atom); - } + // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. + 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); + self.getAtomPtr(atom_index).local_sym_index = 0; + + self.offset_table_free_list.append(self.base.allocator, atom.offset_table_index) catch {}; } -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 = self.local_symbols.items[text_block.local_sym_index]; +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, + }; + 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 = self.local_symbols.items[big_block.local_sym_index]; - 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; @@ -2201,29 +2184,33 @@ 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 = self.local_symbols.items[last.local_sym_index]; - 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 @@ -2238,23 +2225,28 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al } 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); @@ -2262,7 +2254,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al return vaddr; } -fn allocateLocalSymbol(self: *Elf) !u32 { +pub fn allocateLocalSymbol(self: *Elf) !u32 { try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1); const index = blk: { @@ -2289,40 +2281,30 @@ fn allocateLocalSymbol(self: *Elf) !u32 { return index; } -pub fn allocateDeclIndexes(self: *Elf, decl_index: Module.Decl.Index) !void { - if (self.llvm_object) |_| return; - - const mod = self.base.options.module.?; - const decl = mod.declPtr(decl_index); - if (decl.link.elf.local_sym_index != 0) return; - +pub fn allocateGotOffset(self: *Elf) !u32 { try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1); - try self.decls.putNoClobber(self.base.allocator, decl_index, null); - const decl_name = try decl.getFullyQualifiedName(mod); - defer self.base.allocator.free(decl_name); - - log.debug("allocating symbol indexes for {s}", .{decl_name}); - decl.link.elf.local_sym_index = try self.allocateLocalSymbol(); - try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.elf.local_sym_index, &decl.link.elf); + const index = blk: { + if (self.offset_table_free_list.popOrNull()) |index| { + log.debug(" (reusing GOT offset at index {d})", .{index}); + break :blk index; + } else { + log.debug(" (allocating GOT offset at index {d})", .{self.offset_table.items.len}); + const index = @intCast(u32, self.offset_table.items.len); + _ = self.offset_table.addOneAssumeCapacity(); + self.offset_table_count_dirty = true; + break :blk index; + } + }; - if (self.offset_table_free_list.popOrNull()) |i| { - decl.link.elf.offset_table_index = i; - } else { - decl.link.elf.offset_table_index = @intCast(u32, self.offset_table.items.len); - _ = self.offset_table.addOneAssumeCapacity(); - self.offset_table_count_dirty = true; - } - self.offset_table.items[decl.link.elf.offset_table_index] = 0; + self.offset_table.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| { - self.freeTextBlock(atom, self.phdr_load_ro_index.?); - self.local_symbol_free_list.append(self.base.allocator, atom.local_sym_index) catch {}; - self.local_symbols.items[atom.local_sym_index].st_info = 0; - _ = self.atom_by_index_table.remove(atom.local_sym_index); + self.freeAtom(atom); } unnamed_consts.clearAndFree(self.base.allocator); } @@ -2335,52 +2317,59 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { const mod = self.base.options.module.?; const decl = mod.declPtr(decl_index); - const kv = self.decls.fetchRemove(decl_index); - if (kv.?.value) |index| { - self.freeTextBlock(&decl.link.elf, index); + log.debug("freeDecl {*}", .{decl}); + + 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); } - // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. - if (decl.link.elf.local_sym_index != 0) { - self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {}; - self.local_symbols.items[decl.link.elf.local_sym_index].st_info = 0; - _ = self.atom_by_index_table.remove(decl.link.elf.local_sym_index); - decl.link.elf.local_sym_index = 0; - - self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {}; + if (self.dwarf) |*dw| { + dw.freeDecl(decl_index); } +} - 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); @@ -2390,61 +2379,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); - assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes() - const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index]; - 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.local_sym_index); + 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 }); + + self.offset_table.items[atom.offset_table_index] = vaddr; + local_sym.st_value = vaddr; + local_sym.st_size = code.len; - try self.writeSymbol(decl.link.elf.local_sym_index); - try self.writeOffsetTableEntry(decl.link.elf.offset_table_index); + 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; @@ -2461,12 +2454,15 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven const tracy = trace(@src()); defer tracy.end(); - var code_buffer = std.ArrayList(u8).init(self.base.allocator); - defer code_buffer.deinit(); - const decl_index = func.owner_decl; const decl = module.declPtr(decl_index); + + 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(); @@ -2479,7 +2475,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); const code = switch (res) { - .appended => code_buffer.items, + .ok => code_buffer.items, .fail => |em| { decl.analysis = .codegen_failure; try module.failed_decls.put(module.gpa, decl_index, em); @@ -2525,7 +2521,9 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v } } - assert(!self.unnamed_const_atoms.contains(decl_index)); + 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(); @@ -2542,19 +2540,18 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v }, &code_buffer, .{ .dwarf = ds, }, .{ - .parent_atom_index = decl.link.elf.local_sym_index, + .parent_atom_index = atom.getSymbolIndex().?, }) else try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .none, .{ - .parent_atom_index = decl.link.elf.local_sym_index, + .parent_atom_index = atom.getSymbolIndex().?, }); const code = switch (res) { - .externally_managed => |x| x, - .appended => code_buffer.items, + .ok => code_buffer.items, .fail => |em| { decl.analysis = .codegen_failure; try module.failed_decls.put(module.gpa, decl_index, em); @@ -2579,47 +2576,38 @@ 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; - 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).?; - log.debug("allocating symbol indexes for {s}", .{name}); - atom.local_sym_index = try self.allocateLocalSymbol(); - try self.atom_by_index_table.putNoClobber(self.base.allocator, atom.local_sym_index, atom); + const atom_index = try self.createAtom(); const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{ .none = {}, }, .{ - .parent_atom_index = atom.local_sym_index, + .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?, }); const code = switch (res) { - .externally_managed => |x| x, - .appended => code_buffer.items, + .ok => code_buffer.items, .fail => |em| { decl.analysis = .codegen_failure; try mod.failed_decls.put(mod.gpa, decl_index, em); @@ -2629,31 +2617,27 @@ 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 local_sym = &self.local_symbols.items[atom.local_sym_index]; - 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, - }; - - try self.writeSymbol(atom.local_sym_index); - try unnamed_consts.append(self.base.allocator, atom); + const shdr_index = self.rodata_section_index.?; + const phdr_index = self.sections.items(.phdr_index)[shdr_index]; + const local_sym = self.getAtom(atom_index).getSymbolPtr(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); + + log.debug("allocated text block for {s} at 0x{x}", .{ name, local_sym.st_value }); + + try self.writeSymbol(self.getAtom(atom_index).getSymbolIndex().?); + 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.local_sym_index; + return self.getAtom(atom_index).getSymbolIndex().?; } pub fn updateDeclExports( @@ -2672,17 +2656,16 @@ pub fn updateDeclExports( const tracy = trace(@src()); defer tracy.end(); - try self.global_symbols.ensureUnusedCapacity(self.base.allocator, exports.len); + const gpa = self.base.allocator; + const decl = module.declPtr(decl_index); - if (decl.link.elf.local_sym_index == 0) return; - const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index]; + const atom_index = try self.getOrCreateAtomForDecl(decl_index); + const atom = self.getAtom(atom_index); + const decl_sym = atom.getSymbol(self); + const decl_metadata = self.decls.getPtr(decl_index).?; + const shdr_index = decl_metadata.shdr; - 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).?; + try self.global_symbols.ensureUnusedCapacity(gpa, exports.len); for (exports) |exp| { if (exp.options.section) |section_name| { @@ -2715,10 +2698,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, @@ -2726,30 +2709,29 @@ 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); } } } /// Must be called only after a successful call to `updateDecl`. -pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl) !void { +pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl_index: Module.Decl.Index) !void { const tracy = trace(@src()); defer tracy.end(); + const decl = mod.declPtr(decl_index); const decl_name = try decl.getFullyQualifiedName(mod); defer self.base.allocator.free(decl_name); @@ -2757,16 +2739,18 @@ pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl) if (self.llvm_object) |_| return; if (self.dwarf) |*dw| { - try dw.updateDeclLineNumber(decl); + try dw.updateDeclLineNumber(mod, decl_index); } } -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 { @@ -2795,7 +2779,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]); } @@ -2803,7 +2787,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]); } @@ -2817,11 +2801,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 => { @@ -2847,7 +2831,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) { @@ -2869,7 +2853,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 => { @@ -2899,7 +2883,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), @@ -3042,7 +3026,7 @@ fn getLDMOption(target: std.Target) ?[]const u8 { } } -fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { +pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { return actual_size +| (actual_size / ideal_factor); } @@ -3249,10 +3233,58 @@ 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).?; +} + +/// Returns name of the global symbol at index. +pub fn getGlobalName(self: *const Elf, index: u32) []const u8 { + const sym = self.global_symbols.items[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); +} |
