From a94d5895cfbc7b2e695ba1edaad8f4af3d2081ab Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 12 Feb 2024 21:45:15 +0100 Subject: [PATCH 1/8] elf: do not prealloc input objects, pread selectively --- src/link/Elf.zig | 79 ++++++--- src/link/Elf/Archive.zig | 56 ++++--- src/link/Elf/Atom.zig | 10 +- src/link/Elf/Object.zig | 240 +++++++++++++++------------- src/link/Elf/SharedObject.zig | 165 ++++++++++--------- src/link/Elf/ZigObject.zig | 23 ++- src/link/Elf/eh_frame.zig | 31 ++-- src/link/Elf/file.zig | 11 +- src/link/Elf/synthetic_sections.zig | 5 +- 9 files changed, 339 insertions(+), 281 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b139605f75d4..fc8e82176f04 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -35,6 +35,10 @@ llvm_object: ?*LlvmObject = null, /// Index of each input file also encodes the priority or precedence of one input file /// over another. files: std.MultiArrayList(File.Entry) = .{}, +/// Long-lived list of all file descriptors. +/// We store them globally rather than per actual File so that we can re-use +/// one file handle per every object file within an archive. +file_handles: std.ArrayListUnmanaged(File.Handle) = .{}, zig_object_index: ?File.Index = null, linker_defined_index: ?File.Index = null, objects: std.ArrayListUnmanaged(File.Index) = .{}, @@ -444,6 +448,11 @@ pub fn deinit(self: *Elf) void { if (self.llvm_object) |llvm_object| llvm_object.deinit(); + for (self.file_handles.items) |fh| { + fh.close(); + } + self.file_handles.deinit(gpa); + for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) { .null => {}, .zig_object => data.zig_object.deinit(gpa), @@ -1244,9 +1253,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) for (self.objects.items) |index| { try self.file(index).?.object.init(self); } - for (self.shared_objects.items) |index| { - try self.file(index).?.shared_object.init(self); - } if (comp.link_errors.items.len > 0) return error.FlushFailure; @@ -1463,7 +1469,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const for (files.items) |index| { const file_ptr = self.file(index).?; try file_ptr.updateArStrtab(gpa, &ar_strtab); - file_ptr.updateArSize(); + try file_ptr.updateArSize(self); } // Update file offsets of contributing objects. @@ -1515,7 +1521,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const // Write object files for (files.items) |index| { if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0); - try self.file(index).?.writeAr(buffer.writer()); + try self.file(index).?.writeAr(self, buffer.writer()); } assert(buffer.items.len == total_size); @@ -1902,13 +1908,13 @@ fn parseObject(self: *Elf, path: []const u8) ParseError!void { defer tracy.end(); const gpa = self.base.comp.gpa; - const in_file = try std.fs.cwd().openFile(path, .{}); - defer in_file.close(); - const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); + const handle = try std.fs.cwd().openFile(path, .{}); + const fh = try self.addFileHandle(handle); + const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); self.files.set(index, .{ .object = .{ .path = try gpa.dupe(u8, path), - .data = data, + .file_handle = fh, .index = index, } }); try self.objects.append(gpa, index); @@ -1922,12 +1928,12 @@ fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void { defer tracy.end(); const gpa = self.base.comp.gpa; - const in_file = try std.fs.cwd().openFile(path, .{}); - defer in_file.close(); - const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); - var archive = Archive{ .path = try gpa.dupe(u8, path), .data = data }; + const handle = try std.fs.cwd().openFile(path, .{}); + const fh = try self.addFileHandle(handle); + + var archive = Archive{}; defer archive.deinit(gpa); - try archive.parse(self); + try archive.parse(self, path, fh); const objects = try archive.objects.toOwnedSlice(gpa); defer gpa.free(objects); @@ -1948,13 +1954,12 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void { defer tracy.end(); const gpa = self.base.comp.gpa; - const in_file = try std.fs.cwd().openFile(lib.path, .{}); - defer in_file.close(); - const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); + const handle = try std.fs.cwd().openFile(lib.path, .{}); + defer handle.close(); + const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); self.files.set(index, .{ .shared_object = .{ .path = try gpa.dupe(u8, lib.path), - .data = data, .index = index, .needed = lib.needed, .alive = lib.needed, @@ -1962,7 +1967,7 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void { try self.shared_objects.append(gpa, index); const shared_object = self.file(index).?.shared_object; - try shared_object.parse(self); + try shared_object.parse(self, handle); } fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { @@ -2135,7 +2140,7 @@ fn resolveSymbols(self: *Elf) void { const cg = self.comdatGroup(cg_index); const cg_owner = self.comdatGroupOwner(cg.owner); if (cg_owner.file != index) { - for (object.comdatGroupMembers(cg.shndx)) |shndx| { + for (cg.comdatGroupMembers(self)) |shndx| { const atom_index = object.atoms.items[shndx]; if (self.atom(atom_index)) |atom_ptr| { atom_ptr.flags.alive = false; @@ -5862,6 +5867,19 @@ pub fn file(self: *Elf, index: File.Index) ?File { }; } +pub fn addFileHandle(self: *Elf, handle: std.fs.File) !File.HandleIndex { + const gpa = self.base.comp.gpa; + const index: File.HandleIndex = @intCast(self.file_handles.items.len); + const fh = try self.file_handles.addOne(gpa); + fh.* = handle; + return index; +} + +pub fn fileHandle(self: Elf, index: File.HandleIndex) File.Handle { + assert(index < self.file_handles.items.len); + return self.file_handles.items[index]; +} + /// Returns pointer-to-symbol described at sym_index. pub fn symbol(self: *Elf, sym_index: Symbol.Index) *Symbol { return &self.symbols.items[sym_index]; @@ -6395,6 +6413,15 @@ fn fmtDumpState( } } +/// Caller owns the memory. +pub fn preadAllAlloc(allocator: Allocator, handle: std.fs.File, offset: usize, size: usize) ![]u8 { + const buffer = try allocator.alloc(u8, size); + errdefer allocator.free(buffer); + const amt = try handle.preadAll(buffer, offset); + if (amt != size) return error.InputOutput; + return buffer; +} + /// Binary search pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize { if (!@hasDecl(@TypeOf(predicate), "predicate")) @@ -6441,12 +6468,22 @@ pub const base_tag: link.File.Tag = .elf; const ComdatGroupOwner = struct { file: File.Index = 0, + const Index = u32; }; pub const ComdatGroup = struct { owner: ComdatGroupOwner.Index, - shndx: u16, + file: File.Index, + shndx: u32, + members_start: u32, + members_len: u32, + + pub fn comdatGroupMembers(cg: ComdatGroup, elf_file: *Elf) []const u32 { + const object = elf_file.file(cg.file).?.object; + return object.comdat_group_data.items[cg.members_start..][0..cg.members_len]; + } + pub const Index = u32; }; diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index 65135abbc77a..a2f83dda5a2f 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -1,8 +1,5 @@ -path: []const u8, -data: []const u8, - objects: std.ArrayListUnmanaged(Object) = .{}, -strtab: []const u8 = &[0]u8{}, +strtab: std.ArrayListUnmanaged(u8) = .{}, pub fn isArchive(path: []const u8) !bool { const file = try std.fs.cwd().openFile(path, .{}); @@ -14,68 +11,79 @@ pub fn isArchive(path: []const u8) !bool { } pub fn deinit(self: *Archive, allocator: Allocator) void { - allocator.free(self.path); - allocator.free(self.data); self.objects.deinit(allocator); + self.strtab.deinit(allocator); } -pub fn parse(self: *Archive, elf_file: *Elf) !void { +pub fn parse(self: *Archive, elf_file: *Elf, path: []const u8, handle_index: File.HandleIndex) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; + const handle = elf_file.fileHandle(handle_index); + const size = (try handle.stat()).size; - var stream = std.io.fixedBufferStream(self.data); - const reader = stream.reader(); + const reader = handle.reader(); _ = try reader.readBytesNoEof(elf.ARMAG.len); + var pos: usize = elf.ARMAG.len; while (true) { - if (stream.pos >= self.data.len) break; - if (!mem.isAligned(stream.pos, 2)) stream.pos += 1; + if (pos >= size) break; + if (!mem.isAligned(pos, 2)) { + try handle.seekBy(1); + pos += 1; + } const hdr = try reader.readStruct(elf.ar_hdr); + pos += @sizeOf(elf.ar_hdr); if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) { - try elf_file.reportParseError(self.path, "invalid archive header delimiter: {s}", .{ + try elf_file.reportParseError(path, "invalid archive header delimiter: {s}", .{ std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag), }); return error.MalformedArchive; } - const size = try hdr.size(); + const obj_size = try hdr.size(); defer { - _ = stream.seekBy(size) catch {}; + _ = handle.seekBy(obj_size) catch {}; + pos += obj_size; } if (hdr.isSymtab() or hdr.isSymtab64()) continue; if (hdr.isStrtab()) { - self.strtab = self.data[stream.pos..][0..size]; + try self.strtab.resize(gpa, obj_size); + const amt = try handle.preadAll(self.strtab.items, pos); + if (amt != obj_size) return error.InputOutput; continue; } if (hdr.isSymdef() or hdr.isSymdefSorted()) continue; const name = if (hdr.name()) |name| - try gpa.dupe(u8, name) + name else if (try hdr.nameOffset()) |off| - try gpa.dupe(u8, self.getString(off)) + self.getString(off) else unreachable; const object = Object{ - .archive = try gpa.dupe(u8, self.path), - .path = name, - .data = try gpa.dupe(u8, self.data[stream.pos..][0..size]), + .archive = .{ + .path = try gpa.dupe(u8, path), + .offset = pos, + }, + .path = try gpa.dupe(u8, name), + .file_handle = handle_index, .index = undefined, .alive = false, }; - log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, self.path }); + log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, path }); try self.objects.append(gpa, object); } } fn getString(self: Archive, off: u32) []const u8 { - assert(off < self.strtab.len); - const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.ptr + off)), 0); + assert(off < self.strtab.items.len); + const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.items.ptr + off)), 0); return name[0 .. name.len - 1]; } @@ -86,7 +94,7 @@ pub fn setArHdr(opts: struct { name: []const u8, name_off: u32, }, - size: u32, + size: usize, }) elf.ar_hdr { var hdr: elf.ar_hdr = .{ .ar_name = undefined, diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index a7b876f619a2..cb3e7718b272 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -22,6 +22,12 @@ output_section_index: u16 = 0, /// Index of the input section containing this atom's relocs. relocs_section_index: u32 = 0, +/// Start index of the relocations belonging to this atom. +rel_index: u32 = 0, + +/// Number of relocations belonging to this atom. +rel_num: u32 = 0, + /// Index of this atom in the linker's atoms table. atom_index: Index = 0, @@ -52,7 +58,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) Object.ElfShdr { +pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr { return switch (self.file(elf_file).?) { .object => |x| x.shdrs.items[self.input_section_index], .zig_object => |x| x.inputShdr(self.atom_index, elf_file), @@ -289,7 +295,7 @@ pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela { const shndx = self.relocsShndx() orelse return &[0]elf.Elf64_Rela{}; return switch (self.file(elf_file).?) { .zig_object => |x| x.relocs.items[shndx].items, - .object => |x| x.getRelocs(shndx), + .object => |x| x.relocs.items[self.rel_index..][0..self.rel_num], else => unreachable, }; } diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 1f01f471c931..3a9d32f51c80 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -1,10 +1,10 @@ -archive: ?[]const u8 = null, +archive: ?InArchive = null, path: []const u8, -data: []const u8, +file_handle: File.HandleIndex, index: File.Index, header: ?elf.Elf64_Ehdr = null, -shdrs: std.ArrayListUnmanaged(ElfShdr) = .{}, +shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{}, symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, @@ -12,9 +12,12 @@ first_global: ?Symbol.Index = null, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{}, +comdat_group_data: std.ArrayListUnmanaged(u32) = .{}, +relocs: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, fdes: std.ArrayListUnmanaged(Fde) = .{}, cies: std.ArrayListUnmanaged(Cie) = .{}, +eh_frame_data: std.ArrayListUnmanaged(u8) = .{}, alive: bool = true, num_dynrelocs: u32 = 0, @@ -35,24 +38,30 @@ pub fn isObject(path: []const u8) !bool { } pub fn deinit(self: *Object, allocator: Allocator) void { - if (self.archive) |path| allocator.free(path); + if (self.archive) |*ar| allocator.free(ar.path); allocator.free(self.path); - allocator.free(self.data); self.shdrs.deinit(allocator); self.symtab.deinit(allocator); self.strtab.deinit(allocator); self.symbols.deinit(allocator); self.atoms.deinit(allocator); self.comdat_groups.deinit(allocator); + self.comdat_group_data.deinit(allocator); + self.relocs.deinit(allocator); self.fdes.deinit(allocator); self.cies.deinit(allocator); + self.eh_frame_data.deinit(allocator); } pub fn parse(self: *Object, elf_file: *Elf) !void { - var stream = std.io.fixedBufferStream(self.data); - const reader = stream.reader(); + const gpa = elf_file.base.comp.gpa; + const offset = if (self.archive) |ar| ar.offset else 0; + const handle = elf_file.fileHandle(self.file_handle); + const file_size = (try handle.stat()).size; - self.header = try reader.readStruct(elf.Elf64_Ehdr); + const header_buffer = try Elf.preadAllAlloc(gpa, handle, offset, @sizeOf(elf.Elf64_Ehdr)); + defer gpa.free(header_buffer); + self.header = @as(*align(1) const elf.Elf64_Ehdr, @ptrCast(header_buffer)).*; const target = elf_file.base.comp.root_mod.resolved_target.result; if (target.cpu.arch != self.header.?.e_machine.toTargetCpuArch().?) { @@ -66,12 +75,10 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { if (self.header.?.e_shnum == 0) return; - const comp = elf_file.base.comp; - const gpa = comp.gpa; - - if (self.data.len < self.header.?.e_shoff or - self.data.len < self.header.?.e_shoff + @as(u64, @intCast(self.header.?.e_shnum)) * @sizeOf(elf.Elf64_Shdr)) - { + const shoff = math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow; + const shnum = math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow; + const shsize = shnum * @sizeOf(elf.Elf64_Shdr); + if (file_size < offset + shoff or file_size < offset + shoff + shsize) { try elf_file.reportParseError2( self.index, "corrupt header: section header table extends past the end of file", @@ -80,25 +87,23 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { return error.MalformedObject; } - const shoff = math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow; - const shdrs = @as( - [*]align(1) const elf.Elf64_Shdr, - @ptrCast(self.data.ptr + shoff), - )[0..self.header.?.e_shnum]; - try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len); + const shdrs_buffer = try Elf.preadAllAlloc(gpa, handle, offset + shoff, shsize); + defer gpa.free(shdrs_buffer); + const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(shdrs_buffer.ptr))[0..shnum]; + try self.shdrs.appendUnalignedSlice(gpa, shdrs); - for (shdrs) |shdr| { + for (self.shdrs.items) |shdr| { if (shdr.sh_type != elf.SHT_NOBITS) { - if (self.data.len < shdr.sh_offset or self.data.len < shdr.sh_offset + shdr.sh_size) { + if (file_size < offset + shdr.sh_offset or file_size < offset + shdr.sh_offset + shdr.sh_size) { try elf_file.reportParseError2(self.index, "corrupt section: extends past the end of file", .{}); return error.MalformedObject; } } - self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr)); } - const shstrtab = self.shdrContents(self.header.?.e_shstrndx); - for (shdrs) |shdr| { + const shstrtab = try self.preadShdrContentsAlloc(gpa, handle, self.header.?.e_shstrndx); + defer gpa.free(shstrtab); + for (self.shdrs.items) |shdr| { if (shdr.sh_name >= shstrtab.len) { try elf_file.reportParseError2(self.index, "corrupt section name offset", .{}); return error.MalformedObject; @@ -112,10 +117,11 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { } else null; if (symtab_index) |index| { - const shdr = shdrs[index]; + const shdr = self.shdrs.items[index]; self.first_global = shdr.sh_info; - const raw_symtab = self.shdrContents(index); + const raw_symtab = try self.preadShdrContentsAlloc(gpa, handle, index); + defer gpa.free(raw_symtab); const nsyms = math.divExact(usize, raw_symtab.len, @sizeOf(elf.Elf64_Sym)) catch { try elf_file.reportParseError2(self.index, "symbol table not evenly divisible", .{}); return error.MalformedObject; @@ -123,7 +129,9 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms]; const strtab_bias = @as(u32, @intCast(self.strtab.items.len)); - try self.strtab.appendSlice(gpa, self.shdrContents(@as(u16, @intCast(shdr.sh_link)))); + const strtab = try self.preadShdrContentsAlloc(gpa, handle, shdr.sh_link); + defer gpa.free(strtab); + try self.strtab.appendSlice(gpa, strtab); try self.symtab.ensureUnusedCapacity(gpa, symtab.len); for (symtab) |sym| { @@ -138,22 +146,23 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { } pub fn init(self: *Object, elf_file: *Elf) !void { - try self.initAtoms(elf_file); - try self.initSymtab(elf_file); + const gpa = elf_file.base.comp.gpa; + const handle = elf_file.fileHandle(self.file_handle); + + try self.initAtoms(gpa, handle, elf_file); + try self.initSymtab(gpa, 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); + try self.parseEhFrame(gpa, handle, @as(u32, @intCast(i)), elf_file); } } -fn initAtoms(self: *Object, elf_file: *Elf) !void { - const comp = elf_file.base.comp; - const gpa = comp.gpa; +fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void { const shdrs = self.shdrs.items; - try self.atoms.resize(gpa, shdrs.len); + try self.atoms.resize(allocator, shdrs.len); @memset(self.atoms.items, 0); for (shdrs, 0..) |shdr, i| { @@ -177,8 +186,9 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void { break :blk self.getString(group_info_sym.st_name); }; - const shndx = @as(u16, @intCast(i)); - const group_raw_data = self.shdrContents(shndx); + const shndx = @as(u32, @intCast(i)); + const group_raw_data = try self.preadShdrContentsAlloc(allocator, handle, shndx); + defer allocator.free(group_raw_data); 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]; @@ -188,14 +198,20 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void { continue; } + const group_start = @as(u32, @intCast(self.comdat_group_data.items.len)); + try self.comdat_group_data.appendUnalignedSlice(allocator, group_members[1..]); + const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature); const comdat_group_index = try elf_file.addComdatGroup(); const comdat_group = elf_file.comdatGroup(comdat_group_index); comdat_group.* = .{ .owner = gop.index, + .file = self.index, .shndx = shndx, + .members_start = group_start, + .members_len = @intCast(group_nmembers - 1), }; - try self.comdat_groups.append(gpa, comdat_group_index); + try self.comdat_groups.append(allocator, comdat_group_index); }, elf.SHT_SYMTAB_SHNDX => @panic("TODO SHT_SYMTAB_SHNDX"), @@ -210,7 +226,7 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void { else => { const shndx = @as(u16, @intCast(i)); if (self.skipShdr(shndx, elf_file)) continue; - try self.addAtom(shdr, shndx, elf_file); + try self.addAtom(allocator, handle, shdr, shndx, elf_file); }, } } @@ -220,14 +236,19 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void { elf.SHT_REL, elf.SHT_RELA => { const atom_index = self.atoms.items[shdr.sh_info]; if (elf_file.atom(atom_index)) |atom| { - atom.relocs_section_index = @as(u16, @intCast(i)); + const relocs = try self.preadRelocsAlloc(allocator, handle, @intCast(i)); + defer allocator.free(relocs); + atom.relocs_section_index = @intCast(i); + atom.rel_index = @intCast(self.relocs.items.len); + atom.rel_num = @intCast(relocs.len); + try self.relocs.appendUnalignedSlice(allocator, relocs); } }, else => {}, }; } -fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOfMemory}!void { +fn addAtom(self: *Object, allocator: Allocator, handle: std.fs.File, shdr: elf.Elf64_Shdr, shndx: u32, elf_file: *Elf) !void { const atom_index = try elf_file.addAtom(); const atom = elf_file.atom(atom_index).?; atom.atom_index = atom_index; @@ -237,7 +258,8 @@ fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOf self.atoms.items[shndx] = atom_index; if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) { - const data = self.shdrContents(shndx); + const data = try self.preadShdrContentsAlloc(allocator, handle, shndx); + defer allocator.free(data); const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*; atom.size = chdr.ch_size; atom.alignment = Alignment.fromNonzeroByteUnits(chdr.ch_addralign); @@ -247,7 +269,7 @@ fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOf } } -fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMemory}!u16 { +fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) error{OutOfMemory}!u16 { const name = blk: { const name = self.getString(shdr.sh_name); if (elf_file.base.isRelocatable()) break :blk name; @@ -310,12 +332,10 @@ fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool { return ignore; } -fn initSymtab(self: *Object, elf_file: *Elf) !void { - const comp = elf_file.base.comp; - const gpa = comp.gpa; +fn initSymtab(self: *Object, allocator: Allocator, elf_file: *Elf) !void { const first_global = self.first_global orelse self.symtab.items.len; - try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.items.len); + try self.symbols.ensureTotalCapacityPrecise(allocator, self.symtab.items.len); for (self.symtab.items[0..first_global], 0..) |sym, i| { const index = try elf_file.addSymbol(); @@ -335,19 +355,24 @@ fn initSymtab(self: *Object, elf_file: *Elf) !void { } } -fn parseEhFrame(self: *Object, shndx: u16, elf_file: *Elf) !void { +fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx: u32, elf_file: *Elf) !void { const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { - elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u16, @intCast(i)), + elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u32, @intCast(i)), else => {}, } else { + // TODO: convert into an error log.debug("{s}: missing reloc section for unwind info section", .{self.fmtPath()}); return; }; - const comp = elf_file.base.comp; - const gpa = comp.gpa; - const raw = self.shdrContents(shndx); - const relocs = self.getRelocs(relocs_shndx); + const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx); + defer allocator.free(raw); + const data_start = @as(u32, @intCast(self.eh_frame_data.items.len)); + try self.eh_frame_data.appendSlice(allocator, raw); + const relocs = try self.preadRelocsAlloc(allocator, handle, relocs_shndx); + defer allocator.free(relocs); + const rel_start = @as(u32, @intCast(self.relocs.items.len)); + try self.relocs.appendUnalignedSlice(allocator, relocs); const fdes_start = self.fdes.items.len; const cies_start = self.cies.items.len; @@ -355,22 +380,20 @@ fn parseEhFrame(self: *Object, shndx: u16, elf_file: *Elf) !void { while (try it.next()) |rec| { const rel_range = filterRelocs(relocs, rec.offset, rec.size + 4); switch (rec.tag) { - .cie => try self.cies.append(gpa, .{ - .offset = rec.offset, + .cie => try self.cies.append(allocator, .{ + .offset = data_start + rec.offset, .size = rec.size, - .rel_index = @as(u32, @intCast(rel_range.start)), + .rel_index = rel_start + @as(u32, @intCast(rel_range.start)), .rel_num = @as(u32, @intCast(rel_range.len)), - .rel_section_index = relocs_shndx, .input_section_index = shndx, .file_index = self.index, }), - .fde => try self.fdes.append(gpa, .{ - .offset = rec.offset, + .fde => try self.fdes.append(allocator, .{ + .offset = data_start + rec.offset, .size = rec.size, .cie_index = undefined, - .rel_index = @as(u32, @intCast(rel_range.start)), + .rel_index = rel_start + @as(u32, @intCast(rel_range.start)), .rel_num = @as(u32, @intCast(rel_range.len)), - .rel_section_index = relocs_shndx, .input_section_index = shndx, .file_index = self.index, }), @@ -773,21 +796,30 @@ pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf } } -pub fn updateArSize(self: *Object) void { - self.output_ar_state.size = self.data.len; +pub fn updateArSize(self: *Object, elf_file: *Elf) !void { + const handle = elf_file.fileHandle(self.file_handle); + const size = (try handle.stat()).size; + self.output_ar_state.size = size; } -pub fn writeAr(self: Object, writer: anytype) !void { +pub fn writeAr(self: Object, elf_file: *Elf, writer: anytype) !void { + const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; const name = self.path; const hdr = Archive.setArHdr(.{ .name = if (name.len <= Archive.max_member_name_len) .{ .name = name } else .{ .name_off = self.output_ar_state.name_off }, - .size = @intCast(self.data.len), + .size = size, }); try writer.writeAll(mem.asBytes(&hdr)); - try writer.writeAll(self.data); + const handle = elf_file.fileHandle(self.file_handle); + const gpa = elf_file.base.comp.gpa; + const data = try gpa.alloc(u8, size); + defer gpa.free(data); + const amt = try handle.preadAll(data, 0); + if (amt != size) return error.InputOutput; + try writer.writeAll(data); } pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void { @@ -859,12 +891,6 @@ pub fn globals(self: Object) []const Symbol.Index { return self.symbols.items[start..]; } -pub fn shdrContents(self: Object, index: u32) []const u8 { - assert(index < self.shdrs.items.len); - const shdr = self.shdrs.items[index]; - return self.data[shdr.sh_offset..][0..shdr.sh_size]; -} - /// Returns atom's code and optionally uncompresses data if required (for compressed sections). /// Caller owns the memory. pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index) ![]u8 { @@ -872,8 +898,11 @@ pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index) const gpa = comp.gpa; const atom_ptr = elf_file.atom(atom_index).?; assert(atom_ptr.file_index == self.index); - const data = self.shdrContents(atom_ptr.input_section_index); const shdr = atom_ptr.inputShdr(elf_file); + const handle = elf_file.fileHandle(self.file_handle); + const data = try self.preadShdrContentsAlloc(gpa, handle, atom_ptr.input_section_index); + defer if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) gpa.free(data); + if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) { const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*; switch (chdr.ch_type) { @@ -892,31 +921,35 @@ pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index) }, else => @panic("TODO unhandled compression scheme"), } - } else return gpa.dupe(u8, data); -} + } -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; + return data; } pub fn asFile(self: *Object) File { return .{ .object = self }; } -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]; -} - pub fn getString(self: Object, off: u32) [:0]const u8 { assert(off < self.strtab.items.len); return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); } +/// Caller owns the memory. +fn preadShdrContentsAlloc(self: Object, allocator: Allocator, handle: std.fs.File, index: u32) ![]u8 { + assert(index < self.shdrs.items.len); + const offset = if (self.archive) |ar| ar.offset else 0; + const shdr = self.shdrs.items[index]; + return Elf.preadAllAlloc(allocator, handle, offset + shdr.sh_offset, shdr.sh_size); +} + +/// Caller owns the memory. +fn preadRelocsAlloc(self: Object, allocator: Allocator, handle: std.fs.File, shndx: u32) ![]align(1) const elf.Elf64_Rela { + const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx); + const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela)); + return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num]; +} + pub fn format( self: *Object, comptime unused_fmt_string: []const u8, @@ -1053,7 +1086,7 @@ fn formatComdatGroups( const cg_owner = elf_file.comdatGroupOwner(cg.owner); if (cg_owner.file != object.index) continue; try writer.print(" COMDAT({d})\n", .{cg_index}); - const cg_members = object.comdatGroupMembers(cg.shndx); + const cg_members = cg.comdatGroupMembers(elf_file); for (cg_members) |shndx| { const atom_index = object.atoms.items[shndx]; const atom = elf_file.atom(atom_index) orelse continue; @@ -1074,40 +1107,17 @@ fn formatPath( ) !void { _ = unused_fmt_string; _ = options; - if (object.archive) |path| { - try writer.writeAll(path); + if (object.archive) |ar| { + try writer.writeAll(ar.path); try writer.writeByte('('); try writer.writeAll(object.path); try writer.writeByte(')'); } 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, - - pub 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 InArchive = struct { + path: []const u8, + offset: u64, }; const Object = @This(); diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig index 658090ef5133..d40367f304ec 100644 --- a/src/link/Elf/SharedObject.zig +++ b/src/link/Elf/SharedObject.zig @@ -1,22 +1,18 @@ path: []const u8, -data: []const u8, index: File.Index, header: ?elf.Elf64_Ehdr = null, -shdrs: std.ArrayListUnmanaged(ElfShdr) = .{}, +shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{}, symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, /// Version symtab contains version strings of the symbols if present. versyms: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{}, verstrings: std.ArrayListUnmanaged(u32) = .{}, + symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, aliases: ?std.ArrayListUnmanaged(u32) = null, - -dynsym_sect_index: ?u16 = null, -dynamic_sect_index: ?u16 = null, -versym_sect_index: ?u16 = null, -verdef_sect_index: ?u16 = null, +dynamic_table: std.ArrayListUnmanaged(elf.Elf64_Dyn) = .{}, needed: bool, alive: bool, @@ -36,23 +32,24 @@ pub fn isSharedObject(path: []const u8) !bool { pub fn deinit(self: *SharedObject, allocator: Allocator) void { allocator.free(self.path); - allocator.free(self.data); + self.shdrs.deinit(allocator); self.symtab.deinit(allocator); self.strtab.deinit(allocator); self.versyms.deinit(allocator); self.verstrings.deinit(allocator); self.symbols.deinit(allocator); if (self.aliases) |*aliases| aliases.deinit(allocator); - self.shdrs.deinit(allocator); + self.dynamic_table.deinit(allocator); } -pub fn parse(self: *SharedObject, elf_file: *Elf) !void { +pub fn parse(self: *SharedObject, elf_file: *Elf, handle: std.fs.File) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; - var stream = std.io.fixedBufferStream(self.data); - const reader = stream.reader(); + const file_size = (try handle.stat()).size; - self.header = try reader.readStruct(elf.Elf64_Ehdr); + const header_buffer = try Elf.preadAllAlloc(gpa, handle, 0, @sizeOf(elf.Elf64_Ehdr)); + defer gpa.free(header_buffer); + self.header = @as(*align(1) const elf.Elf64_Ehdr, @ptrCast(header_buffer)).*; const target = elf_file.base.comp.root_mod.resolved_target.result; if (target.cpu.arch != self.header.?.e_machine.toTargetCpuArch().?) { @@ -64,9 +61,10 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void { return error.InvalidCpuArch; } - if (self.data.len < self.header.?.e_shoff or - self.data.len < self.header.?.e_shoff + @as(u64, @intCast(self.header.?.e_shnum)) * @sizeOf(elf.Elf64_Shdr)) - { + const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow; + const shnum = std.math.cast(usize, self.header.?.e_shnum) orelse return error.Overflow; + const shsize = shnum * @sizeOf(elf.Elf64_Shdr); + if (file_size < shoff or file_size < shoff + shsize) { try elf_file.reportParseError2( self.index, "corrupted header: section header table extends past the end of file", @@ -75,45 +73,84 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void { return error.MalformedObject; } - const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow; - - const shdrs = @as( - [*]align(1) const elf.Elf64_Shdr, - @ptrCast(self.data.ptr + shoff), - )[0..self.header.?.e_shnum]; - try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len); + const shdrs_buffer = try Elf.preadAllAlloc(gpa, handle, shoff, shsize); + defer gpa.free(shdrs_buffer); + const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(shdrs_buffer.ptr))[0..shnum]; + try self.shdrs.appendUnalignedSlice(gpa, shdrs); - for (shdrs, 0..) |shdr, i| { + var dynsym_sect_index: ?u32 = null; + var dynamic_sect_index: ?u32 = null; + var versym_sect_index: ?u32 = null; + var verdef_sect_index: ?u32 = null; + for (self.shdrs.items, 0..) |shdr, i| { if (shdr.sh_type != elf.SHT_NOBITS) { - if (self.data.len < shdr.sh_offset or self.data.len < shdr.sh_offset + shdr.sh_size) { + if (file_size < shdr.sh_offset or file_size < shdr.sh_offset + shdr.sh_size) { try elf_file.reportParseError2(self.index, "corrupted section header", .{}); return error.MalformedObject; } } - self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr)); switch (shdr.sh_type) { - elf.SHT_DYNSYM => self.dynsym_sect_index = @as(u16, @intCast(i)), - elf.SHT_DYNAMIC => self.dynamic_sect_index = @as(u16, @intCast(i)), - elf.SHT_GNU_VERSYM => self.versym_sect_index = @as(u16, @intCast(i)), - elf.SHT_GNU_VERDEF => self.verdef_sect_index = @as(u16, @intCast(i)), + elf.SHT_DYNSYM => dynsym_sect_index = @intCast(i), + elf.SHT_DYNAMIC => dynamic_sect_index = @intCast(i), + elf.SHT_GNU_VERSYM => versym_sect_index = @intCast(i), + elf.SHT_GNU_VERDEF => verdef_sect_index = @intCast(i), else => {}, } } - try self.parseVersions(elf_file); + if (dynamic_sect_index) |index| { + const shdr = self.shdrs.items[index]; + const raw = try Elf.preadAllAlloc(gpa, handle, shdr.sh_offset, shdr.sh_size); + defer gpa.free(raw); + const num = @divExact(raw.len, @sizeOf(elf.Elf64_Dyn)); + const dyntab = @as([*]align(1) const elf.Elf64_Dyn, @ptrCast(raw.ptr))[0..num]; + try self.dynamic_table.appendUnalignedSlice(gpa, dyntab); + } + + const symtab = if (dynsym_sect_index) |index| blk: { + const shdr = self.shdrs.items[index]; + const buffer = try Elf.preadAllAlloc(gpa, handle, shdr.sh_offset, shdr.sh_size); + const nsyms = @divExact(buffer.len, @sizeOf(elf.Elf64_Sym)); + break :blk @as([*]align(1) const elf.Elf64_Sym, @ptrCast(buffer.ptr))[0..nsyms]; + } else &[0]elf.Elf64_Sym{}; + defer gpa.free(symtab); + + const strtab = if (dynsym_sect_index) |index| blk: { + const symtab_shdr = self.shdrs.items[index]; + const shdr = self.shdrs.items[symtab_shdr.sh_link]; + const buffer = try Elf.preadAllAlloc(gpa, handle, shdr.sh_offset, shdr.sh_size); + break :blk buffer; + } else &[0]u8{}; + defer gpa.free(strtab); + + try self.parseVersions(elf_file, handle, .{ + .symtab = symtab, + .verdef_sect_index = verdef_sect_index, + .versym_sect_index = versym_sect_index, + }); + + try self.initSymtab(elf_file, .{ + .symtab = symtab, + .strtab = strtab, + }); } -fn parseVersions(self: *SharedObject, elf_file: *Elf) !void { +fn parseVersions(self: *SharedObject, elf_file: *Elf, handle: std.fs.File, opts: struct { + symtab: []align(1) const elf.Elf64_Sym, + verdef_sect_index: ?u32, + versym_sect_index: ?u32, +}) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; - const symtab = self.getSymtabRaw(); try self.verstrings.resize(gpa, 2); self.verstrings.items[elf.VER_NDX_LOCAL] = 0; self.verstrings.items[elf.VER_NDX_GLOBAL] = 0; - if (self.verdef_sect_index) |shndx| { - const verdefs = self.shdrContents(shndx); + if (opts.verdef_sect_index) |shndx| { + const shdr = self.shdrs.items[shndx]; + const verdefs = try Elf.preadAllAlloc(gpa, handle, shdr.sh_offset, shdr.sh_size); + defer gpa.free(verdefs); const nverdefs = self.verdefNum(); try self.verstrings.resize(gpa, self.verstrings.items.len + nverdefs); @@ -131,10 +168,12 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void { } } - try self.versyms.ensureTotalCapacityPrecise(gpa, symtab.len); + try self.versyms.ensureTotalCapacityPrecise(gpa, opts.symtab.len); - if (self.versym_sect_index) |shndx| { - const versyms_raw = self.shdrContents(shndx); + if (opts.versym_sect_index) |shndx| { + const shdr = self.shdrs.items[shndx]; + const versyms_raw = try Elf.preadAllAlloc(gpa, handle, shdr.sh_offset, shdr.sh_size); + defer gpa.free(versyms_raw); const nversyms = @divExact(versyms_raw.len, @sizeOf(elf.Elf64_Versym)); const versyms = @as([*]align(1) const elf.Elf64_Versym, @ptrCast(versyms_raw.ptr))[0..nversyms]; for (versyms) |ver| { @@ -144,22 +183,23 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void { ver; self.versyms.appendAssumeCapacity(normalized_ver); } - } else for (0..symtab.len) |_| { + } else for (0..opts.symtab.len) |_| { self.versyms.appendAssumeCapacity(elf.VER_NDX_GLOBAL); } } -pub fn init(self: *SharedObject, elf_file: *Elf) !void { +fn initSymtab(self: *SharedObject, elf_file: *Elf, opts: struct { + symtab: []align(1) const elf.Elf64_Sym, + strtab: []const u8, +}) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; - const symtab = self.getSymtabRaw(); - const strtab = self.getStrtabRaw(); - try self.strtab.appendSlice(gpa, strtab); - try self.symtab.ensureTotalCapacityPrecise(gpa, symtab.len); - try self.symbols.ensureTotalCapacityPrecise(gpa, symtab.len); + try self.strtab.appendSlice(gpa, opts.strtab); + try self.symtab.ensureTotalCapacityPrecise(gpa, opts.symtab.len); + try self.symbols.ensureTotalCapacityPrecise(gpa, opts.symtab.len); - for (symtab, 0..) |sym, i| { + for (opts.symtab, 0..) |sym, i| { const hidden = self.versyms.items[i] & elf.VERSYM_HIDDEN != 0; const name = self.getString(sym.st_name); // We need to garble up the name so that we don't pick this symbol @@ -250,11 +290,6 @@ pub fn writeSymtab(self: SharedObject, elf_file: *Elf) void { } } -pub fn shdrContents(self: SharedObject, index: u16) []const u8 { - const shdr = self.shdrs.items[index]; - return self.data[shdr.sh_offset..][0..shdr.sh_size]; -} - pub fn versionString(self: SharedObject, index: elf.Elf64_Versym) [:0]const u8 { const off = self.verstrings.items[index & elf.VERSYM_VERSION]; return self.getString(off); @@ -264,16 +299,8 @@ pub fn asFile(self: *SharedObject) File { return .{ .shared_object = self }; } -fn dynamicTable(self: *SharedObject) []align(1) const elf.Elf64_Dyn { - const shndx = self.dynamic_sect_index orelse return &[0]elf.Elf64_Dyn{}; - const raw = self.shdrContents(shndx); - const num = @divExact(raw.len, @sizeOf(elf.Elf64_Dyn)); - return @as([*]align(1) const elf.Elf64_Dyn, @ptrCast(raw.ptr))[0..num]; -} - fn verdefNum(self: *SharedObject) u32 { - const entries = self.dynamicTable(); - for (entries) |entry| switch (entry.d_tag) { + for (self.dynamic_table.items) |entry| switch (entry.d_tag) { elf.DT_VERDEFNUM => return @as(u32, @intCast(entry.d_val)), else => {}, }; @@ -281,8 +308,7 @@ fn verdefNum(self: *SharedObject) u32 { } pub fn soname(self: *SharedObject) []const u8 { - const entries = self.dynamicTable(); - for (entries) |entry| switch (entry.d_tag) { + for (self.dynamic_table.items) |entry| switch (entry.d_tag) { elf.DT_SONAME => return self.getString(@as(u32, @intCast(entry.d_val))), else => {}, }; @@ -342,20 +368,6 @@ pub fn getString(self: SharedObject, off: u32) [:0]const u8 { return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); } -pub fn getSymtabRaw(self: SharedObject) []align(1) const elf.Elf64_Sym { - const index = self.dynsym_sect_index orelse return &[0]elf.Elf64_Sym{}; - const raw_symtab = self.shdrContents(index); - const nsyms = @divExact(raw_symtab.len, @sizeOf(elf.Elf64_Sym)); - const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms]; - return symtab; -} - -pub fn getStrtabRaw(self: SharedObject) []const u8 { - const index = self.dynsym_sect_index orelse return &[0]u8{}; - const shdr = self.shdrs.items[index]; - return self.shdrContents(@as(u16, @intCast(shdr.sh_link))); -} - pub fn format( self: SharedObject, comptime unused_fmt_string: []const u8, @@ -407,6 +419,5 @@ const mem = std.mem; const Allocator = mem.Allocator; const Elf = @import("../Elf.zig"); -const ElfShdr = @import("Object.zig").ElfShdr; const File = @import("file.zig").File; const Symbol = @import("Symbol.zig"); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index f3ba16f243e0..a132b1d453c2 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -305,19 +305,16 @@ pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index { } /// TODO actually create fake input shdrs and return that instead. -pub fn inputShdr(self: ZigObject, atom_index: Atom.Index, elf_file: *Elf) Object.ElfShdr { +pub fn inputShdr(self: ZigObject, atom_index: Atom.Index, elf_file: *Elf) elf.Elf64_Shdr { _ = 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; + const atom = elf_file.atom(atom_index) orelse return Elf.null_shdr; + const shndx = atom.outputShndx() orelse return 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); + return shdr; } pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) void { @@ -525,7 +522,7 @@ pub fn writeAr(self: ZigObject, writer: anytype) !void { .{ .name = name } else .{ .name_off = self.output_ar_state.name_off }, - .size = @intCast(self.data.items.len), + .size = self.data.items.len, }); try writer.writeAll(mem.asBytes(&hdr)); try writer.writeAll(self.data.items); diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index 75401824abe5..4825ddb4764d 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -5,7 +5,6 @@ pub const Fde = struct { cie_index: u32, rel_index: u32 = 0, rel_num: u32 = 0, - rel_section_index: u32 = 0, input_section_index: u32 = 0, file_index: u32 = 0, alive: bool = true, @@ -20,10 +19,9 @@ pub const Fde = struct { return base + fde.out_offset; } - pub fn data(fde: Fde, elf_file: *Elf) []const u8 { + pub fn data(fde: Fde, elf_file: *Elf) []u8 { const object = elf_file.file(fde.file_index).?.object; - const contents = object.shdrContents(fde.input_section_index); - return contents[fde.offset..][0..fde.calcSize()]; + return object.eh_frame_data.items[fde.offset..][0..fde.calcSize()]; } pub fn cie(fde: Fde, elf_file: *Elf) Cie { @@ -50,7 +48,7 @@ pub const Fde = struct { pub fn relocs(fde: Fde, elf_file: *Elf) []align(1) const elf.Elf64_Rela { const object = elf_file.file(fde.file_index).?.object; - return object.getRelocs(fde.rel_section_index)[fde.rel_index..][0..fde.rel_num]; + return object.relocs.items[fde.rel_index..][0..fde.rel_num]; } pub fn format( @@ -106,7 +104,6 @@ pub const Cie = struct { size: usize, rel_index: u32 = 0, rel_num: u32 = 0, - rel_section_index: u32 = 0, input_section_index: u32 = 0, file_index: u32 = 0, /// Includes 4byte size cell. @@ -121,10 +118,9 @@ pub const Cie = struct { return base + cie.out_offset; } - pub fn data(cie: Cie, elf_file: *Elf) []const u8 { + pub fn data(cie: Cie, elf_file: *Elf) []u8 { const object = elf_file.file(cie.file_index).?.object; - const contents = object.shdrContents(cie.input_section_index); - return contents[cie.offset..][0..cie.calcSize()]; + return object.eh_frame_data.items[cie.offset..][0..cie.calcSize()]; } pub fn calcSize(cie: Cie) usize { @@ -133,7 +129,7 @@ pub const Cie = struct { pub fn relocs(cie: Cie, elf_file: *Elf) []align(1) const elf.Elf64_Rela { const object = elf_file.file(cie.file_index).?.object; - return object.getRelocs(cie.rel_section_index)[cie.rel_index..][0..cie.rel_num]; + return object.relocs.items[cie.rel_index..][0..cie.rel_num]; } pub fn eql(cie: Cie, other: Cie, elf_file: *Elf) bool { @@ -330,9 +326,6 @@ fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file: } pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { - const comp = elf_file.base.comp; - const gpa = comp.gpa; - relocs_log.debug("{x}: .eh_frame", .{elf_file.shdrs.items[elf_file.eh_frame_section_index.?].sh_addr}); for (elf_file.objects.items) |index| { @@ -341,8 +334,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { for (object.cies.items) |cie| { if (!cie.alive) continue; - const contents = try gpa.dupe(u8, cie.data(elf_file)); - defer gpa.free(contents); + const contents = cie.data(elf_file); for (cie.relocs(elf_file)) |rel| { const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); @@ -359,8 +351,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, fde.data(elf_file)); - defer gpa.free(contents); + const contents = fde.data(elf_file); std.mem.writeInt( i32, @@ -382,9 +373,6 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { } pub fn writeEhFrameObject(elf_file: *Elf, writer: anytype) !void { - const comp = elf_file.base.comp; - const gpa = comp.gpa; - for (elf_file.objects.items) |index| { const object = elf_file.file(index).?.object; @@ -400,8 +388,7 @@ pub fn writeEhFrameObject(elf_file: *Elf, writer: anytype) !void { for (object.fdes.items) |fde| { if (!fde.alive) continue; - const contents = try gpa.dupe(u8, fde.data(elf_file)); - defer gpa.free(contents); + const contents = fde.data(elf_file); std.mem.writeInt( i32, diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 30222b148a67..9f03cb7852f5 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -162,18 +162,18 @@ pub const File = union(enum) { state.name_off = try ar_strtab.insert(allocator, path); } - pub fn updateArSize(file: File) void { + pub fn updateArSize(file: File, elf_file: *Elf) !void { return switch (file) { .zig_object => |x| x.updateArSize(), - .object => |x| x.updateArSize(), + .object => |x| x.updateArSize(elf_file), inline else => unreachable, }; } - pub fn writeAr(file: File, writer: anytype) !void { + pub fn writeAr(file: File, elf_file: *Elf, writer: anytype) !void { return switch (file) { .zig_object => |x| x.writeAr(writer), - .object => |x| x.writeAr(writer), + .object => |x| x.writeAr(elf_file, writer), inline else => unreachable, }; } @@ -187,6 +187,9 @@ pub const File = union(enum) { object: Object, shared_object: SharedObject, }; + + pub const Handle = std.fs.File; + pub const HandleIndex = Index; }; const std = @import("std"); diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index 68a36e14ce4e..6f76605e3126 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -1582,15 +1582,14 @@ pub const ComdatGroupSection = struct { pub fn size(cgs: ComdatGroupSection, elf_file: *Elf) usize { const cg = elf_file.comdatGroup(cgs.cg_index); - const object = cgs.file(elf_file).?.object; - const members = object.comdatGroupMembers(cg.shndx); + const members = cg.comdatGroupMembers(elf_file); return (members.len + 1) * @sizeOf(u32); } pub fn write(cgs: ComdatGroupSection, elf_file: *Elf, writer: anytype) !void { const cg = elf_file.comdatGroup(cgs.cg_index); const object = cgs.file(elf_file).?.object; - const members = object.comdatGroupMembers(cg.shndx); + const members = cg.comdatGroupMembers(elf_file); try writer.writeInt(u32, elf.GRP_COMDAT, .little); for (members) |shndx| { const shdr = object.shdrs.items[shndx]; From 616a8f9853e461400f6f5c49dede3bf3a0ca25b6 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 12 Feb 2024 23:37:05 +0100 Subject: [PATCH 2/8] elf: move code paths responsible for emitting object and archive into relocatable module --- CMakeLists.txt | 1 + src/link/Elf.zig | 550 ++--------------------------------- src/link/Elf/relocatable.zig | 520 +++++++++++++++++++++++++++++++++ 3 files changed, 544 insertions(+), 527 deletions(-) create mode 100644 src/link/Elf/relocatable.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 44c6787dcdb0..9c8da993a780 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -593,6 +593,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/link/Elf/eh_frame.zig" "${CMAKE_SOURCE_DIR}/src/link/Elf/file.zig" "${CMAKE_SOURCE_DIR}/src/link/Elf/gc.zig" + "${CMAKE_SOURCE_DIR}/src/link/Elf/relocatable.zig" "${CMAKE_SOURCE_DIR}/src/link/Elf/synthetic_sections.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO.zig" "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig" diff --git a/src/link/Elf.zig b/src/link/Elf.zig index fc8e82176f04..a21fa8ffa0d9 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -570,7 +570,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { return null; } -fn allocatedSize(self: *Elf, start: u64) u64 { +pub fn allocatedSize(self: *Elf, start: u64) u64 { if (start == 0) return 0; var min_pos: u64 = std.math.maxInt(u64); if (self.shdr_table_offset) |off| { @@ -597,7 +597,7 @@ fn allocatedVirtualSize(self: *Elf, start: u64) u64 { return min_pos - start; } -fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 { +pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 { var start: u64 = 0; while (self.detectAllocCollision(start, object_size)) |item_end| { start = mem.alignForward(u64, item_end, min_alignment); @@ -1083,8 +1083,8 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) }; if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self); - if (self.base.isStaticLib()) return self.flushStaticLib(comp, module_obj_path); - if (self.base.isObject()) return self.flushObject(comp, module_obj_path); + if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path); + if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path); // Here we will parse input positional and library files (if referenced). // This will roughly match in any linker backend we support. @@ -1388,222 +1388,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) if (comp.link_errors.items.len > 0) return error.FlushFailure; } -pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { - const gpa = comp.gpa; - - var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); - defer positionals.deinit(); - - try positionals.ensureUnusedCapacity(comp.objects.len); - positionals.appendSliceAssumeCapacity(comp.objects); - - // This is a set of object files emitted by clang in a single `build-exe` invocation. - // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up - // in this set. - for (comp.c_object_table.keys()) |key| { - try positionals.append(.{ .path = key.status.success.object_path }); - } - - if (module_obj_path) |path| try positionals.append(.{ .path = path }); - - for (positionals.items) |obj| { - self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) { - error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported - else => |e| try self.reportParseError( - obj.path, - "unexpected error: parsing input file failed with error {s}", - .{@errorName(e)}, - ), - }; - } - - if (comp.link_errors.items.len > 0) return error.FlushFailure; - - // First, we flush relocatable object file generated with our backends. - if (self.zigObjectPtr()) |zig_object| { - zig_object.resolveSymbols(self); - zig_object.claimUnresolvedObject(self); - - try self.initSymtab(); - try self.initShStrtab(); - try self.sortShdrs(); - try zig_object.addAtomsToRelaSections(self); - try self.updateSectionSizesObject(); - - try self.allocateAllocSectionsObject(); - try self.allocateNonAllocSections(); - - if (build_options.enable_logging) { - state_log.debug("{}", .{self.dumpState()}); - } - - try self.writeSyntheticSectionsObject(); - try self.writeShdrTable(); - try self.writeElfHeader(); - - // TODO we can avoid reading in the file contents we just wrote if we give the linker - // ability to write directly to a buffer. - try zig_object.readFileContents(self); - } - - var files = std.ArrayList(File.Index).init(gpa); - defer files.deinit(); - try files.ensureTotalCapacityPrecise(self.objects.items.len + 1); - if (self.zigObjectPtr()) |zig_object| files.appendAssumeCapacity(zig_object.index); - for (self.objects.items) |index| files.appendAssumeCapacity(index); - - // Update ar symtab from parsed objects - var ar_symtab: Archive.ArSymtab = .{}; - defer ar_symtab.deinit(gpa); - - for (files.items) |index| { - try self.file(index).?.updateArSymtab(&ar_symtab, self); - } - - ar_symtab.sort(); - - // Save object paths in filenames strtab. - var ar_strtab: Archive.ArStrtab = .{}; - defer ar_strtab.deinit(gpa); - - for (files.items) |index| { - const file_ptr = self.file(index).?; - try file_ptr.updateArStrtab(gpa, &ar_strtab); - try file_ptr.updateArSize(self); - } - - // Update file offsets of contributing objects. - const total_size: usize = blk: { - var pos: usize = elf.ARMAG.len; - pos += @sizeOf(elf.ar_hdr) + ar_symtab.size(.p64); - - if (ar_strtab.size() > 0) { - pos = mem.alignForward(usize, pos, 2); - pos += @sizeOf(elf.ar_hdr) + ar_strtab.size(); - } - - for (files.items) |index| { - const file_ptr = self.file(index).?; - const state = switch (file_ptr) { - .zig_object => |x| &x.output_ar_state, - .object => |x| &x.output_ar_state, - else => unreachable, - }; - pos = mem.alignForward(usize, pos, 2); - state.file_off = pos; - pos += @sizeOf(elf.ar_hdr) + (math.cast(usize, state.size) orelse return error.Overflow); - } - - break :blk pos; - }; - - if (build_options.enable_logging) { - state_log.debug("ar_symtab\n{}\n", .{ar_symtab.fmt(self)}); - state_log.debug("ar_strtab\n{}\n", .{ar_strtab}); - } - - var buffer = std.ArrayList(u8).init(gpa); - defer buffer.deinit(); - try buffer.ensureTotalCapacityPrecise(total_size); - - // Write magic - try buffer.writer().writeAll(elf.ARMAG); - - // Write symtab - try ar_symtab.write(.p64, self, buffer.writer()); - - // Write strtab - if (ar_strtab.size() > 0) { - if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0); - try ar_strtab.write(buffer.writer()); - } - - // Write object files - for (files.items) |index| { - if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0); - try self.file(index).?.writeAr(self, buffer.writer()); - } - - assert(buffer.items.len == total_size); - - try self.base.file.?.setEndPos(total_size); - try self.base.file.?.pwriteAll(buffer.items, 0); - - if (comp.link_errors.items.len > 0) return error.FlushFailure; -} - -pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { - const gpa = self.base.comp.gpa; - - var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); - defer positionals.deinit(); - try positionals.ensureUnusedCapacity(comp.objects.len); - positionals.appendSliceAssumeCapacity(comp.objects); - - // This is a set of object files emitted by clang in a single `build-exe` invocation. - // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up - // in this set. - for (comp.c_object_table.keys()) |key| { - try positionals.append(.{ .path = key.status.success.object_path }); - } - - if (module_obj_path) |path| try positionals.append(.{ .path = path }); - - for (positionals.items) |obj| { - self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) { - error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported - else => |e| try self.reportParseError( - obj.path, - "unexpected error: parsing input file failed with error {s}", - .{@errorName(e)}, - ), - }; - } - - if (comp.link_errors.items.len > 0) return error.FlushFailure; - - // Init all objects - for (self.objects.items) |index| { - try self.file(index).?.object.init(self); - } - - if (comp.link_errors.items.len > 0) return error.FlushFailure; - - // Now, we are ready to resolve the symbols across all input files. - // We will first resolve the files in the ZigObject, next in the parsed - // input Object files. - self.resolveSymbols(); - self.markEhFrameAtomsDead(); - self.claimUnresolvedObject(); - - try self.initSectionsObject(); - try self.sortShdrs(); - if (self.zigObjectPtr()) |zig_object| { - try zig_object.addAtomsToRelaSections(self); - } - for (self.objects.items) |index| { - const object = self.file(index).?.object; - try object.addAtomsToOutputSections(self); - try object.addAtomsToRelaSections(self); - } - try self.updateSectionSizesObject(); - - try self.allocateAllocSectionsObject(); - try self.allocateNonAllocSections(); - self.allocateAtoms(); - - if (build_options.enable_logging) { - state_log.debug("{}", .{self.dumpState()}); - } - - try self.writeAtomsObject(); - try self.writeSyntheticSectionsObject(); - try self.writeShdrTable(); - try self.writeElfHeader(); - - if (comp.link_errors.items.len > 0) return error.FlushFailure; -} - /// --verbose-link output fn dumpArgv(self: *Elf, comp: *Compilation) !void { const gpa = self.base.comp.gpa; @@ -1880,7 +1664,7 @@ const ParseError = error{ InvalidCharacter, } || LdScript.Error || std.os.AccessError || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError; -fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void { +pub fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void { const tracy = trace(@src()); defer tracy.end(); if (try Object.isObject(path)) { @@ -2089,7 +1873,7 @@ fn accessLibPath( /// 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) void { +pub fn resolveSymbols(self: *Elf) void { // Resolve symbols in the ZigObject. For now, we assume that it's always live. if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resolveSymbols(self); // Resolve symbols on the set of all objects and shared objects (even if some are unneeded). @@ -2173,7 +1957,7 @@ fn markLive(self: *Elf) void { } } -fn markEhFrameAtomsDead(self: *Elf) void { +pub fn markEhFrameAtomsDead(self: *Elf) void { for (self.objects.items) |index| { const file_ptr = self.file(index).?; if (!file_ptr.isAlive()) continue; @@ -2239,15 +2023,6 @@ fn claimUnresolved(self: *Elf) void { } } -fn claimUnresolvedObject(self: *Elf) void { - if (self.zigObjectPtr()) |zig_object| { - zig_object.claimUnresolvedObject(self); - } - for (self.objects.items) |index| { - self.file(index).?.object.claimUnresolvedObject(self); - } -} - /// In scanRelocs we will go over all live atoms and scan their relocs. /// This will help us work out what synthetics to emit, GOT indirection, etc. /// This is also the point where we will report undefined symbols for any @@ -2985,7 +2760,7 @@ fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) } } -fn writeShdrTable(self: *Elf) !void { +pub fn writeShdrTable(self: *Elf) !void { const gpa = self.base.comp.gpa; const target = self.base.comp.root_mod.resolved_target.result; const target_endian = target.cpu.arch.endian(); @@ -3082,7 +2857,7 @@ fn writePhdrTable(self: *Elf) !void { } } -fn writeElfHeader(self: *Elf) !void { +pub fn writeElfHeader(self: *Elf) !void { const comp = self.base.comp; if (comp.link_errors.items.len > 0) return; // We had errors, so skip flushing to render the output unusable @@ -3658,61 +3433,7 @@ fn initSyntheticSections(self: *Elf) !void { try self.initShStrtab(); } -fn initSectionsObject(self: *Elf) !void { - const ptr_size = self.ptrWidthBytes(); - - for (self.objects.items) |index| { - const object = self.file(index).?.object; - try object.initOutputSections(self); - try object.initRelaSections(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, - .offset = std.math.maxInt(u64), - }); - self.eh_frame_rela_section_index = try self.addRelaShdr(".rela.eh_frame", self.eh_frame_section_index.?); - } - - try self.initComdatGroups(); - try self.initSymtab(); - try self.initShStrtab(); -} - -fn initComdatGroups(self: *Elf) !void { - const gpa = self.base.comp.gpa; - - for (self.objects.items) |index| { - const object = self.file(index).?.object; - - for (object.comdat_groups.items) |cg_index| { - const cg = self.comdatGroup(cg_index); - const cg_owner = self.comdatGroupOwner(cg.owner); - if (cg_owner.file != index) continue; - - const cg_sec = try self.comdat_group_sections.addOne(gpa); - cg_sec.* = .{ - .shndx = try self.addSection(.{ - .name = ".group", - .type = elf.SHT_GROUP, - .entsize = @sizeOf(u32), - .addralign = @alignOf(u32), - .offset = std.math.maxInt(u64), - }), - .cg_index = cg_index, - }; - } - } -} - -fn initSymtab(self: *Elf) !void { +pub fn initSymtab(self: *Elf) !void { const small_ptr = switch (self.ptr_width) { .p32 => true, .p64 => false, @@ -3737,7 +3458,7 @@ fn initSymtab(self: *Elf) !void { } } -fn initShStrtab(self: *Elf) !void { +pub fn initShStrtab(self: *Elf) !void { if (self.shstrtab_section_index == null) { self.shstrtab_section_index = try self.addSection(.{ .name = ".shstrtab", @@ -4043,7 +3764,7 @@ fn shdrRank(self: *Elf, shndx: u16) u8 { } } -fn sortShdrs(self: *Elf) !void { +pub fn sortShdrs(self: *Elf) !void { const Entry = struct { shndx: u16, @@ -4355,58 +4076,7 @@ fn updateSectionSizes(self: *Elf) !void { self.updateShStrtabSize(); } -fn updateSectionSizesObject(self: *Elf) !void { - for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| { - const shdr = &self.shdrs.items[shndx]; - for (atom_list.items) |atom_index| { - const atom_ptr = self.atom(atom_index) orelse continue; - if (!atom_ptr.flags.alive) continue; - const offset = atom_ptr.alignment.forward(shdr.sh_size); - const padding = offset - shdr.sh_size; - atom_ptr.value = offset; - shdr.sh_size += padding + atom_ptr.size; - shdr.sh_addralign = @max(shdr.sh_addralign, atom_ptr.alignment.toByteUnits(1)); - } - } - - for (self.output_rela_sections.values()) |sec| { - const shdr = &self.shdrs.items[sec.shndx]; - for (sec.atom_list.items) |atom_index| { - const atom_ptr = self.atom(atom_index) orelse continue; - if (!atom_ptr.flags.alive) continue; - const relocs = atom_ptr.relocs(self); - shdr.sh_size += shdr.sh_entsize * relocs.len; - } - - if (shdr.sh_size == 0) shdr.sh_offset = 0; - } - - if (self.eh_frame_section_index) |index| { - self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self); - } - if (self.eh_frame_rela_section_index) |index| { - const shdr = &self.shdrs.items[index]; - shdr.sh_size = eh_frame.calcEhFrameRelocs(self) * shdr.sh_entsize; - } - - try self.updateSymtabSize(); - self.updateComdatGroupsSizes(); - self.updateShStrtabSize(); -} - -fn updateComdatGroupsSizes(self: *Elf) void { - for (self.comdat_group_sections.items) |cg| { - const shdr = &self.shdrs.items[cg.shndx]; - shdr.sh_size = cg.size(self); - shdr.sh_link = self.symtab_section_index.?; - - const sym = self.symbol(cg.symbol(self)); - shdr.sh_info = sym.outputSymtabIndex(self) orelse - self.sectionSymbolOutputSymtabIndex(sym.outputShndx().?); - } -} - -fn updateShStrtabSize(self: *Elf) void { +pub fn updateShStrtabSize(self: *Elf) void { if (self.shstrtab_section_index) |index| { self.shdrs.items[index].sh_size = self.shstrtab.items.len; } @@ -4491,7 +4161,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void { /// Allocates alloc sections and creates load segments for sections /// extracted from input object files. -fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void { +pub fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void { // We use this struct to track maximum alignment of all TLS sections. // According to https://github.com/rui314/mold/commit/bd46edf3f0fe9e1a787ea453c4657d535622e61f in mold, // in-file offsets have to be aligned against the start of TLS program header. @@ -4638,27 +4308,8 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void { } } -/// Allocates alloc sections when merging relocatable objects files together. -fn allocateAllocSectionsObject(self: *Elf) !void { - for (self.shdrs.items) |*shdr| { - if (shdr.sh_type == elf.SHT_NULL) continue; - if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; - if (shdr.sh_type == elf.SHT_NOBITS) { - shdr.sh_offset = 0; - continue; - } - const needed_size = shdr.sh_size; - if (needed_size > self.allocatedSize(shdr.sh_offset)) { - shdr.sh_size = 0; - const new_offset = self.findFreeSpace(needed_size, shdr.sh_addralign); - shdr.sh_offset = new_offset; - shdr.sh_size = needed_size; - } - } -} - /// Allocates non-alloc sections (debug info, symtabs, etc.). -fn allocateNonAllocSections(self: *Elf) !void { +pub fn allocateNonAllocSections(self: *Elf) !void { for (self.shdrs.items, 0..) |*shdr, shndx| { if (shdr.sh_type == elf.SHT_NULL) continue; if (shdr.sh_flags & elf.SHF_ALLOC != 0) continue; @@ -4757,7 +4408,7 @@ fn allocateSpecialPhdrs(self: *Elf) void { } } -fn allocateAtoms(self: *Elf) void { +pub fn allocateAtoms(self: *Elf) void { if (self.zigObjectPtr()) |zig_object| { zig_object.allocateTlvAtoms(self); } @@ -4854,76 +4505,7 @@ fn writeAtoms(self: *Elf) !void { try self.reportUndefinedSymbols(&undefs); } -fn writeAtomsObject(self: *Elf) !void { - const gpa = self.base.comp.gpa; - - // 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; - - const atom_list = self.output_sections.get(@intCast(shndx)) orelse continue; - if (atom_list.items.len == 0) continue; - - log.debug("writing atoms in '{s}' section", .{self.getShString(shdr.sh_name)}); - - // TODO really, really handle debug section separately - const base_offset = if (self.isDebugSection(@intCast(shndx))) blk: { - const zig_object = self.zigObjectPtr().?; - if (shndx == self.debug_info_section_index.?) - break :blk zig_object.debug_info_section_zig_size; - if (shndx == self.debug_abbrev_section_index.?) - break :blk zig_object.debug_abbrev_section_zig_size; - if (shndx == self.debug_str_section_index.?) - break :blk zig_object.debug_str_section_zig_size; - if (shndx == self.debug_aranges_section_index.?) - break :blk zig_object.debug_aranges_section_zig_size; - if (shndx == self.debug_line_section_index.?) - break :blk zig_object.debug_line_section_zig_size; - unreachable; - } else 0; - const sh_offset = shdr.sh_offset + base_offset; - const sh_size = math.cast(usize, shdr.sh_size - base_offset) orelse return error.Overflow; - - const buffer = try gpa.alloc(u8, sh_size); - defer gpa.free(buffer); - const padding_byte: u8 = if (shdr.sh_type == elf.SHT_PROGBITS and - shdr.sh_flags & elf.SHF_EXECINSTR != 0) - 0xcc // int3 - else - 0; - @memset(buffer, padding_byte); - - for (atom_list.items) |atom_index| { - const atom_ptr = self.atom(atom_index).?; - assert(atom_ptr.flags.alive); - - const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse - return error.Overflow; - const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow; - - log.debug("writing atom({d}) from 0x{x} to 0x{x}", .{ - atom_index, - sh_offset + offset, - sh_offset + offset + size, - }); - - // TODO decompress directly into provided buffer - const out_code = buffer[offset..][0..size]; - const in_code = switch (atom_ptr.file(self).?) { - .object => |x| try x.codeDecompressAlloc(self, atom_index), - .zig_object => |x| try x.codeAlloc(self, atom_index), - else => unreachable, - }; - defer gpa.free(in_code); - @memcpy(out_code, in_code); - } - - try self.base.file.?.pwriteAll(buffer, sh_offset); - } -} - -fn updateSymtabSize(self: *Elf) !void { +pub fn updateSymtabSize(self: *Elf) !void { var nlocals: u32 = 0; var nglobals: u32 = 0; var strsize: u32 = 0; @@ -5141,94 +4723,7 @@ fn writeSyntheticSections(self: *Elf) !void { try self.writeShStrtab(); } -fn writeSyntheticSectionsObject(self: *Elf) !void { - const gpa = self.base.comp.gpa; - - for (self.output_rela_sections.values()) |sec| { - if (sec.atom_list.items.len == 0) continue; - - const shdr = self.shdrs.items[sec.shndx]; - - const num_relocs = math.cast(usize, @divExact(shdr.sh_size, shdr.sh_entsize)) orelse - return error.Overflow; - var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs); - defer relocs.deinit(); - - for (sec.atom_list.items) |atom_index| { - const atom_ptr = self.atom(atom_index) orelse continue; - if (!atom_ptr.flags.alive) continue; - try atom_ptr.writeRelocs(self, &relocs); - } - assert(relocs.items.len == num_relocs); - - const SortRelocs = struct { - pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool { - _ = ctx; - return lhs.r_offset < rhs.r_offset; - } - }; - - mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan); - - log.debug("writing {s} from 0x{x} to 0x{x}", .{ - self.getShString(shdr.sh_name), - shdr.sh_offset, - shdr.sh_offset + shdr.sh_size, - }); - - try self.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset); - } - - if (self.eh_frame_section_index) |shndx| { - const shdr = self.shdrs.items[shndx]; - const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; - var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); - defer buffer.deinit(); - try eh_frame.writeEhFrameObject(self, buffer.writer()); - log.debug("writing .eh_frame from 0x{x} to 0x{x}", .{ - shdr.sh_offset, - shdr.sh_offset + shdr.sh_size, - }); - assert(buffer.items.len == sh_size); - try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); - } - if (self.eh_frame_rela_section_index) |shndx| { - const shdr = self.shdrs.items[shndx]; - const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; - var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); - defer buffer.deinit(); - try eh_frame.writeEhFrameRelocs(self, buffer.writer()); - assert(buffer.items.len == sh_size); - log.debug("writing .rela.eh_frame from 0x{x} to 0x{x}", .{ - shdr.sh_offset, - shdr.sh_offset + shdr.sh_size, - }); - try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); - } - - try self.writeComdatGroups(); - try self.writeSymtab(); - try self.writeShStrtab(); -} - -fn writeComdatGroups(self: *Elf) !void { - const gpa = self.base.comp.gpa; - for (self.comdat_group_sections.items) |cgs| { - const shdr = self.shdrs.items[cgs.shndx]; - const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; - var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); - defer buffer.deinit(); - try cgs.write(self, buffer.writer()); - assert(buffer.items.len == sh_size); - log.debug("writing COMDAT group from 0x{x} to 0x{x}", .{ - shdr.sh_offset, - shdr.sh_offset + shdr.sh_size, - }); - try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); - } -} - -fn writeShStrtab(self: *Elf) !void { +pub fn writeShStrtab(self: *Elf) !void { if (self.shstrtab_section_index) |index| { const shdr = self.shdrs.items[index]; log.debug("writing .shstrtab from 0x{x} to 0x{x}", .{ shdr.sh_offset, shdr.sh_offset + shdr.sh_size }); @@ -5236,7 +4731,7 @@ fn writeShStrtab(self: *Elf) !void { } } -fn writeSymtab(self: *Elf) !void { +pub fn writeSymtab(self: *Elf) !void { const target = self.base.comp.root_mod.resolved_target.result; const gpa = self.base.comp.gpa; const symtab_shdr = self.shdrs.items[self.symtab_section_index.?]; @@ -5367,7 +4862,7 @@ pub fn sectionSymbolOutputSymtabIndex(self: Elf, shndx: u32) u32 { } /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF. -fn ptrWidthBytes(self: Elf) u8 { +pub fn ptrWidthBytes(self: Elf) u8 { return switch (self.ptr_width) { .p32 => 4, .p64 => 8, @@ -5713,7 +5208,7 @@ fn addPhdr(self: *Elf, opts: struct { return index; } -fn addRelaShdr(self: *Elf, name: [:0]const u8, shndx: u16) !u16 { +pub fn addRelaShdr(self: *Elf, name: [:0]const u8, shndx: u16) !u16 { const entsize: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Rela), .p64 => @sizeOf(elf.Elf64_Rela), @@ -6340,7 +5835,7 @@ fn formatPhdr( }); } -fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) { +pub fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) { return .{ .data = self }; } @@ -6579,6 +6074,7 @@ const glibc = @import("../glibc.zig"); const link = @import("../link.zig"); const lldMain = @import("../main.zig").lldMain; const musl = @import("../musl.zig"); +const relocatable = @import("Elf/relocatable.zig"); const target_util = @import("../target.zig"); const trace = @import("../tracy.zig").trace; const synthetic_sections = @import("Elf/synthetic_sections.zig"); diff --git a/src/link/Elf/relocatable.zig b/src/link/Elf/relocatable.zig new file mode 100644 index 000000000000..f3985582e842 --- /dev/null +++ b/src/link/Elf/relocatable.zig @@ -0,0 +1,520 @@ +pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { + const gpa = comp.gpa; + + var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); + defer positionals.deinit(); + + try positionals.ensureUnusedCapacity(comp.objects.len); + positionals.appendSliceAssumeCapacity(comp.objects); + + // This is a set of object files emitted by clang in a single `build-exe` invocation. + // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up + // in this set. + for (comp.c_object_table.keys()) |key| { + try positionals.append(.{ .path = key.status.success.object_path }); + } + + if (module_obj_path) |path| try positionals.append(.{ .path = path }); + + for (positionals.items) |obj| { + elf_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) { + error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported + else => |e| try elf_file.reportParseError( + obj.path, + "unexpected error: parsing input file failed with error {s}", + .{@errorName(e)}, + ), + }; + } + + if (comp.link_errors.items.len > 0) return error.FlushFailure; + + // First, we flush relocatable object file generated with our backends. + if (elf_file.zigObjectPtr()) |zig_object| { + zig_object.resolveSymbols(elf_file); + zig_object.claimUnresolvedObject(elf_file); + + try elf_file.initSymtab(); + try elf_file.initShStrtab(); + try elf_file.sortShdrs(); + try zig_object.addAtomsToRelaSections(elf_file); + try updateSectionSizes(elf_file); + + try allocateAllocSections(elf_file); + try elf_file.allocateNonAllocSections(); + + if (build_options.enable_logging) { + state_log.debug("{}", .{elf_file.dumpState()}); + } + + try writeSyntheticSections(elf_file); + try elf_file.writeShdrTable(); + try elf_file.writeElfHeader(); + + // TODO we can avoid reading in the file contents we just wrote if we give the linker + // ability to write directly to a buffer. + try zig_object.readFileContents(elf_file); + } + + var files = std.ArrayList(File.Index).init(gpa); + defer files.deinit(); + try files.ensureTotalCapacityPrecise(elf_file.objects.items.len + 1); + if (elf_file.zigObjectPtr()) |zig_object| files.appendAssumeCapacity(zig_object.index); + for (elf_file.objects.items) |index| files.appendAssumeCapacity(index); + + // Update ar symtab from parsed objects + var ar_symtab: Archive.ArSymtab = .{}; + defer ar_symtab.deinit(gpa); + + for (files.items) |index| { + try elf_file.file(index).?.updateArSymtab(&ar_symtab, elf_file); + } + + ar_symtab.sort(); + + // Save object paths in filenames strtab. + var ar_strtab: Archive.ArStrtab = .{}; + defer ar_strtab.deinit(gpa); + + for (files.items) |index| { + const file_ptr = elf_file.file(index).?; + try file_ptr.updateArStrtab(gpa, &ar_strtab); + try file_ptr.updateArSize(elf_file); + } + + // Update file offsets of contributing objects. + const total_size: usize = blk: { + var pos: usize = elf.ARMAG.len; + pos += @sizeOf(elf.ar_hdr) + ar_symtab.size(.p64); + + if (ar_strtab.size() > 0) { + pos = mem.alignForward(usize, pos, 2); + pos += @sizeOf(elf.ar_hdr) + ar_strtab.size(); + } + + for (files.items) |index| { + const file_ptr = elf_file.file(index).?; + const state = switch (file_ptr) { + .zig_object => |x| &x.output_ar_state, + .object => |x| &x.output_ar_state, + else => unreachable, + }; + pos = mem.alignForward(usize, pos, 2); + state.file_off = pos; + pos += @sizeOf(elf.ar_hdr) + (math.cast(usize, state.size) orelse return error.Overflow); + } + + break :blk pos; + }; + + if (build_options.enable_logging) { + state_log.debug("ar_symtab\n{}\n", .{ar_symtab.fmt(elf_file)}); + state_log.debug("ar_strtab\n{}\n", .{ar_strtab}); + } + + var buffer = std.ArrayList(u8).init(gpa); + defer buffer.deinit(); + try buffer.ensureTotalCapacityPrecise(total_size); + + // Write magic + try buffer.writer().writeAll(elf.ARMAG); + + // Write symtab + try ar_symtab.write(.p64, elf_file, buffer.writer()); + + // Write strtab + if (ar_strtab.size() > 0) { + if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0); + try ar_strtab.write(buffer.writer()); + } + + // Write object files + for (files.items) |index| { + if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0); + try elf_file.file(index).?.writeAr(elf_file, buffer.writer()); + } + + assert(buffer.items.len == total_size); + + try elf_file.base.file.?.setEndPos(total_size); + try elf_file.base.file.?.pwriteAll(buffer.items, 0); + + if (comp.link_errors.items.len > 0) return error.FlushFailure; +} + +pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { + const gpa = elf_file.base.comp.gpa; + + var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); + defer positionals.deinit(); + try positionals.ensureUnusedCapacity(comp.objects.len); + positionals.appendSliceAssumeCapacity(comp.objects); + + // This is a set of object files emitted by clang in a single `build-exe` invocation. + // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up + // in this set. + for (comp.c_object_table.keys()) |key| { + try positionals.append(.{ .path = key.status.success.object_path }); + } + + if (module_obj_path) |path| try positionals.append(.{ .path = path }); + + for (positionals.items) |obj| { + elf_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) { + error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported + else => |e| try elf_file.reportParseError( + obj.path, + "unexpected error: parsing input file failed with error {s}", + .{@errorName(e)}, + ), + }; + } + + if (comp.link_errors.items.len > 0) return error.FlushFailure; + + // Init all objects + for (elf_file.objects.items) |index| { + try elf_file.file(index).?.object.init(elf_file); + } + + if (comp.link_errors.items.len > 0) return error.FlushFailure; + + // Now, we are ready to resolve the symbols across all input files. + // We will first resolve the files in the ZigObject, next in the parsed + // input Object files. + elf_file.resolveSymbols(); + elf_file.markEhFrameAtomsDead(); + claimUnresolved(elf_file); + + try initSections(elf_file); + try elf_file.sortShdrs(); + if (elf_file.zigObjectPtr()) |zig_object| { + try zig_object.addAtomsToRelaSections(elf_file); + } + for (elf_file.objects.items) |index| { + const object = elf_file.file(index).?.object; + try object.addAtomsToOutputSections(elf_file); + try object.addAtomsToRelaSections(elf_file); + } + try updateSectionSizes(elf_file); + + try allocateAllocSections(elf_file); + try elf_file.allocateNonAllocSections(); + elf_file.allocateAtoms(); + + if (build_options.enable_logging) { + state_log.debug("{}", .{elf_file.dumpState()}); + } + + try writeAtoms(elf_file); + try writeSyntheticSections(elf_file); + try elf_file.writeShdrTable(); + try elf_file.writeElfHeader(); + + if (comp.link_errors.items.len > 0) return error.FlushFailure; +} + +fn claimUnresolved(elf_file: *Elf) void { + if (elf_file.zigObjectPtr()) |zig_object| { + zig_object.claimUnresolvedObject(elf_file); + } + for (elf_file.objects.items) |index| { + elf_file.file(index).?.object.claimUnresolvedObject(elf_file); + } +} + +fn initSections(elf_file: *Elf) !void { + const ptr_size = elf_file.ptrWidthBytes(); + + for (elf_file.objects.items) |index| { + const object = elf_file.file(index).?.object; + try object.initOutputSections(elf_file); + try object.initRelaSections(elf_file); + } + + const needs_eh_frame = for (elf_file.objects.items) |index| { + if (elf_file.file(index).?.object.cies.items.len > 0) break true; + } else false; + if (needs_eh_frame) { + elf_file.eh_frame_section_index = try elf_file.addSection(.{ + .name = ".eh_frame", + .type = elf.SHT_PROGBITS, + .flags = elf.SHF_ALLOC, + .addralign = ptr_size, + .offset = std.math.maxInt(u64), + }); + elf_file.eh_frame_rela_section_index = try elf_file.addRelaShdr(".rela.eh_frame", elf_file.eh_frame_section_index.?); + } + + try initComdatGroups(elf_file); + try elf_file.initSymtab(); + try elf_file.initShStrtab(); +} + +fn initComdatGroups(elf_file: *Elf) !void { + const gpa = elf_file.base.comp.gpa; + + for (elf_file.objects.items) |index| { + const object = elf_file.file(index).?.object; + + for (object.comdat_groups.items) |cg_index| { + const cg = elf_file.comdatGroup(cg_index); + const cg_owner = elf_file.comdatGroupOwner(cg.owner); + if (cg_owner.file != index) continue; + + const cg_sec = try elf_file.comdat_group_sections.addOne(gpa); + cg_sec.* = .{ + .shndx = try elf_file.addSection(.{ + .name = ".group", + .type = elf.SHT_GROUP, + .entsize = @sizeOf(u32), + .addralign = @alignOf(u32), + .offset = std.math.maxInt(u64), + }), + .cg_index = cg_index, + }; + } + } +} + +fn updateSectionSizes(elf_file: *Elf) !void { + for (elf_file.output_sections.keys(), elf_file.output_sections.values()) |shndx, atom_list| { + const shdr = &elf_file.shdrs.items[shndx]; + for (atom_list.items) |atom_index| { + const atom_ptr = elf_file.atom(atom_index) orelse continue; + if (!atom_ptr.flags.alive) continue; + const offset = atom_ptr.alignment.forward(shdr.sh_size); + const padding = offset - shdr.sh_size; + atom_ptr.value = offset; + shdr.sh_size += padding + atom_ptr.size; + shdr.sh_addralign = @max(shdr.sh_addralign, atom_ptr.alignment.toByteUnits(1)); + } + } + + for (elf_file.output_rela_sections.values()) |sec| { + const shdr = &elf_file.shdrs.items[sec.shndx]; + for (sec.atom_list.items) |atom_index| { + const atom_ptr = elf_file.atom(atom_index) orelse continue; + if (!atom_ptr.flags.alive) continue; + const relocs = atom_ptr.relocs(elf_file); + shdr.sh_size += shdr.sh_entsize * relocs.len; + } + + if (shdr.sh_size == 0) shdr.sh_offset = 0; + } + + if (elf_file.eh_frame_section_index) |index| { + elf_file.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(elf_file); + } + if (elf_file.eh_frame_rela_section_index) |index| { + const shdr = &elf_file.shdrs.items[index]; + shdr.sh_size = eh_frame.calcEhFrameRelocs(elf_file) * shdr.sh_entsize; + } + + try elf_file.updateSymtabSize(); + updateComdatGroupsSizes(elf_file); + elf_file.updateShStrtabSize(); +} + +fn updateComdatGroupsSizes(elf_file: *Elf) void { + for (elf_file.comdat_group_sections.items) |cg| { + const shdr = &elf_file.shdrs.items[cg.shndx]; + shdr.sh_size = cg.size(elf_file); + shdr.sh_link = elf_file.symtab_section_index.?; + + const sym = elf_file.symbol(cg.symbol(elf_file)); + shdr.sh_info = sym.outputSymtabIndex(elf_file) orelse + elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx().?); + } +} + +/// Allocates alloc sections when merging relocatable objects files together. +fn allocateAllocSections(elf_file: *Elf) !void { + for (elf_file.shdrs.items) |*shdr| { + if (shdr.sh_type == elf.SHT_NULL) continue; + if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; + if (shdr.sh_type == elf.SHT_NOBITS) { + shdr.sh_offset = 0; + continue; + } + const needed_size = shdr.sh_size; + if (needed_size > elf_file.allocatedSize(shdr.sh_offset)) { + shdr.sh_size = 0; + const new_offset = elf_file.findFreeSpace(needed_size, shdr.sh_addralign); + shdr.sh_offset = new_offset; + shdr.sh_size = needed_size; + } + } +} + +fn writeAtoms(elf_file: *Elf) !void { + const gpa = elf_file.base.comp.gpa; + + // TODO iterate over `output_sections` directly + for (elf_file.shdrs.items, 0..) |shdr, shndx| { + if (shdr.sh_type == elf.SHT_NULL) continue; + if (shdr.sh_type == elf.SHT_NOBITS) continue; + + const atom_list = elf_file.output_sections.get(@intCast(shndx)) orelse continue; + if (atom_list.items.len == 0) continue; + + log.debug("writing atoms in '{s}' section", .{elf_file.getShString(shdr.sh_name)}); + + // TODO really, really handle debug section separately + const base_offset = if (elf_file.isDebugSection(@intCast(shndx))) blk: { + const zig_object = elf_file.zigObjectPtr().?; + if (shndx == elf_file.debug_info_section_index.?) + break :blk zig_object.debug_info_section_zig_size; + if (shndx == elf_file.debug_abbrev_section_index.?) + break :blk zig_object.debug_abbrev_section_zig_size; + if (shndx == elf_file.debug_str_section_index.?) + break :blk zig_object.debug_str_section_zig_size; + if (shndx == elf_file.debug_aranges_section_index.?) + break :blk zig_object.debug_aranges_section_zig_size; + if (shndx == elf_file.debug_line_section_index.?) + break :blk zig_object.debug_line_section_zig_size; + unreachable; + } else 0; + const sh_offset = shdr.sh_offset + base_offset; + const sh_size = math.cast(usize, shdr.sh_size - base_offset) orelse return error.Overflow; + + const buffer = try gpa.alloc(u8, sh_size); + defer gpa.free(buffer); + const padding_byte: u8 = if (shdr.sh_type == elf.SHT_PROGBITS and + shdr.sh_flags & elf.SHF_EXECINSTR != 0) + 0xcc // int3 + else + 0; + @memset(buffer, padding_byte); + + for (atom_list.items) |atom_index| { + const atom_ptr = elf_file.atom(atom_index).?; + assert(atom_ptr.flags.alive); + + const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse + return error.Overflow; + const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow; + + log.debug("writing atom({d}) from 0x{x} to 0x{x}", .{ + atom_index, + sh_offset + offset, + sh_offset + offset + size, + }); + + // TODO decompress directly into provided buffer + const out_code = buffer[offset..][0..size]; + const in_code = switch (atom_ptr.file(elf_file).?) { + .object => |x| try x.codeDecompressAlloc(elf_file, atom_index), + .zig_object => |x| try x.codeAlloc(elf_file, atom_index), + else => unreachable, + }; + defer gpa.free(in_code); + @memcpy(out_code, in_code); + } + + try elf_file.base.file.?.pwriteAll(buffer, sh_offset); + } +} + +fn writeSyntheticSections(elf_file: *Elf) !void { + const gpa = elf_file.base.comp.gpa; + + for (elf_file.output_rela_sections.values()) |sec| { + if (sec.atom_list.items.len == 0) continue; + + const shdr = elf_file.shdrs.items[sec.shndx]; + + const num_relocs = math.cast(usize, @divExact(shdr.sh_size, shdr.sh_entsize)) orelse + return error.Overflow; + var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs); + defer relocs.deinit(); + + for (sec.atom_list.items) |atom_index| { + const atom_ptr = elf_file.atom(atom_index) orelse continue; + if (!atom_ptr.flags.alive) continue; + try atom_ptr.writeRelocs(elf_file, &relocs); + } + assert(relocs.items.len == num_relocs); + + const SortRelocs = struct { + pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool { + _ = ctx; + return lhs.r_offset < rhs.r_offset; + } + }; + + mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan); + + log.debug("writing {s} from 0x{x} to 0x{x}", .{ + elf_file.getShString(shdr.sh_name), + shdr.sh_offset, + shdr.sh_offset + shdr.sh_size, + }); + + try elf_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset); + } + + if (elf_file.eh_frame_section_index) |shndx| { + const shdr = elf_file.shdrs.items[shndx]; + const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; + var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); + defer buffer.deinit(); + try eh_frame.writeEhFrameObject(elf_file, buffer.writer()); + log.debug("writing .eh_frame from 0x{x} to 0x{x}", .{ + shdr.sh_offset, + shdr.sh_offset + shdr.sh_size, + }); + assert(buffer.items.len == sh_size); + try elf_file.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); + } + if (elf_file.eh_frame_rela_section_index) |shndx| { + const shdr = elf_file.shdrs.items[shndx]; + const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; + var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); + defer buffer.deinit(); + try eh_frame.writeEhFrameRelocs(elf_file, buffer.writer()); + assert(buffer.items.len == sh_size); + log.debug("writing .rela.eh_frame from 0x{x} to 0x{x}", .{ + shdr.sh_offset, + shdr.sh_offset + shdr.sh_size, + }); + try elf_file.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); + } + + try writeComdatGroups(elf_file); + try elf_file.writeSymtab(); + try elf_file.writeShStrtab(); +} + +fn writeComdatGroups(elf_file: *Elf) !void { + const gpa = elf_file.base.comp.gpa; + for (elf_file.comdat_group_sections.items) |cgs| { + const shdr = elf_file.shdrs.items[cgs.shndx]; + const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; + var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); + defer buffer.deinit(); + try cgs.write(elf_file, buffer.writer()); + assert(buffer.items.len == sh_size); + log.debug("writing COMDAT group from 0x{x} to 0x{x}", .{ + shdr.sh_offset, + shdr.sh_offset + shdr.sh_size, + }); + try elf_file.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); + } +} + +const assert = std.debug.assert; +const build_options = @import("build_options"); +const eh_frame = @import("eh_frame.zig"); +const elf = std.elf; +const link = @import("../../link.zig"); +const log = std.log.scoped(.link); +const math = std.math; +const mem = std.mem; +const state_log = std.log.scoped(.link_state); +const std = @import("std"); + +const Archive = @import("Archive.zig"); +const Compilation = @import("../../Compilation.zig"); +const Elf = @import("../Elf.zig"); +const File = @import("file.zig").File; From 8bd01eb7a914416a772b365dc75d83890067e26c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 12 Feb 2024 23:59:19 +0100 Subject: [PATCH 3/8] elf: refactor archive specific object parsing logic --- src/link/Elf.zig | 18 ++++------ src/link/Elf/Object.zig | 65 ++++++++++++++++++---------------- src/link/Elf/relocatable.zig | 67 ++++++++++++++++++++++++++++++------ 3 files changed, 97 insertions(+), 53 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index a21fa8ffa0d9..9f8c6c17934e 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1075,6 +1075,10 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) // --verbose-link if (comp.verbose_link) try self.dumpArgv(comp); + if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self); + if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path); + if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path); + const csu = try CsuObjects.init(arena, comp); const compiler_rt_path: ?[]const u8 = blk: { if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; @@ -1082,10 +1086,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) break :blk null; }; - if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self); - if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path); - if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path); - // Here we will parse input positional and library files (if referenced). // This will roughly match in any linker backend we support. var positionals = std.ArrayList(Compilation.LinkObject).init(arena); @@ -1249,13 +1249,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) if (comp.link_errors.items.len > 0) return error.FlushFailure; - // Init all objects - for (self.objects.items) |index| { - try self.file(index).?.object.init(self); - } - - if (comp.link_errors.items.len > 0) return error.FlushFailure; - // Dedup shared objects { var seen_dsos = std.StringHashMap(void).init(gpa); @@ -1651,7 +1644,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { Compilation.dump_argv(argv.items); } -const ParseError = error{ +pub const ParseError = error{ MalformedObject, MalformedArchive, InvalidCpuArch, @@ -1662,6 +1655,7 @@ const ParseError = error{ FileSystem, NotSupported, InvalidCharacter, + UnknownFileType, } || LdScript.Error || std.os.AccessError || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError; pub fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void { diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 3a9d32f51c80..92ac64169af4 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -55,12 +55,26 @@ pub fn deinit(self: *Object, allocator: Allocator) void { pub fn parse(self: *Object, elf_file: *Elf) !void { const gpa = elf_file.base.comp.gpa; - const offset = if (self.archive) |ar| ar.offset else 0; const handle = elf_file.fileHandle(self.file_handle); + + try self.parseCommon(gpa, handle, elf_file); + try self.initAtoms(gpa, handle, elf_file); + try self.initSymtab(gpa, 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(gpa, handle, @as(u32, @intCast(i)), elf_file); + } +} + +fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void { + const offset = if (self.archive) |ar| ar.offset else 0; const file_size = (try handle.stat()).size; - const header_buffer = try Elf.preadAllAlloc(gpa, handle, offset, @sizeOf(elf.Elf64_Ehdr)); - defer gpa.free(header_buffer); + const header_buffer = try Elf.preadAllAlloc(allocator, handle, offset, @sizeOf(elf.Elf64_Ehdr)); + defer allocator.free(header_buffer); self.header = @as(*align(1) const elf.Elf64_Ehdr, @ptrCast(header_buffer)).*; const target = elf_file.base.comp.root_mod.resolved_target.result; @@ -87,10 +101,10 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { return error.MalformedObject; } - const shdrs_buffer = try Elf.preadAllAlloc(gpa, handle, offset + shoff, shsize); - defer gpa.free(shdrs_buffer); + const shdrs_buffer = try Elf.preadAllAlloc(allocator, handle, offset + shoff, shsize); + defer allocator.free(shdrs_buffer); const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(shdrs_buffer.ptr))[0..shnum]; - try self.shdrs.appendUnalignedSlice(gpa, shdrs); + try self.shdrs.appendUnalignedSlice(allocator, shdrs); for (self.shdrs.items) |shdr| { if (shdr.sh_type != elf.SHT_NOBITS) { @@ -101,15 +115,15 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { } } - const shstrtab = try self.preadShdrContentsAlloc(gpa, handle, self.header.?.e_shstrndx); - defer gpa.free(shstrtab); + const shstrtab = try self.preadShdrContentsAlloc(allocator, handle, self.header.?.e_shstrndx); + defer allocator.free(shstrtab); for (self.shdrs.items) |shdr| { if (shdr.sh_name >= shstrtab.len) { try elf_file.reportParseError2(self.index, "corrupt section name offset", .{}); return error.MalformedObject; } } - try self.strtab.appendSlice(gpa, shstrtab); + try self.strtab.appendSlice(allocator, shstrtab); const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { elf.SHT_SYMTAB => break @as(u16, @intCast(i)), @@ -120,8 +134,8 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { const shdr = self.shdrs.items[index]; self.first_global = shdr.sh_info; - const raw_symtab = try self.preadShdrContentsAlloc(gpa, handle, index); - defer gpa.free(raw_symtab); + const raw_symtab = try self.preadShdrContentsAlloc(allocator, handle, index); + defer allocator.free(raw_symtab); const nsyms = math.divExact(usize, raw_symtab.len, @sizeOf(elf.Elf64_Sym)) catch { try elf_file.reportParseError2(self.index, "symbol table not evenly divisible", .{}); return error.MalformedObject; @@ -129,11 +143,11 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms]; const strtab_bias = @as(u32, @intCast(self.strtab.items.len)); - const strtab = try self.preadShdrContentsAlloc(gpa, handle, shdr.sh_link); - defer gpa.free(strtab); - try self.strtab.appendSlice(gpa, strtab); + const strtab = try self.preadShdrContentsAlloc(allocator, handle, shdr.sh_link); + defer allocator.free(strtab); + try self.strtab.appendSlice(allocator, strtab); - try self.symtab.ensureUnusedCapacity(gpa, symtab.len); + try self.symtab.ensureUnusedCapacity(allocator, symtab.len); for (symtab) |sym| { const out_sym = self.symtab.addOneAssumeCapacity(); out_sym.* = sym; @@ -145,21 +159,6 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { } } -pub fn init(self: *Object, elf_file: *Elf) !void { - const gpa = elf_file.base.comp.gpa; - const handle = elf_file.fileHandle(self.file_handle); - - try self.initAtoms(gpa, handle, elf_file); - try self.initSymtab(gpa, 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(gpa, handle, @as(u32, @intCast(i)), elf_file); - } -} - fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: *Elf) !void { const shdrs = self.shdrs.items; try self.atoms.resize(allocator, shdrs.len); @@ -782,6 +781,12 @@ pub fn addAtomsToRelaSections(self: Object, elf_file: *Elf) !void { } } +pub fn parseAr(self: *Object, elf_file: *Elf) !void { + const gpa = elf_file.base.comp.gpa; + const handle = elf_file.fileHandle(self.file_handle); + try self.parseCommon(gpa, handle, elf_file); +} + pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; diff --git a/src/link/Elf/relocatable.zig b/src/link/Elf/relocatable.zig index f3985582e842..9492c30ab8e5 100644 --- a/src/link/Elf/relocatable.zig +++ b/src/link/Elf/relocatable.zig @@ -7,18 +7,20 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co try positionals.ensureUnusedCapacity(comp.objects.len); positionals.appendSliceAssumeCapacity(comp.objects); - // This is a set of object files emitted by clang in a single `build-exe` invocation. - // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up - // in this set. for (comp.c_object_table.keys()) |key| { try positionals.append(.{ .path = key.status.success.object_path }); } if (module_obj_path) |path| try positionals.append(.{ .path = path }); + if (comp.include_compiler_rt) { + try positionals.append(.{ .path = comp.compiler_rt_obj.?.full_object_path }); + } + for (positionals.items) |obj| { - elf_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) { + parsePositional(elf_file, obj.path) catch |err| switch (err) { error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported + error.UnknownFileType => try elf_file.reportParseError(obj.path, "unknown file type for an object file", .{}), else => |e| try elf_file.reportParseError( obj.path, "unexpected error: parsing input file failed with error {s}", @@ -172,13 +174,6 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const if (comp.link_errors.items.len > 0) return error.FlushFailure; - // Init all objects - for (elf_file.objects.items) |index| { - try elf_file.file(index).?.object.init(elf_file); - } - - if (comp.link_errors.items.len > 0) return error.FlushFailure; - // Now, we are ready to resolve the symbols across all input files. // We will first resolve the files in the ZigObject, next in the parsed // input Object files. @@ -214,6 +209,55 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const if (comp.link_errors.items.len > 0) return error.FlushFailure; } +fn parsePositional(elf_file: *Elf, path: []const u8) Elf.ParseError!void { + if (try Object.isObject(path)) { + try parseObject(elf_file, path); + } else if (try Archive.isArchive(path)) { + try parseArchive(elf_file, path); + } else return error.UnknownFileType; + // TODO: should we check for LD script? + // Actually, should we even unpack an archive? +} + +fn parseObject(elf_file: *Elf, path: []const u8) Elf.ParseError!void { + const gpa = elf_file.base.comp.gpa; + const handle = try std.fs.cwd().openFile(path, .{}); + const fh = try elf_file.addFileHandle(handle); + + const index = @as(File.Index, @intCast(try elf_file.files.addOne(gpa))); + elf_file.files.set(index, .{ .object = .{ + .path = try gpa.dupe(u8, path), + .file_handle = fh, + .index = index, + } }); + try elf_file.objects.append(gpa, index); + + const object = elf_file.file(index).?.object; + try object.parseAr(elf_file); +} + +fn parseArchive(elf_file: *Elf, path: []const u8) Elf.ParseError!void { + const gpa = elf_file.base.comp.gpa; + const handle = try std.fs.cwd().openFile(path, .{}); + const fh = try elf_file.addFileHandle(handle); + + var archive = Archive{}; + defer archive.deinit(gpa); + try archive.parse(elf_file, path, fh); + + const objects = try archive.objects.toOwnedSlice(gpa); + defer gpa.free(objects); + + for (objects) |extracted| { + const index = @as(File.Index, @intCast(try elf_file.files.addOne(gpa))); + elf_file.files.set(index, .{ .object = extracted }); + const object = &elf_file.files.items(.data)[index].object; + object.index = index; + try object.parseAr(elf_file); + try elf_file.objects.append(gpa, index); + } +} + fn claimUnresolved(elf_file: *Elf) void { if (elf_file.zigObjectPtr()) |zig_object| { zig_object.claimUnresolvedObject(elf_file); @@ -518,3 +562,4 @@ const Archive = @import("Archive.zig"); const Compilation = @import("../../Compilation.zig"); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; +const Object = @import("Object.zig"); From e5483b4ffc20614463e42741f132a55d58c01880 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 13 Feb 2024 10:48:10 +0100 Subject: [PATCH 4/8] elf: fix 32bit build --- src/link/Elf.zig | 4 ++-- src/link/Elf/Object.zig | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 9f8c6c17934e..7f421d5d2695 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -5903,8 +5903,8 @@ fn fmtDumpState( } /// Caller owns the memory. -pub fn preadAllAlloc(allocator: Allocator, handle: std.fs.File, offset: usize, size: usize) ![]u8 { - const buffer = try allocator.alloc(u8, size); +pub fn preadAllAlloc(allocator: Allocator, handle: std.fs.File, offset: u64, size: u64) ![]u8 { + const buffer = try allocator.alloc(u8, math.cast(usize, size) orelse return error.Overflow); errdefer allocator.free(buffer); const amt = try handle.preadAll(buffer, offset); if (amt != size) return error.InputOutput; diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 92ac64169af4..882025ab8c22 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -945,7 +945,9 @@ fn preadShdrContentsAlloc(self: Object, allocator: Allocator, handle: std.fs.Fil assert(index < self.shdrs.items.len); const offset = if (self.archive) |ar| ar.offset else 0; const shdr = self.shdrs.items[index]; - return Elf.preadAllAlloc(allocator, handle, offset + shdr.sh_offset, shdr.sh_size); + const sh_offset = math.cast(u64, shdr.sh_offset) orelse return error.Overflow; + const sh_size = math.cast(u64, shdr.sh_size) orelse return error.Overflow; + return Elf.preadAllAlloc(allocator, handle, offset + sh_offset, sh_size); } /// Caller owns the memory. From de30b30202f8327fd152dba8c74e40849c2ddad3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 13 Feb 2024 18:57:49 +0100 Subject: [PATCH 5/8] elf: scrap reader for preads when parsing archives --- src/link/Elf/Archive.zig | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index a2f83dda5a2f..dff8d09afa61 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -21,18 +21,17 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: []const u8, handle_index: Fil const handle = elf_file.fileHandle(handle_index); const size = (try handle.stat()).size; - const reader = handle.reader(); - _ = try reader.readBytesNoEof(elf.ARMAG.len); - var pos: usize = elf.ARMAG.len; while (true) { if (pos >= size) break; - if (!mem.isAligned(pos, 2)) { - try handle.seekBy(1); - pos += 1; - } + if (!mem.isAligned(pos, 2)) pos += 1; - const hdr = try reader.readStruct(elf.ar_hdr); + var hdr_buffer: [@sizeOf(elf.ar_hdr)]u8 = undefined; + { + const amt = try handle.preadAll(&hdr_buffer, pos); + if (amt != @sizeOf(elf.ar_hdr)) return error.InputOutput; + } + const hdr = @as(*align(1) const elf.ar_hdr, @ptrCast(&hdr_buffer)).*; pos += @sizeOf(elf.ar_hdr); if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) { @@ -44,7 +43,6 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: []const u8, handle_index: Fil const obj_size = try hdr.size(); defer { - _ = handle.seekBy(obj_size) catch {}; pos += obj_size; } From c22bb3805821d7ffe60e048f1efe362aad703668 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 13 Feb 2024 19:18:27 +0100 Subject: [PATCH 6/8] macho: scrap reader for preads when parsing archives --- src/link/Elf/Archive.zig | 4 +--- src/link/MachO/Archive.zig | 28 ++++++++++++---------------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index dff8d09afa61..76ab157bab51 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -42,9 +42,7 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: []const u8, handle_index: Fil } const obj_size = try hdr.size(); - defer { - pos += obj_size; - } + defer pos += obj_size; if (hdr.isSymtab() or hdr.isSymtab64()) continue; if (hdr.isStrtab()) { diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index aba31d17424f..d2c954f07797 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -24,20 +24,18 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: const handle = macho_file.getFileHandle(handle_index); const offset = if (fat_arch) |ar| ar.offset else 0; const size = if (fat_arch) |ar| ar.size else (try handle.stat()).size; - try handle.seekTo(offset); - const reader = handle.reader(); - _ = try reader.readBytesNoEof(SARMAG); - - var pos: usize = SARMAG; + var pos: usize = offset + SARMAG; while (true) { if (pos >= size) break; - if (!mem.isAligned(pos, 2)) { - try handle.seekBy(1); - pos += 1; - } + if (!mem.isAligned(pos, 2)) pos += 1; - const hdr = try reader.readStruct(ar_hdr); + var hdr_buffer: [@sizeOf(ar_hdr)]u8 = undefined; + { + const amt = try handle.preadAll(&hdr_buffer, pos); + if (amt != @sizeOf(ar_hdr)) return error.InputOutput; + } + const hdr = @as(*align(1) const ar_hdr, @ptrCast(&hdr_buffer)).*; pos += @sizeOf(ar_hdr); if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) { @@ -53,17 +51,15 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: if (try hdr.nameLength()) |len| { hdr_size -= len; const buf = try arena.allocator().alloc(u8, len); - try reader.readNoEof(buf); + const amt = try handle.preadAll(buf, pos); + if (amt != len) return error.InputOutput; pos += len; const actual_len = mem.indexOfScalar(u8, buf, @as(u8, 0)) orelse len; break :name buf[0..actual_len]; } unreachable; }; - defer { - _ = handle.seekBy(hdr_size) catch {}; - pos += hdr_size; - } + defer pos += hdr_size; if (mem.eql(u8, name, SYMDEF) or mem.eql(u8, name, SYMDEF64) or @@ -73,7 +69,7 @@ pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: const object = Object{ .archive = .{ .path = try gpa.dupe(u8, path), - .offset = offset + pos, + .offset = pos, }, .path = try gpa.dupe(u8, name), .file_handle = handle_index, From e401930fa862e3b9b3eedc8eb405c501fbf3de30 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 13 Feb 2024 16:42:28 +0100 Subject: [PATCH 7/8] elf: store relative offsets in atom and symbol --- src/link/Elf.zig | 16 ++------- src/link/Elf/Atom.zig | 39 +++++++++++++--------- src/link/Elf/Object.zig | 11 ------- src/link/Elf/Symbol.zig | 15 ++++++--- src/link/Elf/ZigObject.zig | 51 +++++++++++------------------ src/link/Elf/eh_frame.zig | 2 +- src/link/Elf/relocatable.zig | 1 - src/link/Elf/synthetic_sections.zig | 4 +-- 8 files changed, 61 insertions(+), 78 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 7f421d5d2695..7ef4684765c4 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1332,7 +1332,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) try self.sortPhdrs(); try self.allocateNonAllocSections(); self.allocateSpecialPhdrs(); - self.allocateAtoms(); self.allocateLinkerDefinedSymbols(); // Dump the state for easy debugging. @@ -1352,7 +1351,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) if (shdr.sh_type == elf.SHT_NOBITS) continue; const code = try zig_object.codeAlloc(self, atom_index); defer gpa.free(code); - const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr; + const file_offset = shdr.sh_offset + atom_ptr.value; atom_ptr.resolveRelocsAlloc(self, code) catch |err| switch (err) { // TODO error.RelaxFail, error.InvalidInstruction, error.CannotEncode => { @@ -2907,7 +2906,7 @@ pub fn writeElfHeader(self: *Elf) !void { mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian); index += 4; - const e_entry = if (self.entry_index) |entry_index| self.symbol(entry_index).value else 0; + const e_entry = if (self.entry_index) |entry_index| self.symbol(entry_index).address(.{}, self) else 0; const phdr_table_offset = if (self.phdr_table_index) |phndx| self.phdrs.items[phndx].p_offset else 0; switch (self.ptr_width) { .p32 => { @@ -4402,15 +4401,6 @@ fn allocateSpecialPhdrs(self: *Elf) void { } } -pub fn allocateAtoms(self: *Elf) void { - if (self.zigObjectPtr()) |zig_object| { - zig_object.allocateTlvAtoms(self); - } - for (self.objects.items) |index| { - self.file(index).?.object.allocateAtoms(self); - } -} - fn writeAtoms(self: *Elf) !void { const gpa = self.base.comp.gpa; @@ -4464,7 +4454,7 @@ fn writeAtoms(self: *Elf) !void { const atom_ptr = self.atom(atom_index).?; assert(atom_ptr.flags.alive); - const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse + const offset = math.cast(usize, atom_ptr.value - base_offset) orelse return error.Overflow; const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow; diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index cb3e7718b272..b1b07e569f1b 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -54,6 +54,12 @@ pub fn name(self: Atom, elf_file: *Elf) []const u8 { }; } +pub fn address(self: Atom, elf_file: *Elf) u64 { + const shndx = self.outputShndx() orelse return self.value; + const shdr = elf_file.shdrs.items[shndx]; + return shdr.sh_addr + self.value; +} + pub fn file(self: Atom, elf_file: *Elf) ?File { return elf_file.file(self.file_index); } @@ -85,14 +91,17 @@ pub fn priority(self: Atom, elf_file: *Elf) u64 { /// File offset relocation happens transparently, so it is not included in /// this calculation. pub fn capacity(self: Atom, elf_file: *Elf) u64 { - const next_value = if (elf_file.atom(self.next_index)) |next| next.value else std.math.maxInt(u32); - return next_value - self.value; + const next_addr = if (elf_file.atom(self.next_index)) |next| + next.address(elf_file) + else + std.math.maxInt(u32); + return next_addr - self.address(elf_file); } pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { // No need to keep a free list node for the last block. const next = elf_file.atom(self.next_index) orelse return false; - const cap = next.value - self.value; + const cap = next.address(elf_file) - self.address(elf_file); const ideal_cap = Elf.padToIdeal(self.size); if (cap <= ideal_cap) return false; const surplus = cap - ideal_cap; @@ -160,15 +169,15 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { atom_placement = last.atom_index; break :blk new_start_vaddr; } else { - break :blk shdr.sh_addr; + break :blk 0; } }; log.debug("allocated atom({d}) : '{s}' at 0x{x} to 0x{x}", .{ self.atom_index, self.name(elf_file), - self.value, - self.value + self.size, + self.address(elf_file), + self.address(elf_file) + self.size, }); const expand_section = if (atom_placement) |placement_index| @@ -176,7 +185,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { else true; if (expand_section) { - const needed_size = (self.value + self.size) - shdr.sh_addr; + const needed_size = self.value + self.size; try elf_file.growAllocSection(self.outputShndx().?, needed_size); last_atom_index.* = self.atom_index; @@ -301,7 +310,7 @@ pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela { } pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.Elf64_Rela)) !void { - relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); + relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) }); const file_ptr = self.file(elf_file).?; for (self.relocs(elf_file)) |rel| { @@ -322,7 +331,7 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El var r_sym: u32 = 0; switch (target.type(elf_file)) { elf.STT_SECTION => { - r_addend += @intCast(target.value); + r_addend += @intCast(target.address(.{}, elf_file)); r_sym = elf_file.sectionSymbolOutputSymtabIndex(target.outputShndx().?); }, else => { @@ -778,7 +787,7 @@ fn reportUndefined( } pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void { - relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); + relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) }); const file_ptr = self.file(elf_file).?; var stream = std.io.fixedBufferStream(code); @@ -802,7 +811,7 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void { // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ // // Address of the source atom. - const P = @as(i64, @intCast(self.value + rel.r_offset)); + const P = @as(i64, @intCast(self.address(elf_file) + rel.r_offset)); // Addend from the relocation. const A = rel.r_addend; // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub. @@ -969,7 +978,7 @@ fn resolveDynAbsReloc( ) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; - const P = self.value + rel.r_offset; + const P = self.address(elf_file) + rel.r_offset; 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; @@ -1058,7 +1067,7 @@ fn applyDynamicReloc(value: i64, elf_file: *Elf, writer: anytype) !void { } pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void { - relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); + relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) }); const file_ptr = self.file(elf_file).?; var stream = std.io.fixedBufferStream(code); @@ -1097,7 +1106,7 @@ pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: any // We will use equation format to resolve relocations: // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ // - const P = @as(i64, @intCast(self.value + rel.r_offset)); + const P = @as(i64, @intCast(self.address(elf_file) + rel.r_offset)); // Addend from the relocation. const A = rel.r_addend; // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub. @@ -1248,7 +1257,7 @@ fn format2( const atom = ctx.atom; const elf_file = ctx.elf_file; try writer.print("atom({d}) : {s} : @{x} : shdr({d}) : align({x}) : size({x})", .{ - atom.atom_index, atom.name(elf_file), atom.value, + atom.atom_index, atom.name(elf_file), atom.address(elf_file), atom.output_section_index, atom.alignment, atom.size, }); if (atom.fde_start != atom.fde_end) { diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 882025ab8c22..246fd9a1552c 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -718,21 +718,11 @@ pub fn addAtomsToOutputSections(self: *Object, elf_file: *Elf) !void { if (!gop.found_existing) gop.value_ptr.* = .{}; try gop.value_ptr.append(gpa, atom_index); } -} - -pub fn allocateAtoms(self: Object, elf_file: *Elf) void { - for (self.atoms.items) |atom_index| { - const atom = elf_file.atom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - const shdr = elf_file.shdrs.items[atom.output_section_index]; - atom.value += shdr.sh_addr; - } for (self.locals()) |local_index| { const local = elf_file.symbol(local_index); const atom = local.atom(elf_file) orelse continue; if (!atom.flags.alive) continue; - local.value += atom.value; local.output_section_index = atom.output_section_index; } @@ -741,7 +731,6 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void { const atom = global.atom(elf_file) orelse continue; if (!atom.flags.alive) continue; if (global.file(elf_file).?.index() != self.index) continue; - global.value += atom.value; global.output_section_index = atom.output_section_index; } } diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 7c7d3fd17c95..b2eee739ed77 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -104,6 +104,9 @@ pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf // Lazy-bound function it is! return symbol.pltAddress(elf_file); } + if (symbol.atom(elf_file)) |atom_ptr| { + return atom_ptr.address(elf_file) + symbol.value; + } return symbol.value; } @@ -247,11 +250,11 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { if (symbol.flags.is_canonical) break :blk symbol.address(.{}, elf_file); break :blk 0; } - if (st_shndx == elf.SHN_ABS or st_shndx == elf.SHN_COMMON) break :blk symbol.value; + if (st_shndx == elf.SHN_ABS or st_shndx == elf.SHN_COMMON) break :blk symbol.address(.{ .plt = false }, elf_file); const shdr = &elf_file.shdrs.items[st_shndx]; if (shdr.sh_flags & elf.SHF_TLS != 0 and file_ptr != .linker_defined) - break :blk symbol.value - elf_file.tlsAddress(); - break :blk symbol.value; + break :blk symbol.address(.{ .plt = false }, elf_file) - elf_file.tlsAddress(); + break :blk symbol.address(.{ .plt = false }, elf_file); }; out.st_info = (st_bind << 4) | st_type; out.st_other = esym.st_other; @@ -323,7 +326,11 @@ fn format2( _ = options; _ = unused_fmt_string; const symbol = ctx.symbol; - try writer.print("%{d} : {s} : @{x}", .{ symbol.esym_index, symbol.fmtName(ctx.elf_file), symbol.value }); + try writer.print("%{d} : {s} : @{x}", .{ + symbol.esym_index, + symbol.fmtName(ctx.elf_file), + symbol.address(.{}, ctx.elf_file), + }); if (symbol.file(ctx.elf_file)) |file_ptr| { if (symbol.isAbs(ctx.elf_file)) { if (symbol.elfSym(ctx.elf_file).st_shndx == elf.SHN_UNDEF) { diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index a132b1d453c2..6febd30fc4cf 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -401,19 +401,6 @@ pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void { } } -pub fn allocateTlvAtoms(self: ZigObject, elf_file: *Elf) void { - for (self.tls_variables.keys(), self.tls_variables.values()) |atom_index, tlv| { - const atom = elf_file.atom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - const local = elf_file.symbol(tlv.symbol_index); - const shdr = elf_file.shdrs.items[atom.output_section_index]; - atom.value += shdr.sh_addr; - local.value = atom.value; - - // TODO exported TLS vars - } -} - pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void { const gpa = elf_file.base.comp.gpa; for (self.atoms.items) |atom_index| { @@ -644,7 +631,7 @@ pub fn codeAlloc(self: ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8 return code; } - const file_offset = shdr.sh_offset + atom.value - shdr.sh_addr; + const file_offset = shdr.sh_offset + atom.value; const size = std.math.cast(usize, atom.size) orelse return error.Overflow; const code = try gpa.alloc(u8, size); errdefer gpa.free(code); @@ -664,7 +651,7 @@ pub fn getDeclVAddr( ) !u64 { const this_sym_index = try self.getOrCreateMetadataForDecl(elf_file, decl_index); const this_sym = elf_file.symbol(this_sym_index); - const vaddr = this_sym.value; + const vaddr = this_sym.address(.{}, elf_file); const parent_atom = elf_file.symbol(reloc_info.parent_atom_index).atom(elf_file).?; try parent_atom.addReloc(elf_file, .{ .r_offset = reloc_info.offset, @@ -682,7 +669,7 @@ pub fn getAnonDeclVAddr( ) !u64 { const sym_index = self.anon_decls.get(decl_val).?.symbol_index; const sym = elf_file.symbol(sym_index); - const vaddr = sym.value; + const vaddr = sym.address(.{}, elf_file); const parent_atom = elf_file.symbol(reloc_info.parent_atom_index).atom(elf_file).?; try parent_atom.addReloc(elf_file, .{ .r_offset = reloc_info.offset, @@ -941,13 +928,13 @@ fn updateDeclCode( if (old_size > 0 and elf_file.base.child_pid == null) { const capacity = atom_ptr.capacity(elf_file); - const need_realloc = code.len > capacity or !required_alignment.check(sym.value); + const need_realloc = code.len > capacity or !required_alignment.check(atom_ptr.value); if (need_realloc) { try atom_ptr.grow(elf_file); log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, old_vaddr, atom_ptr.value }); if (old_vaddr != atom_ptr.value) { - sym.value = atom_ptr.value; - esym.st_value = atom_ptr.value; + sym.value = 0; + esym.st_value = 0; if (!elf_file.base.isRelocatable()) { log.debug(" (writing new offset table entry)", .{}); @@ -963,9 +950,9 @@ fn updateDeclCode( try atom_ptr.allocate(elf_file); errdefer self.freeDeclMetadata(elf_file, sym_index); - sym.value = atom_ptr.value; + sym.value = 0; sym.flags.needs_zig_got = true; - esym.st_value = atom_ptr.value; + esym.st_value = 0; if (!elf_file.base.isRelocatable()) { const gop = try sym.getOrCreateZigGotEntry(sym_index, elf_file); @@ -981,7 +968,7 @@ fn updateDeclCode( .iov_len = code.len, }}; var remote_vec: [1]std.os.iovec_const = .{.{ - .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.value)))), + .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.address(.{}, elf_file))))), .iov_len = code.len, }}; const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0); @@ -996,7 +983,7 @@ fn updateDeclCode( const shdr = elf_file.shdrs.items[shdr_index]; if (shdr.sh_type != elf.SHT_NOBITS) { - const file_offset = shdr.sh_offset + sym.value - shdr.sh_addr; + const file_offset = shdr.sh_offset + atom_ptr.value; try elf_file.base.file.?.pwriteAll(code, file_offset); } } @@ -1022,12 +1009,14 @@ fn updateTlv( const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index]; const atom_ptr = sym.atom(elf_file).?; + sym.value = 0; sym.output_section_index = shndx; atom_ptr.output_section_index = shndx; sym.name_offset = try self.strtab.insert(gpa, decl_name); atom_ptr.flags.alive = true; atom_ptr.name_offset = sym.name_offset; + esym.st_value = 0; esym.st_name = sym.name_offset; esym.st_info = elf.STT_TLS; esym.st_size = code.len; @@ -1117,7 +1106,7 @@ pub fn updateFunc( try self.dwarf.?.commitDeclState( mod, decl_index, - sym.value, + sym.address(.{}, elf_file), sym.atom(elf_file).?.size, ds, ); @@ -1202,7 +1191,7 @@ pub fn updateDecl( try self.dwarf.?.commitDeclState( mod, decl_index, - sym.value, + sym.address(.{}, elf_file), sym.atom(elf_file).?.size, ds, ); @@ -1281,9 +1270,9 @@ fn updateLazySymbol( try atom_ptr.allocate(elf_file); errdefer self.freeDeclMetadata(elf_file, symbol_index); - local_sym.value = atom_ptr.value; + local_sym.value = 0; local_sym.flags.needs_zig_got = true; - local_esym.st_value = atom_ptr.value; + local_esym.st_value = 0; if (!elf_file.base.isRelocatable()) { const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, elf_file); @@ -1291,7 +1280,7 @@ fn updateLazySymbol( } const shdr = elf_file.shdrs.items[output_section_index]; - const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr; + const file_offset = shdr.sh_offset + atom_ptr.value; try elf_file.base.file.?.pwriteAll(code, file_offset); } @@ -1384,11 +1373,11 @@ fn lowerConst( // TODO rename and re-audit this method errdefer self.freeDeclMetadata(elf_file, sym_index); - local_sym.value = atom_ptr.value; - local_esym.st_value = atom_ptr.value; + local_sym.value = 0; + local_esym.st_value = 0; const shdr = elf_file.shdrs.items[output_section_index]; - const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr; + const file_offset = shdr.sh_offset + atom_ptr.value; try elf_file.base.file.?.pwriteAll(code, file_offset); return .{ .ok = sym_index }; diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index 4825ddb4764d..648c3d45f390 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -409,7 +409,7 @@ fn emitReloc(elf_file: *Elf, rec: anytype, sym: *const Symbol, rel: elf.Elf64_Re var r_sym: u32 = 0; switch (sym.type(elf_file)) { elf.STT_SECTION => { - r_addend += @intCast(sym.value); + r_addend += @intCast(sym.address(.{}, elf_file)); r_sym = elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx().?); }, else => { diff --git a/src/link/Elf/relocatable.zig b/src/link/Elf/relocatable.zig index 9492c30ab8e5..017329dde773 100644 --- a/src/link/Elf/relocatable.zig +++ b/src/link/Elf/relocatable.zig @@ -195,7 +195,6 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const try allocateAllocSections(elf_file); try elf_file.allocateNonAllocSections(); - elf_file.allocateAtoms(); if (build_options.enable_logging) { state_log.debug("{}", .{elf_file.dumpState()}); diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index 6f76605e3126..47e10281fa74 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -297,7 +297,7 @@ pub const ZigGotSection = struct { const off = zig_got.entryOffset(index, elf_file); const vaddr = zig_got.entryAddress(index, elf_file); const entry = zig_got.entries.items[index]; - const value = elf_file.symbol(entry).value; + const value = elf_file.symbol(entry).address(.{}, elf_file); switch (entry_size) { 2 => { var buf: [2]u8 = undefined; @@ -1004,7 +1004,7 @@ pub const GotPltSection = struct { { // [0]: _DYNAMIC const symbol = elf_file.symbol(elf_file.dynamic_index.?); - try writer.writeInt(u64, symbol.value, .little); + try writer.writeInt(u64, symbol.address(.{}, elf_file), .little); } // [1]: 0x0 // [2]: 0x0 From 216a5594f62aca7524012e0a2bbf226c4064fae4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 13 Feb 2024 17:24:46 +0100 Subject: [PATCH 8/8] elf: use u32 for all section indexes --- src/link/Elf.zig | 114 ++++++++++++++-------------- src/link/Elf/Atom.zig | 4 +- src/link/Elf/Object.zig | 10 +-- src/link/Elf/Symbol.zig | 10 +-- src/link/Elf/ZigObject.zig | 8 +- src/link/Elf/synthetic_sections.zig | 10 +-- 6 files changed, 78 insertions(+), 78 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 7ef4684765c4..248db26b0167 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -138,40 +138,40 @@ comdat_group_sections: std.ArrayListUnmanaged(ComdatGroupSection) = .{}, /// Tracked section headers with incremental updates to Zig object. /// .rela.* sections are only used when emitting a relocatable object file. -zig_text_section_index: ?u16 = null, -zig_data_rel_ro_section_index: ?u16 = null, -zig_data_section_index: ?u16 = null, -zig_bss_section_index: ?u16 = null, -zig_got_section_index: ?u16 = null, - -debug_info_section_index: ?u16 = null, -debug_abbrev_section_index: ?u16 = null, -debug_str_section_index: ?u16 = null, -debug_aranges_section_index: ?u16 = null, -debug_line_section_index: ?u16 = null, - -copy_rel_section_index: ?u16 = null, -dynamic_section_index: ?u16 = null, -dynstrtab_section_index: ?u16 = null, -dynsymtab_section_index: ?u16 = null, -eh_frame_section_index: ?u16 = null, -eh_frame_rela_section_index: ?u16 = null, -eh_frame_hdr_section_index: ?u16 = null, -hash_section_index: ?u16 = null, -gnu_hash_section_index: ?u16 = null, -got_section_index: ?u16 = null, -got_plt_section_index: ?u16 = null, -interp_section_index: ?u16 = null, -plt_section_index: ?u16 = null, -plt_got_section_index: ?u16 = null, -rela_dyn_section_index: ?u16 = null, -rela_plt_section_index: ?u16 = null, -versym_section_index: ?u16 = null, -verneed_section_index: ?u16 = null, - -shstrtab_section_index: ?u16 = null, -strtab_section_index: ?u16 = null, -symtab_section_index: ?u16 = null, +zig_text_section_index: ?u32 = null, +zig_data_rel_ro_section_index: ?u32 = null, +zig_data_section_index: ?u32 = null, +zig_bss_section_index: ?u32 = null, +zig_got_section_index: ?u32 = null, + +debug_info_section_index: ?u32 = null, +debug_abbrev_section_index: ?u32 = null, +debug_str_section_index: ?u32 = null, +debug_aranges_section_index: ?u32 = null, +debug_line_section_index: ?u32 = null, + +copy_rel_section_index: ?u32 = null, +dynamic_section_index: ?u32 = null, +dynstrtab_section_index: ?u32 = null, +dynsymtab_section_index: ?u32 = null, +eh_frame_section_index: ?u32 = null, +eh_frame_rela_section_index: ?u32 = null, +eh_frame_hdr_section_index: ?u32 = null, +hash_section_index: ?u32 = null, +gnu_hash_section_index: ?u32 = null, +got_section_index: ?u32 = null, +got_plt_section_index: ?u32 = null, +interp_section_index: ?u32 = null, +plt_section_index: ?u32 = null, +plt_got_section_index: ?u32 = null, +rela_dyn_section_index: ?u32 = null, +rela_plt_section_index: ?u32 = null, +versym_section_index: ?u32 = null, +verneed_section_index: ?u32 = null, + +shstrtab_section_index: ?u32 = null, +strtab_section_index: ?u32 = null, +symtab_section_index: ?u32 = null, // Linker-defined symbols dynamic_index: ?Symbol.Index = null, @@ -931,7 +931,7 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { try self.base.file.?.pwriteAll(&[1]u8{0}, end_pos); } -pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { +pub fn growAllocSection(self: *Elf, shdr_index: u32, needed_size: u64) !void { const shdr = &self.shdrs.items[shdr_index]; const maybe_phdr = if (self.phdr_to_shdr_table.get(shdr_index)) |phndx| &self.phdrs.items[phndx] else null; const is_zerofill = shdr.sh_type == elf.SHT_NOBITS; @@ -981,7 +981,7 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { pub fn growNonAllocSection( self: *Elf, - shdr_index: u16, + shdr_index: u32, needed_size: u64, min_alignment: u32, requires_file_copy: bool, @@ -1018,7 +1018,7 @@ pub fn growNonAllocSection( self.markDirty(shdr_index); } -pub fn markDirty(self: *Elf, shdr_index: u16) void { +pub fn markDirty(self: *Elf, shdr_index: u32) void { const zig_object = self.zigObjectPtr().?; if (zig_object.dwarf) |_| { if (self.debug_info_section_index.? == shdr_index) { @@ -2969,7 +2969,7 @@ pub fn writeElfHeader(self: *Elf) !void { mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian); index += 2; - mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_section_index.?, endian); + mem.writeInt(u16, hdr_buf[index..][0..2], @intCast(self.shstrtab_section_index.?), endian); index += 2; assert(index == e_ehsize); @@ -3709,7 +3709,7 @@ fn sortPhdrs(self: *Elf) error{OutOfMemory}!void { } } -fn shdrRank(self: *Elf, shndx: u16) u8 { +fn shdrRank(self: *Elf, shndx: u32) u8 { const shdr = self.shdrs.items[shndx]; const name = self.getShString(shdr.sh_name); const flags = shdr.sh_flags; @@ -3759,7 +3759,7 @@ fn shdrRank(self: *Elf, shndx: u16) u8 { pub fn sortShdrs(self: *Elf) !void { const Entry = struct { - shndx: u16, + shndx: u32, pub fn lessThan(elf_file: *Elf, lhs: @This(), rhs: @This()) bool { return elf_file.shdrRank(lhs.shndx) < elf_file.shdrRank(rhs.shndx); @@ -3770,15 +3770,15 @@ pub fn sortShdrs(self: *Elf) !void { var entries = try std.ArrayList(Entry).initCapacity(gpa, self.shdrs.items.len); defer entries.deinit(); for (0..self.shdrs.items.len) |shndx| { - entries.appendAssumeCapacity(.{ .shndx = @as(u16, @intCast(shndx)) }); + entries.appendAssumeCapacity(.{ .shndx = @intCast(shndx) }); } mem.sort(Entry, entries.items, self, Entry.lessThan); - const backlinks = try gpa.alloc(u16, entries.items.len); + const backlinks = try gpa.alloc(u32, entries.items.len); defer gpa.free(backlinks); for (entries.items, 0..) |entry, i| { - backlinks[entry.shndx] = @as(u16, @intCast(i)); + backlinks[entry.shndx] = @intCast(i); } const slice = try self.shdrs.toOwnedSlice(gpa); @@ -3792,10 +3792,10 @@ pub fn sortShdrs(self: *Elf) !void { try self.resetShdrIndexes(backlinks); } -fn resetShdrIndexes(self: *Elf, backlinks: []const u16) !void { +fn resetShdrIndexes(self: *Elf, backlinks: []const u32) !void { const gpa = self.base.comp.gpa; - for (&[_]*?u16{ + for (&[_]*?u32{ &self.eh_frame_section_index, &self.eh_frame_rela_section_index, &self.eh_frame_hdr_section_index, @@ -4191,7 +4191,7 @@ pub fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void { // virtual and file offsets. However, the simple one will do for one // as we are more interested in quick turnaround and compatibility // with `findFreeSpace` mechanics than anything else. - const Cover = std.ArrayList(u16); + const Cover = std.ArrayList(u32); const gpa = self.base.comp.gpa; var covers: [max_number_of_object_segments]Cover = undefined; for (&covers) |*cover| { @@ -4347,7 +4347,7 @@ pub fn allocateNonAllocSections(self: *Elf) !void { } fn allocateSpecialPhdrs(self: *Elf) void { - for (&[_]struct { ?u16, ?u16 }{ + for (&[_]struct { ?u16, ?u32 }{ .{ self.phdr_interp_index, self.interp_section_index }, .{ self.phdr_dynamic_index, self.dynamic_section_index }, .{ self.phdr_gnu_eh_frame_index, self.eh_frame_hdr_section_index }, @@ -4370,7 +4370,7 @@ fn allocateSpecialPhdrs(self: *Elf) void { if (self.phdr_tls_index) |index| { const slice = self.shdrs.items; const phdr = &self.phdrs.items[index]; - var shndx: u16 = 0; + var shndx: u32 = 0; while (shndx < slice.len) { const shdr = slice[shndx]; if (shdr.sh_flags & elf.SHF_TLS == 0) { @@ -5138,8 +5138,8 @@ const CsuObjects = struct { } }; -pub fn isZigSection(self: Elf, shndx: u16) bool { - inline for (&[_]?u16{ +pub fn isZigSection(self: Elf, shndx: u32) bool { + inline for (&[_]?u32{ self.zig_text_section_index, self.zig_data_rel_ro_section_index, self.zig_data_section_index, @@ -5153,8 +5153,8 @@ pub fn isZigSection(self: Elf, shndx: u16) bool { return false; } -pub fn isDebugSection(self: Elf, shndx: u16) bool { - inline for (&[_]?u16{ +pub fn isDebugSection(self: Elf, shndx: u32) bool { + inline for (&[_]?u32{ self.debug_info_section_index, self.debug_abbrev_section_index, self.debug_str_section_index, @@ -5192,7 +5192,7 @@ fn addPhdr(self: *Elf, opts: struct { return index; } -pub fn addRelaShdr(self: *Elf, name: [:0]const u8, shndx: u16) !u16 { +pub fn addRelaShdr(self: *Elf, name: [:0]const u8, shndx: u32) !u32 { const entsize: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Rela), .p64 => @sizeOf(elf.Elf64_Rela), @@ -5223,9 +5223,9 @@ pub const AddSectionOpts = struct { offset: u64 = 0, }; -pub fn addSection(self: *Elf, opts: AddSectionOpts) !u16 { +pub fn addSection(self: *Elf, opts: AddSectionOpts) !u32 { const gpa = self.base.comp.gpa; - const index = @as(u16, @intCast(self.shdrs.items.len)); + const index = @as(u32, @intCast(self.shdrs.items.len)); const shdr = try self.shdrs.addOne(gpa); shdr.* = .{ .sh_name = try self.insertShString(opts.name), @@ -5242,10 +5242,10 @@ pub fn addSection(self: *Elf, opts: AddSectionOpts) !u16 { return index; } -pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 { +pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u32 { for (self.shdrs.items, 0..) |*shdr, i| { const this_name = self.getShString(shdr.sh_name); - if (mem.eql(u8, this_name, name)) return @as(u16, @intCast(i)); + if (mem.eql(u8, this_name, name)) return @intCast(i); } else return null; } diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index b1b07e569f1b..330544d8bbfd 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -17,7 +17,7 @@ alignment: Alignment = .@"1", input_section_index: u32 = 0, /// Index of the output section. -output_section_index: u16 = 0, +output_section_index: u32 = 0, /// Index of the input section containing this atom's relocs. relocs_section_index: u32 = 0, @@ -77,7 +77,7 @@ pub fn relocsShndx(self: Atom) ?u32 { return self.relocs_section_index; } -pub fn outputShndx(self: Atom) ?u16 { +pub fn outputShndx(self: Atom) ?u32 { if (self.output_section_index == 0) return null; return self.output_section_index; } diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 246fd9a1552c..395e6680a121 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -126,7 +126,7 @@ fn parseCommon(self: *Object, allocator: Allocator, handle: std.fs.File, elf_fil try self.strtab.appendSlice(allocator, shstrtab); const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { - elf.SHT_SYMTAB => break @as(u16, @intCast(i)), + elf.SHT_SYMTAB => break @as(u32, @intCast(i)), else => {}, } else null; @@ -223,7 +223,7 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file: => {}, else => { - const shndx = @as(u16, @intCast(i)); + const shndx = @as(u32, @intCast(i)); if (self.skipShdr(shndx, elf_file)) continue; try self.addAtom(allocator, handle, shdr, shndx, elf_file); }, @@ -268,7 +268,7 @@ fn addAtom(self: *Object, allocator: Allocator, handle: std.fs.File, shdr: elf.E } } -fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) error{OutOfMemory}!u16 { +fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) error{OutOfMemory}!u32 { const name = blk: { const name = self.getString(shdr.sh_name); if (elf_file.base.isRelocatable()) break :blk name; @@ -316,7 +316,7 @@ fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) error{O return out_shndx; } -fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool { +fn skipShdr(self: *Object, index: u32, elf_file: *Elf) bool { const comp = elf_file.base.comp; const shdr = self.shdrs.items[index]; const name = self.getString(shdr.sh_name); @@ -673,7 +673,7 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { var sh_flags: u32 = elf.SHF_ALLOC | elf.SHF_WRITE; if (is_tls) sh_flags |= elf.SHF_TLS; - const shndx = @as(u16, @intCast(self.shdrs.items.len)); + const shndx = @as(u32, @intCast(self.shdrs.items.len)); const shdr = try self.shdrs.addOne(gpa); const sh_size = math.cast(usize, this_sym.st_size) orelse return error.Overflow; shdr.* = .{ diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index b2eee739ed77..ea1b4f4b6b3d 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -15,7 +15,7 @@ file_index: File.Index = 0, atom_index: Atom.Index = 0, /// Assigned output section index for this atom. -output_section_index: u16 = 0, +output_section_index: u32 = 0, /// Index of the source symbol this symbol references. /// Use `elfSym` to pull the source symbol from the relevant file. @@ -37,7 +37,7 @@ pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool { file_ptr != .linker_defined; } -pub fn outputShndx(symbol: Symbol) ?u16 { +pub fn outputShndx(symbol: Symbol) ?u32 { if (symbol.output_section_index == 0) return null; return symbol.output_section_index; } @@ -237,12 +237,12 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { if (file_ptr == .shared_object) break :blk elf.STB_GLOBAL; break :blk esym.st_bind(); }; - const st_shndx = blk: { - if (symbol.flags.has_copy_rel) break :blk elf_file.copy_rel_section_index.?; + const st_shndx: u16 = blk: { + if (symbol.flags.has_copy_rel) break :blk @intCast(elf_file.copy_rel_section_index.?); if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; if (elf_file.base.isRelocatable() and esym.st_shndx == elf.SHN_COMMON) break :blk elf.SHN_COMMON; if (symbol.atom(elf_file) == null and file_ptr != .linker_defined) break :blk elf.SHN_ABS; - break :blk symbol.outputShndx() orelse elf.SHN_UNDEF; + break :blk @intCast(symbol.outputShndx() orelse elf.SHN_UNDEF); }; const st_value = blk: { if (symbol.flags.has_copy_rel) break :blk symbol.address(.{}, elf_file); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 6febd30fc4cf..93673954604f 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -840,7 +840,7 @@ fn getDeclShdrIndex( elf_file: *Elf, decl: *const Module.Decl, code: []const u8, -) error{OutOfMemory}!u16 { +) error{OutOfMemory}!u32 { _ = self; const mod = elf_file.base.comp.module.?; const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded; @@ -894,7 +894,7 @@ fn updateDeclCode( elf_file: *Elf, decl_index: InternPool.DeclIndex, sym_index: Symbol.Index, - shdr_index: u16, + shdr_index: u32, code: []const u8, stt_bits: u8, ) !void { @@ -993,7 +993,7 @@ fn updateTlv( elf_file: *Elf, decl_index: InternPool.DeclIndex, sym_index: Symbol.Index, - shndx: u16, + shndx: u32, code: []const u8, ) !void { const gpa = elf_file.base.comp.gpa; @@ -1334,7 +1334,7 @@ fn lowerConst( name: []const u8, tv: TypedValue, required_alignment: InternPool.Alignment, - output_section_index: u16, + output_section_index: u32, src_loc: Module.SrcLoc, ) !LowerConstResult { const gpa = elf_file.base.comp.gpa; diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index 47e10281fa74..93516764bc51 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -388,7 +388,7 @@ pub const ZigGotSection = struct { .st_name = st_name, .st_info = elf.STT_OBJECT, .st_other = 0, - .st_shndx = elf_file.zig_got_section_index.?, + .st_shndx = @intCast(elf_file.zig_got_section_index.?), .st_value = st_value, .st_size = st_size, }; @@ -813,7 +813,7 @@ pub const GotSection = struct { .st_name = st_name, .st_info = elf.STT_OBJECT, .st_other = 0, - .st_shndx = elf_file.got_section_index.?, + .st_shndx = @intCast(elf_file.got_section_index.?), .st_value = st_value, .st_size = st_size, }; @@ -953,7 +953,7 @@ pub const PltSection = struct { .st_name = st_name, .st_info = elf.STT_FUNC, .st_other = 0, - .st_shndx = elf_file.plt_section_index.?, + .st_shndx = @intCast(elf_file.plt_section_index.?), .st_value = sym.pltAddress(elf_file), .st_size = 16, }; @@ -1082,7 +1082,7 @@ pub const PltGotSection = struct { .st_name = st_name, .st_info = elf.STT_FUNC, .st_other = 0, - .st_shndx = elf_file.plt_got_section_index.?, + .st_shndx = @intCast(elf_file.plt_got_section_index.?), .st_value = sym.pltGotAddress(elf_file), .st_size = 16, }; @@ -1132,7 +1132,7 @@ pub const CopyRelSection = struct { } } - pub fn updateSectionSize(copy_rel: CopyRelSection, shndx: u16, elf_file: *Elf) !void { + pub fn updateSectionSize(copy_rel: CopyRelSection, shndx: u32, elf_file: *Elf) !void { const shdr = &elf_file.shdrs.items[shndx]; for (copy_rel.symbols.items) |sym_index| { const symbol = elf_file.symbol(sym_index);