From 75f4420c2db46cc3d9fe9c75ac035c588bbcf4bf Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 30 Sep 2023 12:09:16 +0200 Subject: elf: increase Atom.Index resolution to u32 --- src/link/Elf/ZigModule.zig | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'src/link/Elf/ZigModule.zig') diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index c79680dbf5..51e2085384 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -13,7 +13,7 @@ local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{}, -atoms: std.AutoArrayHashMapUnmanaged(Atom.Index, void) = .{}, +atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{}, output_symtab_size: Elf.SymtabSize = .{}, @@ -56,7 +56,8 @@ pub fn addAtom(self: *ZigModule, elf_file: *Elf) !Symbol.Index { const symbol_index = try elf_file.addSymbol(); const esym_index = try self.addLocalEsym(gpa); - try self.atoms.putNoClobber(gpa, atom_index, {}); + const shndx = @as(u16, @intCast(self.atoms.items.len)); + try self.atoms.append(gpa, atom_index); try self.local_symbols.append(gpa, symbol_index); const atom_ptr = elf_file.atom(atom_index).?; @@ -67,10 +68,10 @@ pub fn addAtom(self: *ZigModule, elf_file: *Elf) !Symbol.Index { symbol_ptr.atom_index = atom_index; const esym = &self.local_esyms.items[esym_index]; - esym.st_shndx = atom_index; + esym.st_shndx = shndx; symbol_ptr.esym_index = esym_index; - const relocs_index = @as(Atom.Index, @intCast(self.relocs.items.len)); + const relocs_index = @as(u16, @intCast(self.relocs.items.len)); const relocs = try self.relocs.addOne(gpa); relocs.* = .{}; atom_ptr.relocs_section_index = relocs_index; @@ -86,7 +87,7 @@ pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void { if (esym.st_shndx == elf.SHN_UNDEF) continue; if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) { - const atom_index = esym.st_shndx; + const atom_index = self.atoms.items[esym.st_shndx]; const atom = elf_file.atom(atom_index) orelse continue; if (!atom.flags.alive) continue; } @@ -95,7 +96,7 @@ pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void { if (self.asFile().symbolRank(esym, false) < global.symbolRank(elf_file)) { const atom_index = switch (esym.st_shndx) { elf.SHN_ABS, elf.SHN_COMMON => 0, - else => esym.st_shndx, + else => self.atoms.items[esym.st_shndx], }; const output_section_index = if (elf_file.atom(atom_index)) |atom| atom.outputShndx().? @@ -141,7 +142,7 @@ pub fn claimUnresolved(self: *ZigModule, elf_file: *Elf) void { } pub fn scanRelocs(self: *ZigModule, elf_file: *Elf, undefs: anytype) !void { - for (self.atoms.keys()) |atom_index| { + for (self.atoms.items) |atom_index| { const atom = elf_file.atom(atom_index) orelse continue; if (!atom.flags.alive) continue; if (try atom.scanRelocsRequiresCode(elf_file)) { @@ -324,7 +325,7 @@ fn formatAtoms( _ = unused_fmt_string; _ = options; try writer.writeAll(" atoms\n"); - for (ctx.self.atoms.keys()) |atom_index| { + for (ctx.self.atoms.items) |atom_index| { const atom = ctx.elf_file.atom(atom_index) orelse continue; try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)}); } -- cgit v1.2.3 From 9ccd94d56037e05e87755887e334aa6a1a096ec5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 3 Oct 2023 16:40:39 +0200 Subject: elf: refactor object.shdrContents to never error out --- src/link/Elf.zig | 54 ++++++++++++++++++++--- src/link/Elf/Atom.zig | 27 +++++++++--- src/link/Elf/Object.zig | 106 ++++++++++++++++++++++++++++++--------------- src/link/Elf/ZigModule.zig | 2 +- src/link/Elf/eh_frame.zig | 50 ++++++++++----------- 5 files changed, 167 insertions(+), 72 deletions(-) (limited to 'src/link/Elf/ZigModule.zig') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 530b682b15..dd8784027e 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1235,7 +1235,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // input Object files. // Any qualifing unresolved symbol will be upgraded to an absolute, weak // symbol for potential resolution at load-time. - try self.resolveSymbols(); + self.resolveSymbols(); + self.markEhFrameAtomsDead(); self.markImportsExports(); self.claimUnresolved(); @@ -1610,7 +1611,7 @@ fn parseArchive( /// 4. Reset state of all resolved globals since we will redo this bit on the pruned set. /// 5. Remove references to dead objects/shared objects /// 6. Re-run symbol resolution on pruned objects and shared objects sets. -fn resolveSymbols(self: *Elf) error{Overflow}!void { +fn resolveSymbols(self: *Elf) void { // Resolve symbols in the ZigModule. For now, we assume that it's always live. if (self.zig_module_index) |index| self.file(index).?.resolveSymbols(self); // Resolve symbols on the set of all objects and shared objects (even if some are unneeded). @@ -1652,11 +1653,11 @@ fn resolveSymbols(self: *Elf) error{Overflow}!void { const cg = self.comdatGroup(cg_index); const cg_owner = self.comdatGroupOwner(cg.owner); if (cg_owner.file != index) { - for (try object.comdatGroupMembers(cg.shndx)) |shndx| { + for (object.comdatGroupMembers(cg.shndx)) |shndx| { const atom_index = object.atoms.items[shndx]; if (self.atom(atom_index)) |atom_ptr| { atom_ptr.flags.alive = false; - // atom_ptr.markFdesDead(self); + atom_ptr.markFdesDead(self); } } } @@ -1680,6 +1681,14 @@ fn markLive(self: *Elf) void { } } +fn markEhFrameAtomsDead(self: *Elf) void { + for (self.objects.items) |index| { + const file_ptr = self.file(index).?; + if (!file_ptr.isAlive()) continue; + file_ptr.object.markEhFrameAtomsDead(self); + } +} + fn markImportsExports(self: *Elf) void { const mark = struct { fn mark(elf_file: *Elf, file_index: File.Index) void { @@ -3389,17 +3398,39 @@ fn initSections(self: *Elf) !void { .p32 => true, .p64 => false, }; + const ptr_size = self.ptrWidthBytes(); for (self.objects.items) |index| { try self.file(index).?.object.initOutputSections(self); } + 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) { + self.eh_frame_section_index = try self.addSection(.{ + .name = ".eh_frame", + .type = elf.SHT_PROGBITS, + .flags = elf.SHF_ALLOC, + .addralign = ptr_size, + }); + + if (self.base.options.eh_frame_hdr) { + self.eh_frame_hdr_section_index = try self.addSection(.{ + .name = ".eh_frame_hdr", + .type = elf.SHT_PROGBITS, + .flags = elf.SHF_ALLOC, + .addralign = ptr_size, + }); + } + } + if (self.got.entries.items.len > 0 and self.got_section_index == null) { self.got_section_index = try self.addSection(.{ .name = ".got", .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, - .addralign = self.ptrWidthBytes(), + .addralign = ptr_size, }); } @@ -3533,6 +3564,18 @@ fn updateSectionSizes(self: *Elf) !void { self.file(index).?.object.updateSectionSizes(self); } + if (self.eh_frame_section_index) |index| { + const shdr = &self.shdrs.items[index]; + shdr.sh_size = try eh_frame.calcEhFrameSize(self); + shdr.sh_addralign = @alignOf(u64); + } + + if (self.eh_frame_hdr_section_index) |index| { + const shdr = &self.shdrs.items[index]; + shdr.sh_size = eh_frame.calcEhFrameHdrSize(self); + shdr.sh_addralign = @alignOf(u32); + } + if (self.got_section_index) |index| { self.shdrs.items[index].sh_size = self.got.size(self); } @@ -4937,6 +4980,7 @@ const math = std.math; const mem = std.mem; const codegen = @import("../codegen.zig"); +const eh_frame = @import("Elf/eh_frame.zig"); const glibc = @import("../glibc.zig"); const link = @import("../link.zig"); const lldMain = @import("../main.zig").lldMain; diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 762c572abc..294486f043 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -49,7 +49,7 @@ pub fn file(self: Atom, elf_file: *Elf) ?File { return elf_file.file(self.file_index); } -pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr { +pub fn inputShdr(self: Atom, elf_file: *Elf) Object.ElfShdr { const object = self.file(elf_file).?.object; return object.shdrs.items[self.input_section_index]; } @@ -272,7 +272,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void { self.* = .{}; } -pub fn relocs(self: Atom, elf_file: *Elf) error{Overflow}![]align(1) const elf.Elf64_Rela { +pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela { return switch (self.file(elf_file).?) { .zig_module => |x| x.relocs.items[self.relocs_section_index].items, .object => |x| x.getRelocs(self.relocs_section_index), @@ -280,6 +280,18 @@ pub fn relocs(self: Atom, elf_file: *Elf) error{Overflow}![]align(1) const elf.E }; } +pub fn fdes(self: Atom, elf_file: *Elf) []Fde { + if (self.fde_start == self.fde_end) return &[0]Fde{}; + const object = self.file(elf_file).?.object; + return object.fdes.items[self.fde_start..self.fde_end]; +} + +pub fn markFdesDead(self: Atom, elf_file: *Elf) void { + for (self.fdes(elf_file)) |*fde| { + fde.alive = false; + } +} + pub fn addReloc(self: Atom, elf_file: *Elf, reloc: elf.Elf64_Rela) !void { const gpa = elf_file.base.allocator; const file_ptr = self.file(elf_file).?; @@ -296,8 +308,8 @@ pub fn freeRelocs(self: Atom, elf_file: *Elf) void { zig_module.relocs.items[self.relocs_section_index].clearRetainingCapacity(); } -pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) error{Overflow}!bool { - for (try self.relocs(elf_file)) |rel| { +pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool { + for (self.relocs(elf_file)) |rel| { if (rel.r_type() == elf.R_X86_64_GOTTPOFF) return true; } return false; @@ -306,7 +318,7 @@ pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) error{Overflow}!bool { pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void { const is_dyn_lib = elf_file.isDynLib(); const file_ptr = self.file(elf_file).?; - const rels = try self.relocs(elf_file); + const rels = self.relocs(elf_file); var i: usize = 0; while (i < rels.len) : (i += 1) { const rel = rels[i]; @@ -456,7 +468,7 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { var stream = std.io.fixedBufferStream(code); const cwriter = stream.writer(); - const rels = try self.relocs(elf_file); + const rels = self.relocs(elf_file); var i: usize = 0; while (i < rels.len) : (i += 1) { const rel = rels[i]; @@ -841,11 +853,14 @@ const x86_64 = struct { const std = @import("std"); const assert = std.debug.assert; const elf = std.elf; +const eh_frame = @import("eh_frame.zig"); const log = std.log.scoped(.link); const relocs_log = std.log.scoped(.link_relocs); const Allocator = std.mem.Allocator; const Atom = @This(); const Elf = @import("../Elf.zig"); +const Fde = eh_frame.Fde; const File = @import("file.zig").File; +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 de89ee822a..8fb44e0faa 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -4,7 +4,7 @@ data: []const u8, index: File.Index, header: ?elf.Elf64_Ehdr = null, -shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{}, +shdrs: std.ArrayListUnmanaged(ElfShdr) = .{}, strings: StringTable(.object_strings) = .{}, symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{}, strtab: []const u8 = &[0]u8{}, @@ -64,8 +64,13 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { [*]align(1) const elf.Elf64_Shdr, @ptrCast(self.data.ptr + shoff), )[0..self.header.?.e_shnum]; - try self.shdrs.appendUnalignedSlice(gpa, shdrs); - try self.strings.buffer.appendSlice(gpa, try self.shdrContents(self.header.?.e_shstrndx)); + try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len); + + for (shdrs) |shdr| { + self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr)); + } + + try self.strings.buffer.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)), @@ -76,21 +81,21 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { const shdr = shdrs[index]; self.first_global = shdr.sh_info; - const symtab = try self.shdrContents(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 = try self.shdrContents(@as(u16, @intCast(shdr.sh_link))); + self.strtab = self.shdrContents(@as(u16, @intCast(shdr.sh_link))); } try self.initAtoms(elf_file); try self.initSymtab(elf_file); - // for (self.shdrs.items, 0..) |shdr, i| { - // const atom = elf_file.atom(self.atoms.items[i]) orelse continue; - // if (!atom.alive) continue; - // if (shdr.sh_type == elf.SHT_X86_64_UNWIND or mem.eql(u8, atom.name(elf_file), ".eh_frame")) - // try self.parseEhFrame(@as(u16, @intCast(i)), elf_file); - // } + for (self.shdrs.items, 0..) |shdr, i| { + const atom = elf_file.atom(self.atoms.items[i]) orelse continue; + if (!atom.flags.alive) continue; + if (shdr.sh_type == elf.SHT_X86_64_UNWIND or mem.eql(u8, atom.name(elf_file), ".eh_frame")) + try self.parseEhFrame(@as(u16, @intCast(i)), elf_file); + } } fn initAtoms(self: *Object, elf_file: *Elf) !void { @@ -120,7 +125,7 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void { }; const shndx = @as(u16, @intCast(i)); - const group_raw_data = try self.shdrContents(shndx); + const group_raw_data = self.shdrContents(shndx); const group_nmembers = @divExact(group_raw_data.len, @sizeOf(u32)); const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers]; @@ -173,11 +178,11 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void { fn addAtom( self: *Object, - shdr: elf.Elf64_Shdr, + shdr: ElfShdr, shndx: u16, name: [:0]const u8, elf_file: *Elf, -) error{ OutOfMemory, Overflow }!void { +) error{OutOfMemory}!void { const atom_index = try elf_file.addAtom(); const atom = elf_file.atom(atom_index).?; atom.atom_index = atom_index; @@ -188,7 +193,7 @@ fn addAtom( self.atoms.items[shndx] = atom_index; if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) { - const data = try self.shdrContents(shndx); + const data = self.shdrContents(shndx); const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*; atom.size = chdr.ch_size; atom.alignment = Alignment.fromNonzeroByteUnits(chdr.ch_addralign); @@ -198,7 +203,7 @@ fn addAtom( } } -fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) error{OutOfMemory}!u16 { +fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMemory}!u16 { const name = blk: { const name = self.strings.getAssumeExists(shdr.sh_name); if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name; @@ -244,7 +249,6 @@ fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) error{O } fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool { - _ = elf_file; const shdr = self.shdrs.items[index]; const name = self.strings.getAssumeExists(shdr.sh_name); const ignore = blk: { @@ -252,9 +256,8 @@ fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool { if (mem.startsWith(u8, name, ".comment")) break :blk true; if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true; if (mem.startsWith(u8, name, ".eh_frame")) break :blk true; - // if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and - // mem.startsWith(u8, name, ".debug")) break :blk true; - if (shdr.sh_flags & elf.SHF_ALLOC == 0 and mem.startsWith(u8, name, ".debug")) break :blk true; + if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and + mem.startsWith(u8, name, ".debug")) break :blk true; break :blk false; }; return ignore; @@ -303,8 +306,8 @@ fn parseEhFrame(self: *Object, shndx: u16, elf_file: *Elf) !void { }; const gpa = elf_file.base.allocator; - const raw = try self.shdrContents(shndx); - const relocs = try self.getRelocs(relocs_shndx); + const raw = self.shdrContents(shndx); + const relocs = self.getRelocs(relocs_shndx); const fdes_start = self.fdes.items.len; const cies_start = self.cies.items.len; @@ -408,7 +411,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void { const shdr = atom.inputShdr(elf_file); if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; if (shdr.sh_type == elf.SHT_NOBITS) continue; - if (try atom.scanRelocsRequiresCode(elf_file)) { + if (atom.scanRelocsRequiresCode(elf_file)) { // TODO ideally, we don't have to decompress at this stage (should already be done) // and we just fetch the code slice. const code = try self.codeDecompressAlloc(elf_file, atom_index); @@ -418,7 +421,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void { } for (self.cies.items) |cie| { - for (try cie.relocs(elf_file)) |rel| { + for (cie.relocs(elf_file)) |rel| { const sym = elf_file.symbol(self.symbols.items[rel.r_sym()]); if (sym.flags.import) { if (sym.type(elf_file) != elf.STT_FUNC) @@ -518,6 +521,15 @@ pub fn markLive(self: *Object, elf_file: *Elf) void { } } +pub fn markEhFrameAtomsDead(self: Object, elf_file: *Elf) void { + for (self.atoms.items) |atom_index| { + const atom = elf_file.atom(atom_index) orelse continue; + const is_eh_frame = atom.inputShdr(elf_file).sh_type == elf.SHT_X86_64_UNWIND or + mem.eql(u8, atom.name(elf_file), ".eh_frame"); + if (atom.flags.alive and is_eh_frame) atom.flags.alive = false; + } +} + pub fn checkDuplicates(self: *Object, elf_file: *Elf) void { const first_global = self.first_global orelse return; for (self.globals(), 0..) |index, i| { @@ -747,12 +759,10 @@ pub fn globals(self: Object) []const Symbol.Index { return self.symbols.items[start..]; } -fn shdrContents(self: Object, index: u32) error{Overflow}![]const u8 { +pub fn shdrContents(self: Object, index: u32) []const u8 { assert(index < self.shdrs.items.len); const shdr = self.shdrs.items[index]; - const offset = math.cast(usize, shdr.sh_offset) orelse return error.Overflow; - const size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; - return self.data[offset..][0..size]; + return self.data[shdr.sh_offset..][0..shdr.sh_size]; } /// Returns atom's code and optionally uncompresses data if required (for compressed sections). @@ -761,7 +771,7 @@ pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index) const gpa = elf_file.base.allocator; const atom_ptr = elf_file.atom(atom_index).?; assert(atom_ptr.file_index == self.index); - const data = try self.shdrContents(atom_ptr.input_section_index); + const data = self.shdrContents(atom_ptr.input_section_index); const shdr = atom_ptr.inputShdr(elf_file); if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) { const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*; @@ -789,8 +799,8 @@ fn getString(self: *Object, off: u32) [:0]const u8 { return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0); } -pub fn comdatGroupMembers(self: *Object, index: u16) error{Overflow}![]align(1) const u32 { - const raw = try self.shdrContents(index); +pub fn comdatGroupMembers(self: *Object, index: u16) []align(1) const u32 { + const raw = self.shdrContents(index); const nmembers = @divExact(raw.len, @sizeOf(u32)); const members = @as([*]align(1) const u32, @ptrCast(raw.ptr))[1..nmembers]; return members; @@ -800,8 +810,8 @@ pub fn asFile(self: *Object) File { return .{ .object = self }; } -pub fn getRelocs(self: *Object, shndx: u32) error{Overflow}![]align(1) const elf.Elf64_Rela { - const raw = try self.shdrContents(shndx); +pub fn getRelocs(self: *Object, shndx: u32) []align(1) const elf.Elf64_Rela { + const raw = self.shdrContents(shndx); const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela)); return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num]; } @@ -941,7 +951,7 @@ fn formatComdatGroups( const cg = elf_file.comdatGroup(cg_index); const cg_owner = elf_file.comdatGroupOwner(cg.owner); if (cg_owner.file != object.index) continue; - const cg_members = object.comdatGroupMembers(cg.shndx) catch continue; + const cg_members = object.comdatGroupMembers(cg.shndx); for (cg_members) |shndx| { const atom_index = object.atoms.items[shndx]; const atom = elf_file.atom(atom_index) orelse continue; @@ -970,6 +980,34 @@ fn formatPath( } else try writer.writeAll(object.path); } +pub const ElfShdr = struct { + sh_name: u32, + sh_type: u32, + sh_flags: u64, + sh_addr: u64, + sh_offset: usize, + sh_size: usize, + sh_link: u32, + sh_info: u32, + sh_addralign: u64, + sh_entsize: u64, + + fn fromElf64Shdr(shdr: elf.Elf64_Shdr) error{Overflow}!ElfShdr { + return .{ + .sh_name = shdr.sh_name, + .sh_type = shdr.sh_type, + .sh_flags = shdr.sh_flags, + .sh_addr = shdr.sh_addr, + .sh_offset = math.cast(usize, shdr.sh_offset) orelse return error.Overflow, + .sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow, + .sh_link = shdr.sh_link, + .sh_info = shdr.sh_info, + .sh_addralign = shdr.sh_addralign, + .sh_entsize = shdr.sh_entsize, + }; + } +}; + const Object = @This(); const std = @import("std"); diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index 51e2085384..c5a0adf626 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -145,7 +145,7 @@ pub fn scanRelocs(self: *ZigModule, elf_file: *Elf, undefs: anytype) !void { for (self.atoms.items) |atom_index| { const atom = elf_file.atom(atom_index) orelse continue; if (!atom.flags.alive) continue; - if (try atom.scanRelocsRequiresCode(elf_file)) { + if (atom.scanRelocsRequiresCode(elf_file)) { // TODO ideally we don't have to fetch the code here. // Perhaps it would make sense to save the code until flushModule where we // would free all of generated code? diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index 8c676504e2..d2f525f321 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -20,9 +20,9 @@ pub const Fde = struct { return base + fde.out_offset; } - pub fn data(fde: Fde, elf_file: *Elf) error{Overflow}![]const u8 { + pub fn data(fde: Fde, elf_file: *Elf) []const u8 { const object = elf_file.file(fde.file_index).?.object; - const contents = try object.shdrContents(fde.input_section_index); + const contents = object.shdrContents(fde.input_section_index); return contents[fde.offset..][0..fde.calcSize()]; } @@ -32,24 +32,25 @@ pub const Fde = struct { } pub fn ciePointer(fde: Fde, elf_file: *Elf) u32 { - return std.mem.readIntLittle(u32, fde.data(elf_file)[4..8]); + const fde_data = fde.data(elf_file); + return std.mem.readIntLittle(u32, fde_data[4..8]); } pub fn calcSize(fde: Fde) u64 { return fde.size + 4; } - pub fn atom(fde: Fde, elf_file: *Elf) error{Overflow}!*Atom { + pub fn atom(fde: Fde, elf_file: *Elf) *Atom { const object = elf_file.file(fde.file_index).?.object; - const rel = (try fde.relocs(elf_file))[0]; + const rel = fde.relocs(elf_file)[0]; const sym = object.symtab[rel.r_sym()]; const atom_index = object.atoms.items[sym.st_shndx]; return elf_file.atom(atom_index).?; } - pub fn relocs(fde: Fde, elf_file: *Elf) error{Overflow}![]align(1) const elf.Elf64_Rela { + pub fn relocs(fde: Fde, elf_file: *Elf) []align(1) const elf.Elf64_Rela { const object = elf_file.file(fde.file_index).?.object; - return (try object.getRelocs(fde.rel_section_index))[fde.rel_index..][0..fde.rel_num]; + return object.getRelocs(fde.rel_section_index)[fde.rel_index..][0..fde.rel_num]; } pub fn format( @@ -88,10 +89,7 @@ pub const Fde = struct { const fde = ctx.fde; const elf_file = ctx.elf_file; const base_addr = fde.address(elf_file); - const atom_name = if (fde.atom(elf_file)) |atom_ptr| - atom_ptr.name(elf_file) - else |_| - ""; + const atom_name = fde.atom(elf_file).name(elf_file); try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{ base_addr + fde.out_offset, fde.calcSize(), @@ -123,9 +121,9 @@ pub const Cie = struct { return base + cie.out_offset; } - pub fn data(cie: Cie, elf_file: *Elf) error{Overflow}![]const u8 { + pub fn data(cie: Cie, elf_file: *Elf) []const u8 { const object = elf_file.file(cie.file_index).?.object; - const contents = try object.shdrContents(cie.input_section_index); + const contents = object.shdrContents(cie.input_section_index); return contents[cie.offset..][0..cie.calcSize()]; } @@ -133,16 +131,16 @@ pub const Cie = struct { return cie.size + 4; } - pub fn relocs(cie: Cie, elf_file: *Elf) error{Overflow}![]align(1) const elf.Elf64_Rela { + pub fn relocs(cie: Cie, elf_file: *Elf) []align(1) const elf.Elf64_Rela { const object = elf_file.file(cie.file_index).?.object; - return (try object.getRelocs(cie.rel_section_index))[cie.rel_index..][0..cie.rel_num]; + return object.getRelocs(cie.rel_section_index)[cie.rel_index..][0..cie.rel_num]; } - pub fn eql(cie: Cie, other: Cie, elf_file: *Elf) error{Overflow}!bool { - if (!std.mem.eql(u8, try cie.data(elf_file), try other.data(elf_file))) return false; + pub fn eql(cie: Cie, other: Cie, elf_file: *Elf) bool { + if (!std.mem.eql(u8, cie.data(elf_file), other.data(elf_file))) return false; - const cie_relocs = try cie.relocs(elf_file); - const other_relocs = try other.relocs(elf_file); + const cie_relocs = cie.relocs(elf_file); + const other_relocs = other.relocs(elf_file); if (cie_relocs.len != other_relocs.len) return false; for (cie_relocs, other_relocs) |cie_rel, other_rel| { @@ -152,8 +150,8 @@ pub const Cie = struct { const cie_object = elf_file.file(cie.file_index).?.object; const other_object = elf_file.file(other.file_index).?.object; - const cie_sym = cie_object.symbol(cie_rel.r_sym(), elf_file); - const other_sym = other_object.symbol(other_rel.r_sym(), elf_file); + const cie_sym = cie_object.symbols.items[cie_rel.r_sym()]; + const other_sym = other_object.symbols.items[other_rel.r_sym()]; if (!std.mem.eql(u8, std.mem.asBytes(&cie_sym), std.mem.asBytes(&other_sym))) return false; } return true; @@ -319,10 +317,10 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { for (object.cies.items) |cie| { if (!cie.alive) continue; - const contents = try gpa.dupe(u8, try cie.data(elf_file)); + const contents = try gpa.dupe(u8, cie.data(elf_file)); defer gpa.free(contents); - for (try cie.relocs(elf_file)) |rel| { + for (cie.relocs(elf_file)) |rel| { const sym = object.symbol(rel.r_sym(), elf_file); try resolveReloc(cie, sym, rel, elf_file, contents); } @@ -337,7 +335,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { for (object.fdes.items) |fde| { if (!fde.alive) continue; - const contents = try gpa.dupe(u8, try fde.data(elf_file)); + const contents = try gpa.dupe(u8, fde.data(elf_file)); defer gpa.free(contents); std.mem.writeIntLittle( @@ -346,7 +344,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { @as(i32, @truncate(@as(i64, @intCast(fde.out_offset + 4)) - @as(i64, @intCast(fde.cie(elf_file).out_offset)))), ); - for (try fde.relocs(elf_file)) |rel| { + for (fde.relocs(elf_file)) |rel| { const sym = object.symbol(rel.r_sym(), elf_file); try resolveReloc(fde, sym, rel, elf_file, contents); } @@ -395,7 +393,7 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void { for (object.fdes.items) |fde| { if (!fde.alive) continue; - const relocs = try fde.relocs(elf_file); + const relocs = fde.relocs(elf_file); assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips... const rel = relocs[0]; const sym = object.symbol(rel.r_sym(), elf_file); -- cgit v1.2.3 From 85d451f96cd8f2854fa6a092625b8a2f3c474a73 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 13 Oct 2023 21:32:54 +0200 Subject: elf: re-enable self-hosted backends --- src/link/Elf.zig | 68 ++++++++++++++++++++++++++++++++++++++++++---- src/link/Elf/Atom.zig | 34 ++++++++++++++--------- src/link/Elf/Symbol.zig | 2 +- src/link/Elf/ZigModule.zig | 27 +++++++++++++++++- 4 files changed, 110 insertions(+), 21 deletions(-) (limited to 'src/link/Elf/ZigModule.zig') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 1feb9dd5a9..c3628661d6 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -762,6 +762,7 @@ pub fn initMetadata(self: *Elf) !void { .name = ".zig.got", .phdr_index = self.phdr_zig_got_index.?, .alignment = ptr_size, + .flags = elf.SHF_ALLOC | elf.SHF_WRITE, }); } @@ -785,7 +786,7 @@ pub fn initMetadata(self: *Elf) !void { if (self.zig_bss_section_index == null) { self.zig_bss_section_index = try self.allocateAllocSection(.{ - .name = ".bss.zig", + .name = ".zig.bss", .phdr_index = self.phdr_zig_load_zerofill_index.?, .alignment = ptr_size, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, @@ -1558,7 +1559,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node for (zig_module.atoms.items) |atom_index| { const atom_ptr = self.atom(atom_index) orelse continue; if (!atom_ptr.flags.alive) continue; - const shdr = &self.shdrs.items[atom_ptr.outputShndx().?]; + const out_shndx = atom_ptr.outputShndx() orelse continue; + const shdr = &self.shdrs.items[out_shndx]; if (shdr.sh_type == elf.SHT_NOBITS) continue; const code = try zig_module.codeAlloc(self, atom_index); defer gpa.free(code); @@ -3744,6 +3746,9 @@ fn initSections(self: *Elf) !void { const needs_rela_dyn = blk: { if (self.got.flags.needs_rela or self.got.flags.needs_tlsld or self.copy_rel.symbols.items.len > 0) break :blk true; + if (self.zig_module_index) |index| { + if (self.file(index).?.zig_module.num_dynrelocs > 0) break :blk true; + } for (self.objects.items) |index| { if (self.file(index).?.object.num_dynrelocs > 0) break :blk true; } @@ -4243,6 +4248,33 @@ fn sortSections(self: *Elf) !void { shdr.sh_link = self.dynsymtab_section_index.?; shdr.sh_info = self.plt_section_index.?; } + + if (self.zig_module_index) |index| { + const zig_module = self.file(index).?.zig_module; + for (zig_module.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]; + } + + for (zig_module.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]; + } + + for (zig_module.globals()) |global_index| { + const global = self.symbol(global_index); + const atom_ptr = global.atom(self) orelse continue; + if (!atom_ptr.flags.alive) continue; + if (global.file(self).?.index() != index) continue; + const out_shndx = global.outputShndx() orelse continue; + global.output_section_index = backlinks[out_shndx]; + } + } } fn updateSectionSizes(self: *Elf) !void { @@ -4286,6 +4318,9 @@ fn updateSectionSizes(self: *Elf) !void { if (self.rela_dyn_section_index) |shndx| { var num = self.got.numRela(self) + self.copy_rel.numRela(); + if (self.zig_module_index) |index| { + num += self.file(index).?.zig_module.num_dynrelocs; + } for (self.objects.items) |index| { num += self.file(index).?.object.num_dynrelocs; } @@ -4373,9 +4408,10 @@ fn shdrToPhdrFlags(sh_flags: u64) u32 { fn calcNumberOfSegments(self: *Elf) usize { var count: usize = 0; var flags: u64 = 0; - for (self.shdrs.items) |shdr| { + for (self.shdrs.items, 0..) |shdr, shndx| { if (shdr.sh_type == elf.SHT_NULL) continue; if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; + if (self.isZigSection(@intCast(shndx))) continue; if (flags != shdrToPhdrFlags(shdr.sh_flags)) count += 1; flags = shdrToPhdrFlags(shdr.sh_flags); } @@ -4474,7 +4510,10 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void { } } } - covers[nphdrs - 1].len = shndx - covers[nphdrs - 1].start; + + if (nphdrs > 0) { + covers[nphdrs - 1].len = shndx - covers[nphdrs - 1].start; + } // Now we can proceed with allocating the sections in virtual memory. // As the base address we take the end address of the PHDR table. @@ -4647,6 +4686,7 @@ fn writeAtoms(self: *Elf) !void { undefs.deinit(); } + // TODO iterate over `output_sections` directly for (self.shdrs.items, 0..) |shdr, shndx| { if (shdr.sh_type == elf.SHT_NULL) continue; if (shdr.sh_type == elf.SHT_NOBITS) continue; @@ -5830,7 +5870,10 @@ fn fmtDumpState( if (self.zig_module_index) |index| { const zig_module = self.file(index).?.zig_module; try writer.print("zig_module({d}) : {s}\n", .{ index, zig_module.path }); - try writer.print("{}\n", .{zig_module.fmtSymtab(self)}); + try writer.print("{}{}\n", .{ + zig_module.fmtAtoms(self), + zig_module.fmtSymtab(self), + }); } for (self.objects.items) |index| { @@ -5872,7 +5915,7 @@ fn fmtDumpState( self.fmtShdr(shdr), }); } - try writer.writeAll("Output phdrs\n"); + try writer.writeAll("\nOutput phdrs\n"); for (self.phdrs.items, 0..) |phdr, phndx| { try writer.print("phdr{d} : {}\n", .{ phndx, self.fmtPhdr(phdr) }); } @@ -5983,6 +6026,19 @@ pub const null_sym = elf.Elf64_Sym{ .st_size = 0, }; +pub const null_shdr = elf.Elf64_Shdr{ + .sh_name = 0, + .sh_type = 0, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = 0, + .sh_size = 0, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = 0, + .sh_entsize = 0, +}; + const SystemLib = struct { needed: bool = false, path: []const u8, diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index ae55260ea8..fae96dec5e 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -50,8 +50,11 @@ pub fn file(self: Atom, elf_file: *Elf) ?File { } pub fn inputShdr(self: Atom, elf_file: *Elf) Object.ElfShdr { - const object = self.file(elf_file).?.object; - return object.shdrs.items[self.input_section_index]; + return switch (self.file(elf_file).?) { + .object => |x| x.shdrs.items[self.input_section_index], + .zig_module => |x| x.inputShdr(self.atom_index, elf_file), + else => unreachable, + }; } pub fn outputShndx(self: Atom) ?u16 { @@ -199,7 +202,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { _ = free_list.swapRemove(i); } - self.flags.allocated = true; + self.flags.alive = true; } pub fn shrink(self: *Atom, elf_file: *Elf) void { @@ -471,7 +474,11 @@ fn scanReloc( elf_file: *Elf, ) error{OutOfMemory}!void { const is_writeable = self.inputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0; - const object = self.file(elf_file).?.object; + const num_dynrelocs = switch (self.file(elf_file).?) { + .linker_defined => unreachable, + .shared_object => unreachable, + inline else => |x| &x.num_dynrelocs, + }; switch (action) { .none => {}, @@ -500,7 +507,7 @@ fn scanReloc( try self.reportTextRelocError(symbol, rel, elf_file); } } - object.num_dynrelocs += 1; + num_dynrelocs.* += 1; } else { symbol.flags.needs_copy_rel = true; } @@ -517,7 +524,7 @@ fn scanReloc( .dyn_cplt => { if (is_writeable) { - object.num_dynrelocs += 1; + num_dynrelocs.* += 1; } else { symbol.flags.needs_plt = true; symbol.flags.is_canonical = true; @@ -532,7 +539,7 @@ fn scanReloc( try self.reportTextRelocError(symbol, rel, elf_file); } } - object.num_dynrelocs += 1; + num_dynrelocs.* += 1; if (action == .ifunc) elf_file.num_ifunc_dynrelocs += 1; }, @@ -898,9 +905,13 @@ fn resolveDynAbsReloc( const A = rel.r_addend; const S = @as(i64, @intCast(target.address(.{}, elf_file))); const is_writeable = self.inputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0; - const object = self.file(elf_file).?.object; - try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, object.num_dynrelocs); + const num_dynrelocs = switch (self.file(elf_file).?) { + .linker_defined => unreachable, + .shared_object => unreachable, + inline else => |x| x.num_dynrelocs, + }; + try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, num_dynrelocs); switch (action) { .@"error", @@ -1165,7 +1176,7 @@ fn format2( _ = unused_fmt_string; const atom = ctx.atom; const elf_file = ctx.elf_file; - try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x})", .{ + try writer.print("atom({d}) : {s} : @{x} : shdr({d}) : align({x}) : size({x})", .{ atom.atom_index, atom.name(elf_file), atom.value, atom.output_section_index, atom.alignment, atom.size, }); @@ -1191,9 +1202,6 @@ pub const Flags = packed struct { /// Specifies if the atom has been visited during garbage collection. visited: bool = false, - - /// Specifies whether this atom has been allocated in the output section. - allocated: bool = false, }; const x86_64 = struct { diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 8e01caf8ac..00e6e15a8a 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -314,7 +314,7 @@ fn format2( try writer.writeAll(" : absolute"); } } else if (symbol.outputShndx()) |shndx| { - try writer.print(" : sect({d})", .{shndx}); + try writer.print(" : shdr({d})", .{shndx}); } if (symbol.atom(ctx.elf_file)) |atom_ptr| { try writer.print(" : atom({d})", .{atom_ptr.atom_index}); diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index c5a0adf626..4532d7b448 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -16,6 +16,8 @@ globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{}, atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{}, +num_dynrelocs: u32 = 0, + output_symtab_size: Elf.SymtabSize = .{}, pub fn deinit(self: *ZigModule, allocator: Allocator) void { @@ -79,6 +81,22 @@ pub fn addAtom(self: *ZigModule, elf_file: *Elf) !Symbol.Index { return symbol_index; } +/// TODO actually create fake input shdrs and return that instead. +pub fn inputShdr(self: ZigModule, atom_index: Atom.Index, elf_file: *Elf) Object.ElfShdr { + _ = self; + const shdr = shdr: { + const atom = elf_file.atom(atom_index) orelse break :shdr Elf.null_shdr; + const shndx = atom.outputShndx() orelse break :shdr Elf.null_shdr; + var shdr = elf_file.shdrs.items[shndx]; + shdr.sh_addr = 0; + shdr.sh_offset = 0; + shdr.sh_size = atom.size; + shdr.sh_addralign = atom.alignment.toByteUnits(1); + break :shdr shdr; + }; + return Object.ElfShdr.fromElf64Shdr(shdr) catch unreachable; +} + pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void { for (self.globals(), 0..) |index, i| { const esym_index = @as(Symbol.Index, @intCast(i)) | 0x10000000; @@ -145,6 +163,8 @@ pub fn scanRelocs(self: *ZigModule, elf_file: *Elf, undefs: anytype) !void { for (self.atoms.items) |atom_index| { const atom = elf_file.atom(atom_index) orelse continue; if (!atom.flags.alive) continue; + const shdr = atom.inputShdr(elf_file); + if (shdr.sh_type == elf.SHT_NOBITS) continue; if (atom.scanRelocsRequiresCode(elf_file)) { // TODO ideally we don't have to fetch the code here. // Perhaps it would make sense to save the code until flushModule where we @@ -273,7 +293,10 @@ pub fn codeAlloc(self: ZigModule, elf_file: *Elf, atom_index: Atom.Index) ![]u8 const code = try gpa.alloc(u8, size); errdefer gpa.free(code); const amt = try elf_file.base.file.?.preadAll(code, file_offset); - if (amt != code.len) return error.InputOutput; + if (amt != code.len) { + log.err("fetching code for {s} failed", .{atom.name(elf_file)}); + return error.InputOutput; + } return code; } @@ -334,11 +357,13 @@ fn formatAtoms( const assert = std.debug.assert; const std = @import("std"); const elf = std.elf; +const log = std.log.scoped(.link); const Allocator = std.mem.Allocator; const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; const Module = @import("../../Module.zig"); +const Object = @import("Object.zig"); const Symbol = @import("Symbol.zig"); const ZigModule = @This(); -- cgit v1.2.3