From 5e78600f0f5e790c0429817d8249020fd65b49f8 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Nov 2023 22:48:02 +0100 Subject: [PATCH] elf: actually track output symtab index of symbols --- src/link/Elf.zig | 251 +++++++++++++++------------- src/link/Elf/Atom.zig | 26 +++ src/link/Elf/LinkerDefined.zig | 2 +- src/link/Elf/Object.zig | 17 +- src/link/Elf/SharedObject.zig | 2 +- src/link/Elf/Symbol.zig | 19 +++ src/link/Elf/ZigObject.zig | 99 ++--------- src/link/Elf/file.zig | 42 ++--- src/link/Elf/synthetic_sections.zig | 40 ++--- 9 files changed, 234 insertions(+), 264 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index da5690c4de..fd52a4327e 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -24,6 +24,7 @@ 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(u16, std.ArrayListUnmanaged(Atom.Index)) = .{}, +output_rela_sections: std.AutoArrayHashMapUnmanaged(u16, std.ArrayListUnmanaged(Atom.Index)) = .{}, /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. /// Same order as in the file. @@ -105,11 +106,8 @@ zig_got: ZigGotSection = .{}, /// Tracked section headers with incremental updates to Zig object. /// .rela.* sections are only used when emitting a relocatable object file. zig_text_section_index: ?u16 = null, -zig_text_rela_section_index: ?u16 = null, zig_data_rel_ro_section_index: ?u16 = null, -zig_data_rel_ro_rela_section_index: ?u16 = null, zig_data_section_index: ?u16 = null, -zig_data_rela_section_index: ?u16 = null, zig_bss_section_index: ?u16 = null, zig_got_section_index: ?u16 = null, @@ -357,6 +355,10 @@ pub fn deinit(self: *Elf) void { list.deinit(gpa); } self.output_sections.deinit(gpa); + for (self.output_rela_sections.values()) |*list| { + list.deinit(gpa); + } + self.output_rela_sections.deinit(gpa); self.shstrtab.deinit(gpa); self.symtab.deinit(gpa); self.strtab.deinit(gpa); @@ -597,10 +599,8 @@ pub fn initMetadata(self: *Elf) !void { const shdr = &self.shdrs.items[self.zig_text_section_index.?]; fillSection(self, shdr, self.base.options.program_code_size_hint, self.phdr_zig_load_re_index); if (self.isRelocatable()) { - self.zig_text_rela_section_index = try self.addRelaShdr( - ".rela.text.zig", - self.zig_text_section_index.?, - ); + const rela_shndx = try self.addRelaShdr(".rela.text.zig", self.zig_text_section_index.?); + try self.output_rela_sections.putNoClobber(gpa, rela_shndx, .{}); } else { try self.phdr_to_shdr_table.putNoClobber( gpa, @@ -644,10 +644,11 @@ pub fn initMetadata(self: *Elf) !void { const shdr = &self.shdrs.items[self.zig_data_rel_ro_section_index.?]; fillSection(self, shdr, 1024, self.phdr_zig_load_ro_index); if (self.isRelocatable()) { - self.zig_data_rel_ro_rela_section_index = try self.addRelaShdr( + const rela_shndx = try self.addRelaShdr( ".rela.data.rel.ro.zig", self.zig_data_rel_ro_section_index.?, ); + try self.output_rela_sections.putNoClobber(gpa, rela_shndx, .{}); } else { try self.phdr_to_shdr_table.putNoClobber( gpa, @@ -670,10 +671,11 @@ pub fn initMetadata(self: *Elf) !void { const shdr = &self.shdrs.items[self.zig_data_section_index.?]; fillSection(self, shdr, 1024, self.phdr_zig_load_rw_index); if (self.isRelocatable()) { - self.zig_data_rela_section_index = try self.addRelaShdr( + const rela_shndx = try self.addRelaShdr( ".rela.data.zig", self.zig_data_section_index.?, ); + try self.output_rela_sections.putNoClobber(gpa, rela_shndx, .{}); } else { try self.phdr_to_shdr_table.putNoClobber( gpa, @@ -1514,16 +1516,13 @@ pub fn flushStaticLib(self: *Elf) link.File.FlushError!void { try self.initSymtab(); try self.initShStrtab(); try self.sortShdrs(); - zig_object.updateRelaSectionsSizes(self); - self.updateSymtabSizeZigObject(zig_object); - self.updateShStrtabSize(); + try zig_object.addAtomsToRelaSections(self); + try self.updateSectionSizesObject(); try self.allocateNonAllocSections(); try self.writeShdrTable(); - try zig_object.writeRelaSections(self); - try self.writeSymtabZigObject(zig_object); - try self.writeShStrtab(); + try self.writeSyntheticSectionsObject(); try self.writeElfHeader(); } @@ -1620,7 +1619,9 @@ pub fn flushObject(self: *Elf) link.File.FlushError!void { try self.initSectionsObject(); try self.sortShdrs(); for (self.objects.items) |index| { - try self.file(index).?.object.addAtomsToOutputSections(self); + const object = self.file(index).?.object; + try object.addAtomsToOutputSections(self); + try object.addAtomsToRelaSections(self); } try self.updateSectionSizesObject(); @@ -3452,7 +3453,7 @@ fn initSectionsObject(self: *Elf) !void { .addralign = ptr_size, .offset = std.math.maxInt(u64), }); - _ = try self.addRelaShdr(".rela.eh_frame", self.eh_frame_section_index.?); + // _ = try self.addRelaShdr(".rela.eh_frame", self.eh_frame_section_index.?); } try self.initSymtab(); @@ -3841,12 +3842,9 @@ fn sortShdrs(self: *Elf) !void { &self.versym_section_index, &self.verneed_section_index, &self.zig_text_section_index, - &self.zig_text_rela_section_index, &self.zig_got_section_index, &self.zig_data_rel_ro_section_index, - &self.zig_data_rel_ro_rela_section_index, &self.zig_data_section_index, - &self.zig_data_rela_section_index, &self.zig_bss_section_index, &self.debug_str_section_index, &self.debug_info_section_index, @@ -4066,7 +4064,7 @@ fn updateSectionSizes(self: *Elf) !void { self.shdrs.items[index].sh_size = self.verneed.size(); } - self.updateSymtabSize(); + try self.updateSymtabSize(); self.updateShStrtabSize(); } @@ -4084,19 +4082,21 @@ fn updateSectionSizesObject(self: *Elf) !void { } } - if (self.zigObjectPtr()) |zig_object| { - zig_object.updateRelaSectionsSizes(self); - } - - for (self.objects.items) |index| { - self.file(index).?.object.updateRelaSectionsSizes(self); + for (self.output_rela_sections.keys(), self.output_rela_sections.values()) |shndx, atom_list| { + const shdr = &self.shdrs.items[shndx]; + for (atom_list.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.flags.alive) continue; + const relocs = atom_ptr.relocs(self); + shdr.sh_size += shdr.sh_entsize * relocs.len; + } } if (self.eh_frame_section_index) |index| { self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self); } - self.updateSymtabSize(); + try self.updateSymtabSize(); self.updateShStrtabSize(); } @@ -4597,96 +4597,123 @@ fn writeAtomsObject(self: *Elf) !void { } } -fn updateSymtabSize(self: *Elf) void { - var sizes = SymtabSize{}; +fn updateSymtabSize(self: *Elf) !void { + var nlocals: u32 = 0; + var nglobals: u32 = 0; + var strsize: u32 = 0; - if (self.zigObjectPtr()) |zig_object| { - zig_object.asFile().updateSymtabSize(self); - sizes.add(zig_object.output_symtab_size); - } + const gpa = self.base.allocator; + var files = std.ArrayList(File.Index).init(gpa); + defer files.deinit(); + try files.ensureTotalCapacityPrecise(self.objects.items.len + self.shared_objects.items.len + 1); + if (self.zig_object_index) |index| files.appendAssumeCapacity(index); for (self.objects.items) |index| { - const file_ptr = self.file(index).?; - file_ptr.updateSymtabSize(self); - sizes.add(file_ptr.object.output_symtab_size); + files.appendAssumeCapacity(index); + } + for (self.shared_objects.items) |index| { + files.appendAssumeCapacity(index); } - for (self.shared_objects.items) |index| { + // Section symbols + for (self.output_sections.keys()) |_| { + nlocals += 1; + } + + for (files.items) |index| { const file_ptr = self.file(index).?; - file_ptr.updateSymtabSize(self); - sizes.add(file_ptr.shared_object.output_symtab_size); + const ctx = switch (file_ptr) { + inline else => |x| &x.output_symtab_ctx, + }; + ctx.ilocal = nlocals + 1; + ctx.iglobal = nglobals + 1; + try file_ptr.updateSymtabSize(self); + nlocals += ctx.nlocals; + nglobals += ctx.nglobals; + strsize += ctx.strsize; } if (self.zig_got_section_index) |_| { + self.zig_got.output_symtab_ctx.ilocal = nlocals + 1; self.zig_got.updateSymtabSize(self); - sizes.add(self.zig_got.output_symtab_size); + nlocals += self.zig_got.output_symtab_ctx.nlocals; + strsize += self.zig_got.output_symtab_ctx.strsize; } if (self.got_section_index) |_| { + self.got.output_symtab_ctx.ilocal = nlocals + 1; self.got.updateSymtabSize(self); - sizes.add(self.got.output_symtab_size); + nlocals += self.got.output_symtab_ctx.nlocals; + strsize += self.got.output_symtab_ctx.strsize; } if (self.plt_section_index) |_| { + self.plt.output_symtab_ctx.ilocal = nlocals + 1; self.plt.updateSymtabSize(self); - sizes.add(self.plt.output_symtab_size); + nlocals += self.plt.output_symtab_ctx.nlocals; + strsize += self.plt.output_symtab_ctx.strsize; } if (self.plt_got_section_index) |_| { + self.plt_got.output_symtab_ctx.ilocal = nlocals + 1; self.plt_got.updateSymtabSize(self); - sizes.add(self.plt_got.output_symtab_size); + nlocals += self.plt_got.output_symtab_ctx.nlocals; + strsize += self.plt_got.output_symtab_ctx.strsize; } - if (self.linker_defined_index) |index| { + for (files.items) |index| { const file_ptr = self.file(index).?; - file_ptr.updateSymtabSize(self); - sizes.add(file_ptr.linker_defined.output_symtab_size); - } - - // Section symbols - for (self.output_sections.keys()) |_| { - sizes.nlocals += 1; + const ctx = switch (file_ptr) { + inline else => |x| &x.output_symtab_ctx, + }; + ctx.iglobal += nlocals; } const symtab_shdr = &self.shdrs.items[self.symtab_section_index.?]; - symtab_shdr.sh_info = sizes.nlocals + 1; + symtab_shdr.sh_info = nlocals + 1; symtab_shdr.sh_link = self.strtab_section_index.?; const sym_size: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Sym), .p64 => @sizeOf(elf.Elf64_Sym), }; - const needed_size = (sizes.nlocals + sizes.nglobals + 1) * sym_size; + const needed_size = (nlocals + nglobals + 1) * sym_size; symtab_shdr.sh_size = needed_size; const strtab = &self.shdrs.items[self.strtab_section_index.?]; - strtab.sh_size = sizes.strsize + 1; + strtab.sh_size = strsize + 1; } fn updateSymtabSizeZigObject(self: *Elf, zig_object: *ZigObject) void { - var sizes = SymtabSize{}; - - zig_object.asFile().updateSymtabSize(self); - sizes.add(zig_object.output_symtab_size); + var nlocals: u32 = 0; + var nglobals: u32 = 0; + var strsize: u32 = 0; // Section symbols for (self.output_sections.keys()) |_| { - sizes.nlocals += 1; + nlocals += 1; } + zig_object.output_symtab_ctx.ilocal = nlocals + 1; + try zig_object.asFile().updateSymtabSize(self); + nlocals += zig_object.output_symtab_ctx.nlocals; + nglobals += zig_object.output_symtab_ctx.nglobals; + strsize += zig_object.output_symtab_ctx.strsize; + zig_object.output_symtab_ctx.iglobal = nlocals + 1; + const symtab_shdr = &self.shdrs.items[self.symtab_section_index.?]; - symtab_shdr.sh_info = sizes.nlocals + 1; + symtab_shdr.sh_info = nlocals + 1; symtab_shdr.sh_link = self.strtab_section_index.?; const sym_size: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Sym), .p64 => @sizeOf(elf.Elf64_Sym), }; - const needed_size = (sizes.nlocals + sizes.nglobals + 1) * sym_size; + const needed_size = (nlocals + nglobals + 1) * sym_size; symtab_shdr.sh_size = needed_size; const strtab = &self.shdrs.items[self.strtab_section_index.?]; - strtab.sh_size = sizes.strsize + 1; + strtab.sh_size = strsize + 1; } fn writeSyntheticSections(self: *Elf) !void { @@ -4822,12 +4849,31 @@ fn writeSyntheticSections(self: *Elf) !void { fn writeSyntheticSectionsObject(self: *Elf) !void { const gpa = self.base.allocator; - if (self.zigObjectPtr()) |zig_object| { - try zig_object.writeRelaSections(self); - } + for (self.output_rela_sections.keys(), self.output_rela_sections.values()) |shndx, atom_list| { + if (atom_list.items.len == 0) continue; - for (self.objects.items) |index| { - try self.file(index).?.object.writeRelaSections(self); + const shdr = self.shdrs.items[shndx]; + + const num_relocs = @divExact(shdr.sh_size, shdr.sh_entsize); + var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs); + defer relocs.deinit(); + + for (atom_list.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.flags.alive) continue; + try atom_ptr.writeRelocs(self, &relocs); + } + + const SortRelocs = struct { + pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool { + _ = ctx; + return lhs.r_offset < rhs.r_offset; + } + }; + + mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan); + + try self.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset); } if (self.eh_frame_section_index) |shndx| { @@ -4836,7 +4882,7 @@ fn writeSyntheticSectionsObject(self: *Elf) !void { var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); defer buffer.deinit(); try eh_frame.writeEhFrame(self, buffer.writer()); - // try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); + try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); } try self.writeSymtab(); @@ -4850,16 +4896,6 @@ fn writeShStrtab(self: *Elf) !void { } } -const WriteSymtabCtx = struct { - ilocal: usize, - iglobal: usize, - - fn incr(this: *@This(), ss: SymtabSize) void { - this.ilocal += ss.nlocals; - this.iglobal += ss.nglobals; - } -}; - fn writeSymtab(self: *Elf) !void { const gpa = self.base.allocator; const symtab_shdr = self.shdrs.items[self.symtab_section_index.?]; @@ -4876,54 +4912,41 @@ fn writeSymtab(self: *Elf) !void { const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow; try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size); - var ctx: WriteSymtabCtx = .{ - .ilocal = 1, - .iglobal = symtab_shdr.sh_info, - }; - - ctx.incr(self.writeSectionSymbols(ctx)); + self.writeSectionSymbols(); if (self.zigObjectPtr()) |zig_object| { - zig_object.asFile().writeSymtab(self, ctx); - ctx.incr(zig_object.output_symtab_size); + zig_object.asFile().writeSymtab(self); } for (self.objects.items) |index| { const file_ptr = self.file(index).?; - file_ptr.writeSymtab(self, ctx); - ctx.incr(file_ptr.object.output_symtab_size); + file_ptr.writeSymtab(self); } for (self.shared_objects.items) |index| { const file_ptr = self.file(index).?; - file_ptr.writeSymtab(self, ctx); - ctx.incr(file_ptr.shared_object.output_symtab_size); + file_ptr.writeSymtab(self); } if (self.zig_got_section_index) |_| { - self.zig_got.writeSymtab(self, ctx); - ctx.incr(self.zig_got.output_symtab_size); + self.zig_got.writeSymtab(self); } if (self.got_section_index) |_| { - self.got.writeSymtab(self, ctx); - ctx.incr(self.got.output_symtab_size); + self.got.writeSymtab(self); } if (self.plt_section_index) |_| { - self.plt.writeSymtab(self, ctx); - ctx.incr(self.plt.output_symtab_size); + self.plt.writeSymtab(self); } if (self.plt_got_section_index) |_| { - self.plt_got.writeSymtab(self, ctx); - ctx.incr(self.plt_got.output_symtab_size); + self.plt_got.writeSymtab(self); } if (self.linker_defined_index) |index| { const file_ptr = self.file(index).?; - file_ptr.writeSymtab(self, ctx); - ctx.incr(file_ptr.linker_defined.output_symtab_size); + file_ptr.writeSymtab(self); } const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); @@ -4972,15 +4995,8 @@ fn writeSymtabZigObject(self: *Elf, zig_object: *ZigObject) !void { const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow; try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size); - var ctx: WriteSymtabCtx = .{ - .ilocal = 1, - .iglobal = symtab_shdr.sh_info, - }; - - ctx.incr(self.writeSectionSymbols(ctx)); - - zig_object.asFile().writeSymtab(self, ctx); - ctx.incr(zig_object.output_symtab_size); + self.writeSectionSymbols(); + zig_object.asFile().writeSymtab(self); const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); switch (self.ptr_width) { @@ -5012,8 +5028,8 @@ fn writeSymtabZigObject(self: *Elf, zig_object: *ZigObject) !void { try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset); } -fn writeSectionSymbols(self: *Elf, ctx: WriteSymtabCtx) SymtabSize { - var ilocal = ctx.ilocal; +fn writeSectionSymbols(self: *Elf) void { + var ilocal: u32 = 1; for (self.output_sections.keys()) |shndx| { const shdr = self.shdrs.items[shndx]; const out_sym = &self.symtab.items[ilocal]; @@ -5027,7 +5043,6 @@ fn writeSectionSymbols(self: *Elf, ctx: WriteSymtabCtx) SymtabSize { }; ilocal += 1; } - return .{ .nlocals = @intCast(ilocal - ctx.ilocal) }; } /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF. @@ -6056,16 +6071,12 @@ pub const ComdatGroup = struct { pub const Index = u32; }; -pub const SymtabSize = struct { +pub const SymtabCtx = struct { + ilocal: u32 = 0, + iglobal: u32 = 0, nlocals: u32 = 0, nglobals: u32 = 0, strsize: u32 = 0, - - fn add(ss: *SymtabSize, other: SymtabSize) void { - ss.nlocals += other.nlocals; - ss.nglobals += other.nglobals; - ss.strsize += other.strsize; - } }; pub const null_sym = elf.Elf64_Sym{ diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 98a9f13771..95202a3d4c 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -293,6 +293,32 @@ pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela { }; } +pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.Elf64_Rela)) !void { + const file_ptr = self.file(elf_file).?; + for (self.relocs(elf_file)) |rel| { + const target_index = switch (file_ptr) { + .zig_object => |x| x.symbol(rel.r_sym()), + .object => |x| x.symbols.items[rel.r_sym()], + else => unreachable, + }; + const target = elf_file.symbol(target_index); + const r_sym = target.outputSymtabIndex(elf_file); + const r_offset = self.value + rel.r_offset; + const r_addend = rel.r_addend; + const r_type = switch (rel.r_type()) { + Elf.R_X86_64_ZIG_GOT32, + Elf.R_X86_64_ZIG_GOTPCREL, + => unreachable, // Sanity check if we accidentally emitted those. + else => |r_type| r_type, + }; + out_relocs.appendAssumeCapacity(.{ + .r_offset = r_offset, + .r_addend = r_addend, + .r_info = (@as(u64, @intCast(r_sym)) << 32) | r_type, + }); + } +} + pub fn fdes(self: Atom, elf_file: *Elf) []Fde { if (self.fde_start == self.fde_end) return &[0]Fde{}; const object = self.file(elf_file).?.object; diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig index 49e0e8f71d..938f22985f 100644 --- a/src/link/Elf/LinkerDefined.zig +++ b/src/link/Elf/LinkerDefined.zig @@ -3,7 +3,7 @@ symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, -output_symtab_size: Elf.SymtabSize = .{}, +output_symtab_ctx: Elf.SymtabCtx = .{}, pub fn deinit(self: *LinkerDefined, allocator: Allocator) void { self.symtab.deinit(allocator); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index fa9aac3c43..637dc6114e 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -19,7 +19,7 @@ cies: std.ArrayListUnmanaged(Cie) = .{}, alive: bool = true, num_dynrelocs: u32 = 0, -output_symtab_size: Elf.SymtabSize = .{}, +output_symtab_ctx: Elf.SymtabCtx = .{}, output_ar_state: Archive.ArState = .{}, pub fn isObject(path: []const u8) !bool { @@ -672,22 +672,19 @@ pub fn initRelaSections(self: Object, elf_file: *Elf) !void { } } -pub fn updateRelaSectionsSizes(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; const shndx = atom.relocsShndx() orelse continue; const shdr = self.shdrs.items[shndx]; const out_shndx = self.initOutputSection(elf_file, shdr) catch unreachable; - const out_shdr = &elf_file.shdrs.items[out_shndx]; - const relocs = atom.relocs(elf_file); - out_shdr.sh_size += out_shdr.sh_entsize * relocs.len; - } -} -pub fn writeRelaSections(self: Object, elf_file: *Elf) !void { - _ = self; - _ = elf_file; + const gpa = elf_file.base.allocator; + const gop = try elf_file.output_rela_sections.getOrPut(gpa, out_shndx); + if (!gop.found_existing) gop.value_ptr.* = .{}; + try gop.value_ptr.append(gpa, atom_index); + } } pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void { diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig index 1785232625..2317719012 100644 --- a/src/link/Elf/SharedObject.zig +++ b/src/link/Elf/SharedObject.zig @@ -21,7 +21,7 @@ verdef_sect_index: ?u16 = null, needed: bool, alive: bool, -output_symtab_size: Elf.SymtabSize = .{}, +output_symtab_ctx: Elf.SymtabCtx = .{}, pub fn isSharedObject(path: []const u8) !bool { const file = try std.fs.cwd().openFile(path, .{}); diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 9a9daff358..ed3260b77a 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -107,6 +107,24 @@ pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf return symbol.value; } +pub fn outputSymtabIndex(symbol: Symbol, elf_file: *Elf) u32 { + assert(symbol.flags.output_symtab); + const file_ptr = symbol.file(elf_file).?; + const symtab_ctx = switch (file_ptr) { + inline else => |x| x.output_symtab_ctx, + }; + const idx = symbol.extra(elf_file).?.symtab; + return if (symbol.isLocal(elf_file)) idx + symtab_ctx.ilocal else idx + symtab_ctx.iglobal; +} + +pub fn setOutputSymtabIndex(symbol: *Symbol, index: u32, elf_file: *Elf) !void { + if (symbol.extra(elf_file)) |extras| { + var new_extras = extras; + new_extras.symtab = index; + symbol.setExtra(new_extras, elf_file); + } else try symbol.addExtra(.{ .symtab = index }, elf_file); +} + pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { if (!symbol.flags.has_got) return 0; const extras = symbol.extra(elf_file).?; @@ -388,6 +406,7 @@ pub const Extra = struct { plt: u32 = 0, plt_got: u32 = 0, dynamic: u32 = 0, + symtab: u32 = 0, copy_rel: u32 = 0, tlsgd: u32 = 0, gottp: u32 = 0, diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 3c5174b86e..0ade656a3d 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -18,7 +18,7 @@ relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{}, num_dynrelocs: u32 = 0, -output_symtab_size: Elf.SymtabSize = .{}, +output_symtab_ctx: Elf.SymtabCtx = .{}, output_ar_state: Archive.ArState = .{}, dwarf: ?Dwarf = null, @@ -533,94 +533,17 @@ pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void { try writer.writeAll(contents); } -pub fn updateRelaSectionsSizes(self: ZigObject, elf_file: *Elf) void { - _ = self; +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; + _ = atom.relocsShndx() orelse continue; + const out_shndx = atom.outputShndx().?; - for (&[_]?u16{ - elf_file.zig_text_rela_section_index, - elf_file.zig_data_rel_ro_rela_section_index, - elf_file.zig_data_rela_section_index, - }) |maybe_index| { - const index = maybe_index orelse continue; - const shdr = &elf_file.shdrs.items[index]; - const meta = elf_file.last_atom_and_free_list_table.get(@intCast(shdr.sh_info)).?; - const last_atom_index = meta.last_atom_index; - - var atom = elf_file.atom(last_atom_index) orelse continue; - while (true) { - const relocs = atom.relocs(elf_file); - shdr.sh_size += relocs.len * shdr.sh_entsize; - if (elf_file.atom(atom.prev_index)) |prev| { - atom = prev; - } else break; - } - } - - for (&[_]?u16{ - elf_file.zig_text_rela_section_index, - elf_file.zig_data_rel_ro_rela_section_index, - elf_file.zig_data_rela_section_index, - }) |maybe_index| { - const index = maybe_index orelse continue; - const shdr = &elf_file.shdrs.items[index]; - if (shdr.sh_size == 0) shdr.sh_offset = 0; - } -} - -pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; - - for (&[_]?u16{ - elf_file.zig_text_rela_section_index, - elf_file.zig_data_rel_ro_rela_section_index, - elf_file.zig_data_rela_section_index, - }) |maybe_index| { - const index = maybe_index orelse continue; - const shdr = elf_file.shdrs.items[index]; - const meta = elf_file.last_atom_and_free_list_table.get(@intCast(shdr.sh_info)).?; - const last_atom_index = meta.last_atom_index; - - var atom = elf_file.atom(last_atom_index) orelse continue; - - var relocs = std.ArrayList(elf.Elf64_Rela).init(gpa); - defer relocs.deinit(); - try relocs.ensureTotalCapacityPrecise(@intCast(@divExact(shdr.sh_size, shdr.sh_entsize))); - - while (true) { - for (atom.relocs(elf_file)) |rel| { - const target = elf_file.symbol(self.symbol(rel.r_sym())); - const r_offset = atom.value + rel.r_offset; - const r_sym: u32 = if (target.flags.global) - (target.esym_index & symbol_mask) + @as(u32, @intCast(self.local_esyms.slice().len)) - else - target.esym_index; - const r_type = switch (rel.r_type()) { - Elf.R_X86_64_ZIG_GOT32, - Elf.R_X86_64_ZIG_GOTPCREL, - => unreachable, // Sanity check if we accidentally emitted those. - else => |r_type| r_type, - }; - relocs.appendAssumeCapacity(.{ - .r_offset = r_offset, - .r_addend = rel.r_addend, - .r_info = (@as(u64, @intCast(r_sym + 1)) << 32) | r_type, - }); - } - if (elf_file.atom(atom.prev_index)) |prev| { - atom = prev; - } else break; - } - - const SortRelocs = struct { - pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool { - _ = ctx; - return lhs.r_offset < rhs.r_offset; - } - }; - - mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan); - - try elf_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset); + const gpa = elf_file.base.allocator; + const gop = try elf_file.output_rela_sections.getOrPut(gpa, out_shndx); + if (!gop.found_existing) gop.value_ptr.* = .{}; + try gop.value_ptr.append(gpa, atom_index); } } diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 0fae6bc9f4..357e1ee12d 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -127,9 +127,9 @@ pub const File = union(enum) { }; } - pub fn updateSymtabSize(file: File, elf_file: *Elf) void { - const output_symtab_size = switch (file) { - inline else => |x| &x.output_symtab_size, + pub fn updateSymtabSize(file: File, elf_file: *Elf) !void { + const output_symtab_ctx = switch (file) { + inline else => |x| &x.output_symtab_ctx, }; for (file.locals()) |local_index| { const local = elf_file.symbol(local_index); @@ -140,8 +140,9 @@ pub const File = union(enum) { else => {}, } local.flags.output_symtab = true; - output_symtab_size.nlocals += 1; - output_symtab_size.strsize += @as(u32, @intCast(local.name(elf_file).len)) + 1; + try local.setOutputSymtabIndex(output_symtab_ctx.nlocals, elf_file); + output_symtab_ctx.nlocals += 1; + output_symtab_ctx.strsize += @as(u32, @intCast(local.name(elf_file).len)) + 1; } for (file.globals()) |global_index| { @@ -151,28 +152,28 @@ pub const File = union(enum) { if (global.atom(elf_file)) |atom| if (!atom.flags.alive) continue; global.flags.output_symtab = true; if (global.isLocal(elf_file)) { - output_symtab_size.nlocals += 1; + try global.setOutputSymtabIndex(output_symtab_ctx.nlocals, elf_file); + output_symtab_ctx.nlocals += 1; } else { - output_symtab_size.nglobals += 1; + try global.setOutputSymtabIndex(output_symtab_ctx.nglobals, elf_file); + output_symtab_ctx.nglobals += 1; } - output_symtab_size.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1; + output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1; } } - pub fn writeSymtab(file: File, elf_file: *Elf, ctx: anytype) void { - var ilocal: usize = ctx.ilocal; + pub fn writeSymtab(file: File, elf_file: *Elf) void { for (file.locals()) |local_index| { const local = elf_file.symbol(local_index); if (!local.flags.output_symtab) continue; - const out_sym = &elf_file.symtab.items[ilocal]; + const idx = local.outputSymtabIndex(elf_file); + const out_sym = &elf_file.symtab.items[idx]; out_sym.st_name = @intCast(elf_file.strtab.items.len); elf_file.strtab.appendSliceAssumeCapacity(local.name(elf_file)); elf_file.strtab.appendAssumeCapacity(0); local.setOutputSym(elf_file, out_sym); - ilocal += 1; } - var iglobal: usize = ctx.iglobal; for (file.globals()) |global_index| { const global = elf_file.symbol(global_index); const file_ptr = global.file(elf_file) orelse continue; @@ -181,17 +182,10 @@ pub const File = union(enum) { const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file)); elf_file.strtab.appendAssumeCapacity(0); - if (global.isLocal(elf_file)) { - const out_sym = &elf_file.symtab.items[ilocal]; - out_sym.st_name = st_name; - global.setOutputSym(elf_file, out_sym); - ilocal += 1; - } else { - const out_sym = &elf_file.symtab.items[iglobal]; - out_sym.st_name = st_name; - global.setOutputSym(elf_file, out_sym); - iglobal += 1; - } + const idx = global.outputSymtabIndex(elf_file); + const out_sym = &elf_file.symtab.items[idx]; + out_sym.st_name = st_name; + global.setOutputSym(elf_file, out_sym); } } diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index 2602940d41..0c9f94b5b9 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -222,7 +222,7 @@ pub const DynamicSection = struct { pub const ZigGotSection = struct { entries: std.ArrayListUnmanaged(Symbol.Index) = .{}, - output_symtab_size: Elf.SymtabSize = .{}, + output_symtab_ctx: Elf.SymtabCtx = .{}, flags: Flags = .{}, const Flags = packed struct { @@ -359,15 +359,15 @@ pub const ZigGotSection = struct { } pub fn updateSymtabSize(zig_got: *ZigGotSection, elf_file: *Elf) void { - zig_got.output_symtab_size.nlocals = @as(u32, @intCast(zig_got.entries.items.len)); + zig_got.output_symtab_ctx.nlocals = @as(u32, @intCast(zig_got.entries.items.len)); for (zig_got.entries.items) |entry| { const name = elf_file.symbol(entry).name(elf_file); - zig_got.output_symtab_size.strsize += @as(u32, @intCast(name.len + "$ziggot".len)) + 1; + zig_got.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$ziggot".len)) + 1; } } - pub fn writeSymtab(zig_got: ZigGotSection, elf_file: *Elf, ctx: anytype) void { - for (zig_got.entries.items, ctx.ilocal.., 0..) |entry, ilocal, index| { + pub fn writeSymtab(zig_got: ZigGotSection, elf_file: *Elf) void { + for (zig_got.entries.items, zig_got.output_symtab_ctx.ilocal.., 0..) |entry, ilocal, index| { const symbol = elf_file.symbol(entry); const symbol_name = symbol.name(elf_file); const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); @@ -420,7 +420,7 @@ pub const ZigGotSection = struct { pub const GotSection = struct { entries: std.ArrayListUnmanaged(Entry) = .{}, - output_symtab_size: Elf.SymtabSize = .{}, + output_symtab_ctx: Elf.SymtabCtx = .{}, tlsld_index: ?u32 = null, flags: Flags = .{}, @@ -760,18 +760,18 @@ pub const GotSection = struct { } pub fn updateSymtabSize(got: *GotSection, elf_file: *Elf) void { - got.output_symtab_size.nlocals = @as(u32, @intCast(got.entries.items.len)); + got.output_symtab_ctx.nlocals = @as(u32, @intCast(got.entries.items.len)); for (got.entries.items) |entry| { const symbol_name = switch (entry.tag) { .tlsld => "", inline else => elf_file.symbol(entry.symbol_index).name(elf_file), }; - got.output_symtab_size.strsize += @as(u32, @intCast(symbol_name.len + @tagName(entry.tag).len)) + 1 + 1; + got.output_symtab_ctx.strsize += @as(u32, @intCast(symbol_name.len + @tagName(entry.tag).len)) + 1 + 1; } } - pub fn writeSymtab(got: GotSection, elf_file: *Elf, ctx: anytype) void { - for (got.entries.items, ctx.ilocal..) |entry, ilocal| { + pub fn writeSymtab(got: GotSection, elf_file: *Elf) void { + for (got.entries.items, got.output_symtab_ctx.ilocal..) |entry, ilocal| { const symbol = switch (entry.tag) { .tlsld => null, inline else => elf_file.symbol(entry.symbol_index), @@ -831,7 +831,7 @@ pub const GotSection = struct { pub const PltSection = struct { symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, - output_symtab_size: Elf.SymtabSize = .{}, + output_symtab_ctx: Elf.SymtabCtx = .{}, pub const preamble_size = 32; @@ -909,15 +909,15 @@ pub const PltSection = struct { } pub fn updateSymtabSize(plt: *PltSection, elf_file: *Elf) void { - plt.output_symtab_size.nlocals = @as(u32, @intCast(plt.symbols.items.len)); + plt.output_symtab_ctx.nlocals = @as(u32, @intCast(plt.symbols.items.len)); for (plt.symbols.items) |sym_index| { const name = elf_file.symbol(sym_index).name(elf_file); - plt.output_symtab_size.strsize += @as(u32, @intCast(name.len + "$plt".len)) + 1; + plt.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$plt".len)) + 1; } } - pub fn writeSymtab(plt: PltSection, elf_file: *Elf, ctx: anytype) void { - var ilocal = ctx.ilocal; + pub fn writeSymtab(plt: PltSection, elf_file: *Elf) void { + var ilocal = plt.output_symtab_ctx.ilocal; for (plt.symbols.items) |sym_index| { const sym = elf_file.symbol(sym_index); const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); @@ -968,7 +968,7 @@ pub const GotPltSection = struct { pub const PltGotSection = struct { symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, - output_symtab_size: Elf.SymtabSize = .{}, + output_symtab_ctx: Elf.SymtabCtx = .{}, pub fn deinit(plt_got: *PltGotSection, allocator: Allocator) void { plt_got.symbols.deinit(allocator); @@ -1008,15 +1008,15 @@ pub const PltGotSection = struct { } pub fn updateSymtabSize(plt_got: *PltGotSection, elf_file: *Elf) void { - plt_got.output_symtab_size.nlocals = @as(u32, @intCast(plt_got.symbols.items.len)); + plt_got.output_symtab_ctx.nlocals = @as(u32, @intCast(plt_got.symbols.items.len)); for (plt_got.symbols.items) |sym_index| { const name = elf_file.symbol(sym_index).name(elf_file); - plt_got.output_symtab_size.strsize += @as(u32, @intCast(name.len + "$pltgot".len)) + 1; + plt_got.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$pltgot".len)) + 1; } } - pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf, ctx: anytype) void { - var ilocal = ctx.ilocal; + pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf) void { + var ilocal = plt_got.output_symtab_ctx.ilocal; for (plt_got.symbols.items) |sym_index| { const sym = elf_file.symbol(sym_index); const st_name = @as(u32, @intCast(elf_file.strtab.items.len));