diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 5e5c05c1cdb1442434d722fb40e0a9336f990b8c..982015f61f6e5a7fcae31368437972dfee24237e 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -54,7 +54,7 @@ phdr_to_shdr_table: std.AutoHashMapUnmanaged(u32, u32) = .{}, shdr_table_offset: ?u64 = null, /// Table of lists of atoms per output section. /// This table is not used to track incrementally generated atoms. -output_sections: std.AutoArrayHashMapUnmanaged(u32, std.ArrayListUnmanaged(Atom.Index)) = .{}, +output_sections: std.AutoArrayHashMapUnmanaged(u32, std.ArrayListUnmanaged(Ref)) = .{}, output_rela_sections: std.AutoArrayHashMapUnmanaged(u32, RelaSection) = .{}, /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. @@ -93,7 +93,6 @@ phdr_gnu_stack_index: ?u16 = null, /// TODO I think ELF permits multiple TLS segments but for now, assume one per file. phdr_tls_index: ?u16 = null, -entry_index: ?Symbol.Index = null, page_size: u32, default_sym_version: elf.Elf64_Versym, @@ -174,25 +173,6 @@ shstrtab_section_index: ?u32 = null, strtab_section_index: ?u32 = null, symtab_section_index: ?u32 = null, -// Linker-defined symbols -dynamic_index: ?Symbol.Index = null, -ehdr_start_index: ?Symbol.Index = null, -init_array_start_index: ?Symbol.Index = null, -init_array_end_index: ?Symbol.Index = null, -fini_array_start_index: ?Symbol.Index = null, -fini_array_end_index: ?Symbol.Index = null, -preinit_array_start_index: ?Symbol.Index = null, -preinit_array_end_index: ?Symbol.Index = null, -got_index: ?Symbol.Index = null, -plt_index: ?Symbol.Index = null, -end_index: ?Symbol.Index = null, -gnu_eh_frame_hdr_index: ?Symbol.Index = null, -dso_handle_index: ?Symbol.Index = null, -rela_iplt_start_index: ?Symbol.Index = null, -rela_iplt_end_index: ?Symbol.Index = null, -global_pointer_index: ?Symbol.Index = null, -start_stop_indexes: std.ArrayListUnmanaged(u32) = .{}, - /// An array of symbols parsed across all input files. symbols: std.ArrayListUnmanaged(Symbol) = .{}, symbols_extra: std.ArrayListUnmanaged(u32) = .{}, @@ -203,30 +183,17 @@ resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, has_text_reloc: bool = false, num_ifunc_dynrelocs: usize = 0, -/// List of atoms that are owned directly by the linker. -atoms: std.ArrayListUnmanaged(Atom) = .{}, -atoms_extra: std.ArrayListUnmanaged(u32) = .{}, - /// List of range extension thunks. thunks: std.ArrayListUnmanaged(Thunk) = .{}, /// List of output merge sections with deduped contents. merge_sections: std.ArrayListUnmanaged(MergeSection) = .{}, -/// List of output merge subsections. -/// Each subsection is akin to Atom but belongs to a MergeSection. -merge_subsections: std.ArrayListUnmanaged(MergeSubsection) = .{}, -/// List of input merge sections as parsed from input relocatables. -merge_input_sections: std.ArrayListUnmanaged(InputMergeSection) = .{}, /// Table of last atom index in a section and matching atom free list if any. last_atom_and_free_list_table: LastAtomAndFreeListTable = .{}, -comdat_groups: std.ArrayListUnmanaged(ComdatGroup) = .{}, -comdat_groups_owners: std.ArrayListUnmanaged(ComdatGroupOwner) = .{}, -comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{}, - /// Global string table used to provide quick access to global symbol resolvers -/// such as `resolver` and `comdat_groups_table`. +/// such as `resolver`. strings: StringTable = .{}, first_eflags: ?elf.Elf64_Word = null, @@ -378,20 +345,15 @@ pub fn createEmpty( try self.symbols.append(gpa, .{}); // Index 0 is always a null symbol. try self.symbols_extra.append(gpa, 0); - // Allocate atom index 0 to null atom - try self.atoms.append(gpa, .{}); - try self.atoms_extra.append(gpa, 0); // Append null file at index 0 try self.files.append(gpa, .null); // Append null byte to string tables try self.shstrtab.append(gpa, 0); try self.strtab.append(gpa, 0); // There must always be a null shdr in index 0 - _ = try self.addSection(.{ .name = "" }); + _ = try self.addSection(.{}); // Append null symbol in output symtab try self.symtab.append(gpa, null_sym); - // Append null input merge section. - try self.merge_input_sections.append(gpa, .{}); if (!is_obj_or_ar) { try self.dynstrtab.append(gpa, 0); @@ -502,10 +464,7 @@ pub fn deinit(self: *Elf) void { self.symbols_extra.deinit(gpa); self.symbols_free_list.deinit(gpa); self.resolver.deinit(gpa); - self.start_stop_indexes.deinit(gpa); - self.atoms.deinit(gpa); - self.atoms_extra.deinit(gpa); for (self.thunks.items) |*th| { th.deinit(gpa); } @@ -514,19 +473,11 @@ pub fn deinit(self: *Elf) void { sect.deinit(gpa); } self.merge_sections.deinit(gpa); - self.merge_subsections.deinit(gpa); - for (self.merge_input_sections.items) |*sect| { - sect.deinit(gpa); - } - self.merge_input_sections.deinit(gpa); for (self.last_atom_and_free_list_table.values()) |*value| { value.free_list.deinit(gpa); } self.last_atom_and_free_list_table.deinit(gpa); - self.comdat_groups.deinit(gpa); - self.comdat_groups_owners.deinit(gpa); - self.comdat_groups_table.deinit(gpa); self.strings.deinit(gpa); self.got.deinit(gpa); @@ -744,7 +695,7 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { if (self.zig_text_section_index == null) { self.zig_text_section_index = try self.addSection(.{ - .name = ".text.zig", + .name = try self.insertShString(".text.zig"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, .addralign = 1, @@ -753,7 +704,7 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { const shdr = &self.shdrs.items[self.zig_text_section_index.?]; fillSection(self, shdr, options.program_code_size_hint, self.phdr_zig_load_re_index); if (self.base.isRelocatable()) { - const rela_shndx = try self.addRelaShdr(".rela.text.zig", self.zig_text_section_index.?); + const rela_shndx = try self.addRelaShdr(try self.insertShString(".rela.text.zig"), self.zig_text_section_index.?); try self.output_rela_sections.putNoClobber(gpa, self.zig_text_section_index.?, .{ .shndx = rela_shndx, }); @@ -770,7 +721,7 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { if (self.zig_got_section_index == null and !self.base.isRelocatable()) { self.zig_got_section_index = try self.addSection(.{ - .name = ".got.zig", + .name = try self.insertShString(".got.zig"), .type = elf.SHT_PROGBITS, .addralign = ptr_size, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, @@ -791,7 +742,7 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { if (self.zig_data_rel_ro_section_index == null) { self.zig_data_rel_ro_section_index = try self.addSection(.{ - .name = ".data.rel.ro.zig", + .name = try self.insertShString(".data.rel.ro.zig"), .type = elf.SHT_PROGBITS, .addralign = 1, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, @@ -801,7 +752,7 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { fillSection(self, shdr, 1024, self.phdr_zig_load_ro_index); if (self.base.isRelocatable()) { const rela_shndx = try self.addRelaShdr( - ".rela.data.rel.ro.zig", + try self.insertShString(".rela.data.rel.ro.zig"), self.zig_data_rel_ro_section_index.?, ); try self.output_rela_sections.putNoClobber(gpa, self.zig_data_rel_ro_section_index.?, .{ @@ -820,7 +771,7 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { if (self.zig_data_section_index == null) { self.zig_data_section_index = try self.addSection(.{ - .name = ".data.zig", + .name = try self.insertShString(".data.zig"), .type = elf.SHT_PROGBITS, .addralign = ptr_size, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, @@ -830,7 +781,7 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { fillSection(self, shdr, 1024, self.phdr_zig_load_rw_index); if (self.base.isRelocatable()) { const rela_shndx = try self.addRelaShdr( - ".rela.data.zig", + try self.insertShString(".rela.data.zig"), self.zig_data_section_index.?, ); try self.output_rela_sections.putNoClobber(gpa, self.zig_data_section_index.?, .{ @@ -849,7 +800,7 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { if (self.zig_bss_section_index == null) { self.zig_bss_section_index = try self.addSection(.{ - .name = ".bss.zig", + .name = try self.insertShString(".bss.zig"), .type = elf.SHT_NOBITS, .addralign = ptr_size, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, @@ -873,7 +824,7 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { assert(dw.strtab.buffer.items.len == 0); try dw.strtab.buffer.append(gpa, 0); self.debug_str_section_index = try self.addSection(.{ - .name = ".debug_str", + .name = try self.insertShString(".debug_str"), .flags = elf.SHF_MERGE | elf.SHF_STRINGS, .entsize = 1, .type = elf.SHT_PROGBITS, @@ -891,7 +842,7 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { if (self.debug_info_section_index == null) { self.debug_info_section_index = try self.addSection(.{ - .name = ".debug_info", + .name = try self.insertShString(".debug_info"), .type = elf.SHT_PROGBITS, .addralign = 1, .offset = std.math.maxInt(u64), @@ -907,7 +858,7 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { if (self.debug_abbrev_section_index == null) { self.debug_abbrev_section_index = try self.addSection(.{ - .name = ".debug_abbrev", + .name = try self.insertShString(".debug_abbrev"), .type = elf.SHT_PROGBITS, .addralign = 1, .offset = std.math.maxInt(u64), @@ -923,7 +874,7 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { if (self.debug_aranges_section_index == null) { self.debug_aranges_section_index = try self.addSection(.{ - .name = ".debug_aranges", + .name = try self.insertShString(".debug_aranges"), .type = elf.SHT_PROGBITS, .addralign = 16, .offset = std.math.maxInt(u64), @@ -939,7 +890,7 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { if (self.debug_line_section_index == null) { self.debug_line_section_index = try self.addSection(.{ - .name = ".debug_line", + .name = try self.insertShString(".debug_line"), .type = elf.SHT_PROGBITS, .addralign = 1, .offset = std.math.maxInt(u64), @@ -1315,6 +1266,9 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); self.files.set(index, .{ .linker_defined = .{ .index = index } }); self.linker_defined_index = index; + const object = self.linkerDefinedPtr().?; + try object.init(gpa); + try object.initSymbols(self); } // Now, we are ready to resolve the symbols across all input files. @@ -1322,20 +1276,13 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod // input Object files. // Any qualifing unresolved symbol will be upgraded to an absolute, weak // symbol for potential resolution at load-time. - self.resolveSymbols(); + try self.resolveSymbols(); self.markEhFrameAtomsDead(); try self.resolveMergeSections(); try self.convertCommonSymbols(); self.markImportsExports(); - // Look for entry address in objects if not set by the incremental compiler. - if (self.entry_index == null) { - if (self.entry_name) |name| { - self.entry_index = self.globalByName(name); - } - } - if (self.base.gc_sections) { try gc.gcAtoms(self); @@ -1353,7 +1300,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod try self.finalizeMergeSections(); try self.initOutputSections(); try self.initMergeSections(); - try self.addLinkerDefinedSymbols(); self.claimUnresolved(); // Scan and create missing synthetic entries such as GOT indirection. @@ -1379,7 +1325,9 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod try self.sortPhdrs(); try self.allocateNonAllocSections(); self.allocateSpecialPhdrs(); - self.allocateLinkerDefinedSymbols(); + if (self.linkerDefinedPtr()) |obj| { + obj.allocateSymbols(self); + } // Dump the state for easy debugging. // State can be dumped via `--debug-log link_state`. @@ -1389,15 +1337,15 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod // Beyond this point, everything has been allocated a virtual address and we can resolve // the relocations, and commit objects to file. - if (self.zigObjectPtr()) |zig_object| { + if (self.zigObjectPtr()) |zo| { var has_reloc_errors = false; - for (zig_object.atoms.items) |atom_index| { - const atom_ptr = self.atom(atom_index) orelse continue; - if (!atom_ptr.flags.alive) continue; - const out_shndx = atom_ptr.outputShndx() orelse continue; + for (zo.atoms_indexes.items) |atom_index| { + const atom_ptr = zo.atom(atom_index) orelse continue; + if (!atom_ptr.alive) continue; + const out_shndx = atom_ptr.output_section_index; const shdr = &self.shdrs.items[out_shndx]; if (shdr.sh_type == elf.SHT_NOBITS) continue; - const code = try zig_object.codeAlloc(self, atom_index); + const code = try zo.codeAlloc(self, atom_index); defer gpa.free(code); const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value)); atom_ptr.resolveRelocsAlloc(self, code) catch |err| switch (err) { @@ -1427,7 +1375,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod else => |e| return e, }; - if (self.entry_index == null and self.base.isExe()) { + if (self.base.isExe() and self.linkerDefinedPtr().?.entry_index == null) { log.debug("flushing. no_entry_point_found = true", .{}); comp.link_error_flags.no_entry_point_found = true; } else { @@ -1972,12 +1920,13 @@ fn accessLibPath( /// 4. Reset state of all resolved globals since we will redo this bit on the pruned set. /// 5. Remove references to dead objects/shared objects /// 6. Re-run symbol resolution on pruned objects and shared objects sets. -pub fn resolveSymbols(self: *Elf) void { +pub fn resolveSymbols(self: *Elf) !void { // Resolve symbols in the ZigObject. For now, we assume that it's always live. if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resolveSymbols(self); // Resolve symbols on the set of all objects and shared objects (even if some are unneeded). for (self.objects.items) |index| self.file(index).?.resolveSymbols(self); for (self.shared_objects.items) |index| self.file(index).?.resolveSymbols(self); + if (self.linkerDefinedPtr()) |obj| obj.asFile().resolveSymbols(self); // Mark live objects. self.markLive(); @@ -1986,6 +1935,7 @@ pub fn resolveSymbols(self: *Elf) void { if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resetGlobals(self); for (self.objects.items) |index| self.file(index).?.resetGlobals(self); for (self.shared_objects.items) |index| self.file(index).?.resetGlobals(self); + if (self.linkerDefinedPtr()) |obj| obj.asFile().resetGlobals(self); // Prune dead objects and shared objects. var i: usize = 0; @@ -2003,41 +1953,25 @@ pub fn resolveSymbols(self: *Elf) void { } else i += 1; } - // Dedup comdat groups. - for (self.objects.items) |index| { - const object = self.file(index).?.object; - for (object.comdat_groups.items) |cg_index| { - const cg = self.comdatGroup(cg_index); - const cg_owner = self.comdatGroupOwner(cg.owner); - const owner_file_index = if (self.file(cg_owner.file)) |file_ptr| - file_ptr.object.index - else - std.math.maxInt(File.Index); - cg_owner.file = @min(owner_file_index, index); + { + // Dedup comdat groups. + var table = std.StringHashMap(Ref).init(self.base.comp.gpa); + defer table.deinit(); + + for (self.objects.items) |index| { + try self.file(index).?.object.resolveComdatGroups(self, &table); } - } - for (self.objects.items) |index| { - const object = self.file(index).?.object; - for (object.comdat_groups.items) |cg_index| { - const cg = self.comdatGroup(cg_index); - const cg_owner = self.comdatGroupOwner(cg.owner); - if (cg_owner.file != index) { - for (cg.comdatGroupMembers(self)) |shndx| { - const atom_index = object.atoms.items[shndx]; - if (self.atom(atom_index)) |atom_ptr| { - atom_ptr.flags.alive = false; - atom_ptr.markFdesDead(self); - } - } - } + for (self.objects.items) |index| { + self.file(index).?.object.markComdatGroupsDead(self); } } // Re-resolve the symbols. - if (self.zigObjectPtr()) |zig_object| zig_object.resolveSymbols(self); + if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resolveSymbols(self); for (self.objects.items) |index| self.file(index).?.resolveSymbols(self); for (self.shared_objects.items) |index| self.file(index).?.resolveSymbols(self); + if (self.linkerDefinedPtr()) |obj| obj.asFile().resolveSymbols(self); } /// Traverses all objects and shared objects marking any object referenced by @@ -2129,7 +2063,7 @@ fn claimUnresolved(self: *Elf) void { fn scanRelocs(self: *Elf) !void { const gpa = self.base.comp.gpa; - var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Atom.Index)).init(gpa); + var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Ref)).init(gpa); defer { var it = undefs.iterator(); while (it.next()) |entry| { @@ -2976,10 +2910,10 @@ pub fn writeElfHeader(self: *Elf) !void { mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian); index += 4; - const e_entry = if (self.entry_index) |entry_index| - @as(u64, @intCast(self.symbol(entry_index).address(.{}, self))) - else - 0; + const e_entry: u64 = if (self.linkerDefinedPtr()) |obj| blk: { + const entry_index = obj.entry_index orelse break :blk 0; + break :blk @intCast(self.symbol(entry_index).address(.{}, self)); + } else 0; const phdr_table_offset = if (self.phdr_table_index) |phndx| self.phdrs.items[phndx].p_offset else 0; switch (self.ptr_width) { .p32 => { @@ -3106,205 +3040,6 @@ pub fn deleteExport( return self.zigObjectPtr().?.deleteExport(self, exported, name); } -fn addLinkerDefinedSymbols(self: *Elf) !void { - const comp = self.base.comp; - const gpa = comp.gpa; - - const linker_defined_index = self.linker_defined_index orelse return; - const linker_defined = self.file(linker_defined_index).?.linker_defined; - self.dynamic_index = try linker_defined.addGlobal("_DYNAMIC", self); - self.ehdr_start_index = try linker_defined.addGlobal("__ehdr_start", self); - self.init_array_start_index = try linker_defined.addGlobal("__init_array_start", self); - self.init_array_end_index = try linker_defined.addGlobal("__init_array_end", self); - self.fini_array_start_index = try linker_defined.addGlobal("__fini_array_start", self); - self.fini_array_end_index = try linker_defined.addGlobal("__fini_array_end", self); - self.preinit_array_start_index = try linker_defined.addGlobal("__preinit_array_start", self); - self.preinit_array_end_index = try linker_defined.addGlobal("__preinit_array_end", self); - self.got_index = try linker_defined.addGlobal("_GLOBAL_OFFSET_TABLE_", self); - self.plt_index = try linker_defined.addGlobal("_PROCEDURE_LINKAGE_TABLE_", self); - self.end_index = try linker_defined.addGlobal("_end", self); - - if (comp.link_eh_frame_hdr) { - self.gnu_eh_frame_hdr_index = try linker_defined.addGlobal("__GNU_EH_FRAME_HDR", self); - } - - if (self.globalByName("__dso_handle")) |index| { - if (self.symbol(index).file(self) == null) - self.dso_handle_index = try linker_defined.addGlobal("__dso_handle", self); - } - - self.rela_iplt_start_index = try linker_defined.addGlobal("__rela_iplt_start", self); - self.rela_iplt_end_index = try linker_defined.addGlobal("__rela_iplt_end", self); - - for (self.shdrs.items) |shdr| { - if (self.getStartStopBasename(shdr)) |name| { - try self.start_stop_indexes.ensureUnusedCapacity(gpa, 2); - - const start = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name}); - defer gpa.free(start); - const stop = try std.fmt.allocPrintZ(gpa, "__stop_{s}", .{name}); - defer gpa.free(stop); - - self.start_stop_indexes.appendAssumeCapacity(try linker_defined.addGlobal(start, self)); - self.start_stop_indexes.appendAssumeCapacity(try linker_defined.addGlobal(stop, self)); - } - } - - if (self.getTarget().cpu.arch.isRISCV() and self.isEffectivelyDynLib()) { - self.global_pointer_index = try linker_defined.addGlobal("__global_pointer$", self); - } - - linker_defined.resolveSymbols(self); -} - -fn allocateLinkerDefinedSymbols(self: *Elf) void { - const comp = self.base.comp; - const link_mode = comp.config.link_mode; - - // _DYNAMIC - if (self.dynamic_section_index) |shndx| { - const shdr = &self.shdrs.items[shndx]; - const symbol_ptr = self.symbol(self.dynamic_index.?); - symbol_ptr.value = @intCast(shdr.sh_addr); - symbol_ptr.output_section_index = shndx; - } - - // __ehdr_start - { - const symbol_ptr = self.symbol(self.ehdr_start_index.?); - symbol_ptr.value = @intCast(self.image_base); - symbol_ptr.output_section_index = 1; - } - - // __init_array_start, __init_array_end - if (self.sectionByName(".init_array")) |shndx| { - const start_sym = self.symbol(self.init_array_start_index.?); - const end_sym = self.symbol(self.init_array_end_index.?); - const shdr = &self.shdrs.items[shndx]; - start_sym.output_section_index = shndx; - start_sym.value = @intCast(shdr.sh_addr); - end_sym.output_section_index = shndx; - end_sym.value = @intCast(shdr.sh_addr + shdr.sh_size); - } - - // __fini_array_start, __fini_array_end - if (self.sectionByName(".fini_array")) |shndx| { - const start_sym = self.symbol(self.fini_array_start_index.?); - const end_sym = self.symbol(self.fini_array_end_index.?); - const shdr = &self.shdrs.items[shndx]; - start_sym.output_section_index = shndx; - start_sym.value = @intCast(shdr.sh_addr); - end_sym.output_section_index = shndx; - end_sym.value = @intCast(shdr.sh_addr + shdr.sh_size); - } - - // __preinit_array_start, __preinit_array_end - if (self.sectionByName(".preinit_array")) |shndx| { - const start_sym = self.symbol(self.preinit_array_start_index.?); - const end_sym = self.symbol(self.preinit_array_end_index.?); - const shdr = &self.shdrs.items[shndx]; - start_sym.output_section_index = shndx; - start_sym.value = @intCast(shdr.sh_addr); - end_sym.output_section_index = shndx; - end_sym.value = @intCast(shdr.sh_addr + shdr.sh_size); - } - - // _GLOBAL_OFFSET_TABLE_ - if (self.getTarget().cpu.arch == .x86_64) { - if (self.got_plt_section_index) |shndx| { - const shdr = self.shdrs.items[shndx]; - const sym = self.symbol(self.got_index.?); - sym.value = @intCast(shdr.sh_addr); - sym.output_section_index = shndx; - } - } else { - if (self.got_section_index) |shndx| { - const shdr = self.shdrs.items[shndx]; - const sym = self.symbol(self.got_index.?); - sym.value = @intCast(shdr.sh_addr); - sym.output_section_index = shndx; - } - } - - // _PROCEDURE_LINKAGE_TABLE_ - if (self.plt_section_index) |shndx| { - const shdr = &self.shdrs.items[shndx]; - const symbol_ptr = self.symbol(self.plt_index.?); - symbol_ptr.value = @intCast(shdr.sh_addr); - symbol_ptr.output_section_index = shndx; - } - - // __dso_handle - if (self.dso_handle_index) |index| { - const shdr = &self.shdrs.items[1]; - const symbol_ptr = self.symbol(index); - symbol_ptr.value = @intCast(shdr.sh_addr); - symbol_ptr.output_section_index = 0; - } - - // __GNU_EH_FRAME_HDR - if (self.eh_frame_hdr_section_index) |shndx| { - const shdr = &self.shdrs.items[shndx]; - const symbol_ptr = self.symbol(self.gnu_eh_frame_hdr_index.?); - symbol_ptr.value = @intCast(shdr.sh_addr); - symbol_ptr.output_section_index = shndx; - } - - // __rela_iplt_start, __rela_iplt_end - if (self.rela_dyn_section_index) |shndx| blk: { - if (link_mode != .static or comp.config.pie) break :blk; - const shdr = &self.shdrs.items[shndx]; - const end_addr = shdr.sh_addr + shdr.sh_size; - const start_addr = end_addr - self.calcNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela); - const start_sym = self.symbol(self.rela_iplt_start_index.?); - const end_sym = self.symbol(self.rela_iplt_end_index.?); - start_sym.value = @intCast(start_addr); - start_sym.output_section_index = shndx; - end_sym.value = @intCast(end_addr); - end_sym.output_section_index = shndx; - } - - // _end - { - const end_symbol = self.symbol(self.end_index.?); - for (self.shdrs.items, 0..) |shdr, shndx| { - if (shdr.sh_flags & elf.SHF_ALLOC != 0) { - end_symbol.value = @intCast(shdr.sh_addr + shdr.sh_size); - end_symbol.output_section_index = @intCast(shndx); - } - } - } - - // __start_*, __stop_* - { - var index: usize = 0; - while (index < self.start_stop_indexes.items.len) : (index += 2) { - const start = self.symbol(self.start_stop_indexes.items[index]); - const name = start.name(self); - const stop = self.symbol(self.start_stop_indexes.items[index + 1]); - const shndx = self.sectionByName(name["__start_".len..]).?; - const shdr = &self.shdrs.items[shndx]; - start.value = @intCast(shdr.sh_addr); - start.output_section_index = shndx; - stop.value = @intCast(shdr.sh_addr + shdr.sh_size); - stop.output_section_index = shndx; - } - } - - // __global_pointer$ - if (self.global_pointer_index) |index| { - const sym = self.symbol(index); - if (self.sectionByName(".sdata")) |shndx| { - const shdr = self.shdrs.items[shndx]; - sym.value = @intCast(shdr.sh_addr + 0x800); - sym.output_section_index = shndx; - } else { - sym.value = 0; - sym.output_section_index = 0; - } - } -} - fn checkDuplicates(self: *Elf) !void { const gpa = self.base.comp.gpa; @@ -3327,12 +3062,13 @@ fn checkDuplicates(self: *Elf) !void { } pub fn addCommentString(self: *Elf) !void { + const gpa = self.base.comp.gpa; const msec_index = try self.getOrCreateMergeSection(".comment", elf.SHF_MERGE | elf.SHF_STRINGS, elf.SHT_PROGBITS); const msec = self.mergeSection(msec_index); - const res = try msec.insertZ(self.base.comp.gpa, "zig " ++ builtin.zig_version_string); + const res = try msec.insertZ(gpa, "zig " ++ builtin.zig_version_string); if (res.found_existing) return; - const msub_index = try self.addMergeSubsection(); - const msub = self.mergeSubsection(msub_index); + const msub_index = try msec.addMergeSubsection(gpa); + const msub = msec.mergeSubsection(msub_index); msub.merge_section_index = msec_index; msub.string_index = res.key.pos; msub.alignment = .@"1"; @@ -3350,7 +3086,7 @@ pub fn resolveMergeSections(self: *Elf) !void { for (self.objects.items) |index| { const file_ptr = self.file(index).?; if (!file_ptr.isAlive()) continue; - file_ptr.object.initMergeSections(self) catch |err| switch (err) { + file_ptr.object.initInputMergeSections(self) catch |err| switch (err) { error.MalformedObject => has_errors = true, else => |e| return e, }; @@ -3358,6 +3094,12 @@ pub fn resolveMergeSections(self: *Elf) !void { if (has_errors) return error.FlushFailure; + for (self.objects.items) |index| { + const file_ptr = self.file(index).?; + if (!file_ptr.isAlive()) continue; + try file_ptr.object.initOutputMergeSections(self); + } + for (self.objects.items) |index| { const file_ptr = self.file(index).?; if (!file_ptr.isAlive()) continue; @@ -3372,15 +3114,15 @@ pub fn resolveMergeSections(self: *Elf) !void { pub fn finalizeMergeSections(self: *Elf) !void { for (self.merge_sections.items) |*msec| { - try msec.finalize(self); + try msec.finalize(self.base.comp.gpa); } } pub fn updateMergeSectionSizes(self: *Elf) !void { for (self.merge_sections.items) |*msec| { const shdr = &self.shdrs.items[msec.output_section_index]; - for (msec.subsections.items) |msub_index| { - const msub = self.mergeSubsection(msub_index); + for (msec.finalized_subsections.items) |msub_index| { + const msub = msec.mergeSubsection(msub_index); assert(msub.alive); const offset = msub.alignment.forward(shdr.sh_size); const padding = offset - shdr.sh_size; @@ -3396,14 +3138,14 @@ pub fn writeMergeSections(self: *Elf) !void { var buffer = std.ArrayList(u8).init(gpa); defer buffer.deinit(); - for (self.merge_sections.items) |msec| { + for (self.merge_sections.items) |*msec| { const shdr = self.shdrs.items[msec.output_section_index]; const size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; try buffer.ensureTotalCapacity(size); buffer.appendNTimesAssumeCapacity(0, size); - for (msec.subsections.items) |msub_index| { - const msub = self.mergeSubsection(msub_index); + for (msec.finalized_subsections.items) |msub_index| { + const msub = msec.mergeSubsection(msub_index); assert(msub.alive); const string = msub.getString(self); const off = math.cast(usize, msub.value) orelse return error.Overflow; @@ -3423,18 +3165,18 @@ fn initOutputSections(self: *Elf) !void { pub fn initMergeSections(self: *Elf) !void { for (self.merge_sections.items) |*msec| { - if (msec.subsections.items.len == 0) continue; + if (msec.finalized_subsections.items.len == 0) continue; const name = msec.name(self); const shndx = self.sectionByName(name) orelse try self.addSection(.{ - .name = name, + .name = msec.name_offset, .type = msec.type, .flags = msec.flags, }); msec.output_section_index = shndx; - var entsize = self.mergeSubsection(msec.subsections.items[0]).entsize; - for (msec.subsections.items) |index| { - const msub = self.mergeSubsection(index); + var entsize = msec.mergeSubsection(msec.finalized_subsections.items[0]).entsize; + for (msec.finalized_subsections.items) |msub_index| { + const msub = msec.mergeSubsection(msub_index); entsize = @min(entsize, msub.entsize); } const shdr = &self.shdrs.items[shndx]; @@ -3452,7 +3194,7 @@ fn initSyntheticSections(self: *Elf) !void { } else false; if (needs_eh_frame) { self.eh_frame_section_index = try self.addSection(.{ - .name = ".eh_frame", + .name = try self.insertShString(".eh_frame"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC, .addralign = ptr_size, @@ -3461,7 +3203,7 @@ fn initSyntheticSections(self: *Elf) !void { if (comp.link_eh_frame_hdr) { self.eh_frame_hdr_section_index = try self.addSection(.{ - .name = ".eh_frame_hdr", + .name = try self.insertShString(".eh_frame_hdr"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC, .addralign = 4, @@ -3472,7 +3214,7 @@ fn initSyntheticSections(self: *Elf) !void { if (self.got.entries.items.len > 0) { self.got_section_index = try self.addSection(.{ - .name = ".got", + .name = try self.insertShString(".got"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, .addralign = ptr_size, @@ -3481,7 +3223,7 @@ fn initSyntheticSections(self: *Elf) !void { } self.got_plt_section_index = try self.addSection(.{ - .name = ".got.plt", + .name = try self.insertShString(".got.plt"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, .addralign = @alignOf(u64), @@ -3501,7 +3243,7 @@ fn initSyntheticSections(self: *Elf) !void { }; if (needs_rela_dyn) { self.rela_dyn_section_index = try self.addSection(.{ - .name = ".rela.dyn", + .name = try self.insertShString(".rela.dyn"), .type = elf.SHT_RELA, .flags = elf.SHF_ALLOC, .addralign = @alignOf(elf.Elf64_Rela), @@ -3512,14 +3254,14 @@ fn initSyntheticSections(self: *Elf) !void { if (self.plt.symbols.items.len > 0) { self.plt_section_index = try self.addSection(.{ - .name = ".plt", + .name = try self.insertShString(".plt"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, .addralign = 16, .offset = std.math.maxInt(u64), }); self.rela_plt_section_index = try self.addSection(.{ - .name = ".rela.plt", + .name = try self.insertShString(".rela.plt"), .type = elf.SHT_RELA, .flags = elf.SHF_ALLOC, .addralign = @alignOf(elf.Elf64_Rela), @@ -3530,7 +3272,7 @@ fn initSyntheticSections(self: *Elf) !void { if (self.plt_got.symbols.items.len > 0) { self.plt_got_section_index = try self.addSection(.{ - .name = ".plt.got", + .name = try self.insertShString(".plt.got"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, .addralign = 16, @@ -3540,7 +3282,7 @@ fn initSyntheticSections(self: *Elf) !void { if (self.copy_rel.symbols.items.len > 0) { self.copy_rel_section_index = try self.addSection(.{ - .name = ".copyrel", + .name = try self.insertShString(".copyrel"), .type = elf.SHT_NOBITS, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, .offset = std.math.maxInt(u64), @@ -3558,7 +3300,7 @@ fn initSyntheticSections(self: *Elf) !void { }; if (needs_interp) { self.interp_section_index = try self.addSection(.{ - .name = ".interp", + .name = try self.insertShString(".interp"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC, .addralign = 1, @@ -3568,7 +3310,7 @@ fn initSyntheticSections(self: *Elf) !void { if (self.isEffectivelyDynLib() or self.shared_objects.items.len > 0 or comp.config.pie) { self.dynstrtab_section_index = try self.addSection(.{ - .name = ".dynstr", + .name = try self.insertShString(".dynstr"), .flags = elf.SHF_ALLOC, .type = elf.SHT_STRTAB, .entsize = 1, @@ -3576,7 +3318,7 @@ fn initSyntheticSections(self: *Elf) !void { .offset = std.math.maxInt(u64), }); self.dynamic_section_index = try self.addSection(.{ - .name = ".dynamic", + .name = try self.insertShString(".dynamic"), .flags = elf.SHF_ALLOC | elf.SHF_WRITE, .type = elf.SHT_DYNAMIC, .entsize = @sizeOf(elf.Elf64_Dyn), @@ -3584,7 +3326,7 @@ fn initSyntheticSections(self: *Elf) !void { .offset = std.math.maxInt(u64), }); self.dynsymtab_section_index = try self.addSection(.{ - .name = ".dynsym", + .name = try self.insertShString(".dynsym"), .flags = elf.SHF_ALLOC, .type = elf.SHT_DYNSYM, .addralign = @alignOf(elf.Elf64_Sym), @@ -3593,7 +3335,7 @@ fn initSyntheticSections(self: *Elf) !void { .offset = std.math.maxInt(u64), }); self.hash_section_index = try self.addSection(.{ - .name = ".hash", + .name = try self.insertShString(".hash"), .flags = elf.SHF_ALLOC, .type = elf.SHT_HASH, .addralign = 4, @@ -3601,7 +3343,7 @@ fn initSyntheticSections(self: *Elf) !void { .offset = std.math.maxInt(u64), }); self.gnu_hash_section_index = try self.addSection(.{ - .name = ".gnu.hash", + .name = try self.insertShString(".gnu.hash"), .flags = elf.SHF_ALLOC, .type = elf.SHT_GNU_HASH, .addralign = 8, @@ -3614,7 +3356,7 @@ fn initSyntheticSections(self: *Elf) !void { } else false; if (needs_versions) { self.versym_section_index = try self.addSection(.{ - .name = ".gnu.version", + .name = try self.insertShString(".gnu.version"), .flags = elf.SHF_ALLOC, .type = elf.SHT_GNU_VERSYM, .addralign = @alignOf(elf.Elf64_Versym), @@ -3622,7 +3364,7 @@ fn initSyntheticSections(self: *Elf) !void { .offset = std.math.maxInt(u64), }); self.verneed_section_index = try self.addSection(.{ - .name = ".gnu.version_r", + .name = try self.insertShString(".gnu.version_r"), .flags = elf.SHF_ALLOC, .type = elf.SHT_GNU_VERNEED, .addralign = @alignOf(elf.Elf64_Verneed), @@ -3642,7 +3384,7 @@ pub fn initSymtab(self: *Elf) !void { }; if (self.symtab_section_index == null) { self.symtab_section_index = try self.addSection(.{ - .name = ".symtab", + .name = try self.insertShString(".symtab"), .type = elf.SHT_SYMTAB, .addralign = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym), .entsize = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym), @@ -3651,7 +3393,7 @@ pub fn initSymtab(self: *Elf) !void { } if (self.strtab_section_index == null) { self.strtab_section_index = try self.addSection(.{ - .name = ".strtab", + .name = try self.insertShString(".strtab"), .type = elf.SHT_STRTAB, .entsize = 1, .addralign = 1, @@ -3663,7 +3405,7 @@ pub fn initSymtab(self: *Elf) !void { pub fn initShStrtab(self: *Elf) !void { if (self.shstrtab_section_index == null) { self.shstrtab_section_index = try self.addSection(.{ - .name = ".shstrtab", + .name = try self.insertShString(".shstrtab"), .type = elf.SHT_STRTAB, .entsize = 1, .addralign = 1, @@ -3733,11 +3475,11 @@ fn sortInitFini(self: *Elf) !void { const Entry = struct { priority: i32, - atom_index: Atom.Index, + atom_ref: Ref, pub fn lessThan(ctx: *Elf, lhs: @This(), rhs: @This()) bool { if (lhs.priority == rhs.priority) { - return ctx.atom(lhs.atom_index).?.priority(ctx) < ctx.atom(rhs.atom_index).?.priority(ctx); + return ctx.atom(lhs.atom_ref).?.priority(ctx) < ctx.atom(rhs.atom_ref).?.priority(ctx); } return lhs.priority < rhs.priority; } @@ -3768,8 +3510,8 @@ fn sortInitFini(self: *Elf) !void { try entries.ensureTotalCapacityPrecise(atom_list.items.len); defer entries.deinit(); - for (atom_list.items) |atom_index| { - const atom_ptr = self.atom(atom_index).?; + for (atom_list.items) |ref| { + const atom_ptr = self.atom(ref).?; const object = atom_ptr.file(self).?.object; const priority = blk: { if (is_ctor_dtor) { @@ -3782,14 +3524,14 @@ fn sortInitFini(self: *Elf) !void { const priority = std.fmt.parseUnsigned(u16, it.first(), 10) catch default; break :blk priority; }; - entries.appendAssumeCapacity(.{ .priority = priority, .atom_index = atom_index }); + entries.appendAssumeCapacity(.{ .priority = priority, .atom_ref = ref }); } mem.sort(Entry, entries.items, self, Entry.lessThan); atom_list.clearRetainingCapacity(); for (entries.items) |entry| { - atom_list.appendAssumeCapacity(entry.atom_index); + atom_list.appendAssumeCapacity(entry.atom_ref); } } } @@ -4155,26 +3897,11 @@ fn resetShdrIndexes(self: *Elf, backlinks: []const u32) !void { } } - if (self.zigObjectPtr()) |zig_object| { - for (zig_object.atoms.items) |atom_index| { - const atom_ptr = self.atom(atom_index) orelse continue; + if (self.zigObjectPtr()) |zo| { + for (zo.atoms_indexes.items) |atom_index| { + const atom_ptr = zo.atom(atom_index) orelse continue; atom_ptr.output_section_index = backlinks[atom_ptr.output_section_index]; } - - for (zig_object.locals()) |local_index| { - const local = self.symbol(local_index); - local.output_section_index = backlinks[local.output_section_index]; - } - - for (zig_object.globals()) |global_index| { - const global = self.symbol(global_index); - const atom_ptr = global.atom(self) orelse continue; - if (!atom_ptr.flags.alive) continue; - // TODO claim unresolved for objects - if (global.file(self).?.index() != zig_object.index) continue; - const out_shndx = global.outputShndx() orelse continue; - global.output_section_index = backlinks[out_shndx]; - } } for (self.output_rela_sections.keys(), self.output_rela_sections.values()) |shndx, sec| { @@ -4194,9 +3921,9 @@ fn updateSectionSizes(self: *Elf) !void { const shdr = &self.shdrs.items[shndx]; if (atom_list.items.len == 0) continue; if (self.requiresThunks() and shdr.sh_flags & elf.SHF_EXECINSTR != 0) continue; - for (atom_list.items) |atom_index| { - const atom_ptr = self.atom(atom_index) orelse continue; - if (!atom_ptr.flags.alive) continue; + for (atom_list.items) |ref| { + const atom_ptr = self.atom(ref) orelse continue; + if (!atom_ptr.alive) continue; const offset = atom_ptr.alignment.forward(shdr.sh_size); const padding = offset - shdr.sh_size; atom_ptr.value = @intCast(offset); @@ -4630,7 +4357,7 @@ fn allocateSpecialPhdrs(self: *Elf) void { fn writeAtoms(self: *Elf) !void { const gpa = self.base.comp.gpa; - var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Atom.Index)).init(gpa); + var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Ref)).init(gpa); defer { var it = undefs.iterator(); while (it.next()) |entry| { @@ -4678,21 +4405,21 @@ fn writeAtoms(self: *Elf) !void { 0; @memset(buffer, padding_byte); - for (atom_list.items) |atom_index| { - const atom_ptr = self.atom(atom_index).?; - assert(atom_ptr.flags.alive); + for (atom_list.items) |ref| { + const atom_ptr = self.atom(ref).?; + assert(atom_ptr.alive); const offset = math.cast(usize, atom_ptr.value - @as(i64, @intCast(base_offset))) orelse return error.Overflow; const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow; - log.debug("writing atom({d}) at 0x{x}", .{ atom_index, sh_offset + offset }); + log.debug("writing atom({}) at 0x{x}", .{ ref, sh_offset + offset }); // TODO decompress directly into provided buffer const out_code = buffer[offset..][0..size]; const in_code = switch (atom_ptr.file(self).?) { - .object => |x| try x.codeDecompressAlloc(self, atom_index), - .zig_object => |x| try x.codeAlloc(self, atom_index), + .object => |x| try x.codeDecompressAlloc(self, ref.index), + .zig_object => |x| try x.codeAlloc(self, ref.index), else => unreachable, }; defer gpa.free(in_code); @@ -5015,9 +4742,8 @@ pub fn writeSymtab(self: *Elf) !void { file_ptr.writeSymtab(self); } - if (self.linker_defined_index) |index| { - const file_ptr = self.file(index).?; - file_ptr.writeSymtab(self); + if (self.linkerDefinedPtr()) |obj| { + obj.asFile().writeSymtab(self); } if (self.zig_got_section_index) |_| { @@ -5479,7 +5205,7 @@ fn addPhdr(self: *Elf, opts: struct { return index; } -pub fn addRelaShdr(self: *Elf, name: [:0]const u8, shndx: u32) !u32 { +pub fn addRelaShdr(self: *Elf, name: u32, shndx: u32) !u32 { const entsize: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Rela), .p64 => @sizeOf(elf.Elf64_Rela), @@ -5500,7 +5226,7 @@ pub fn addRelaShdr(self: *Elf, name: [:0]const u8, shndx: u32) !u32 { } pub const AddSectionOpts = struct { - name: [:0]const u8, + name: u32 = 0, type: u32 = elf.SHT_NULL, flags: u64 = 0, link: u32 = 0, @@ -5515,7 +5241,7 @@ pub fn addSection(self: *Elf, opts: AddSectionOpts) !u32 { const index = @as(u32, @intCast(self.shdrs.items.len)); const shdr = try self.shdrs.addOne(gpa); shdr.* = .{ - .sh_name = try self.insertShString(opts.name), + .sh_name = opts.name, .sh_type = opts.type, .sh_flags = opts.flags, .sh_addr = 0, @@ -5580,7 +5306,7 @@ fn sortRelaDyn(self: *Elf) void { mem.sort(elf.Elf64_Rela, self.rela_dyn.items, self, Sort.lessThan); } -fn calcNumIRelativeRelocs(self: *Elf) usize { +pub fn calcNumIRelativeRelocs(self: *Elf) usize { var count: usize = self.num_ifunc_dynrelocs; for (self.got.entries.items) |entry| { @@ -5602,7 +5328,7 @@ pub fn isCIdentifier(name: []const u8) bool { return true; } -fn getStartStopBasename(self: *Elf, shdr: elf.Elf64_Shdr) ?[]const u8 { +pub fn getStartStopBasename(self: *Elf, shdr: elf.Elf64_Shdr) ?[]const u8 { const name = self.getShString(shdr.sh_name); if (shdr.sh_flags & elf.SHF_ALLOC != 0 and name.len > 0) { if (isCIdentifier(name)) return name; @@ -5610,64 +5336,6 @@ fn getStartStopBasename(self: *Elf, shdr: elf.Elf64_Shdr) ?[]const u8 { return null; } -pub fn atom(self: *Elf, atom_index: Atom.Index) ?*Atom { - if (atom_index == 0) return null; - assert(atom_index < self.atoms.items.len); - return &self.atoms.items[atom_index]; -} - -pub fn addAtom(self: *Elf) !Atom.Index { - const gpa = self.base.comp.gpa; - const index = @as(Atom.Index, @intCast(self.atoms.items.len)); - const atom_ptr = try self.atoms.addOne(gpa); - atom_ptr.* = .{ .atom_index = index }; - return index; -} - -pub fn addAtomExtra(self: *Elf, extra: Atom.Extra) !u32 { - const fields = @typeInfo(Atom.Extra).Struct.fields; - try self.atoms_extra.ensureUnusedCapacity(self.base.comp.gpa, fields.len); - return self.addAtomExtraAssumeCapacity(extra); -} - -pub fn addAtomExtraAssumeCapacity(self: *Elf, extra: Atom.Extra) u32 { - const index = @as(u32, @intCast(self.atoms_extra.items.len)); - const fields = @typeInfo(Atom.Extra).Struct.fields; - inline for (fields) |field| { - self.atoms_extra.appendAssumeCapacity(switch (field.type) { - u32 => @field(extra, field.name), - else => @compileError("bad field type"), - }); - } - return index; -} - -pub fn atomExtra(self: *Elf, index: u32) ?Atom.Extra { - if (index == 0) return null; - const fields = @typeInfo(Atom.Extra).Struct.fields; - var i: usize = index; - var result: Atom.Extra = undefined; - inline for (fields) |field| { - @field(result, field.name) = switch (field.type) { - u32 => self.atoms_extra.items[i], - else => @compileError("bad field type"), - }; - i += 1; - } - return result; -} - -pub fn setAtomExtra(self: *Elf, index: u32, extra: Atom.Extra) void { - assert(index > 0); - const fields = @typeInfo(Atom.Extra).Struct.fields; - inline for (fields, 0..) |field, i| { - self.atoms_extra.items[index + i] = switch (field.type) { - u32 => @field(extra, field.name), - else => @compileError("bad field type"), - }; - } -} - pub fn addThunk(self: *Elf) !Thunk.Index { const index = @as(Thunk.Index, @intCast(self.thunks.items.len)); const th = try self.thunks.addOne(self.base.comp.gpa); @@ -5704,6 +5372,15 @@ pub fn fileHandle(self: Elf, index: File.HandleIndex) File.Handle { return self.file_handles.items[index]; } +pub fn atom(self: *Elf, ref: Ref) ?*Atom { + const file_ptr = self.file(ref.file) orelse return null; + return file_ptr.atom(ref.index); +} + +pub fn comdatGroup(self: *Elf, ref: Ref) *ComdatGroup { + return self.file(ref.file).?.comdatGroup(ref.index); +} + /// Returns pointer-to-symbol described at sym_index. pub fn symbol(self: *Elf, sym_index: Symbol.Index) *Symbol { return &self.symbols.items[sym_index]; @@ -5795,11 +5472,6 @@ pub fn getOrPutGlobal(self: *Elf, name: []const u8) !GetOrPutGlobalResult { }; } -pub fn globalByName(self: *Elf, name: []const u8) ?Symbol.Index { - const name_off = self.strings.getOffset(name) orelse return null; - return self.resolver.get(name_off); -} - pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 { return self.zigObjectPtr().?.getGlobalSymbol(self, name, lib_name); } @@ -5809,69 +5481,12 @@ pub fn zigObjectPtr(self: *Elf) ?*ZigObject { return self.file(index).?.zig_object; } -const GetOrCreateComdatGroupOwnerResult = struct { - found_existing: bool, - index: ComdatGroupOwner.Index, -}; - -pub fn getOrCreateComdatGroupOwner(self: *Elf, name: [:0]const u8) !GetOrCreateComdatGroupOwnerResult { - const gpa = self.base.comp.gpa; - const off = try self.strings.insert(gpa, name); - const gop = try self.comdat_groups_table.getOrPut(gpa, off); - if (!gop.found_existing) { - const index: ComdatGroupOwner.Index = @intCast(self.comdat_groups_owners.items.len); - const owner = try self.comdat_groups_owners.addOne(gpa); - owner.* = .{}; - gop.value_ptr.* = index; - } - return .{ - .found_existing = gop.found_existing, - .index = gop.value_ptr.*, - }; -} - -pub fn addComdatGroup(self: *Elf) !ComdatGroup.Index { - const gpa = self.base.comp.gpa; - const index = @as(ComdatGroup.Index, @intCast(self.comdat_groups.items.len)); - _ = try self.comdat_groups.addOne(gpa); - return index; -} - -pub fn comdatGroup(self: *Elf, index: ComdatGroup.Index) *ComdatGroup { - assert(index < self.comdat_groups.items.len); - return &self.comdat_groups.items[index]; -} - -pub fn comdatGroupOwner(self: *Elf, index: ComdatGroupOwner.Index) *ComdatGroupOwner { - assert(index < self.comdat_groups_owners.items.len); - return &self.comdat_groups_owners.items[index]; -} - -pub fn addInputMergeSection(self: *Elf) !InputMergeSection.Index { - const index: InputMergeSection.Index = @intCast(self.merge_input_sections.items.len); - const msec = try self.merge_input_sections.addOne(self.base.comp.gpa); - msec.* = .{}; - return index; -} - -pub fn inputMergeSection(self: *Elf, index: InputMergeSection.Index) ?*InputMergeSection { - if (index == 0) return null; - return &self.merge_input_sections.items[index]; -} - -pub fn addMergeSubsection(self: *Elf) !MergeSubsection.Index { - const index: MergeSubsection.Index = @intCast(self.merge_subsections.items.len); - const msec = try self.merge_subsections.addOne(self.base.comp.gpa); - msec.* = .{}; - return index; -} - -pub fn mergeSubsection(self: *Elf, index: MergeSubsection.Index) *MergeSubsection { - assert(index < self.merge_subsections.items.len); - return &self.merge_subsections.items[index]; +pub fn linkerDefinedPtr(self: *Elf) ?*LinkerDefined { + const index = self.linker_defined_index orelse return null; + return self.file(index).?.linker_defined; } -pub fn getOrCreateMergeSection(self: *Elf, name: []const u8, flags: u64, @"type": u32) !MergeSection.Index { +pub fn getOrCreateMergeSection(self: *Elf, name: [:0]const u8, flags: u64, @"type": u32) !MergeSection.Index { const gpa = self.base.comp.gpa; const out_name = name: { if (self.base.isRelocatable()) break :name name; @@ -5879,11 +5494,11 @@ pub fn getOrCreateMergeSection(self: *Elf, name: []const u8, flags: u64, @"type" break :name if (flags & elf.SHF_STRINGS != 0) ".rodata.str" else ".rodata.cst"; break :name name; }; - const out_off = try self.strings.insert(gpa, out_name); + for (self.merge_sections.items, 0..) |msec, index| { + if (mem.eql(u8, msec.name(self), out_name)) return @intCast(index); + } + const out_off = try self.insertShString(out_name); const out_flags = flags & ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP); - for (self.merge_sections.items, 0..) |msec, index| { - if (msec.name_offset == out_off) return @intCast(index); - } const index = @as(MergeSection.Index, @intCast(self.merge_sections.items.len)); const msec = try self.merge_sections.addOne(gpa); msec.* = .{ @@ -5974,9 +5589,9 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void { var err = try self.base.addErrorWithNotesAssumeCapacity(nnotes); try err.addMsg("undefined symbol: {s}", .{self.symbol(undef_index).name(self)}); - for (atoms[0..natoms]) |atom_index| { - const atom_ptr = self.atom(atom_index).?; - const file_ptr = self.file(atom_ptr.file_index).?; + for (atoms[0..natoms]) |ref| { + const atom_ptr = self.atom(ref).?; + const file_ptr = self.file(ref.file).?; try err.addNote("referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) }); } @@ -6253,7 +5868,7 @@ fn fmtDumpState( try writer.writeAll("Output COMDAT groups\n"); for (self.comdat_group_sections.items) |cg| { - try writer.print(" shdr({d}) : COMDAT({d})\n", .{ cg.shndx, cg.cg_index }); + try writer.print(" shdr({d}) : COMDAT({})\n", .{ cg.shndx, cg.cg_ref }); } try writer.writeAll("\nOutput merge sections\n"); @@ -6340,21 +5955,24 @@ const default_entry_addr = 0x8000000; pub const base_tag: link.File.Tag = .elf; -const ComdatGroupOwner = struct { - file: File.Index = 0, - - const Index = u32; -}; - pub const ComdatGroup = struct { - owner: ComdatGroupOwner.Index, - file: File.Index, + signature_off: u32, + file_index: File.Index, shndx: u32, members_start: u32, members_len: u32, + alive: bool = true, + + pub fn file(cg: ComdatGroup, elf_file: *Elf) File { + return elf_file.file(cg.file_index).?; + } + + pub fn signature(cg: ComdatGroup, elf_file: *Elf) [:0]const u8 { + return cg.file(elf_file).object.getString(cg.signature_off); + } pub fn comdatGroupMembers(cg: ComdatGroup, elf_file: *Elf) []const u32 { - const object = elf_file.file(cg.file).?.object; + const object = cg.file(elf_file).object; return object.comdat_group_data.items[cg.members_start..][0..cg.members_len]; } @@ -6396,6 +6014,22 @@ pub const SystemLib = struct { path: []const u8, }; +pub const Ref = struct { + index: u32, + file: u32, + + pub fn format( + ref: Ref, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = unused_fmt_string; + _ = options; + try writer.print("ref({},{})", .{ ref.index, ref.file }); + } +}; + const LastAtomAndFreeList = struct { /// Index of the last allocated atom in this section. last_atom_index: Atom.Index = 0, @@ -6421,7 +6055,7 @@ const LastAtomAndFreeListTable = std.AutoArrayHashMapUnmanaged(u32, LastAtomAndF const RelaSection = struct { shndx: u32, - atom_list: std.ArrayListUnmanaged(Atom.Index) = .{}, + atom_list: std.ArrayListUnmanaged(Ref) = .{}, }; const RelaSectionTable = std.AutoArrayHashMapUnmanaged(u32, RelaSection); diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index bf2de7ccd90a1d9d7704eb73e8b37388bd09bc76..30285f204a9b980ca97208a0c0450c1725edaf0e 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -30,14 +30,17 @@ atom_index: Index = 0, prev_index: Index = 0, next_index: Index = 0, -/// Flags we use for state tracking. -flags: Flags = .{}, +/// 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, extra_index: u32 = 0, pub const Alignment = @import("../../InternPool.zig").Alignment; -pub fn name(self: Atom, elf_file: *Elf) []const u8 { +pub fn name(self: Atom, elf_file: *Elf) [:0]const u8 { const file_ptr = self.file(elf_file).?; return switch (file_ptr) { inline else => |x| x.getString(self.name_offset), @@ -45,8 +48,7 @@ pub fn name(self: Atom, elf_file: *Elf) []const u8 { } pub fn address(self: Atom, elf_file: *Elf) i64 { - const shndx = self.outputShndx() orelse return self.value; - const shdr = elf_file.shdrs.items[shndx]; + const shdr = elf_file.shdrs.items[self.output_section_index]; return @as(i64, @intCast(shdr.sh_addr)) + self.value; } @@ -55,7 +57,7 @@ pub fn debugTombstoneValue(self: Atom, target: Symbol, elf_file: *Elf) ?u64 { if (msub.alive) return null; } if (target.atom(elf_file)) |atom_ptr| { - if (atom_ptr.flags.alive) return null; + if (atom_ptr.alive) return null; } const atom_name = self.name(elf_file); if (!mem.startsWith(u8, atom_name, ".debug")) return null; @@ -67,8 +69,7 @@ pub fn file(self: Atom, elf_file: *Elf) ?File { } pub fn thunk(self: Atom, elf_file: *Elf) *Thunk { - assert(self.flags.thunk); - const extras = self.extra(elf_file).?; + const extras = self.extra(elf_file); return elf_file.thunk(extras.thunk); } @@ -85,11 +86,6 @@ pub fn relocsShndx(self: Atom) ?u32 { return self.relocs_section_index; } -pub fn outputShndx(self: Atom) ?u32 { - if (self.output_section_index == 0) return null; - return self.output_section_index; -} - pub fn priority(self: Atom, elf_file: *Elf) u64 { const index = self.file(elf_file).?.index(); return (@as(u64, @intCast(index)) << 32) | @as(u64, @intCast(self.input_section_index)); @@ -99,7 +95,8 @@ pub fn priority(self: Atom, elf_file: *Elf) u64 { /// File offset relocation happens transparently, so it is not included in /// this calculation. pub fn capacity(self: Atom, elf_file: *Elf) u64 { - const next_addr = if (elf_file.atom(self.next_index)) |next| + const zo = elf_file.zigObjectPtr().?; + const next_addr = if (zo.atom(self.next_index)) |next| next.address(elf_file) else std.math.maxInt(u32); @@ -107,8 +104,9 @@ pub fn capacity(self: Atom, elf_file: *Elf) u64 { } pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { + const zo = elf_file.zigObjectPtr().?; // No need to keep a free list node for the last block. - const next = elf_file.atom(self.next_index) orelse return false; + const next = zo.atom(self.next_index) orelse return false; const cap: u64 = @intCast(next.address(elf_file) - self.address(elf_file)); const ideal_cap = Elf.padToIdeal(self.size); if (cap <= ideal_cap) return false; @@ -117,8 +115,9 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { } pub fn allocate(self: *Atom, elf_file: *Elf) !void { - const shdr = &elf_file.shdrs.items[self.outputShndx().?]; - const meta = elf_file.last_atom_and_free_list_table.getPtr(self.outputShndx().?).?; + const zo = elf_file.zigObjectPtr().?; + const shdr = &elf_file.shdrs.items[self.output_section_index]; + const meta = elf_file.last_atom_and_free_list_table.getPtr(self.output_section_index).?; const free_list = &meta.free_list; const last_atom_index = &meta.last_atom_index; const new_atom_ideal_capacity = Elf.padToIdeal(self.size); @@ -137,7 +136,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { 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).?; + const big_atom = zo.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); @@ -169,7 +168,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { free_list_removal = i; } break :blk @intCast(new_start_vaddr); - } else if (elf_file.atom(last_atom_index.*)) |last| { + } else if (zo.atom(last_atom_index.*)) |last| { const ideal_capacity = Elf.padToIdeal(last.size); const ideal_capacity_end_vaddr = @as(u64, @intCast(last.value)) + ideal_capacity; const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr); @@ -189,12 +188,12 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { }); const expand_section = if (atom_placement) |placement_index| - elf_file.atom(placement_index).?.next_index == 0 + zo.atom(placement_index).?.next_index == 0 else true; if (expand_section) { const needed_size: u64 = @intCast(self.value + @as(i64, @intCast(self.size))); - try elf_file.growAllocSection(self.outputShndx().?, needed_size); + try elf_file.growAllocSection(self.output_section_index, needed_size); last_atom_index.* = self.atom_index; const zig_object = elf_file.zigObjectPtr().?; @@ -214,15 +213,15 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { // 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 (elf_file.atom(self.prev_index)) |prev| { + if (zo.atom(self.prev_index)) |prev| { prev.next_index = self.next_index; } - if (elf_file.atom(self.next_index)) |next| { + if (zo.atom(self.next_index)) |next| { next.prev_index = self.prev_index; } if (atom_placement) |big_atom_index| { - const big_atom = elf_file.atom(big_atom_index).?; + const big_atom = zo.atom(big_atom_index).?; self.prev_index = big_atom_index; self.next_index = big_atom.next_index; big_atom.next_index = self.atom_index; @@ -234,7 +233,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { _ = free_list.swapRemove(i); } - self.flags.alive = true; + self.alive = true; } pub fn shrink(self: *Atom, elf_file: *Elf) void { @@ -250,9 +249,10 @@ pub fn grow(self: *Atom, elf_file: *Elf) !void { pub fn free(self: *Atom, elf_file: *Elf) void { log.debug("freeAtom {d} ({s})", .{ self.atom_index, self.name(elf_file) }); + const zo = elf_file.zigObjectPtr().?; const comp = elf_file.base.comp; const gpa = comp.gpa; - const shndx = self.outputShndx().?; + const shndx = self.output_section_index; const meta = elf_file.last_atom_and_free_list_table.getPtr(shndx).?; const free_list = &meta.free_list; const last_atom_index = &meta.last_atom_index; @@ -272,9 +272,9 @@ pub fn free(self: *Atom, elf_file: *Elf) void { } } - if (elf_file.atom(last_atom_index.*)) |last_atom| { + if (zo.atom(last_atom_index.*)) |last_atom| { if (last_atom.atom_index == self.atom_index) { - if (elf_file.atom(self.prev_index)) |_| { + if (zo.atom(self.prev_index)) |_| { // TODO shrink the section size here last_atom_index.* = self.prev_index; } else { @@ -283,7 +283,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void { } } - if (elf_file.atom(self.prev_index)) |prev| { + if (zo.atom(self.prev_index)) |prev| { prev.next_index = self.next_index; if (!already_have_free_list_node and prev.*.freeListEligible(elf_file)) { // The free list is heuristics, it doesn't have to be perfect, so we can @@ -294,7 +294,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void { self.prev_index = 0; } - if (elf_file.atom(self.next_index)) |next| { + if (zo.atom(self.next_index)) |next| { next.prev_index = self.prev_index; } else { self.next_index = 0; @@ -313,7 +313,7 @@ pub fn relocs(self: Atom, elf_file: *Elf) []const elf.Elf64_Rela { switch (self.file(elf_file).?) { .zig_object => |x| return x.relocs.items[shndx].items, .object => |x| { - const extras = self.extra(elf_file).?; + const extras = self.extra(elf_file); return x.relocs.items[extras.rel_index..][0..extras.rel_count]; }, else => unreachable, @@ -337,12 +337,12 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El var r_addend = rel.r_addend; var r_sym: u32 = 0; switch (target.type(elf_file)) { - elf.STT_SECTION => if (target.mergeSubsection(elf_file)) |msub| { + elf.STT_SECTION => { r_addend += @intCast(target.address(.{}, elf_file)); - r_sym = elf_file.sectionSymbolOutputSymtabIndex(msub.mergeSection(elf_file).output_section_index); - } else { - r_addend += @intCast(target.address(.{}, elf_file)); - r_sym = elf_file.sectionSymbolOutputSymtabIndex(target.outputShndx().?); + r_sym = if (target.outputShndx(elf_file)) |osec| + elf_file.sectionSymbolOutputSymtabIndex(osec) + else + 0; }, else => { r_sym = target.outputSymtabIndex(elf_file) orelse 0; @@ -366,10 +366,12 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El } pub fn fdes(self: Atom, elf_file: *Elf) []Fde { - if (!self.flags.fde) return &[0]Fde{}; - const extras = self.extra(elf_file).?; - const object = self.file(elf_file).?.object; - return object.fdes.items[extras.fde_start..][0..extras.fde_count]; + const extras = self.extra(elf_file); + return switch (self.file(elf_file).?) { + .shared_object => unreachable, + .linker_defined, .zig_object => &[0]Fde{}, + .object => |x| x.fdes.items[extras.fde_start..][0..extras.fde_count], + }; } pub fn markFdesDead(self: Atom, elf_file: *Elf) void { @@ -712,9 +714,9 @@ fn reportUndefined( { const gop = try undefs.getOrPut(sym_index); if (!gop.found_existing) { - gop.value_ptr.* = std.ArrayList(Atom.Index).init(gpa); + gop.value_ptr.* = std.ArrayList(Elf.Ref).init(gpa); } - try gop.value_ptr.append(self.atom_index); + try gop.value_ptr.append(.{ .index = self.atom_index, .file = self.file_index }); return true; } @@ -1001,25 +1003,23 @@ const AddExtraOpts = struct { rel_count: ?u32 = null, }; -pub fn addExtra(atom: *Atom, opts: AddExtraOpts, elf_file: *Elf) !void { - if (atom.extra(elf_file) == null) { - atom.extra_index = try elf_file.addAtomExtra(.{}); - } - var extras = atom.extra(elf_file).?; +pub fn addExtra(atom: *Atom, opts: AddExtraOpts, elf_file: *Elf) void { + const file_ptr = atom.file(elf_file).?; + var extras = file_ptr.atomExtra(atom.extra_index); inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| { if (@field(opts, field.name)) |x| { @field(extras, field.name) = x; } } - atom.setExtra(extras, elf_file); + file_ptr.setAtomExtra(atom.extra_index, extras); } -pub inline fn extra(atom: Atom, elf_file: *Elf) ?Extra { - return elf_file.atomExtra(atom.extra_index); +pub inline fn extra(atom: Atom, elf_file: *Elf) Extra { + return atom.file(elf_file).?.atomExtra(atom.extra_index); } pub inline fn setExtra(atom: Atom, extras: Extra, elf_file: *Elf) void { - elf_file.setAtomExtra(atom.extra_index, extras); + atom.file(elf_file).?.setAtomExtra(atom.extra_index, extras); } pub fn format( @@ -1061,9 +1061,9 @@ fn format2( atom.atom_index, atom.name(elf_file), atom.address(elf_file), atom.output_section_index, atom.alignment, atom.size, }); - if (atom.flags.fde) { + if (atom.fdes(elf_file).len > 0) { try writer.writeAll(" : fdes{ "); - const extras = atom.extra(elf_file).?; + const extras = atom.extra(elf_file); for (atom.fdes(elf_file), extras.fde_start..) |fde, i| { try writer.print("{d}", .{i}); if (!fde.alive) try writer.writeAll("([*])"); @@ -1071,27 +1071,13 @@ fn format2( } try writer.writeAll(" }"); } - if (!atom.flags.alive) { + if (!atom.alive) { try writer.writeAll(" : [*]"); } } pub const Index = u32; -pub const Flags = packed struct { - /// 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, - - /// Whether this atom has a range extension thunk. - thunk: bool = false, - - /// Whether this atom has FDE records. - fde: bool = false, -}; - const x86_64 = struct { fn scanReloc( atom: Atom, diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig index 97edd34791884e0e1ddfc9a14506f9b8e41569c8..676c49cc7db71fb810774b849877acf3d3e2b02e 100644 --- a/src/link/Elf/LinkerDefined.zig +++ b/src/link/Elf/LinkerDefined.zig @@ -1,17 +1,89 @@ index: File.Index, + symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +entry_index: ?Symbol.Index = null, +dynamic_index: ?Symbol.Index = null, +ehdr_start_index: ?Symbol.Index = null, +init_array_start_index: ?Symbol.Index = null, +init_array_end_index: ?Symbol.Index = null, +fini_array_start_index: ?Symbol.Index = null, +fini_array_end_index: ?Symbol.Index = null, +preinit_array_start_index: ?Symbol.Index = null, +preinit_array_end_index: ?Symbol.Index = null, +got_index: ?Symbol.Index = null, +plt_index: ?Symbol.Index = null, +end_index: ?Symbol.Index = null, +gnu_eh_frame_hdr_index: ?Symbol.Index = null, +dso_handle_index: ?Symbol.Index = null, +rela_iplt_start_index: ?Symbol.Index = null, +rela_iplt_end_index: ?Symbol.Index = null, +global_pointer_index: ?Symbol.Index = null, +start_stop_indexes: std.ArrayListUnmanaged(u32) = .{}, + output_symtab_ctx: Elf.SymtabCtx = .{}, pub fn deinit(self: *LinkerDefined, allocator: Allocator) void { self.symtab.deinit(allocator); self.strtab.deinit(allocator); self.symbols.deinit(allocator); + self.start_stop_indexes.deinit(allocator); } -pub fn addGlobal(self: *LinkerDefined, name: [:0]const u8, elf_file: *Elf) !u32 { +pub fn init(self: *LinkerDefined, allocator: Allocator) !void { + // Null byte in strtab + try self.strtab.append(allocator, 0); +} + +pub fn initSymbols(self: *LinkerDefined, elf_file: *Elf) !void { + const gpa = elf_file.base.comp.gpa; + + if (elf_file.entry_name) |name| { + self.entry_index = try self.addGlobal(name, elf_file); + } + + self.dynamic_index = try self.addGlobal("_DYNAMIC", elf_file); + self.ehdr_start_index = try self.addGlobal("__ehdr_start", elf_file); + self.init_array_start_index = try self.addGlobal("__init_array_start", elf_file); + self.init_array_end_index = try self.addGlobal("__init_array_end", elf_file); + self.fini_array_start_index = try self.addGlobal("__fini_array_start", elf_file); + self.fini_array_end_index = try self.addGlobal("__fini_array_end", elf_file); + self.preinit_array_start_index = try self.addGlobal("__preinit_array_start", elf_file); + self.preinit_array_end_index = try self.addGlobal("__preinit_array_end", elf_file); + self.got_index = try self.addGlobal("_GLOBAL_OFFSET_TABLE_", elf_file); + self.plt_index = try self.addGlobal("_PROCEDURE_LINKAGE_TABLE_", elf_file); + self.end_index = try self.addGlobal("_end", elf_file); + + if (elf_file.base.comp.link_eh_frame_hdr) { + self.gnu_eh_frame_hdr_index = try self.addGlobal("__GNU_EH_FRAME_HDR", elf_file); + } + + self.dso_handle_index = try self.addGlobal("__dso_handle", elf_file); + self.rela_iplt_start_index = try self.addGlobal("__rela_iplt_start", elf_file); + self.rela_iplt_end_index = try self.addGlobal("__rela_iplt_end", elf_file); + + for (elf_file.shdrs.items) |shdr| { + if (elf_file.getStartStopBasename(shdr)) |name| { + try self.start_stop_indexes.ensureUnusedCapacity(gpa, 2); + + const start = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name}); + defer gpa.free(start); + const stop = try std.fmt.allocPrintZ(gpa, "__stop_{s}", .{name}); + defer gpa.free(stop); + + self.start_stop_indexes.appendAssumeCapacity(try self.addGlobal(start, elf_file)); + self.start_stop_indexes.appendAssumeCapacity(try self.addGlobal(stop, elf_file)); + } + } + + if (elf_file.getTarget().cpu.arch.isRISCV() and elf_file.isEffectivelyDynLib()) { + self.global_pointer_index = try self.addGlobal("__global_pointer$", elf_file); + } +} + +fn addGlobal(self: *LinkerDefined, name: []const u8, elf_file: *Elf) !u32 { const comp = elf_file.base.comp; const gpa = comp.gpa; try self.symtab.ensureUnusedCapacity(gpa, 1); @@ -41,7 +113,7 @@ pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void { const global = elf_file.symbol(index); if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) { global.value = 0; - global.atom_index = 0; + global.ref = .{ .index = 0, .file = 0 }; global.file_index = self.index; global.esym_index = sym_idx; global.version_index = elf_file.default_sym_version; @@ -49,6 +121,154 @@ pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void { } } +pub fn allocateSymbols(self: *LinkerDefined, elf_file: *Elf) void { + const comp = elf_file.base.comp; + const link_mode = comp.config.link_mode; + + // _DYNAMIC + if (elf_file.dynamic_section_index) |shndx| { + const shdr = &elf_file.shdrs.items[shndx]; + const symbol_ptr = elf_file.symbol(self.dynamic_index.?); + symbol_ptr.value = @intCast(shdr.sh_addr); + symbol_ptr.output_section_index = shndx; + } + + // __ehdr_start + { + const symbol_ptr = elf_file.symbol(self.ehdr_start_index.?); + symbol_ptr.value = @intCast(elf_file.image_base); + symbol_ptr.output_section_index = 1; + } + + // __init_array_start, __init_array_end + if (elf_file.sectionByName(".init_array")) |shndx| { + const start_sym = elf_file.symbol(self.init_array_start_index.?); + const end_sym = elf_file.symbol(self.init_array_end_index.?); + const shdr = &elf_file.shdrs.items[shndx]; + start_sym.output_section_index = shndx; + start_sym.value = @intCast(shdr.sh_addr); + end_sym.output_section_index = shndx; + end_sym.value = @intCast(shdr.sh_addr + shdr.sh_size); + } + + // __fini_array_start, __fini_array_end + if (elf_file.sectionByName(".fini_array")) |shndx| { + const start_sym = elf_file.symbol(self.fini_array_start_index.?); + const end_sym = elf_file.symbol(self.fini_array_end_index.?); + const shdr = &elf_file.shdrs.items[shndx]; + start_sym.output_section_index = shndx; + start_sym.value = @intCast(shdr.sh_addr); + end_sym.output_section_index = shndx; + end_sym.value = @intCast(shdr.sh_addr + shdr.sh_size); + } + + // __preinit_array_start, __preinit_array_end + if (elf_file.sectionByName(".preinit_array")) |shndx| { + const start_sym = elf_file.symbol(self.preinit_array_start_index.?); + const end_sym = elf_file.symbol(self.preinit_array_end_index.?); + const shdr = &elf_file.shdrs.items[shndx]; + start_sym.output_section_index = shndx; + start_sym.value = @intCast(shdr.sh_addr); + end_sym.output_section_index = shndx; + end_sym.value = @intCast(shdr.sh_addr + shdr.sh_size); + } + + // _GLOBAL_OFFSET_TABLE_ + if (elf_file.getTarget().cpu.arch == .x86_64) { + if (elf_file.got_plt_section_index) |shndx| { + const shdr = elf_file.shdrs.items[shndx]; + const sym = elf_file.symbol(self.got_index.?); + sym.value = @intCast(shdr.sh_addr); + sym.output_section_index = shndx; + } + } else { + if (elf_file.got_section_index) |shndx| { + const shdr = elf_file.shdrs.items[shndx]; + const sym = elf_file.symbol(self.got_index.?); + sym.value = @intCast(shdr.sh_addr); + sym.output_section_index = shndx; + } + } + + // _PROCEDURE_LINKAGE_TABLE_ + if (elf_file.plt_section_index) |shndx| { + const shdr = &elf_file.shdrs.items[shndx]; + const symbol_ptr = elf_file.symbol(self.plt_index.?); + symbol_ptr.value = @intCast(shdr.sh_addr); + symbol_ptr.output_section_index = shndx; + } + + // __dso_handle + if (self.dso_handle_index) |index| { + const shdr = &elf_file.shdrs.items[1]; + const symbol_ptr = elf_file.symbol(index); + symbol_ptr.value = @intCast(shdr.sh_addr); + symbol_ptr.output_section_index = 0; + } + + // __GNU_EH_FRAME_HDR + if (elf_file.eh_frame_hdr_section_index) |shndx| { + const shdr = &elf_file.shdrs.items[shndx]; + const symbol_ptr = elf_file.symbol(self.gnu_eh_frame_hdr_index.?); + symbol_ptr.value = @intCast(shdr.sh_addr); + symbol_ptr.output_section_index = shndx; + } + + // __rela_iplt_start, __rela_iplt_end + if (elf_file.rela_dyn_section_index) |shndx| blk: { + if (link_mode != .static or comp.config.pie) break :blk; + const shdr = &elf_file.shdrs.items[shndx]; + const end_addr = shdr.sh_addr + shdr.sh_size; + const start_addr = end_addr - elf_file.calcNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela); + const start_sym = elf_file.symbol(self.rela_iplt_start_index.?); + const end_sym = elf_file.symbol(self.rela_iplt_end_index.?); + start_sym.value = @intCast(start_addr); + start_sym.output_section_index = shndx; + end_sym.value = @intCast(end_addr); + end_sym.output_section_index = shndx; + } + + // _end + { + const end_symbol = elf_file.symbol(self.end_index.?); + for (elf_file.shdrs.items, 0..) |shdr, shndx| { + if (shdr.sh_flags & elf.SHF_ALLOC != 0) { + end_symbol.value = @intCast(shdr.sh_addr + shdr.sh_size); + end_symbol.output_section_index = @intCast(shndx); + } + } + } + + // __start_*, __stop_* + { + var index: usize = 0; + while (index < self.start_stop_indexes.items.len) : (index += 2) { + const start = elf_file.symbol(self.start_stop_indexes.items[index]); + const name = start.name(elf_file); + const stop = elf_file.symbol(self.start_stop_indexes.items[index + 1]); + const shndx = elf_file.sectionByName(name["__start_".len..]).?; + const shdr = &elf_file.shdrs.items[shndx]; + start.value = @intCast(shdr.sh_addr); + start.output_section_index = shndx; + stop.value = @intCast(shdr.sh_addr + shdr.sh_size); + stop.output_section_index = shndx; + } + } + + // __global_pointer$ + if (self.global_pointer_index) |index| { + const sym = elf_file.symbol(index); + if (elf_file.sectionByName(".sdata")) |shndx| { + const shdr = elf_file.shdrs.items[shndx]; + sym.value = @intCast(shdr.sh_addr + 0x800); + sym.output_section_index = shndx; + } else { + sym.value = 0; + sym.output_section_index = 0; + } + } +} + pub fn globals(self: LinkerDefined) []const Symbol.Index { return self.symbols.items; } @@ -127,6 +347,7 @@ const mem = std.mem; const std = @import("std"); const Allocator = mem.Allocator; +const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; const LinkerDefined = @This(); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index e24bc431e81d97a998596a062954ffd869fef5e4..9185ca0ef7c4e61943adb8b45c7f87d740df0ef3 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -10,12 +10,17 @@ symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, first_global: ?Symbol.Index = null, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, -atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, -comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{}, -comdat_group_data: std.ArrayListUnmanaged(u32) = .{}, relocs: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, -merge_sections: std.ArrayListUnmanaged(InputMergeSection.Index) = .{}, +atoms: std.ArrayListUnmanaged(Atom) = .{}, +atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{}, +atoms_extra: std.ArrayListUnmanaged(u32) = .{}, + +comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup) = .{}, +comdat_group_data: std.ArrayListUnmanaged(u32) = .{}, + +input_merge_sections: std.ArrayListUnmanaged(InputMergeSection) = .{}, +input_merge_sections_indexes: std.ArrayListUnmanaged(InputMergeSection.Index) = .{}, fdes: std.ArrayListUnmanaged(Fde) = .{}, cies: std.ArrayListUnmanaged(Cie) = .{}, @@ -47,13 +52,19 @@ pub fn deinit(self: *Object, allocator: Allocator) void { self.strtab.deinit(allocator); self.symbols.deinit(allocator); self.atoms.deinit(allocator); + self.atoms_indexes.deinit(allocator); + self.atoms_extra.deinit(allocator); self.comdat_groups.deinit(allocator); self.comdat_group_data.deinit(allocator); self.relocs.deinit(allocator); self.fdes.deinit(allocator); self.cies.deinit(allocator); self.eh_frame_data.deinit(allocator); - self.merge_sections.deinit(allocator); + for (self.input_merge_sections.items) |*isec| { + isec.deinit(allocator); + } + self.input_merge_sections.deinit(allocator); + self.input_merge_sections_indexes.deinit(allocator); } pub fn parse(self: *Object, elf_file: *Elf) !void { @@ -62,14 +73,20 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { const handle = elf_file.fileHandle(self.file_handle); try self.parseCommon(gpa, handle, elf_file); + + // Append null input merge section + try self.input_merge_sections.append(gpa, .{}); + // Allocate atom index 0 to null atom + try self.atoms.append(gpa, .{ .extra_index = try self.addAtomExtra(gpa, .{}) }); + try self.initAtoms(gpa, handle, elf_file); try self.initSymtab(gpa, elf_file); for (self.shdrs.items, 0..) |shdr, i| { - const atom = elf_file.atom(self.atoms.items[i]) orelse continue; - if (!atom.flags.alive) continue; + const atom_ptr = self.atom(self.atoms_indexes.items[i]) orelse continue; + if (!atom_ptr.alive) continue; if ((cpu_arch == .x86_64 and shdr.sh_type == elf.SHT_X86_64_UNWIND) or - mem.eql(u8, atom.name(elf_file), ".eh_frame")) + mem.eql(u8, atom_ptr.name(elf_file), ".eh_frame")) { try self.parseEhFrame(gpa, handle, @as(u32, @intCast(i)), elf_file); } @@ -169,8 +186,11 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void { const shdrs = self.shdrs.items; - try self.atoms.resize(allocator, shdrs.len); - @memset(self.atoms.items, 0); + try self.atoms.ensureTotalCapacityPrecise(allocator, shdrs.len); + try self.atoms_extra.ensureTotalCapacityPrecise(allocator, shdrs.len * @sizeOf(Atom.Extra)); + try self.atoms_indexes.ensureTotalCapacityPrecise(allocator, shdrs.len); + try self.atoms_indexes.resize(allocator, shdrs.len); + @memset(self.atoms_indexes.items, 0); for (shdrs, 0..) |shdr, i| { if (shdr.sh_flags & elf.SHF_EXCLUDE != 0 and @@ -188,37 +208,53 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: const group_signature = blk: { if (group_info_sym.st_name == 0 and group_info_sym.st_type() == elf.STT_SECTION) { const sym_shdr = shdrs[group_info_sym.st_shndx]; - break :blk self.getString(sym_shdr.sh_name); + break :blk sym_shdr.sh_name; } - break :blk self.getString(group_info_sym.st_name); + break :blk group_info_sym.st_name; }; const shndx = @as(u32, @intCast(i)); const group_raw_data = try self.preadShdrContentsAlloc(allocator, handle, shndx); defer allocator.free(group_raw_data); - const group_nmembers = @divExact(group_raw_data.len, @sizeOf(u32)); + const group_nmembers = math.divExact(usize, group_raw_data.len, @sizeOf(u32)) catch { + try elf_file.reportParseError2( + self.index, + "corrupt section group: not evenly divisible ", + .{}, + ); + return error.MalformedObject; + }; + if (group_nmembers == 0) { + try elf_file.reportParseError2( + self.index, + "corrupt section group: empty section", + .{}, + ); + return error.MalformedObject; + } const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers]; if (group_members[0] != elf.GRP_COMDAT) { - // TODO convert into an error - log.debug("{}: unknown SHT_GROUP format", .{self.fmtPath()}); - continue; + try elf_file.reportParseError2( + self.index, + "corrupt section group: unknown SHT_GROUP format", + .{}, + ); + return error.MalformedObject; } const group_start = @as(u32, @intCast(self.comdat_group_data.items.len)); try self.comdat_group_data.appendUnalignedSlice(allocator, group_members[1..]); - const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature); - const comdat_group_index = try elf_file.addComdatGroup(); - const comdat_group = elf_file.comdatGroup(comdat_group_index); + const comdat_group_index = try self.addComdatGroup(allocator); + const comdat_group = self.comdatGroup(comdat_group_index); comdat_group.* = .{ - .owner = gop.index, - .file = self.index, + .signature_off = group_signature, + .file_index = self.index, .shndx = shndx, .members_start = group_start, .members_len = @intCast(group_nmembers - 1), }; - try self.comdat_groups.append(allocator, comdat_group_index); }, elf.SHT_SYMTAB_SHNDX => @panic("TODO SHT_SYMTAB_SHNDX"), @@ -233,7 +269,19 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: else => { const shndx = @as(u32, @intCast(i)); if (self.skipShdr(shndx, elf_file)) continue; - try self.addAtom(allocator, handle, shdr, shndx, elf_file); + const size, const alignment = if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) blk: { + const data = try self.preadShdrContentsAlloc(allocator, handle, shndx); + defer allocator.free(data); + const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*; + break :blk .{ chdr.ch_size, Alignment.fromNonzeroByteUnits(chdr.ch_addralign) }; + } else .{ shdr.sh_size, Alignment.fromNonzeroByteUnits(shdr.sh_addralign) }; + const atom_index = self.addAtomAssumeCapacity(.{ + .name = shdr.sh_name, + .shndx = shndx, + .size = size, + .alignment = alignment, + }); + self.atoms_indexes.items[shndx] = atom_index; }, } } @@ -241,14 +289,14 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: // Parse relocs sections if any. for (shdrs, 0..) |shdr, i| switch (shdr.sh_type) { elf.SHT_REL, elf.SHT_RELA => { - const atom_index = self.atoms.items[shdr.sh_info]; - if (elf_file.atom(atom_index)) |atom| { + const atom_index = self.atoms_indexes.items[shdr.sh_info]; + if (self.atom(atom_index)) |atom_ptr| { const relocs = try self.preadRelocsAlloc(allocator, handle, @intCast(i)); defer allocator.free(relocs); - atom.relocs_section_index = @intCast(i); + atom_ptr.relocs_section_index = @intCast(i); const rel_index: u32 = @intCast(self.relocs.items.len); const rel_count: u32 = @intCast(relocs.len); - try atom.addExtra(.{ .rel_index = rel_index, .rel_count = rel_count }, elf_file); + atom_ptr.addExtra(.{ .rel_index = rel_index, .rel_count = rel_count }, elf_file); try self.relocs.appendUnalignedSlice(allocator, relocs); if (elf_file.getTarget().cpu.arch == .riscv64) { sortRelocs(self.relocs.items[rel_index..][0..rel_count]); @@ -259,27 +307,6 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: }; } -fn addAtom(self: *Object, allocator: Allocator, handle: std.fs.File, shdr: elf.Elf64_Shdr, shndx: u32, elf_file: *Elf) !void { - const atom_index = try elf_file.addAtom(); - const atom = elf_file.atom(atom_index).?; - atom.atom_index = atom_index; - atom.name_offset = shdr.sh_name; - atom.file_index = self.index; - atom.input_section_index = shndx; - self.atoms.items[shndx] = atom_index; - - if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) { - const data = try self.preadShdrContentsAlloc(allocator, handle, shndx); - defer allocator.free(data); - const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*; - atom.size = chdr.ch_size; - atom.alignment = Alignment.fromNonzeroByteUnits(chdr.ch_addralign); - } else { - atom.size = shdr.sh_size; - atom.alignment = Alignment.fromNonzeroByteUnits(shdr.sh_addralign); - } -} - fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) error{OutOfMemory}!u32 { const name = blk: { const name = self.getString(shdr.sh_name); @@ -327,7 +354,7 @@ fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) error{O const out_shndx = elf_file.sectionByName(name) orelse try elf_file.addSection(.{ .type = @"type", .flags = flags, - .name = name, + .name = try elf_file.insertShString(name), }); return out_shndx; } @@ -359,8 +386,10 @@ fn initSymtab(self: *Object, allocator: Allocator, elf_file: *Elf) !void { sym_ptr.value = @intCast(sym.st_value); sym_ptr.name_offset = sym.st_name; sym_ptr.esym_index = @as(u32, @intCast(i)); - sym_ptr.atom_index = if (sym.st_shndx == elf.SHN_ABS) 0 else self.atoms.items[sym.st_shndx]; sym_ptr.file_index = self.index; + if (sym.st_shndx != elf.SHN_ABS) { + sym_ptr.ref = .{ .index = self.atoms_indexes.items[sym.st_shndx], .file = self.index }; + } } for (self.symtab.items[first_global..]) |sym| { @@ -447,15 +476,14 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx: var i: u32 = @as(u32, @intCast(fdes_start)); while (i < self.fdes.items.len) { const fde = self.fdes.items[i]; - const atom = fde.atom(elf_file); + const atom_ptr = fde.atom(elf_file); const start = i; i += 1; while (i < self.fdes.items.len) : (i += 1) { const next_fde = self.fdes.items[i]; - if (atom.atom_index != next_fde.atom(elf_file).atom_index) break; + if (atom_ptr.atom_index != next_fde.atom(elf_file).atom_index) break; } - try atom.addExtra(.{ .fde_start = start, .fde_count = i - start }, elf_file); - atom.flags.fde = true; + atom_ptr.addExtra(.{ .fde_start = start, .fde_count = i - start }, elf_file); } } @@ -498,19 +526,19 @@ fn filterRelocs( pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; - for (self.atoms.items) |atom_index| { - const atom = elf_file.atom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - const shdr = atom.inputShdr(elf_file); + for (self.atoms_indexes.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.alive) continue; + const shdr = atom_ptr.inputShdr(elf_file); if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; if (shdr.sh_type == elf.SHT_NOBITS) continue; - if (atom.scanRelocsRequiresCode(elf_file)) { + if (atom_ptr.scanRelocsRequiresCode(elf_file)) { // TODO ideally, we don't have to decompress at this stage (should already be done) // and we just fetch the code slice. const code = try self.codeDecompressAlloc(elf_file, atom_index); defer gpa.free(code); - try atom.scanRelocs(elf_file, code, undefs); - } else try atom.scanRelocs(elf_file, null, undefs); + try atom_ptr.scanRelocs(elf_file, code, undefs); + } else try atom_ptr.scanRelocs(elf_file, null, undefs); } for (self.cies.items) |cie| { @@ -538,19 +566,21 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void { if (esym.st_shndx == elf.SHN_UNDEF) continue; if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) { - const atom_index = self.atoms.items[esym.st_shndx]; - const atom = elf_file.atom(atom_index) orelse continue; - if (!atom.flags.alive) continue; + const atom_index = self.atoms_indexes.items[esym.st_shndx]; + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.alive) continue; } const global = elf_file.symbol(index); if (self.asFile().symbolRank(esym, !self.alive) < global.symbolRank(elf_file)) { - const atom_index = switch (esym.st_shndx) { - elf.SHN_ABS, elf.SHN_COMMON => 0, - else => self.atoms.items[esym.st_shndx], - }; + switch (esym.st_shndx) { + elf.SHN_ABS, elf.SHN_COMMON => {}, + else => global.ref = .{ + .index = self.atoms_indexes.items[esym.st_shndx], + .file = self.index, + }, + } global.value = @intCast(esym.st_value); - global.atom_index = atom_index; global.esym_index = esym_index; global.file_index = self.index; global.version_index = elf_file.default_sym_version; @@ -579,7 +609,7 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void { }; global.value = 0; - global.atom_index = 0; + global.ref = .{ .index = 0, .file = 0 }; global.esym_index = esym_index; global.file_index = self.index; global.version_index = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version; @@ -600,7 +630,7 @@ pub fn claimUnresolvedObject(self: *Object, elf_file: *Elf) void { } global.value = 0; - global.atom_index = 0; + global.ref = .{ .index = 0, .file = 0 }; global.esym_index = esym_index; global.file_index = self.index; } @@ -624,13 +654,13 @@ pub fn markLive(self: *Object, elf_file: *Elf) void { } } -pub fn markEhFrameAtomsDead(self: Object, elf_file: *Elf) void { +pub fn markEhFrameAtomsDead(self: *Object, elf_file: *Elf) void { const cpu_arch = elf_file.getTarget().cpu.arch; - for (self.atoms.items) |atom_index| { - const atom = elf_file.atom(atom_index) orelse continue; - const is_eh_frame = (cpu_arch == .x86_64 and atom.inputShdr(elf_file).sh_type == elf.SHT_X86_64_UNWIND) or - mem.eql(u8, atom.name(elf_file), ".eh_frame"); - if (atom.flags.alive and is_eh_frame) atom.flags.alive = false; + for (self.atoms_indexes.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + const is_eh_frame = (cpu_arch == .x86_64 and atom_ptr.inputShdr(elf_file).sh_type == elf.SHT_X86_64_UNWIND) or + mem.eql(u8, atom_ptr.name(elf_file), ".eh_frame"); + if (atom_ptr.alive and is_eh_frame) atom_ptr.alive = false; } } @@ -648,9 +678,9 @@ pub fn checkDuplicates(self: *Object, dupes: anytype, elf_file: *Elf) error{OutO sym.st_shndx == elf.SHN_COMMON) continue; if (sym.st_shndx != elf.SHN_ABS) { - const atom_index = self.atoms.items[sym.st_shndx]; - const atom = elf_file.atom(atom_index) orelse continue; - if (!atom.flags.alive) continue; + const atom_index = self.atoms_indexes.items[sym.st_shndx]; + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.alive) continue; } const gop = try dupes.getOrPut(index); @@ -661,25 +691,24 @@ pub fn checkDuplicates(self: *Object, dupes: anytype, elf_file: *Elf) error{OutO } } -pub fn initMergeSections(self: *Object, elf_file: *Elf) !void { +pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void { const gpa = elf_file.base.comp.gpa; - try self.merge_sections.resize(gpa, self.shdrs.items.len); - @memset(self.merge_sections.items, 0); + try self.input_merge_sections.ensureUnusedCapacity(gpa, self.shdrs.items.len); + try self.input_merge_sections_indexes.resize(gpa, self.shdrs.items.len); + @memset(self.input_merge_sections_indexes.items, 0); for (self.shdrs.items, 0..) |shdr, shndx| { if (shdr.sh_flags & elf.SHF_MERGE == 0) continue; - const atom_index = self.atoms.items[shndx]; - const atom_ptr = elf_file.atom(atom_index) orelse continue; - if (!atom_ptr.flags.alive) continue; + const atom_index = self.atoms_indexes.items[shndx]; + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.alive) continue; if (atom_ptr.relocs(elf_file).len > 0) continue; - const imsec_idx = try elf_file.addInputMergeSection(); - const imsec = elf_file.inputMergeSection(imsec_idx).?; - self.merge_sections.items[shndx] = imsec_idx; - - imsec.merge_section_index = try elf_file.getOrCreateMergeSection(atom_ptr.name(elf_file), shdr.sh_flags, shdr.sh_type); + const imsec_idx = try self.addInputMergeSection(gpa); + const imsec = self.inputMergeSection(imsec_idx).?; + self.input_merge_sections_indexes.items[shndx] = imsec_idx; imsec.atom_index = atom_index; const data = try self.codeDecompressAlloc(elf_file, atom_index); @@ -734,18 +763,31 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void { } } - atom_ptr.flags.alive = false; + atom_ptr.alive = false; + } +} + +pub fn initOutputMergeSections(self: *Object, elf_file: *Elf) !void { + for (self.input_merge_sections_indexes.items) |index| { + const imsec = self.inputMergeSection(index) orelse continue; + const atom_ptr = self.atom(imsec.atom_index).?; + const shdr = atom_ptr.inputShdr(elf_file); + imsec.merge_section_index = try elf_file.getOrCreateMergeSection( + atom_ptr.name(elf_file), + shdr.sh_flags, + shdr.sh_type, + ); } } pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { const gpa = elf_file.base.comp.gpa; - for (self.merge_sections.items) |index| { - const imsec = elf_file.inputMergeSection(index) orelse continue; + for (self.input_merge_sections_indexes.items) |index| { + const imsec = self.inputMergeSection(index) orelse continue; if (imsec.offsets.items.len == 0) continue; const msec = elf_file.mergeSection(imsec.merge_section_index); - const atom_ptr = elf_file.atom(imsec.atom_index).?; + const atom_ptr = self.atom(imsec.atom_index).?; const isec = atom_ptr.inputShdr(elf_file); try imsec.subsections.resize(gpa, imsec.strings.items.len); @@ -754,8 +796,8 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { const string = imsec.bytes.items[str.pos..][0..str.len]; const res = try msec.insert(gpa, string); if (!res.found_existing) { - const msub_index = try elf_file.addMergeSubsection(); - const msub = elf_file.mergeSubsection(msub_index); + const msub_index = try msec.addMergeSubsection(gpa); + const msub = msec.mergeSubsection(msub_index); msub.merge_section_index = imsec.merge_section_index; msub.string_index = res.key.pos; msub.alignment = atom_ptr.alignment; @@ -776,10 +818,10 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { if (esym.st_shndx == elf.SHN_COMMON or esym.st_shndx == elf.SHN_UNDEF or esym.st_shndx == elf.SHN_ABS) continue; - const imsec_index = self.merge_sections.items[esym.st_shndx]; - const imsec = elf_file.inputMergeSection(imsec_index) orelse continue; + const imsec_index = self.input_merge_sections_indexes.items[esym.st_shndx]; + const imsec = self.inputMergeSection(imsec_index) orelse continue; if (imsec.offsets.items.len == 0) continue; - const msub_index, const offset = imsec.findSubsection(@intCast(esym.st_value)) orelse { + const res = imsec.findSubsection(@intCast(esym.st_value)) orelse { var err = try elf_file.base.addErrorWithNotes(2); try err.addMsg("invalid symbol value: {x}", .{esym.st_value}); try err.addNote("for symbol {s}", .{sym.name(elf_file)}); @@ -787,45 +829,44 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { return error.MalformedObject; }; - try sym.addExtra(.{ .subsection = msub_index }, elf_file); + sym.ref = .{ .index = res.msub_index, .file = imsec.merge_section_index }; sym.flags.merge_subsection = true; - sym.value = offset; + sym.value = res.offset; } - for (self.atoms.items) |atom_index| { - const atom_ptr = elf_file.atom(atom_index) orelse continue; - if (!atom_ptr.flags.alive) continue; - const extras = atom_ptr.extra(elf_file) orelse continue; + for (self.atoms_indexes.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.alive) continue; + const extras = atom_ptr.extra(elf_file); const relocs = self.relocs.items[extras.rel_index..][0..extras.rel_count]; for (relocs) |*rel| { const esym = self.symtab.items[rel.r_sym()]; if (esym.st_type() != elf.STT_SECTION) continue; - const imsec_index = self.merge_sections.items[esym.st_shndx]; - const imsec = elf_file.inputMergeSection(imsec_index) orelse continue; + const imsec_index = self.input_merge_sections_indexes.items[esym.st_shndx]; + const imsec = self.inputMergeSection(imsec_index) orelse continue; if (imsec.offsets.items.len == 0) continue; - const msub_index, const offset = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse { + const msec = elf_file.mergeSection(imsec.merge_section_index); + const res = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse { var err = try elf_file.base.addErrorWithNotes(1); try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset}); try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); return error.MalformedObject; }; - const msub = elf_file.mergeSubsection(msub_index); - const msec = msub.mergeSection(elf_file); const out_sym_idx: u64 = @intCast(self.symbols.items.len); try self.symbols.ensureUnusedCapacity(gpa, 1); - const name = try std.fmt.allocPrint(gpa, "{s}$subsection{d}", .{ msec.name(elf_file), msub_index }); + const name = try std.fmt.allocPrint(gpa, "{s}$subsection{d}", .{ msec.name(elf_file), res.msub_index }); defer gpa.free(name); const sym_index = try elf_file.addSymbol(); const sym = elf_file.symbol(sym_index); sym.* = .{ - .value = @bitCast(@as(i64, @intCast(offset)) - rel.r_addend), + .value = @bitCast(@as(i64, @intCast(res.offset)) - rel.r_addend), .name_offset = try self.addString(gpa, name), .esym_index = rel.r_sym(), .file_index = self.index, }; - try sym.addExtra(.{ .subsection = msub_index }, elf_file); + sym.ref = .{ .index = res.msub_index, .file = imsec.merge_section_index }; sym.flags.merge_subsection = true; self.symbols.addOneAssumeCapacity().* = sym_index; rel.r_info = (out_sym_idx << 32) | rel.r_type(); @@ -857,21 +898,10 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; - const atom_index = try elf_file.addAtom(); - try self.atoms.append(gpa, atom_index); - const is_tls = global.type(elf_file) == elf.STT_TLS; const name = if (is_tls) ".tls_common" else ".common"; - - const atom = elf_file.atom(atom_index).?; const name_offset = @as(u32, @intCast(self.strtab.items.len)); try self.strtab.writer(gpa).print("{s}\x00", .{name}); - atom.atom_index = atom_index; - atom.name_offset = name_offset; - atom.file_index = self.index; - atom.size = this_sym.st_size; - const alignment = this_sym.st_value; - atom.alignment = Alignment.fromNonzeroByteUnits(alignment); var sh_flags: u32 = elf.SHF_ALLOC | elf.SHF_WRITE; if (is_tls) sh_flags |= elf.SHF_TLS; @@ -887,78 +917,84 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { .sh_size = sh_size, .sh_link = 0, .sh_info = 0, - .sh_addralign = alignment, + .sh_addralign = this_sym.st_value, .sh_entsize = 0, }; - atom.input_section_index = shndx; + + const atom_index = try self.addAtom(gpa, .{ + .name = name_offset, + .shndx = shndx, + .size = this_sym.st_size, + .alignment = Alignment.fromNonzeroByteUnits(this_sym.st_value), + }); + try self.atoms_indexes.append(gpa, atom_index); global.value = 0; - global.atom_index = atom_index; + global.ref = .{ .index = atom_index, .file = self.index }; global.flags.weak = false; } } -pub fn initOutputSections(self: Object, elf_file: *Elf) !void { - for (self.atoms.items) |atom_index| { - const atom = elf_file.atom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - const shdr = atom.inputShdr(elf_file); +pub fn resolveComdatGroups(self: *Object, elf_file: *Elf, table: anytype) !void { + for (self.comdat_groups.items, 0..) |*cg, cgi| { + const signature = cg.signature(elf_file); + const gop = try table.getOrPut(signature); + if (!gop.found_existing) { + gop.value_ptr.* = .{ .index = @intCast(cgi), .file = self.index }; + continue; + } + const current = elf_file.comdatGroup(gop.value_ptr.*); + cg.alive = false; + if (self.index < current.file_index) { + current.alive = false; + cg.alive = true; + gop.value_ptr.* = .{ .index = @intCast(cgi), .file = self.index }; + } + } +} + +pub fn markComdatGroupsDead(self: *Object, elf_file: *Elf) void { + for (self.comdat_groups.items) |cg| { + if (cg.alive) continue; + for (cg.comdatGroupMembers(elf_file)) |shndx| { + const atom_index = self.atoms_indexes.items[shndx]; + if (self.atom(atom_index)) |atom_ptr| { + atom_ptr.alive = false; + atom_ptr.markFdesDead(elf_file); + } + } + } +} + +pub fn initOutputSections(self: *Object, elf_file: *Elf) !void { + for (self.atoms_indexes.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.alive) continue; + const shdr = atom_ptr.inputShdr(elf_file); _ = try self.initOutputSection(elf_file, shdr); } } pub fn addAtomsToOutputSections(self: *Object, elf_file: *Elf) !void { - for (self.atoms.items) |atom_index| { - const atom = elf_file.atom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - const shdr = atom.inputShdr(elf_file); - atom.output_section_index = self.initOutputSection(elf_file, shdr) catch unreachable; + for (self.atoms_indexes.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.alive) continue; + const shdr = atom_ptr.inputShdr(elf_file); + atom_ptr.output_section_index = self.initOutputSection(elf_file, shdr) catch unreachable; const comp = elf_file.base.comp; const gpa = comp.gpa; - const gop = try elf_file.output_sections.getOrPut(gpa, atom.output_section_index); + const gop = try elf_file.output_sections.getOrPut(gpa, atom_ptr.output_section_index); if (!gop.found_existing) gop.value_ptr.* = .{}; - try gop.value_ptr.append(gpa, atom_index); - } - - for (self.locals()) |local_index| { - const local = elf_file.symbol(local_index); - if (local.mergeSubsection(elf_file)) |msub| { - if (!msub.alive) continue; - local.output_section_index = msub.mergeSection(elf_file).output_section_index; - continue; - } - const atom = local.atom(elf_file) orelse continue; - if (!atom.flags.alive) continue; - local.output_section_index = atom.output_section_index; - } - - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - if (global.file(elf_file).?.index() != self.index) continue; - if (global.mergeSubsection(elf_file)) |msub| { - if (!msub.alive) continue; - global.output_section_index = msub.mergeSection(elf_file).output_section_index; - continue; - } - const atom = global.atom(elf_file) orelse continue; - if (!atom.flags.alive) continue; - global.output_section_index = atom.output_section_index; - } - - for (self.symbols.items[self.symtab.items.len..]) |local_index| { - const local = elf_file.symbol(local_index); - const msub = local.mergeSubsection(elf_file).?; - if (!msub.alive) continue; - local.output_section_index = msub.mergeSection(elf_file).output_section_index; + try gop.value_ptr.append(gpa, .{ .index = atom_index, .file = self.index }); } } -pub fn initRelaSections(self: Object, elf_file: *Elf) !void { - for (self.atoms.items) |atom_index| { - const atom = elf_file.atom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - const shndx = atom.relocsShndx() orelse continue; +pub fn initRelaSections(self: *Object, elf_file: *Elf) !void { + for (self.atoms_indexes.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.alive) continue; + const shndx = atom_ptr.relocsShndx() orelse continue; const shdr = self.shdrs.items[shndx]; const out_shndx = try self.initOutputSection(elf_file, shdr); const out_shdr = &elf_file.shdrs.items[out_shndx]; @@ -968,24 +1004,24 @@ pub fn initRelaSections(self: Object, elf_file: *Elf) !void { } } -pub fn addAtomsToRelaSections(self: Object, elf_file: *Elf) !void { - for (self.atoms.items) |atom_index| { - const atom = elf_file.atom(atom_index) orelse continue; - if (!atom.flags.alive) continue; +pub fn addAtomsToRelaSections(self: *Object, elf_file: *Elf) !void { + for (self.atoms_indexes.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.alive) continue; const shndx = blk: { - const shndx = atom.relocsShndx() orelse continue; + const shndx = atom_ptr.relocsShndx() orelse continue; const shdr = self.shdrs.items[shndx]; break :blk self.initOutputSection(elf_file, shdr) catch unreachable; }; const shdr = &elf_file.shdrs.items[shndx]; - shdr.sh_info = atom.outputShndx().?; + shdr.sh_info = atom_ptr.output_section_index; shdr.sh_link = elf_file.symtab_section_index.?; const comp = elf_file.base.comp; const gpa = comp.gpa; - const gop = try elf_file.output_rela_sections.getOrPut(gpa, atom.outputShndx().?); + const gop = try elf_file.output_rela_sections.getOrPut(gpa, atom_ptr.output_section_index); if (!gop.found_existing) gop.value_ptr.* = .{ .shndx = shndx }; - try gop.value_ptr.atom_list.append(gpa, atom_index); + try gop.value_ptr.atom_list.append(gpa, .{ .index = atom_index, .file = self.index }); } } @@ -1041,7 +1077,7 @@ pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void { const isAlive = struct { fn isAlive(sym: *const Symbol, ctx: *Elf) bool { if (sym.mergeSubsection(ctx)) |msub| return msub.alive; - if (sym.atom(ctx)) |atom_ptr| return atom_ptr.flags.alive; + if (sym.atom(ctx)) |atom_ptr| return atom_ptr.alive; return true; } }.isAlive; @@ -1119,11 +1155,10 @@ pub fn globals(self: Object) []const Symbol.Index { /// Returns atom's code and optionally uncompresses data if required (for compressed sections). /// Caller owns the memory. -pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index) ![]u8 { +pub fn codeDecompressAlloc(self: *Object, elf_file: *Elf, atom_index: Atom.Index) ![]u8 { const comp = elf_file.base.comp; const gpa = comp.gpa; - const atom_ptr = elf_file.atom(atom_index).?; - assert(atom_ptr.file_index == self.index); + const atom_ptr = self.atom(atom_index).?; const shdr = atom_ptr.inputShdr(elf_file); const handle = elf_file.fileHandle(self.file_handle); const data = try self.preadShdrContentsAlloc(gpa, handle, atom_ptr.input_section_index); @@ -1184,6 +1219,105 @@ fn preadRelocsAlloc(self: Object, allocator: Allocator, handle: std.fs.File, shn return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num]; } +const AddAtomArgs = struct { + name: u32, + shndx: u32, + size: u64, + alignment: Alignment, +}; + +fn addAtom(self: *Object, allocator: Allocator, args: AddAtomArgs) !Atom.Index { + try self.atoms.ensureUnusedCapacity(allocator, 1); + try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra)); + return self.addAtomAssumeCapacity(args); +} + +fn addAtomAssumeCapacity(self: *Object, args: AddAtomArgs) Atom.Index { + const atom_index: Atom.Index = @intCast(self.atoms.items.len); + const atom_ptr = self.atoms.addOneAssumeCapacity(); + atom_ptr.* = .{ + .atom_index = atom_index, + .name_offset = args.name, + .file_index = self.index, + .input_section_index = args.shndx, + .extra_index = self.addAtomExtraAssumeCapacity(.{}), + .size = args.size, + .alignment = args.alignment, + }; + return atom_index; +} + +pub fn atom(self: *Object, atom_index: Atom.Index) ?*Atom { + if (atom_index == 0) return null; + assert(atom_index < self.atoms.items.len); + return &self.atoms.items[atom_index]; +} + +pub fn addAtomExtra(self: *Object, allocator: Allocator, extra: Atom.Extra) !u32 { + const fields = @typeInfo(Atom.Extra).Struct.fields; + try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len); + return self.addAtomExtraAssumeCapacity(extra); +} + +pub fn addAtomExtraAssumeCapacity(self: *Object, extra: Atom.Extra) u32 { + const index = @as(u32, @intCast(self.atoms_extra.items.len)); + const fields = @typeInfo(Atom.Extra).Struct.fields; + inline for (fields) |field| { + self.atoms_extra.appendAssumeCapacity(switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn atomExtra(self: *Object, index: u32) Atom.Extra { + const fields = @typeInfo(Atom.Extra).Struct.fields; + var i: usize = index; + var result: Atom.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.atoms_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setAtomExtra(self: *Object, index: u32, extra: Atom.Extra) void { + const fields = @typeInfo(Atom.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.atoms_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + +fn addInputMergeSection(self: *Object, allocator: Allocator) !InputMergeSection.Index { + const index: InputMergeSection.Index = @intCast(self.input_merge_sections.items.len); + const msec = try self.input_merge_sections.addOne(allocator); + msec.* = .{}; + return index; +} + +fn inputMergeSection(self: *Object, index: InputMergeSection.Index) ?*InputMergeSection { + if (index == 0) return null; + return &self.input_merge_sections.items[index]; +} + +fn addComdatGroup(self: *Object, allocator: Allocator) !Elf.ComdatGroup.Index { + const index = @as(Elf.ComdatGroup.Index, @intCast(self.comdat_groups.items.len)); + _ = try self.comdat_groups.addOne(allocator); + return index; +} + +pub fn comdatGroup(self: *Object, index: Elf.ComdatGroup.Index) *Elf.ComdatGroup { + assert(index < self.comdat_groups.items.len); + return &self.comdat_groups.items[index]; +} + pub fn format( self: *Object, comptime unused_fmt_string: []const u8, @@ -1247,9 +1381,9 @@ fn formatAtoms( _ = options; const object = ctx.object; try writer.writeAll(" atoms\n"); - for (object.atoms.items) |atom_index| { - const atom = ctx.elf_file.atom(atom_index) orelse continue; - try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)}); + for (object.atoms_indexes.items) |atom_index| { + const atom_ptr = object.atom(atom_index) orelse continue; + try writer.print(" {}\n", .{atom_ptr.fmt(ctx.elf_file)}); } } @@ -1315,16 +1449,15 @@ fn formatComdatGroups( const object = ctx.object; const elf_file = ctx.elf_file; try writer.writeAll(" COMDAT groups\n"); - for (object.comdat_groups.items) |cg_index| { - const cg = elf_file.comdatGroup(cg_index); - const cg_owner = elf_file.comdatGroupOwner(cg.owner); - if (cg_owner.file != object.index) continue; - try writer.print(" COMDAT({d})\n", .{cg_index}); + for (object.comdat_groups.items, 0..) |cg, cg_index| { + try writer.print(" COMDAT({d})", .{cg_index}); + if (!cg.alive) try writer.writeAll(" : [*]"); + try writer.writeByte('\n'); const cg_members = cg.comdatGroupMembers(elf_file); for (cg_members) |shndx| { - const atom_index = object.atoms.items[shndx]; - const atom = elf_file.atom(atom_index) orelse continue; - try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom.name(elf_file) }); + const atom_index = object.atoms_indexes.items[shndx]; + const atom_ptr = object.atom(atom_index) orelse continue; + try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom_ptr.name(elf_file) }); } } } diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig index 2ec5b211e848ae4bd62c2600fa67ecf9bf1db61d..461a56ae39b993f265c7fe548d78e20e1e98baf6 100644 --- a/src/link/Elf/SharedObject.zig +++ b/src/link/Elf/SharedObject.zig @@ -232,7 +232,7 @@ pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) void { const global = elf_file.symbol(index); if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) { global.value = @intCast(this_sym.st_value); - global.atom_index = 0; + global.ref = .{ .index = 0, .file = 0 }; global.esym_index = esym_index; global.version_index = self.versyms.items[esym_index]; global.file_index = self.index; diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 21061f036bdd31bdb0c8a387ad5fa40c2d290317..68d23cb74dbf975ad7aa08022b66284ece737593 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -9,10 +9,9 @@ name_offset: u32 = 0, /// Index of file where this symbol is defined. file_index: File.Index = 0, -/// Index of atom containing this symbol. -/// Index of 0 means there is no associated atom with this symbol. -/// Use `atom` to get the pointer to the atom. -atom_index: Atom.Index = 0, +/// Reference to Atom or merge subsection containing this symbol if any. +/// Use `atom` or `mergeSubsection` to get the pointer to the atom. +ref: Elf.Ref = .{ .index = 0, .file = 0 }, /// Assigned output section index for this symbol. output_section_index: u32 = 0, @@ -34,11 +33,15 @@ pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool { const file_ptr = symbol.file(elf_file).?; if (file_ptr == .shared_object) return symbol.elfSym(elf_file).st_shndx == elf.SHN_ABS; return !symbol.flags.import and symbol.atom(elf_file) == null and - symbol.mergeSubsection(elf_file) == null and symbol.outputShndx() == null and + symbol.mergeSubsection(elf_file) == null and symbol.outputShndx(elf_file) == null and file_ptr != .linker_defined; } -pub fn outputShndx(symbol: Symbol) ?u32 { +pub fn outputShndx(symbol: Symbol, elf_file: *Elf) ?u32 { + if (symbol.mergeSubsection(elf_file)) |msub| + return if (msub.alive) msub.mergeSection(elf_file).output_section_index else null; + if (symbol.atom(elf_file)) |atom_ptr| + return if (atom_ptr.alive) atom_ptr.output_section_index else null; if (symbol.output_section_index == 0) return null; return symbol.output_section_index; } @@ -68,13 +71,15 @@ pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 { } pub fn atom(symbol: Symbol, elf_file: *Elf) ?*Atom { - return elf_file.atom(symbol.atom_index); + if (symbol.flags.merge_subsection) return null; + const file_ptr = elf_file.file(symbol.ref.file) orelse return null; + return file_ptr.atom(symbol.ref.index); } pub fn mergeSubsection(symbol: Symbol, elf_file: *Elf) ?*MergeSubsection { if (!symbol.flags.merge_subsection) return null; - const extras = symbol.extra(elf_file).?; - return elf_file.mergeSubsection(extras.subsection); + const msec = elf_file.mergeSection(symbol.ref.file); + return msec.mergeSubsection(symbol.ref.index); } pub fn file(symbol: Symbol, elf_file: *Elf) ?File { @@ -116,7 +121,7 @@ pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf return symbol.pltAddress(elf_file); } if (symbol.atom(elf_file)) |atom_ptr| { - if (!atom_ptr.flags.alive) { + if (!atom_ptr.alive) { if (mem.eql(u8, atom_ptr.name(elf_file), ".eh_frame")) { const sym_name = symbol.name(elf_file); const sh_addr, const sh_size = blk: { @@ -258,7 +263,6 @@ const AddExtraOpts = struct { gottp: ?u32 = null, tlsdesc: ?u32 = null, zig_got: ?u32 = null, - subsection: ?u32 = null, }; pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) !void { @@ -298,7 +302,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { if (elf_file.base.isRelocatable() and esym.st_shndx == elf.SHN_COMMON) break :blk elf.SHN_COMMON; if (symbol.mergeSubsection(elf_file)) |msub| break :blk @intCast(msub.mergeSection(elf_file).output_section_index); if (symbol.atom(elf_file) == null and file_ptr != .linker_defined) break :blk elf.SHN_ABS; - break :blk @intCast(symbol.outputShndx() orelse elf.SHN_UNDEF); + break :blk @intCast(symbol.outputShndx(elf_file) orelse elf.SHN_UNDEF); }; const st_value = blk: { if (symbol.flags.has_copy_rel) break :blk symbol.address(.{}, elf_file); @@ -382,22 +386,23 @@ fn format2( _ = options; _ = unused_fmt_string; const symbol = ctx.symbol; + const elf_file = ctx.elf_file; try writer.print("%{d} : {s} : @{x}", .{ symbol.esym_index, - symbol.fmtName(ctx.elf_file), - symbol.address(.{}, ctx.elf_file), + symbol.fmtName(elf_file), + symbol.address(.{}, elf_file), }); - if (symbol.file(ctx.elf_file)) |file_ptr| { - if (symbol.isAbs(ctx.elf_file)) { - if (symbol.elfSym(ctx.elf_file).st_shndx == elf.SHN_UNDEF) { + if (symbol.file(elf_file)) |file_ptr| { + if (symbol.isAbs(elf_file)) { + if (symbol.elfSym(elf_file).st_shndx == elf.SHN_UNDEF) { try writer.writeAll(" : undef"); } else { try writer.writeAll(" : absolute"); } - } else if (symbol.outputShndx()) |shndx| { + } else if (symbol.outputShndx(elf_file)) |shndx| { try writer.print(" : shdr({d})", .{shndx}); } - if (symbol.atom(ctx.elf_file)) |atom_ptr| { + if (symbol.atom(elf_file)) |atom_ptr| { try writer.print(" : atom({d})", .{atom_ptr.atom_index}); } var buf: [2]u8 = .{'_'} ** 2; @@ -483,7 +488,7 @@ pub const Extra = struct { gottp: u32 = 0, tlsdesc: u32 = 0, zig_got: u32 = 0, - subsection: u32 = 0, + merge_section: u32 = 0, }; pub const Index = u32; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 8fee0d64df617aa9a05212bbf2e60e77a56fcf9e..454646bfeb089dfd661b655f64443d73aa9dd139 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -15,7 +15,9 @@ local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{}, -atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, +atoms: std.ArrayListUnmanaged(Atom) = .{}, +atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{}, +atoms_extra: std.ArrayListUnmanaged(u32) = .{}, relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{}, num_dynrelocs: u32 = 0, @@ -80,7 +82,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; - try self.atoms.append(gpa, 0); // null input section + try self.atoms.append(gpa, .{ .extra_index = try self.addAtomExtra(gpa, .{}) }); // null input section try self.relocs.append(gpa, .{}); // null relocs section try self.strtab.buffer.append(gpa, 0); @@ -117,6 +119,8 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { self.global_symbols.deinit(allocator); self.globals_lookup.deinit(allocator); self.atoms.deinit(allocator); + self.atoms_indexes.deinit(allocator); + self.atoms_extra.deinit(allocator); for (self.relocs.items) |*list| { list.deinit(allocator); } @@ -276,24 +280,20 @@ pub fn addGlobalEsym(self: *ZigObject, allocator: Allocator) !Symbol.Index { return index | global_symbol_bit; } -pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index { +pub fn newAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index { const gpa = elf_file.base.comp.gpa; - const atom_index = try elf_file.addAtom(); + const atom_index = try self.addAtom(gpa); const symbol_index = try elf_file.addSymbol(); const esym_index = try self.addLocalEsym(gpa); - const shndx = @as(u32, @intCast(self.atoms.items.len)); - try self.atoms.append(gpa, atom_index); + try self.atoms_indexes.append(gpa, atom_index); try self.local_symbols.append(gpa, symbol_index); - const atom_ptr = elf_file.atom(atom_index).?; - atom_ptr.file_index = self.index; - const symbol_ptr = elf_file.symbol(symbol_index); symbol_ptr.file_index = self.index; - symbol_ptr.atom_index = atom_index; + symbol_ptr.ref = .{ .index = atom_index, .file = self.index }; - self.local_esyms.items(.shndx)[esym_index] = shndx; + self.local_esyms.items(.shndx)[esym_index] = atom_index; self.local_esyms.items(.elf_sym)[esym_index].st_shndx = SHN_ATOM; symbol_ptr.esym_index = esym_index; @@ -301,21 +301,22 @@ pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index { const relocs_index = @as(u32, @intCast(self.relocs.items.len)); const relocs = try self.relocs.addOne(gpa); relocs.* = .{}; + + const atom_ptr = self.atom(atom_index).?; atom_ptr.relocs_section_index = relocs_index; return symbol_index; } /// TODO actually create fake input shdrs and return that instead. -pub fn inputShdr(self: ZigObject, atom_index: Atom.Index, elf_file: *Elf) elf.Elf64_Shdr { - _ = self; - const atom = elf_file.atom(atom_index) orelse return Elf.null_shdr; - const shndx = atom.outputShndx() orelse return Elf.null_shdr; +pub fn inputShdr(self: *ZigObject, atom_index: Atom.Index, elf_file: *Elf) elf.Elf64_Shdr { + const atom_ptr = self.atom(atom_index) orelse return Elf.null_shdr; + const shndx = atom_ptr.output_section_index; var shdr = elf_file.shdrs.items[shndx]; shdr.sh_addr = 0; shdr.sh_offset = 0; - shdr.sh_size = atom.size; - shdr.sh_addralign = atom.alignment.toByteUnits() orelse 1; + shdr.sh_size = atom_ptr.size; + shdr.sh_addralign = atom_ptr.alignment.toByteUnits() orelse 1; return shdr; } @@ -329,27 +330,21 @@ pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) void { if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) { assert(esym.st_shndx == SHN_ATOM); - const atom_index = self.atoms.items[shndx]; - const atom = elf_file.atom(atom_index) orelse continue; - if (!atom.flags.alive) continue; + const atom_ptr = self.atom(shndx) orelse continue; + if (!atom_ptr.alive) continue; } const global = elf_file.symbol(index); if (self.asFile().symbolRank(esym, false) < global.symbolRank(elf_file)) { const atom_index = switch (esym.st_shndx) { elf.SHN_ABS, elf.SHN_COMMON => 0, - SHN_ATOM => self.atoms.items[shndx], + SHN_ATOM => shndx, else => unreachable, }; - const output_section_index = if (elf_file.atom(atom_index)) |atom| - atom.outputShndx().? - else - elf.SHN_UNDEF; global.value = @intCast(esym.st_value); - global.atom_index = atom_index; + global.ref = .{ .index = atom_index, .file = self.index }; global.esym_index = esym_index; global.file_index = self.index; - global.output_section_index = output_section_index; global.version_index = elf_file.default_sym_version; if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true; } @@ -376,7 +371,7 @@ pub fn claimUnresolved(self: ZigObject, elf_file: *Elf) void { }; global.value = 0; - global.atom_index = 0; + global.ref = .{ .index = 0, .file = 0 }; global.esym_index = esym_index; global.file_index = self.index; global.version_index = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version; @@ -397,7 +392,7 @@ pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void { } global.value = 0; - global.atom_index = 0; + global.ref = .{ .index = 0, .file = 0 }; global.esym_index = esym_index; global.file_index = self.index; } @@ -405,19 +400,19 @@ pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void { pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void { const gpa = elf_file.base.comp.gpa; - for (self.atoms.items) |atom_index| { - const atom = elf_file.atom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - const shdr = atom.inputShdr(elf_file); + for (self.atoms_indexes.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.alive) continue; + const shdr = atom_ptr.inputShdr(elf_file); if (shdr.sh_type == elf.SHT_NOBITS) continue; - if (atom.scanRelocsRequiresCode(elf_file)) { + if (atom_ptr.scanRelocsRequiresCode(elf_file)) { // TODO ideally we don't have to fetch the code here. // Perhaps it would make sense to save the code until flushModule where we // would free all of generated code? const code = try self.codeAlloc(elf_file, atom_index); defer gpa.free(code); - try atom.scanRelocs(elf_file, code, undefs); - } else try atom.scanRelocs(elf_file, null, undefs); + try atom_ptr.scanRelocs(elf_file, code, undefs); + } else try atom_ptr.scanRelocs(elf_file, null, undefs); } } @@ -450,9 +445,8 @@ pub fn checkDuplicates(self: *ZigObject, dupes: anytype, elf_file: *Elf) error{O esym.st_shndx == elf.SHN_COMMON) continue; if (esym.st_shndx == SHN_ATOM) { - const atom_index = self.atoms.items[shndx]; - const atom = elf_file.atom(atom_index) orelse continue; - if (!atom.flags.alive) continue; + const atom_ptr = self.atom(shndx) orelse continue; + if (!atom_ptr.alive) continue; } const gop = try dupes.getOrPut(index); @@ -493,7 +487,7 @@ pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: * const global = elf_file.symbol(global_index); const file_ptr = global.file(elf_file).?; assert(file_ptr.index() == self.index); - if (global.outputShndx() == null) continue; + if (global.outputShndx(elf_file) == null) continue; const off = try ar_symtab.strtab.insert(gpa, global.name(elf_file)); ar_symtab.symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index }); @@ -517,20 +511,20 @@ pub fn writeAr(self: ZigObject, writer: anytype) !void { try writer.writeAll(self.data.items); } -pub fn addAtomsToRelaSections(self: ZigObject, elf_file: *Elf) !void { - for (self.atoms.items) |atom_index| { - const atom = elf_file.atom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - const rela_shndx = atom.relocsShndx() orelse continue; +pub fn addAtomsToRelaSections(self: *ZigObject, elf_file: *Elf) !void { + for (self.atoms_indexes.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.alive) continue; + const rela_shndx = atom_ptr.relocsShndx() orelse continue; // TODO this check will become obsolete when we rework our relocs mechanism at the ZigObject level if (self.relocs.items[rela_shndx].items.len == 0) continue; - const out_shndx = atom.outputShndx().?; + const out_shndx = atom_ptr.output_section_index; const out_shdr = elf_file.shdrs.items[out_shndx]; if (out_shdr.sh_type == elf.SHT_NOBITS) continue; const gpa = elf_file.base.comp.gpa; const sec = elf_file.output_rela_sections.getPtr(out_shndx).?; - try sec.atom_list.append(gpa, atom_index); + try sec.atom_list.append(gpa, .{ .index = atom_index, .file = self.index }); } } @@ -561,7 +555,7 @@ pub fn globals(self: ZigObject) []const Symbol.Index { pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void { for (self.locals()) |local_index| { const local = elf_file.symbol(local_index); - if (local.atom(elf_file)) |atom| if (!atom.flags.alive) continue; + if (local.atom(elf_file)) |atom_ptr| if (!atom_ptr.alive) continue; const esym = local.elfSym(elf_file); switch (esym.st_type()) { elf.STT_SECTION, elf.STT_NOTYPE => continue, @@ -577,7 +571,7 @@ pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void { const global = elf_file.symbol(global_index); const file_ptr = global.file(elf_file) orelse continue; if (file_ptr.index() != self.index) continue; - if (global.atom(elf_file)) |atom| if (!atom.flags.alive) continue; + if (global.atom(elf_file)) |atom_ptr| if (!atom_ptr.alive) continue; global.flags.output_symtab = true; if (global.isLocal(elf_file)) { try global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file); @@ -621,11 +615,10 @@ pub fn asFile(self: *ZigObject) File { /// Returns atom's code. /// Caller owns the memory. -pub fn codeAlloc(self: ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8 { +pub fn codeAlloc(self: *ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8 { const gpa = elf_file.base.comp.gpa; - const atom = elf_file.atom(atom_index).?; - assert(atom.file_index == self.index); - const shdr = &elf_file.shdrs.items[atom.outputShndx().?]; + const atom_ptr = self.atom(atom_index).?; + const shdr = &elf_file.shdrs.items[atom_ptr.output_section_index]; if (shdr.sh_flags & elf.SHF_TLS != 0) { const tlv = self.tls_variables.get(atom_index).?; @@ -633,13 +626,13 @@ pub fn codeAlloc(self: ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8 return code; } - const file_offset = shdr.sh_offset + @as(u64, @intCast(atom.value)); - const size = std.math.cast(usize, atom.size) orelse return error.Overflow; + const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value)); + const size = std.math.cast(usize, atom_ptr.size) orelse return error.Overflow; const code = try gpa.alloc(u8, size); errdefer gpa.free(code); const amt = try elf_file.base.file.?.preadAll(code, file_offset); if (amt != code.len) { - log.err("fetching code for {s} failed", .{atom.name(elf_file)}); + log.err("fetching code for {s} failed", .{atom_ptr.name(elf_file)}); return error.InputOutput; } return code; @@ -760,7 +753,7 @@ pub fn getOrCreateMetadataForLazySymbol( }; switch (metadata.state.*) { .unused => { - const symbol_index = try self.addAtom(elf_file); + const symbol_index = try self.newAtom(elf_file); const sym = elf_file.symbol(symbol_index); sym.flags.needs_zig_got = true; metadata.symbol_index.* = symbol_index; @@ -824,7 +817,7 @@ pub fn getOrCreateMetadataForDecl( const gop = try self.decls.getOrPut(gpa, decl_index); if (!gop.found_existing) { const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded; - const symbol_index = try self.addAtom(elf_file); + const symbol_index = try self.newAtom(elf_file); const mod = elf_file.base.comp.module.?; const decl = mod.declPtr(decl_index); const sym = elf_file.symbol(symbol_index); @@ -861,14 +854,14 @@ fn getDeclShdrIndex( if (is_all_zeroes) break :blk elf_file.sectionByName(".tbss") orelse try elf_file.addSection(.{ .type = elf.SHT_NOBITS, .flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS, - .name = ".tbss", + .name = try elf_file.insertShString(".tbss"), .offset = std.math.maxInt(u64), }); break :blk elf_file.sectionByName(".tdata") orelse try elf_file.addSection(.{ .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS, - .name = ".tdata", + .name = try elf_file.insertShString(".tdata"), .offset = std.math.maxInt(u64), }); } @@ -919,14 +912,13 @@ fn updateDeclCode( const sym = elf_file.symbol(sym_index); const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index]; const atom_ptr = sym.atom(elf_file).?; + const name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip)); - sym.output_section_index = shdr_index; + atom_ptr.alive = true; + atom_ptr.name_offset = name_offset; atom_ptr.output_section_index = shdr_index; - - sym.name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip)); - atom_ptr.flags.alive = true; - atom_ptr.name_offset = sym.name_offset; - esym.st_name = sym.name_offset; + sym.name_offset = name_offset; + esym.st_name = name_offset; esym.st_info |= stt_bits; esym.st_size = code.len; @@ -1018,16 +1010,17 @@ fn updateTlv( const sym = elf_file.symbol(sym_index); const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index]; const atom_ptr = sym.atom(elf_file).?; + const name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip)); sym.value = 0; - sym.output_section_index = shndx; + sym.name_offset = name_offset; + atom_ptr.output_section_index = shndx; + atom_ptr.alive = true; + atom_ptr.name_offset = name_offset; - sym.name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip)); - atom_ptr.flags.alive = true; - atom_ptr.name_offset = sym.name_offset; esym.st_value = 0; - esym.st_name = sym.name_offset; + esym.st_name = name_offset; esym.st_info = elf.STT_TLS; esym.st_size = code.len; @@ -1048,7 +1041,7 @@ fn updateTlv( { const gop = try elf_file.output_sections.getOrPut(gpa, atom_ptr.output_section_index); if (!gop.found_existing) gop.value_ptr.* = .{}; - try gop.value_ptr.append(gpa, atom_ptr.atom_index); + try gop.value_ptr.append(gpa, .{ .index = atom_ptr.atom_index, .file = self.index }); } } @@ -1242,13 +1235,12 @@ fn updateLazySymbol( }; const local_sym = elf_file.symbol(symbol_index); local_sym.name_offset = name_str_index; - local_sym.output_section_index = output_section_index; const local_esym = &self.local_esyms.items(.elf_sym)[local_sym.esym_index]; 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(elf_file).?; - atom_ptr.flags.alive = true; + atom_ptr.alive = true; atom_ptr.name_offset = name_str_index; atom_ptr.alignment = required_alignment; atom_ptr.size = code.len; @@ -1307,8 +1299,7 @@ pub fn lowerUnnamedConst( return error.CodegenFail; }, }; - const sym = elf_file.symbol(sym_index); - try unnamed_consts.append(gpa, sym.atom_index); + try unnamed_consts.append(gpa, sym_index); return sym_index; } @@ -1332,7 +1323,7 @@ fn lowerConst( var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); - const sym_index = try self.addAtom(elf_file); + const sym_index = try self.newAtom(elf_file); const res = try codegen.generateSymbol( &elf_file.base, @@ -1351,13 +1342,12 @@ fn lowerConst( const local_sym = elf_file.symbol(sym_index); const name_str_index = try self.strtab.insert(gpa, name); local_sym.name_offset = name_str_index; - local_sym.output_section_index = output_section_index; const local_esym = &self.local_esyms.items(.elf_sym)[local_sym.esym_index]; 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(elf_file).?; - atom_ptr.flags.alive = true; + atom_ptr.alive = true; atom_ptr.name_offset = name_str_index; atom_ptr.alignment = required_alignment; atom_ptr.size = code.len; @@ -1530,6 +1520,72 @@ pub fn getString(self: ZigObject, off: u32) [:0]const u8 { return self.strtab.getAssumeExists(off); } +fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index { + try self.atoms.ensureUnusedCapacity(allocator, 1); + try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra)); + return self.addAtomAssumeCapacity(); +} + +fn addAtomAssumeCapacity(self: *ZigObject) Atom.Index { + const atom_index: Atom.Index = @intCast(self.atoms.items.len); + const atom_ptr = self.atoms.addOneAssumeCapacity(); + atom_ptr.* = .{ + .file_index = self.index, + .atom_index = atom_index, + .extra_index = self.addAtomExtraAssumeCapacity(.{}), + }; + return atom_index; +} + +pub fn atom(self: *ZigObject, atom_index: Atom.Index) ?*Atom { + if (atom_index == 0) return null; + assert(atom_index < self.atoms.items.len); + return &self.atoms.items[atom_index]; +} + +fn addAtomExtra(self: *ZigObject, allocator: Allocator, extra: Atom.Extra) !u32 { + const fields = @typeInfo(Atom.Extra).Struct.fields; + try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len); + return self.addAtomExtraAssumeCapacity(extra); +} + +fn addAtomExtraAssumeCapacity(self: *ZigObject, extra: Atom.Extra) u32 { + const index = @as(u32, @intCast(self.atoms_extra.items.len)); + const fields = @typeInfo(Atom.Extra).Struct.fields; + inline for (fields) |field| { + self.atoms_extra.appendAssumeCapacity(switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn atomExtra(self: ZigObject, index: u32) Atom.Extra { + const fields = @typeInfo(Atom.Extra).Struct.fields; + var i: usize = index; + var result: Atom.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.atoms_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setAtomExtra(self: *ZigObject, index: u32, extra: Atom.Extra) void { + assert(index > 0); + const fields = @typeInfo(Atom.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.atoms_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { return .{ .data = .{ .self = self, @@ -1578,9 +1634,9 @@ fn formatAtoms( _ = unused_fmt_string; _ = options; try writer.writeAll(" atoms\n"); - for (ctx.self.atoms.items) |atom_index| { - const atom = ctx.elf_file.atom(atom_index) orelse continue; - try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)}); + for (ctx.self.atoms_indexes.items) |atom_index| { + const atom_ptr = ctx.self.atom(atom_index) orelse continue; + try writer.print(" {}\n", .{atom_ptr.fmt(ctx.elf_file)}); } } diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index 7f227a06e37992f211e276ad6df43d4f8d2a4008..1ca1b909065410ef794084b763e4c8f05e350d77 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -42,8 +42,8 @@ pub const Fde = struct { const object = elf_file.file(fde.file_index).?.object; const rel = fde.relocs(elf_file)[0]; const sym = object.symtab.items[rel.r_sym()]; - const atom_index = object.atoms.items[sym.st_shndx]; - return elf_file.atom(atom_index).?; + const atom_index = object.atoms_indexes.items[sym.st_shndx]; + return object.atom(atom_index).?; } pub fn relocs(fde: Fde, elf_file: *Elf) []align(1) const elf.Elf64_Rela { @@ -421,7 +421,7 @@ fn emitReloc(elf_file: *Elf, rec: anytype, sym: *const Symbol, rel: elf.Elf64_Re switch (sym.type(elf_file)) { elf.STT_SECTION => { r_addend += @intCast(sym.address(.{}, elf_file)); - r_sym = elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx().?); + r_sym = elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx(elf_file).?); }, else => { r_sym = sym.outputSymtabIndex(elf_file) orelse 0; diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index cf1f0db2e73e23cb7db98ca57fd34b36e8d832d8..9d2560e11bbb73345e1b9ba7f46bd4290ab787af 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -98,11 +98,34 @@ pub const File = union(enum) { } } + pub fn atom(file: File, atom_index: Atom.Index) ?*Atom { + return switch (file) { + .shared_object => unreachable, + .linker_defined => null, + inline else => |x| x.atom(atom_index), + }; + } + pub fn atoms(file: File) []const Atom.Index { return switch (file) { - .linker_defined, .shared_object => &[0]Atom.Index{}, - .zig_object => |x| x.atoms.items, - .object => |x| x.atoms.items, + .shared_object => unreachable, + .linker_defined => &[0]Atom.Index{}, + .zig_object => |x| x.atoms_indexes.items, + .object => |x| x.atoms_indexes.items, + }; + } + + pub fn atomExtra(file: File, extra_index: u32) Atom.Extra { + return switch (file) { + .shared_object, .linker_defined => unreachable, + inline else => |x| x.atomExtra(extra_index), + }; + } + + pub fn setAtomExtra(file: File, extra_index: u32, extra: Atom.Extra) void { + return switch (file) { + .shared_object, .linker_defined => unreachable, + inline else => |x| x.setAtomExtra(extra_index, extra), }; } @@ -114,6 +137,13 @@ pub const File = union(enum) { }; } + pub fn comdatGroup(file: File, ind: Elf.ComdatGroup.Index) *Elf.ComdatGroup { + return switch (file) { + .linker_defined, .shared_object, .zig_object => unreachable, + .object => |x| x.comdatGroup(ind), + }; + } + pub fn symbol(file: File, ind: Symbol.Index) Symbol.Index { return switch (file) { .zig_object => |x| x.symbol(ind), @@ -134,6 +164,12 @@ pub const File = union(enum) { }; } + pub fn getString(file: File, off: u32) [:0]const u8 { + return switch (file) { + inline else => |x| x.getString(off), + }; + } + pub fn updateSymtabSize(file: File, elf_file: *Elf) !void { return switch (file) { inline else => |x| x.updateSymtabSize(elf_file), diff --git a/src/link/Elf/gc.zig b/src/link/Elf/gc.zig index 49b5664decb2c7b045790849570334c3208fe502..de2d43f738da9ea700ce0ee952a0ed69432f8386 100644 --- a/src/link/Elf/gc.zig +++ b/src/link/Elf/gc.zig @@ -16,9 +16,11 @@ pub fn gcAtoms(elf_file: *Elf) !void { } fn collectRoots(roots: *std.ArrayList(*Atom), files: []const File.Index, elf_file: *Elf) !void { - if (elf_file.entry_index) |index| { - const global = elf_file.symbol(index); - try markSymbol(global, roots, elf_file); + if (elf_file.linkerDefinedPtr()) |obj| { + if (obj.entry_index) |index| { + const global = elf_file.symbol(index); + try markSymbol(global, roots, elf_file); + } } for (files) |index| { @@ -35,8 +37,8 @@ fn collectRoots(roots: *std.ArrayList(*Atom), files: []const File.Index, elf_fil const file = elf_file.file(index).?; for (file.atoms()) |atom_index| { - const atom = elf_file.atom(atom_index) orelse continue; - if (!atom.flags.alive) continue; + const atom = file.atom(atom_index) orelse continue; + if (!atom.alive) continue; const shdr = atom.inputShdr(elf_file); const name = atom.name(elf_file); @@ -54,7 +56,7 @@ fn collectRoots(roots: *std.ArrayList(*Atom), files: []const File.Index, elf_fil break :blk false; }; if (is_gc_root and markAtom(atom)) try roots.append(atom); - if (shdr.sh_flags & elf.SHF_ALLOC == 0) atom.flags.visited = true; + if (shdr.sh_flags & elf.SHF_ALLOC == 0) atom.visited = true; } // Mark every atom referenced by CIE as alive. @@ -77,22 +79,22 @@ fn markSymbol(sym: *Symbol, roots: *std.ArrayList(*Atom), elf_file: *Elf) !void } fn markAtom(atom: *Atom) bool { - const already_visited = atom.flags.visited; - atom.flags.visited = true; - return atom.flags.alive and !already_visited; + const already_visited = atom.visited; + atom.visited = true; + return atom.alive and !already_visited; } fn markLive(atom: *Atom, elf_file: *Elf) void { if (@import("build_options").enable_logging) track_live_level.incr(); - assert(atom.flags.visited); + assert(atom.visited); const file = atom.file(elf_file).?; for (atom.fdes(elf_file)) |fde| { for (fde.relocs(elf_file)[1..]) |rel| { const target_sym = elf_file.symbol(file.symbol(rel.r_sym())); const target_atom = target_sym.atom(elf_file) orelse continue; - target_atom.flags.alive = true; + target_atom.alive = true; gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index }); if (markAtom(target_atom)) markLive(target_atom, elf_file); } @@ -105,7 +107,7 @@ fn markLive(atom: *Atom, elf_file: *Elf) void { continue; } const target_atom = target_sym.atom(elf_file) orelse continue; - target_atom.flags.alive = true; + target_atom.alive = true; gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index }); if (markAtom(target_atom)) markLive(target_atom, elf_file); } @@ -120,10 +122,11 @@ fn mark(roots: std.ArrayList(*Atom), elf_file: *Elf) void { fn prune(files: []const File.Index, elf_file: *Elf) void { for (files) |index| { - for (elf_file.file(index).?.atoms()) |atom_index| { - const atom = elf_file.atom(atom_index) orelse continue; - if (atom.flags.alive and !atom.flags.visited) { - atom.flags.alive = false; + const file = elf_file.file(index).?; + for (file.atoms()) |atom_index| { + const atom = file.atom(atom_index) orelse continue; + if (atom.alive and !atom.visited) { + atom.alive = false; atom.markFdesDead(elf_file); } } @@ -133,9 +136,10 @@ fn prune(files: []const File.Index, elf_file: *Elf) void { pub fn dumpPrunedAtoms(elf_file: *Elf) !void { const stderr = std.io.getStdErr().writer(); for (elf_file.objects.items) |index| { - for (elf_file.file(index).?.object.atoms.items) |atom_index| { - const atom = elf_file.atom(atom_index) orelse continue; - if (!atom.flags.alive) + const file = elf_file.file(index).?; + for (file.atoms()) |atom_index| { + const atom = file.atom(atom_index) orelse continue; + if (!atom.alive) // TODO should we simply print to stderr? try stderr.print("link: removing unused section '{s}' in file '{}'\n", .{ atom.name(elf_file), diff --git a/src/link/Elf/merge_section.zig b/src/link/Elf/merge_section.zig index ab716794fcbbd3335ef0290bc32c1e07f9b01c71..baec316c3d9f3e2694e790fb32a1a867a7644c1a 100644 --- a/src/link/Elf/merge_section.zig +++ b/src/link/Elf/merge_section.zig @@ -10,16 +10,18 @@ pub const MergeSection = struct { IndexContext, std.hash_map.default_max_load_percentage, ) = .{}, - subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .{}, + subsections: std.ArrayListUnmanaged(MergeSubsection) = .{}, + finalized_subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .{}, pub fn deinit(msec: *MergeSection, allocator: Allocator) void { msec.bytes.deinit(allocator); msec.table.deinit(allocator); msec.subsections.deinit(allocator); + msec.finalized_subsections.deinit(allocator); } pub fn name(msec: MergeSection, elf_file: *Elf) [:0]const u8 { - return elf_file.strings.getAssumeExists(msec.name_offset); + return elf_file.getShString(msec.name_offset); } pub fn address(msec: MergeSection, elf_file: *Elf) i64 { @@ -58,25 +60,26 @@ pub const MergeSection = struct { /// Finalizes the merge section and clears hash table. /// Sorts all owned subsections. - pub fn finalize(msec: *MergeSection, elf_file: *Elf) !void { - const gpa = elf_file.base.comp.gpa; - try msec.subsections.ensureTotalCapacityPrecise(gpa, msec.table.count()); + pub fn finalize(msec: *MergeSection, allocator: Allocator) !void { + try msec.finalized_subsections.ensureTotalCapacityPrecise(allocator, msec.subsections.items.len); var it = msec.table.iterator(); while (it.next()) |entry| { - const msub = elf_file.mergeSubsection(entry.value_ptr.*); + const msub = msec.mergeSubsection(entry.value_ptr.*); if (!msub.alive) continue; - msec.subsections.appendAssumeCapacity(entry.value_ptr.*); + msec.finalized_subsections.appendAssumeCapacity(entry.value_ptr.*); } - msec.table.clearAndFree(gpa); + msec.table.clearAndFree(allocator); const sortFn = struct { - pub fn sortFn(ctx: *Elf, lhs: MergeSubsection.Index, rhs: MergeSubsection.Index) bool { + pub fn sortFn(ctx: *MergeSection, lhs: MergeSubsection.Index, rhs: MergeSubsection.Index) bool { const lhs_msub = ctx.mergeSubsection(lhs); const rhs_msub = ctx.mergeSubsection(rhs); if (lhs_msub.alignment.compareStrict(.eq, rhs_msub.alignment)) { if (lhs_msub.size == rhs_msub.size) { - return mem.order(u8, lhs_msub.getString(ctx), rhs_msub.getString(ctx)) == .lt; + const lhs_string = ctx.bytes.items[lhs_msub.string_index..][0..lhs_msub.size]; + const rhs_string = ctx.bytes.items[rhs_msub.string_index..][0..rhs_msub.size]; + return mem.order(u8, lhs_string, rhs_string) == .lt; } return lhs_msub.size < rhs_msub.size; } @@ -84,7 +87,19 @@ pub const MergeSection = struct { } }.sortFn; - std.mem.sort(MergeSubsection.Index, msec.subsections.items, elf_file, sortFn); + std.mem.sort(MergeSubsection.Index, msec.finalized_subsections.items, msec, sortFn); + } + + pub fn addMergeSubsection(msec: *MergeSection, allocator: Allocator) !MergeSubsection.Index { + const index: MergeSubsection.Index = @intCast(msec.subsections.items.len); + const msub = try msec.subsections.addOne(allocator); + msub.* = .{}; + return index; + } + + pub fn mergeSubsection(msec: *MergeSection, index: MergeSubsection.Index) *MergeSubsection { + assert(index < msec.subsections.items.len); + return &msec.subsections.items[index]; } pub const IndexContext = struct { @@ -154,8 +169,8 @@ pub const MergeSection = struct { msec.type, msec.flags, }); - for (msec.subsections.items) |index| { - try writer.print(" {}\n", .{elf_file.mergeSubsection(index).fmt(elf_file)}); + for (msec.subsections.items) |msub| { + try writer.print(" {}\n", .{msub.fmt(elf_file)}); } } @@ -250,18 +265,26 @@ pub const InputMergeSection = struct { // TODO: imsec.strings.clearAndFree(allocator); } - pub fn findSubsection(imsec: InputMergeSection, offset: u32) ?struct { MergeSubsection.Index, u32 } { + const FindSubsectionResult = struct { + msub_index: MergeSubsection.Index, + offset: u32, + }; + + pub fn findSubsection(imsec: InputMergeSection, offset: u32) ?FindSubsectionResult { // TODO: binary search for (imsec.offsets.items, 0..) |off, index| { if (offset < off) return .{ - imsec.subsections.items[index - 1], - offset - imsec.offsets.items[index - 1], + .msub_index = imsec.subsections.items[index - 1], + .offset = offset - imsec.offsets.items[index - 1], }; } const last = imsec.offsets.items.len - 1; const last_off = imsec.offsets.items[last]; const last_len = imsec.strings.items[last].len; - if (offset < last_off + last_len) return .{ imsec.subsections.items[last], offset - last_off }; + if (offset < last_off + last_len) return .{ + .msub_index = imsec.subsections.items[last], + .offset = offset - last_off, + }; return null; } diff --git a/src/link/Elf/relocatable.zig b/src/link/Elf/relocatable.zig index 1bbb320c98c0a4272c162234ca4b9b45f6719ec5..ce65b8eb72789dd367b200cec2a278f0a26f17b5 100644 --- a/src/link/Elf/relocatable.zig +++ b/src/link/Elf/relocatable.zig @@ -190,7 +190,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const // Now, we are ready to resolve the symbols across all input files. // We will first resolve the files in the ZigObject, next in the parsed // input Object files. - elf_file.resolveSymbols(); + try elf_file.resolveSymbols(); elf_file.markEhFrameAtomsDead(); try elf_file.resolveMergeSections(); try elf_file.addCommentString(); @@ -299,13 +299,16 @@ fn initSections(elf_file: *Elf) !void { } else false; if (needs_eh_frame) { elf_file.eh_frame_section_index = try elf_file.addSection(.{ - .name = ".eh_frame", + .name = try elf_file.insertShString(".eh_frame"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC, .addralign = ptr_size, .offset = std.math.maxInt(u64), }); - elf_file.eh_frame_rela_section_index = try elf_file.addRelaShdr(".rela.eh_frame", elf_file.eh_frame_section_index.?); + elf_file.eh_frame_rela_section_index = try elf_file.addRelaShdr( + try elf_file.insertShString(".rela.eh_frame"), + elf_file.eh_frame_section_index.?, + ); } try initComdatGroups(elf_file); @@ -318,22 +321,18 @@ fn initComdatGroups(elf_file: *Elf) !void { for (elf_file.objects.items) |index| { const object = elf_file.file(index).?.object; - - for (object.comdat_groups.items) |cg_index| { - const cg = elf_file.comdatGroup(cg_index); - const cg_owner = elf_file.comdatGroupOwner(cg.owner); - if (cg_owner.file != index) continue; - + for (object.comdat_groups.items, 0..) |cg, cg_index| { + if (!cg.alive) continue; const cg_sec = try elf_file.comdat_group_sections.addOne(gpa); cg_sec.* = .{ .shndx = try elf_file.addSection(.{ - .name = ".group", + .name = try elf_file.insertShString(".group"), .type = elf.SHT_GROUP, .entsize = @sizeOf(u32), .addralign = @alignOf(u32), .offset = std.math.maxInt(u64), }), - .cg_index = cg_index, + .cg_ref = .{ .index = @intCast(cg_index), .file = index }, }; } } @@ -342,9 +341,9 @@ fn initComdatGroups(elf_file: *Elf) !void { fn updateSectionSizes(elf_file: *Elf) !void { for (elf_file.output_sections.keys(), elf_file.output_sections.values()) |shndx, atom_list| { const shdr = &elf_file.shdrs.items[shndx]; - for (atom_list.items) |atom_index| { - const atom_ptr = elf_file.atom(atom_index) orelse continue; - if (!atom_ptr.flags.alive) continue; + for (atom_list.items) |ref| { + const atom_ptr = elf_file.atom(ref) orelse continue; + if (!atom_ptr.alive) continue; const offset = atom_ptr.alignment.forward(shdr.sh_size); const padding = offset - shdr.sh_size; atom_ptr.value = @intCast(offset); @@ -355,9 +354,9 @@ fn updateSectionSizes(elf_file: *Elf) !void { for (elf_file.output_rela_sections.values()) |sec| { const shdr = &elf_file.shdrs.items[sec.shndx]; - for (sec.atom_list.items) |atom_index| { - const atom_ptr = elf_file.atom(atom_index) orelse continue; - if (!atom_ptr.flags.alive) continue; + for (sec.atom_list.items) |ref| { + const atom_ptr = elf_file.atom(ref) orelse continue; + if (!atom_ptr.alive) continue; const relocs = atom_ptr.relocs(elf_file); shdr.sh_size += shdr.sh_entsize * relocs.len; } @@ -386,7 +385,7 @@ fn updateComdatGroupsSizes(elf_file: *Elf) void { const sym = elf_file.symbol(cg.symbol(elf_file)); shdr.sh_info = sym.outputSymtabIndex(elf_file) orelse - elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx().?); + elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx(elf_file).?); } } @@ -449,16 +448,16 @@ fn writeAtoms(elf_file: *Elf) !void { 0; @memset(buffer, padding_byte); - for (atom_list.items) |atom_index| { - const atom_ptr = elf_file.atom(atom_index).?; - assert(atom_ptr.flags.alive); + for (atom_list.items) |ref| { + const atom_ptr = elf_file.atom(ref).?; + assert(atom_ptr.alive); const offset = math.cast(usize, atom_ptr.value - @as(i64, @intCast(shdr.sh_addr - base_offset))) orelse return error.Overflow; const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow; - log.debug("writing atom({d}) from 0x{x} to 0x{x}", .{ - atom_index, + log.debug("writing atom({}) from 0x{x} to 0x{x}", .{ + ref, sh_offset + offset, sh_offset + offset + size, }); @@ -466,8 +465,8 @@ fn writeAtoms(elf_file: *Elf) !void { // TODO decompress directly into provided buffer const out_code = buffer[offset..][0..size]; const in_code = switch (atom_ptr.file(elf_file).?) { - .object => |x| try x.codeDecompressAlloc(elf_file, atom_index), - .zig_object => |x| try x.codeAlloc(elf_file, atom_index), + .object => |x| try x.codeDecompressAlloc(elf_file, ref.index), + .zig_object => |x| try x.codeAlloc(elf_file, ref.index), else => unreachable, }; defer gpa.free(in_code); @@ -491,9 +490,9 @@ fn writeSyntheticSections(elf_file: *Elf) !void { var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs); defer relocs.deinit(); - for (sec.atom_list.items) |atom_index| { - const atom_ptr = elf_file.atom(atom_index) orelse continue; - if (!atom_ptr.flags.alive) continue; + for (sec.atom_list.items) |ref| { + const atom_ptr = elf_file.atom(ref) orelse continue; + if (!atom_ptr.alive) continue; try atom_ptr.writeRelocs(elf_file, &relocs); } assert(relocs.items.len == num_relocs); diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index 5a96650bc508921530abcfe028f7b162a16256d6..c5c23598906c36feb2d806d41481572895bfcad9 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -1075,7 +1075,7 @@ pub const GotPltSection = struct { _ = got_plt; { // [0]: _DYNAMIC - const symbol = elf_file.symbol(elf_file.dynamic_index.?); + const symbol = elf_file.symbol(elf_file.linkerDefinedPtr().?.dynamic_index.?); try writer.writeInt(u64, @intCast(symbol.address(.{}, elf_file)), .little); } // [1]: 0x0 @@ -1670,45 +1670,44 @@ pub const VerneedSection = struct { pub const ComdatGroupSection = struct { shndx: u32, - cg_index: u32, + cg_ref: Elf.Ref, - fn file(cgs: ComdatGroupSection, elf_file: *Elf) ?File { - const cg = elf_file.comdatGroup(cgs.cg_index); - const cg_owner = elf_file.comdatGroupOwner(cg.owner); - return elf_file.file(cg_owner.file); + fn comdatGroup(cgs: ComdatGroupSection, elf_file: *Elf) *Elf.ComdatGroup { + const cg_file = elf_file.file(cgs.cg_ref.file).?; + return cg_file.object.comdatGroup(cgs.cg_ref.index); } pub fn symbol(cgs: ComdatGroupSection, elf_file: *Elf) Symbol.Index { - const cg = elf_file.comdatGroup(cgs.cg_index); - const object = cgs.file(elf_file).?.object; + const cg = cgs.comdatGroup(elf_file); + const object = cg.file(elf_file).object; const shdr = object.shdrs.items[cg.shndx]; return object.symbols.items[shdr.sh_info]; } pub fn size(cgs: ComdatGroupSection, elf_file: *Elf) usize { - const cg = elf_file.comdatGroup(cgs.cg_index); + const cg = cgs.comdatGroup(elf_file); const members = cg.comdatGroupMembers(elf_file); return (members.len + 1) * @sizeOf(u32); } pub fn write(cgs: ComdatGroupSection, elf_file: *Elf, writer: anytype) !void { - const cg = elf_file.comdatGroup(cgs.cg_index); - const object = cgs.file(elf_file).?.object; + const cg = cgs.comdatGroup(elf_file); + const object = cg.file(elf_file).object; const members = cg.comdatGroupMembers(elf_file); try writer.writeInt(u32, elf.GRP_COMDAT, .little); for (members) |shndx| { const shdr = object.shdrs.items[shndx]; switch (shdr.sh_type) { elf.SHT_RELA => { - const atom_index = object.atoms.items[shdr.sh_info]; - const atom = elf_file.atom(atom_index).?; - const rela = elf_file.output_rela_sections.get(atom.outputShndx().?).?; + const atom_index = object.atoms_indexes.items[shdr.sh_info]; + const atom = object.atom(atom_index).?; + const rela = elf_file.output_rela_sections.get(atom.output_section_index).?; try writer.writeInt(u32, rela.shndx, .little); }, else => { - const atom_index = object.atoms.items[shndx]; - const atom = elf_file.atom(atom_index).?; - try writer.writeInt(u32, atom.outputShndx().?, .little); + const atom_index = object.atoms_indexes.items[shndx]; + const atom = object.atom(atom_index).?; + try writer.writeInt(u32, atom.output_section_index, .little); }, } } diff --git a/src/link/Elf/thunks.zig b/src/link/Elf/thunks.zig index 48694416c9180b5c2354257c90e5698ff3809103..8353eea98a5a06dd0952d8fe4951c5dc69cf3a76 100644 --- a/src/link/Elf/thunks.zig +++ b/src/link/Elf/thunks.zig @@ -6,22 +6,21 @@ pub fn createThunks(shndx: u32, elf_file: *Elf) !void { const atoms = elf_file.output_sections.get(shndx).?.items; assert(atoms.len > 0); - for (atoms) |atom_index| { - elf_file.atom(atom_index).?.value = -1; + for (atoms) |ref| { + elf_file.atom(ref).?.value = -1; } var i: usize = 0; while (i < atoms.len) { const start = i; const start_atom = elf_file.atom(atoms[start]).?; - assert(start_atom.flags.alive); + assert(start_atom.alive); start_atom.value = try advance(shdr, start_atom.size, start_atom.alignment); i += 1; while (i < atoms.len) : (i += 1) { - const atom_index = atoms[i]; - const atom = elf_file.atom(atom_index).?; - assert(atom.flags.alive); + const atom = elf_file.atom(atoms[i]).?; + assert(atom.alive); if (@as(i64, @intCast(atom.alignment.forward(shdr.sh_size))) - start_atom.value >= max_distance) break; atom.value = try advance(shdr, atom.size, atom.alignment); @@ -33,10 +32,10 @@ pub fn createThunks(shndx: u32, elf_file: *Elf) !void { thunk.output_section_index = shndx; // Scan relocs in the group and create trampolines for any unreachable callsite - for (atoms[start..i]) |atom_index| { - const atom = elf_file.atom(atom_index).?; + for (atoms[start..i]) |ref| { + const atom = elf_file.atom(ref).?; const file = atom.file(elf_file).?; - log.debug("atom({d}) {s}", .{ atom_index, atom.name(elf_file) }); + log.debug("atom({}) {s}", .{ ref, atom.name(elf_file) }); for (atom.relocs(elf_file)) |rel| { const is_reachable = switch (cpu_arch) { .aarch64 => aarch64.isReachable(atom, rel, elf_file), @@ -51,8 +50,7 @@ pub fn createThunks(shndx: u32, elf_file: *Elf) !void { }; try thunk.symbols.put(gpa, target, {}); } - try atom.addExtra(.{ .thunk = thunk_index }, elf_file); - atom.flags.thunk = true; + atom.addExtra(.{ .thunk = thunk_index }, elf_file); } thunk.value = try advance(shdr, thunk.size(elf_file), Atom.Alignment.fromNonzeroByteUnits(2)); diff --git a/src/link/MachO/InternalObject.zig b/src/link/MachO/InternalObject.zig index 9720251f97748ac441fceb60573bb9d744a0e3dc..7e0f375ec172ab2dcd295af5dd0ac53a601fd739 100644 --- a/src/link/MachO/InternalObject.zig +++ b/src/link/MachO/InternalObject.zig @@ -45,8 +45,7 @@ pub fn deinit(self: *InternalObject, allocator: Allocator) void { pub fn init(self: *InternalObject, allocator: Allocator) !void { // Atom at index 0 is reserved as null atom. - try self.atoms.append(allocator, .{}); - try self.atoms_extra.append(allocator, 0); + try self.atoms.append(allocator, .{ .extra = try self.addAtomExtra(allocator, .{}) }); // Null byte in strtab try self.strtab.append(allocator, 0); } diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 4e939008a590c1a4d5502b6b758203da856fafa3..eb240017eaad4e940f5ecea1ff5c50c3a985836e 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -1634,7 +1634,7 @@ fn isThreadlocal(macho_file: *MachO, decl_index: InternPool.DeclIndex) bool { fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index { try self.atoms.ensureUnusedCapacity(allocator, 1); - try self.atoms_extra.ensureUnusedCapacity(allocator, 1); + try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra)); return self.addAtomAssumeCapacity(); } diff --git a/test/link/elf.zig b/test/link/elf.zig index d97afd4d8f9c324deb70d2bdb31890275968147b..71b722a9f983fef707652ac26117c516c7252007 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -59,6 +59,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { // Exercise linker with LLVM backend // musl tests elf_step.dependOn(testAbsSymbols(b, .{ .target = musl_target })); + elf_step.dependOn(testComdatElimination(b, .{ .target = musl_target })); elf_step.dependOn(testCommonSymbols(b, .{ .target = musl_target })); elf_step.dependOn(testCommonSymbolsInArchive(b, .{ .target = musl_target })); elf_step.dependOn(testCommentString(b, .{ .target = musl_target })); @@ -368,6 +369,109 @@ fn testCanonicalPlt(b: *Build, opts: Options) *Step { return test_step; } +fn testComdatElimination(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "comdat-elimination", opts); + + const a_o = addObject(b, opts, .{ + .name = "a", + .cpp_source_bytes = + \\#include + \\inline void foo() { + \\ printf("calling foo in a\n"); + \\} + \\void hello() { + \\ foo(); + \\} + , + }); + a_o.linkLibCpp(); + + const main_o = addObject(b, opts, .{ + .name = "main", + .cpp_source_bytes = + \\#include + \\inline void foo() { + \\ printf("calling foo in main\n"); + \\} + \\void hello(); + \\int main() { + \\ foo(); + \\ hello(); + \\ return 0; + \\} + , + }); + main_o.linkLibCpp(); + + { + const exe = addExecutable(b, opts, .{ .name = "main1" }); + exe.addObject(a_o); + exe.addObject(main_o); + exe.linkLibCpp(); + + const run = addRunArtifact(exe); + run.expectStdOutEqual( + \\calling foo in a + \\calling foo in a + \\ + ); + test_step.dependOn(&run.step); + } + + { + const exe = addExecutable(b, opts, .{ .name = "main2" }); + exe.addObject(main_o); + exe.addObject(a_o); + exe.linkLibCpp(); + + const run = addRunArtifact(exe); + run.expectStdOutEqual( + \\calling foo in main + \\calling foo in main + \\ + ); + test_step.dependOn(&run.step); + } + + { + const c_o = addObject(b, opts, .{ .name = "c" }); + c_o.addObject(main_o); + c_o.addObject(a_o); + + const exe = addExecutable(b, opts, .{ .name = "main3" }); + exe.addObject(c_o); + exe.linkLibCpp(); + + const run = addRunArtifact(exe); + run.expectStdOutEqual( + \\calling foo in main + \\calling foo in main + \\ + ); + test_step.dependOn(&run.step); + } + + { + const d_o = addObject(b, opts, .{ .name = "d" }); + d_o.addObject(a_o); + d_o.addObject(main_o); + + const exe = addExecutable(b, opts, .{ .name = "main4" }); + exe.addObject(d_o); + exe.linkLibCpp(); + + const run = addRunArtifact(exe); + run.expectStdOutEqual( + \\calling foo in a + \\calling foo in a + \\ + ); + test_step.dependOn(&run.step); + } + + return test_step; +} + fn testCommentString(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "comment-string", opts);