From 6f0476e41d2f9b040a9883b288171c6a50c29ed5 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 29 Oct 2025 18:05:49 -0400 Subject: Elf2: start implementing input object loading --- src/Compilation.zig | 32 ++++---------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) (limited to 'src/Compilation.zig') diff --git a/src/Compilation.zig b/src/Compilation.zig index 3670bc51b5..99d4d80b92 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -258,8 +258,6 @@ test_filters: []const []const u8, link_task_wait_group: WaitGroup = .{}, link_prog_node: std.Progress.Node = .none, -link_const_prog_node: std.Progress.Node = .none, -link_synth_prog_node: std.Progress.Node = .none, llvm_opt_bisect_limit: c_int, @@ -3066,35 +3064,13 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE // we also want it around during `flush`. if (comp.bin_file) |lf| { comp.link_prog_node = main_progress_node.start("Linking", 0); - if (lf.cast(.elf2)) |elf| { - comp.link_prog_node.increaseEstimatedTotalItems(3); - comp.link_const_prog_node = comp.link_prog_node.start("Constants", 0); - comp.link_synth_prog_node = comp.link_prog_node.start("Synthetics", 0); - elf.mf.update_prog_node = comp.link_prog_node.start("Relocations", elf.mf.updates.items.len); - } else if (lf.cast(.coff2)) |coff| { - comp.link_prog_node.increaseEstimatedTotalItems(3); - comp.link_const_prog_node = comp.link_prog_node.start("Constants", 0); - comp.link_synth_prog_node = comp.link_prog_node.start("Synthetics", 0); - coff.mf.update_prog_node = comp.link_prog_node.start("Relocations", coff.mf.updates.items.len); - } + lf.startProgress(comp.link_prog_node); } - defer { + defer if (comp.bin_file) |lf| { + lf.endProgress(); comp.link_prog_node.end(); comp.link_prog_node = .none; - comp.link_const_prog_node.end(); - comp.link_const_prog_node = .none; - comp.link_synth_prog_node.end(); - comp.link_synth_prog_node = .none; - if (comp.bin_file) |lf| { - if (lf.cast(.elf2)) |elf| { - elf.mf.update_prog_node.end(); - elf.mf.update_prog_node = .none; - } else if (lf.cast(.coff2)) |coff| { - coff.mf.update_prog_node.end(); - coff.mf.update_prog_node = .none; - } - } - } + }; try comp.performAllTheWork(main_progress_node); -- cgit v1.2.3 From 7542c3260f93e92fd8da7e5329bc80fd601964e9 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 29 Oct 2025 18:01:10 -0400 Subject: Elf2: load relocations from input objects --- lib/std/elf.zig | 46 +++++ src/Compilation.zig | 2 +- src/codegen/x86_64/Emit.zig | 12 +- src/link/Elf2.zig | 422 ++++++++++++++++++++++++-------------------- src/link/MappedFile.zig | 6 +- 5 files changed, 289 insertions(+), 199 deletions(-) (limited to 'src/Compilation.zig') diff --git a/lib/std/elf.zig b/lib/std/elf.zig index c5f8a7ea80..2f00cef22c 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -943,11 +943,30 @@ pub const Elf32 = struct { unused: u5 = 0, }; }; + pub const Rel = extern struct { + offset: Elf32.Addr, + info: Info, + addend: u0 = 0, + + pub const Info = packed struct(u32) { + type: u8, + sym: u24, + }; + }; + pub const Rela = extern struct { + offset: Elf32.Addr, + info: Info, + addend: i32, + + pub const Info = Elf32.Rel.Info; + }; comptime { assert(@sizeOf(Elf32.Ehdr) == 52); assert(@sizeOf(Elf32.Phdr) == 32); assert(@sizeOf(Elf32.Shdr) == 40); assert(@sizeOf(Elf32.Sym) == 16); + assert(@sizeOf(Elf32.Rel) == 8); + assert(@sizeOf(Elf32.Rela) == 12); } }; pub const Elf64 = struct { @@ -1008,11 +1027,30 @@ pub const Elf64 = struct { pub const Info = Elf32.Sym.Info; pub const Other = Elf32.Sym.Other; }; + pub const Rel = extern struct { + offset: Elf64.Addr, + info: Info, + addend: u0 = 0, + + pub const Info = packed struct(u64) { + type: u32, + sym: u32, + }; + }; + pub const Rela = extern struct { + offset: Elf64.Addr, + info: Info, + addend: i64, + + pub const Info = Elf64.Rel.Info; + }; comptime { assert(@sizeOf(Elf64.Ehdr) == 64); assert(@sizeOf(Elf64.Phdr) == 56); assert(@sizeOf(Elf64.Shdr) == 64); assert(@sizeOf(Elf64.Sym) == 24); + assert(@sizeOf(Elf64.Rel) == 16); + assert(@sizeOf(Elf64.Rela) == 24); } }; pub const ElfN = switch (@sizeOf(usize)) { @@ -1428,6 +1466,14 @@ pub const CLASS = enum(u8) { _, pub const NUM = @typeInfo(CLASS).@"enum".fields.len; + + pub fn ElfN(comptime class: CLASS) type { + return switch (class) { + .NONE, _ => comptime unreachable, + .@"32" => Elf32, + .@"64" => Elf64, + }; + } }; /// Deprecated, use `@intFromEnum(std.elf.DATA.NONE)` diff --git a/src/Compilation.zig b/src/Compilation.zig index 99d4d80b92..488f7db675 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1989,7 +1989,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, break :s if (is_exe_or_dyn_lib and build_options.have_llvm) .dyn_lib else .zcu; }, } - if (options.config.use_new_linker) break :s .zcu; + if (options.config.use_new_linker) break :s .obj; } if (need_llvm and !build_options.have_llvm) break :s .none; // impossible to build without llvm if (is_exe_or_dyn_lib) break :s .lib; diff --git a/src/codegen/x86_64/Emit.zig b/src/codegen/x86_64/Emit.zig index 3f4ef1b999..0aac146bc0 100644 --- a/src/codegen/x86_64/Emit.zig +++ b/src/codegen/x86_64/Emit.zig @@ -217,9 +217,7 @@ pub fn emitMir(emit: *Emit) Error!void { }, emit.lower.target), reloc_info), .mov => try emit.encodeInst(try .new(.none, .mov, &.{ lowered_inst.ops[0], - .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{ - .base = .{ .reg = .ds }, - }) }, + .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{}) }, }, emit.lower.target), reloc_info), else => unreachable, } else if (reloc.target.is_extern) switch (lowered_inst.encoding.mnemonic) { @@ -720,7 +718,7 @@ pub fn emitMir(emit: *Emit) Error!void { for (emit.table_relocs.items) |table_reloc| try atom.addReloc(gpa, .{ .r_offset = table_reloc.source_offset, - .r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"32"), + .r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"32S"), .r_addend = @as(i64, table_offset) + table_reloc.target_offset, }, zo); for (emit.lower.mir.table) |entry| { @@ -738,7 +736,7 @@ pub fn emitMir(emit: *Emit) Error!void { table_reloc.source_offset, @enumFromInt(emit.atom_index), @as(i64, table_offset) + table_reloc.target_offset, - .{ .X86_64 = .@"32" }, + .{ .X86_64 = .@"32S" }, ); for (emit.lower.mir.table) |entry| { try elf.addReloc( @@ -824,7 +822,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI const zo = elf_file.zigObjectPtr().?; const atom = zo.symbol(emit.atom_index).atom(elf_file).?; const r_type: std.elf.R_X86_64 = if (!emit.pic) - .@"32" + .@"32S" else if (reloc.target.is_extern and !reloc.target.force_pcrel_direct) .GOTPCREL else @@ -855,7 +853,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI end_offset - 4, @enumFromInt(reloc.target.index), reloc.off, - .{ .X86_64 = .@"32" }, + .{ .X86_64 = .@"32S" }, ) else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc( @enumFromInt(emit.atom_index), end_offset - 4, diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig index b7a27f1dcd..162fe99796 100644 --- a/src/link/Elf2.zig +++ b/src/link/Elf2.zig @@ -12,10 +12,8 @@ inputs: std.ArrayList(struct { }), input_sections: std.ArrayList(struct { ii: Node.InputIndex, - ni: MappedFile.Node.Index, - addr: u64, - offset: u64, - size: u64, + si: Symbol.Index, + file_location: MappedFile.Node.FileLocation, }), input_section_pending_index: u32, globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index), @@ -59,7 +57,7 @@ pub const Node = union(enum) { return elf.inputs.items[@intFromEnum(ii)].si; } - pub fn nextSymbol(ii: InputIndex, elf: *const Elf) Symbol.Index { + pub fn endSymbol(ii: InputIndex, elf: *const Elf) Symbol.Index { const next_ii = @intFromEnum(ii) + 1; return if (next_ii < elf.inputs.items.len) @as(InputIndex, @enumFromInt(next_ii)).symbol(elf) @@ -75,17 +73,12 @@ pub const Node = union(enum) { return elf.input_sections.items[@intFromEnum(isi)].ii; } - pub fn node(isi: InputSectionIndex, elf: *const Elf) MappedFile.Node.Index { - return elf.input_sections.items[@intFromEnum(isi)].ni; + pub fn symbol(isi: InputSectionIndex, elf: *const Elf) Symbol.Index { + return elf.input_sections.items[@intFromEnum(isi)].si; } - pub fn addr(isi: InputSectionIndex, elf: *Elf) *u64 { - return &elf.input_sections.items[@intFromEnum(isi)].addr; - } - - pub fn fileLocation(isi: InputSectionIndex, elf: *const Elf) struct { u64, u64 } { - const input_section = &elf.input_sections.items[@intFromEnum(isi)]; - return .{ input_section.offset, input_section.size }; + pub fn fileLocation(isi: InputSectionIndex, elf: *const Elf) MappedFile.Node.FileLocation { + return elf.input_sections.items[@intFromEnum(isi)].file_location; } }; @@ -302,9 +295,12 @@ pub const Symbol = struct { } pub fn applyLocationRelocs(si: Symbol.Index, elf: *Elf) void { - for (elf.relocs.items[@intFromEnum(si.get(elf).loc_relocs)..]) |*reloc| { - if (reloc.loc != si) break; - reloc.apply(elf); + switch (si.get(elf).loc_relocs) { + .none => {}, + else => |loc_relocs| for (elf.relocs.items[@intFromEnum(loc_relocs)..]) |*reloc| { + if (reloc.loc != si) break; + reloc.apply(elf); + }, } } @@ -390,7 +386,7 @@ pub const Reloc = extern struct { target_value, target_endian, ), - .PC32 => std.mem.writeInt( + .PC32, .PLT32 => std.mem.writeInt( i32, loc_slice[0..4], @intCast(@as(i64, @bitCast(target_value -% loc_value))), @@ -402,6 +398,12 @@ pub const Reloc = extern struct { @intCast(target_value), target_endian, ), + .@"32S" => std.mem.writeInt( + i32, + loc_slice[0..4], + @intCast(@as(i64, @bitCast(target_value))), + target_endian, + ), .TPOFF32 => { const phdr = @field(elf.phdrSlice(), @tagName(class)); const ph = &phdr[elf.getNode(elf.known.tls).segment]; @@ -632,12 +634,7 @@ fn initHeaders( switch (class) { .NONE, _ => unreachable, inline else => |ct_class| { - const ElfN = switch (ct_class) { - .NONE, _ => comptime unreachable, - .@"32" => std.elf.Elf32, - .@"64" => std.elf.Elf64, - }; - + const ElfN = ct_class.ElfN(); assert(Node.Known.ehdr == try elf.mf.addOnlyChildNode(gpa, Node.Known.rodata, .{ .size = @sizeOf(ElfN.Ehdr), .alignment = addr_align, @@ -717,11 +714,7 @@ fn initHeaders( switch (class) { .NONE, _ => unreachable, inline else => |ct_class| { - const ElfN = switch (ct_class) { - .NONE, _ => comptime unreachable, - .@"32" => std.elf.Elf32, - .@"64" => std.elf.Elf64, - }; + const ElfN = ct_class.ElfN(); const target_endian = elf.targetEndian(); const phdr: []ElfN.Phdr = @ptrCast(@alignCast(Node.Known.phdr.slice(&elf.mf))); @@ -1293,23 +1286,32 @@ pub fn loadInput(elf: *Elf, input: link.Input) !void { switch (elf.identClass()) { .NONE, _ => unreachable, inline else => |class| { - const ehdr = try r.peekStruct( - @typeInfo(@FieldType(EhdrPtr, @tagName(class))).pointer.child, - target_endian, - ); + const ElfN = class.ElfN(); + const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian); if (ehdr.type != .REL) return diags.failParse(object.path, "unsupported object type", .{}); if (ehdr.machine != elf.ehdrField(.machine)) return diags.failParse(object.path, "wrong machine", .{}); if (ehdr.shoff == 0 or ehdr.shnum <= 1) return; - if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum) - return diags.failParse(object.path, "missing section names", .{}); - const Shdr = @typeInfo(@FieldType(ShdrSlice, @tagName(class))).pointer.child; - if (ehdr.shentsize < @sizeOf(Shdr)) + if (ehdr.shentsize < @sizeOf(ElfN.Shdr)) return diags.failParse(object.path, "unsupported shentsize", .{}); + const sections = try gpa.alloc(struct { + shdr: ElfN.Shdr, + si: Symbol.Index, + }, ehdr.shnum); + defer gpa.free(sections); + try fr.seekTo(ehdr.shoff); + for (sections) |*section| { + section.* = .{ + .shdr = try r.peekStruct(ElfN.Shdr, target_endian), + .si = .null, + }; + try fr.seekBy(ehdr.shentsize); + } const shstrtab = shstrtab: { - try fr.seekTo(ehdr.shoff + ehdr.shentsize * ehdr.shstrndx); - const shdr = try r.peekStruct(Shdr, target_endian); + if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum) + return diags.failParse(object.path, "missing section names", .{}); + const shdr = §ions[ehdr.shstrndx].shdr; if (shdr.type != std.elf.SHT_STRTAB) return diags.failParse(object.path, "invalid shstrtab type", .{}); const shstrtab = try gpa.alloc(u8, @intCast(shdr.size)); @@ -1319,141 +1321,186 @@ pub fn loadInput(elf: *Elf, input: link.Input) !void { break :shstrtab shstrtab; }; defer gpa.free(shstrtab); - const Sym = @typeInfo(@FieldType(SymPtr, @tagName(class))).pointer.child; - try fr.seekTo(ehdr.shoff + ehdr.shentsize); - var symtab_shdr: Shdr = .{ - .name = 0, - .type = 0, - .flags = .{ .shf = .{} }, - .addr = 0, - .offset = 0, - .size = 0, - .link = 0, - .info = 0, - .addralign = 0, - .entsize = 0, - }; - const sections = try gpa.alloc(struct { - shndx: u32, - ni: MappedFile.Node.Index, - }, ehdr.shnum); - defer gpa.free(sections); - @memset(sections, .{ .shndx = std.elf.SHN_UNDEF, .ni = .none }); - for (sections[1..ehdr.shnum]) |*section| { - const shdr = try r.peekStruct(Shdr, target_endian); - try r.discardAll(ehdr.shentsize); - switch (shdr.type) { - else => continue, - std.elf.SHT_PROGBITS => {}, - std.elf.SHT_SYMTAB => { - if (symtab_shdr.entsize > 0) - return diags.failParse(object.path, "too many symtabs", .{}); - if (shdr.entsize < @sizeOf(Sym)) - return diags.failParse(object.path, "unsupported entsize", .{}); - symtab_shdr = shdr; - continue; - }, - std.elf.SHT_NOBITS => {}, - } - if (shdr.name >= shstrtab.len) continue; - const name = std.mem.sliceTo(shstrtab[shdr.name..], 0); - const si = namedSection(name) orelse continue; - section.shndx = elf.targetLoad(&@field(elf.symPtr(si), @tagName(class)).shndx); - try elf.nodes.ensureUnusedCapacity(gpa, 1); - try elf.input_sections.ensureUnusedCapacity(gpa, 1); - section.ni = try elf.mf.addLastChildNode(gpa, si.node(elf), .{ - .size = shdr.size, - .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert( - usize, - @intCast(@max(shdr.addralign, 1)), - )), - .moved = true, - }); - elf.nodes.appendAssumeCapacity(.{ - .input_section = @enumFromInt(elf.input_sections.items.len), - }); - elf.input_sections.addOneAssumeCapacity().* = .{ - .ii = ii, - .ni = section.ni, - .addr = shdr.addr, - .offset = shdr.offset, - .size = shdr.size, - }; - elf.synth_prog_node.increaseEstimatedTotalItems(1); - } - if (symtab_shdr.info <= 1) return; - const strtab = strtab: { - try fr.seekTo(ehdr.shoff + ehdr.shentsize * symtab_shdr.link); - const shdr = try r.peekStruct(Shdr, target_endian); - if (shdr.type != std.elf.SHT_STRTAB) - return diags.failParse(object.path, "invalid strtab type", .{}); - const strtab = try gpa.alloc(u8, @intCast(shdr.size)); - errdefer gpa.free(strtab); - try fr.seekTo(shdr.offset); - try r.readSliceAll(strtab); - break :strtab strtab; + try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1); + try elf.symtab.ensureUnusedCapacity(gpa, ehdr.shnum - 1); + try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1); + for (sections[1..]) |*section| switch (section.shdr.type) { + else => {}, + std.elf.SHT_PROGBITS, std.elf.SHT_NOBITS => { + if (section.shdr.name >= shstrtab.len) continue; + const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); + const parent_si = namedSection(name) orelse continue; + const ni = try elf.mf.addLastChildNode(gpa, parent_si.node(elf), .{ + .size = section.shdr.size, + .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert( + usize, + @intCast(@max(section.shdr.addralign, 1)), + )), + .moved = true, + }); + elf.nodes.appendAssumeCapacity(.{ + .input_section = @enumFromInt(elf.input_sections.items.len), + }); + section.si = try elf.initSymbolAssumeCapacity(.{ + .type = .SECTION, + .shndx = elf.targetLoad(&@field(elf.symPtr(parent_si), @tagName(class)).shndx), + }); + section.si.get(elf).ni = ni; + elf.input_sections.addOneAssumeCapacity().* = .{ + .ii = ii, + .si = section.si, + .file_location = .{ + .offset = section.shdr.offset, + .size = section.shdr.size, + }, + }; + elf.synth_prog_node.increaseEstimatedTotalItems(1); + }, }; - defer gpa.free(strtab); - try elf.symtab.ensureUnusedCapacity(gpa, symtab_shdr.info); - try elf.globals.ensureUnusedCapacity(gpa, symtab_shdr.info); - try fr.seekTo(symtab_shdr.offset + symtab_shdr.entsize); - for (1..try std.math.divExact( - usize, - @intCast(symtab_shdr.size), - @intCast(symtab_shdr.entsize), - )) |_| { - const input_sym = try r.peekStruct(Sym, target_endian); - try r.discardAll64(symtab_shdr.entsize); - switch (input_sym.info.type) { - else => continue, - .OBJECT, .FUNC => {}, - } - if (input_sym.name >= strtab.len or input_sym.shndx >= sections.len) continue; - const name = std.mem.sliceTo(strtab[input_sym.name..], 0); - const si = try elf.initSymbolAssumeCapacity(.{ - .name = name, - .value = input_sym.value, - .size = input_sym.size, - .type = input_sym.info.type, - .bind = input_sym.info.bind, - .visibility = input_sym.other.visibility, - .shndx = @intCast(sections[input_sym.shndx].shndx), - }); - { - const sym = si.get(elf); - sym.ni = sections[input_sym.shndx].ni; - assert(sym.loc_relocs == .none); - sym.loc_relocs = @enumFromInt(elf.relocs.items.len); - } - switch (input_sym.info.bind) { - else => {}, - .GLOBAL => { - const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad( - &@field(elf.symPtr(si), @tagName(class)).name, - )); - if (gop.found_existing) switch (elf.targetLoad( - switch (elf.symPtr(gop.value_ptr.*)) { - inline else => |sym| &sym.info, + var symmap: std.ArrayList(Symbol.Index) = .empty; + defer symmap.deinit(gpa); + for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) { + else => {}, + std.elf.SHT_SYMTAB => { + if (symtab.shdr.entsize < @sizeOf(ElfN.Sym)) + return diags.failParse(object.path, "unsupported symtab entsize", .{}); + const strtab = strtab: { + if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum) + return diags.failParse(object.path, "missing symbol names", .{}); + const shdr = §ions[symtab.shdr.link].shdr; + if (shdr.type != std.elf.SHT_STRTAB) + return diags.failParse(object.path, "invalid strtab type", .{}); + const strtab = try gpa.alloc(u8, @intCast(shdr.size)); + errdefer gpa.free(strtab); + try fr.seekTo(shdr.offset); + try r.readSliceAll(strtab); + break :strtab strtab; + }; + defer gpa.free(strtab); + const symnum = try std.math.divExact( + u32, + @intCast(symtab.shdr.size), + @intCast(symtab.shdr.entsize), + ); + symmap.clearRetainingCapacity(); + try symmap.resize(gpa, symnum); + try elf.symtab.ensureUnusedCapacity(gpa, symnum); + try elf.globals.ensureUnusedCapacity(gpa, symnum); + try fr.seekTo(symtab.shdr.offset + symtab.shdr.entsize); + symmap.items[0] = .null; + for (symmap.items[1..]) |*si| { + si.* = .null; + const input_sym = try r.peekStruct(ElfN.Sym, target_endian); + try fr.seekBy(@intCast(symtab.shdr.entsize)); + if (input_sym.name >= strtab.len or + input_sym.shndx == std.elf.SHN_UNDEF or + input_sym.shndx >= ehdr.shnum) continue; + switch (input_sym.info.type) { + else => continue, + .SECTION => { + const section = §ions[input_sym.shndx]; + if (input_sym.value == section.shdr.addr) si.* = section.si; + continue; + }, + .OBJECT, .FUNC => {}, + } + const name = std.mem.sliceTo(strtab[input_sym.name..], 0); + const parent_si = sections[input_sym.shndx].si; + si.* = try elf.initSymbolAssumeCapacity(.{ + .name = name, + .value = input_sym.value, + .size = input_sym.size, + .type = input_sym.info.type, + .bind = input_sym.info.bind, + .visibility = input_sym.other.visibility, + .shndx = elf.targetLoad(switch (elf.symPtr(parent_si)) { + inline else => |parent_sym| &parent_sym.shndx, + }), + }); + si.get(elf).ni = parent_si.get(elf).ni; + switch (input_sym.info.bind) { + else => {}, + .GLOBAL => { + const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad( + &@field(elf.symPtr(si.*), @tagName(class)).name, + )); + if (gop.found_existing) switch (elf.targetLoad( + switch (elf.symPtr(gop.value_ptr.*)) { + inline else => |sym| &sym.info, + }, + ).bind) { + else => unreachable, + .GLOBAL => return diags.failParse( + object.path, + "multiple definitions of '{s}'", + .{name}, + ), + .WEAK => {}, + }; + gop.value_ptr.* = si.*; + }, + .WEAK => { + const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad( + &@field(elf.symPtr(si.*), @tagName(class)).name, + )); + if (!gop.found_existing) gop.value_ptr.* = si.*; }, - ).bind) { - else => unreachable, - .GLOBAL => return diags.failParse( + } + } + for (sections[1..]) |*rels| switch (rels.shdr.type) { + else => {}, + inline std.elf.SHT_REL, std.elf.SHT_RELA => |sht| { + if (rels.shdr.link != symtab_shndx or + rels.shdr.info == std.elf.SHN_UNDEF or + rels.shdr.info >= ehdr.shnum) continue; + const Rel = switch (sht) { + else => comptime unreachable, + std.elf.SHT_REL => ElfN.Rel, + std.elf.SHT_RELA => ElfN.Rela, + }; + if (rels.shdr.entsize < @sizeOf(Rel)) return diags.failParse( object.path, - "multiple definitions of '{s}'", - .{name}, - ), - .WEAK => {}, - }; - gop.value_ptr.* = si; - }, - .WEAK => { - const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad( - &@field(elf.symPtr(si), @tagName(class)).name, - )); - if (!gop.found_existing) gop.value_ptr.* = si; - }, - } - } + "unsupported rel entsize", + .{}, + ); + const loc_sec = §ions[rels.shdr.info]; + if (loc_sec.si == .null) continue; + const relnum = try std.math.divExact( + u32, + @intCast(rels.shdr.size), + @intCast(rels.shdr.entsize), + ); + try elf.relocs.ensureUnusedCapacity(gpa, relnum); + try fr.seekTo(rels.shdr.offset); + for (0..relnum) |_| { + const rel = try r.peekStruct(Rel, target_endian); + try fr.seekBy(@intCast(rels.shdr.entsize)); + if (rel.info.sym >= symnum) continue; + const target_si = symmap.items[rel.info.sym]; + if (target_si == .null) continue; + elf.addReloc( + loc_sec.si, + rel.offset - loc_sec.shdr.addr, + target_si, + rel.addend, + switch (elf.ehdrField(.machine)) { + else => unreachable, + inline .AARCH64, + .PPC64, + .RISCV, + .X86_64, + => |machine| @unionInit( + Reloc.Type, + @tagName(machine), + @enumFromInt(rel.info.type), + ), + }, + ) catch unreachable; + } + }, + }; + }, + }; }, } }, @@ -1512,11 +1559,10 @@ pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target_si: Symbol.In reloc_info.addend, switch (elf.ehdrField(.machine)) { else => unreachable, - .X86_64 => .{ .X86_64 = switch (elf.identClass()) { - .NONE, _ => unreachable, - .@"32" => .@"32", - .@"64" => .@"64", - } }, + .AARCH64 => .{ .AARCH64 = .ABS64 }, + .PPC64 => .{ .PPC64 = .ADDR64 }, + .RISCV => .{ .RISCV = .@"64" }, + .X86_64 => .{ .X86_64 = .@"64" }, }, ); return switch (elf.symPtr(target_si)) { @@ -1912,11 +1958,11 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { if (elf.input_section_pending_index < elf.input_sections.items.len) { const isi: Node.InputSectionIndex = @enumFromInt(elf.input_section_pending_index); elf.input_section_pending_index += 1; - const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.node(elf))); + const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.symbol(elf).node(elf))); defer sub_prog_node.end(); elf.flushInputSection(isi) catch |err| switch (err) { else => |e| return comp.link_diags.fail("linker failed to read input section '{s}' from \"{f}\": {t}", .{ - elf.sectionName(elf.getNode(isi.node(elf).parent(&elf.mf)).section), + elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section), isi.input(elf).path(elf).fmtEscapeString(), e, }), @@ -1954,7 +2000,7 @@ fn idleProgNode( .section => |si| elf.sectionName(si), .input_section => |isi| std.fmt.bufPrint(&name, "{f} {s}", .{ isi.input(elf).path(elf), - elf.sectionName(elf.getNode(isi.node(elf).parent(&elf.mf)).section), + elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section), }) catch &name, .nav => |nmi| { const ip = &elf.base.comp.zcu.?.intern_pool; @@ -2078,8 +2124,8 @@ fn flushLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void { } fn flushInputSection(elf: *Elf, isi: Node.InputSectionIndex) !void { - const offset, const size = isi.fileLocation(elf); - if (size == 0) return; + const file_loc = isi.fileLocation(elf); + if (file_loc.size == 0) return; const comp = elf.base.comp; const gpa = comp.gpa; const io = comp.io; @@ -2087,11 +2133,11 @@ fn flushInputSection(elf: *Elf, isi: Node.InputSectionIndex) !void { const file = try path.root_dir.handle.adaptToNewApi().openFile(io, path.sub_path, .{}); defer file.close(io); var fr = file.reader(io, &.{}); - try fr.seekTo(offset); + try fr.seekTo(file_loc.offset); var nw: MappedFile.Node.Writer = undefined; - isi.node(elf).writer(&elf.mf, gpa, &nw); + isi.symbol(elf).node(elf).writer(&elf.mf, gpa, &nw); defer nw.deinit(); - if (try nw.interface.sendFileAll(&fr, .limited(@intCast(size))) != size) return error.EndOfStream; + if (try nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) != file_loc.size) return error.EndOfStream; } fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { @@ -2132,13 +2178,13 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { }, }, .input_section => |isi| { - const addr = isi.addr(elf); - const old_addr = addr.*; + const old_addr = switch (elf.symPtr(isi.symbol(elf))) { + inline else => |sym| elf.targetLoad(&sym.value), + }; const new_addr = elf.computeNodeVAddr(ni); - addr.* = new_addr; const ii = isi.input(elf); var si = ii.symbol(elf); - const end_si = ii.nextSymbol(elf); + const end_si = ii.endSymbol(elf); while (cond: { si = si.next(); break :cond si != end_si; diff --git a/src/link/MappedFile.zig b/src/link/MappedFile.zig index 7550638197..7d2f153a59 100644 --- a/src/link/MappedFile.zig +++ b/src/link/MappedFile.zig @@ -144,6 +144,8 @@ pub const Node = extern struct { } }; + pub const FileLocation = struct { offset: u64, size: u64 }; + pub const Index = enum(u32) { none, _, @@ -275,7 +277,7 @@ pub const Node = extern struct { ni: Node.Index, mf: *const MappedFile, set_has_content: bool, - ) struct { offset: u64, size: u64 } { + ) FileLocation { var offset, const size = ni.location(mf).resolve(mf); var parent_ni = ni; while (true) { @@ -402,8 +404,6 @@ pub const Node = extern struct { file_reader.interface.toss(n); return n; } - - assert(file_reader.logicalPos() == file_reader.pos); const w: *Writer = @fieldParentPtr("interface", interface); const n: usize = @intCast(w.mf.copyFileRange( file_reader.file, -- cgit v1.2.3 From c4478e078b88c836b53d0e42065b0f68e62c0add Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 14 Oct 2025 22:24:26 -0400 Subject: Elf2: load archives --- lib/std/Io/File.zig | 13 +- src/Compilation.zig | 1 - src/link/Elf2.zig | 667 +++++++++++++++++++++++++++++------------------- src/link/MappedFile.zig | 9 +- 4 files changed, 420 insertions(+), 270 deletions(-) (limited to 'src/Compilation.zig') diff --git a/lib/std/Io/File.zig b/lib/std/Io/File.zig index cc4ce64a22..a83f8e62a2 100644 --- a/lib/std/Io/File.zig +++ b/lib/std/Io/File.zig @@ -434,8 +434,7 @@ pub const Reader = struct { return err; }; } - r.interface.seek = 0; - r.interface.end = 0; + r.interface.tossBuffered(); }, .failure => return r.seek_err.?, } @@ -467,15 +466,11 @@ pub const Reader = struct { } fn setLogicalPos(r: *Reader, offset: u64) void { - const logical_pos = logicalPos(r); + const logical_pos = r.logicalPos(); if (offset < logical_pos or offset >= r.pos) { - r.interface.seek = 0; - r.interface.end = 0; + r.interface.tossBuffered(); r.pos = offset; - } else { - const logical_delta: usize = @intCast(offset - logical_pos); - r.interface.seek += logical_delta; - } + } else r.interface.toss(@intCast(offset - logical_pos)); } /// Number of slices to store on the stack, when trying to send as many byte diff --git a/src/Compilation.zig b/src/Compilation.zig index 488f7db675..64721552bc 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1989,7 +1989,6 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, break :s if (is_exe_or_dyn_lib and build_options.have_llvm) .dyn_lib else .zcu; }, } - if (options.config.use_new_linker) break :s .obj; } if (need_llvm and !build_options.have_llvm) break :s .none; // impossible to build without llvm if (is_exe_or_dyn_lib) break :s .lib; diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig index 162fe99796..687aa5963b 100644 --- a/src/link/Elf2.zig +++ b/src/link/Elf2.zig @@ -8,12 +8,13 @@ shstrtab: StringTable, strtab: StringTable, inputs: std.ArrayList(struct { path: std.Build.Cache.Path, + member: ?[]const u8, si: Symbol.Index, }), input_sections: std.ArrayList(struct { ii: Node.InputIndex, - si: Symbol.Index, file_location: MappedFile.Node.FileLocation, + si: Symbol.Index, }), input_section_pending_index: u32, globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index), @@ -53,6 +54,10 @@ pub const Node = union(enum) { return elf.inputs.items[@intFromEnum(ii)].path; } + pub fn member(ii: InputIndex, elf: *const Elf) ?[]const u8 { + return elf.inputs.items[@intFromEnum(ii)].member; + } + pub fn symbol(ii: InputIndex, elf: *const Elf) Symbol.Index { return elf.inputs.items[@intFromEnum(ii)].si; } @@ -73,13 +78,13 @@ pub const Node = union(enum) { return elf.input_sections.items[@intFromEnum(isi)].ii; } - pub fn symbol(isi: InputSectionIndex, elf: *const Elf) Symbol.Index { - return elf.input_sections.items[@intFromEnum(isi)].si; - } - pub fn fileLocation(isi: InputSectionIndex, elf: *const Elf) MappedFile.Node.FileLocation { return elf.input_sections.items[@intFromEnum(isi)].file_location; } + + pub fn symbol(isi: InputSectionIndex, elf: *const Elf) Symbol.Index { + return elf.input_sections.items[@intFromEnum(isi)].si; + } }; pub const NavMapIndex = enum(u32) { @@ -571,6 +576,7 @@ pub fn deinit(elf: *Elf) void { elf.symtab.deinit(gpa); elf.shstrtab.map.deinit(gpa); elf.strtab.map.deinit(gpa); + for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m); elf.inputs.deinit(gpa); elf.input_sections.deinit(gpa); elf.globals.deinit(gpa); @@ -1255,256 +1261,370 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !Symbol.Index { return lazy_gop.value_ptr.*; } -pub fn loadInput(elf: *Elf, input: link.Input) !void { +pub fn loadInput(elf: *Elf, input: link.Input) (std.fs.File.Reader.SizeError || + std.Io.File.Reader.Error || MappedFile.Error || error{ EndOfStream, LinkFailure })!void { + const io = elf.base.comp.io; + var buf: [4096]u8 = undefined; + switch (input) { + else => {}, + .object => |object| { + var fsr: FileSliceReader = .init(object.file.reader(io, &.{})); + fsr.reset(try fsr.file.getSize(), &buf); + elf.loadObject(object.path, null, &fsr) catch |err| switch (err) { + error.ReadFailed => return fsr.file.err.?, + else => |e| return e, + }; + }, + .archive => |archive| { + var fsr: FileSliceReader = .init(archive.file.reader(io, &buf)); + elf.loadArchive(archive.path, &fsr) catch |err| switch (err) { + error.ReadFailed => return fsr.file.err.?, + else => |e| return e, + }; + }, + } +} +const FileSliceReader = struct { + file: std.Io.File.Reader, + file_location: MappedFile.Node.FileLocation, + reader: std.Io.Reader.Limited, + + pub fn init(file: std.Io.File.Reader) FileSliceReader { + return .{ .file = file, .file_location = undefined, .reader = undefined }; + } + + pub fn reset(fsr: *FileSliceReader, size: u64, buffer: []u8) void { + fsr.file_location = .{ + .offset = fsr.file.logicalPos(), + .size = size, + }; + fsr.reader = .init(&fsr.file.interface, .limited(@intCast(size)), buffer); + } + + pub fn pos(fsr: *const FileSliceReader) u64 { + return fsr.file.logicalPos() - fsr.file_location.offset; + } + + pub fn logicalPos(fsr: *const FileSliceReader) u64 { + return fsr.pos() - fsr.reader.interface.bufferedLen(); + } + + pub fn seekTo(fsr: *FileSliceReader, offset: u64) std.Io.File.Reader.SeekError!void { + if (offset > fsr.file_location.size) return error.EndOfStream; + const logical_pos = fsr.logicalPos(); + if (offset < logical_pos or offset >= fsr.pos()) { + fsr.reader.interface.tossBuffered(); + try fsr.file.seekTo(fsr.file_location.offset + offset); + fsr.reader.remaining = .limited(@intCast(fsr.file_location.size - offset)); + } else fsr.reader.interface.toss(@intCast(offset - logical_pos)); + } +}; +fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fsr: *FileSliceReader) !void { const comp = elf.base.comp; const gpa = comp.gpa; const diags = &comp.link_diags; - switch (input) { - .object => |object| { - const ii: Node.InputIndex = @enumFromInt(elf.inputs.items.len); - log.debug("loadInput(.{{ .object = {f} }}) = {d}", .{ object.path, ii }); - { - try elf.symtab.ensureUnusedCapacity(gpa, 1); - try elf.inputs.ensureUnusedCapacity(gpa, 1); - const si = try elf.initSymbolAssumeCapacity(.{ - .name = std.fs.path.stem(object.path.sub_path), - .type = .FILE, - .shndx = std.elf.SHN_ABS, - }); - elf.inputs.addOneAssumeCapacity().* = .{ - .path = object.path, - .si = si, + const r = &fsr.file.interface; + + log.debug("loadArchive({f})", .{path.fmtEscapeString()}); + if (!std.mem.eql(u8, try r.take(std.elf.ARMAG.len), std.elf.ARMAG)) + return diags.failParse(path, "bad magic", .{}); + var strtab: std.Io.Writer.Allocating = .init(gpa); + defer strtab.deinit(); + while (r.takeStruct(std.elf.ar_hdr, native_endian)) |header| { + if (!std.mem.eql(u8, &header.ar_fmag, std.elf.ARFMAG)) + return diags.failParse(path, "bad file magic", .{}); + const size = header.size() catch + return diags.failParse(path, "bad member size", .{}); + if (std.mem.eql(u8, &header.ar_name, std.elf.STRNAME)) { + strtab.clearRetainingCapacity(); + try strtab.ensureTotalCapacityPrecise(size); + r.streamExact(&strtab.writer, size) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + else => |e| return e, + }; + continue; + } + load_object: { + const member = header.name() orelse member: { + const strtab_offset = header.nameOffset() catch |err| switch (err) { + error.Overflow => break :member error.Overflow, + error.InvalidCharacter => break :load_object, + } orelse break :load_object; + const strtab_written = strtab.written(); + if (strtab_offset > strtab_written.len) break :member error.Overflow; + const member = std.mem.sliceTo(strtab_written[strtab_offset..], '\n'); + break :member if (std.mem.endsWith(u8, member, "/")) + member[0 .. member.len - "/".len] + else + member; + } catch |err| switch (err) { + error.Overflow => return diags.failParse(path, "bad member name offset", .{}), + }; + if (!std.mem.endsWith(u8, member, ".o")) break :load_object; + var buf: [4096]u8 = undefined; + fsr.reset(size, &buf); + try elf.loadObject(path, member, fsr); + try fsr.seekTo(size); + continue; + } + try r.discardAll(size); + } else |err| switch (err) { + error.EndOfStream => if (!fsr.file.atEnd()) return error.EndOfStream, + else => |e| return e, + } +} +fn fmtMemberString(member: ?[]const u8) std.fmt.Alt(?[]const u8, memberStringEscape) { + return .{ .data = member }; +} +fn memberStringEscape(member: ?[]const u8, w: *std.Io.Writer) std.Io.Writer.Error!void { + try w.print("({f})", .{std.zig.fmtString(member orelse return)}); +} +fn loadObject(elf: *Elf, path: std.Build.Cache.Path, member: ?[]const u8, fsr: *FileSliceReader) !void { + const comp = elf.base.comp; + const gpa = comp.gpa; + const diags = &comp.link_diags; + const r = &fsr.reader.interface; + + const ii: Node.InputIndex = @enumFromInt(elf.inputs.items.len); + log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberString(member) }); + const ident = try r.peek(std.elf.EI.NIDENT); + if (!std.mem.eql(u8, ident, elf.mf.contents[0..std.elf.EI.NIDENT])) + return diags.failParse(path, "bad ident", .{}); + try elf.symtab.ensureUnusedCapacity(gpa, 1); + try elf.inputs.ensureUnusedCapacity(gpa, 1); + elf.inputs.addOneAssumeCapacity().* = .{ + .path = path, + .member = if (member) |m| try gpa.dupe(u8, m) else null, + .si = try elf.initSymbolAssumeCapacity(.{ + .name = std.fs.path.stem(member orelse path.sub_path), + .type = .FILE, + .shndx = std.elf.SHN_ABS, + }), + }; + const target_endian = elf.targetEndian(); + switch (elf.identClass()) { + .NONE, _ => unreachable, + inline else => |class| { + const ElfN = class.ElfN(); + const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian); + if (ehdr.type != .REL) return diags.failParse(path, "unsupported object type", .{}); + if (ehdr.machine != elf.ehdrField(.machine)) + return diags.failParse(path, "bad machine", .{}); + if (ehdr.shoff == 0 or ehdr.shnum <= 1) return; + if (ehdr.shentsize < @sizeOf(ElfN.Shdr)) + return diags.failParse(path, "unsupported shentsize", .{}); + const sections = try gpa.alloc(struct { shdr: ElfN.Shdr, si: Symbol.Index }, ehdr.shnum); + defer gpa.free(sections); + try fsr.seekTo(ehdr.shoff); + for (sections) |*section| { + section.* = .{ + .shdr = try r.peekStruct(ElfN.Shdr, target_endian), + .si = .null, }; + try r.discardAll(ehdr.shentsize); + switch (section.shdr.type) { + std.elf.SHT_NULL, std.elf.SHT_NOBITS => {}, + else => if (section.shdr.offset + section.shdr.size > fsr.file_location.size) + return diags.failParse(path, "bad section offset/size", .{}), + } } - var buf: [4096]u8 = undefined; - var fr = object.file.reader(comp.io, &buf); - const r = &fr.interface; - const ident = try r.peek(std.elf.EI.NIDENT); - if (!std.mem.eql(u8, elf.mf.contents[0..std.elf.EI.NIDENT], ident)) - return diags.failParse(object.path, "wrong ident", .{}); - const target_endian = elf.targetEndian(); - switch (elf.identClass()) { - .NONE, _ => unreachable, - inline else => |class| { - const ElfN = class.ElfN(); - const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian); - if (ehdr.type != .REL) - return diags.failParse(object.path, "unsupported object type", .{}); - if (ehdr.machine != elf.ehdrField(.machine)) - return diags.failParse(object.path, "wrong machine", .{}); - if (ehdr.shoff == 0 or ehdr.shnum <= 1) return; - if (ehdr.shentsize < @sizeOf(ElfN.Shdr)) - return diags.failParse(object.path, "unsupported shentsize", .{}); - const sections = try gpa.alloc(struct { - shdr: ElfN.Shdr, - si: Symbol.Index, - }, ehdr.shnum); - defer gpa.free(sections); - try fr.seekTo(ehdr.shoff); - for (sections) |*section| { - section.* = .{ - .shdr = try r.peekStruct(ElfN.Shdr, target_endian), - .si = .null, - }; - try fr.seekBy(ehdr.shentsize); - } - const shstrtab = shstrtab: { - if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum) - return diags.failParse(object.path, "missing section names", .{}); - const shdr = §ions[ehdr.shstrndx].shdr; - if (shdr.type != std.elf.SHT_STRTAB) - return diags.failParse(object.path, "invalid shstrtab type", .{}); - const shstrtab = try gpa.alloc(u8, @intCast(shdr.size)); - errdefer gpa.free(shstrtab); - try fr.seekTo(shdr.offset); - try r.readSliceAll(shstrtab); - break :shstrtab shstrtab; - }; - defer gpa.free(shstrtab); - try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1); - try elf.symtab.ensureUnusedCapacity(gpa, ehdr.shnum - 1); - try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1); - for (sections[1..]) |*section| switch (section.shdr.type) { - else => {}, - std.elf.SHT_PROGBITS, std.elf.SHT_NOBITS => { - if (section.shdr.name >= shstrtab.len) continue; - const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); - const parent_si = namedSection(name) orelse continue; - const ni = try elf.mf.addLastChildNode(gpa, parent_si.node(elf), .{ - .size = section.shdr.size, - .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert( - usize, - @intCast(@max(section.shdr.addralign, 1)), - )), - .moved = true, - }); - elf.nodes.appendAssumeCapacity(.{ - .input_section = @enumFromInt(elf.input_sections.items.len), - }); - section.si = try elf.initSymbolAssumeCapacity(.{ - .type = .SECTION, - .shndx = elf.targetLoad(&@field(elf.symPtr(parent_si), @tagName(class)).shndx), - }); - section.si.get(elf).ni = ni; - elf.input_sections.addOneAssumeCapacity().* = .{ - .ii = ii, - .si = section.si, - .file_location = .{ - .offset = section.shdr.offset, - .size = section.shdr.size, - }, - }; - elf.synth_prog_node.increaseEstimatedTotalItems(1); + const shstrtab = shstrtab: { + if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum) + return diags.failParse(path, "missing section names", .{}); + const shdr = §ions[ehdr.shstrndx].shdr; + if (shdr.type != std.elf.SHT_STRTAB) + return diags.failParse(path, "invalid shstrtab type", .{}); + const shstrtab = try gpa.alloc(u8, @intCast(shdr.size)); + errdefer gpa.free(shstrtab); + try fsr.seekTo(shdr.offset); + try r.readSliceAll(shstrtab); + break :shstrtab shstrtab; + }; + defer gpa.free(shstrtab); + try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1); + try elf.symtab.ensureUnusedCapacity(gpa, ehdr.shnum - 1); + try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1); + for (sections[1..]) |*section| switch (section.shdr.type) { + else => {}, + std.elf.SHT_PROGBITS, std.elf.SHT_NOBITS => { + if (section.shdr.name >= shstrtab.len) continue; + const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); + const parent_si = namedSection(name) orelse continue; + const ni = try elf.mf.addLastChildNode(gpa, parent_si.node(elf), .{ + .size = section.shdr.size, + .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert( + usize, + @intCast(@max(section.shdr.addralign, 1)), + )), + .moved = true, + }); + elf.nodes.appendAssumeCapacity(.{ + .input_section = @enumFromInt(elf.input_sections.items.len), + }); + section.si = try elf.initSymbolAssumeCapacity(.{ + .type = .SECTION, + .shndx = elf.targetLoad(&@field(elf.symPtr(parent_si), @tagName(class)).shndx), + }); + section.si.get(elf).ni = ni; + elf.input_sections.addOneAssumeCapacity().* = .{ + .ii = ii, + .si = section.si, + .file_location = .{ + .offset = fsr.file_location.offset + section.shdr.offset, + .size = section.shdr.size, }, }; - var symmap: std.ArrayList(Symbol.Index) = .empty; - defer symmap.deinit(gpa); - for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) { + elf.synth_prog_node.increaseEstimatedTotalItems(1); + }, + }; + var symmap: std.ArrayList(Symbol.Index) = .empty; + defer symmap.deinit(gpa); + for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) { + else => {}, + std.elf.SHT_SYMTAB => { + if (symtab.shdr.entsize < @sizeOf(ElfN.Sym)) + return diags.failParse(path, "unsupported symtab entsize", .{}); + const strtab = strtab: { + if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum) + return diags.failParse(path, "missing symbol names", .{}); + const shdr = §ions[symtab.shdr.link].shdr; + if (shdr.type != std.elf.SHT_STRTAB) + return diags.failParse(path, "invalid strtab type", .{}); + const strtab = try gpa.alloc(u8, @intCast(shdr.size)); + errdefer gpa.free(strtab); + try fsr.seekTo(shdr.offset); + try r.readSliceAll(strtab); + break :strtab strtab; + }; + defer gpa.free(strtab); + const symnum = std.math.divExact( + u32, + @intCast(symtab.shdr.size), + @intCast(symtab.shdr.entsize), + ) catch return diags.failParse( + path, + "symtab section size (0x{x}) is not a multiple of entsize (0x{x})", + .{ symtab.shdr.size, symtab.shdr.entsize }, + ); + symmap.clearRetainingCapacity(); + try symmap.resize(gpa, symnum); + try elf.symtab.ensureUnusedCapacity(gpa, symnum); + try elf.globals.ensureUnusedCapacity(gpa, symnum); + try fsr.seekTo(symtab.shdr.offset + symtab.shdr.entsize); + symmap.items[0] = .null; + for (symmap.items[1..]) |*si| { + si.* = .null; + const input_sym = try r.peekStruct(ElfN.Sym, target_endian); + try r.discardAll64(symtab.shdr.entsize); + if (input_sym.name >= strtab.len or input_sym.shndx == std.elf.SHN_UNDEF or + input_sym.shndx >= ehdr.shnum) continue; + switch (input_sym.info.type) { + else => continue, + .SECTION => { + const section = §ions[input_sym.shndx]; + if (input_sym.value == section.shdr.addr) si.* = section.si; + continue; + }, + .OBJECT, .FUNC => {}, + } + const name = std.mem.sliceTo(strtab[input_sym.name..], 0); + const parent_si = sections[input_sym.shndx].si; + si.* = try elf.initSymbolAssumeCapacity(.{ + .name = name, + .value = input_sym.value, + .size = input_sym.size, + .type = input_sym.info.type, + .bind = input_sym.info.bind, + .visibility = input_sym.other.visibility, + .shndx = elf.targetLoad(switch (elf.symPtr(parent_si)) { + inline else => |parent_sym| &parent_sym.shndx, + }), + }); + si.get(elf).ni = parent_si.get(elf).ni; + switch (input_sym.info.bind) { + else => {}, + .GLOBAL => { + const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad( + &@field(elf.symPtr(si.*), @tagName(class)).name, + )); + if (gop.found_existing) switch (elf.targetLoad( + switch (elf.symPtr(gop.value_ptr.*)) { + inline else => |sym| &sym.info, + }, + ).bind) { + else => unreachable, + .GLOBAL => return diags.failParse( + path, + "multiple definitions of '{s}'", + .{name}, + ), + .WEAK => {}, + }; + gop.value_ptr.* = si.*; + }, + .WEAK => { + const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad( + &@field(elf.symPtr(si.*), @tagName(class)).name, + )); + if (!gop.found_existing) gop.value_ptr.* = si.*; + }, + } + } + for (sections[1..]) |*rels| switch (rels.shdr.type) { else => {}, - std.elf.SHT_SYMTAB => { - if (symtab.shdr.entsize < @sizeOf(ElfN.Sym)) - return diags.failParse(object.path, "unsupported symtab entsize", .{}); - const strtab = strtab: { - if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum) - return diags.failParse(object.path, "missing symbol names", .{}); - const shdr = §ions[symtab.shdr.link].shdr; - if (shdr.type != std.elf.SHT_STRTAB) - return diags.failParse(object.path, "invalid strtab type", .{}); - const strtab = try gpa.alloc(u8, @intCast(shdr.size)); - errdefer gpa.free(strtab); - try fr.seekTo(shdr.offset); - try r.readSliceAll(strtab); - break :strtab strtab; + inline std.elf.SHT_REL, std.elf.SHT_RELA => |sht| { + if (rels.shdr.link != symtab_shndx or rels.shdr.info == std.elf.SHN_UNDEF or + rels.shdr.info >= ehdr.shnum) continue; + const Rel = switch (sht) { + else => comptime unreachable, + std.elf.SHT_REL => ElfN.Rel, + std.elf.SHT_RELA => ElfN.Rela, }; - defer gpa.free(strtab); - const symnum = try std.math.divExact( + if (rels.shdr.entsize < @sizeOf(Rel)) + return diags.failParse(path, "unsupported rel entsize", .{}); + const loc_sec = §ions[rels.shdr.info]; + if (loc_sec.si == .null) continue; + const relnum = std.math.divExact( u32, - @intCast(symtab.shdr.size), - @intCast(symtab.shdr.entsize), + @intCast(rels.shdr.size), + @intCast(rels.shdr.entsize), + ) catch return diags.failParse( + path, + "relocation section size (0x{x}) is not a multiple of entsize (0x{x})", + .{ rels.shdr.size, rels.shdr.entsize }, ); - symmap.clearRetainingCapacity(); - try symmap.resize(gpa, symnum); - try elf.symtab.ensureUnusedCapacity(gpa, symnum); - try elf.globals.ensureUnusedCapacity(gpa, symnum); - try fr.seekTo(symtab.shdr.offset + symtab.shdr.entsize); - symmap.items[0] = .null; - for (symmap.items[1..]) |*si| { - si.* = .null; - const input_sym = try r.peekStruct(ElfN.Sym, target_endian); - try fr.seekBy(@intCast(symtab.shdr.entsize)); - if (input_sym.name >= strtab.len or - input_sym.shndx == std.elf.SHN_UNDEF or - input_sym.shndx >= ehdr.shnum) continue; - switch (input_sym.info.type) { - else => continue, - .SECTION => { - const section = §ions[input_sym.shndx]; - if (input_sym.value == section.shdr.addr) si.* = section.si; - continue; - }, - .OBJECT, .FUNC => {}, - } - const name = std.mem.sliceTo(strtab[input_sym.name..], 0); - const parent_si = sections[input_sym.shndx].si; - si.* = try elf.initSymbolAssumeCapacity(.{ - .name = name, - .value = input_sym.value, - .size = input_sym.size, - .type = input_sym.info.type, - .bind = input_sym.info.bind, - .visibility = input_sym.other.visibility, - .shndx = elf.targetLoad(switch (elf.symPtr(parent_si)) { - inline else => |parent_sym| &parent_sym.shndx, - }), - }); - si.get(elf).ni = parent_si.get(elf).ni; - switch (input_sym.info.bind) { - else => {}, - .GLOBAL => { - const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad( - &@field(elf.symPtr(si.*), @tagName(class)).name, - )); - if (gop.found_existing) switch (elf.targetLoad( - switch (elf.symPtr(gop.value_ptr.*)) { - inline else => |sym| &sym.info, - }, - ).bind) { - else => unreachable, - .GLOBAL => return diags.failParse( - object.path, - "multiple definitions of '{s}'", - .{name}, - ), - .WEAK => {}, - }; - gop.value_ptr.* = si.*; + try elf.relocs.ensureUnusedCapacity(gpa, relnum); + try fsr.seekTo(rels.shdr.offset); + for (0..relnum) |_| { + const rel = try r.peekStruct(Rel, target_endian); + try r.discardAll64(rels.shdr.entsize); + if (rel.info.sym >= symnum) continue; + const target_si = symmap.items[rel.info.sym]; + if (target_si == .null) continue; + elf.addRelocAssumeCapacity( + loc_sec.si, + rel.offset - loc_sec.shdr.addr, + target_si, + rel.addend, + switch (elf.ehdrField(.machine)) { + else => unreachable, + inline .AARCH64, + .PPC64, + .RISCV, + .X86_64, + => |machine| @unionInit( + Reloc.Type, + @tagName(machine), + @enumFromInt(rel.info.type), + ), }, - .WEAK => { - const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad( - &@field(elf.symPtr(si.*), @tagName(class)).name, - )); - if (!gop.found_existing) gop.value_ptr.* = si.*; - }, - } + ); } - for (sections[1..]) |*rels| switch (rels.shdr.type) { - else => {}, - inline std.elf.SHT_REL, std.elf.SHT_RELA => |sht| { - if (rels.shdr.link != symtab_shndx or - rels.shdr.info == std.elf.SHN_UNDEF or - rels.shdr.info >= ehdr.shnum) continue; - const Rel = switch (sht) { - else => comptime unreachable, - std.elf.SHT_REL => ElfN.Rel, - std.elf.SHT_RELA => ElfN.Rela, - }; - if (rels.shdr.entsize < @sizeOf(Rel)) return diags.failParse( - object.path, - "unsupported rel entsize", - .{}, - ); - const loc_sec = §ions[rels.shdr.info]; - if (loc_sec.si == .null) continue; - const relnum = try std.math.divExact( - u32, - @intCast(rels.shdr.size), - @intCast(rels.shdr.entsize), - ); - try elf.relocs.ensureUnusedCapacity(gpa, relnum); - try fr.seekTo(rels.shdr.offset); - for (0..relnum) |_| { - const rel = try r.peekStruct(Rel, target_endian); - try fr.seekBy(@intCast(rels.shdr.entsize)); - if (rel.info.sym >= symnum) continue; - const target_si = symmap.items[rel.info.sym]; - if (target_si == .null) continue; - elf.addReloc( - loc_sec.si, - rel.offset - loc_sec.shdr.addr, - target_si, - rel.addend, - switch (elf.ehdrField(.machine)) { - else => unreachable, - inline .AARCH64, - .PPC64, - .RISCV, - .X86_64, - => |machine| @unionInit( - Reloc.Type, - @tagName(machine), - @enumFromInt(rel.info.type), - ), - }, - ) catch unreachable; - } - }, - }; }, }; }, - } + }; }, - else => {}, } } @@ -1530,6 +1650,7 @@ fn prelinkInner(elf: *Elf) !void { }); elf.inputs.addOneAssumeCapacity().* = .{ .path = elf.base.emit, + .member = null, .si = si, }; } @@ -1667,10 +1788,20 @@ pub fn addReloc( addend: i64, @"type": Reloc.Type, ) !void { - const gpa = elf.base.comp.gpa; + try elf.relocs.ensureUnusedCapacity(elf.base.comp.gpa, 1); + elf.addRelocAssumeCapacity(loc_si, offset, target_si, addend, @"type"); +} +pub fn addRelocAssumeCapacity( + elf: *Elf, + loc_si: Symbol.Index, + offset: u64, + target_si: Symbol.Index, + addend: i64, + @"type": Reloc.Type, +) void { const target = target_si.get(elf); const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len); - (try elf.relocs.addOne(gpa)).* = .{ + elf.relocs.addOneAssumeCapacity().* = .{ .type = @"type", .prev = .none, .next = target.target_relocs, @@ -1961,11 +2092,20 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.symbol(elf).node(elf))); defer sub_prog_node.end(); elf.flushInputSection(isi) catch |err| switch (err) { - else => |e| return comp.link_diags.fail("linker failed to read input section '{s}' from \"{f}\": {t}", .{ - elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section), - isi.input(elf).path(elf).fmtEscapeString(), - e, - }), + else => |e| { + const ii = isi.input(elf); + return comp.link_diags.fail( + "linker failed to read input section '{s}' from \"{f}{f}\": {t}", + .{ + elf.sectionName( + elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section, + ), + ii.path(elf).fmtEscapeString(), + fmtMemberString(ii.member(elf)), + e, + }, + ); + }, }; break :task; } @@ -1998,10 +2138,14 @@ fn idleProgNode( return prog_node.start(name: switch (node) { else => |tag| @tagName(tag), .section => |si| elf.sectionName(si), - .input_section => |isi| std.fmt.bufPrint(&name, "{f} {s}", .{ - isi.input(elf).path(elf), - elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section), - }) catch &name, + .input_section => |isi| { + const ii = isi.input(elf); + break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{ + ii.path(elf).fmtEscapeString(), + fmtMemberString(ii.member(elf)), + elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section), + }) catch &name; + }, .nav => |nmi| { const ip = &elf.base.comp.zcu.?.intern_pool; break :name ip.getNav(nmi.navIndex(elf)).fqn.toSlice(ip); @@ -2128,16 +2272,17 @@ fn flushInputSection(elf: *Elf, isi: Node.InputSectionIndex) !void { if (file_loc.size == 0) return; const comp = elf.base.comp; const gpa = comp.gpa; - const io = comp.io; - const path = isi.input(elf).path(elf); - const file = try path.root_dir.handle.adaptToNewApi().openFile(io, path.sub_path, .{}); - defer file.close(io); - var fr = file.reader(io, &.{}); + const ii = isi.input(elf); + const path = ii.path(elf); + const file = try path.root_dir.handle.adaptToNewApi().openFile(comp.io, path.sub_path, .{}); + defer file.close(comp.io); + var fr = file.reader(comp.io, &.{}); try fr.seekTo(file_loc.offset); var nw: MappedFile.Node.Writer = undefined; isi.symbol(elf).node(elf).writer(&elf.mf, gpa, &nw); defer nw.deinit(); - if (try nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) != file_loc.size) return error.EndOfStream; + if (try nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) != file_loc.size) + return error.EndOfStream; } fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { @@ -2396,10 +2541,14 @@ pub fn printNode( switch (node) { else => {}, .section => |si| try w.print("({s})", .{elf.sectionName(si)}), - .input_section => |isi| try w.print("({f}, {s})", .{ - isi.input(elf).path(elf), - elf.sectionName(elf.getNode(isi.node(elf).parent(&elf.mf)).section), - }), + .input_section => |isi| { + const ii = isi.input(elf); + try w.print("({f}{f}, {s})", .{ + ii.path(elf).fmtEscapeString(), + fmtMemberString(ii.member(elf)), + elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section), + }); + }, .nav => |nmi| { const zcu = elf.base.comp.zcu.?; const ip = &zcu.intern_pool; diff --git a/src/link/MappedFile.zig b/src/link/MappedFile.zig index 7d2f153a59..8b4d67cf63 100644 --- a/src/link/MappedFile.zig +++ b/src/link/MappedFile.zig @@ -144,7 +144,14 @@ pub const Node = extern struct { } }; - pub const FileLocation = struct { offset: u64, size: u64 }; + pub const FileLocation = struct { + offset: u64, + size: u64, + + pub fn end(fl: FileLocation) u64 { + return fl.offset + fl.size; + } + }; pub const Index = enum(u32) { none, -- cgit v1.2.3