From 25c53f08a6add493043a407ce15bc727dc33356d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 31 Oct 2023 13:27:47 +0100 Subject: [PATCH 01/36] 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 --- CMakeLists.txt | 2 +- src/link/Coff.zig | 16 +- src/link/Dwarf.zig | 4 +- src/link/Elf.zig | 245 ++++++++++++++++------------ src/link/Elf/Atom.zig | 7 +- src/link/Elf/LinkerDefined.zig | 42 ++--- src/link/Elf/Object.zig | 167 ++++++------------- src/link/Elf/SharedObject.zig | 112 ++++++------- src/link/Elf/Symbol.zig | 39 +++-- src/link/Elf/ZigObject.zig | 102 +++--------- src/link/Elf/eh_frame.zig | 2 +- src/link/Elf/file.zig | 85 +++++++++- src/link/Elf/synthetic_sections.zig | 106 +++++------- src/link/MachO.zig | 4 +- src/link/MachO/DebugSymbols.zig | 4 +- src/link/MachO/zld.zig | 1 - src/link/strtab.zig | 121 -------------- 17 files changed, 439 insertions(+), 620 deletions(-) delete mode 100644 src/link/strtab.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b27fd555e..536d36e245 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -624,7 +624,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig" "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig" "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin" - "${CMAKE_SOURCE_DIR}/src/link/strtab.zig" + "${CMAKE_SOURCE_DIR}/src/link/StringTable.zig" "${CMAKE_SOURCE_DIR}/src/link/tapi.zig" "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig" "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig" 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, shdr.sh_offset }); + log.debug("writing {d} symbols at 0x{x}", .{ nsyms, symtab_shdr.sh_offset }); - const symtab = try gpa.alloc(elf.Elf64_Sym, nsyms); - defer gpa.free(symtab); - symtab[0] = null_sym; + try self.symtab.resize(gpa, nsyms); + try self.strtab.ensureUnusedCapacity(gpa, strtab_shdr.sh_size - 1); - var ctx: struct { ilocal: usize, iglobal: usize, symtab: []elf.Elf64_Sym } = .{ + const Ctx = struct { + ilocal: usize, + iglobal: usize, + + 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; - } - }; -} From b8c8565e93d3625e5eb46a3f64a461f86b8d8eb4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 1 Nov 2023 10:26:38 +0100 Subject: [PATCH 02/36] elf: implement --gc-sections for non-LLVM Zig source --- src/link/Elf/file.zig | 16 ++++++ src/link/Elf/gc.zig | 43 +++++++++------ test/link/elf.zig | 120 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 157 insertions(+), 22 deletions(-) diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 2b3112e7aa..a401203ba7 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -99,6 +99,21 @@ pub const File = union(enum) { }; } + pub fn cies(file: File) []const Cie { + return switch (file) { + .zig_object => &[0]Cie{}, + .object => |x| x.cies.items, + inline else => unreachable, + }; + } + + pub fn symbol(file: File, ind: Symbol.Index) Symbol.Index { + return switch (file) { + .zig_object => |x| x.symbol(ind), + inline else => |x| x.symbols.items[ind], + }; + } + pub fn locals(file: File) []const Symbol.Index { return switch (file) { .linker_defined, .shared_object => &[0]Symbol.Index{}, @@ -196,6 +211,7 @@ const elf = std.elf; const Allocator = std.mem.Allocator; const Atom = @import("Atom.zig"); +const Cie = @import("eh_frame.zig").Cie; const Elf = @import("../Elf.zig"); const LinkerDefined = @import("LinkerDefined.zig"); const Object = @import("Object.zig"); diff --git a/src/link/Elf/gc.zig b/src/link/Elf/gc.zig index 26489d3d1a..ade2b75782 100644 --- a/src/link/Elf/gc.zig +++ b/src/link/Elf/gc.zig @@ -1,19 +1,27 @@ pub fn gcAtoms(elf_file: *Elf) !void { - var roots = std.ArrayList(*Atom).init(elf_file.base.allocator); + const gpa = elf_file.base.allocator; + const num_files = elf_file.objects.items.len + @intFromBool(elf_file.zig_object_index != null); + var files = try std.ArrayList(File.Index).initCapacity(gpa, num_files); + defer files.deinit(); + if (elf_file.zig_object_index) |index| files.appendAssumeCapacity(index); + for (elf_file.objects.items) |index| files.appendAssumeCapacity(index); + + var roots = std.ArrayList(*Atom).init(gpa); defer roots.deinit(); - try collectRoots(&roots, elf_file); + try collectRoots(&roots, files.items, elf_file); + mark(roots, elf_file); - prune(elf_file); + prune(files.items, elf_file); } -fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void { +fn collectRoots(roots: *std.ArrayList(*Atom), files: []const File.Index, elf_file: *Elf) !void { if (elf_file.entry_index) |index| { const global = elf_file.symbol(index); try markSymbol(global, roots, elf_file); } - for (elf_file.objects.items) |index| { - for (elf_file.file(index).?.object.globals()) |global_index| { + for (files) |index| { + for (elf_file.file(index).?.globals()) |global_index| { const global = elf_file.symbol(global_index); if (global.file(elf_file)) |file| { if (file.index() == index and global.flags.@"export") @@ -22,10 +30,10 @@ fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void { } } - for (elf_file.objects.items) |index| { - const object = elf_file.file(index).?.object; + for (files) |index| { + const file = elf_file.file(index).?; - for (object.atoms.items) |atom_index| { + for (file.atoms()) |atom_index| { const atom = elf_file.atom(atom_index) orelse continue; if (!atom.flags.alive) continue; @@ -49,9 +57,9 @@ fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void { } // Mark every atom referenced by CIE as alive. - for (object.cies.items) |cie| { + for (file.cies()) |cie| { for (cie.relocs(elf_file)) |rel| { - const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); + const sym = elf_file.symbol(file.symbol(rel.r_sym())); try markSymbol(sym, roots, elf_file); } } @@ -73,11 +81,11 @@ fn markLive(atom: *Atom, elf_file: *Elf) void { if (@import("build_options").enable_logging) track_live_level.incr(); assert(atom.flags.visited); - const object = atom.file(elf_file).?.object; + const file = atom.file(elf_file).?; for (atom.fdes(elf_file)) |fde| { for (fde.relocs(elf_file)[1..]) |rel| { - const target_sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); + const target_sym = elf_file.symbol(file.symbol(rel.r_sym())); const target_atom = target_sym.atom(elf_file) orelse continue; target_atom.flags.alive = true; gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index }); @@ -86,7 +94,7 @@ fn markLive(atom: *Atom, elf_file: *Elf) void { } for (atom.relocs(elf_file)) |rel| { - const target_sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); + const target_sym = elf_file.symbol(file.symbol(rel.r_sym())); const target_atom = target_sym.atom(elf_file) orelse continue; target_atom.flags.alive = true; gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index }); @@ -101,9 +109,9 @@ fn mark(roots: std.ArrayList(*Atom), elf_file: *Elf) void { } } -fn prune(elf_file: *Elf) void { - for (elf_file.objects.items) |index| { - for (elf_file.file(index).?.object.atoms.items) |atom_index| { +fn prune(files: []const File.Index, elf_file: *Elf) void { + for (files) |index| { + for (elf_file.file(index).?.atoms()) |atom_index| { const atom = elf_file.atom(atom_index) orelse continue; if (atom.flags.alive and !atom.flags.visited) { atom.flags.alive = false; @@ -158,4 +166,5 @@ const mem = std.mem; const Allocator = mem.Allocator; const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); +const File = @import("file.zig").File; const Symbol = @import("Symbol.zig"); diff --git a/test/link/elf.zig b/test/link/elf.zig index dad3604c31..766d1980e2 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -6,9 +6,13 @@ pub fn build(b: *Build) void { const elf_step = b.step("test-elf", "Run ELF tests"); b.default_step = elf_step; - const musl_target = CrossTarget{ + const default_target = CrossTarget{ .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs .os_tag = .linux, + }; + const musl_target = CrossTarget{ + .cpu_arch = .x86_64, + .os_tag = .linux, .abi = .musl, }; const glibc_target = CrossTarget{ @@ -18,7 +22,8 @@ pub fn build(b: *Build) void { }; // Exercise linker with self-hosted backend (no LLVM) - elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false })); + elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target })); + elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target })); elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = glibc_target })); elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = musl_target })); @@ -876,6 +881,110 @@ fn testGcSections(b: *Build, opts: Options) *Step { return test_step; } +fn testGcSectionsZig(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "gc-sections-zig", opts); + + const obj = addObject(b, "obj", .{ + .target = opts.target, + .use_llvm = true, + .use_lld = true, + }); + addCSourceBytes(obj, + \\int live_var1 = 1; + \\int live_var2 = 2; + \\int dead_var1 = 3; + \\int dead_var2 = 4; + \\void live_fn1() {} + \\void live_fn2() { live_fn1(); } + \\void dead_fn1() {} + \\void dead_fn2() { dead_fn1(); } + , &.{}); + obj.link_function_sections = true; + obj.link_data_sections = true; + + { + const exe = addExecutable(b, "test1", opts); + addZigSourceBytes(exe, + \\const std = @import("std"); + \\extern var live_var1: i32; + \\extern var live_var2: i32; + \\extern fn live_fn2() void; + \\pub fn main() void { + \\ const stdout = std.io.getStdOut(); + \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable; + \\ live_fn2(); + \\} + ); + exe.addObject(obj); + exe.link_gc_sections = false; + + const run = addRunArtifact(exe); + run.expectStdOutEqual("1 2\n"); + test_step.dependOn(&run.step); + + const check = exe.checkObject(); + check.checkInSymtab(); + check.checkContains("live_var1"); + check.checkInSymtab(); + check.checkContains("live_var2"); + check.checkInSymtab(); + check.checkContains("dead_var1"); + check.checkInSymtab(); + check.checkContains("dead_var2"); + check.checkInSymtab(); + check.checkContains("live_fn1"); + check.checkInSymtab(); + check.checkContains("live_fn2"); + check.checkInSymtab(); + check.checkContains("dead_fn1"); + check.checkInSymtab(); + check.checkContains("dead_fn2"); + test_step.dependOn(&check.step); + } + + { + const exe = addExecutable(b, "test2", opts); + addZigSourceBytes(exe, + \\const std = @import("std"); + \\extern var live_var1: i32; + \\extern var live_var2: i32; + \\extern fn live_fn2() void; + \\pub fn main() void { + \\ const stdout = std.io.getStdOut(); + \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable; + \\ live_fn2(); + \\} + ); + exe.addObject(obj); + exe.link_gc_sections = true; + + const run = addRunArtifact(exe); + run.expectStdOutEqual("1 2\n"); + test_step.dependOn(&run.step); + + const check = exe.checkObject(); + check.checkInSymtab(); + check.checkContains("live_var1"); + check.checkInSymtab(); + check.checkContains("live_var2"); + check.checkInSymtab(); + check.checkNotPresent("dead_var1"); + check.checkInSymtab(); + check.checkNotPresent("dead_var2"); + check.checkInSymtab(); + check.checkContains("live_fn1"); + check.checkInSymtab(); + check.checkContains("live_fn2"); + check.checkInSymtab(); + check.checkNotPresent("dead_fn1"); + check.checkInSymtab(); + check.checkNotPresent("dead_fn2"); + test_step.dependOn(&check.step); + } + + return test_step; +} + fn testHiddenWeakUndef(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "hidden-weak-undef", opts); @@ -3114,6 +3223,7 @@ const Options = struct { target: CrossTarget = .{ .cpu_arch = .x86_64, .os_tag = .linux }, optimize: std.builtin.OptimizeMode = .Debug, use_llvm: bool = true, + use_lld: bool = false, }; fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step { @@ -3134,7 +3244,7 @@ fn addExecutable(b: *Build, name: []const u8, opts: Options) *Compile { .target = opts.target, .optimize = opts.optimize, .use_llvm = opts.use_llvm, - .use_lld = false, + .use_lld = opts.use_lld, }); } @@ -3144,7 +3254,7 @@ fn addObject(b: *Build, name: []const u8, opts: Options) *Compile { .target = opts.target, .optimize = opts.optimize, .use_llvm = opts.use_llvm, - .use_lld = false, + .use_lld = opts.use_lld, }); } @@ -3164,7 +3274,7 @@ fn addSharedLibrary(b: *Build, name: []const u8, opts: Options) *Compile { .target = opts.target, .optimize = opts.optimize, .use_llvm = opts.use_llvm, - .use_lld = false, + .use_lld = opts.use_lld, }); } From 1be94878e37c4038a33089b432f99c085361ee0f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 1 Nov 2023 15:56:30 +0100 Subject: [PATCH 03/36] elf: generate invalid object file but with almost correct header --- src/link/Elf.zig | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b051e05163..447b54ad19 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -897,7 +897,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } else null; const gc_sections = self.base.options.gc_sections orelse false; - if (self.base.options.output_mode == .Obj and self.zig_object_index == null) { + if (self.isObject() and self.zig_object_index == null) { // TODO this will become -r route I guess. For now, just copy the object file. assert(self.base.file == null); // TODO uncomment once we implement -r const the_object_path = blk: { @@ -1364,7 +1364,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // If we haven't already, create a linker-generated input file comprising of // linker-defined synthetic symbols only such as `_DYNAMIC`, etc. - if (self.linker_defined_index == null) { + if (self.linker_defined_index == null and !self.isObject()) { const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); self.files.set(index, .{ .linker_defined = .{ .index = index } }); self.linker_defined_index = index; @@ -1377,6 +1377,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // symbol for potential resolution at load-time. self.resolveSymbols(); self.markEhFrameAtomsDead(); + + if (self.isObject()) { + return self.flushObject(comp); + } + try self.convertCommonSymbols(); self.markImportsExports(); @@ -1470,6 +1475,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } } +pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void { + _ = comp; + try self.initSections(); + try self.writeShdrTable(); + try self.writeHeader(); +} + const ParseError = error{ UnknownFileType, InvalidCpuArch, @@ -2766,7 +2778,7 @@ fn writeHeader(self: *Elf) !void { index += 4; const e_entry = if (self.entry_index) |entry_index| self.symbol(entry_index).value else 0; - const phdr_table_offset = self.phdrs.items[self.phdr_table_index.?].p_offset; + const phdr_table_offset = if (self.phdr_table_index) |phndx| self.phdrs.items[phndx].p_offset else 0; switch (self.ptr_width) { .p32 => { mem.writeInt(u32, hdr_buf[index..][0..4], @as(u32, @intCast(e_entry)), endian); @@ -4845,6 +4857,10 @@ pub fn isStatic(self: Elf) bool { return self.base.options.link_mode == .Static; } +pub fn isObject(self: Elf) bool { + return self.base.options.effectiveOutputMode() == .Obj; +} + pub fn isExe(self: Elf) bool { return self.base.options.effectiveOutputMode() == .Exe; } From b1136a695f5cc463bafd6727ff5bb60d618d8ce9 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 1 Nov 2023 16:29:59 +0100 Subject: [PATCH 04/36] elf: remove now obsolete allocateSegment helper --- src/link/Elf.zig | 80 +++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 48 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 447b54ad19..10fcc904ab 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -484,38 +484,6 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 { return start; } -const AllocateSegmentOpts = struct { - addr: u64, - memsz: u64, - filesz: u64, - alignment: u64, - flags: u32 = elf.PF_R, -}; - -pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}!u16 { - const off = self.findFreeSpace(opts.filesz, opts.alignment); - const index = try self.addPhdr(.{ - .type = elf.PT_LOAD, - .offset = off, - .filesz = opts.filesz, - .addr = opts.addr, - .memsz = opts.memsz, - .@"align" = opts.alignment, - .flags = opts.flags, - }); - log.debug("allocating phdr({d})({c}{c}{c}) from 0x{x} to 0x{x} (0x{x} - 0x{x})", .{ - index, - if (opts.flags & elf.PF_R != 0) @as(u8, 'R') else '_', - if (opts.flags & elf.PF_W != 0) @as(u8, 'W') else '_', - if (opts.flags & elf.PF_X != 0) @as(u8, 'X') else '_', - off, - off + opts.filesz, - opts.addr, - opts.addr + opts.memsz, - }); - return index; -} - const AllocateAllocSectionOpts = struct { name: [:0]const u8, phdr_index: u16, @@ -590,11 +558,15 @@ pub fn initMetadata(self: *Elf) !void { comptime assert(number_of_zig_segments == 5); if (self.phdr_zig_load_re_index == null) { - self.phdr_zig_load_re_index = try self.allocateSegment(.{ + const filesz = self.base.options.program_code_size_hint; + const off = self.findFreeSpace(filesz, self.page_size); + self.phdr_zig_load_re_index = try self.addPhdr(.{ + .type = elf.PT_LOAD, + .offset = off, + .filesz = filesz, .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000, - .memsz = self.base.options.program_code_size_hint, - .filesz = self.base.options.program_code_size_hint, - .alignment = self.page_size, + .memsz = filesz, + .@"align" = self.page_size, .flags = elf.PF_X | elf.PF_R | elf.PF_W, }); } @@ -603,33 +575,45 @@ pub fn initMetadata(self: *Elf) !void { // We really only need ptr alignment but since we are using PROGBITS, linux requires // page align. const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); - self.phdr_zig_got_index = try self.allocateSegment(.{ + const filesz = @as(u64, ptr_size) * self.base.options.symbol_count_hint; + const off = self.findFreeSpace(filesz, alignment); + self.phdr_zig_got_index = try self.addPhdr(.{ + .type = elf.PT_LOAD, + .offset = off, + .filesz = filesz, .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000, - .memsz = @as(u64, ptr_size) * self.base.options.symbol_count_hint, - .filesz = @as(u64, ptr_size) * self.base.options.symbol_count_hint, - .alignment = alignment, + .memsz = filesz, + .@"align" = alignment, .flags = elf.PF_R | elf.PF_W, }); } if (self.phdr_zig_load_ro_index == null) { const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); - self.phdr_zig_load_ro_index = try self.allocateSegment(.{ + const filesz: u64 = 1024; + const off = self.findFreeSpace(filesz, alignment); + self.phdr_zig_load_ro_index = try self.addPhdr(.{ + .type = elf.PT_LOAD, + .offset = off, + .filesz = filesz, .addr = if (ptr_bit_width >= 32) 0xc000000 else 0xa000, - .memsz = 1024, - .filesz = 1024, - .alignment = alignment, + .memsz = filesz, + .@"align" = alignment, .flags = elf.PF_R | elf.PF_W, }); } if (self.phdr_zig_load_rw_index == null) { const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); - self.phdr_zig_load_rw_index = try self.allocateSegment(.{ + const filesz: u64 = 1024; + const off = self.findFreeSpace(filesz, alignment); + self.phdr_zig_load_rw_index = try self.addPhdr(.{ + .type = elf.PT_LOAD, + .offset = off, + .filesz = filesz, .addr = if (ptr_bit_width >= 32) 0x10000000 else 0xc000, - .memsz = 1024, - .filesz = 1024, - .alignment = alignment, + .memsz = filesz, + .@"align" = alignment, .flags = elf.PF_R | elf.PF_W, }); } From abf6c20cb93f9b9b9b3ef0d935bd1e12063f4c36 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 1 Nov 2023 16:52:52 +0100 Subject: [PATCH 05/36] elf: rename .rodata to .data.rel.ro and remove allocateAllocSection helper --- src/link/Elf.zig | 276 +++++++++++++++++++++---------------- src/link/Elf/ZigObject.zig | 10 +- 2 files changed, 161 insertions(+), 125 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 10fcc904ab..be1ae021b3 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -104,7 +104,7 @@ zig_got: ZigGotSection = .{}, /// Tracked section headers with incremental updates to Zig object zig_text_section_index: ?u16 = null, -zig_rodata_section_index: ?u16 = null, +zig_data_rel_ro_section_index: ?u16 = null, zig_data_section_index: ?u16 = null, zig_bss_section_index: ?u16 = null, zig_got_section_index: ?u16 = null, @@ -484,40 +484,6 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 { return start; } -const AllocateAllocSectionOpts = struct { - name: [:0]const u8, - phdr_index: u16, - alignment: u64 = 1, - flags: u64 = elf.SHF_ALLOC, - type: u32 = elf.SHT_PROGBITS, -}; - -pub fn allocateAllocSection(self: *Elf, opts: AllocateAllocSectionOpts) error{OutOfMemory}!u16 { - const gpa = self.base.allocator; - const phdr = &self.phdrs.items[opts.phdr_index]; - const index = try self.addSection(.{ - .name = opts.name, - .type = opts.type, - .flags = opts.flags, - .addralign = opts.alignment, - .offset = std.math.maxInt(u64), - }); - const shdr = &self.shdrs.items[index]; - try self.phdr_to_shdr_table.putNoClobber(gpa, index, opts.phdr_index); - log.debug("allocating '{s}' in phdr({d}) from 0x{x} to 0x{x} (0x{x} - 0x{x})", .{ - opts.name, - opts.phdr_index, - phdr.p_offset, - phdr.p_offset + phdr.p_filesz, - phdr.p_vaddr, - phdr.p_vaddr + phdr.p_memsz, - }); - shdr.sh_addr = phdr.p_vaddr; - shdr.sh_offset = phdr.p_offset; - shdr.sh_size = phdr.p_memsz; - return index; -} - const AllocateNonAllocSectionOpts = struct { name: [:0]const u8, size: u64, @@ -557,123 +523,193 @@ pub fn initMetadata(self: *Elf) !void { comptime assert(number_of_zig_segments == 5); - if (self.phdr_zig_load_re_index == null) { - const filesz = self.base.options.program_code_size_hint; - const off = self.findFreeSpace(filesz, self.page_size); - self.phdr_zig_load_re_index = try self.addPhdr(.{ - .type = elf.PT_LOAD, - .offset = off, - .filesz = filesz, - .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000, - .memsz = filesz, - .@"align" = self.page_size, - .flags = elf.PF_X | elf.PF_R | elf.PF_W, - }); - } + if (!self.isObject()) { + if (self.phdr_zig_load_re_index == null) { + const filesz = self.base.options.program_code_size_hint; + const off = self.findFreeSpace(filesz, self.page_size); + self.phdr_zig_load_re_index = try self.addPhdr(.{ + .type = elf.PT_LOAD, + .offset = off, + .filesz = filesz, + .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000, + .memsz = filesz, + .@"align" = self.page_size, + .flags = elf.PF_X | elf.PF_R | elf.PF_W, + }); + } - if (self.phdr_zig_got_index == null) { - // We really only need ptr alignment but since we are using PROGBITS, linux requires - // page align. - const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); - const filesz = @as(u64, ptr_size) * self.base.options.symbol_count_hint; - const off = self.findFreeSpace(filesz, alignment); - self.phdr_zig_got_index = try self.addPhdr(.{ - .type = elf.PT_LOAD, - .offset = off, - .filesz = filesz, - .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000, - .memsz = filesz, - .@"align" = alignment, - .flags = elf.PF_R | elf.PF_W, - }); - } + if (self.phdr_zig_got_index == null) { + // We really only need ptr alignment but since we are using PROGBITS, linux requires + // page align. + const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); + const filesz = @as(u64, ptr_size) * self.base.options.symbol_count_hint; + const off = self.findFreeSpace(filesz, alignment); + self.phdr_zig_got_index = try self.addPhdr(.{ + .type = elf.PT_LOAD, + .offset = off, + .filesz = filesz, + .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000, + .memsz = filesz, + .@"align" = alignment, + .flags = elf.PF_R | elf.PF_W, + }); + } - if (self.phdr_zig_load_ro_index == null) { - const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); - const filesz: u64 = 1024; - const off = self.findFreeSpace(filesz, alignment); - self.phdr_zig_load_ro_index = try self.addPhdr(.{ - .type = elf.PT_LOAD, - .offset = off, - .filesz = filesz, - .addr = if (ptr_bit_width >= 32) 0xc000000 else 0xa000, - .memsz = filesz, - .@"align" = alignment, - .flags = elf.PF_R | elf.PF_W, - }); - } + if (self.phdr_zig_load_ro_index == null) { + const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); + const filesz: u64 = 1024; + const off = self.findFreeSpace(filesz, alignment); + self.phdr_zig_load_ro_index = try self.addPhdr(.{ + .type = elf.PT_LOAD, + .offset = off, + .filesz = filesz, + .addr = if (ptr_bit_width >= 32) 0xc000000 else 0xa000, + .memsz = filesz, + .@"align" = alignment, + .flags = elf.PF_R | elf.PF_W, + }); + } - if (self.phdr_zig_load_rw_index == null) { - const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); - const filesz: u64 = 1024; - const off = self.findFreeSpace(filesz, alignment); - self.phdr_zig_load_rw_index = try self.addPhdr(.{ - .type = elf.PT_LOAD, - .offset = off, - .filesz = filesz, - .addr = if (ptr_bit_width >= 32) 0x10000000 else 0xc000, - .memsz = filesz, - .@"align" = alignment, - .flags = elf.PF_R | elf.PF_W, - }); - } + if (self.phdr_zig_load_rw_index == null) { + const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); + const filesz: u64 = 1024; + const off = self.findFreeSpace(filesz, alignment); + self.phdr_zig_load_rw_index = try self.addPhdr(.{ + .type = elf.PT_LOAD, + .offset = off, + .filesz = filesz, + .addr = if (ptr_bit_width >= 32) 0x10000000 else 0xc000, + .memsz = filesz, + .@"align" = alignment, + .flags = elf.PF_R | elf.PF_W, + }); + } - if (self.phdr_zig_load_zerofill_index == null) { - const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); - self.phdr_zig_load_zerofill_index = try self.addPhdr(.{ - .type = elf.PT_LOAD, - .addr = if (ptr_bit_width >= 32) 0x14000000 else 0xf000, - .memsz = 1024, - .@"align" = alignment, - .flags = elf.PF_R | elf.PF_W, - }); + if (self.phdr_zig_load_zerofill_index == null) { + const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); + self.phdr_zig_load_zerofill_index = try self.addPhdr(.{ + .type = elf.PT_LOAD, + .addr = if (ptr_bit_width >= 32) 0x14000000 else 0xf000, + .memsz = 1024, + .@"align" = alignment, + .flags = elf.PF_R | elf.PF_W, + }); + } } if (self.zig_text_section_index == null) { - self.zig_text_section_index = try self.allocateAllocSection(.{ + self.zig_text_section_index = try self.addSection(.{ .name = ".zig.text", - .phdr_index = self.phdr_zig_load_re_index.?, + .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, + .addralign = 1, + .offset = std.math.maxInt(u64), }); + const shdr = &self.shdrs.items[self.zig_text_section_index.?]; + if (self.phdr_zig_load_re_index) |phndx| { + const phdr = self.phdrs.items[phndx]; + shdr.sh_addr = phdr.p_vaddr; + shdr.sh_offset = phdr.p_offset; + shdr.sh_size = phdr.p_memsz; + try self.phdr_to_shdr_table.putNoClobber(gpa, self.zig_text_section_index.?, phndx); + } else { + const size = self.base.options.program_code_size_hint; + const off = self.findFreeSpace(size, 1); + shdr.sh_offset = off; + shdr.sh_size = size; + } try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_text_section_index.?, .{}); } if (self.zig_got_section_index == null) { - self.zig_got_section_index = try self.allocateAllocSection(.{ + // TODO we don't actually need this section in a relocatable object file + self.zig_got_section_index = try self.addSection(.{ .name = ".zig.got", - .phdr_index = self.phdr_zig_got_index.?, - .alignment = ptr_size, + .type = elf.SHT_PROGBITS, + .addralign = ptr_size, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, + .offset = std.math.maxInt(u64), }); + const shdr = &self.shdrs.items[self.zig_got_section_index.?]; + if (self.phdr_zig_got_index) |phndx| { + const phdr = self.phdrs.items[phndx]; + shdr.sh_addr = phdr.p_vaddr; + shdr.sh_offset = phdr.p_offset; + shdr.sh_size = phdr.p_memsz; + try self.phdr_to_shdr_table.putNoClobber(gpa, self.zig_got_section_index.?, phndx); + } else { + const size = @as(u64, ptr_size) * self.base.options.symbol_count_hint; + const off = self.findFreeSpace(size, ptr_size); + shdr.sh_offset = off; + shdr.sh_size = size; + } } - if (self.zig_rodata_section_index == null) { - self.zig_rodata_section_index = try self.allocateAllocSection(.{ - .name = ".zig.rodata", - .phdr_index = self.phdr_zig_load_ro_index.?, + if (self.zig_data_rel_ro_section_index == null) { + self.zig_data_rel_ro_section_index = try self.addSection(.{ + .name = ".zig.data.rel.ro", + .type = elf.SHT_PROGBITS, + .addralign = 1, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, // TODO rename this section to .data.rel.ro + .offset = std.math.maxInt(u64), }); - try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_rodata_section_index.?, .{}); + const shdr = &self.shdrs.items[self.zig_data_rel_ro_section_index.?]; + if (self.phdr_zig_load_ro_index) |phndx| { + const phdr = self.phdrs.items[phndx]; + shdr.sh_addr = phdr.p_vaddr; + shdr.sh_offset = phdr.p_offset; + shdr.sh_size = phdr.p_memsz; + try self.phdr_to_shdr_table.putNoClobber(gpa, self.zig_data_rel_ro_section_index.?, phndx); + } else { + const size: u64 = 1024; + const off = self.findFreeSpace(size, 1); + shdr.sh_offset = off; + shdr.sh_size = size; + } + try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_data_rel_ro_section_index.?, .{}); } if (self.zig_data_section_index == null) { - self.zig_data_section_index = try self.allocateAllocSection(.{ + self.zig_data_section_index = try self.addSection(.{ .name = ".zig.data", - .phdr_index = self.phdr_zig_load_rw_index.?, - .alignment = ptr_size, + .type = elf.SHT_PROGBITS, + .addralign = ptr_size, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, + .offset = std.math.maxInt(u64), }); + const shdr = &self.shdrs.items[self.zig_data_section_index.?]; + if (self.phdr_zig_load_rw_index) |phndx| { + const phdr = self.phdrs.items[phndx]; + shdr.sh_addr = phdr.p_vaddr; + shdr.sh_offset = phdr.p_offset; + shdr.sh_size = phdr.p_memsz; + try self.phdr_to_shdr_table.putNoClobber(gpa, self.zig_data_section_index.?, phndx); + } else { + const size: u64 = 1024; + const off = self.findFreeSpace(size, ptr_size); + shdr.sh_offset = off; + shdr.sh_size = size; + } try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_data_section_index.?, .{}); } if (self.zig_bss_section_index == null) { - self.zig_bss_section_index = try self.allocateAllocSection(.{ + self.zig_bss_section_index = try self.addSection(.{ .name = ".zig.bss", - .phdr_index = self.phdr_zig_load_zerofill_index.?, - .alignment = ptr_size, - .flags = elf.SHF_ALLOC | elf.SHF_WRITE, .type = elf.SHT_NOBITS, + .addralign = ptr_size, + .flags = elf.SHF_ALLOC | elf.SHF_WRITE, + .offset = 0, }); + const shdr = &self.shdrs.items[self.zig_bss_section_index.?]; + if (self.phdr_zig_load_zerofill_index) |phndx| { + const phdr = self.phdrs.items[phndx]; + shdr.sh_addr = phdr.p_vaddr; + shdr.sh_size = phdr.p_memsz; + try self.phdr_to_shdr_table.putNoClobber(gpa, self.zig_bss_section_index.?, phndx); + } else { + shdr.sh_size = 1024; + } try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_bss_section_index.?, .{}); } @@ -3628,7 +3664,7 @@ fn sortShdrs(self: *Elf) !void { &self.verneed_section_index, &self.zig_text_section_index, &self.zig_got_section_index, - &self.zig_rodata_section_index, + &self.zig_data_rel_ro_section_index, &self.zig_data_section_index, &self.zig_bss_section_index, &self.debug_str_section_index, @@ -4856,7 +4892,7 @@ pub fn isDynLib(self: Elf) bool { pub fn isZigSection(self: Elf, shndx: u16) bool { inline for (&[_]?u16{ self.zig_text_section_index, - self.zig_rodata_section_index, + self.zig_data_rel_ro_section_index, self.zig_data_section_index, self.zig_bss_section_index, self.zig_got_section_index, diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index f5162dbc69..4cb1da9407 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -510,7 +510,7 @@ pub fn lowerAnonDecl( name, tv, decl_alignment, - elf_file.zig_rodata_section_index.?, + elf_file.zig_data_rel_ro_section_index.?, src_loc, ) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, @@ -622,7 +622,7 @@ fn getDeclShdrIndex(self: *ZigObject, elf_file: *Elf, decl_index: Module.Decl.In .Fn => elf_file.zig_text_section_index.?, else => blk: { if (decl.getOwnedVariable(mod)) |variable| { - if (variable.is_const) break :blk elf_file.zig_rodata_section_index.?; + if (variable.is_const) break :blk elf_file.zig_data_rel_ro_section_index.?; if (variable.init.toValue().isUndefDeep(mod)) { const mode = elf_file.base.options.optimize_mode; if (mode == .Debug or mode == .ReleaseSafe) break :blk elf_file.zig_data_section_index.?; @@ -636,7 +636,7 @@ fn getDeclShdrIndex(self: *ZigObject, elf_file: *Elf, decl_index: Module.Decl.In if (is_all_zeroes) break :blk elf_file.zig_bss_section_index.?; break :blk elf_file.zig_data_section_index.?; } - break :blk elf_file.zig_rodata_section_index.?; + break :blk elf_file.zig_data_rel_ro_section_index.?; }, }; return shdr_index; @@ -937,7 +937,7 @@ fn updateLazySymbol( const output_section_index = switch (sym.kind) { .code => elf_file.zig_text_section_index.?, - .const_data => elf_file.zig_rodata_section_index.?, + .const_data => elf_file.zig_data_rel_ro_section_index.?, }; const local_sym = elf_file.symbol(symbol_index); const phdr_index = elf_file.phdr_to_shdr_table.get(output_section_index).?; @@ -991,7 +991,7 @@ pub fn lowerUnnamedConst( name, typed_value, typed_value.ty.abiAlignment(mod), - elf_file.zig_rodata_section_index.?, + elf_file.zig_data_rel_ro_section_index.?, decl.srcLoc(mod), )) { .ok => |sym_index| sym_index, From 74f12d0691292e9730327971bbf6c27a78095ae8 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 1 Nov 2023 17:06:50 +0100 Subject: [PATCH 06/36] elf: remove now obsolete allocateNonAllocSection helper --- src/link/Elf.zig | 89 ++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index be1ae021b3..09658cfd7a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -484,36 +484,6 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 { return start; } -const AllocateNonAllocSectionOpts = struct { - name: [:0]const u8, - size: u64, - alignment: u16 = 1, - flags: u32 = 0, - type: u32 = elf.SHT_PROGBITS, - link: u32 = 0, - info: u32 = 0, - entsize: u64 = 0, -}; - -fn allocateNonAllocSection(self: *Elf, opts: AllocateNonAllocSectionOpts) error{OutOfMemory}!u16 { - const index = try self.addSection(.{ - .name = opts.name, - .type = opts.type, - .flags = opts.flags, - .link = opts.link, - .info = opts.info, - .addralign = opts.alignment, - .entsize = opts.entsize, - .offset = std.math.maxInt(u64), - }); - const shdr = &self.shdrs.items[index]; - const off = self.findFreeSpace(opts.size, opts.alignment); - log.debug("allocating '{s}' from 0x{x} to 0x{x} ", .{ opts.name, off, off + opts.size }); - shdr.sh_offset = off; - shdr.sh_size = opts.size; - return index; -} - /// TODO move to ZigObject pub fn initMetadata(self: *Elf) !void { const gpa = self.base.allocator; @@ -718,48 +688,79 @@ pub fn initMetadata(self: *Elf) !void { if (self.debug_str_section_index == null) { assert(dw.strtab.buffer.items.len == 0); try dw.strtab.buffer.append(gpa, 0); - self.debug_str_section_index = try self.allocateNonAllocSection(.{ + self.debug_str_section_index = try self.addSection(.{ .name = ".debug_str", - .size = @intCast(dw.strtab.buffer.items.len), .flags = elf.SHF_MERGE | elf.SHF_STRINGS, .entsize = 1, + .type = elf.SHT_PROGBITS, + .addralign = 1, + .offset = std.math.maxInt(u64), }); + const shdr = &self.shdrs.items[self.debug_str_section_index.?]; + const size = @as(u64, @intCast(dw.strtab.buffer.items.len)); + const off = self.findFreeSpace(size, 1); + shdr.sh_offset = off; + shdr.sh_size = size; zig_object.debug_strtab_dirty = true; } if (self.debug_info_section_index == null) { - self.debug_info_section_index = try self.allocateNonAllocSection(.{ + self.debug_info_section_index = try self.addSection(.{ .name = ".debug_info", - .size = 200, - .alignment = 1, + .type = elf.SHT_PROGBITS, + .addralign = 1, + .offset = std.math.maxInt(u64), }); + const shdr = &self.shdrs.items[self.debug_info_section_index.?]; + const size: u64 = 200; + const off = self.findFreeSpace(size, 1); + shdr.sh_offset = off; + shdr.sh_size = size; zig_object.debug_info_header_dirty = true; } if (self.debug_abbrev_section_index == null) { - self.debug_abbrev_section_index = try self.allocateNonAllocSection(.{ + self.debug_abbrev_section_index = try self.addSection(.{ .name = ".debug_abbrev", - .size = 128, - .alignment = 1, + .type = elf.SHT_PROGBITS, + .addralign = 1, + .offset = std.math.maxInt(u64), }); + const shdr = &self.shdrs.items[self.debug_abbrev_section_index.?]; + const size: u64 = 128; + const off = self.findFreeSpace(size, 1); + shdr.sh_offset = off; + shdr.sh_size = size; zig_object.debug_abbrev_section_dirty = true; } if (self.debug_aranges_section_index == null) { - self.debug_aranges_section_index = try self.allocateNonAllocSection(.{ + self.debug_aranges_section_index = try self.addSection(.{ .name = ".debug_aranges", - .size = 160, - .alignment = 16, + .type = elf.SHT_PROGBITS, + .addralign = 16, + .offset = std.math.maxInt(u64), }); + const shdr = &self.shdrs.items[self.debug_aranges_section_index.?]; + const size: u64 = 160; + const off = self.findFreeSpace(size, 16); + shdr.sh_offset = off; + shdr.sh_size = size; zig_object.debug_aranges_section_dirty = true; } if (self.debug_line_section_index == null) { - self.debug_line_section_index = try self.allocateNonAllocSection(.{ + self.debug_line_section_index = try self.addSection(.{ .name = ".debug_line", - .size = 250, - .alignment = 1, + .type = elf.SHT_PROGBITS, + .addralign = 1, + .offset = std.math.maxInt(u64), }); + const shdr = &self.shdrs.items[self.debug_line_section_index.?]; + const size: u64 = 250; + const off = self.findFreeSpace(size, 1); + shdr.sh_offset = off; + shdr.sh_size = size; zig_object.debug_line_header_dirty = true; } } From 0ee2ab413fd0370a8ced8d433c71d1d951dab41c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 1 Nov 2023 18:01:29 +0100 Subject: [PATCH 07/36] elf: emit valid section headers table when building an object file --- src/link/Elf.zig | 42 ++++++++++++++++++++++++-------------- src/link/Elf/ZigObject.zig | 24 +++++++++------------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 09658cfd7a..c4bd3d4c10 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -768,15 +768,15 @@ pub fn initMetadata(self: *Elf) !void { pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { const shdr = &self.shdrs.items[shdr_index]; - const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?; - const phdr = &self.phdrs.items[phdr_index]; + const maybe_phdr = if (self.phdr_to_shdr_table.get(shdr_index)) |phndx| &self.phdrs.items[phndx] else null; const is_zerofill = shdr.sh_type == elf.SHT_NOBITS; if (needed_size > self.allocatedSize(shdr.sh_offset) and !is_zerofill) { const existing_size = shdr.sh_size; shdr.sh_size = 0; // Must move the entire section. - const new_offset = self.findFreeSpace(needed_size, self.page_size); + const alignment = if (maybe_phdr) |phdr| phdr.p_align else shdr.sh_addralign; + const new_offset = self.findFreeSpace(needed_size, alignment); log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{ self.getShString(shdr.sh_name), @@ -789,25 +789,27 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { if (amt != existing_size) return error.InputOutput; shdr.sh_offset = new_offset; - phdr.p_offset = new_offset; + if (maybe_phdr) |phdr| phdr.p_offset = new_offset; } shdr.sh_size = needed_size; if (!is_zerofill) { - phdr.p_filesz = needed_size; + if (maybe_phdr) |phdr| phdr.p_filesz = needed_size; } - const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr); - if (needed_size > mem_capacity) { - var err = try self.addErrorWithNotes(2); - try err.addMsg(self, "fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{ - phdr_index, - }); - try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{}); - try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{}); - } + if (maybe_phdr) |phdr| { + const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr); + if (needed_size > mem_capacity) { + var err = try self.addErrorWithNotes(2); + try err.addMsg(self, "fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{ + self.phdr_to_shdr_table.get(shdr_index).?, + }); + try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{}); + try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{}); + } - phdr.p_memsz = needed_size; + phdr.p_memsz = needed_size; + } self.markDirty(shdr_index); } @@ -1499,7 +1501,17 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void { _ = comp; try self.initSections(); + try self.sortShdrs(); + try self.updateSectionSizes(); + + try self.allocateNonAllocSections(); + + if (build_options.enable_logging) { + state_log.debug("{}", .{self.dumpState()}); + } + try self.writeShdrTable(); + try self.writeSyntheticSections(); try self.writeHeader(); } diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 4cb1da9407..82c3fe45f8 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -180,16 +180,16 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf) !void { } if (self.debug_info_header_dirty) { - const text_phdr = &elf_file.phdrs.items[elf_file.phdr_zig_load_re_index.?]; - const low_pc = text_phdr.p_vaddr; - const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; + const text_shdr = elf_file.shdrs.items[elf_file.zig_text_section_index.?]; + const low_pc = text_shdr.sh_addr; + const high_pc = text_shdr.sh_addr + text_shdr.sh_size; try dw.writeDbgInfoHeader(elf_file.base.options.module.?, low_pc, high_pc); self.debug_info_header_dirty = false; } if (self.debug_aranges_section_dirty) { - const text_phdr = &elf_file.phdrs.items[elf_file.phdr_zig_load_re_index.?]; - try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz); + const text_shdr = elf_file.shdrs.items[elf_file.zig_text_section_index.?]; + try dw.writeDbgAranges(text_shdr.sh_addr, text_shdr.sh_size); self.debug_aranges_section_dirty = false; } @@ -731,9 +731,7 @@ fn updateDeclCode( const shdr = elf_file.shdrs.items[shdr_index]; if (shdr.sh_type != elf.SHT_NOBITS) { - const phdr_index = elf_file.phdr_to_shdr_table.get(shdr_index).?; - const section_offset = sym.value - elf_file.phdrs.items[phdr_index].p_vaddr; - const file_offset = shdr.sh_offset + section_offset; + const file_offset = shdr.sh_offset + sym.value - shdr.sh_addr; try elf_file.base.file.?.pwriteAll(code, file_offset); } } @@ -940,7 +938,6 @@ fn updateLazySymbol( .const_data => elf_file.zig_data_rel_ro_section_index.?, }; const local_sym = elf_file.symbol(symbol_index); - const phdr_index = elf_file.phdr_to_shdr_table.get(output_section_index).?; local_sym.name_offset = name_str_index; local_sym.output_section_index = output_section_index; const local_esym = &self.local_esyms.items(.elf_sym)[local_sym.esym_index]; @@ -963,8 +960,8 @@ fn updateLazySymbol( const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, elf_file); try elf_file.zig_got.writeOne(elf_file, gop.index); - const section_offset = atom_ptr.value - elf_file.phdrs.items[phdr_index].p_vaddr; - const file_offset = elf_file.shdrs.items[output_section_index].sh_offset + section_offset; + const shdr = elf_file.shdrs.items[output_section_index]; + const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr; try elf_file.base.file.?.pwriteAll(code, file_offset); } @@ -1038,7 +1035,6 @@ fn lowerConst( .fail => |em| return .{ .fail = em }, }; - 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 self.insertString(gpa, name); local_sym.name_offset = name_str_index; @@ -1061,8 +1057,8 @@ fn lowerConst( local_sym.value = atom_ptr.value; local_esym.st_value = atom_ptr.value; - const section_offset = atom_ptr.value - elf_file.phdrs.items[phdr_index].p_vaddr; - const file_offset = elf_file.shdrs.items[output_section_index].sh_offset + section_offset; + const shdr = elf_file.shdrs.items[output_section_index]; + const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr; try elf_file.base.file.?.pwriteAll(code, file_offset); return .{ .ok = sym_index }; From 21853bc310fb6029f949bcf9aca59f69c768a664 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 1 Nov 2023 20:35:45 +0100 Subject: [PATCH 08/36] elf: emit .rela shdrs for output sections --- src/link/Elf.zig | 155 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 107 insertions(+), 48 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index c4bd3d4c10..d62d786143 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -99,15 +99,20 @@ plt_got: PltGotSection = .{}, copy_rel: CopyRelSection = .{}, /// .rela.plt section rela_plt: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, -/// .zig.got section +/// .got.zig section zig_got: ZigGotSection = .{}, -/// Tracked section headers with incremental updates to Zig object +/// 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, +zig_got_rela_section_index: ?u16 = null, debug_info_section_index: ?u16 = null, debug_abbrev_section_index: ?u16 = null, @@ -491,6 +496,21 @@ pub fn initMetadata(self: *Elf) !void { const ptr_bit_width = self.base.options.target.ptrBitWidth(); const is_linux = self.base.options.target.os.tag == .linux; + const fillSection = struct { + fn fillSection(elf_file: *Elf, shdr: *elf.Elf64_Shdr, size: u64, phndx: ?u16) void { + if (elf_file.isObject()) { + const off = elf_file.findFreeSpace(size, shdr.sh_addralign); + shdr.sh_offset = off; + shdr.sh_size = size; + } else { + const phdr = elf_file.phdrs.items[phndx.?]; + shdr.sh_addr = phdr.p_vaddr; + shdr.sh_offset = phdr.p_offset; + shdr.sh_size = phdr.p_memsz; + } + } + }.fillSection; + comptime assert(number_of_zig_segments == 5); if (!self.isObject()) { @@ -569,24 +589,25 @@ pub fn initMetadata(self: *Elf) !void { if (self.zig_text_section_index == null) { self.zig_text_section_index = try self.addSection(.{ - .name = ".zig.text", + .name = ".text.zig", .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, .addralign = 1, .offset = std.math.maxInt(u64), }); const shdr = &self.shdrs.items[self.zig_text_section_index.?]; - if (self.phdr_zig_load_re_index) |phndx| { - const phdr = self.phdrs.items[phndx]; - shdr.sh_addr = phdr.p_vaddr; - shdr.sh_offset = phdr.p_offset; - shdr.sh_size = phdr.p_memsz; - try self.phdr_to_shdr_table.putNoClobber(gpa, self.zig_text_section_index.?, phndx); + fillSection(self, shdr, self.base.options.program_code_size_hint, self.phdr_zig_load_re_index); + if (self.isObject()) { + self.zig_text_rela_section_index = try self.addRelaShdr( + ".rela.text.zig", + self.zig_text_section_index.?, + ); } else { - const size = self.base.options.program_code_size_hint; - const off = self.findFreeSpace(size, 1); - shdr.sh_offset = off; - shdr.sh_size = size; + try self.phdr_to_shdr_table.putNoClobber( + gpa, + self.zig_text_section_index.?, + self.phdr_zig_load_re_index.?, + ); } try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_text_section_index.?, .{}); } @@ -594,78 +615,80 @@ pub fn initMetadata(self: *Elf) !void { if (self.zig_got_section_index == null) { // TODO we don't actually need this section in a relocatable object file self.zig_got_section_index = try self.addSection(.{ - .name = ".zig.got", + .name = ".got.zig", .type = elf.SHT_PROGBITS, .addralign = ptr_size, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, .offset = std.math.maxInt(u64), }); const shdr = &self.shdrs.items[self.zig_got_section_index.?]; - if (self.phdr_zig_got_index) |phndx| { - const phdr = self.phdrs.items[phndx]; - shdr.sh_addr = phdr.p_vaddr; - shdr.sh_offset = phdr.p_offset; - shdr.sh_size = phdr.p_memsz; - try self.phdr_to_shdr_table.putNoClobber(gpa, self.zig_got_section_index.?, phndx); - } else { - const size = @as(u64, ptr_size) * self.base.options.symbol_count_hint; - const off = self.findFreeSpace(size, ptr_size); - shdr.sh_offset = off; - shdr.sh_size = size; + fillSection( + self, + shdr, + @as(u64, ptr_size) * self.base.options.symbol_count_hint, + self.phdr_zig_got_index, + ); + if (self.isObject()) { + self.zig_got_rela_section_index = try self.addRelaShdr( + ".rela.got.zig", + self.zig_got_section_index.?, + ); } } if (self.zig_data_rel_ro_section_index == null) { self.zig_data_rel_ro_section_index = try self.addSection(.{ - .name = ".zig.data.rel.ro", + .name = ".data.rel.ro.zig", .type = elf.SHT_PROGBITS, .addralign = 1, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, // TODO rename this section to .data.rel.ro .offset = std.math.maxInt(u64), }); const shdr = &self.shdrs.items[self.zig_data_rel_ro_section_index.?]; - if (self.phdr_zig_load_ro_index) |phndx| { - const phdr = self.phdrs.items[phndx]; - shdr.sh_addr = phdr.p_vaddr; - shdr.sh_offset = phdr.p_offset; - shdr.sh_size = phdr.p_memsz; - try self.phdr_to_shdr_table.putNoClobber(gpa, self.zig_data_rel_ro_section_index.?, phndx); + fillSection(self, shdr, 1024, self.phdr_zig_load_ro_index); + if (self.isObject()) { + self.zig_data_rel_ro_rela_section_index = try self.addRelaShdr( + ".rela.data.rel.ro.zig", + self.zig_data_rel_ro_section_index.?, + ); } else { - const size: u64 = 1024; - const off = self.findFreeSpace(size, 1); - shdr.sh_offset = off; - shdr.sh_size = size; + try self.phdr_to_shdr_table.putNoClobber( + gpa, + self.zig_data_rel_ro_section_index.?, + self.phdr_zig_load_ro_index.?, + ); } try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_data_rel_ro_section_index.?, .{}); } if (self.zig_data_section_index == null) { self.zig_data_section_index = try self.addSection(.{ - .name = ".zig.data", + .name = ".data.zig", .type = elf.SHT_PROGBITS, .addralign = ptr_size, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, .offset = std.math.maxInt(u64), }); const shdr = &self.shdrs.items[self.zig_data_section_index.?]; - if (self.phdr_zig_load_rw_index) |phndx| { - const phdr = self.phdrs.items[phndx]; - shdr.sh_addr = phdr.p_vaddr; - shdr.sh_offset = phdr.p_offset; - shdr.sh_size = phdr.p_memsz; - try self.phdr_to_shdr_table.putNoClobber(gpa, self.zig_data_section_index.?, phndx); + fillSection(self, shdr, 1024, self.phdr_zig_load_rw_index); + if (self.isObject()) { + self.zig_data_rela_section_index = try self.addRelaShdr( + ".rela.data.zig", + self.zig_data_section_index.?, + ); } else { - const size: u64 = 1024; - const off = self.findFreeSpace(size, ptr_size); - shdr.sh_offset = off; - shdr.sh_size = size; + try self.phdr_to_shdr_table.putNoClobber( + gpa, + self.zig_data_section_index.?, + self.phdr_zig_load_rw_index.?, + ); } try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_data_section_index.?, .{}); } if (self.zig_bss_section_index == null) { self.zig_bss_section_index = try self.addSection(.{ - .name = ".zig.bss", + .name = ".bss.zig", .type = elf.SHT_NOBITS, .addralign = ptr_size, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, @@ -3676,9 +3699,13 @@ 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_got_rela_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, @@ -3737,6 +3764,18 @@ fn sortShdrs(self: *Elf) !void { shdr.sh_info = self.plt_section_index.?; } + for (&[_]?u16{ + self.zig_text_rela_section_index, + self.zig_got_rela_section_index, + self.zig_data_rel_ro_rela_section_index, + self.zig_data_rela_section_index, + }) |maybe_index| { + const index = maybe_index orelse continue; + const shdr = &self.shdrs.items[index]; + shdr.sh_link = self.symtab_section_index.?; + shdr.sh_info = backlinks[shdr.sh_info]; + } + { var phdr_to_shdr_table = try self.phdr_to_shdr_table.clone(gpa); defer phdr_to_shdr_table.deinit(gpa); @@ -4955,6 +4994,26 @@ fn addPhdr(self: *Elf, opts: struct { return index; } +fn addRelaShdr(self: *Elf, name: [:0]const u8, shndx: u16) !u16 { + const entsize: u64 = switch (self.ptr_width) { + .p32 => @sizeOf(elf.Elf32_Rela), + .p64 => @sizeOf(elf.Elf64_Rela), + }; + const addralign: u64 = switch (self.ptr_width) { + .p32 => @alignOf(elf.Elf32_Rela), + .p64 => @alignOf(elf.Elf64_Rela), + }; + return self.addSection(.{ + .name = name, + .type = elf.SHT_RELA, + .flags = elf.SHF_INFO_LINK, + .entsize = entsize, + .info = shndx, + .addralign = addralign, + .offset = std.math.maxInt(u64), + }); +} + pub const AddSectionOpts = struct { name: [:0]const u8, type: u32 = elf.SHT_NULL, From ec2671d16b9363ad7f39312552456cf5e449e930 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 1 Nov 2023 22:51:18 +0100 Subject: [PATCH 09/36] elf: update .rela section sizes; skip .got.zig when emitting object --- src/link/Elf.zig | 41 ++++++++++++++++------------ src/link/Elf/ZigObject.zig | 56 ++++++++++++++++++++++++++++++++------ 2 files changed, 72 insertions(+), 25 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index d62d786143..12ff4a76c9 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -112,7 +112,6 @@ 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, -zig_got_rela_section_index: ?u16 = null, debug_info_section_index: ?u16 = null, debug_abbrev_section_index: ?u16 = null, @@ -612,8 +611,7 @@ pub fn initMetadata(self: *Elf) !void { try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_text_section_index.?, .{}); } - if (self.zig_got_section_index == null) { - // TODO we don't actually need this section in a relocatable object file + if (self.zig_got_section_index == null and !self.isObject()) { self.zig_got_section_index = try self.addSection(.{ .name = ".got.zig", .type = elf.SHT_PROGBITS, @@ -622,18 +620,11 @@ pub fn initMetadata(self: *Elf) !void { .offset = std.math.maxInt(u64), }); const shdr = &self.shdrs.items[self.zig_got_section_index.?]; - fillSection( - self, - shdr, - @as(u64, ptr_size) * self.base.options.symbol_count_hint, - self.phdr_zig_got_index, - ); - if (self.isObject()) { - self.zig_got_rela_section_index = try self.addRelaShdr( - ".rela.got.zig", - self.zig_got_section_index.?, - ); - } + const phndx = self.phdr_zig_got_index.?; + const phdr = self.phdrs.items[phndx]; + shdr.sh_addr = phdr.p_vaddr; + shdr.sh_offset = phdr.p_offset; + shdr.sh_size = phdr.p_memsz; } if (self.zig_data_rel_ro_section_index == null) { @@ -3701,7 +3692,6 @@ fn sortShdrs(self: *Elf) !void { &self.zig_text_section_index, &self.zig_text_rela_section_index, &self.zig_got_section_index, - &self.zig_got_rela_section_index, &self.zig_data_rel_ro_section_index, &self.zig_data_rel_ro_rela_section_index, &self.zig_data_section_index, @@ -3766,7 +3756,6 @@ fn sortShdrs(self: *Elf) !void { for (&[_]?u16{ self.zig_text_rela_section_index, - self.zig_got_rela_section_index, self.zig_data_rel_ro_rela_section_index, self.zig_data_rela_section_index, }) |maybe_index| { @@ -3776,6 +3765,20 @@ fn sortShdrs(self: *Elf) !void { shdr.sh_info = backlinks[shdr.sh_info]; } + { + var last_atom_and_free_list_table = try self.last_atom_and_free_list_table.clone(gpa); + defer last_atom_and_free_list_table.deinit(gpa); + + self.last_atom_and_free_list_table.clearRetainingCapacity(); + + var it = last_atom_and_free_list_table.iterator(); + while (it.next()) |entry| { + const shndx = entry.key_ptr.*; + const meta = entry.value_ptr.*; + self.last_atom_and_free_list_table.putAssumeCapacityNoClobber(backlinks[shndx], meta); + } + } + { var phdr_to_shdr_table = try self.phdr_to_shdr_table.clone(gpa); defer phdr_to_shdr_table.deinit(gpa); @@ -3832,6 +3835,10 @@ fn updateSectionSizes(self: *Elf) !void { } } + if (self.zigObjectPtr()) |zig_object| { + zig_object.updateRelaSectionSizes(self); + } + if (self.eh_frame_section_index) |index| { self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self); } diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 82c3fe45f8..2b4872275c 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -398,6 +398,40 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void { } } +pub fn updateRelaSectionSizes(self: ZigObject, elf_file: *Elf) void { + _ = self; + + 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 symbol(self: *ZigObject, index: Symbol.Index) Symbol.Index { const is_global = index & global_symbol_bit != 0; const actual_index = index & symbol_mask; @@ -689,10 +723,12 @@ fn updateDeclCode( sym.value = atom_ptr.value; esym.st_value = atom_ptr.value; - log.debug(" (writing new offset table entry)", .{}); - assert(sym.flags.has_zig_got); - const extra = sym.extra(elf_file).?; - try elf_file.zig_got.writeOne(elf_file, extra.zig_got); + if (!elf_file.isObject()) { + log.debug(" (writing new offset table entry)", .{}); + assert(sym.flags.has_zig_got); + const extra = sym.extra(elf_file).?; + try elf_file.zig_got.writeOne(elf_file, extra.zig_got); + } } } else if (code.len < old_size) { atom_ptr.shrink(elf_file); @@ -704,8 +740,10 @@ fn updateDeclCode( sym.value = atom_ptr.value; esym.st_value = atom_ptr.value; - const gop = try sym.getOrCreateZigGotEntry(sym_index, elf_file); - try elf_file.zig_got.writeOne(elf_file, gop.index); + if (!elf_file.isObject()) { + const gop = try sym.getOrCreateZigGotEntry(sym_index, elf_file); + try elf_file.zig_got.writeOne(elf_file, gop.index); + } } if (elf_file.base.child_pid) |pid| { @@ -957,8 +995,10 @@ fn updateLazySymbol( local_sym.value = atom_ptr.value; local_esym.st_value = atom_ptr.value; - const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, elf_file); - try elf_file.zig_got.writeOne(elf_file, gop.index); + if (!elf_file.isObject()) { + const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, elf_file); + try elf_file.zig_got.writeOne(elf_file, gop.index); + } const shdr = elf_file.shdrs.items[output_section_index]; const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr; From 3606b5df3f185edd69af823d3cae213e4b93ff39 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 1 Nov 2023 23:08:50 +0100 Subject: [PATCH 10/36] elf: improve Symbol to handle emitting relocatable object files --- src/link/Elf.zig | 2 +- src/link/Elf/Symbol.zig | 5 +++-- src/link/Elf/ZigObject.zig | 1 + src/link/Elf/file.zig | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 12ff4a76c9..53c66b046e 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1943,7 +1943,7 @@ fn scanRelocs(self: *Elf) !void { for (self.symbols.items, 0..) |*sym, i| { const index = @as(u32, @intCast(i)); - if (!sym.isLocal() and !sym.flags.has_dynamic) { + if (!sym.isLocal(self) and !sym.flags.has_dynamic) { log.debug("'{s}' is non-local", .{sym.name(self)}); try self.dynsym.addSymbol(index, self); } diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index b75c458e68..01a8129b32 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -42,7 +42,8 @@ pub fn outputShndx(symbol: Symbol) ?u16 { return symbol.output_section_index; } -pub fn isLocal(symbol: Symbol) bool { +pub fn isLocal(symbol: Symbol, elf_file: *Elf) bool { + if (elf_file.isObject()) return symbol.elfSym(elf_file).st_bind() == elf.STB_LOCAL; return !(symbol.flags.import or symbol.flags.@"export"); } @@ -208,7 +209,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { const esym = symbol.elfSym(elf_file); const st_type = symbol.type(elf_file); const st_bind: u8 = blk: { - if (symbol.isLocal()) break :blk 0; + if (symbol.isLocal(elf_file)) break :blk 0; if (symbol.flags.weak) break :blk elf.STB_WEAK; if (file_ptr == .shared_object) break :blk elf.STB_GLOBAL; break :blk esym.st_bind(); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 2b4872275c..8089dda565 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -1192,6 +1192,7 @@ pub fn updateExports( global_esym.st_shndx = esym.st_shndx; global_esym.st_info = (stb_bits << 4) | stt_bits; global_esym.st_name = name_off; + global_esym.st_size = esym.st_size; self.global_esyms.items(.shndx)[actual_esym_index] = esym_shndx; } } diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index a401203ba7..84d8ba396e 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -150,7 +150,7 @@ pub const File = union(enum) { 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()) { + if (global.isLocal(elf_file)) { output_symtab_size.nlocals += 1; } else { output_symtab_size.nglobals += 1; @@ -181,7 +181,7 @@ 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()) { + 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); From 5698be3ef042eb8c17d75f51fd15d73eebd64a7b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 1 Nov 2023 23:34:24 +0100 Subject: [PATCH 11/36] elf: write out contents of .rela sections --- src/link/Elf.zig | 4 +++ src/link/Elf/ZigObject.zig | 54 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 53c66b046e..1c8f54a1f5 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -4400,6 +4400,10 @@ fn updateSymtabSize(self: *Elf) !void { fn writeSyntheticSections(self: *Elf) !void { const gpa = self.base.allocator; + if (self.zigObjectPtr()) |zig_object| { + try zig_object.writeRelaSections(self); + } + if (self.interp_section_index) |shndx| { const shdr = self.shdrs.items[shndx]; const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 8089dda565..bb4d37f9ee 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -432,6 +432,60 @@ pub fn updateRelaSectionSizes(self: ZigObject, elf_file: *Elf) void { } } +pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void { + _ = self; + 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(@divExact(shdr.sh_size, shdr.sh_entsize)); + + while (true) { + for (atom.relocs(elf_file)) |rel| { + relocs.appendAssumeCapacity(switch (rel.r_type()) { + Elf.R_X86_64_ZIG_GOT32 => .{ + .r_offset = rel.r_offset, + .r_addend = rel.r_addend, + .r_info = (@as(u64, @intCast(rel.r_sym())) << 32) | elf.R_X86_64_32, + }, + Elf.R_X86_64_ZIG_GOTPCREL => .{ + .r_offset = rel.r_offset, + .r_addend = rel.r_addend, + .r_info = (@as(u64, @intCast(rel.r_sym())) << 32) | elf.R_X86_64_PC32, + }, + else => rel, + }); + } + 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); + } +} + pub fn symbol(self: *ZigObject, index: Symbol.Index) Symbol.Index { const is_global = index & global_symbol_bit != 0; const actual_index = index & symbol_mask; From e8f522122ac83e94b2fe58aa369917f3686743c5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 1 Nov 2023 23:42:01 +0100 Subject: [PATCH 12/36] elf: fix properly updating .got.zig section --- src/link/Elf.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 1c8f54a1f5..c09cff4cc6 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -625,6 +625,11 @@ pub fn initMetadata(self: *Elf) !void { shdr.sh_addr = phdr.p_vaddr; shdr.sh_offset = phdr.p_offset; shdr.sh_size = phdr.p_memsz; + try self.phdr_to_shdr_table.putNoClobber( + gpa, + self.zig_got_section_index.?, + self.phdr_zig_got_index.?, + ); } if (self.zig_data_rel_ro_section_index == null) { From 8055f687659de87ce0101cb3e459699b4541a8b7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 2 Nov 2023 00:20:56 +0100 Subject: [PATCH 13/36] elf: make sure we never emit .got.zig relocs when linking object files --- src/arch/x86_64/CodeGen.zig | 4 ++-- src/codegen.zig | 2 +- src/link/Elf/Symbol.zig | 6 ++++++ src/link/Elf/ZigObject.zig | 36 +++++++++++++++++++----------------- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index d923f32bdd..b65a704351 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -10235,7 +10235,7 @@ fn genCall(self: *Self, info: union(enum) { if (self.bin_file.cast(link.File.Elf)) |elf_file| { const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl); const sym = elf_file.symbol(sym_index); - _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); + try sym.createZigGotEntry(sym_index, elf_file); if (self.bin_file.options.pic) { const callee_reg: Register = switch (resolved_cc) { .SysV => callee: { @@ -13103,7 +13103,7 @@ fn genLazySymbolRef( const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const sym = elf_file.symbol(sym_index); - _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); + try sym.createZigGotEntry(sym_index, elf_file); if (self.bin_file.options.pic) { switch (tag) { diff --git a/src/codegen.zig b/src/codegen.zig index bb2a9f9324..c5062228de 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -909,7 +909,7 @@ fn genDeclRef( } const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); const sym = elf_file.symbol(sym_index); - _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); + try sym.createZigGotEntry(sym_index, elf_file); return GenResult.mcv(.{ .load_symbol = sym.esym_index }); } else if (bin_file.cast(link.File.MachO)) |macho_file| { if (is_extern) { diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 01a8129b32..113020e84e 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -168,11 +168,17 @@ const GetOrCreateZigGotEntryResult = struct { }; pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !GetOrCreateZigGotEntryResult { + assert(!elf_file.isObject()); if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.zig_got }; const index = try elf_file.zig_got.addSymbol(symbol_index, elf_file); return .{ .found_existing = false, .index = index }; } +pub fn createZigGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !void { + if (elf_file.isObject()) return; + _ = try symbol.getOrCreateZigGotEntry(symbol_index, elf_file); +} + pub fn zigGotAddress(symbol: Symbol, elf_file: *Elf) u64 { if (!symbol.flags.has_zig_got) return 0; const extras = symbol.extra(elf_file).?; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index bb4d37f9ee..2160e5a773 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -433,7 +433,6 @@ pub fn updateRelaSectionSizes(self: ZigObject, elf_file: *Elf) void { } pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void { - _ = self; const gpa = elf_file.base.allocator; for (&[_]?u16{ @@ -454,18 +453,18 @@ pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void { while (true) { for (atom.relocs(elf_file)) |rel| { - relocs.appendAssumeCapacity(switch (rel.r_type()) { - Elf.R_X86_64_ZIG_GOT32 => .{ - .r_offset = rel.r_offset, - .r_addend = rel.r_addend, - .r_info = (@as(u64, @intCast(rel.r_sym())) << 32) | elf.R_X86_64_32, - }, - Elf.R_X86_64_ZIG_GOTPCREL => .{ - .r_offset = rel.r_offset, - .r_addend = rel.r_addend, - .r_info = (@as(u64, @intCast(rel.r_sym())) << 32) | elf.R_X86_64_PC32, - }, - else => rel, + var r_sym = rel.r_sym() & symbol_mask; + if (self.isGlobal(rel.r_sym())) r_sym += @intCast(self.local_esyms.slice().len + 1); + 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 = rel.r_offset, + .r_addend = rel.r_addend, + .r_info = (@as(u64, @intCast(r_sym)) << 32) | r_type, }); } if (elf_file.atom(atom.prev_index)) |prev| { @@ -486,17 +485,20 @@ pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void { } } +pub fn isGlobal(self: ZigObject, index: Symbol.Index) bool { + _ = self; + return index & global_symbol_bit != 0; +} + pub fn symbol(self: *ZigObject, index: Symbol.Index) Symbol.Index { - const is_global = index & global_symbol_bit != 0; const actual_index = index & symbol_mask; - if (is_global) return self.global_symbols.items[actual_index]; + if (self.isGlobal(index)) return self.global_symbols.items[actual_index]; return self.local_symbols.items[actual_index]; } pub fn elfSym(self: *ZigObject, index: Symbol.Index) *elf.Elf64_Sym { - const is_global = index & global_symbol_bit != 0; const actual_index = index & symbol_mask; - if (is_global) return &self.global_esyms.items(.elf_sym)[actual_index]; + if (self.isGlobal(index)) return &self.global_esyms.items(.elf_sym)[actual_index]; return &self.local_esyms.items(.elf_sym)[actual_index]; } From dbe13200f1b5a20367b9e5ec288a8ec27fa2d505 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 2 Nov 2023 01:33:06 +0100 Subject: [PATCH 14/36] elf: emit STT_SECTION symbols --- src/link/Elf.zig | 16 +++++------ src/link/Elf/Symbol.zig | 2 ++ src/link/Elf/ZigObject.zig | 54 +++++++++++++++++++++++++++++--------- src/link/Elf/file.zig | 3 ++- 4 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index c09cff4cc6..20c3ba842c 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -494,6 +494,7 @@ pub fn initMetadata(self: *Elf) !void { const ptr_size = self.ptrWidthBytes(); const ptr_bit_width = self.base.options.target.ptrBitWidth(); const is_linux = self.base.options.target.os.tag == .linux; + const zig_object = self.zigObjectPtr().?; const fillSection = struct { fn fillSection(elf_file: *Elf, shdr: *elf.Elf64_Shdr, size: u64, phndx: ?u16) void { @@ -597,6 +598,7 @@ 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.isObject()) { + try zig_object.addSectionSymbol(self.zig_text_section_index.?, self); self.zig_text_rela_section_index = try self.addRelaShdr( ".rela.text.zig", self.zig_text_section_index.?, @@ -643,6 +645,7 @@ 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.isObject()) { + try zig_object.addSectionSymbol(self.zig_data_rel_ro_section_index.?, self); self.zig_data_rel_ro_rela_section_index = try self.addRelaShdr( ".rela.data.rel.ro.zig", self.zig_data_rel_ro_section_index.?, @@ -668,6 +671,7 @@ 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.isObject()) { + try zig_object.addSectionSymbol(self.zig_data_section_index.?, self); self.zig_data_rela_section_index = try self.addRelaShdr( ".rela.data.zig", self.zig_data_section_index.?, @@ -697,12 +701,12 @@ pub fn initMetadata(self: *Elf) !void { shdr.sh_size = phdr.p_memsz; try self.phdr_to_shdr_table.putNoClobber(gpa, self.zig_bss_section_index.?, phndx); } else { + try zig_object.addSectionSymbol(self.zig_bss_section_index.?, self); shdr.sh_size = 1024; } try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_bss_section_index.?, .{}); } - const zig_object = self.zigObjectPtr().?; if (zig_object.dwarf) |*dw| { if (self.debug_str_section_index == null) { assert(dw.strtab.buffer.items.len == 0); @@ -3801,23 +3805,19 @@ fn sortShdrs(self: *Elf) !void { if (self.zigObjectPtr()) |zig_object| { for (zig_object.atoms.items) |atom_index| { const atom_ptr = self.atom(atom_index) orelse continue; - if (!atom_ptr.flags.alive) continue; - const out_shndx = atom_ptr.outputShndx() orelse continue; - atom_ptr.output_section_index = backlinks[out_shndx]; + atom_ptr.output_section_index = backlinks[atom_ptr.output_section_index]; } for (zig_object.locals()) |local_index| { const local = self.symbol(local_index); - const atom_ptr = local.atom(self) orelse continue; - if (!atom_ptr.flags.alive) continue; - const out_shndx = local.outputShndx() orelse continue; - local.output_section_index = backlinks[out_shndx]; + local.output_section_index = backlinks[local.output_section_index]; } for (zig_object.globals()) |global_index| { const global = self.symbol(global_index); const atom_ptr = global.atom(self) orelse continue; if (!atom_ptr.flags.alive) continue; + // TODO claim unresolved for objects if (global.file(self).?.index() != zig_object.index) continue; const out_shndx = global.outputShndx() orelse continue; global.output_section_index = backlinks[out_shndx]; diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 113020e84e..957be0d069 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -223,6 +223,8 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { const st_shndx = blk: { if (symbol.flags.has_copy_rel) break :blk elf_file.copy_rel_section_index.?; if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; + // TODO I think this is wrong and obsolete + if (elf_file.isObject() and st_type == elf.STT_SECTION) break :blk symbol.outputShndx().?; if (symbol.atom(elf_file) == null and file_ptr != .linker_defined) break :blk elf.SHN_ABS; break :blk symbol.outputShndx() orelse elf.SHN_UNDEF; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 2160e5a773..28c28410e7 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -87,7 +87,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { const esym_index = try self.addLocalEsym(gpa); const esym = &self.local_esyms.items(.elf_sym)[esym_index]; esym.st_name = name_off; - esym.st_info |= elf.STT_FILE; + esym.st_info = elf.STT_FILE; esym.st_shndx = elf.SHN_ABS; symbol_ptr.esym_index = esym_index; @@ -284,6 +284,22 @@ pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index { return symbol_index; } +pub fn addSectionSymbol(self: *ZigObject, shndx: u16, elf_file: *Elf) !void { + assert(elf_file.isObject()); + const gpa = elf_file.base.allocator; + const symbol_index = try elf_file.addSymbol(); + try self.local_symbols.append(gpa, symbol_index); + const symbol_ptr = elf_file.symbol(symbol_index); + symbol_ptr.file_index = self.index; + symbol_ptr.output_section_index = shndx; + + const esym_index = try self.addLocalEsym(gpa); + const esym = &self.local_esyms.items(.elf_sym)[esym_index]; + esym.st_info = elf.STT_SECTION; + esym.st_shndx = shndx; + symbol_ptr.esym_index = esym_index; +} + /// TODO actually create fake input shdrs and return that instead. pub fn inputShdr(self: ZigObject, atom_index: Atom.Index, elf_file: *Elf) Object.ElfShdr { _ = self; @@ -435,6 +451,16 @@ pub fn updateRelaSectionSizes(self: ZigObject, elf_file: *Elf) void { pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void { const gpa = elf_file.base.allocator; + // const getSectionSymbol = struct { + // fn getSectionSymbol(zig_object: ZigObject, shndx: u16, ctx: *Elf) Symbol.Index { + // for (zig_object.locals()) |local_index| { + // const local = ctx.symbol(local_index); + // if (local.type(ctx) == elf.STT_SECTION and local.output_section_index == shndx) + // return local.esym_index; + // } else unreachable; + // } + // }.getSectionSymbol; + for (&[_]?u16{ elf_file.zig_text_rela_section_index, elf_file.zig_data_rel_ro_rela_section_index, @@ -453,8 +479,13 @@ pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void { while (true) { for (atom.relocs(elf_file)) |rel| { - var r_sym = rel.r_sym() & symbol_mask; - if (self.isGlobal(rel.r_sym())) r_sym += @intCast(self.local_esyms.slice().len + 1); + const target = elf_file.symbol(self.symbol(rel.r_sym())); + const r_offset = target.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; + // getSectionSymbol(self, target.outputShndx().?, elf_file); const r_type = switch (rel.r_type()) { Elf.R_X86_64_ZIG_GOT32, Elf.R_X86_64_ZIG_GOTPCREL, @@ -462,9 +493,9 @@ pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void { else => |r_type| r_type, }; relocs.appendAssumeCapacity(.{ - .r_offset = rel.r_offset, + .r_offset = r_offset, .r_addend = rel.r_addend, - .r_info = (@as(u64, @intCast(r_sym)) << 32) | r_type, + .r_info = (@as(u64, @intCast(r_sym + 1)) << 32) | r_type, }); } if (elf_file.atom(atom.prev_index)) |prev| { @@ -485,28 +516,27 @@ pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void { } } -pub fn isGlobal(self: ZigObject, index: Symbol.Index) bool { - _ = self; +inline fn isGlobal(index: Symbol.Index) bool { return index & global_symbol_bit != 0; } -pub fn symbol(self: *ZigObject, index: Symbol.Index) Symbol.Index { +pub fn symbol(self: ZigObject, index: Symbol.Index) Symbol.Index { const actual_index = index & symbol_mask; - if (self.isGlobal(index)) return self.global_symbols.items[actual_index]; + if (isGlobal(index)) return self.global_symbols.items[actual_index]; return self.local_symbols.items[actual_index]; } pub fn elfSym(self: *ZigObject, index: Symbol.Index) *elf.Elf64_Sym { const actual_index = index & symbol_mask; - if (self.isGlobal(index)) return &self.global_esyms.items(.elf_sym)[actual_index]; + if (isGlobal(index)) return &self.global_esyms.items(.elf_sym)[actual_index]; return &self.local_esyms.items(.elf_sym)[actual_index]; } -pub fn locals(self: *ZigObject) []const Symbol.Index { +pub fn locals(self: ZigObject) []const Symbol.Index { return self.local_symbols.items; } -pub fn globals(self: *ZigObject) []const Symbol.Index { +pub fn globals(self: ZigObject) []const Symbol.Index { return self.global_symbols.items; } diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 84d8ba396e..a168ec541b 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -136,7 +136,8 @@ pub const File = union(enum) { 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, + elf.STT_SECTION => if (!elf_file.isObject()) continue, + elf.STT_NOTYPE => continue, else => {}, } local.flags.output_symtab = true; From 7c5c59191ec1d8879b70c47c54577e23f6262cc4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 2 Nov 2023 11:45:22 +0100 Subject: [PATCH 15/36] elf: claim unresolved dangling symbols as undef externs when emitting object --- src/link/Elf.zig | 8 +++ src/link/Elf/ZigObject.zig | 101 ++++++++++++++++++++++++++++++++----- 2 files changed, 97 insertions(+), 12 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 20c3ba842c..42d3c3ef20 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1523,6 +1523,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void { _ = comp; + self.claimUnresolvedObject(); + try self.initSections(); try self.sortShdrs(); try self.updateSectionSizes(); @@ -1924,6 +1926,12 @@ fn claimUnresolved(self: *Elf) void { } } +fn claimUnresolvedObject(self: *Elf) void { + if (self.zigObjectPtr()) |zig_object| { + zig_object.claimUnresolvedObject(self); + } +} + /// In scanRelocs we will go over all live atoms and scan their relocs. /// This will help us work out what synthetics to emit, GOT indirection, etc. /// This is also the point where we will report undefined symbols for any diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 28c28410e7..3d48cd405e 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -210,6 +210,8 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf) !void { self.saveDebugSectionsSizes(elf_file); } + try self.sortSymbols(elf_file); + // The point of flushModule() is to commit changes, so in theory, nothing should // be dirty after this. However, it is possible for some things to remain // dirty because they fail to be written in the event of compile errors, @@ -353,7 +355,7 @@ pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) void { } } -pub fn claimUnresolved(self: *ZigObject, elf_file: *Elf) void { +pub fn claimUnresolved(self: ZigObject, elf_file: *Elf) void { for (self.globals(), 0..) |index, i| { const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit; const esym = self.global_esyms.items(.elf_sym)[i]; @@ -381,6 +383,26 @@ pub fn claimUnresolved(self: *ZigObject, elf_file: *Elf) void { } } +pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void { + for (self.globals(), 0..) |index, i| { + const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit; + const esym = self.global_esyms.items(.elf_sym)[i]; + + if (esym.st_shndx != elf.SHN_UNDEF) continue; + + const global = elf_file.symbol(index); + if (global.file(elf_file)) |file| { + if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF or + file.index() <= self.index) continue; + } + + global.value = 0; + global.atom_index = 0; + global.esym_index = esym_index; + global.file_index = self.index; + } +} + pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void { for (self.atoms.items) |atom_index| { const atom = elf_file.atom(atom_index) orelse continue; @@ -414,6 +436,72 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void { } } +fn sortSymbols(self: *ZigObject, elf_file: *Elf) error{OutOfMemory}!void { + _ = self; + _ = elf_file; + // const Entry = struct { + // index: Symbol.Index, + + // const Ctx = struct { + // zobj: ZigObject, + // efile: *Elf, + // }; + + // pub fn lessThan(ctx: Ctx, lhs: @This(), rhs: @This()) bool { + // const lhs_sym = ctx.efile.symbol(zobj.symbol(lhs.index)); + // const rhs_sym = ctx.efile.symbol(zobj.symbol(rhs.index)); + // if (lhs_sym.outputShndx() != null and rhs_sym.outputShndx() != null) { + // if (lhs_sym.output_section_index == rhs_sym.output_section_index) { + // if (lhs_sym.value == rhs_sym.value) { + // return lhs_sym.name_offset < rhs_sym.name_offset; + // } + // return lhs_sym.value < rhs_sym.value; + // } + // return lhs_sym.output_section_index < rhs_sym.output_section_index; + // } + // if (lhs_sym.outputShndx() != null) { + // if (rhs_sym.isAbs(ctx.efile)) return false; + // return true; + // } + // return false; + // } + // }; + + // const gpa = elf_file.base.allocator; + + // { + // const sorted = try gpa.alloc(Entry, self.local_symbols.items.len); + // defer gpa.free(sorted); + // for (0..self.local_symbols.items.len) |index| { + // sorted[i] = .{ .index = @as(Symbol.Index, @intCast(index)) }; + // } + // mem.sort(Entry, sorted, .{ .zobj = self, .efile = elf_file }, Entry.lessThan); + + // const backlinks = try gpa.alloc(Symbol.Index, sorted.len); + // defer gpa.free(backlinks); + // for (sorted, 0..) |entry, i| { + // backlinks[entry.index] = @as(Symbol.Index, @intCast(i)); + // } + + // const local_symbols = try self.local_symbols.toOwnedSlice(gpa); + // defer gpa.free(local_symbols); + + // try self.local_symbols.ensureTotalCapacityPrecise(gpa, local_symbols.len); + // for (sorted) |entry| { + // self.local_symbols.appendAssumeCapacity(local_symbols[entry.index]); + // } + + // for (self.) + // } + + // const sorted_globals = try gpa.alloc(Entry, self.global_symbols.items.len); + // defer gpa.free(sorted_globals); + // for (self.global_symbols.items, 0..) |index, i| { + // sorted_globals[i] = .{ .index = index }; + // } + // mem.sort(Entry, sorted_globals, elf_file, Entry.lessThan); +} + pub fn updateRelaSectionSizes(self: ZigObject, elf_file: *Elf) void { _ = self; @@ -451,16 +539,6 @@ pub fn updateRelaSectionSizes(self: ZigObject, elf_file: *Elf) void { pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void { const gpa = elf_file.base.allocator; - // const getSectionSymbol = struct { - // fn getSectionSymbol(zig_object: ZigObject, shndx: u16, ctx: *Elf) Symbol.Index { - // for (zig_object.locals()) |local_index| { - // const local = ctx.symbol(local_index); - // if (local.type(ctx) == elf.STT_SECTION and local.output_section_index == shndx) - // return local.esym_index; - // } else unreachable; - // } - // }.getSectionSymbol; - for (&[_]?u16{ elf_file.zig_text_rela_section_index, elf_file.zig_data_rel_ro_rela_section_index, @@ -485,7 +563,6 @@ pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void { (target.esym_index & symbol_mask) + @as(u32, @intCast(self.local_esyms.slice().len)) else target.esym_index; - // getSectionSymbol(self, target.outputShndx().?, elf_file); const r_type = switch (rel.r_type()) { Elf.R_X86_64_ZIG_GOT32, Elf.R_X86_64_ZIG_GOTPCREL, From 96f221236dbea6c17d11fb41e83d9dcc12c48200 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 2 Nov 2023 11:49:41 +0100 Subject: [PATCH 16/36] elf: fix r_offset when emitting relocs for the linker --- src/link/Elf/ZigObject.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 3d48cd405e..d0dd8a3bca 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -558,7 +558,7 @@ pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void { while (true) { for (atom.relocs(elf_file)) |rel| { const target = elf_file.symbol(self.symbol(rel.r_sym())); - const r_offset = target.value + rel.r_offset; + 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 From 5affd29b4799712d4cbf00231ed52de4034b345d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 2 Nov 2023 12:10:41 +0100 Subject: [PATCH 17/36] elf: use StringTable for strtab management in ZigObject --- src/link/Elf/ZigObject.zig | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index d0dd8a3bca..920af93cb2 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -9,7 +9,7 @@ index: File.Index, local_esyms: std.MultiArrayList(ElfSym) = .{}, global_esyms: std.MultiArrayList(ElfSym) = .{}, -strtab: std.ArrayListUnmanaged(u8) = .{}, +strtab: StringTable = .{}, local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{}, @@ -75,9 +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); + try self.strtab.buffer.append(gpa, 0); - const name_off = try self.insertString(gpa, std.fs.path.stem(self.path)); + const name_off = try self.strtab.insert(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); @@ -864,7 +864,7 @@ fn updateDeclCode( sym.output_section_index = shdr_index; atom_ptr.output_section_index = shdr_index; - sym.name_offset = try self.insertString(gpa, decl_name); + sym.name_offset = try self.strtab.insert(gpa, decl_name); atom_ptr.flags.alive = true; atom_ptr.name_offset = sym.name_offset; esym.st_name = sym.name_offset; @@ -1106,7 +1106,7 @@ fn updateLazySymbol( sym.ty.fmt(mod), }); defer gpa.free(name); - break :blk try self.insertString(gpa, name); + break :blk try self.strtab.insert(gpa, name); }; const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl| @@ -1239,7 +1239,7 @@ fn lowerConst( }; const local_sym = elf_file.symbol(sym_index); - const name_str_index = try self.insertString(gpa, name); + const name_str_index = try self.strtab.insert(gpa, name); local_sym.name_offset = name_str_index; local_sym.output_section_index = output_section_index; const local_esym = &self.local_esyms.items(.elf_sym)[local_sym.esym_index]; @@ -1334,18 +1334,12 @@ 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 self.insertString(gpa, exp_name); + const name_off = try self.strtab.insert(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); - const lookup_gop = try self.globals_lookup.getOrPut(gpa, name_off); - const global_esym = self.elfSym(global_esym_index); - global_esym.st_name = name_off; - lookup_gop.value_ptr.* = global_esym_index; + const global_esym_index = try self.getGlobalSymbol(elf_file, exp_name, null); try metadata.exports.append(gpa, global_esym_index); - const gop = try elf_file.getOrPutGlobal(exp_name); - try self.global_symbols.append(gpa, gop.index); break :blk global_esym_index; }; @@ -1405,7 +1399,7 @@ 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 self.insertString(gpa, name); + const off = try self.strtab.insert(gpa, name); const lookup_gop = try self.globals_lookup.getOrPut(gpa, off); if (!lookup_gop.found_existing) { const esym_index = try self.addGlobalEsym(gpa); @@ -1419,15 +1413,7 @@ pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_n } 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; + return self.strtab.getAssumeExists(off); } pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { @@ -1538,5 +1524,6 @@ const Liveness = @import("../../Liveness.zig"); const Module = @import("../../Module.zig"); const Object = @import("Object.zig"); const Symbol = @import("Symbol.zig"); +const StringTable = @import("../StringTable.zig"); const TypedValue = @import("../../TypedValue.zig"); const ZigObject = @This(); From ccb2afacc00264897cc474befc13f36239c91b03 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 2 Nov 2023 12:46:37 +0100 Subject: [PATCH 18/36] elf: postpone creation of .got.zig entry until code emit --- src/arch/x86_64/CodeGen.zig | 5 ++--- src/arch/x86_64/Emit.zig | 10 +++++++--- src/codegen.zig | 2 +- src/link/Elf/Symbol.zig | 7 ++----- src/link/Elf/ZigObject.zig | 2 ++ 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index b65a704351..f842033484 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -10235,7 +10235,7 @@ fn genCall(self: *Self, info: union(enum) { if (self.bin_file.cast(link.File.Elf)) |elf_file| { const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl); const sym = elf_file.symbol(sym_index); - try sym.createZigGotEntry(sym_index, elf_file); + sym.flags.needs_zig_got = true; if (self.bin_file.options.pic) { const callee_reg: Register = switch (resolved_cc) { .SysV => callee: { @@ -13103,8 +13103,7 @@ fn genLazySymbolRef( const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const sym = elf_file.symbol(sym_index); - try sym.createZigGotEntry(sym_index, elf_file); - + sym.flags.needs_zig_got = true; if (self.bin_file.options.pic) { switch (tag) { .lea, .call => try self.genSetReg(reg, Type.usize, .{ diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 36f4b05a6e..c2f7d8c436 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -86,9 +86,13 @@ pub fn emitMir(emit: *Emit) Error!void { }), .linker_reloc => |data| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| { const atom = elf_file.symbol(data.atom_index).atom(elf_file).?; - const sym = elf_file.symbol(elf_file.zigObjectPtr().?.symbol(data.sym_index)); + const sym_index = elf_file.zigObjectPtr().?.symbol(data.sym_index); + const sym = elf_file.symbol(sym_index); + if (sym.flags.needs_zig_got and emit.lower.bin_file.options.effectiveOutputMode() != .Obj) { + _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); + } if (emit.lower.bin_file.options.pic) { - const r_type: u32 = if (sym.flags.has_zig_got) + const r_type: u32 = if (sym.flags.needs_zig_got) link.File.Elf.R_X86_64_ZIG_GOTPCREL else if (sym.flags.needs_got) std.elf.R_X86_64_GOTPCREL @@ -100,7 +104,7 @@ pub fn emitMir(emit: *Emit) Error!void { .r_addend = -4, }); } else { - const r_type: u32 = if (sym.flags.has_zig_got) + const r_type: u32 = if (sym.flags.needs_zig_got) link.File.Elf.R_X86_64_ZIG_GOT32 else if (sym.flags.needs_got) std.elf.R_X86_64_GOT32 diff --git a/src/codegen.zig b/src/codegen.zig index c5062228de..d2a84ba800 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -909,7 +909,7 @@ fn genDeclRef( } const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); const sym = elf_file.symbol(sym_index); - try sym.createZigGotEntry(sym_index, elf_file); + sym.flags.needs_zig_got = true; return GenResult.mcv(.{ .load_symbol = sym.esym_index }); } else if (bin_file.cast(link.File.MachO)) |macho_file| { if (is_extern) { diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 957be0d069..b792725df7 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -169,16 +169,12 @@ const GetOrCreateZigGotEntryResult = struct { pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !GetOrCreateZigGotEntryResult { assert(!elf_file.isObject()); + assert(symbol.flags.needs_zig_got); if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.zig_got }; const index = try elf_file.zig_got.addSymbol(symbol_index, elf_file); return .{ .found_existing = false, .index = index }; } -pub fn createZigGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !void { - if (elf_file.isObject()) return; - _ = try symbol.getOrCreateZigGotEntry(symbol_index, elf_file); -} - pub fn zigGotAddress(symbol: Symbol, elf_file: *Elf) u64 { if (!symbol.flags.has_zig_got) return 0; const extras = symbol.extra(elf_file).?; @@ -385,6 +381,7 @@ pub const Flags = packed struct { has_tlsdesc: bool = false, /// Whether the symbol contains .zig.got indirection. + needs_zig_got: bool = false, has_zig_got: bool = false, }; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 920af93cb2..d65a39d6d2 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -901,6 +901,7 @@ fn updateDeclCode( errdefer self.freeDeclMetadata(elf_file, sym_index); sym.value = atom_ptr.value; + sym.flags.needs_zig_got = true; esym.st_value = atom_ptr.value; if (!elf_file.isObject()) { @@ -1156,6 +1157,7 @@ fn updateLazySymbol( errdefer self.freeDeclMetadata(elf_file, symbol_index); local_sym.value = atom_ptr.value; + local_sym.flags.needs_zig_got = true; local_esym.st_value = atom_ptr.value; if (!elf_file.isObject()) { From d2c4597eb5fcaba07f15c22092fc8c42c8b70ee3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 2 Nov 2023 13:21:25 +0100 Subject: [PATCH 19/36] x86_64: rewrite .got.zig movs to standard loads when emitting objects --- src/arch/x86_64/Emit.zig | 7 ++++--- src/arch/x86_64/Lower.zig | 26 ++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index c2f7d8c436..50fa0c1ffd 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -85,14 +85,15 @@ pub fn emitMir(emit: *Emit) Error!void { @tagName(emit.lower.bin_file.tag), }), .linker_reloc => |data| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| { + const is_obj = emit.lower.bin_file.options.effectiveOutputMode() == .Obj; const atom = elf_file.symbol(data.atom_index).atom(elf_file).?; const sym_index = elf_file.zigObjectPtr().?.symbol(data.sym_index); const sym = elf_file.symbol(sym_index); - if (sym.flags.needs_zig_got and emit.lower.bin_file.options.effectiveOutputMode() != .Obj) { + if (sym.flags.needs_zig_got and !is_obj) { _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); } if (emit.lower.bin_file.options.pic) { - const r_type: u32 = if (sym.flags.needs_zig_got) + const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj) link.File.Elf.R_X86_64_ZIG_GOTPCREL else if (sym.flags.needs_got) std.elf.R_X86_64_GOTPCREL @@ -104,7 +105,7 @@ pub fn emitMir(emit: *Emit) Error!void { .r_addend = -4, }); } else { - const r_type: u32 = if (sym.flags.needs_zig_got) + const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj) link.File.Elf.R_X86_64_ZIG_GOT32 else if (sym.flags.needs_got) std.elf.R_X86_64_GOT32 diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index fe735a3700..fb1d867384 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -319,6 +319,15 @@ fn reloc(lower: *Lower, target: Reloc.Target) Immediate { } fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void { + const needsZigGot = struct { + fn needsZigGot(sym: bits.Symbol, ctx: *link.File) bool { + const elf_file = ctx.cast(link.File.Elf).?; + const sym_index = elf_file.zigObjectPtr().?.symbol(sym.sym_index); + return elf_file.symbol(sym_index).flags.needs_zig_got; + } + }.needsZigGot; + + const is_obj = lower.bin_file.options.effectiveOutputMode() == .Obj; var emit_prefix = prefix; var emit_mnemonic = mnemonic; var emit_ops_storage: [4]Operand = undefined; @@ -334,7 +343,13 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) assert(mem_op.sib.scale_index.scale == 0); _ = lower.reloc(.{ .linker_reloc = sym }); break :op if (lower.bin_file.options.pic) switch (mnemonic) { - .mov, .lea => .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }, + .lea => { + break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }; + }, + .mov => { + if (is_obj and needsZigGot(sym, lower.bin_file)) emit_mnemonic = .lea; + break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }; + }, else => unreachable, } else switch (mnemonic) { .call => .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ @@ -344,9 +359,12 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) emit_mnemonic = .mov; break :op .{ .imm = Immediate.s(0) }; }, - .mov => .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ - .base = .{ .reg = .ds }, - }) }, + .mov => { + if (is_obj and needsZigGot(sym, lower.bin_file)) emit_mnemonic = .lea; + break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ + .base = .{ .reg = .ds }, + }) }; + }, else => unreachable, }; }, From 481ee1b598b02cf5790ed12ed162a9f749a1ac92 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 2 Nov 2023 14:12:08 +0100 Subject: [PATCH 20/36] elf: enable static-lib flush path --- src/arch/x86_64/Emit.zig | 12 +++++--- src/arch/x86_64/Lower.zig | 10 +++++-- src/link/Elf.zig | 58 +++++++++++++++++++++++--------------- src/link/Elf/Atom.zig | 3 +- src/link/Elf/ZigObject.zig | 8 +++--- 5 files changed, 57 insertions(+), 34 deletions(-) diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 50fa0c1ffd..a1a2e2cb1c 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -85,15 +85,19 @@ pub fn emitMir(emit: *Emit) Error!void { @tagName(emit.lower.bin_file.tag), }), .linker_reloc => |data| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| { - const is_obj = emit.lower.bin_file.options.effectiveOutputMode() == .Obj; + const is_obj_or_static_lib = switch (emit.lower.bin_file.options.output_mode) { + .Exe => false, + .Obj => true, + .Lib => emit.lower.bin_file.options.link_mode == .Static, + }; const atom = elf_file.symbol(data.atom_index).atom(elf_file).?; const sym_index = elf_file.zigObjectPtr().?.symbol(data.sym_index); const sym = elf_file.symbol(sym_index); - if (sym.flags.needs_zig_got and !is_obj) { + if (sym.flags.needs_zig_got and !is_obj_or_static_lib) { _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); } if (emit.lower.bin_file.options.pic) { - const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj) + const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib) link.File.Elf.R_X86_64_ZIG_GOTPCREL else if (sym.flags.needs_got) std.elf.R_X86_64_GOTPCREL @@ -105,7 +109,7 @@ pub fn emitMir(emit: *Emit) Error!void { .r_addend = -4, }); } else { - const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj) + const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib) link.File.Elf.R_X86_64_ZIG_GOT32 else if (sym.flags.needs_got) std.elf.R_X86_64_GOT32 diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index fb1d867384..913e55f00a 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -327,7 +327,11 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) } }.needsZigGot; - const is_obj = lower.bin_file.options.effectiveOutputMode() == .Obj; + const is_obj_or_static_lib = switch (lower.bin_file.options.output_mode) { + .Exe => false, + .Obj => true, + .Lib => lower.bin_file.options.link_mode == .Static, + }; var emit_prefix = prefix; var emit_mnemonic = mnemonic; var emit_ops_storage: [4]Operand = undefined; @@ -347,7 +351,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }; }, .mov => { - if (is_obj and needsZigGot(sym, lower.bin_file)) emit_mnemonic = .lea; + if (is_obj_or_static_lib and needsZigGot(sym, lower.bin_file)) emit_mnemonic = .lea; break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }; }, else => unreachable, @@ -360,7 +364,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) break :op .{ .imm = Immediate.s(0) }; }, .mov => { - if (is_obj and needsZigGot(sym, lower.bin_file)) emit_mnemonic = .lea; + if (is_obj_or_static_lib and needsZigGot(sym, lower.bin_file)) emit_mnemonic = .lea; break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ .base = .{ .reg = .ds }, }) }; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 42d3c3ef20..a10d17d75f 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -498,7 +498,7 @@ pub fn initMetadata(self: *Elf) !void { const fillSection = struct { fn fillSection(elf_file: *Elf, shdr: *elf.Elf64_Shdr, size: u64, phndx: ?u16) void { - if (elf_file.isObject()) { + if (elf_file.isRelocatable()) { const off = elf_file.findFreeSpace(size, shdr.sh_addralign); shdr.sh_offset = off; shdr.sh_size = size; @@ -513,7 +513,7 @@ pub fn initMetadata(self: *Elf) !void { comptime assert(number_of_zig_segments == 5); - if (!self.isObject()) { + if (!self.isRelocatable()) { if (self.phdr_zig_load_re_index == null) { const filesz = self.base.options.program_code_size_hint; const off = self.findFreeSpace(filesz, self.page_size); @@ -597,7 +597,7 @@ 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.isObject()) { + if (self.isRelocatable()) { try zig_object.addSectionSymbol(self.zig_text_section_index.?, self); self.zig_text_rela_section_index = try self.addRelaShdr( ".rela.text.zig", @@ -613,7 +613,7 @@ pub fn initMetadata(self: *Elf) !void { try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_text_section_index.?, .{}); } - if (self.zig_got_section_index == null and !self.isObject()) { + if (self.zig_got_section_index == null and !self.isRelocatable()) { self.zig_got_section_index = try self.addSection(.{ .name = ".got.zig", .type = elf.SHT_PROGBITS, @@ -644,7 +644,7 @@ 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.isObject()) { + if (self.isRelocatable()) { try zig_object.addSectionSymbol(self.zig_data_rel_ro_section_index.?, self); self.zig_data_rel_ro_rela_section_index = try self.addRelaShdr( ".rela.data.rel.ro.zig", @@ -670,7 +670,7 @@ 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.isObject()) { + if (self.isRelocatable()) { try zig_object.addSectionSymbol(self.zig_data_section_index.?, self); self.zig_data_rela_section_index = try self.addRelaShdr( ".rela.data.zig", @@ -904,10 +904,6 @@ pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link if (use_lld) { return self.linkWithLLD(comp, prog_node); } - if (self.base.options.output_mode == .Lib and self.isStatic()) { - // TODO writing static library files - return error.TODOImplementWritingLibFiles; - } try self.flushModule(comp, prog_node); } @@ -943,7 +939,12 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } else null; const gc_sections = self.base.options.gc_sections orelse false; - if (self.isObject() and self.zig_object_index == null) { + if (self.isRelocatable() and self.zig_object_index == null) { + if (self.isStaticLib()) { + var err = try self.addErrorWithNotes(0); + try err.addMsg(self, "fatal linker error: emitting static libs unimplemented", .{}); + return; + } // TODO this will become -r route I guess. For now, just copy the object file. assert(self.base.file == null); // TODO uncomment once we implement -r const the_object_path = blk: { @@ -1389,6 +1390,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self); + if (self.isStaticLib()) return self.flushStaticLib(comp); // Dedup shared objects { @@ -1424,9 +1426,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node self.resolveSymbols(); self.markEhFrameAtomsDead(); - if (self.isObject()) { - return self.flushObject(comp); - } + if (self.isObject()) return self.flushObject(comp); try self.convertCommonSymbols(); self.markImportsExports(); @@ -1511,7 +1511,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.writeAtoms(); try self.writeSyntheticSections(); - if (self.entry_index == null and self.base.options.effectiveOutputMode() == .Exe) { + if (self.entry_index == null and self.isExe()) { log.debug("flushing. no_entry_point_found = true", .{}); self.error_flags.no_entry_point_found = true; } else { @@ -1521,6 +1521,12 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } } +pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void { + _ = comp; + var err = try self.addErrorWithNotes(0); + try err.addMsg(self, "fatal linker error: emitting static libs unimplemented", .{}); +} + pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void { _ = comp; self.claimUnresolvedObject(); @@ -2822,7 +2828,7 @@ fn writeHeader(self: *Elf) !void { assert(index == 16); - const elf_type: elf.ET = switch (self.base.options.effectiveOutputMode()) { + const elf_type: elf.ET = switch (self.base.options.output_mode) { .Exe => if (self.base.options.pie) .DYN else .EXEC, .Obj => .REL, .Lib => switch (self.base.options.link_mode) { @@ -3147,11 +3153,11 @@ fn initSections(self: *Elf) !void { }; const ptr_size = self.ptrWidthBytes(); - for (self.objects.items) |index| { + if (!self.isStaticLib()) for (self.objects.items) |index| { try self.file(index).?.object.initOutputSections(self); - } + }; - const needs_eh_frame = for (self.objects.items) |index| { + const needs_eh_frame = if (self.isStaticLib()) false else for (self.objects.items) |index| { if (self.file(index).?.object.cies.items.len > 0) break true; } else false; if (needs_eh_frame) { @@ -4954,15 +4960,23 @@ pub fn isStatic(self: Elf) bool { } pub fn isObject(self: Elf) bool { - return self.base.options.effectiveOutputMode() == .Obj; + return self.base.options.output_mode == .Obj; } pub fn isExe(self: Elf) bool { - return self.base.options.effectiveOutputMode() == .Exe; + return self.base.options.output_mode == .Exe; +} + +pub fn isStaticLib(self: Elf) bool { + return self.base.options.output_mode == .Lib and self.isStatic(); +} + +pub fn isRelocatable(self: Elf) bool { + return self.isObject() or self.isStaticLib(); } pub fn isDynLib(self: Elf) bool { - return self.base.options.effectiveOutputMode() == .Lib and self.base.options.link_mode == .Dynamic; + return self.base.options.output_mode == .Lib and !self.isStatic(); } pub fn isZigSection(self: Elf, shndx: u16) bool { diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 6ba2325638..1f6f77cc4b 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -605,7 +605,8 @@ fn dynAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction { } fn outputType(elf_file: *Elf) u2 { - return switch (elf_file.base.options.effectiveOutputMode()) { + assert(!elf_file.isRelocatable()); + return switch (elf_file.base.options.output_mode) { .Obj => unreachable, .Lib => 0, .Exe => if (elf_file.base.options.pie) 1 else 2, diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index d65a39d6d2..5f6cc5488f 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -287,7 +287,7 @@ pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index { } pub fn addSectionSymbol(self: *ZigObject, shndx: u16, elf_file: *Elf) !void { - assert(elf_file.isObject()); + assert(elf_file.isRelocatable()); const gpa = elf_file.base.allocator; const symbol_index = try elf_file.addSymbol(); try self.local_symbols.append(gpa, symbol_index); @@ -886,7 +886,7 @@ fn updateDeclCode( sym.value = atom_ptr.value; esym.st_value = atom_ptr.value; - if (!elf_file.isObject()) { + if (!elf_file.isRelocatable()) { log.debug(" (writing new offset table entry)", .{}); assert(sym.flags.has_zig_got); const extra = sym.extra(elf_file).?; @@ -904,7 +904,7 @@ fn updateDeclCode( sym.flags.needs_zig_got = true; esym.st_value = atom_ptr.value; - if (!elf_file.isObject()) { + if (!elf_file.isRelocatable()) { const gop = try sym.getOrCreateZigGotEntry(sym_index, elf_file); try elf_file.zig_got.writeOne(elf_file, gop.index); } @@ -1160,7 +1160,7 @@ fn updateLazySymbol( local_sym.flags.needs_zig_got = true; local_esym.st_value = atom_ptr.value; - if (!elf_file.isObject()) { + if (!elf_file.isRelocatable()) { const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, elf_file); try elf_file.zig_got.writeOne(elf_file, gop.index); } From eddf9cc65b0abe8a8cdf8c80d8cd97e56e860515 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 2 Nov 2023 17:13:49 +0100 Subject: [PATCH 21/36] elf: collect exports from ZigObject into AR symtab --- src/link/Elf.zig | 115 +++++++++++++++++++++++++++++-------- src/link/Elf/Archive.zig | 6 +- src/link/Elf/Symbol.zig | 6 +- src/link/Elf/ZigObject.zig | 16 ++++++ src/link/Elf/file.zig | 10 +++- 5 files changed, 123 insertions(+), 30 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index a10d17d75f..9e7fdf7b49 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -186,6 +186,12 @@ comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{} /// such as `resolver` and `comdat_groups_table`. strings: StringTable = .{}, +/// Static archive state. +/// TODO it may be wise to move it somewhere else, but for the time being, it +/// is far easier to pollute global state. +ar_symtab: std.ArrayListUnmanaged(struct { u32, File.Index }) = .{}, +ar_strtab: StringTable = .{}, + /// When allocating, the ideal_capacity is calculated by /// actual_capacity + (actual_capacity / ideal_factor) const ideal_factor = 3; @@ -392,6 +398,9 @@ pub fn deinit(self: *Elf) void { self.copy_rel.deinit(gpa); self.rela_dyn.deinit(gpa); self.rela_plt.deinit(gpa); + + self.ar_symtab.deinit(gpa); + self.ar_strtab.deinit(gpa); } pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 { @@ -1383,15 +1392,15 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node if (csu.crtend) |v| try positionals.append(.{ .path = v }); if (csu.crtn) |v| try positionals.append(.{ .path = v }); + if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self); + if (self.isStaticLib()) return self.flushStaticLib(comp); + for (positionals.items) |obj| { var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined }; self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err| try self.handleAndReportParseError(obj.path, err, &parse_ctx); } - if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self); - if (self.isStaticLib()) return self.flushStaticLib(comp); - // Dedup shared objects { var seen_dsos = std.StringHashMap(void).init(gpa); @@ -1412,7 +1421,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // If we haven't already, create a linker-generated input file comprising of // linker-defined synthetic symbols only such as `_DYNAMIC`, etc. - if (self.linker_defined_index == null and !self.isObject()) { + if (self.linker_defined_index == null and !self.isRelocatable()) { const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); self.files.set(index, .{ .linker_defined = .{ .index = index } }); self.linker_defined_index = index; @@ -1517,14 +1526,55 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } else { log.debug("flushing. no_entry_point_found = false", .{}); self.error_flags.no_entry_point_found = false; - try self.writeHeader(); + try self.writeElfHeader(); } } pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void { _ = comp; - var err = try self.addErrorWithNotes(0); - try err.addMsg(self, "fatal linker error: emitting static libs unimplemented", .{}); + + // First, we flush relocatable object file generated with our backends. + if (self.zigObjectPtr()) |zig_object| { + zig_object.resolveSymbols(self); + zig_object.claimUnresolvedObject(self); + + try self.initSymtab(); + try self.initShStrtab(); + try self.sortShdrs(); + zig_object.updateRelaSectionSizes(self); + try self.updateSymtabSize(); + self.updateShStrtabSize(); + + try self.allocateNonAllocSections(); + + try self.writeShdrTable(); + try zig_object.writeRelaSections(self); + try self.writeSymtab(); + try self.writeShStrtab(); + try self.writeElfHeader(); + + // Update ar symbol and string tables. + try zig_object.asFile().updateArSymtab(self); + + for (self.ar_symtab.items, 0..) |entry, i| { + std.debug.print("{d}: {s} in {}\n", .{ + i, + self.ar_strtab.getAssumeExists(entry[0]), + self.file(entry[1]).?.fmtPath(), + }); + } + } + + // TODO parse positionals that we want to make part of the archive + + if (build_options.enable_logging) { + state_log.debug("{}", .{self.dumpState()}); + } + + // try self.writeArHdr(); + // TODO beyond this point I expect writing out objects parsed from the cmdline + + try self.writeArMagic(); } pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void { @@ -1543,7 +1593,7 @@ pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void { try self.writeShdrTable(); try self.writeSyntheticSections(); - try self.writeHeader(); + try self.writeElfHeader(); } const ParseError = error{ @@ -2797,7 +2847,7 @@ fn writePhdrTable(self: *Elf) !void { } } -fn writeHeader(self: *Elf) !void { +fn writeElfHeader(self: *Elf) !void { var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined; var index: usize = 0; @@ -2918,6 +2968,15 @@ fn writeHeader(self: *Elf) !void { try self.base.file.?.pwriteAll(hdr_buf[0..index], 0); } +fn writeArMagic(self: *Elf) !void { + // Magic bytes. + var buffer: [@as(usize, Archive.SARMAG) + 1]u8 = undefined; + var stream = std.io.fixedBufferStream(&buffer); + const writer = stream.writer(); + try writer.print("{s}\x00", .{Archive.ARMAG}); + try self.base.file.?.pwriteAll(&buffer, 0); +} + pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); return self.zigObjectPtr().?.freeDecl(self, decl_index); @@ -3147,17 +3206,13 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void { } fn initSections(self: *Elf) !void { - const small_ptr = switch (self.ptr_width) { - .p32 => true, - .p64 => false, - }; const ptr_size = self.ptrWidthBytes(); - if (!self.isStaticLib()) for (self.objects.items) |index| { + for (self.objects.items) |index| { try self.file(index).?.object.initOutputSections(self); - }; + } - const needs_eh_frame = if (self.isStaticLib()) false else for (self.objects.items) |index| { + const needs_eh_frame = for (self.objects.items) |index| { if (self.file(index).?.object.cies.items.len > 0) break true; } else false; if (needs_eh_frame) { @@ -3340,6 +3395,15 @@ fn initSections(self: *Elf) !void { } } + try self.initSymtab(); + try self.initShStrtab(); +} + +fn initSymtab(self: *Elf) !void { + const small_ptr = switch (self.ptr_width) { + .p32 => true, + .p64 => false, + }; if (self.symtab_section_index == null) { self.symtab_section_index = try self.addSection(.{ .name = ".symtab", @@ -3358,6 +3422,9 @@ fn initSections(self: *Elf) !void { .offset = std.math.maxInt(u64), }); } +} + +fn initShStrtab(self: *Elf) !void { if (self.shstrtab_section_index == null) { self.shstrtab_section_index = try self.addSection(.{ .name = ".shstrtab", @@ -3933,10 +4000,11 @@ fn updateSectionSizes(self: *Elf) !void { self.shdrs.items[index].sh_size = self.verneed.size(); } - if (self.symtab_section_index != null) { - try self.updateSymtabSize(); - } + try self.updateSymtabSize(); + self.updateShStrtabSize(); +} +fn updateShStrtabSize(self: *Elf) void { if (self.shstrtab_section_index) |index| { self.shdrs.items[index].sh_size = self.shstrtab.items.len; } @@ -4546,14 +4614,15 @@ fn writeSyntheticSections(self: *Elf) !void { try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_plt.items), shdr.sh_offset); } + try self.writeSymtab(); + try self.writeShStrtab(); +} + +fn writeShStrtab(self: *Elf) !void { if (self.shstrtab_section_index) |index| { const shdr = self.shdrs.items[index]; try self.base.file.?.pwriteAll(self.shstrtab.items, shdr.sh_offset); } - - if (self.symtab_section_index) |_| { - try self.writeSymtab(); - } } fn writeSymtab(self: *Elf) !void { diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index 0eb2f2d404..1493ded684 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -13,11 +13,11 @@ pub const ARMAG: *const [SARMAG:0]u8 = "!\n"; pub const SARMAG: u4 = 8; /// String in ar_fmag at the end of each header. -const ARFMAG: *const [2:0]u8 = "`\n"; +pub const ARFMAG: *const [2:0]u8 = "`\n"; -const SYM64NAME: *const [7:0]u8 = "/SYM64/"; +pub const SYM64NAME: *const [7:0]u8 = "/SYM64/"; -const ar_hdr = extern struct { +pub const ar_hdr = extern struct { /// Member file name, sometimes / terminated. ar_name: [16]u8, diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index b792725df7..13d9041813 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -43,7 +43,7 @@ pub fn outputShndx(symbol: Symbol) ?u16 { } pub fn isLocal(symbol: Symbol, elf_file: *Elf) bool { - if (elf_file.isObject()) return symbol.elfSym(elf_file).st_bind() == elf.STB_LOCAL; + if (elf_file.isRelocatable()) return symbol.elfSym(elf_file).st_bind() == elf.STB_LOCAL; return !(symbol.flags.import or symbol.flags.@"export"); } @@ -168,7 +168,7 @@ const GetOrCreateZigGotEntryResult = struct { }; pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !GetOrCreateZigGotEntryResult { - assert(!elf_file.isObject()); + assert(!elf_file.isRelocatable()); assert(symbol.flags.needs_zig_got); if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.zig_got }; const index = try elf_file.zig_got.addSymbol(symbol_index, elf_file); @@ -220,7 +220,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { if (symbol.flags.has_copy_rel) break :blk elf_file.copy_rel_section_index.?; if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; // TODO I think this is wrong and obsolete - if (elf_file.isObject() and st_type == elf.STT_SECTION) break :blk symbol.outputShndx().?; + if (elf_file.isRelocatable() and st_type == elf.STT_SECTION) break :blk symbol.outputShndx().?; if (symbol.atom(elf_file) == null and file_ptr != .linker_defined) break :blk elf.SHN_ABS; break :blk symbol.outputShndx() orelse elf.SHN_UNDEF; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 5f6cc5488f..fd8de4002b 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -502,6 +502,22 @@ fn sortSymbols(self: *ZigObject, elf_file: *Elf) error{OutOfMemory}!void { // mem.sort(Entry, sorted_globals, elf_file, Entry.lessThan); } +pub fn updateArSymtab(self: ZigObject, elf_file: *Elf) !void { + const gpa = elf_file.base.allocator; + + try elf_file.ar_symtab.ensureUnusedCapacity(gpa, self.globals().len); + + for (self.globals()) |global_index| { + const global = elf_file.symbol(global_index); + const file_ptr = global.file(elf_file).?; + assert(file_ptr.index() == self.index); + if (global.type(elf_file) == elf.SHN_UNDEF) continue; + + const off = try elf_file.ar_strtab.insert(gpa, global.name(elf_file)); + elf_file.ar_symtab.appendAssumeCapacity(.{ off, self.index }); + } +} + pub fn updateRelaSectionSizes(self: ZigObject, elf_file: *Elf) void { _ = self; diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index a168ec541b..49d097ab61 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -136,7 +136,7 @@ pub const File = union(enum) { if (local.atom(elf_file)) |atom| if (!atom.flags.alive) continue; const esym = local.elfSym(elf_file); switch (esym.st_type()) { - elf.STT_SECTION => if (!elf_file.isObject()) continue, + elf.STT_SECTION => if (!elf_file.isRelocatable()) continue, elf.STT_NOTYPE => continue, else => {}, } @@ -196,6 +196,14 @@ pub const File = union(enum) { } } + pub fn updateArSymtab(file: File, elf_file: *Elf) !void { + return switch (file) { + .zig_object => |x| x.updateArSymtab(elf_file), + .object => @panic("TODO"), + inline else => unreachable, + }; + } + pub const Index = u32; pub const Entry = union(enum) { From 3b9455f0052d4ae648e8eec9685455eb501abcfd Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 2 Nov 2023 19:33:10 +0100 Subject: [PATCH 22/36] elf: generate pretty rudimentary archive --- src/link/Elf.zig | 196 ++++++++++++++++++++++++++++++++----- src/link/Elf/Archive.zig | 4 +- src/link/Elf/ZigObject.zig | 2 +- 3 files changed, 177 insertions(+), 25 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 9e7fdf7b49..d8bd4d9552 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -189,7 +189,7 @@ strings: StringTable = .{}, /// Static archive state. /// TODO it may be wise to move it somewhere else, but for the time being, it /// is far easier to pollute global state. -ar_symtab: std.ArrayListUnmanaged(struct { u32, File.Index }) = .{}, +ar_symtab: std.ArrayListUnmanaged(ArSymtabEntry) = .{}, ar_strtab: StringTable = .{}, /// When allocating, the ideal_capacity is calculated by @@ -1532,6 +1532,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void { _ = comp; + const gpa = self.base.allocator; // First, we flush relocatable object file generated with our backends. if (self.zigObjectPtr()) |zig_object| { @@ -1553,28 +1554,162 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void try self.writeShStrtab(); try self.writeElfHeader(); - // Update ar symbol and string tables. + // Update ar symbol table. try zig_object.asFile().updateArSymtab(self); - - for (self.ar_symtab.items, 0..) |entry, i| { - std.debug.print("{d}: {s} in {}\n", .{ - i, - self.ar_strtab.getAssumeExists(entry[0]), - self.file(entry[1]).?.fmtPath(), - }); - } } // TODO parse positionals that we want to make part of the archive + mem.sort(ArSymtabEntry, self.ar_symtab.items, {}, ArSymtabEntry.lessThan); + if (build_options.enable_logging) { state_log.debug("{}", .{self.dumpState()}); } - // try self.writeArHdr(); - // TODO beyond this point I expect writing out objects parsed from the cmdline + // Save object paths in strtab. + var files = std.AutoHashMap(File.Index, struct { u32, u64, u64 }).init(gpa); + defer files.deinit(); + try files.ensureUnusedCapacity(@intCast(self.objects.items.len + 1)); - try self.writeArMagic(); + if (self.zigObjectPtr()) |zig_object| { + files.putAssumeCapacityNoClobber(zig_object.index, .{ try self.ar_strtab.insert(gpa, zig_object.path), 0, 0 }); + } + + // Encode ar symtab in 64bit format. + var ar_symtab = std.ArrayList(u8).init(gpa); + defer ar_symtab.deinit(); + try ar_symtab.ensureTotalCapacityPrecise(8 * (3 * self.ar_symtab.items.len + 1)); + + // Number of symbols + ar_symtab.writer().writeInt(u64, @as(u64, @intCast(self.ar_symtab.items.len)), .big) catch unreachable; + + // Offsets which we will relocate later. + for (0..self.ar_symtab.items.len) |_| { + ar_symtab.writer().writeInt(u64, 0, .big) catch unreachable; + } + + // ASCII offsets into the strtab. + for (self.ar_symtab.items) |entry| { + ar_symtab.writer().print("/{d}", .{entry.off}) catch unreachable; + } + + // Align to 8bytes if required + { + const end = ar_symtab.items.len; + const aligned = mem.alignForward(usize, end, 8); + ar_symtab.writer().writeByteNTimes(0, aligned - end) catch unreachable; + } + + assert(mem.isAligned(ar_symtab.items.len, 8)); + + // Calculate required size for headers before ZigObject pos in file. + if (self.zigObjectPtr()) |zig_object| { + var file_off: u64 = 0; + // Magic + file_off += Archive.SARMAG; + // Symtab + file_off += @sizeOf(Archive.ar_hdr) + @as(u64, @intCast(ar_symtab.items.len)); + // Strtab + file_off += @sizeOf(Archive.ar_hdr) + @as(u64, @intCast(self.ar_strtab.buffer.items.len)); + // And because we are nice, we will align to 8 bytes. + file_off = mem.alignForward(u64, file_off, 8); + + const files_ptr = files.getPtr(zig_object.index).?; + files_ptr[1] = file_off; + + // Move ZigObject into place. + { + var end_pos: u64 = self.shdr_table_offset.?; + for (self.shdrs.items) |shdr| { + end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size); + } + const contents = try gpa.alloc(u8, end_pos); + defer gpa.free(contents); + const amt = try self.base.file.?.preadAll(contents, 0); + if (amt != end_pos) return error.InputOutput; + try self.base.file.?.pwriteAll(contents, file_off + @sizeOf(Archive.ar_hdr)); + + files_ptr[2] = end_pos; + } + } + + // Fixup file offsets in the symtab. + for (self.ar_symtab.items, 1..) |entry, i| { + const file_off = files.get(entry.file_index).?[1]; + mem.writeInt(u64, ar_symtab.items[8 * i ..][0..8], file_off, .big); + } + + var pos: usize = Archive.SARMAG; + + // Write symtab. + { + const hdr = setArHdr(.{ .kind = .symtab, .name_off = 0, .size = @intCast(ar_symtab.items.len) }); + try self.base.file.?.pwriteAll(mem.asBytes(&hdr), pos); + pos += @sizeOf(Archive.ar_hdr); + try self.base.file.?.pwriteAll(ar_symtab.items, pos); + pos += ar_symtab.items.len; + } + + // Write strtab. + { + const hdr = setArHdr(.{ + .kind = .strtab, + .name_off = 0, + .size = @intCast(mem.alignForward(usize, self.ar_strtab.buffer.items.len, 8)), + }); + try self.base.file.?.pwriteAll(mem.asBytes(&hdr), pos); + pos += @sizeOf(Archive.ar_hdr); + try self.base.file.?.pwriteAll(self.ar_strtab.buffer.items, pos); + pos += self.ar_strtab.buffer.items.len; + } + + // Zig object if defined + if (self.zigObjectPtr()) |zig_object| { + const entry = files.get(zig_object.index).?; + const hdr = setArHdr(.{ .kind = .object, .name_off = entry[0], .size = @intCast(entry[2]) }); + try self.base.file.?.pwriteAll(mem.asBytes(&hdr), entry[1]); + } + + // TODO parsed positionals + + // Magic bytes. + { + try self.base.file.?.pwriteAll(Archive.ARMAG, 0); + } +} + +fn setArHdr(opts: struct { + kind: enum { symtab, strtab, object }, + name_off: u32, + size: u32, +}) Archive.ar_hdr { + var hdr: Archive.ar_hdr = .{ + .ar_name = undefined, + .ar_date = undefined, + .ar_uid = undefined, + .ar_gid = undefined, + .ar_mode = undefined, + .ar_size = undefined, + .ar_fmag = undefined, + }; + @memset(mem.asBytes(&hdr), 0x20); + @memcpy(&hdr.ar_fmag, Archive.ARFMAG); + + { + var stream = std.io.fixedBufferStream(&hdr.ar_name); + const writer = stream.writer(); + switch (opts.kind) { + .symtab => writer.print("{s}", .{Archive.SYM64NAME}) catch unreachable, + .strtab => writer.print("//", .{}) catch unreachable, + .object => writer.print("/{d}", .{opts.name_off}) catch unreachable, + } + } + { + var stream = std.io.fixedBufferStream(&hdr.ar_size); + stream.writer().print("{d}", .{opts.size}) catch unreachable; + } + + return hdr; } pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void { @@ -2968,15 +3103,6 @@ fn writeElfHeader(self: *Elf) !void { try self.base.file.?.pwriteAll(hdr_buf[0..index], 0); } -fn writeArMagic(self: *Elf) !void { - // Magic bytes. - var buffer: [@as(usize, Archive.SARMAG) + 1]u8 = undefined; - var stream = std.io.fixedBufferStream(&buffer); - const writer = stream.writer(); - try writer.print("{s}\x00", .{Archive.ARMAG}); - try self.base.file.?.pwriteAll(&buffer, 0); -} - pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); return self.zigObjectPtr().?.freeDecl(self, decl_index); @@ -5683,6 +5809,19 @@ fn fmtDumpState( } try writer.print("{}\n", .{self.got.fmt(self)}); try writer.print("{}\n", .{self.zig_got.fmt(self)}); + + if (self.isStaticLib()) { + try writer.writeAll("ar symtab\n"); + for (self.ar_symtab.items, 0..) |entry, i| { + try writer.print(" {d} : {s} in file({d})\n", .{ + i, + self.ar_strtab.getAssumeExists(entry.off), + entry.file_index, + }); + } + try writer.writeByte('\n'); + } + try writer.writeAll("Output shdrs\n"); for (self.shdrs.items, 0..) |shdr, shndx| { try writer.print("shdr({d}) : phdr({?d}) : {}\n", .{ @@ -5815,6 +5954,19 @@ const LastAtomAndFreeList = struct { const LastAtomAndFreeListTable = std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFreeList); +const ArSymtabEntry = struct { + off: u32, + file_index: File.Index, + + pub fn lessThan(ctx: void, lhs: ArSymtabEntry, rhs: ArSymtabEntry) bool { + _ = ctx; + if (lhs.off == rhs.off) { + return lhs.file_index < rhs.file_index; + } + return lhs.off < rhs.off; + } +}; + pub const R_X86_64_ZIG_GOT32 = elf.R_X86_64_NUM + 1; pub const R_X86_64_ZIG_GOTPCREL = elf.R_X86_64_NUM + 2; diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index 1493ded684..49496ec7b5 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -10,7 +10,7 @@ strtab: []const u8 = &[0]u8{}, /// String that begins an archive file. pub const ARMAG: *const [SARMAG:0]u8 = "!\n"; /// Size of that string. -pub const SARMAG: u4 = 8; +pub const SARMAG = 8; /// String in ar_fmag at the end of each header. pub const ARFMAG: *const [2:0]u8 = "`\n"; @@ -58,7 +58,7 @@ pub const ar_hdr = extern struct { } fn isSymtab(self: ar_hdr) bool { - return mem.eql(u8, getValue(&self.ar_name), "/"); + return mem.eql(u8, getValue(&self.ar_name), "/") or mem.eql(u8, getValue(&self.ar_name), SYM64NAME); } }; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index fd8de4002b..12ba88b7a9 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -514,7 +514,7 @@ pub fn updateArSymtab(self: ZigObject, elf_file: *Elf) !void { if (global.type(elf_file) == elf.SHN_UNDEF) continue; const off = try elf_file.ar_strtab.insert(gpa, global.name(elf_file)); - elf_file.ar_symtab.appendAssumeCapacity(.{ off, self.index }); + elf_file.ar_symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index }); } } From 875beb25b5493ee3b0aedd730f831e4ea5d124cc Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 2 Nov 2023 21:44:27 +0100 Subject: [PATCH 23/36] elf: fix writing symtab to an archive --- src/link/Elf.zig | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index d8bd4d9552..85274d37d3 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1566,19 +1566,24 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void state_log.debug("{}", .{self.dumpState()}); } - // Save object paths in strtab. + // Save object paths in filenames strtab. + var ar_strtab = std.ArrayList(u8).init(gpa); + defer ar_strtab.deinit(); + var files = std.AutoHashMap(File.Index, struct { u32, u64, u64 }).init(gpa); defer files.deinit(); try files.ensureUnusedCapacity(@intCast(self.objects.items.len + 1)); if (self.zigObjectPtr()) |zig_object| { - files.putAssumeCapacityNoClobber(zig_object.index, .{ try self.ar_strtab.insert(gpa, zig_object.path), 0, 0 }); + const off = @as(u32, @intCast(ar_strtab.items.len)); + try ar_strtab.writer().print("{s}/\n", .{zig_object.path}); + files.putAssumeCapacityNoClobber(zig_object.index, .{ off, 0, 0 }); } // Encode ar symtab in 64bit format. var ar_symtab = std.ArrayList(u8).init(gpa); defer ar_symtab.deinit(); - try ar_symtab.ensureTotalCapacityPrecise(8 * (3 * self.ar_symtab.items.len + 1)); + try ar_symtab.ensureUnusedCapacity(8 * (self.ar_symtab.items.len + 1)); // Number of symbols ar_symtab.writer().writeInt(u64, @as(u64, @intCast(self.ar_symtab.items.len)), .big) catch unreachable; @@ -1590,14 +1595,15 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void // ASCII offsets into the strtab. for (self.ar_symtab.items) |entry| { - ar_symtab.writer().print("/{d}", .{entry.off}) catch unreachable; + const name = self.ar_strtab.getAssumeExists(entry.off); + try ar_symtab.writer().print("{s}\x00", .{name}); } // Align to 8bytes if required { const end = ar_symtab.items.len; const aligned = mem.alignForward(usize, end, 8); - ar_symtab.writer().writeByteNTimes(0, aligned - end) catch unreachable; + try ar_symtab.writer().writeByteNTimes(0, aligned - end); } assert(mem.isAligned(ar_symtab.items.len, 8)); @@ -1610,7 +1616,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void // Symtab file_off += @sizeOf(Archive.ar_hdr) + @as(u64, @intCast(ar_symtab.items.len)); // Strtab - file_off += @sizeOf(Archive.ar_hdr) + @as(u64, @intCast(self.ar_strtab.buffer.items.len)); + file_off += @sizeOf(Archive.ar_hdr) + @as(u64, @intCast(ar_strtab.items.len)); // And because we are nice, we will align to 8 bytes. file_off = mem.alignForward(u64, file_off, 8); @@ -1655,12 +1661,12 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void const hdr = setArHdr(.{ .kind = .strtab, .name_off = 0, - .size = @intCast(mem.alignForward(usize, self.ar_strtab.buffer.items.len, 8)), + .size = @intCast(mem.alignForward(usize, ar_strtab.items.len, 8)), }); try self.base.file.?.pwriteAll(mem.asBytes(&hdr), pos); pos += @sizeOf(Archive.ar_hdr); - try self.base.file.?.pwriteAll(self.ar_strtab.buffer.items, pos); - pos += self.ar_strtab.buffer.items.len; + try self.base.file.?.pwriteAll(ar_strtab.items, pos); + pos += ar_strtab.items.len; } // Zig object if defined From 33a0a72e87bd01ce6dbd10fa327c74168a0166db Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 3 Nov 2023 10:03:03 +0100 Subject: [PATCH 24/36] elf: align ar_hdr to at least 2 bytes --- src/link/Elf.zig | 21 +++++++++++++++++---- src/link/Elf/Archive.zig | 2 +- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 85274d37d3..d938355da5 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1580,6 +1580,13 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void files.putAssumeCapacityNoClobber(zig_object.index, .{ off, 0, 0 }); } + // Align to even byte boundary + { + const end = ar_strtab.items.len; + const aligned = mem.alignForward(usize, end, 2); + try ar_strtab.writer().writeByteNTimes(0, aligned - end); + } + // Encode ar symtab in 64bit format. var ar_symtab = std.ArrayList(u8).init(gpa); defer ar_symtab.deinit(); @@ -1599,7 +1606,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void try ar_symtab.writer().print("{s}\x00", .{name}); } - // Align to 8bytes if required + // Align to 8 bytes if required { const end = ar_symtab.items.len; const aligned = mem.alignForward(usize, end, 8); @@ -1617,8 +1624,6 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void file_off += @sizeOf(Archive.ar_hdr) + @as(u64, @intCast(ar_symtab.items.len)); // Strtab file_off += @sizeOf(Archive.ar_hdr) + @as(u64, @intCast(ar_strtab.items.len)); - // And because we are nice, we will align to 8 bytes. - file_off = mem.alignForward(u64, file_off, 8); const files_ptr = files.getPtr(zig_object.index).?; files_ptr[1] = file_off; @@ -1661,7 +1666,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void const hdr = setArHdr(.{ .kind = .strtab, .name_off = 0, - .size = @intCast(mem.alignForward(usize, ar_strtab.items.len, 8)), + .size = @intCast(ar_strtab.items.len), }); try self.base.file.?.pwriteAll(mem.asBytes(&hdr), pos); pos += @sizeOf(Archive.ar_hdr); @@ -1674,8 +1679,16 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void const entry = files.get(zig_object.index).?; const hdr = setArHdr(.{ .kind = .object, .name_off = entry[0], .size = @intCast(entry[2]) }); try self.base.file.?.pwriteAll(mem.asBytes(&hdr), entry[1]); + pos += @sizeOf(Archive.ar_hdr) + entry[2]; } + if (pos % 2 != 0) { + pos += 1; + try self.base.file.?.pwriteAll(&[1]u8{0}, pos); + } + + assert(mem.isAligned(pos, 2)); + // TODO parsed positionals // Magic bytes. diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index 49496ec7b5..bdefbfad04 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -88,7 +88,7 @@ pub fn parse(self: *Archive, elf_file: *Elf) !void { if (stream.pos % 2 != 0) { stream.pos += 1; } - + // TODO flag an error if stream.pos > self.data.len after alignment const hdr = reader.readStruct(ar_hdr) catch break; if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) { From 912a774a1519756dd68e514e5b5ac3e4e6f4e511 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 3 Nov 2023 11:55:43 +0100 Subject: [PATCH 25/36] x86_64: rewrite call r/m64 to call rel32 for .got.zig refs when object --- src/arch/x86_64/Emit.zig | 30 +++++++++++++++++++----------- src/arch/x86_64/Lower.zig | 4 +++- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index a1a2e2cb1c..db951a71a2 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -109,17 +109,25 @@ pub fn emitMir(emit: *Emit) Error!void { .r_addend = -4, }); } else { - const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib) - link.File.Elf.R_X86_64_ZIG_GOT32 - else if (sym.flags.needs_got) - std.elf.R_X86_64_GOT32 - else - std.elf.R_X86_64_32; - try atom.addReloc(elf_file, .{ - .r_offset = end_offset - 4, - .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type, - .r_addend = 0, - }); + if (lowered_inst.encoding.mnemonic == .call and sym.flags.needs_zig_got and is_obj_or_static_lib) { + try atom.addReloc(elf_file, .{ + .r_offset = end_offset - 4, + .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | std.elf.R_X86_64_PC32, + .r_addend = -4, + }); + } else { + const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib) + link.File.Elf.R_X86_64_ZIG_GOT32 + else if (sym.flags.needs_got) + std.elf.R_X86_64_GOT32 + else + std.elf.R_X86_64_32; + try atom.addReloc(elf_file, .{ + .r_offset = end_offset - 4, + .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type, + .r_addend = 0, + }); + } } } else unreachable, .linker_got, diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index 913e55f00a..621d61d7f8 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -356,7 +356,9 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) }, else => unreachable, } else switch (mnemonic) { - .call => .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ + .call => break :op if (is_obj_or_static_lib and needsZigGot(sym, lower.bin_file)) .{ + .imm = Immediate.s(0), + } else .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ .base = .{ .reg = .ds }, }) }, .lea => { From 65adbec918d7ccc1dd32a48dc010dca87d48411d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 3 Nov 2023 17:42:32 +0100 Subject: [PATCH 26/36] link: commit missing source StringTable.zig --- src/link/StringTable.zig | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/link/StringTable.zig diff --git a/src/link/StringTable.zig b/src/link/StringTable.zig new file mode 100644 index 0000000000..2375bf4449 --- /dev/null +++ b/src/link/StringTable.zig @@ -0,0 +1,49 @@ +buffer: std.ArrayListUnmanaged(u8) = .{}, +table: std.HashMapUnmanaged(u32, void, 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 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) return gop.key_ptr.*; + + try self.buffer.ensureUnusedCapacity(gpa, string.len + 1); + const new_off = @as(u32, @intCast(self.buffer.items.len)); + + self.buffer.appendSliceAssumeCapacity(string); + self.buffer.appendAssumeCapacity(0); + + gop.key_ptr.* = new_off; + + return new_off; +} + +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 { + 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; +} + +const std = @import("std"); +const mem = std.mem; + +const Allocator = mem.Allocator; +const Self = @This(); +const StringIndexAdapter = std.hash_map.StringIndexAdapter; +const StringIndexContext = std.hash_map.StringIndexContext; From a333ac846dd2ac8ed67f84a15bef272ccd65df37 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Nov 2023 11:24:39 -0700 Subject: [PATCH 27/36] Compilation: refactor logic for library-building capabilities And mark the stage2_x86_64 backend as being capable of building compiler-rt and zig libc. --- src/Compilation.zig | 74 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index f51500cf43..7c8dd84457 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -810,16 +810,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { return error.ExportTableAndImportTableConflict; } - // The `have_llvm` condition is here only because native backends cannot yet build compiler-rt. - // Once they are capable this condition could be removed. When removing this condition, - // also test the use case of `build-obj -fcompiler-rt` with the native backends - // and make sure the compiler-rt symbols are emitted. - const is_p9 = options.target.os.tag == .plan9; - const is_spv = options.target.cpu.arch.isSpirV(); - const capable_of_building_compiler_rt = build_options.have_llvm and !is_p9 and !is_spv; - const capable_of_building_zig_libc = build_options.have_llvm and !is_p9 and !is_spv; - const capable_of_building_ssp = build_options.have_llvm and !is_p9 and !is_spv; - const comp: *Compilation = comp: { // For allocations that have the same lifetime as Compilation. This arena is used only during this // initialization and then is freed in deinit(). @@ -1094,6 +1084,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { if (stack_check and !target_util.supportsStackProbing(options.target)) return error.StackCheckUnsupportedByTarget; + const capable_of_building_ssp = canBuildLibSsp(options.target, use_llvm); + const stack_protector: u32 = options.want_stack_protector orelse b: { if (!target_util.supportsStackProtector(options.target)) break :b @as(u32, 0); @@ -1752,6 +1744,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { const target = comp.getTarget(); + const capable_of_building_compiler_rt = canBuildLibCompilerRt(target, comp.bin_file.options.use_llvm); + const capable_of_building_zig_libc = canBuildZigLibC(target, comp.bin_file.options.use_llvm); + // Add a `CObject` for each `c_source_files`. try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len); for (options.c_source_files) |c_source_file| { @@ -6238,9 +6233,66 @@ pub fn dump_argv(argv: []const []const u8) void { nosuspend stderr.print("{s}\n", .{argv[argv.len - 1]}) catch {}; } +fn canBuildLibCompilerRt(target: std.Target, use_llvm: bool) bool { + switch (target.os.tag) { + .plan9 => return false, + else => {}, + } + switch (target.cpu.arch) { + .spirv32, .spirv64 => return false, + else => {}, + } + return switch (zigBackend(target, use_llvm)) { + .stage2_llvm, + .stage2_x86_64, + => true, + + else => false, + }; +} + +fn canBuildLibSsp(target: std.Target, use_llvm: bool) bool { + switch (target.os.tag) { + .plan9 => return false, + else => {}, + } + switch (target.cpu.arch) { + .spirv32, .spirv64 => return false, + else => {}, + } + return switch (zigBackend(target, use_llvm)) { + .stage2_llvm => true, + else => false, + }; +} + +/// Not to be confused with canBuildLibC, which builds musl, glibc, and similar. +/// This one builds lib/c.zig. +fn canBuildZigLibC(target: std.Target, use_llvm: bool) bool { + switch (target.os.tag) { + .plan9 => return false, + else => {}, + } + switch (target.cpu.arch) { + .spirv32, .spirv64 => return false, + else => {}, + } + return switch (zigBackend(target, use_llvm)) { + .stage2_llvm, + .stage2_x86_64, + => true, + + else => false, + }; +} + pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend { - if (comp.bin_file.options.use_llvm) return .stage2_llvm; const target = comp.bin_file.options.target; + return zigBackend(target, comp.bin_file.options.use_llvm); +} + +fn zigBackend(target: std.Target, use_llvm: bool) std.builtin.CompilerBackend { + if (use_llvm) return .stage2_llvm; if (target.ofmt == .c) return .stage2_c; return switch (target.cpu.arch) { .wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm, From ed2984f335bfaf7cc3cb7841554f4cbb958476dd Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 3 Nov 2023 19:30:51 +0100 Subject: [PATCH 28/36] elf: do not align end of archive to 2bytes; fix archive parser --- src/link/Elf.zig | 7 ------- src/link/Elf/Archive.zig | 5 +++-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index d938355da5..daf952811d 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1682,13 +1682,6 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void pos += @sizeOf(Archive.ar_hdr) + entry[2]; } - if (pos % 2 != 0) { - pos += 1; - try self.base.file.?.pwriteAll(&[1]u8{0}, pos); - } - - assert(mem.isAligned(pos, 2)); - // TODO parsed positionals // Magic bytes. diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index bdefbfad04..2083171ce7 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -85,11 +85,12 @@ pub fn parse(self: *Archive, elf_file: *Elf) !void { _ = try reader.readBytesNoEof(SARMAG); while (true) { + if (stream.pos >= self.data.len) break; + if (stream.pos % 2 != 0) { stream.pos += 1; } - // TODO flag an error if stream.pos > self.data.len after alignment - const hdr = reader.readStruct(ar_hdr) catch break; + const hdr = try reader.readStruct(ar_hdr); if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) { // TODO convert into an error From acd700ac6b4ec03412e2bac6aaf168f80f83f521 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 3 Nov 2023 23:08:48 +0100 Subject: [PATCH 29/36] elf: store ar state per input object file --- src/link/Elf.zig | 233 ++++++--------------------- src/link/Elf/Archive.zig | 322 ++++++++++++++++++++++++++++++------- src/link/Elf/ZigObject.zig | 44 ++++- src/link/Elf/file.zig | 5 +- 4 files changed, 354 insertions(+), 250 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index daf952811d..75df6ce40a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -186,12 +186,6 @@ comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{} /// such as `resolver` and `comdat_groups_table`. strings: StringTable = .{}, -/// Static archive state. -/// TODO it may be wise to move it somewhere else, but for the time being, it -/// is far easier to pollute global state. -ar_symtab: std.ArrayListUnmanaged(ArSymtabEntry) = .{}, -ar_strtab: StringTable = .{}, - /// When allocating, the ideal_capacity is calculated by /// actual_capacity + (actual_capacity / ideal_factor) const ideal_factor = 3; @@ -398,9 +392,6 @@ pub fn deinit(self: *Elf) void { self.copy_rel.deinit(gpa); self.rela_dyn.deinit(gpa); self.rela_plt.deinit(gpa); - - self.ar_symtab.deinit(gpa); - self.ar_strtab.deinit(gpa); } pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 { @@ -1553,175 +1544,74 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void try self.writeSymtab(); try self.writeShStrtab(); try self.writeElfHeader(); - - // Update ar symbol table. - try zig_object.asFile().updateArSymtab(self); } // TODO parse positionals that we want to make part of the archive - mem.sort(ArSymtabEntry, self.ar_symtab.items, {}, ArSymtabEntry.lessThan); + // TODO update ar symtab from parsed positionals - if (build_options.enable_logging) { - state_log.debug("{}", .{self.dumpState()}); + var ar_symtab: Archive.ArSymtab = .{}; + defer ar_symtab.deinit(gpa); + + if (self.zigObjectPtr()) |zig_object| { + try zig_object.updateArSymtab(&ar_symtab, self); } + ar_symtab.sort(); + // Save object paths in filenames strtab. - var ar_strtab = std.ArrayList(u8).init(gpa); - defer ar_strtab.deinit(); - - var files = std.AutoHashMap(File.Index, struct { u32, u64, u64 }).init(gpa); - defer files.deinit(); - try files.ensureUnusedCapacity(@intCast(self.objects.items.len + 1)); + var ar_strtab: Archive.ArStrtab = .{}; + defer ar_strtab.deinit(gpa); if (self.zigObjectPtr()) |zig_object| { - const off = @as(u32, @intCast(ar_strtab.items.len)); - try ar_strtab.writer().print("{s}/\n", .{zig_object.path}); - files.putAssumeCapacityNoClobber(zig_object.index, .{ off, 0, 0 }); + try zig_object.updateArStrtab(gpa, &ar_strtab); + zig_object.updateArSize(self); } - // Align to even byte boundary - { - const end = ar_strtab.items.len; - const aligned = mem.alignForward(usize, end, 2); - try ar_strtab.writer().writeByteNTimes(0, aligned - end); - } + // Update file offsets of contributing objects. + const total_size: u64 = blk: { + var pos: u64 = Archive.SARMAG; + pos += @sizeOf(Archive.ar_hdr) + ar_symtab.size(.p64); + pos = mem.alignForward(u64, pos, 2); + pos += @sizeOf(Archive.ar_hdr) + ar_strtab.size(); - // Encode ar symtab in 64bit format. - var ar_symtab = std.ArrayList(u8).init(gpa); - defer ar_symtab.deinit(); - try ar_symtab.ensureUnusedCapacity(8 * (self.ar_symtab.items.len + 1)); - - // Number of symbols - ar_symtab.writer().writeInt(u64, @as(u64, @intCast(self.ar_symtab.items.len)), .big) catch unreachable; - - // Offsets which we will relocate later. - for (0..self.ar_symtab.items.len) |_| { - ar_symtab.writer().writeInt(u64, 0, .big) catch unreachable; - } - - // ASCII offsets into the strtab. - for (self.ar_symtab.items) |entry| { - const name = self.ar_strtab.getAssumeExists(entry.off); - try ar_symtab.writer().print("{s}\x00", .{name}); - } - - // Align to 8 bytes if required - { - const end = ar_symtab.items.len; - const aligned = mem.alignForward(usize, end, 8); - try ar_symtab.writer().writeByteNTimes(0, aligned - end); - } - - assert(mem.isAligned(ar_symtab.items.len, 8)); - - // Calculate required size for headers before ZigObject pos in file. - if (self.zigObjectPtr()) |zig_object| { - var file_off: u64 = 0; - // Magic - file_off += Archive.SARMAG; - // Symtab - file_off += @sizeOf(Archive.ar_hdr) + @as(u64, @intCast(ar_symtab.items.len)); - // Strtab - file_off += @sizeOf(Archive.ar_hdr) + @as(u64, @intCast(ar_strtab.items.len)); - - const files_ptr = files.getPtr(zig_object.index).?; - files_ptr[1] = file_off; - - // Move ZigObject into place. - { - var end_pos: u64 = self.shdr_table_offset.?; - for (self.shdrs.items) |shdr| { - end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size); - } - const contents = try gpa.alloc(u8, end_pos); - defer gpa.free(contents); - const amt = try self.base.file.?.preadAll(contents, 0); - if (amt != end_pos) return error.InputOutput; - try self.base.file.?.pwriteAll(contents, file_off + @sizeOf(Archive.ar_hdr)); - - files_ptr[2] = end_pos; + if (self.zigObjectPtr()) |zig_object| { + pos = mem.alignForward(u64, pos, 2); + zig_object.output_ar_state.file_off = pos; + pos += @sizeOf(Archive.ar_hdr) + zig_object.output_ar_state.size; } - } - // Fixup file offsets in the symtab. - for (self.ar_symtab.items, 1..) |entry, i| { - const file_off = files.get(entry.file_index).?[1]; - mem.writeInt(u64, ar_symtab.items[8 * i ..][0..8], file_off, .big); - } - - var pos: usize = Archive.SARMAG; - - // Write symtab. - { - const hdr = setArHdr(.{ .kind = .symtab, .name_off = 0, .size = @intCast(ar_symtab.items.len) }); - try self.base.file.?.pwriteAll(mem.asBytes(&hdr), pos); - pos += @sizeOf(Archive.ar_hdr); - try self.base.file.?.pwriteAll(ar_symtab.items, pos); - pos += ar_symtab.items.len; - } - - // Write strtab. - { - const hdr = setArHdr(.{ - .kind = .strtab, - .name_off = 0, - .size = @intCast(ar_strtab.items.len), - }); - try self.base.file.?.pwriteAll(mem.asBytes(&hdr), pos); - pos += @sizeOf(Archive.ar_hdr); - try self.base.file.?.pwriteAll(ar_strtab.items, pos); - pos += ar_strtab.items.len; - } - - // Zig object if defined - if (self.zigObjectPtr()) |zig_object| { - const entry = files.get(zig_object.index).?; - const hdr = setArHdr(.{ .kind = .object, .name_off = entry[0], .size = @intCast(entry[2]) }); - try self.base.file.?.pwriteAll(mem.asBytes(&hdr), entry[1]); - pos += @sizeOf(Archive.ar_hdr) + entry[2]; - } - - // TODO parsed positionals - - // Magic bytes. - { - try self.base.file.?.pwriteAll(Archive.ARMAG, 0); - } -} - -fn setArHdr(opts: struct { - kind: enum { symtab, strtab, object }, - name_off: u32, - size: u32, -}) Archive.ar_hdr { - var hdr: Archive.ar_hdr = .{ - .ar_name = undefined, - .ar_date = undefined, - .ar_uid = undefined, - .ar_gid = undefined, - .ar_mode = undefined, - .ar_size = undefined, - .ar_fmag = undefined, + break :blk pos; }; - @memset(mem.asBytes(&hdr), 0x20); - @memcpy(&hdr.ar_fmag, Archive.ARFMAG); - { - var stream = std.io.fixedBufferStream(&hdr.ar_name); - const writer = stream.writer(); - switch (opts.kind) { - .symtab => writer.print("{s}", .{Archive.SYM64NAME}) catch unreachable, - .strtab => writer.print("//", .{}) catch unreachable, - .object => writer.print("/{d}", .{opts.name_off}) catch unreachable, - } - } - { - var stream = std.io.fixedBufferStream(&hdr.ar_size); - stream.writer().print("{d}", .{opts.size}) catch unreachable; + if (build_options.enable_logging) { + state_log.debug("ar_symtab\n{}\n", .{ar_symtab.fmt(self)}); + state_log.debug("ar_strtab\n{}\n", .{ar_strtab}); } - return hdr; + var buffer = std.ArrayList(u8).init(gpa); + defer buffer.deinit(); + try buffer.ensureTotalCapacityPrecise(total_size); + + // Write magic + try buffer.writer().writeAll(Archive.ARMAG); + + // Write symtab + try ar_symtab.write(.p64, self, buffer.writer()); + if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0); + + // Write strtab + try ar_strtab.write(buffer.writer()); + if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0); + + // Write object files + if (self.zigObjectPtr()) |zig_object| { + try zig_object.writeAr(self, buffer.writer()); + } + + assert(buffer.items.len == total_size); + + try self.base.file.?.pwriteAll(buffer.items, 0); } pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void { @@ -5822,18 +5712,6 @@ fn fmtDumpState( try writer.print("{}\n", .{self.got.fmt(self)}); try writer.print("{}\n", .{self.zig_got.fmt(self)}); - if (self.isStaticLib()) { - try writer.writeAll("ar symtab\n"); - for (self.ar_symtab.items, 0..) |entry, i| { - try writer.print(" {d} : {s} in file({d})\n", .{ - i, - self.ar_strtab.getAssumeExists(entry.off), - entry.file_index, - }); - } - try writer.writeByte('\n'); - } - try writer.writeAll("Output shdrs\n"); for (self.shdrs.items, 0..) |shdr, shndx| { try writer.print("shdr({d}) : phdr({?d}) : {}\n", .{ @@ -5966,19 +5844,6 @@ const LastAtomAndFreeList = struct { const LastAtomAndFreeListTable = std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFreeList); -const ArSymtabEntry = struct { - off: u32, - file_index: File.Index, - - pub fn lessThan(ctx: void, lhs: ArSymtabEntry, rhs: ArSymtabEntry) bool { - _ = ctx; - if (lhs.off == rhs.off) { - return lhs.file_index < rhs.file_index; - } - return lhs.off < rhs.off; - } -}; - pub const R_X86_64_ZIG_GOT32 = elf.R_X86_64_NUM + 1; pub const R_X86_64_ZIG_GOTPCREL = elf.R_X86_64_NUM + 2; diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index 2083171ce7..59576312e8 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -4,69 +4,11 @@ data: []const u8, objects: std.ArrayListUnmanaged(Object) = .{}, strtab: []const u8 = &[0]u8{}, -// Archive files start with the ARMAG identifying string. Then follows a -// `struct ar_hdr', and as many bytes of member file data as its `ar_size' -// member indicates, for each member file. -/// String that begins an archive file. -pub const ARMAG: *const [SARMAG:0]u8 = "!\n"; -/// Size of that string. -pub const SARMAG = 8; - -/// String in ar_fmag at the end of each header. -pub const ARFMAG: *const [2:0]u8 = "`\n"; - -pub const SYM64NAME: *const [7:0]u8 = "/SYM64/"; - -pub const ar_hdr = extern struct { - /// Member file name, sometimes / terminated. - ar_name: [16]u8, - - /// File date, decimal seconds since Epoch. - ar_date: [12]u8, - - /// User ID, in ASCII format. - ar_uid: [6]u8, - - /// Group ID, in ASCII format. - ar_gid: [6]u8, - - /// File mode, in ASCII octal. - ar_mode: [8]u8, - - /// File size, in ASCII decimal. - ar_size: [10]u8, - - /// Always contains ARFMAG. - ar_fmag: [2]u8, - - fn date(self: ar_hdr) !u64 { - const value = getValue(&self.ar_date); - return std.fmt.parseInt(u64, value, 10); - } - - fn size(self: ar_hdr) !u32 { - const value = getValue(&self.ar_size); - return std.fmt.parseInt(u32, value, 10); - } - - fn getValue(raw: []const u8) []const u8 { - return mem.trimRight(u8, raw, &[_]u8{@as(u8, 0x20)}); - } - - fn isStrtab(self: ar_hdr) bool { - return mem.eql(u8, getValue(&self.ar_name), "//"); - } - - fn isSymtab(self: ar_hdr) bool { - return mem.eql(u8, getValue(&self.ar_name), "/") or mem.eql(u8, getValue(&self.ar_name), SYM64NAME); - } -}; - pub fn isArchive(path: []const u8) !bool { const file = try std.fs.cwd().openFile(path, .{}); defer file.close(); const reader = file.reader(); - const magic = reader.readBytesNoEof(Archive.SARMAG) catch return false; + const magic = reader.readBytesNoEof(SARMAG) catch return false; if (!mem.eql(u8, &magic, ARMAG)) return false; return true; } @@ -140,9 +82,267 @@ pub fn parse(self: *Archive, elf_file: *Elf) !void { fn getString(self: Archive, off: u32) []const u8 { assert(off < self.strtab.len); - return mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.ptr + off)), 0); + return mem.sliceTo(@as([*:strtab_delimiter]const u8, @ptrCast(self.strtab.ptr + off)), 0); } +pub fn setArHdr(opts: struct { + kind: enum { symtab, strtab, object }, + name_off: u32, + size: u32, +}) ar_hdr { + var hdr: ar_hdr = .{ + .ar_name = undefined, + .ar_date = undefined, + .ar_uid = undefined, + .ar_gid = undefined, + .ar_mode = undefined, + .ar_size = undefined, + .ar_fmag = undefined, + }; + @memset(mem.asBytes(&hdr), 0x20); + @memcpy(&hdr.ar_fmag, Archive.ARFMAG); + + { + var stream = std.io.fixedBufferStream(&hdr.ar_name); + const writer = stream.writer(); + switch (opts.kind) { + .symtab => writer.print("{s}", .{Archive.SYM64NAME}) catch unreachable, + .strtab => writer.print("//", .{}) catch unreachable, + .object => writer.print("/{d}", .{opts.name_off}) catch unreachable, + } + } + { + var stream = std.io.fixedBufferStream(&hdr.ar_size); + stream.writer().print("{d}", .{opts.size}) catch unreachable; + } + + return hdr; +} + +// Archive files start with the ARMAG identifying string. Then follows a +// `struct ar_hdr', and as many bytes of member file data as its `ar_size' +// member indicates, for each member file. +/// String that begins an archive file. +pub const ARMAG: *const [SARMAG:0]u8 = "!\n"; +/// Size of that string. +pub const SARMAG = 8; + +/// String in ar_fmag at the end of each header. +const ARFMAG: *const [2:0]u8 = "`\n"; + +/// Strtab identifier +const STRNAME: *const [2:0]u8 = "//"; + +/// 32-bit symtab identifier +const SYMNAME: *const [1:0]u8 = "/"; + +/// 64-bit symtab identifier +const SYM64NAME: *const [7:0]u8 = "/SYM64/"; + +const strtab_delimiter = '\n'; + +pub const ar_hdr = extern struct { + /// Member file name, sometimes / terminated. + ar_name: [16]u8, + + /// File date, decimal seconds since Epoch. + ar_date: [12]u8, + + /// User ID, in ASCII format. + ar_uid: [6]u8, + + /// Group ID, in ASCII format. + ar_gid: [6]u8, + + /// File mode, in ASCII octal. + ar_mode: [8]u8, + + /// File size, in ASCII decimal. + ar_size: [10]u8, + + /// Always contains ARFMAG. + ar_fmag: [2]u8, + + fn date(self: ar_hdr) !u64 { + const value = getValue(&self.ar_date); + return std.fmt.parseInt(u64, value, 10); + } + + fn size(self: ar_hdr) !u32 { + const value = getValue(&self.ar_size); + return std.fmt.parseInt(u32, value, 10); + } + + fn getValue(raw: []const u8) []const u8 { + return mem.trimRight(u8, raw, &[_]u8{@as(u8, 0x20)}); + } + + fn isStrtab(self: ar_hdr) bool { + return mem.eql(u8, getValue(&self.ar_name), STRNAME); + } + + fn isSymtab(self: ar_hdr) bool { + return mem.eql(u8, getValue(&self.ar_name), SYMNAME) or mem.eql(u8, getValue(&self.ar_name), SYM64NAME); + } +}; + +pub const ArSymtab = struct { + symtab: std.ArrayListUnmanaged(Entry) = .{}, + strtab: StringTable = .{}, + + pub fn deinit(ar: *ArSymtab, allocator: Allocator) void { + ar.symtab.deinit(allocator); + ar.strtab.deinit(allocator); + } + + pub fn sort(ar: *ArSymtab) void { + mem.sort(Entry, ar.symtab.items, {}, Entry.lessThan); + } + + pub fn size(ar: ArSymtab, kind: enum { p32, p64 }) usize { + const ptr_size: usize = switch (kind) { + .p32 => 4, + .p64 => 8, + }; + var ss: usize = ptr_size + ar.symtab.items.len * ptr_size; + for (ar.symtab.items) |entry| { + ss += ar.strtab.getAssumeExists(entry.off).len + 1; + } + return ss; + } + + pub fn write(ar: ArSymtab, kind: enum { p32, p64 }, elf_file: *Elf, writer: anytype) !void { + assert(kind == .p64); // TODO p32 + const hdr = setArHdr(.{ .kind = .symtab, .name_off = 0, .size = @intCast(ar.size(.p64)) }); + try writer.writeAll(mem.asBytes(&hdr)); + + const gpa = elf_file.base.allocator; + var offsets = std.AutoHashMap(File.Index, u64).init(gpa); + defer offsets.deinit(); + try offsets.ensureUnusedCapacity(@intCast(elf_file.objects.items.len + 1)); + + if (elf_file.zigObjectPtr()) |zig_object| { + offsets.putAssumeCapacityNoClobber(zig_object.index, zig_object.output_ar_state.file_off); + } + + // Number of symbols + try writer.writeInt(u64, @as(u64, @intCast(ar.symtab.items.len)), .big); + + // Offsets to files + for (ar.symtab.items) |entry| { + const off = offsets.get(entry.file_index).?; + try writer.writeInt(u64, off, .big); + } + + // Strings + for (ar.symtab.items) |entry| { + try writer.print("{s}\x00", .{ar.strtab.getAssumeExists(entry.off)}); + } + } + + pub fn format( + ar: ArSymtab, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = ar; + _ = unused_fmt_string; + _ = options; + _ = writer; + @compileError("do not format ar symtab directly; use fmt instead"); + } + + const FormatContext = struct { + ar: ArSymtab, + elf_file: *Elf, + }; + + pub fn fmt(ar: ArSymtab, elf_file: *Elf) std.fmt.Formatter(format2) { + return .{ .data = .{ + .ar = ar, + .elf_file = elf_file, + } }; + } + + fn format2( + ctx: FormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = unused_fmt_string; + _ = options; + const ar = ctx.ar; + const elf_file = ctx.elf_file; + for (ar.symtab.items, 0..) |entry, i| { + const name = ar.strtab.getAssumeExists(entry.off); + const file = elf_file.file(entry.file_index).?; + try writer.print(" {d}: {s} in file({d})({})\n", .{ i, name, entry.file_index, file.fmtPath() }); + } + } + + const Entry = struct { + /// Offset into the string table. + off: u32, + /// Index of the file defining the global. + file_index: File.Index, + + pub fn lessThan(ctx: void, lhs: Entry, rhs: Entry) bool { + _ = ctx; + if (lhs.off == rhs.off) return lhs.file_index < rhs.file_index; + return lhs.off < rhs.off; + } + }; +}; + +pub const ArStrtab = struct { + buffer: std.ArrayListUnmanaged(u8) = .{}, + + pub fn deinit(ar: *ArStrtab, allocator: Allocator) void { + ar.buffer.deinit(allocator); + } + + pub fn insert(ar: *ArStrtab, allocator: Allocator, name: []const u8) error{OutOfMemory}!u32 { + const off = @as(u32, @intCast(ar.buffer.items.len)); + try ar.buffer.writer(allocator).print("{s}/{c}", .{ name, strtab_delimiter }); + return off; + } + + pub fn size(ar: ArStrtab) usize { + return ar.buffer.items.len; + } + + pub fn write(ar: ArStrtab, writer: anytype) !void { + const hdr = setArHdr(.{ .kind = .strtab, .name_off = 0, .size = @intCast(ar.size()) }); + try writer.writeAll(mem.asBytes(&hdr)); + try writer.writeAll(ar.buffer.items); + } + + pub fn format( + ar: ArStrtab, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = unused_fmt_string; + _ = options; + try writer.print("{s}", .{std.fmt.fmtSliceEscapeLower(ar.buffer.items)}); + } +}; + +pub const ArState = struct { + /// Name offset in the string table. + name_off: u32 = 0, + + /// File offset of the ar_hdr describing the contributing + /// object in the archive. + file_off: u64 = 0, + + /// Total size of the contributing object (excludes ar_hdr). + size: u64 = 0, +}; + const std = @import("std"); const assert = std.debug.assert; const elf = std.elf; @@ -153,4 +353,6 @@ const mem = std.mem; const Allocator = mem.Allocator; const Archive = @This(); const Elf = @import("../Elf.zig"); +const File = @import("file.zig").File; const Object = @import("Object.zig"); +const StringTable = @import("../StringTable.zig"); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 12ba88b7a9..bd5ba04834 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -20,6 +20,7 @@ relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{}, num_dynrelocs: u32 = 0, output_symtab_size: Elf.SymtabSize = .{}, +output_ar_state: Archive.ArState = .{}, dwarf: ?Dwarf = null, @@ -502,10 +503,10 @@ fn sortSymbols(self: *ZigObject, elf_file: *Elf) error{OutOfMemory}!void { // mem.sort(Entry, sorted_globals, elf_file, Entry.lessThan); } -pub fn updateArSymtab(self: ZigObject, elf_file: *Elf) !void { +pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void { const gpa = elf_file.base.allocator; - try elf_file.ar_symtab.ensureUnusedCapacity(gpa, self.globals().len); + try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.globals().len); for (self.globals()) |global_index| { const global = elf_file.symbol(global_index); @@ -513,11 +514,45 @@ pub fn updateArSymtab(self: ZigObject, elf_file: *Elf) !void { assert(file_ptr.index() == self.index); if (global.type(elf_file) == elf.SHN_UNDEF) continue; - const off = try elf_file.ar_strtab.insert(gpa, global.name(elf_file)); - elf_file.ar_symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index }); + const off = try ar_symtab.strtab.insert(gpa, global.name(elf_file)); + ar_symtab.symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index }); } } +pub fn updateArStrtab( + self: *ZigObject, + allocator: Allocator, + ar_strtab: *Archive.ArStrtab, +) error{OutOfMemory}!void { + const name = try std.fmt.allocPrint(allocator, "{s}.o", .{std.fs.path.stem(self.path)}); + defer allocator.free(name); + const name_off = try ar_strtab.insert(allocator, name); + self.output_ar_state.name_off = name_off; +} + +pub fn updateArSize(self: *ZigObject, elf_file: *Elf) void { + var end_pos: u64 = elf_file.shdr_table_offset.?; + for (elf_file.shdrs.items) |shdr| { + end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size); + } + self.output_ar_state.size = end_pos; +} + +pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void { + const gpa = elf_file.base.allocator; + const contents = try gpa.alloc(u8, self.output_ar_state.size); + defer gpa.free(contents); + const amt = try elf_file.base.file.?.preadAll(contents, 0); + if (amt != self.output_ar_state.size) return error.InputOutput; + const hdr = Archive.setArHdr(.{ + .kind = .object, + .name_off = self.output_ar_state.name_off, + .size = @intCast(self.output_ar_state.size), + }); + try writer.writeAll(mem.asBytes(&hdr)); + try writer.writeAll(contents); +} + pub fn updateRelaSectionSizes(self: ZigObject, elf_file: *Elf) void { _ = self; @@ -1533,6 +1568,7 @@ const std = @import("std"); const Air = @import("../../Air.zig"); const Allocator = std.mem.Allocator; +const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); const Dwarf = @import("../Dwarf.zig"); const Elf = @import("../Elf.zig"); diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 49d097ab61..e31170a178 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -196,9 +196,9 @@ pub const File = union(enum) { } } - pub fn updateArSymtab(file: File, elf_file: *Elf) !void { + pub fn updateArSymtab(file: File, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void { return switch (file) { - .zig_object => |x| x.updateArSymtab(elf_file), + .zig_object => |x| x.updateArSymtab(ar_symtab, elf_file), .object => @panic("TODO"), inline else => unreachable, }; @@ -219,6 +219,7 @@ const std = @import("std"); const elf = std.elf; const Allocator = std.mem.Allocator; +const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); const Cie = @import("eh_frame.zig").Cie; const Elf = @import("../Elf.zig"); From e3b82eaa661bc964f62a9481ac9ebfe35cf3bb87 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 4 Nov 2023 00:08:55 +0100 Subject: [PATCH 30/36] elf: do not store filename in strtab unless longer than 15 chars --- src/link/Elf.zig | 16 +++++++++++----- src/link/Elf/Archive.zig | 24 +++++++++++++++--------- src/link/Elf/ZigObject.zig | 10 ++++++++-- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 75df6ce40a..b2ac2b687a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1572,8 +1572,11 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void const total_size: u64 = blk: { var pos: u64 = Archive.SARMAG; pos += @sizeOf(Archive.ar_hdr) + ar_symtab.size(.p64); - pos = mem.alignForward(u64, pos, 2); - pos += @sizeOf(Archive.ar_hdr) + ar_strtab.size(); + + if (ar_strtab.size() > 0) { + pos = mem.alignForward(u64, pos, 2); + pos += @sizeOf(Archive.ar_hdr) + ar_strtab.size(); + } if (self.zigObjectPtr()) |zig_object| { pos = mem.alignForward(u64, pos, 2); @@ -1598,19 +1601,22 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void // Write symtab try ar_symtab.write(.p64, self, buffer.writer()); - if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0); // Write strtab - try ar_strtab.write(buffer.writer()); - if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0); + if (ar_strtab.size() > 0) { + if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0); + try ar_strtab.write(buffer.writer()); + } // Write object files if (self.zigObjectPtr()) |zig_object| { + if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0); try zig_object.writeAr(self, buffer.writer()); } assert(buffer.items.len == total_size); + try self.base.file.?.setEndPos(total_size); try self.base.file.?.pwriteAll(buffer.items, 0); } diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index 59576312e8..58a9e541a7 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -61,14 +61,15 @@ pub fn parse(self: *Archive, elf_file: *Elf) !void { const object_name = blk: { if (name[0] == '/') { const off = try std.fmt.parseInt(u32, name[1..], 10); - break :blk self.getString(off); + const object_name = self.getString(off); + break :blk try gpa.dupe(u8, object_name[0 .. object_name.len - 1]); // To account for trailing '/' } - break :blk name; + break :blk try gpa.dupe(u8, name); }; const object = Object{ .archive = try gpa.dupe(u8, self.path), - .path = try gpa.dupe(u8, object_name[0 .. object_name.len - 1]), // To account for trailing '/' + .path = object_name, .data = try gpa.dupe(u8, self.data[stream.pos..][0..size]), .index = undefined, .alive = false, @@ -86,8 +87,12 @@ fn getString(self: Archive, off: u32) []const u8 { } pub fn setArHdr(opts: struct { - kind: enum { symtab, strtab, object }, - name_off: u32, + name: union(enum) { + symtab: void, + strtab: void, + name: []const u8, + name_off: u32, + }, size: u32, }) ar_hdr { var hdr: ar_hdr = .{ @@ -105,10 +110,11 @@ pub fn setArHdr(opts: struct { { var stream = std.io.fixedBufferStream(&hdr.ar_name); const writer = stream.writer(); - switch (opts.kind) { + switch (opts.name) { .symtab => writer.print("{s}", .{Archive.SYM64NAME}) catch unreachable, .strtab => writer.print("//", .{}) catch unreachable, - .object => writer.print("/{d}", .{opts.name_off}) catch unreachable, + .name => |x| writer.print("{s}", .{x}) catch unreachable, + .name_off => |x| writer.print("/{d}", .{x}) catch unreachable, } } { @@ -213,7 +219,7 @@ pub const ArSymtab = struct { pub fn write(ar: ArSymtab, kind: enum { p32, p64 }, elf_file: *Elf, writer: anytype) !void { assert(kind == .p64); // TODO p32 - const hdr = setArHdr(.{ .kind = .symtab, .name_off = 0, .size = @intCast(ar.size(.p64)) }); + const hdr = setArHdr(.{ .name = .symtab, .size = @intCast(ar.size(.p64)) }); try writer.writeAll(mem.asBytes(&hdr)); const gpa = elf_file.base.allocator; @@ -314,7 +320,7 @@ pub const ArStrtab = struct { } pub fn write(ar: ArStrtab, writer: anytype) !void { - const hdr = setArHdr(.{ .kind = .strtab, .name_off = 0, .size = @intCast(ar.size()) }); + const hdr = setArHdr(.{ .name = .strtab, .size = @intCast(ar.size()) }); try writer.writeAll(mem.asBytes(&hdr)); try writer.writeAll(ar.buffer.items); } diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index bd5ba04834..5442d9a4bb 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -526,6 +526,7 @@ pub fn updateArStrtab( ) error{OutOfMemory}!void { const name = try std.fmt.allocPrint(allocator, "{s}.o", .{std.fs.path.stem(self.path)}); defer allocator.free(name); + if (name.len <= 15) return; const name_off = try ar_strtab.insert(allocator, name); self.output_ar_state.name_off = name_off; } @@ -540,13 +541,18 @@ pub fn updateArSize(self: *ZigObject, elf_file: *Elf) void { pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void { const gpa = elf_file.base.allocator; + const contents = try gpa.alloc(u8, self.output_ar_state.size); defer gpa.free(contents); + const amt = try elf_file.base.file.?.preadAll(contents, 0); if (amt != self.output_ar_state.size) return error.InputOutput; + + const name = try std.fmt.allocPrint(gpa, "{s}.o", .{std.fs.path.stem(self.path)}); + defer gpa.free(name); + const hdr = Archive.setArHdr(.{ - .kind = .object, - .name_off = self.output_ar_state.name_off, + .name = if (name.len <= 15) .{ .name = name } else .{ .name_off = self.output_ar_state.name_off }, .size = @intCast(self.output_ar_state.size), }); try writer.writeAll(mem.asBytes(&hdr)); From ee66137269ee5375d7a3ca030ad0989f5edc625f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 4 Nov 2023 00:32:49 +0100 Subject: [PATCH 31/36] elf: add link smoke tests covering emitting obj and static lib --- test/link/elf.zig | 68 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/test/link/elf.zig b/test/link/elf.zig index 766d1980e2..75cc34ec32 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -23,6 +23,8 @@ pub fn build(b: *Build) void { // Exercise linker with self-hosted backend (no LLVM) elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target })); + elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target })); + elf_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = default_target })); elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target })); elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = glibc_target })); elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = musl_target })); @@ -1823,6 +1825,72 @@ fn testLinkingCpp(b: *Build, opts: Options) *Step { return test_step; } +fn testLinkingObj(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "linking-obj", opts); + + const obj = addObject(b, "aobj", opts); + addZigSourceBytes(obj, + \\extern var mod: usize; + \\export fn callMe() usize { + \\ return me * mod; + \\} + \\var me: usize = 42; + ); + + const exe = addExecutable(b, "testobj", opts); + addZigSourceBytes(exe, + \\const std = @import("std"); + \\extern fn callMe() usize; + \\export var mod: usize = 2; + \\pub fn main() void { + \\ std.debug.print("{d}\n", .{callMe()}); + \\} + ); + exe.addObject(obj); + + const run = addRunArtifact(exe); + run.expectStdErrEqual("84\n"); + test_step.dependOn(&run.step); + + return test_step; +} + +fn testLinkingStaticLib(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "linking-static-lib", opts); + + const lib = b.addStaticLibrary(.{ + .name = "alib", + .target = opts.target, + .optimize = opts.optimize, + .use_llvm = opts.use_llvm, + .use_lld = false, + }); + addZigSourceBytes(lib, + \\extern var mod: usize; + \\export fn callMe() usize { + \\ return me * mod; + \\} + \\var me: usize = 42; + ); + + const exe = addExecutable(b, "testlib", opts); + addZigSourceBytes(exe, + \\const std = @import("std"); + \\extern fn callMe() usize; + \\export var mod: usize = 2; + \\pub fn main() void { + \\ std.debug.print("{d}\n", .{callMe()}); + \\} + ); + exe.linkLibrary(lib); + + const run = addRunArtifact(exe); + run.expectStdErrEqual("84\n"); + test_step.dependOn(&run.step); + + return test_step; +} + fn testLinkingZig(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "linking-zig-static", opts); From 56296694d94e83de844e26ac28af19d7b28533fd Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 4 Nov 2023 00:39:58 +0100 Subject: [PATCH 32/36] elf: fix 32bit build --- src/link/Elf.zig | 13 +++++++------ src/link/Elf/ZigObject.zig | 7 ++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b2ac2b687a..a16fb83f1a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1569,19 +1569,19 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void } // Update file offsets of contributing objects. - const total_size: u64 = blk: { - var pos: u64 = Archive.SARMAG; + const total_size: usize = blk: { + var pos: usize = Archive.SARMAG; pos += @sizeOf(Archive.ar_hdr) + ar_symtab.size(.p64); if (ar_strtab.size() > 0) { - pos = mem.alignForward(u64, pos, 2); + pos = mem.alignForward(usize, pos, 2); pos += @sizeOf(Archive.ar_hdr) + ar_strtab.size(); } if (self.zigObjectPtr()) |zig_object| { - pos = mem.alignForward(u64, pos, 2); + pos = mem.alignForward(usize, pos, 2); zig_object.output_ar_state.file_off = pos; - pos += @sizeOf(Archive.ar_hdr) + zig_object.output_ar_state.size; + pos += @sizeOf(Archive.ar_hdr) + (math.cast(usize, zig_object.output_ar_state.size) orelse return error.Overflow); } break :blk pos; @@ -4672,7 +4672,8 @@ fn writeSymtab(self: *Elf) !void { log.debug("writing {d} symbols at 0x{x}", .{ nsyms, symtab_shdr.sh_offset }); try self.symtab.resize(gpa, nsyms); - try self.strtab.ensureUnusedCapacity(gpa, strtab_shdr.sh_size - 1); + const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow; + try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size); const Ctx = struct { ilocal: usize, diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 5442d9a4bb..fda86f8125 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -542,7 +542,8 @@ pub fn updateArSize(self: *ZigObject, elf_file: *Elf) void { pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void { const gpa = elf_file.base.allocator; - const contents = try gpa.alloc(u8, self.output_ar_state.size); + const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; + const contents = try gpa.alloc(u8, size); defer gpa.free(contents); const amt = try elf_file.base.file.?.preadAll(contents, 0); @@ -553,7 +554,7 @@ pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void { const hdr = Archive.setArHdr(.{ .name = if (name.len <= 15) .{ .name = name } else .{ .name_off = self.output_ar_state.name_off }, - .size = @intCast(self.output_ar_state.size), + .size = size, }); try writer.writeAll(mem.asBytes(&hdr)); try writer.writeAll(contents); @@ -610,7 +611,7 @@ pub fn writeRelaSections(self: ZigObject, elf_file: *Elf) !void { var relocs = std.ArrayList(elf.Elf64_Rela).init(gpa); defer relocs.deinit(); - try relocs.ensureTotalCapacityPrecise(@divExact(shdr.sh_size, shdr.sh_entsize)); + try relocs.ensureTotalCapacityPrecise(@intCast(@divExact(shdr.sh_size, shdr.sh_entsize))); while (true) { for (atom.relocs(elf_file)) |rel| { From 9cf3ebe524dcc3fe6e80174041c8ed6687cbfcaa Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 4 Nov 2023 01:07:09 +0100 Subject: [PATCH 33/36] elf: fix more int resolution issues --- src/link/Elf/ZigObject.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index fda86f8125..a6ac6cfc11 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -554,7 +554,7 @@ pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void { const hdr = Archive.setArHdr(.{ .name = if (name.len <= 15) .{ .name = name } else .{ .name_off = self.output_ar_state.name_off }, - .size = size, + .size = @intCast(size), }); try writer.writeAll(mem.asBytes(&hdr)); try writer.writeAll(contents); From bd0416aeb6926225001b2f58a89115cc296708d7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 4 Nov 2023 08:11:07 +0100 Subject: [PATCH 34/36] elf: flag errors on hitting unimplemented codepaths --- src/link/Elf.zig | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index a16fb83f1a..1667f7fc12 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1384,7 +1384,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node if (csu.crtn) |v| try positionals.append(.{ .path = v }); if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self); - if (self.isStaticLib()) return self.flushStaticLib(comp); + if (self.isStaticLib()) return self.flushStaticLib(comp, positionals.items); for (positionals.items) |obj| { var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined }; @@ -1521,8 +1521,17 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } } -pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void { +pub fn flushStaticLib( + self: *Elf, + comp: *Compilation, + positionals: []const Compilation.LinkObject, +) link.File.FlushError!void { _ = comp; + if (positionals.len > 0) { + var err = try self.addErrorWithNotes(1); + try err.addMsg(self, "fatal linker error: too many input positionals", .{}); + try err.addNote(self, "TODO implement linking objects into an static library", .{}); + } const gpa = self.base.allocator; // First, we flush relocatable object file generated with our backends. @@ -1622,6 +1631,13 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void { _ = comp; + + if (self.objects.items.len > 0) { + var err = try self.addErrorWithNotes(1); + try err.addMsg(self, "fatal linker error: too many input positionals", .{}); + try err.addNote(self, "TODO implement '-r' option", .{}); + } + self.claimUnresolvedObject(); try self.initSections(); From 533c88158ee2d0653e0d1f618371d56d81786214 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 4 Nov 2023 08:50:31 +0100 Subject: [PATCH 35/36] elf: actually capture positionals when linking static library --- src/link/Elf.zig | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 1667f7fc12..ab986aeaf9 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1217,6 +1217,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node Compilation.dump_argv(argv.items); } + if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self); + // Here we will parse input positional and library files (if referenced). // This will roughly match in any linker backend we support. var positionals = std.ArrayList(Compilation.LinkObject).init(arena); @@ -1284,6 +1286,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try positionals.append(.{ .path = ssp.full_object_path }); } + if (self.isStaticLib()) return self.flushStaticLib(comp, positionals.items); + for (positionals.items) |obj| { var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined }; self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err| @@ -1383,9 +1387,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node if (csu.crtend) |v| try positionals.append(.{ .path = v }); if (csu.crtn) |v| try positionals.append(.{ .path = v }); - if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self); - if (self.isStaticLib()) return self.flushStaticLib(comp, positionals.items); - for (positionals.items) |obj| { var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined }; self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err| @@ -1531,6 +1532,7 @@ pub fn flushStaticLib( var err = try self.addErrorWithNotes(1); try err.addMsg(self, "fatal linker error: too many input positionals", .{}); try err.addNote(self, "TODO implement linking objects into an static library", .{}); + return; } const gpa = self.base.allocator; @@ -1636,6 +1638,7 @@ pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void { var err = try self.addErrorWithNotes(1); try err.addMsg(self, "fatal linker error: too many input positionals", .{}); try err.addNote(self, "TODO implement '-r' option", .{}); + return; } self.claimUnresolvedObject(); From 7a186d9eb6a84fb22bdb53b9c81a70169e9fa65f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 4 Nov 2023 16:09:52 +0100 Subject: [PATCH 36/36] Compilation: take into account if LLVM is available in lib-building logic --- src/Compilation.zig | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 7c8dd84457..b9ba8359ee 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -6243,11 +6243,9 @@ fn canBuildLibCompilerRt(target: std.Target, use_llvm: bool) bool { else => {}, } return switch (zigBackend(target, use_llvm)) { - .stage2_llvm, - .stage2_x86_64, - => true, - - else => false, + .stage2_llvm => true, + .stage2_x86_64 => if (target.ofmt == .elf) true else build_options.have_llvm, + else => build_options.have_llvm, }; } @@ -6262,7 +6260,7 @@ fn canBuildLibSsp(target: std.Target, use_llvm: bool) bool { } return switch (zigBackend(target, use_llvm)) { .stage2_llvm => true, - else => false, + else => build_options.have_llvm, }; } @@ -6278,11 +6276,9 @@ fn canBuildZigLibC(target: std.Target, use_llvm: bool) bool { else => {}, } return switch (zigBackend(target, use_llvm)) { - .stage2_llvm, - .stage2_x86_64, - => true, - - else => false, + .stage2_llvm => true, + .stage2_x86_64 => if (target.ofmt == .elf) true else build_options.have_llvm, + else => build_options.have_llvm, }; }