From 85d451f96cd8f2854fa6a092625b8a2f3c474a73 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 13 Oct 2023 21:32:54 +0200 Subject: [PATCH] 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(-) 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();