diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2023-10-31 13:27:47 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2023-11-04 09:08:16 +0100 |
| commit | 25c53f08a6add493043a407ce15bc727dc33356d (patch) | |
| tree | 13adbdc0251f44db469f374b82214a5f135eebce /src | |
| parent | f6de3ec963e3a7d96cd4f6c72b0f076f0437c45d (diff) | |
| download | zig-25c53f08a6add493043a407ce15bc727dc33356d.tar.gz zig-25c53f08a6add493043a407ce15bc727dc33356d.zip | |
elf: redo strings management in the linker
* atom names - are stored locally and pulled from defining object's
strtab
* local symbols - same
* global symbols - in principle, we could store them locally, but
for better debugging experience - when things go wrong - we
store the offsets in a global strtab used by the symbol resolver
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/Coff.zig | 16 | ||||
| -rw-r--r-- | src/link/Dwarf.zig | 4 | ||||
| -rw-r--r-- | src/link/Elf.zig | 245 | ||||
| -rw-r--r-- | src/link/Elf/Atom.zig | 7 | ||||
| -rw-r--r-- | src/link/Elf/LinkerDefined.zig | 42 | ||||
| -rw-r--r-- | src/link/Elf/Object.zig | 167 | ||||
| -rw-r--r-- | src/link/Elf/SharedObject.zig | 112 | ||||
| -rw-r--r-- | src/link/Elf/Symbol.zig | 39 | ||||
| -rw-r--r-- | src/link/Elf/ZigObject.zig | 102 | ||||
| -rw-r--r-- | src/link/Elf/eh_frame.zig | 2 | ||||
| -rw-r--r-- | src/link/Elf/file.zig | 85 | ||||
| -rw-r--r-- | src/link/Elf/synthetic_sections.zig | 106 | ||||
| -rw-r--r-- | src/link/MachO.zig | 4 | ||||
| -rw-r--r-- | src/link/MachO/DebugSymbols.zig | 4 | ||||
| -rw-r--r-- | src/link/MachO/zld.zig | 1 | ||||
| -rw-r--r-- | src/link/strtab.zig | 121 |
16 files changed, 438 insertions, 619 deletions
diff --git a/src/link/Coff.zig b/src/link/Coff.zig index fd2415bff5..d34c53e2ea 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -33,10 +33,10 @@ need_got_table: std.AutoHashMapUnmanaged(u32, void) = .{}, locals_free_list: std.ArrayListUnmanaged(u32) = .{}, globals_free_list: std.ArrayListUnmanaged(u32) = .{}, -strtab: StringTable(.strtab) = .{}, +strtab: StringTable = .{}, strtab_offset: ?u32 = null, -temp_strtab: StringTable(.temp_strtab) = .{}, +temp_strtab: StringTable = .{}, got_table: TableSection(SymbolWithLoc) = .{}, @@ -418,7 +418,7 @@ fn populateMissingMetadata(self: *Coff) !void { } if (self.strtab_offset == null) { - const file_size = @as(u32, @intCast(self.strtab.len())); + const file_size = @as(u32, @intCast(self.strtab.buffer.items.len)); self.strtab_offset = self.findFreeSpace(file_size, @alignOf(u32)); // 4bytes aligned seems like a good idea here log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + file_size }); } @@ -2142,7 +2142,7 @@ fn writeStrtab(self: *Coff) !void { if (self.strtab_offset == null) return; const allocated_size = self.allocatedSize(self.strtab_offset.?); - const needed_size = @as(u32, @intCast(self.strtab.len())); + const needed_size = @as(u32, @intCast(self.strtab.buffer.items.len)); if (needed_size > allocated_size) { self.strtab_offset = null; @@ -2154,10 +2154,10 @@ fn writeStrtab(self: *Coff) !void { var buffer = std.ArrayList(u8).init(self.base.allocator); defer buffer.deinit(); try buffer.ensureTotalCapacityPrecise(needed_size); - buffer.appendSliceAssumeCapacity(self.strtab.items()); + buffer.appendSliceAssumeCapacity(self.strtab.buffer.items); // Here, we do a trick in that we do not commit the size of the strtab to strtab buffer, instead // we write the length of the strtab to a temporary buffer that goes to file. - mem.writeInt(u32, buffer.items[0..4], @as(u32, @intCast(self.strtab.len())), .little); + mem.writeInt(u32, buffer.items[0..4], @as(u32, @intCast(self.strtab.buffer.items.len)), .little); try self.base.file.?.pwriteAll(buffer.items, self.strtab_offset.?); } @@ -2325,7 +2325,7 @@ fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 { const end = start + padToIdeal(size); if (self.strtab_offset) |off| { - const tight_size = @as(u32, @intCast(self.strtab.len())); + const tight_size = @as(u32, @intCast(self.strtab.buffer.items.len)); const increased_size = padToIdeal(tight_size); const test_end = off + increased_size; if (end > off and start < test_end) { @@ -2666,7 +2666,7 @@ const InternPool = @import("../InternPool.zig"); const Object = @import("Coff/Object.zig"); const Relocation = @import("Coff/Relocation.zig"); const TableSection = @import("table_section.zig").TableSection; -const StringTable = @import("strtab.zig").StringTable; +const StringTable = @import("StringTable.zig"); const Type = @import("../type.zig").Type; const TypedValue = @import("../TypedValue.zig"); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 82cd6153de..3bfe744443 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -23,7 +23,7 @@ abbrev_table_offset: ?u64 = null, /// TODO replace with InternPool /// Table of debug symbol names. -strtab: StringTable(.strtab) = .{}, +strtab: StringTable = .{}, /// Quick lookup array of all defined source files referenced by at least one Decl. /// They will end up in the DWARF debug_line header as two lists: @@ -2760,6 +2760,6 @@ const LinkFn = File.LinkFn; const LinkerLoad = @import("../codegen.zig").LinkerLoad; const Module = @import("../Module.zig"); const InternPool = @import("../InternPool.zig"); -const StringTable = @import("strtab.zig").StringTable; +const StringTable = @import("StringTable.zig"); const Type = @import("../type.zig").Type; const Value = @import("../value.zig").Value; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 63642d4c6a..b051e05163 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -66,13 +66,15 @@ page_size: u32, default_sym_version: elf.Elf64_Versym, /// .shstrtab buffer -shstrtab: StringTable(.strtab) = .{}, +shstrtab: std.ArrayListUnmanaged(u8) = .{}, +/// .symtab buffer +symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, /// .strtab buffer -strtab: StringTable(.strtab) = .{}, +strtab: std.ArrayListUnmanaged(u8) = .{}, /// Dynamic symbol table. Only populated and emitted when linking dynamically. dynsym: DynsymSection = .{}, /// .dynstrtab buffer -dynstrtab: StringTable(.dynstrtab) = .{}, +dynstrtab: std.ArrayListUnmanaged(u8) = .{}, /// Version symbol table. Only populated and emitted when linking dynamically. versym: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{}, /// .verneed section @@ -156,9 +158,10 @@ start_stop_indexes: std.ArrayListUnmanaged(u32) = .{}, /// An array of symbols parsed across all input files. symbols: std.ArrayListUnmanaged(Symbol) = .{}, symbols_extra: std.ArrayListUnmanaged(u32) = .{}, -resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{}, +resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, + has_text_reloc: bool = false, num_ifunc_dynrelocs: usize = 0, @@ -175,6 +178,10 @@ 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`. +strings: StringTable = .{}, + /// When allocating, the ideal_capacity is calculated by /// actual_capacity + (actual_capacity / ideal_factor) const ideal_factor = 3; @@ -227,13 +234,15 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option // Append null file at index 0 try self.files.append(allocator, .null); // Append null byte to string tables - try self.shstrtab.buffer.append(allocator, 0); - try self.strtab.buffer.append(allocator, 0); + try self.shstrtab.append(allocator, 0); + try self.strtab.append(allocator, 0); // There must always be a null shdr in index 0 _ = try self.addSection(.{ .name = "" }); + // Append null symbol in output symtab + try self.symtab.append(allocator, null_sym); if (!is_obj_or_ar) { - try self.dynstrtab.buffer.append(allocator, 0); + try self.dynstrtab.append(allocator, 0); // Initialize PT_PHDR program header const p_align: u16 = switch (self.ptr_width) { @@ -347,6 +356,7 @@ pub fn deinit(self: *Elf) void { } self.output_sections.deinit(gpa); self.shstrtab.deinit(gpa); + self.symtab.deinit(gpa); self.strtab.deinit(gpa); self.symbols.deinit(gpa); self.symbols_extra.deinit(gpa); @@ -364,6 +374,7 @@ pub fn deinit(self: *Elf) void { 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); self.plt.deinit(gpa); @@ -747,7 +758,7 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { const new_offset = self.findFreeSpace(needed_size, self.page_size); log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{ - self.shstrtab.getAssumeExists(shdr.sh_name), + self.getShString(shdr.sh_name), new_offset, new_offset + existing_size, }); @@ -796,7 +807,7 @@ pub fn growNonAllocSection( const new_offset = self.findFreeSpace(needed_size, min_alignment); log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{ - self.shstrtab.getAssumeExists(shdr.sh_name), + self.getShString(shdr.sh_name), new_offset, new_offset + existing_size, }); @@ -1696,7 +1707,7 @@ fn accessLibPath( /// 6. Re-run symbol resolution on pruned objects and shared objects sets. 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.resolveSymbols(self); + 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); @@ -1705,7 +1716,7 @@ fn resolveSymbols(self: *Elf) void { self.markLive(); // Reset state of all globals after marking live objects. - if (self.zigObjectPtr()) |zig_object| zig_object.resetGlobals(self); + 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); @@ -1767,7 +1778,7 @@ fn resolveSymbols(self: *Elf) void { /// This routine will prune unneeded objects extracted from archives and /// unneeded shared objects. fn markLive(self: *Elf) void { - if (self.zigObjectPtr()) |zig_object| zig_object.markLive(self); + if (self.zigObjectPtr()) |zig_object| zig_object.asFile().markLive(self); for (self.objects.items) |index| { const file_ptr = self.file(index).?; if (file_ptr.isAlive()) file_ptr.markLive(self); @@ -3358,7 +3369,7 @@ fn sortInitFini(self: *Elf) !void { elf.SHT_FINI_ARRAY, => is_init_fini = true, else => { - const name = self.shstrtab.getAssumeExists(shdr.sh_name); + const name = self.getShString(shdr.sh_name); is_ctor_dtor = mem.indexOf(u8, name, ".ctors") != null or mem.indexOf(u8, name, ".dtors") != null; }, } @@ -3520,7 +3531,7 @@ fn sortPhdrs(self: *Elf) error{OutOfMemory}!void { fn shdrRank(self: *Elf, shndx: u16) u8 { const shdr = self.shdrs.items[shndx]; - const name = self.shstrtab.getAssumeExists(shdr.sh_name); + const name = self.getShString(shdr.sh_name); const flags = shdr.sh_flags; switch (shdr.sh_type) { @@ -3801,7 +3812,7 @@ fn updateSectionSizes(self: *Elf) !void { } if (self.dynstrtab_section_index) |index| { - self.shdrs.items[index].sh_size = self.dynstrtab.buffer.items.len; + self.shdrs.items[index].sh_size = self.dynstrtab.items.len; } if (self.versym_section_index) |index| { @@ -3816,26 +3827,8 @@ fn updateSectionSizes(self: *Elf) !void { try self.updateSymtabSize(); } - if (self.strtab_section_index) |index| { - // TODO I don't really this here but we need it to add symbol names from GOT and other synthetic - // sections into .strtab for easier debugging. - if (self.zig_got_section_index) |_| { - try self.zig_got.updateStrtab(self); - } - if (self.got_section_index) |_| { - try self.got.updateStrtab(self); - } - if (self.plt_section_index) |_| { - try self.plt.updateStrtab(self); - } - if (self.plt_got_section_index) |_| { - try self.plt_got.updateStrtab(self); - } - self.shdrs.items[index].sh_size = self.strtab.buffer.items.len; - } - if (self.shstrtab_section_index) |index| { - self.shdrs.items[index].sh_size = self.shstrtab.buffer.items.len; + self.shdrs.items[index].sh_size = self.shstrtab.items.len; } } @@ -4074,7 +4067,7 @@ fn allocateNonAllocSections(self: *Elf) !void { if (self.isDebugSection(@intCast(shndx))) { log.debug("moving {s} from 0x{x} to 0x{x}", .{ - self.shstrtab.getAssumeExists(shdr.sh_name), + self.getShString(shdr.sh_name), shdr.sh_offset, new_offset, }); @@ -4187,7 +4180,7 @@ fn writeAtoms(self: *Elf) !void { const atom_list = self.output_sections.get(@intCast(shndx)) orelse continue; - log.debug("writing atoms in '{s}' section", .{self.shstrtab.getAssumeExists(shdr.sh_name)}); + log.debug("writing atoms in '{s}' section", .{self.getShString(shdr.sh_name)}); // TODO really, really handle debug section separately const base_offset = if (self.isDebugSection(@intCast(shndx))) blk: { @@ -4256,60 +4249,61 @@ fn updateSymtabSize(self: *Elf) !void { var sizes = SymtabSize{}; if (self.zigObjectPtr()) |zig_object| { - zig_object.updateSymtabSize(self); - sizes.nlocals += zig_object.output_symtab_size.nlocals; - sizes.nglobals += zig_object.output_symtab_size.nglobals; + zig_object.asFile().updateSymtabSize(self); + sizes.add(zig_object.output_symtab_size); } for (self.objects.items) |index| { - const object = self.file(index).?.object; - object.updateSymtabSize(self); - sizes.nlocals += object.output_symtab_size.nlocals; - sizes.nglobals += object.output_symtab_size.nglobals; + const file_ptr = self.file(index).?; + file_ptr.updateSymtabSize(self); + sizes.add(file_ptr.object.output_symtab_size); } for (self.shared_objects.items) |index| { - const shared_object = self.file(index).?.shared_object; - shared_object.updateSymtabSize(self); - sizes.nglobals += shared_object.output_symtab_size.nglobals; + const file_ptr = self.file(index).?; + file_ptr.updateSymtabSize(self); + sizes.add(file_ptr.shared_object.output_symtab_size); } if (self.zig_got_section_index) |_| { self.zig_got.updateSymtabSize(self); - sizes.nlocals += self.zig_got.output_symtab_size.nlocals; + sizes.add(self.zig_got.output_symtab_size); } if (self.got_section_index) |_| { self.got.updateSymtabSize(self); - sizes.nlocals += self.got.output_symtab_size.nlocals; + sizes.add(self.got.output_symtab_size); } if (self.plt_section_index) |_| { self.plt.updateSymtabSize(self); - sizes.nlocals += self.plt.output_symtab_size.nlocals; + sizes.add(self.plt.output_symtab_size); } if (self.plt_got_section_index) |_| { self.plt_got.updateSymtabSize(self); - sizes.nlocals += self.plt_got.output_symtab_size.nlocals; + sizes.add(self.plt_got.output_symtab_size); } if (self.linker_defined_index) |index| { - const linker_defined = self.file(index).?.linker_defined; - linker_defined.updateSymtabSize(self); - sizes.nlocals += linker_defined.output_symtab_size.nlocals; + const file_ptr = self.file(index).?; + file_ptr.updateSymtabSize(self); + sizes.add(file_ptr.linker_defined.output_symtab_size); } - const shdr = &self.shdrs.items[self.symtab_section_index.?]; - shdr.sh_info = sizes.nlocals + 1; - shdr.sh_link = self.strtab_section_index.?; + const symtab_shdr = &self.shdrs.items[self.symtab_section_index.?]; + symtab_shdr.sh_info = sizes.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; - shdr.sh_size = needed_size; + symtab_shdr.sh_size = needed_size; + + const strtab = &self.shdrs.items[self.strtab_section_index.?]; + strtab.sh_size = sizes.strsize + 1; } fn writeSyntheticSections(self: *Elf) !void { @@ -4370,7 +4364,7 @@ fn writeSyntheticSections(self: *Elf) !void { if (self.dynstrtab_section_index) |shndx| { const shdr = self.shdrs.items[shndx]; - try self.base.file.?.pwriteAll(self.dynstrtab.buffer.items, shdr.sh_offset); + try self.base.file.?.pwriteAll(self.dynstrtab.items, shdr.sh_offset); } if (self.eh_frame_section_index) |shndx| { @@ -4440,12 +4434,7 @@ fn writeSyntheticSections(self: *Elf) !void { if (self.shstrtab_section_index) |index| { const shdr = self.shdrs.items[index]; - try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shdr.sh_offset); - } - - if (self.strtab_section_index) |index| { - const shdr = self.shdrs.items[index]; - try self.base.file.?.pwriteAll(self.strtab.buffer.items, shdr.sh_offset); + try self.base.file.?.pwriteAll(self.shstrtab.items, shdr.sh_offset); } if (self.symtab_section_index) |_| { @@ -4455,77 +4444,83 @@ fn writeSyntheticSections(self: *Elf) !void { fn writeSymtab(self: *Elf) !void { const gpa = self.base.allocator; - const shdr = &self.shdrs.items[self.symtab_section_index.?]; + const symtab_shdr = self.shdrs.items[self.symtab_section_index.?]; + const strtab_shdr = self.shdrs.items[self.strtab_section_index.?]; const sym_size: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Sym), .p64 => @sizeOf(elf.Elf64_Sym), }; - const nsyms = math.cast(usize, @divExact(shdr.sh_size, sym_size)) orelse return error.Overflow; + const nsyms = math.cast(usize, @divExact(symtab_shdr.sh_size, sym_size)) orelse return error.Overflow; + + log.debug("writing {d} symbols at 0x{x}", .{ nsyms, symtab_shdr.sh_offset }); - log.debug("writing {d} symbols at 0x{x}", .{ nsyms, shdr.sh_offset }); + try self.symtab.resize(gpa, nsyms); + try self.strtab.ensureUnusedCapacity(gpa, strtab_shdr.sh_size - 1); - const symtab = try gpa.alloc(elf.Elf64_Sym, nsyms); - defer gpa.free(symtab); - symtab[0] = null_sym; + const Ctx = struct { + ilocal: usize, + iglobal: usize, - var ctx: struct { ilocal: usize, iglobal: usize, symtab: []elf.Elf64_Sym } = .{ + fn incr(this: *@This(), ss: SymtabSize) void { + this.ilocal += ss.nlocals; + this.iglobal += ss.nglobals; + } + }; + var ctx: Ctx = .{ .ilocal = 1, - .iglobal = shdr.sh_info, - .symtab = symtab, + .iglobal = symtab_shdr.sh_info, }; if (self.zigObjectPtr()) |zig_object| { - zig_object.writeSymtab(self, ctx); - ctx.ilocal += zig_object.output_symtab_size.nlocals; - ctx.iglobal += zig_object.output_symtab_size.nglobals; + zig_object.asFile().writeSymtab(self, ctx); + ctx.incr(zig_object.output_symtab_size); } for (self.objects.items) |index| { - const object = self.file(index).?.object; - object.writeSymtab(self, ctx); - ctx.ilocal += object.output_symtab_size.nlocals; - ctx.iglobal += object.output_symtab_size.nglobals; + const file_ptr = self.file(index).?; + file_ptr.writeSymtab(self, ctx); + ctx.incr(file_ptr.object.output_symtab_size); } for (self.shared_objects.items) |index| { - const shared_object = self.file(index).?.shared_object; - shared_object.writeSymtab(self, ctx); - ctx.iglobal += shared_object.output_symtab_size.nglobals; + const file_ptr = self.file(index).?; + file_ptr.writeSymtab(self, ctx); + ctx.incr(file_ptr.shared_object.output_symtab_size); } if (self.zig_got_section_index) |_| { - try self.zig_got.writeSymtab(self, ctx); - ctx.ilocal += self.zig_got.output_symtab_size.nlocals; + self.zig_got.writeSymtab(self, ctx); + ctx.incr(self.zig_got.output_symtab_size); } if (self.got_section_index) |_| { - try self.got.writeSymtab(self, ctx); - ctx.ilocal += self.got.output_symtab_size.nlocals; + self.got.writeSymtab(self, ctx); + ctx.incr(self.got.output_symtab_size); } if (self.plt_section_index) |_| { - try self.plt.writeSymtab(self, ctx); - ctx.ilocal += self.plt.output_symtab_size.nlocals; + self.plt.writeSymtab(self, ctx); + ctx.incr(self.plt.output_symtab_size); } if (self.plt_got_section_index) |_| { - try self.plt_got.writeSymtab(self, ctx); - ctx.ilocal += self.plt_got.output_symtab_size.nlocals; + self.plt_got.writeSymtab(self, ctx); + ctx.incr(self.plt_got.output_symtab_size); } if (self.linker_defined_index) |index| { - const linker_defined = self.file(index).?.linker_defined; - linker_defined.writeSymtab(self, ctx); - ctx.ilocal += linker_defined.output_symtab_size.nlocals; + const file_ptr = self.file(index).?; + file_ptr.writeSymtab(self, ctx); + ctx.incr(file_ptr.linker_defined.output_symtab_size); } const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); switch (self.ptr_width) { .p32 => { - const buf = try gpa.alloc(elf.Elf32_Sym, symtab.len); + const buf = try gpa.alloc(elf.Elf32_Sym, self.symtab.items.len); defer gpa.free(buf); - for (buf, symtab) |*out, sym| { + for (buf, self.symtab.items) |*out, sym| { out.* = .{ .st_name = sym.st_name, .st_info = sym.st_info, @@ -4536,15 +4531,17 @@ fn writeSymtab(self: *Elf) !void { }; if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out); } - try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), shdr.sh_offset); + try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), symtab_shdr.sh_offset); }, .p64 => { if (foreign_endian) { - for (symtab) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym); + for (self.symtab.items) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym); } - try self.base.file.?.pwriteAll(mem.sliceAsBytes(symtab), shdr.sh_offset); + try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), symtab_shdr.sh_offset); }, } + + try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset); } /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF. @@ -4925,7 +4922,7 @@ pub fn addSection(self: *Elf, opts: AddSectionOpts) !u16 { const index = @as(u16, @intCast(self.shdrs.items.len)); const shdr = try self.shdrs.addOne(gpa); shdr.* = .{ - .sh_name = try self.shstrtab.insert(gpa, opts.name), + .sh_name = try self.insertShString(opts.name), .sh_type = opts.type, .sh_flags = opts.flags, .sh_addr = 0, @@ -4941,7 +4938,7 @@ pub fn addSection(self: *Elf, opts: AddSectionOpts) !u16 { pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 { for (self.shdrs.items, 0..) |*shdr, i| { - const this_name = self.shstrtab.getAssumeExists(shdr.sh_name); + const this_name = self.getShString(shdr.sh_name); if (mem.eql(u8, this_name, name)) return @as(u16, @intCast(i)); } else return null; } @@ -5114,13 +5111,15 @@ const GetOrPutGlobalResult = struct { index: Symbol.Index, }; -pub fn getOrPutGlobal(self: *Elf, name_off: u32) !GetOrPutGlobalResult { +pub fn getOrPutGlobal(self: *Elf, name: []const u8) !GetOrPutGlobalResult { const gpa = self.base.allocator; + const name_off = try self.strings.insert(gpa, name); const gop = try self.resolver.getOrPut(gpa, name_off); if (!gop.found_existing) { const index = try self.addSymbol(); const global = self.symbol(index); global.name_offset = name_off; + global.flags.global = true; gop.value_ptr.* = index; } return .{ @@ -5130,7 +5129,7 @@ pub fn getOrPutGlobal(self: *Elf, name_off: u32) !GetOrPutGlobalResult { } pub fn globalByName(self: *Elf, name: []const u8) ?Symbol.Index { - const name_off = self.strtab.getOffset(name) orelse return null; + const name_off = self.strings.getOffset(name) orelse return null; return self.resolver.get(name_off); } @@ -5148,8 +5147,9 @@ const GetOrCreateComdatGroupOwnerResult = struct { index: ComdatGroupOwner.Index, }; -pub fn getOrCreateComdatGroupOwner(self: *Elf, off: u32) !GetOrCreateComdatGroupOwnerResult { +pub fn getOrCreateComdatGroupOwner(self: *Elf, name: [:0]const u8) !GetOrCreateComdatGroupOwnerResult { const gpa = self.base.allocator; + const off = try self.strings.insert(gpa, name); const gop = try self.comdat_groups_table.getOrPut(gpa, off); if (!gop.found_existing) { const index = @as(ComdatGroupOwner.Index, @intCast(self.comdat_groups_owners.items.len)); @@ -5239,6 +5239,30 @@ fn addErrorWithNotesAssumeCapacity(self: *Elf, note_count: usize) error{OutOfMem return .{ .index = index }; } +pub fn getShString(self: Elf, off: u32) [:0]const u8 { + assert(off < self.shstrtab.items.len); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.shstrtab.items.ptr + off)), 0); +} + +pub fn insertShString(self: *Elf, name: [:0]const u8) error{OutOfMemory}!u32 { + const off = @as(u32, @intCast(self.shstrtab.items.len)); + try self.shstrtab.ensureUnusedCapacity(self.base.allocator, name.len + 1); + self.shstrtab.writer(self.base.allocator).print("{s}\x00", .{name}) catch unreachable; + return off; +} + +pub fn getDynString(self: Elf, off: u32) [:0]const u8 { + assert(off < self.dynstrtab.items.len); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.dynstrtab.items.ptr + off)), 0); +} + +pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 { + const off = @as(u32, @intCast(self.dynstrtab.items.len)); + try self.dynstrtab.ensureUnusedCapacity(self.base.allocator, name.len + 1); + self.dynstrtab.writer(self.base.allocator).print("{s}\x00", .{name}) catch unreachable; + return off; +} + fn reportUndefined(self: *Elf, undefs: anytype) !void { const gpa = self.base.allocator; const max_notes = 4; @@ -5340,8 +5364,8 @@ fn formatShdr( _ = unused_fmt_string; const shdr = ctx.shdr; try writer.print("{s} : @{x} ({x}) : align({x}) : size({x})", .{ - ctx.elf_file.shstrtab.getAssumeExists(shdr.sh_name), shdr.sh_offset, - shdr.sh_addr, shdr.sh_addralign, + ctx.elf_file.getShString(shdr.sh_name), shdr.sh_offset, + shdr.sh_addr, shdr.sh_addralign, shdr.sh_size, }); } @@ -5516,6 +5540,13 @@ pub const ComdatGroup = struct { pub const SymtabSize = struct { 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{ @@ -5621,7 +5652,7 @@ const PltSection = synthetic_sections.PltSection; const PltGotSection = synthetic_sections.PltGotSection; const SharedObject = @import("Elf/SharedObject.zig"); const Symbol = @import("Elf/Symbol.zig"); -const StringTable = @import("strtab.zig").StringTable; +const StringTable = @import("StringTable.zig"); const TypedValue = @import("../TypedValue.zig"); const VerneedSection = synthetic_sections.VerneedSection; const ZigGotSection = synthetic_sections.ZigGotSection; diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 7527b61988..6ba2325638 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -42,7 +42,10 @@ next_index: Index = 0, pub const Alignment = @import("../../InternPool.zig").Alignment; pub fn name(self: Atom, elf_file: *Elf) []const u8 { - return elf_file.strtab.getAssumeExists(self.name_offset); + const file_ptr = self.file(elf_file).?; + return switch (file_ptr) { + inline else => |x| x.getString(self.name_offset), + }; } pub fn file(self: Atom, elf_file: *Elf) ?File { @@ -692,7 +695,7 @@ fn reportUndefined( ) !void { const rel_esym = switch (self.file(elf_file).?) { .zig_object => |x| x.elfSym(rel.r_sym()).*, - .object => |x| x.symtab[rel.r_sym()], + .object => |x| x.symtab.items[rel.r_sym()], else => unreachable, }; const esym = sym.elfSym(elf_file); diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig index 0c6666e8bb..49e0e8f71d 100644 --- a/src/link/Elf/LinkerDefined.zig +++ b/src/link/Elf/LinkerDefined.zig @@ -1,11 +1,13 @@ index: File.Index, symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, +strtab: std.ArrayListUnmanaged(u8) = .{}, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, output_symtab_size: Elf.SymtabSize = .{}, pub fn deinit(self: *LinkerDefined, allocator: Allocator) void { self.symtab.deinit(allocator); + self.strtab.deinit(allocator); self.symbols.deinit(allocator); } @@ -13,16 +15,17 @@ pub fn addGlobal(self: *LinkerDefined, name: [:0]const u8, elf_file: *Elf) !u32 const gpa = elf_file.base.allocator; try self.symtab.ensureUnusedCapacity(gpa, 1); try self.symbols.ensureUnusedCapacity(gpa, 1); + const name_off = @as(u32, @intCast(self.strtab.items.len)); + try self.strtab.writer(gpa).print("{s}\x00", .{name}); self.symtab.appendAssumeCapacity(.{ - .st_name = try elf_file.strtab.insert(gpa, name), + .st_name = name_off, .st_info = elf.STB_GLOBAL << 4, .st_other = @intFromEnum(elf.STV.HIDDEN), .st_shndx = elf.SHN_ABS, .st_value = 0, .st_size = 0, }); - const off = try elf_file.strtab.insert(gpa, name); - const gop = try elf_file.getOrPutGlobal(off); + const gop = try elf_file.getOrPutGlobal(name); self.symbols.addOneAssumeCapacity().* = gop.index; return gop.index; } @@ -37,7 +40,6 @@ 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.name_offset = global.name_offset; global.atom_index = 0; global.file_index = self.index; global.esym_index = sym_idx; @@ -46,26 +48,6 @@ pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void { } } -pub fn updateSymtabSize(self: *LinkerDefined, elf_file: *Elf) void { - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - if (global.file(elf_file)) |file| if (file.index() != self.index) continue; - global.flags.output_symtab = true; - self.output_symtab_size.nlocals += 1; - } -} - -pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf, ctx: anytype) void { - var ilocal = ctx.ilocal; - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - if (global.file(elf_file)) |file| if (file.index() != self.index) continue; - if (!global.flags.output_symtab) continue; - global.setOutputSym(elf_file, &ctx.symtab[ilocal]); - ilocal += 1; - } -} - pub fn globals(self: *LinkerDefined) []const Symbol.Index { return self.symbols.items; } @@ -74,6 +56,11 @@ pub fn asFile(self: *LinkerDefined) File { return .{ .linker_defined = self }; } +pub fn getString(self: LinkerDefined, off: u32) [:0]const u8 { + assert(off < self.strtab.items.len); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); +} + pub fn fmtSymtab(self: *LinkerDefined, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { return .{ .data = .{ .self = self, @@ -101,12 +88,13 @@ fn formatSymtab( } } -const std = @import("std"); +const assert = std.debug.assert; const elf = std.elf; +const mem = std.mem; +const std = @import("std"); -const Allocator = std.mem.Allocator; +const Allocator = mem.Allocator; const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; const LinkerDefined = @This(); -// const Object = @import("Object.zig"); const Symbol = @import("Symbol.zig"); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index e21d6f161c..968ff84853 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -5,11 +5,10 @@ index: File.Index, header: ?elf.Elf64_Ehdr = null, shdrs: std.ArrayListUnmanaged(ElfShdr) = .{}, -strings: StringTable(.object_strings) = .{}, -symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{}, -strtab: []const u8 = &[0]u8{}, -first_global: ?Symbol.Index = null, +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) = .{}, @@ -39,7 +38,8 @@ pub fn deinit(self: *Object, allocator: Allocator) void { allocator.free(self.path); allocator.free(self.data); self.shdrs.deinit(allocator); - self.strings.deinit(allocator); + self.symtab.deinit(allocator); + self.strtab.deinit(allocator); self.symbols.deinit(allocator); self.atoms.deinit(allocator); self.comdat_groups.deinit(allocator); @@ -68,7 +68,7 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr)); } - try self.strings.buffer.appendSlice(gpa, self.shdrContents(self.header.?.e_shstrndx)); + try self.strtab.appendSlice(gpa, self.shdrContents(self.header.?.e_shstrndx)); const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { elf.SHT_SYMTAB => break @as(u16, @intCast(i)), @@ -79,10 +79,22 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { const shdr = shdrs[index]; self.first_global = shdr.sh_info; - const symtab = self.shdrContents(index); - const nsyms = @divExact(symtab.len, @sizeOf(elf.Elf64_Sym)); - self.symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms]; - self.strtab = self.shdrContents(@as(u16, @intCast(shdr.sh_link))); + const raw_symtab = self.shdrContents(index); + const nsyms = @divExact(raw_symtab.len, @sizeOf(elf.Elf64_Sym)); + const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms]; + + const strtab_bias = @as(u32, @intCast(self.strtab.items.len)); + try self.strtab.appendSlice(gpa, self.shdrContents(@as(u16, @intCast(shdr.sh_link)))); + + try self.symtab.ensureUnusedCapacity(gpa, symtab.len); + for (symtab) |sym| { + const out_sym = self.symtab.addOneAssumeCapacity(); + out_sym.* = sym; + out_sym.st_name = if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION) + shdrs[sym.st_shndx].sh_name + else + sym.st_name + strtab_bias; + } } try self.initAtoms(elf_file); @@ -108,16 +120,16 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void { switch (shdr.sh_type) { elf.SHT_GROUP => { - if (shdr.sh_info >= self.symtab.len) { + if (shdr.sh_info >= self.symtab.items.len) { // TODO convert into an error log.debug("{}: invalid symbol index in sh_info", .{self.fmtPath()}); continue; } - const group_info_sym = self.symtab[shdr.sh_info]; + const group_info_sym = self.symtab.items[shdr.sh_info]; 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.strings.getAssumeExists(sym_shdr.sh_name); + break :blk self.getString(sym_shdr.sh_name); } break :blk self.getString(group_info_sym.st_name); }; @@ -133,11 +145,8 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void { continue; } - // Note the assumption about a global strtab used here to disambiguate common - // COMDAT owners. const gpa = elf_file.base.allocator; - const group_signature_off = try elf_file.strtab.insert(gpa, group_signature); - const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature_off); + 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); comdat_group.* = .{ @@ -157,10 +166,9 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void { => {}, else => { - const name = self.strings.getAssumeExists(shdr.sh_name); const shndx = @as(u16, @intCast(i)); if (self.skipShdr(shndx, elf_file)) continue; - try self.addAtom(shdr, shndx, name, elf_file); + try self.addAtom(shdr, shndx, elf_file); }, } } @@ -177,17 +185,11 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void { }; } -fn addAtom( - self: *Object, - shdr: ElfShdr, - shndx: u16, - name: [:0]const u8, - elf_file: *Elf, -) error{OutOfMemory}!void { +fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOfMemory}!void { const atom_index = try elf_file.addAtom(); const atom = elf_file.atom(atom_index).?; atom.atom_index = atom_index; - atom.name_offset = try elf_file.strtab.insert(elf_file.base.allocator, name); + atom.name_offset = shdr.sh_name; atom.file_index = self.index; atom.input_section_index = shndx; self.atoms.items[shndx] = atom_index; @@ -205,7 +207,7 @@ fn addAtom( fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMemory}!u16 { const name = blk: { - const name = self.strings.getAssumeExists(shdr.sh_name); + const name = self.getString(shdr.sh_name); if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name; const sh_name_prefixes: []const [:0]const u8 = &.{ ".text", ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss", @@ -248,7 +250,7 @@ fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMem fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool { const shdr = self.shdrs.items[index]; - const name = self.strings.getAssumeExists(shdr.sh_name); + const name = self.getString(shdr.sh_name); const ignore = blk: { if (mem.startsWith(u8, name, ".note")) break :blk true; if (mem.startsWith(u8, name, ".comment")) break :blk true; @@ -262,33 +264,24 @@ fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool { fn initSymtab(self: *Object, elf_file: *Elf) !void { const gpa = elf_file.base.allocator; - const first_global = self.first_global orelse self.symtab.len; - const shdrs = self.shdrs.items; + const first_global = self.first_global orelse self.symtab.items.len; - try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.len); + try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.items.len); - for (self.symtab[0..first_global], 0..) |sym, i| { + for (self.symtab.items[0..first_global], 0..) |sym, i| { const index = try elf_file.addSymbol(); self.symbols.appendAssumeCapacity(index); const sym_ptr = elf_file.symbol(index); - const name = blk: { - if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION) { - const shdr = shdrs[sym.st_shndx]; - break :blk self.strings.getAssumeExists(shdr.sh_name); - } - break :blk self.getString(sym.st_name); - }; sym_ptr.value = sym.st_value; - sym_ptr.name_offset = try elf_file.strtab.insert(gpa, name); + 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; } - for (self.symtab[first_global..]) |sym| { + for (self.symtab.items[first_global..]) |sym| { const name = self.getString(sym.st_name); - const off = try elf_file.strtab.insert(gpa, name); - const gop = try elf_file.getOrPutGlobal(off); + const gop = try elf_file.getOrPutGlobal(name); self.symbols.addOneAssumeCapacity().* = gop.index; } } @@ -437,7 +430,7 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void { const first_global = self.first_global orelse return; for (self.globals(), 0..) |index, i| { const esym_index = @as(Symbol.Index, @intCast(first_global + i)); - const esym = self.symtab[esym_index]; + const esym = self.symtab.items[esym_index]; if (esym.st_shndx == elf.SHN_UNDEF) continue; @@ -467,7 +460,7 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void { const first_global = self.first_global orelse return; for (self.globals(), 0..) |index, i| { const esym_index = @as(u32, @intCast(first_global + i)); - const esym = self.symtab[esym_index]; + const esym = self.symtab.items[esym_index]; if (esym.st_shndx != elf.SHN_UNDEF) continue; const global = elf_file.symbol(index); @@ -491,20 +484,11 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void { } } -pub fn resetGlobals(self: *Object, elf_file: *Elf) void { - for (self.globals()) |index| { - const global = elf_file.symbol(index); - const off = global.name_offset; - global.* = .{}; - global.name_offset = off; - } -} - pub fn markLive(self: *Object, elf_file: *Elf) void { const first_global = self.first_global orelse return; for (self.globals(), 0..) |index, i| { const sym_idx = first_global + i; - const sym = self.symtab[sym_idx]; + const sym = self.symtab.items[sym_idx]; if (sym.st_bind() == elf.STB_WEAK) continue; const global = elf_file.symbol(index); @@ -531,7 +515,7 @@ pub fn checkDuplicates(self: *Object, elf_file: *Elf) void { const first_global = self.first_global orelse return; for (self.globals(), 0..) |index, i| { const sym_idx = @as(u32, @intCast(first_global + i)); - const this_sym = self.symtab[sym_idx]; + const this_sym = self.symtab.items[sym_idx]; const global = elf_file.symbol(index); const global_file = global.getFile(elf_file) orelse continue; @@ -560,7 +544,7 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { const first_global = self.first_global orelse return; for (self.globals(), 0..) |index, i| { const sym_idx = @as(u32, @intCast(first_global + i)); - const this_sym = self.symtab[sym_idx]; + const this_sym = self.symtab.items[sym_idx]; if (this_sym.st_shndx != elf.SHN_COMMON) continue; const global = elf_file.symbol(index); @@ -584,8 +568,10 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { 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 = try elf_file.strtab.insert(gpa, name); + atom.name_offset = name_offset; atom.file_index = self.index; atom.size = this_sym.st_size; const alignment = this_sym.st_value; @@ -597,7 +583,7 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { const shdr = try self.shdrs.addOne(gpa); const sh_size = math.cast(usize, this_sym.st_size) orelse return error.Overflow; shdr.* = .{ - .sh_name = try self.strings.insert(gpa, name), + .sh_name = name_offset, .sh_type = elf.SHT_NOBITS, .sh_flags = sh_flags, .sh_addr = 0, @@ -665,56 +651,6 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void { } } -pub fn updateSymtabSize(self: *Object, 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; - const esym = local.elfSym(elf_file); - switch (esym.st_type()) { - elf.STT_SECTION, elf.STT_NOTYPE => continue, - else => {}, - } - local.flags.output_symtab = true; - self.output_symtab_size.nlocals += 1; - } - - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - if (global.file(elf_file)) |file| if (file.index() != self.index) continue; - if (global.atom(elf_file)) |atom| if (!atom.flags.alive) continue; - global.flags.output_symtab = true; - if (global.isLocal()) { - self.output_symtab_size.nlocals += 1; - } else { - self.output_symtab_size.nglobals += 1; - } - } -} - -pub fn writeSymtab(self: *Object, elf_file: *Elf, ctx: anytype) void { - var ilocal = ctx.ilocal; - for (self.locals()) |local_index| { - const local = elf_file.symbol(local_index); - if (!local.flags.output_symtab) continue; - local.setOutputSym(elf_file, &ctx.symtab[ilocal]); - ilocal += 1; - } - - var iglobal = ctx.iglobal; - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - if (global.file(elf_file)) |file| if (file.index() != self.index) continue; - if (!global.flags.output_symtab) continue; - if (global.isLocal()) { - global.setOutputSym(elf_file, &ctx.symtab[ilocal]); - ilocal += 1; - } else { - global.setOutputSym(elf_file, &ctx.symtab[iglobal]); - iglobal += 1; - } - } -} - pub fn locals(self: Object) []const Symbol.Index { const end = self.first_global orelse self.symbols.items.len; return self.symbols.items[0..end]; @@ -760,11 +696,6 @@ pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index) } else return gpa.dupe(u8, data); } -fn getString(self: *Object, off: u32) [:0]const u8 { - assert(off < self.strtab.len); - return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0); -} - pub fn comdatGroupMembers(self: *Object, index: u16) []align(1) const u32 { const raw = self.shdrContents(index); const nmembers = @divExact(raw.len, @sizeOf(u32)); @@ -782,6 +713,11 @@ pub fn getRelocs(self: *Object, shndx: u32) []align(1) const elf.Elf64_Rela { return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num]; } +pub fn getString(self: Object, off: u32) [:0]const u8 { + assert(off < self.strtab.items.len); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); +} + pub fn format( self: *Object, comptime unused_fmt_string: []const u8, @@ -991,6 +927,5 @@ const Cie = eh_frame.Cie; const Elf = @import("../Elf.zig"); const Fde = eh_frame.Fde; const File = @import("file.zig").File; -const StringTable = @import("../strtab.zig").StringTable; const Symbol = @import("Symbol.zig"); const Alignment = Atom.Alignment; diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig index 710c025f34..0924d3c761 100644 --- a/src/link/Elf/SharedObject.zig +++ b/src/link/Elf/SharedObject.zig @@ -4,19 +4,20 @@ index: File.Index, header: ?elf.Elf64_Ehdr = null, shdrs: std.ArrayListUnmanaged(ElfShdr) = .{}, -symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{}, -strtab: []const u8 = &[0]u8{}, + +symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, +strtab: std.ArrayListUnmanaged(u8) = .{}, /// Version symtab contains version strings of the symbols if present. versyms: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{}, verstrings: std.ArrayListUnmanaged(u32) = .{}, +symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +aliases: ?std.ArrayListUnmanaged(u32) = null, +dynsym_sect_index: ?u16 = null, dynamic_sect_index: ?u16 = null, versym_sect_index: ?u16 = null, verdef_sect_index: ?u16 = null, -symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, -aliases: ?std.ArrayListUnmanaged(u32) = null, - needed: bool, alive: bool, @@ -36,6 +37,8 @@ pub fn isSharedObject(path: []const u8) !bool { pub fn deinit(self: *SharedObject, allocator: Allocator) void { allocator.free(self.path); allocator.free(self.data); + self.symtab.deinit(allocator); + self.strtab.deinit(allocator); self.versyms.deinit(allocator); self.verstrings.deinit(allocator); self.symbols.deinit(allocator); @@ -51,7 +54,6 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void { self.header = try reader.readStruct(elf.Elf64_Ehdr); const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow; - var dynsym_index: ?u16 = null; const shdrs = @as( [*]align(1) const elf.Elf64_Shdr, @ptrCast(self.data.ptr + shoff), @@ -61,7 +63,7 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void { for (shdrs, 0..) |shdr, i| { self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr)); switch (shdr.sh_type) { - elf.SHT_DYNSYM => dynsym_index = @as(u16, @intCast(i)), + elf.SHT_DYNSYM => self.dynsym_sect_index = @as(u16, @intCast(i)), elf.SHT_DYNAMIC => self.dynamic_sect_index = @as(u16, @intCast(i)), elf.SHT_GNU_VERSYM => self.versym_sect_index = @as(u16, @intCast(i)), elf.SHT_GNU_VERDEF => self.verdef_sect_index = @as(u16, @intCast(i)), @@ -69,20 +71,13 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void { } } - if (dynsym_index) |index| { - const shdr = self.shdrs.items[index]; - const symtab = self.shdrContents(index); - const nsyms = @divExact(symtab.len, @sizeOf(elf.Elf64_Sym)); - self.symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms]; - self.strtab = self.shdrContents(@as(u16, @intCast(shdr.sh_link))); - } - try self.parseVersions(elf_file); try self.initSymtab(elf_file); } fn parseVersions(self: *SharedObject, elf_file: *Elf) !void { const gpa = elf_file.base.allocator; + const symtab = self.getSymtabRaw(); try self.verstrings.resize(gpa, 2); self.verstrings.items[elf.VER_NDX_LOCAL] = 0; @@ -107,7 +102,7 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void { } } - try self.versyms.ensureTotalCapacityPrecise(gpa, self.symtab.len); + try self.versyms.ensureTotalCapacityPrecise(gpa, symtab.len); if (self.versym_sect_index) |shndx| { const versyms_raw = self.shdrContents(shndx); @@ -120,30 +115,39 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void { ver; self.versyms.appendAssumeCapacity(normalized_ver); } - } else for (0..self.symtab.len) |_| { + } else for (0..symtab.len) |_| { self.versyms.appendAssumeCapacity(elf.VER_NDX_GLOBAL); } } fn initSymtab(self: *SharedObject, elf_file: *Elf) !void { const gpa = elf_file.base.allocator; + const symtab = self.getSymtabRaw(); + const strtab = self.getStrtabRaw(); - try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.len); + try self.strtab.appendSlice(gpa, strtab); + try self.symtab.ensureTotalCapacityPrecise(gpa, symtab.len); + try self.symbols.ensureTotalCapacityPrecise(gpa, symtab.len); - for (self.symtab, 0..) |sym, i| { + for (symtab, 0..) |sym, i| { const hidden = self.versyms.items[i] & elf.VERSYM_HIDDEN != 0; const name = self.getString(sym.st_name); // We need to garble up the name so that we don't pick this symbol // during symbol resolution. Thank you GNU! - const off = if (hidden) blk: { - const full_name = try std.fmt.allocPrint(gpa, "{s}@{s}", .{ + const name_off = if (hidden) blk: { + const mangled = try std.fmt.allocPrint(gpa, "{s}@{s}", .{ name, self.versionString(self.versyms.items[i]), }); - defer gpa.free(full_name); - break :blk try elf_file.strtab.insert(gpa, full_name); - } else try elf_file.strtab.insert(gpa, name); - const gop = try elf_file.getOrPutGlobal(off); + defer gpa.free(mangled); + const name_off = @as(u32, @intCast(self.strtab.items.len)); + try self.strtab.writer(gpa).print("{s}\x00", .{mangled}); + break :blk name_off; + } else sym.st_name; + const out_sym = self.symtab.addOneAssumeCapacity(); + out_sym.* = sym; + out_sym.st_name = name_off; + const gop = try elf_file.getOrPutGlobal(self.getString(name_off)); self.symbols.addOneAssumeCapacity().* = gop.index; } } @@ -151,7 +155,7 @@ fn initSymtab(self: *SharedObject, elf_file: *Elf) !void { pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) void { for (self.globals(), 0..) |index, i| { const esym_index = @as(u32, @intCast(i)); - const this_sym = self.symtab[esym_index]; + const this_sym = self.symtab.items[esym_index]; if (this_sym.st_shndx == elf.SHN_UNDEF) continue; @@ -166,18 +170,9 @@ pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) void { } } -pub fn resetGlobals(self: *SharedObject, elf_file: *Elf) void { - for (self.globals()) |index| { - const global = elf_file.symbol(index); - const off = global.name_offset; - global.* = .{}; - global.name_offset = off; - } -} - pub fn markLive(self: *SharedObject, elf_file: *Elf) void { for (self.globals(), 0..) |index, i| { - const sym = self.symtab[i]; + const sym = self.symtab.items[i]; if (sym.st_shndx != elf.SHN_UNDEF) continue; const global = elf_file.symbol(index); @@ -193,27 +188,6 @@ pub fn markLive(self: *SharedObject, elf_file: *Elf) void { } } -pub fn updateSymtabSize(self: *SharedObject, elf_file: *Elf) void { - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - if (global.file(elf_file)) |file| if (file.index() != self.index) continue; - if (global.isLocal()) continue; - global.flags.output_symtab = true; - self.output_symtab_size.nglobals += 1; - } -} - -pub fn writeSymtab(self: *SharedObject, elf_file: *Elf, ctx: anytype) void { - var iglobal = ctx.iglobal; - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - if (global.file(elf_file)) |file| if (file.index() != self.index) continue; - if (!global.flags.output_symtab) continue; - global.setOutputSym(elf_file, &ctx.symtab[iglobal]); - iglobal += 1; - } -} - pub fn globals(self: SharedObject) []const Symbol.Index { return self.symbols.items; } @@ -223,11 +197,6 @@ pub fn shdrContents(self: SharedObject, index: u16) []const u8 { return self.data[shdr.sh_offset..][0..shdr.sh_size]; } -pub fn getString(self: SharedObject, off: u32) [:0]const u8 { - assert(off < self.strtab.len); - return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0); -} - pub fn versionString(self: SharedObject, index: elf.Elf64_Versym) [:0]const u8 { const off = self.verstrings.items[index & elf.VERSYM_VERSION]; return self.getString(off); @@ -309,6 +278,25 @@ pub fn symbolAliases(self: *SharedObject, index: u32, elf_file: *Elf) []const u3 return aliases.items[start..end]; } +pub fn getString(self: SharedObject, off: u32) [:0]const u8 { + assert(off < self.strtab.items.len); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); +} + +pub fn getSymtabRaw(self: SharedObject) []align(1) const elf.Elf64_Sym { + const index = self.dynsym_sect_index orelse return &[0]elf.Elf64_Sym{}; + const raw_symtab = self.shdrContents(index); + const nsyms = @divExact(raw_symtab.len, @sizeOf(elf.Elf64_Sym)); + const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms]; + return symtab; +} + +pub fn getStrtabRaw(self: SharedObject) []const u8 { + const index = self.dynsym_sect_index orelse return &[0]u8{}; + const shdr = self.shdrs.items[index]; + return self.shdrContents(@as(u16, @intCast(shdr.sh_link))); +} + pub fn format( self: SharedObject, comptime unused_fmt_string: []const u8, diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index f6a6e91898..b75c458e68 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -58,7 +58,11 @@ pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 { } pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 { - return elf_file.strtab.getAssumeExists(symbol.name_offset); + if (symbol.flags.global) return elf_file.strings.getAssumeExists(symbol.name_offset); + const file_ptr = symbol.file(elf_file).?; + return switch (file_ptr) { + inline else => |x| x.getString(symbol.name_offset), + }; } pub fn atom(symbol: Symbol, elf_file: *Elf) ?*Atom { @@ -71,11 +75,10 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File { pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { const file_ptr = symbol.file(elf_file).?; - switch (file_ptr) { - .zig_object => |x| return x.elfSym(symbol.esym_index).*, - .linker_defined => |x| return x.symtab.items[symbol.esym_index], - inline else => |x| return x.symtab[symbol.esym_index], - } + return switch (file_ptr) { + .zig_object => |x| x.elfSym(symbol.esym_index).*, + inline else => |x| x.symtab.items[symbol.esym_index], + }; } pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { @@ -201,10 +204,7 @@ pub fn setExtra(symbol: Symbol, extras: Extra, elf_file: *Elf) void { } pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { - const file_ptr = symbol.file(elf_file) orelse { - out.* = Elf.null_sym; - return; - }; + const file_ptr = symbol.file(elf_file).?; const esym = symbol.elfSym(elf_file); const st_type = symbol.type(elf_file); const st_bind: u8 = blk: { @@ -232,14 +232,11 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { break :blk symbol.value - elf_file.tlsAddress(); break :blk symbol.value; }; - out.* = .{ - .st_name = symbol.name_offset, - .st_info = (st_bind << 4) | st_type, - .st_other = esym.st_other, - .st_shndx = st_shndx, - .st_value = st_value, - .st_size = esym.st_size, - }; + out.st_info = (st_bind << 4) | st_type; + out.st_other = esym.st_other; + out.st_shndx = st_shndx; + out.st_value = st_value; + out.st_size = esym.st_size; } pub fn format( @@ -340,6 +337,12 @@ pub const Flags = packed struct { /// Whether this symbol is weak. weak: bool = false, + /// Whether the symbol has its name interned in global symbol + /// resolver table. + /// This happens for any symbol that is considered a global + /// symbol, but is not necessarily an import or export. + global: bool = false, + /// Whether the symbol makes into the output symtab. output_symtab: bool = false, diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index dff6f796fa..f5162dbc69 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -9,6 +9,7 @@ index: File.Index, local_esyms: std.MultiArrayList(ElfSym) = .{}, global_esyms: std.MultiArrayList(ElfSym) = .{}, +strtab: std.ArrayListUnmanaged(u8) = .{}, local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{}, @@ -74,8 +75,9 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { const gpa = elf_file.base.allocator; try self.atoms.append(gpa, 0); // null input section + try self.strtab.append(gpa, 0); - const name_off = try elf_file.strtab.insert(gpa, std.fs.path.stem(self.path)); + const name_off = try self.insertString(gpa, std.fs.path.stem(self.path)); const symbol_index = try elf_file.addSymbol(); try self.local_symbols.append(gpa, symbol_index); const symbol_ptr = elf_file.symbol(symbol_index); @@ -97,6 +99,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { pub fn deinit(self: *ZigObject, allocator: Allocator) void { self.local_esyms.deinit(allocator); self.global_esyms.deinit(allocator); + self.strtab.deinit(allocator); self.local_symbols.deinit(allocator); self.global_symbols.deinit(allocator); self.globals_lookup.deinit(allocator); @@ -379,15 +382,6 @@ pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void { } } -pub fn resetGlobals(self: *ZigObject, elf_file: *Elf) void { - for (self.globals()) |index| { - const global = elf_file.symbol(index); - const off = global.name_offset; - global.* = .{}; - global.name_offset = off; - } -} - pub fn markLive(self: *ZigObject, elf_file: *Elf) void { for (self.globals(), 0..) |index, i| { const esym = self.global_esyms.items(.elf_sym)[i]; @@ -404,60 +398,6 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void { } } -pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) void { - for (self.locals()) |local_index| { - const local = elf_file.symbol(local_index); - const esym = local.elfSym(elf_file); - switch (esym.st_type()) { - elf.STT_SECTION, elf.STT_NOTYPE => { - local.flags.output_symtab = false; - continue; - }, - else => {}, - } - local.flags.output_symtab = true; - self.output_symtab_size.nlocals += 1; - } - - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - if (global.file(elf_file)) |file| if (file.index() != self.index) { - global.flags.output_symtab = false; - continue; - }; - global.flags.output_symtab = true; - if (global.isLocal()) { - self.output_symtab_size.nlocals += 1; - } else { - self.output_symtab_size.nglobals += 1; - } - } -} - -pub fn writeSymtab(self: *ZigObject, elf_file: *Elf, ctx: anytype) void { - var ilocal = ctx.ilocal; - for (self.locals()) |local_index| { - const local = elf_file.symbol(local_index); - if (!local.flags.output_symtab) continue; - local.setOutputSym(elf_file, &ctx.symtab[ilocal]); - ilocal += 1; - } - - var iglobal = ctx.iglobal; - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - if (global.file(elf_file)) |file| if (file.index() != self.index) continue; - if (!global.flags.output_symtab) continue; - if (global.isLocal()) { - global.setOutputSym(elf_file, &ctx.symtab[ilocal]); - ilocal += 1; - } else { - global.setOutputSym(elf_file, &ctx.symtab[iglobal]); - iglobal += 1; - } - } -} - pub fn symbol(self: *ZigObject, index: Symbol.Index) Symbol.Index { const is_global = index & global_symbol_bit != 0; const actual_index = index & symbol_mask; @@ -727,7 +667,7 @@ fn updateDeclCode( sym.output_section_index = shdr_index; atom_ptr.output_section_index = shdr_index; - sym.name_offset = try elf_file.strtab.insert(gpa, decl_name); + sym.name_offset = try self.insertString(gpa, decl_name); atom_ptr.flags.alive = true; atom_ptr.name_offset = sym.name_offset; esym.st_name = sym.name_offset; @@ -967,7 +907,7 @@ fn updateLazySymbol( sym.ty.fmt(mod), }); defer gpa.free(name); - break :blk try elf_file.strtab.insert(gpa, name); + break :blk try self.insertString(gpa, name); }; const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl| @@ -1100,7 +1040,7 @@ fn lowerConst( const phdr_index = elf_file.phdr_to_shdr_table.get(output_section_index).?; const local_sym = elf_file.symbol(sym_index); - const name_str_index = try elf_file.strtab.insert(gpa, name); + const name_str_index = try self.insertString(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]; @@ -1195,8 +1135,8 @@ pub fn updateExports( }; const stt_bits: u8 = @as(u4, @truncate(esym.st_info)); const exp_name = mod.intern_pool.stringToSlice(exp.opts.name); - const name_off = try elf_file.strtab.insert(gpa, exp_name); - const global_esym_index = if (metadata.@"export"(self, elf_file, exp_name)) |exp_index| + const name_off = try self.insertString(gpa, exp_name); + const global_esym_index = if (metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: { const global_esym_index = try self.addGlobalEsym(gpa); @@ -1205,7 +1145,7 @@ pub fn updateExports( global_esym.st_name = name_off; lookup_gop.value_ptr.* = global_esym_index; try metadata.exports.append(gpa, global_esym_index); - const gop = try elf_file.getOrPutGlobal(name_off); + const gop = try elf_file.getOrPutGlobal(exp_name); try self.global_symbols.append(gpa, gop.index); break :blk global_esym_index; }; @@ -1248,7 +1188,7 @@ pub fn deleteDeclExport( const metadata = self.decls.getPtr(decl_index) orelse return; const mod = elf_file.base.options.module.?; const exp_name = mod.intern_pool.stringToSlice(name); - const esym_index = metadata.@"export"(self, elf_file, exp_name) orelse return; + const esym_index = metadata.@"export"(self, exp_name) orelse return; log.debug("deleting export '{s}'", .{exp_name}); const esym = &self.global_esyms.items(.elf_sym)[esym_index.*]; _ = self.globals_lookup.remove(esym.st_name); @@ -1265,19 +1205,31 @@ pub fn deleteDeclExport( pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 { _ = lib_name; const gpa = elf_file.base.allocator; - const off = try elf_file.strtab.insert(gpa, name); + const off = try self.insertString(gpa, name); const lookup_gop = try self.globals_lookup.getOrPut(gpa, off); if (!lookup_gop.found_existing) { const esym_index = try self.addGlobalEsym(gpa); const esym = self.elfSym(esym_index); esym.st_name = off; lookup_gop.value_ptr.* = esym_index; - const gop = try elf_file.getOrPutGlobal(off); + const gop = try elf_file.getOrPutGlobal(name); try self.global_symbols.append(gpa, gop.index); } return lookup_gop.value_ptr.*; } +pub fn getString(self: ZigObject, off: u32) [:0]const u8 { + assert(off < self.strtab.items.len); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); +} + +pub fn insertString(self: *ZigObject, allocator: Allocator, name: []const u8) error{OutOfMemory}!u32 { + const off = @as(u32, @intCast(self.strtab.items.len)); + try self.strtab.ensureUnusedCapacity(allocator, name.len + 1); + self.strtab.writer(allocator).print("{s}\x00", .{name}) catch unreachable; + return off; +} + pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { return .{ .data = .{ .self = self, @@ -1350,9 +1302,9 @@ const DeclMetadata = struct { /// A list of all exports aliases of this Decl. exports: std.ArrayListUnmanaged(Symbol.Index) = .{}, - fn @"export"(m: DeclMetadata, zig_object: *ZigObject, elf_file: *Elf, name: []const u8) ?*u32 { + fn @"export"(m: DeclMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 { for (m.exports.items) |*exp| { - const exp_name = elf_file.strtab.getAssumeExists(zig_object.elfSym(exp.*).st_name); + const exp_name = zig_object.getString(zig_object.elfSym(exp.*).st_name); if (mem.eql(u8, name, exp_name)) return exp; } return null; diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index caf6f9a051..8c0fa0f769 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -43,7 +43,7 @@ pub const Fde = struct { pub fn atom(fde: Fde, elf_file: *Elf) *Atom { const object = elf_file.file(fde.file_index).?.object; const rel = fde.relocs(elf_file)[0]; - const sym = object.symtab[rel.r_sym()]; + const sym = object.symtab.items[rel.r_sym()]; const atom_index = object.atoms.items[sym.st_shndx]; return elf_file.atom(atom_index).?; } diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 88620e2e5b..2b3112e7aa 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -68,9 +68,12 @@ pub const File = union(enum) { } pub fn resetGlobals(file: File, elf_file: *Elf) void { - switch (file) { - .linker_defined => unreachable, - inline else => |x| x.resetGlobals(elf_file), + for (file.globals()) |global_index| { + const global = elf_file.symbol(global_index); + const name_offset = global.name_offset; + global.* = .{}; + global.name_offset = name_offset; + global.flags.global = true; } } @@ -83,15 +86,14 @@ pub const File = union(enum) { pub fn markLive(file: File, elf_file: *Elf) void { switch (file) { - .linker_defined => unreachable, + .linker_defined => {}, inline else => |x| x.markLive(elf_file), } } pub fn atoms(file: File) []const Atom.Index { return switch (file) { - .linker_defined => unreachable, - .shared_object => unreachable, + .linker_defined, .shared_object => &[0]Atom.Index{}, .zig_object => |x| x.atoms.items, .object => |x| x.atoms.items, }; @@ -99,8 +101,7 @@ pub const File = union(enum) { pub fn locals(file: File) []const Symbol.Index { return switch (file) { - .linker_defined => unreachable, - .shared_object => unreachable, + .linker_defined, .shared_object => &[0]Symbol.Index{}, inline else => |x| x.locals(), }; } @@ -111,6 +112,74 @@ 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, + }; + for (file.locals()) |local_index| { + const local = elf_file.symbol(local_index); + if (local.atom(elf_file)) |atom| if (!atom.flags.alive) continue; + const esym = local.elfSym(elf_file); + switch (esym.st_type()) { + elf.STT_SECTION, elf.STT_NOTYPE => continue, + else => {}, + } + local.flags.output_symtab = true; + output_symtab_size.nlocals += 1; + output_symtab_size.strsize += @as(u32, @intCast(local.name(elf_file).len)) + 1; + } + + for (file.globals()) |global_index| { + const global = elf_file.symbol(global_index); + const file_ptr = global.file(elf_file) orelse continue; + if (file_ptr.index() != file.index()) continue; + if (global.atom(elf_file)) |atom| if (!atom.flags.alive) continue; + global.flags.output_symtab = true; + if (global.isLocal()) { + output_symtab_size.nlocals += 1; + } else { + output_symtab_size.nglobals += 1; + } + output_symtab_size.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1; + } + } + + pub fn writeSymtab(file: File, elf_file: *Elf, ctx: anytype) void { + var ilocal = ctx.ilocal; + 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]; + 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 = ctx.iglobal; + for (file.globals()) |global_index| { + const global = elf_file.symbol(global_index); + const file_ptr = global.file(elf_file) orelse continue; + if (file_ptr.index() != file.index()) continue; + if (!global.flags.output_symtab) continue; + 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()) { + 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; + } + } + } + pub const Index = u32; pub const Entry = union(enum) { diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index cfc2064fbe..2602940d41 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -9,7 +9,7 @@ pub const DynamicSection = struct { pub fn addNeeded(dt: *DynamicSection, shared: *SharedObject, elf_file: *Elf) !void { const gpa = elf_file.base.allocator; - const off = try elf_file.dynstrtab.insert(gpa, shared.soname()); + const off = try elf_file.insertDynString(shared.soname()); try dt.needed.append(gpa, off); } @@ -22,11 +22,11 @@ pub const DynamicSection = struct { if (i > 0) try rpath.append(':'); try rpath.appendSlice(path); } - dt.rpath = try elf_file.dynstrtab.insert(gpa, rpath.items); + dt.rpath = try elf_file.insertDynString(rpath.items); } pub fn setSoname(dt: *DynamicSection, soname: []const u8, elf_file: *Elf) !void { - dt.soname = try elf_file.dynstrtab.insert(elf_file.base.allocator, soname); + dt.soname = try elf_file.insertDynString(soname); } fn getFlags(dt: DynamicSection, elf_file: *Elf) ?u64 { @@ -359,31 +359,24 @@ pub const ZigGotSection = struct { } pub fn updateSymtabSize(zig_got: *ZigGotSection, elf_file: *Elf) void { - _ = elf_file; zig_got.output_symtab_size.nlocals = @as(u32, @intCast(zig_got.entries.items.len)); - } - - pub fn updateStrtab(zig_got: ZigGotSection, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; for (zig_got.entries.items) |entry| { - const symbol_name = elf_file.symbol(entry).name(elf_file); - const name = try std.fmt.allocPrint(gpa, "{s}$ziggot", .{symbol_name}); - defer gpa.free(name); - _ = try elf_file.strtab.insert(gpa, name); + const name = elf_file.symbol(entry).name(elf_file); + zig_got.output_symtab_size.strsize += @as(u32, @intCast(name.len + "$ziggot".len)) + 1; } } - pub fn writeSymtab(zig_got: ZigGotSection, elf_file: *Elf, ctx: anytype) !void { - const gpa = elf_file.base.allocator; + pub fn writeSymtab(zig_got: ZigGotSection, elf_file: *Elf, ctx: anytype) void { for (zig_got.entries.items, ctx.ilocal.., 0..) |entry, ilocal, index| { const symbol = elf_file.symbol(entry); const symbol_name = symbol.name(elf_file); - const name = try std.fmt.allocPrint(gpa, "{s}$ziggot", .{symbol_name}); - defer gpa.free(name); - const st_name = try elf_file.strtab.insert(gpa, name); + const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); + elf_file.strtab.appendSliceAssumeCapacity(symbol_name); + elf_file.strtab.appendSliceAssumeCapacity("$ziggot"); + elf_file.strtab.appendAssumeCapacity(0); const st_value = zig_got.entryAddress(@intCast(index), elf_file); const st_size = elf_file.archPtrWidthBytes(); - ctx.symtab[ilocal] = .{ + elf_file.symtab.items[ilocal] = .{ .st_name = st_name, .st_info = elf.STT_OBJECT, .st_other = 0, @@ -767,25 +760,17 @@ pub const GotSection = struct { } pub fn updateSymtabSize(got: *GotSection, elf_file: *Elf) void { - _ = elf_file; got.output_symtab_size.nlocals = @as(u32, @intCast(got.entries.items.len)); - } - - pub fn updateStrtab(got: GotSection, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; for (got.entries.items) |entry| { const symbol_name = switch (entry.tag) { .tlsld => "", inline else => elf_file.symbol(entry.symbol_index).name(elf_file), }; - const name = try std.fmt.allocPrint(gpa, "{s}${s}", .{ symbol_name, @tagName(entry.tag) }); - defer gpa.free(name); - _ = try elf_file.strtab.insert(gpa, name); + got.output_symtab_size.strsize += @as(u32, @intCast(symbol_name.len + @tagName(entry.tag).len)) + 1 + 1; } } - pub fn writeSymtab(got: GotSection, elf_file: *Elf, ctx: anytype) !void { - const gpa = elf_file.base.allocator; + pub fn writeSymtab(got: GotSection, elf_file: *Elf, ctx: anytype) void { for (got.entries.items, ctx.ilocal..) |entry, ilocal| { const symbol = switch (entry.tag) { .tlsld => null, @@ -795,12 +780,14 @@ pub const GotSection = struct { .tlsld => "", inline else => symbol.?.name(elf_file), }; - const name = try std.fmt.allocPrint(gpa, "{s}${s}", .{ symbol_name, @tagName(entry.tag) }); - defer gpa.free(name); - const st_name = try elf_file.strtab.insert(gpa, name); + const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); + elf_file.strtab.appendSliceAssumeCapacity(symbol_name); + elf_file.strtab.appendAssumeCapacity('$'); + elf_file.strtab.appendSliceAssumeCapacity(@tagName(entry.tag)); + elf_file.strtab.appendAssumeCapacity(0); const st_value = entry.address(elf_file); const st_size: u64 = entry.len() * elf_file.archPtrWidthBytes(); - ctx.symtab[ilocal] = .{ + elf_file.symtab.items[ilocal] = .{ .st_name = st_name, .st_info = elf.STT_OBJECT, .st_other = 0, @@ -922,30 +909,22 @@ pub const PltSection = struct { } pub fn updateSymtabSize(plt: *PltSection, elf_file: *Elf) void { - _ = elf_file; plt.output_symtab_size.nlocals = @as(u32, @intCast(plt.symbols.items.len)); - } - - pub fn updateStrtab(plt: PltSection, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; for (plt.symbols.items) |sym_index| { - const sym = elf_file.symbol(sym_index); - const name = try std.fmt.allocPrint(gpa, "{s}$plt", .{sym.name(elf_file)}); - defer gpa.free(name); - _ = try elf_file.strtab.insert(gpa, name); + const name = elf_file.symbol(sym_index).name(elf_file); + plt.output_symtab_size.strsize += @as(u32, @intCast(name.len + "$plt".len)) + 1; } } - pub fn writeSymtab(plt: PltSection, elf_file: *Elf, ctx: anytype) !void { - const gpa = elf_file.base.allocator; - + pub fn writeSymtab(plt: PltSection, elf_file: *Elf, ctx: anytype) void { var ilocal = ctx.ilocal; for (plt.symbols.items) |sym_index| { const sym = elf_file.symbol(sym_index); - const name = try std.fmt.allocPrint(gpa, "{s}$plt", .{sym.name(elf_file)}); - defer gpa.free(name); - const st_name = try elf_file.strtab.insert(gpa, name); - ctx.symtab[ilocal] = .{ + const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); + elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file)); + elf_file.strtab.appendSliceAssumeCapacity("$plt"); + elf_file.strtab.appendAssumeCapacity(0); + elf_file.symtab.items[ilocal] = .{ .st_name = st_name, .st_info = elf.STT_FUNC, .st_other = 0, @@ -1029,29 +1008,22 @@ pub const PltGotSection = struct { } pub fn updateSymtabSize(plt_got: *PltGotSection, elf_file: *Elf) void { - _ = elf_file; plt_got.output_symtab_size.nlocals = @as(u32, @intCast(plt_got.symbols.items.len)); - } - - pub fn updateStrtab(plt_got: PltGotSection, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; for (plt_got.symbols.items) |sym_index| { - const sym = elf_file.symbol(sym_index); - const name = try std.fmt.allocPrint(gpa, "{s}$pltgot", .{sym.name(elf_file)}); - defer gpa.free(name); - _ = try elf_file.strtab.insert(gpa, name); + const name = elf_file.symbol(sym_index).name(elf_file); + plt_got.output_symtab_size.strsize += @as(u32, @intCast(name.len + "$pltgot".len)) + 1; } } - pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf, ctx: anytype) !void { - const gpa = elf_file.base.allocator; + pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf, ctx: anytype) void { var ilocal = ctx.ilocal; for (plt_got.symbols.items) |sym_index| { const sym = elf_file.symbol(sym_index); - const name = try std.fmt.allocPrint(gpa, "{s}$pltgot", .{sym.name(elf_file)}); - defer gpa.free(name); - const st_name = try elf_file.strtab.insert(gpa, name); - ctx.symtab[ilocal] = .{ + const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); + elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file)); + elf_file.strtab.appendSliceAssumeCapacity("$pltgot"); + elf_file.strtab.appendAssumeCapacity(0); + elf_file.symtab.items[ilocal] = .{ .st_name = st_name, .st_info = elf.STT_FUNC, .st_other = 0, @@ -1166,7 +1138,7 @@ pub const DynsymSection = struct { new_extra.dynamic = index; sym.setExtra(new_extra, elf_file); } else try sym.addExtra(.{ .dynamic = index }, elf_file); - const off = try elf_file.dynstrtab.insert(gpa, sym.name(elf_file)); + const off = try elf_file.insertDynString(sym.name(elf_file)); try dynsym.entries.append(gpa, .{ .symbol_index = sym_index, .off = off }); } @@ -1251,7 +1223,7 @@ pub const HashSection = struct { @memset(chains, 0); for (elf_file.dynsym.entries.items, 1..) |entry, i| { - const name = elf_file.dynstrtab.getAssumeExists(entry.off); + const name = elf_file.getDynString(entry.off); const hash = hasher(name) % buckets.len; chains[@as(u32, @intCast(i))] = buckets[hash]; buckets[hash] = @as(u32, @intCast(i)); @@ -1490,7 +1462,7 @@ pub const VerneedSection = struct { sym.* = .{ .vn_version = 1, .vn_cnt = 0, - .vn_file = try elf_file.dynstrtab.insert(gpa, soname), + .vn_file = try elf_file.insertDynString(soname), .vn_aux = 0, .vn_next = 0, }; @@ -1509,7 +1481,7 @@ pub const VerneedSection = struct { .vna_hash = HashSection.hasher(version), .vna_flags = 0, .vna_other = vern.index, - .vna_name = try elf_file.dynstrtab.insert(gpa, version), + .vna_name = try elf_file.insertDynString(version), .vna_next = 0, }; verneed_sym.vn_cnt += 1; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 5a246bcfd1..10c0702217 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -58,7 +58,7 @@ globals_free_list: std.ArrayListUnmanaged(u32) = .{}, dyld_stub_binder_index: ?u32 = null, dyld_private_atom_index: ?Atom.Index = null, -strtab: StringTable(.strtab) = .{}, +strtab: StringTable = .{}, got_table: TableSection(SymbolWithLoc) = .{}, stub_table: TableSection(SymbolWithLoc) = .{}, @@ -5643,7 +5643,7 @@ const Module = @import("../Module.zig"); const InternPool = @import("../InternPool.zig"); const Platform = load_commands.Platform; const Relocation = @import("MachO/Relocation.zig"); -const StringTable = @import("strtab.zig").StringTable; +const StringTable = @import("StringTable.zig"); const TableSection = @import("table_section.zig").TableSection; const Trie = @import("MachO/Trie.zig"); const Type = @import("../type.zig").Type; diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index d20f32c14c..f204093290 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -22,7 +22,7 @@ debug_aranges_section_dirty: bool = false, debug_info_header_dirty: bool = false, debug_line_header_dirty: bool = false, -strtab: StringTable(.strtab) = .{}, +strtab: StringTable = .{}, relocs: std.ArrayListUnmanaged(Reloc) = .{}, pub const Reloc = struct { @@ -567,5 +567,5 @@ const Allocator = mem.Allocator; const Dwarf = @import("../Dwarf.zig"); const MachO = @import("../MachO.zig"); const Module = @import("../../Module.zig"); -const StringTable = @import("../strtab.zig").StringTable; +const StringTable = @import("../StringTable.zig"); const Type = @import("../../type.zig").Type; diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig index b94da1d284..79433b3b1b 100644 --- a/src/link/MachO/zld.zig +++ b/src/link/MachO/zld.zig @@ -1227,7 +1227,6 @@ const LibStub = @import("../tapi.zig").LibStub; const Object = @import("Object.zig"); const Platform = load_commands.Platform; const Section = MachO.Section; -const StringTable = @import("../strtab.zig").StringTable; const SymbolWithLoc = MachO.SymbolWithLoc; const TableSection = @import("../table_section.zig").TableSection; const Trie = @import("Trie.zig"); diff --git a/src/link/strtab.zig b/src/link/strtab.zig deleted file mode 100644 index f854225ef6..0000000000 --- a/src/link/strtab.zig +++ /dev/null @@ -1,121 +0,0 @@ -const std = @import("std"); -const mem = std.mem; - -const Allocator = mem.Allocator; -const StringIndexAdapter = std.hash_map.StringIndexAdapter; -const StringIndexContext = std.hash_map.StringIndexContext; - -pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type { - return struct { - const Self = @This(); - - const log = std.log.scoped(log_scope); - - buffer: std.ArrayListUnmanaged(u8) = .{}, - table: std.HashMapUnmanaged(u32, bool, StringIndexContext, std.hash_map.default_max_load_percentage) = .{}, - - pub fn deinit(self: *Self, gpa: Allocator) void { - self.buffer.deinit(gpa); - self.table.deinit(gpa); - } - - pub fn toOwnedSlice(self: *Self, gpa: Allocator) []const u8 { - const result = self.buffer.toOwnedSlice(gpa); - self.table.clearRetainingCapacity(); - return result; - } - - pub const PrunedResult = struct { - buffer: []const u8, - idx_map: std.AutoHashMap(u32, u32), - }; - - pub fn toPrunedResult(self: *Self, gpa: Allocator) !PrunedResult { - var buffer = std.ArrayList(u8).init(gpa); - defer buffer.deinit(); - try buffer.ensureTotalCapacity(self.buffer.items.len); - buffer.appendAssumeCapacity(0); - - var idx_map = std.AutoHashMap(u32, u32).init(gpa); - errdefer idx_map.deinit(); - try idx_map.ensureTotalCapacity(self.table.count()); - - var it = self.table.iterator(); - while (it.next()) |entry| { - const off = entry.key_ptr.*; - const save = entry.value_ptr.*; - if (!save) continue; - const new_off = @as(u32, @intCast(buffer.items.len)); - buffer.appendSliceAssumeCapacity(self.getAssumeExists(off)); - idx_map.putAssumeCapacityNoClobber(off, new_off); - } - - self.buffer.clearRetainingCapacity(); - self.table.clearRetainingCapacity(); - - return PrunedResult{ - .buffer = buffer.toOwnedSlice(), - .idx_map = idx_map, - }; - } - - pub fn insert(self: *Self, gpa: Allocator, string: []const u8) !u32 { - const gop = try self.table.getOrPutContextAdapted(gpa, @as([]const u8, string), StringIndexAdapter{ - .bytes = &self.buffer, - }, StringIndexContext{ - .bytes = &self.buffer, - }); - if (gop.found_existing) { - const off = gop.key_ptr.*; - gop.value_ptr.* = true; - log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off }); - return off; - } - - try self.buffer.ensureUnusedCapacity(gpa, string.len + 1); - const new_off = @as(u32, @intCast(self.buffer.items.len)); - - log.debug("writing new string '{s}' at offset 0x{x}", .{ string, new_off }); - - self.buffer.appendSliceAssumeCapacity(string); - self.buffer.appendAssumeCapacity(0); - - gop.key_ptr.* = new_off; - gop.value_ptr.* = true; - - return new_off; - } - - pub fn delete(self: *Self, string: []const u8) void { - const value_ptr = self.table.getPtrAdapted(@as([]const u8, string), StringIndexAdapter{ - .bytes = &self.buffer, - }) orelse return; - value_ptr.* = false; - log.debug("marked '{s}' for deletion", .{string}); - } - - pub fn getOffset(self: *Self, string: []const u8) ?u32 { - return self.table.getKeyAdapted(string, StringIndexAdapter{ - .bytes = &self.buffer, - }); - } - - pub fn get(self: Self, off: u32) ?[:0]const u8 { - log.debug("getting string at 0x{x}", .{off}); - if (off >= self.buffer.items.len) return null; - return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.buffer.items.ptr + off)), 0); - } - - pub fn getAssumeExists(self: Self, off: u32) [:0]const u8 { - return self.get(off) orelse unreachable; - } - - pub fn items(self: Self) []const u8 { - return self.buffer.items; - } - - pub fn len(self: Self) usize { - return self.buffer.items.len; - } - }; -} |
