From 1ba3fc90bef145310e09026e7e9e09623117a800 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 11 Oct 2024 23:28:31 -0700 Subject: [PATCH 1/5] link.Elf: eliminate an O(N^2) algorithm in flush() Make shared_objects a StringArrayHashMap so that deduping does not need to happen in flush. That deduping code also was using an O(N^2) algorithm, which is not allowed in this codebase. There is another violation of this rule in resolveSymbols but this commit does not address it. This required reworking shared object parsing, breaking it into independent components so that we could access soname earlier. Shared object parsing had a few problems that I noticed and fixed in this commit: * Many instances of incorrect use of align(1). * `shnum * @sizeOf(elf.Elf64_Shdr)` can overflow based on user data. * `@divExact` can cause illegal behavior based on user data. * Strange versyms logic that wasn't present in mold nor lld. The logic was not commented and there is no git blame information in ziglang/zig nor kubkon/zld. I changed it to match mold and lld instead. * Use of ArrayList for slices of memory that are never resized. * finding DT_VERDEFNUM in a different loop than finding DT_SONAME. Ultimately I think we should follow mold's lead and ignore this integer, relying on null termination instead. * Doing logic based on VER_FLG_BASE rather than ignoring it like mold and LLD do. No comment explaining why the behavior is different. * Mutating the original ELF symbols rather than only storing the mangled name on the new Symbol struct. I noticed something that I didn't try to address in this commit: Symbol stores a lot of redundant information that is already present in the ELF symbols. I suspect that the codebase could benefit from reworking Symbol to not store redundant information. Additionally: * Add some type safety to std.elf. * Eliminate 1-3 file system reads for determining the kind of input files, by taking advantage of file name extension and handling error codes properly. * Move more error handling methods to link.Diags and make them infallible and thread-safe * Make the data dependencies obvious in the parameters of parseSharedObject. It's now clear that the first two steps (Header and Parsed) can be done during the main Compilation pipeline, rather than waiting for flush(). --- lib/std/Build/Cache.zig | 8 + lib/std/elf.zig | 299 ++++++++-------- lib/std/os/linux/vdso.zig | 20 +- src/Compilation.zig | 2 + src/link.zig | 110 ++++-- src/link/Elf.zig | 366 ++++++++++---------- src/link/Elf/Archive.zig | 12 +- src/link/Elf/Atom.zig | 1 + src/link/Elf/LdScript.zig | 6 +- src/link/Elf/Object.zig | 9 +- src/link/Elf/SharedObject.zig | 510 +++++++++++++++------------- src/link/Elf/Symbol.zig | 9 +- src/link/Elf/ZigObject.zig | 9 +- src/link/Elf/relocatable.zig | 34 +- src/link/Elf/synthetic_sections.zig | 38 ++- src/link/MachO.zig | 37 +- src/link/MachO/Archive.zig | 3 +- src/link/MachO/relocatable.zig | 20 +- 18 files changed, 781 insertions(+), 712 deletions(-) diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 53f1dcff294160b37c34bf7f334d95c3d666370e..bd98634ba89a5fda98b90fa65760bfa4672653a4 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -150,6 +150,14 @@ pub const File = struct { inode: fs.File.INode, size: u64, mtime: i128, + + pub fn fromFs(fs_stat: fs.File.Stat) Stat { + return .{ + .inode = fs_stat.inode, + .size = fs_stat.size, + .mtime = fs_stat.mtime, + }; + } }; pub fn deinit(self: *File, gpa: Allocator) void { diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 792694dffbddf8ccf999b2b575f10688ceea3711..f61114b53d34614b009be4bad02c7a2fad97b178 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -258,17 +258,26 @@ pub const DF_1_SINGLETON = 0x02000000; pub const DF_1_STUB = 0x04000000; pub const DF_1_PIE = 0x08000000; -pub const VERSYM_HIDDEN = 0x8000; -pub const VERSYM_VERSION = 0x7fff; +pub const Versym = packed struct(u16) { + VERSION: u15, + HIDDEN: bool, -/// Symbol is local -pub const VER_NDX_LOCAL = 0; -/// Symbol is global -pub const VER_NDX_GLOBAL = 1; -/// Beginning of reserved entries -pub const VER_NDX_LORESERVE = 0xff00; -/// Symbol is to be eliminated -pub const VER_NDX_ELIMINATE = 0xff01; + pub const LOCAL: Versym = @bitCast(@intFromEnum(VER_NDX.LOCAL)); + pub const GLOBAL: Versym = @bitCast(@intFromEnum(VER_NDX.GLOBAL)); +}; + +pub const VER_NDX = enum(u16) { + /// Symbol is local + LOCAL = 0, + /// Symbol is global + GLOBAL = 1, + /// Beginning of reserved entries + LORESERVE = 0xff00, + /// Symbol is to be eliminated + ELIMINATE = 0xff01, + UNSPECIFIED = 0xffff, + _, +}; /// Version definition of the file itself pub const VER_FLG_BASE = 1; @@ -698,12 +707,9 @@ pub const EI_PAD = 9; pub const EI_NIDENT = 16; -pub const Elf32_Half = u16; -pub const Elf64_Half = u16; -pub const Elf32_Word = u32; -pub const Elf32_Sword = i32; -pub const Elf64_Word = u32; -pub const Elf64_Sword = i32; +pub const Half = u16; +pub const Word = u32; +pub const Sword = i32; pub const Elf32_Xword = u64; pub const Elf32_Sxword = i64; pub const Elf64_Xword = u64; @@ -714,53 +720,51 @@ pub const Elf32_Off = u32; pub const Elf64_Off = u64; pub const Elf32_Section = u16; pub const Elf64_Section = u16; -pub const Elf32_Versym = Elf32_Half; -pub const Elf64_Versym = Elf64_Half; pub const Elf32_Ehdr = extern struct { e_ident: [EI_NIDENT]u8, e_type: ET, e_machine: EM, - e_version: Elf32_Word, + e_version: Word, e_entry: Elf32_Addr, e_phoff: Elf32_Off, e_shoff: Elf32_Off, - e_flags: Elf32_Word, - e_ehsize: Elf32_Half, - e_phentsize: Elf32_Half, - e_phnum: Elf32_Half, - e_shentsize: Elf32_Half, - e_shnum: Elf32_Half, - e_shstrndx: Elf32_Half, + e_flags: Word, + e_ehsize: Half, + e_phentsize: Half, + e_phnum: Half, + e_shentsize: Half, + e_shnum: Half, + e_shstrndx: Half, }; pub const Elf64_Ehdr = extern struct { e_ident: [EI_NIDENT]u8, e_type: ET, e_machine: EM, - e_version: Elf64_Word, + e_version: Word, e_entry: Elf64_Addr, e_phoff: Elf64_Off, e_shoff: Elf64_Off, - e_flags: Elf64_Word, - e_ehsize: Elf64_Half, - e_phentsize: Elf64_Half, - e_phnum: Elf64_Half, - e_shentsize: Elf64_Half, - e_shnum: Elf64_Half, - e_shstrndx: Elf64_Half, + e_flags: Word, + e_ehsize: Half, + e_phentsize: Half, + e_phnum: Half, + e_shentsize: Half, + e_shnum: Half, + e_shstrndx: Half, }; pub const Elf32_Phdr = extern struct { - p_type: Elf32_Word, + p_type: Word, p_offset: Elf32_Off, p_vaddr: Elf32_Addr, p_paddr: Elf32_Addr, - p_filesz: Elf32_Word, - p_memsz: Elf32_Word, - p_flags: Elf32_Word, - p_align: Elf32_Word, + p_filesz: Word, + p_memsz: Word, + p_flags: Word, + p_align: Word, }; pub const Elf64_Phdr = extern struct { - p_type: Elf64_Word, - p_flags: Elf64_Word, + p_type: Word, + p_flags: Word, p_offset: Elf64_Off, p_vaddr: Elf64_Addr, p_paddr: Elf64_Addr, @@ -769,44 +773,44 @@ pub const Elf64_Phdr = extern struct { p_align: Elf64_Xword, }; pub const Elf32_Shdr = extern struct { - sh_name: Elf32_Word, - sh_type: Elf32_Word, - sh_flags: Elf32_Word, + sh_name: Word, + sh_type: Word, + sh_flags: Word, sh_addr: Elf32_Addr, sh_offset: Elf32_Off, - sh_size: Elf32_Word, - sh_link: Elf32_Word, - sh_info: Elf32_Word, - sh_addralign: Elf32_Word, - sh_entsize: Elf32_Word, + sh_size: Word, + sh_link: Word, + sh_info: Word, + sh_addralign: Word, + sh_entsize: Word, }; pub const Elf64_Shdr = extern struct { - sh_name: Elf64_Word, - sh_type: Elf64_Word, + sh_name: Word, + sh_type: Word, sh_flags: Elf64_Xword, sh_addr: Elf64_Addr, sh_offset: Elf64_Off, sh_size: Elf64_Xword, - sh_link: Elf64_Word, - sh_info: Elf64_Word, + sh_link: Word, + sh_info: Word, sh_addralign: Elf64_Xword, sh_entsize: Elf64_Xword, }; pub const Elf32_Chdr = extern struct { ch_type: COMPRESS, - ch_size: Elf32_Word, - ch_addralign: Elf32_Word, + ch_size: Word, + ch_addralign: Word, }; pub const Elf64_Chdr = extern struct { ch_type: COMPRESS, - ch_reserved: Elf64_Word = 0, + ch_reserved: Word = 0, ch_size: Elf64_Xword, ch_addralign: Elf64_Xword, }; pub const Elf32_Sym = extern struct { - st_name: Elf32_Word, + st_name: Word, st_value: Elf32_Addr, - st_size: Elf32_Word, + st_size: Word, st_info: u8, st_other: u8, st_shndx: Elf32_Section, @@ -819,7 +823,7 @@ pub const Elf32_Sym = extern struct { } }; pub const Elf64_Sym = extern struct { - st_name: Elf64_Word, + st_name: Word, st_info: u8, st_other: u8, st_shndx: Elf64_Section, @@ -834,16 +838,16 @@ pub const Elf64_Sym = extern struct { } }; pub const Elf32_Syminfo = extern struct { - si_boundto: Elf32_Half, - si_flags: Elf32_Half, + si_boundto: Half, + si_flags: Half, }; pub const Elf64_Syminfo = extern struct { - si_boundto: Elf64_Half, - si_flags: Elf64_Half, + si_boundto: Half, + si_flags: Half, }; pub const Elf32_Rel = extern struct { r_offset: Elf32_Addr, - r_info: Elf32_Word, + r_info: Word, pub inline fn r_sym(self: @This()) u24 { return @truncate(self.r_info >> 8); @@ -865,8 +869,8 @@ pub const Elf64_Rel = extern struct { }; pub const Elf32_Rela = extern struct { r_offset: Elf32_Addr, - r_info: Elf32_Word, - r_addend: Elf32_Sword, + r_info: Word, + r_addend: Sword, pub inline fn r_sym(self: @This()) u24 { return @truncate(self.r_info >> 8); @@ -887,69 +891,49 @@ pub const Elf64_Rela = extern struct { return @truncate(self.r_info); } }; -pub const Elf32_Relr = Elf32_Word; +pub const Elf32_Relr = Word; pub const Elf64_Relr = Elf64_Xword; pub const Elf32_Dyn = extern struct { - d_tag: Elf32_Sword, + d_tag: Sword, d_val: Elf32_Addr, }; pub const Elf64_Dyn = extern struct { d_tag: Elf64_Sxword, d_val: Elf64_Addr, }; -pub const Elf32_Verdef = extern struct { - vd_version: Elf32_Half, - vd_flags: Elf32_Half, - vd_ndx: Elf32_Half, - vd_cnt: Elf32_Half, - vd_hash: Elf32_Word, - vd_aux: Elf32_Word, - vd_next: Elf32_Word, +pub const Verdef = extern struct { + version: Half, + flags: Half, + ndx: VER_NDX, + cnt: Half, + hash: Word, + aux: Word, + next: Word, }; -pub const Elf64_Verdef = extern struct { - vd_version: Elf64_Half, - vd_flags: Elf64_Half, - vd_ndx: Elf64_Half, - vd_cnt: Elf64_Half, - vd_hash: Elf64_Word, - vd_aux: Elf64_Word, - vd_next: Elf64_Word, -}; -pub const Elf32_Verdaux = extern struct { - vda_name: Elf32_Word, - vda_next: Elf32_Word, -}; -pub const Elf64_Verdaux = extern struct { - vda_name: Elf64_Word, - vda_next: Elf64_Word, +pub const Verdaux = extern struct { + name: Word, + next: Word, }; pub const Elf32_Verneed = extern struct { - vn_version: Elf32_Half, - vn_cnt: Elf32_Half, - vn_file: Elf32_Word, - vn_aux: Elf32_Word, - vn_next: Elf32_Word, + vn_version: Half, + vn_cnt: Half, + vn_file: Word, + vn_aux: Word, + vn_next: Word, }; pub const Elf64_Verneed = extern struct { - vn_version: Elf64_Half, - vn_cnt: Elf64_Half, - vn_file: Elf64_Word, - vn_aux: Elf64_Word, - vn_next: Elf64_Word, + vn_version: Half, + vn_cnt: Half, + vn_file: Word, + vn_aux: Word, + vn_next: Word, }; -pub const Elf32_Vernaux = extern struct { - vna_hash: Elf32_Word, - vna_flags: Elf32_Half, - vna_other: Elf32_Half, - vna_name: Elf32_Word, - vna_next: Elf32_Word, -}; -pub const Elf64_Vernaux = extern struct { - vna_hash: Elf64_Word, - vna_flags: Elf64_Half, - vna_other: Elf64_Half, - vna_name: Elf64_Word, - vna_next: Elf64_Word, +pub const Vernaux = extern struct { + hash: Word, + flags: Half, + other: Half, + name: Word, + next: Word, }; pub const Elf32_auxv_t = extern struct { a_type: u32, @@ -964,81 +948,81 @@ pub const Elf64_auxv_t = extern struct { }, }; pub const Elf32_Nhdr = extern struct { - n_namesz: Elf32_Word, - n_descsz: Elf32_Word, - n_type: Elf32_Word, + n_namesz: Word, + n_descsz: Word, + n_type: Word, }; pub const Elf64_Nhdr = extern struct { - n_namesz: Elf64_Word, - n_descsz: Elf64_Word, - n_type: Elf64_Word, + n_namesz: Word, + n_descsz: Word, + n_type: Word, }; pub const Elf32_Move = extern struct { m_value: Elf32_Xword, - m_info: Elf32_Word, - m_poffset: Elf32_Word, - m_repeat: Elf32_Half, - m_stride: Elf32_Half, + m_info: Word, + m_poffset: Word, + m_repeat: Half, + m_stride: Half, }; pub const Elf64_Move = extern struct { m_value: Elf64_Xword, m_info: Elf64_Xword, m_poffset: Elf64_Xword, - m_repeat: Elf64_Half, - m_stride: Elf64_Half, + m_repeat: Half, + m_stride: Half, }; pub const Elf32_gptab = extern union { gt_header: extern struct { - gt_current_g_value: Elf32_Word, - gt_unused: Elf32_Word, + gt_current_g_value: Word, + gt_unused: Word, }, gt_entry: extern struct { - gt_g_value: Elf32_Word, - gt_bytes: Elf32_Word, + gt_g_value: Word, + gt_bytes: Word, }, }; pub const Elf32_RegInfo = extern struct { - ri_gprmask: Elf32_Word, - ri_cprmask: [4]Elf32_Word, - ri_gp_value: Elf32_Sword, + ri_gprmask: Word, + ri_cprmask: [4]Word, + ri_gp_value: Sword, }; pub const Elf_Options = extern struct { kind: u8, size: u8, section: Elf32_Section, - info: Elf32_Word, + info: Word, }; pub const Elf_Options_Hw = extern struct { - hwp_flags1: Elf32_Word, - hwp_flags2: Elf32_Word, + hwp_flags1: Word, + hwp_flags2: Word, }; pub const Elf32_Lib = extern struct { - l_name: Elf32_Word, - l_time_stamp: Elf32_Word, - l_checksum: Elf32_Word, - l_version: Elf32_Word, - l_flags: Elf32_Word, + l_name: Word, + l_time_stamp: Word, + l_checksum: Word, + l_version: Word, + l_flags: Word, }; pub const Elf64_Lib = extern struct { - l_name: Elf64_Word, - l_time_stamp: Elf64_Word, - l_checksum: Elf64_Word, - l_version: Elf64_Word, - l_flags: Elf64_Word, + l_name: Word, + l_time_stamp: Word, + l_checksum: Word, + l_version: Word, + l_flags: Word, }; pub const Elf32_Conflict = Elf32_Addr; pub const Elf_MIPS_ABIFlags_v0 = extern struct { - version: Elf32_Half, + version: Half, isa_level: u8, isa_rev: u8, gpr_size: u8, cpr1_size: u8, cpr2_size: u8, fp_abi: u8, - isa_ext: Elf32_Word, - ases: Elf32_Word, - flags1: Elf32_Word, - flags2: Elf32_Word, + isa_ext: Word, + ases: Word, + flags1: Word, + flags2: Word, }; comptime { @@ -1102,22 +1086,11 @@ pub const Sym = switch (@sizeOf(usize)) { 8 => Elf64_Sym, else => @compileError("expected pointer size of 32 or 64"), }; -pub const Verdef = switch (@sizeOf(usize)) { - 4 => Elf32_Verdef, - 8 => Elf64_Verdef, - else => @compileError("expected pointer size of 32 or 64"), -}; -pub const Verdaux = switch (@sizeOf(usize)) { - 4 => Elf32_Verdaux, - 8 => Elf64_Verdaux, - else => @compileError("expected pointer size of 32 or 64"), -}; pub const Addr = switch (@sizeOf(usize)) { 4 => Elf32_Addr, 8 => Elf64_Addr, else => @compileError("expected pointer size of 32 or 64"), }; -pub const Half = u16; pub const OSABI = enum(u8) { /// UNIX System V ABI diff --git a/lib/std/os/linux/vdso.zig b/lib/std/os/linux/vdso.zig index 4e62679b26e22cefb470fb51c86deaf2b8e6381a..516976098db2abc084dbe31658a5705414f88256 100644 --- a/lib/std/os/linux/vdso.zig +++ b/lib/std/os/linux/vdso.zig @@ -37,7 +37,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { var maybe_strings: ?[*]u8 = null; var maybe_syms: ?[*]elf.Sym = null; var maybe_hashtab: ?[*]linux.Elf_Symndx = null; - var maybe_versym: ?[*]u16 = null; + var maybe_versym: ?[*]elf.Versym = null; var maybe_verdef: ?*elf.Verdef = null; { @@ -48,7 +48,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { elf.DT_STRTAB => maybe_strings = @as([*]u8, @ptrFromInt(p)), elf.DT_SYMTAB => maybe_syms = @as([*]elf.Sym, @ptrFromInt(p)), elf.DT_HASH => maybe_hashtab = @as([*]linux.Elf_Symndx, @ptrFromInt(p)), - elf.DT_VERSYM => maybe_versym = @as([*]u16, @ptrFromInt(p)), + elf.DT_VERSYM => maybe_versym = @as([*]elf.Versym, @ptrFromInt(p)), elf.DT_VERDEF => maybe_verdef = @as(*elf.Verdef, @ptrFromInt(p)), else => {}, } @@ -80,17 +80,15 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { return 0; } -fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [*]u8) bool { +fn checkver(def_arg: *elf.Verdef, vsym_arg: elf.Versym, vername: []const u8, strings: [*]u8) bool { var def = def_arg; - const vsym = @as(u32, @bitCast(vsym_arg)) & 0x7fff; + const vsym_index = vsym_arg.VERSION; while (true) { - if (0 == (def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym) - break; - if (def.vd_next == 0) - return false; - def = @as(*elf.Verdef, @ptrFromInt(@intFromPtr(def) + def.vd_next)); + if (0 == (def.flags & elf.VER_FLG_BASE) and @intFromEnum(def.ndx) == vsym_index) break; + if (def.next == 0) return false; + def = @ptrFromInt(@intFromPtr(def) + def.next); } - const aux = @as(*elf.Verdaux, @ptrFromInt(@intFromPtr(def) + def.vd_aux)); - const vda_name = @as([*:0]u8, @ptrCast(strings + aux.vda_name)); + const aux: *elf.Verdaux = @ptrFromInt(@intFromPtr(def) + def.aux); + const vda_name: [*:0]u8 = @ptrCast(strings + aux.name); return mem.eql(u8, vername, mem.sliceTo(vda_name, 0)); } diff --git a/src/Compilation.zig b/src/Compilation.zig index 74da4b9fe28b1d862a2f87e2a34df06414405808..1451c2adc32751802f17e7e98ebf371b2fdf750e 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1002,6 +1002,7 @@ const CacheUse = union(CacheMode) { pub const LinkObject = struct { path: Path, must_link: bool = false, + needed: bool = false, // When the library is passed via a positional argument, it will be // added as a full path. If it's `-l`, then just the basename. // @@ -2561,6 +2562,7 @@ fn addNonIncrementalStuffToCacheManifest( for (comp.objects) |obj| { _ = try man.addFilePath(obj.path, null); man.hash.add(obj.must_link); + man.hash.add(obj.needed); man.hash.add(obj.loption); } diff --git a/src/link.zig b/src/link.zig index ebfee34c4bf30113a1430e63a1f98c256ae3d744..7280ce3df7e42981c64ccc671ec39185642f02ab 100644 --- a/src/link.zig +++ b/src/link.zig @@ -207,23 +207,19 @@ pub const Diags = struct { pub fn addError(diags: *Diags, comptime format: []const u8, args: anytype) void { @branchHint(.cold); const gpa = diags.gpa; + const eu_main_msg = std.fmt.allocPrint(gpa, format, args); diags.mutex.lock(); defer diags.mutex.unlock(); - diags.msgs.ensureUnusedCapacity(gpa, 1) catch |err| switch (err) { - error.OutOfMemory => { - diags.flags.alloc_failure_occurred = true; - return; - }, + addErrorLockedFallible(diags, eu_main_msg) catch |err| switch (err) { + error.OutOfMemory => diags.setAllocFailureLocked(), }; - const err_msg: Msg = .{ - .msg = std.fmt.allocPrint(gpa, format, args) catch |err| switch (err) { - error.OutOfMemory => { - diags.flags.alloc_failure_occurred = true; - return; - }, - }, - }; - diags.msgs.appendAssumeCapacity(err_msg); + } + + fn addErrorLockedFallible(diags: *Diags, eu_main_msg: Allocator.Error![]u8) Allocator.Error!void { + const gpa = diags.gpa; + const main_msg = try eu_main_msg; + errdefer gpa.free(main_msg); + try diags.msgs.append(gpa, .{ .msg = main_msg }); } pub fn addErrorWithNotes(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes { @@ -242,7 +238,7 @@ pub const Diags = struct { const err = diags.msgs.addOneAssumeCapacity(); err.* = .{ .msg = undefined, - .notes = try gpa.alloc(Diags.Msg, note_count), + .notes = try gpa.alloc(Msg, note_count), }; return .{ .diags = diags, @@ -250,34 +246,93 @@ pub const Diags = struct { }; } - pub fn reportMissingLibraryError( + pub fn addMissingLibraryError( diags: *Diags, checked_paths: []const []const u8, comptime format: []const u8, args: anytype, - ) error{OutOfMemory}!void { + ) void { @branchHint(.cold); - var err = try diags.addErrorWithNotes(checked_paths.len); - try err.addMsg(format, args); - for (checked_paths) |path| { - try err.addNote("tried {s}", .{path}); + const gpa = diags.gpa; + const eu_main_msg = std.fmt.allocPrint(gpa, format, args); + diags.mutex.lock(); + defer diags.mutex.unlock(); + addMissingLibraryErrorLockedFallible(diags, checked_paths, eu_main_msg) catch |err| switch (err) { + error.OutOfMemory => diags.setAllocFailureLocked(), + }; + } + + fn addMissingLibraryErrorLockedFallible( + diags: *Diags, + checked_paths: []const []const u8, + eu_main_msg: Allocator.Error![]u8, + ) Allocator.Error!void { + const gpa = diags.gpa; + const main_msg = try eu_main_msg; + errdefer gpa.free(main_msg); + try diags.msgs.ensureUnusedCapacity(gpa, 1); + const notes = try gpa.alloc(Msg, checked_paths.len); + errdefer gpa.free(notes); + for (checked_paths, notes) |path, *note| { + note.* = .{ .msg = try std.fmt.allocPrint(gpa, "tried {s}", .{path}) }; } + diags.msgs.appendAssumeCapacity(.{ + .msg = main_msg, + .notes = notes, + }); } - pub fn reportParseError( + pub fn addParseError( diags: *Diags, path: Path, comptime format: []const u8, args: anytype, - ) error{OutOfMemory}!void { + ) void { @branchHint(.cold); - var err = try diags.addErrorWithNotes(1); - try err.addMsg(format, args); - try err.addNote("while parsing {}", .{path}); + const gpa = diags.gpa; + const eu_main_msg = std.fmt.allocPrint(gpa, format, args); + diags.mutex.lock(); + defer diags.mutex.unlock(); + addParseErrorLockedFallible(diags, path, eu_main_msg) catch |err| switch (err) { + error.OutOfMemory => diags.setAllocFailureLocked(), + }; + } + + fn addParseErrorLockedFallible(diags: *Diags, path: Path, m: Allocator.Error![]u8) Allocator.Error!void { + const gpa = diags.gpa; + const main_msg = try m; + errdefer gpa.free(main_msg); + try diags.msgs.ensureUnusedCapacity(gpa, 1); + const note = try std.fmt.allocPrint(gpa, "while parsing {}", .{path}); + errdefer gpa.free(note); + const notes = try gpa.create([1]Msg); + errdefer gpa.destroy(notes); + notes.* = .{.{ .msg = note }}; + diags.msgs.appendAssumeCapacity(.{ + .msg = main_msg, + .notes = notes, + }); + } + + pub fn failParse( + diags: *Diags, + path: Path, + comptime format: []const u8, + args: anytype, + ) error{LinkFailure} { + @branchHint(.cold); + addParseError(diags, path, format, args); + return error.LinkFailure; } pub fn setAllocFailure(diags: *Diags) void { @branchHint(.cold); + diags.mutex.lock(); + defer diags.mutex.unlock(); + setAllocFailureLocked(diags); + } + + fn setAllocFailureLocked(diags: *Diags) void { log.debug("memory allocation failure", .{}); diags.flags.alloc_failure_occurred = true; } @@ -727,7 +782,8 @@ pub const File = struct { FailedToEmit, FileSystem, FilesOpenedWithWrongFlags, - /// Indicates an error will be present in `Compilation.link_errors`. + /// Deprecated. Use `LinkFailure` instead. + /// Formerly used to indicate an error will be present in `Compilation.link_errors`. FlushFailure, /// Indicates an error will be present in `Compilation.link_errors`. LinkFailure, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 526c2932d9ba92b67fe3b342fe74c97601437239..0b1439ec806a5feb808b3a96aa97b95fc340fb35 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -46,7 +46,7 @@ file_handles: std.ArrayListUnmanaged(File.Handle) = .empty, zig_object_index: ?File.Index = null, linker_defined_index: ?File.Index = null, objects: std.ArrayListUnmanaged(File.Index) = .empty, -shared_objects: std.ArrayListUnmanaged(File.Index) = .empty, +shared_objects: std.StringArrayHashMapUnmanaged(File.Index) = .empty, /// List of all output sections and their associated metadata. sections: std.MultiArrayList(Section) = .{}, @@ -62,7 +62,7 @@ phdr_indexes: ProgramHeaderIndexes = .{}, section_indexes: SectionIndexes = .{}, page_size: u32, -default_sym_version: elf.Elf64_Versym, +default_sym_version: elf.Versym, /// .shstrtab buffer shstrtab: std.ArrayListUnmanaged(u8) = .empty, @@ -75,7 +75,7 @@ dynsym: DynsymSection = .{}, /// .dynstrtab buffer dynstrtab: std.ArrayListUnmanaged(u8) = .empty, /// Version symbol table. Only populated and emitted when linking dynamically. -versym: std.ArrayListUnmanaged(elf.Elf64_Versym) = .empty, +versym: std.ArrayListUnmanaged(elf.Versym) = .empty, /// .verneed section verneed: VerneedSection = .{}, /// .got section @@ -114,7 +114,7 @@ thunks: std.ArrayListUnmanaged(Thunk) = .empty, merge_sections: std.ArrayListUnmanaged(Merge.Section) = .empty, comment_merge_section_index: ?Merge.Section.Index = null, -first_eflags: ?elf.Elf64_Word = null, +first_eflags: ?elf.Word = null, const SectionIndexes = struct { copy_rel: ?u32 = null, @@ -265,10 +265,7 @@ pub fn createEmpty( }; const is_dyn_lib = output_mode == .Lib and link_mode == .dynamic; - const default_sym_version: elf.Elf64_Versym = if (is_dyn_lib or comp.config.rdynamic) - elf.VER_NDX_GLOBAL - else - elf.VER_NDX_LOCAL; + const default_sym_version: elf.Versym = if (is_dyn_lib or comp.config.rdynamic) .GLOBAL else .LOCAL; // If using LLD to link, this code should produce an object file so that it // can be passed to LLD. @@ -794,58 +791,51 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod // --verbose-link if (comp.verbose_link) try self.dumpArgv(comp); - if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self, tid); + if (self.zigObjectPtr()) |zig_object| try zig_object.flush(self, tid); 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 comp.getCrtPaths(arena); // csu prelude - if (csu.crt0) |path| try parseObjectReportingFailure(self, path); - if (csu.crti) |path| try parseObjectReportingFailure(self, path); - if (csu.crtbegin) |path| try parseObjectReportingFailure(self, path); + if (csu.crt0) |path| parseObjectReportingFailure(self, path); + if (csu.crti) |path| parseObjectReportingFailure(self, path); + if (csu.crtbegin) |path| parseObjectReportingFailure(self, path); for (comp.objects) |obj| { - if (obj.isObject()) { - try parseObjectReportingFailure(self, obj.path); - } else { - try parseLibraryReportingFailure(self, .{ .path = obj.path }, obj.must_link); - } + parseInputReportingFailure(self, obj.path, obj.needed, obj.must_link); } // 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 parseObjectReportingFailure(self, key.status.success.object_path); + parseObjectReportingFailure(self, key.status.success.object_path); } - if (module_obj_path) |path| try parseObjectReportingFailure(self, path); + if (module_obj_path) |path| parseObjectReportingFailure(self, path); - if (comp.config.any_sanitize_thread) try parseCrtFileReportingFailure(self, comp.tsan_lib.?); - if (comp.config.any_fuzz) try parseCrtFileReportingFailure(self, comp.fuzzer_lib.?); + if (comp.config.any_sanitize_thread) parseCrtFileReportingFailure(self, comp.tsan_lib.?); + if (comp.config.any_fuzz) parseCrtFileReportingFailure(self, comp.fuzzer_lib.?); // libc if (!comp.skip_linker_dependencies and !comp.config.link_libc) { - if (comp.libc_static_lib) |lib| try parseCrtFileReportingFailure(self, lib); + if (comp.libc_static_lib) |lib| parseCrtFileReportingFailure(self, lib); } for (comp.system_libs.values()) |lib_info| { - try self.parseLibraryReportingFailure(.{ - .needed = lib_info.needed, - .path = lib_info.path.?, - }, false); + parseInputReportingFailure(self, lib_info.path.?, lib_info.needed, false); } // libc++ dep if (comp.config.link_libcpp) { - try self.parseLibraryReportingFailure(.{ .path = comp.libcxxabi_static_lib.?.full_object_path }, false); - try self.parseLibraryReportingFailure(.{ .path = comp.libcxx_static_lib.?.full_object_path }, false); + parseInputReportingFailure(self, comp.libcxxabi_static_lib.?.full_object_path, false, false); + parseInputReportingFailure(self, comp.libcxx_static_lib.?.full_object_path, false, false); } // libunwind dep if (comp.config.link_libunwind) { - try self.parseLibraryReportingFailure(.{ .path = comp.libunwind_static_lib.?.full_object_path }, false); + parseInputReportingFailure(self, comp.libunwind_static_lib.?.full_object_path, false, false); } // libc dep @@ -869,17 +859,16 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod if (try self.accessLibPath(arena, &test_path, &checked_paths, lc.crt_dir.?, lib_name, .static)) break :success; - try diags.reportMissingLibraryError( + diags.addMissingLibraryError( checked_paths.items, "missing system library: '{s}' was not found", .{lib_name}, ); - continue; } const resolved_path = Path.initCwd(try arena.dupe(u8, test_path.items)); - try self.parseLibraryReportingFailure(.{ .path = resolved_path }, false); + parseInputReportingFailure(self, resolved_path, false, false); } } else if (target.isGnuLibC()) { for (glibc.libs) |lib| { @@ -890,17 +879,15 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod const lib_path = Path.initCwd(try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, })); - try self.parseLibraryReportingFailure(.{ .path = lib_path }, false); + parseInputReportingFailure(self, lib_path, false, false); } - try self.parseLibraryReportingFailure(.{ - .path = try comp.get_libc_crt_file(arena, "libc_nonshared.a"), - }, false); + parseInputReportingFailure(self, try comp.get_libc_crt_file(arena, "libc_nonshared.a"), false, false); } else if (target.isMusl()) { const path = try comp.get_libc_crt_file(arena, switch (link_mode) { .static => "libc.a", .dynamic => "libc.so", }); - try self.parseLibraryReportingFailure(.{ .path = path }, false); + parseInputReportingFailure(self, path, false, false); } else { diags.flags.missing_libc = true; } @@ -912,35 +899,17 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod // to be after the shared libraries, so they are picked up from the shared // libraries, not libcompiler_rt. if (comp.compiler_rt_lib) |crt_file| { - try parseLibraryReportingFailure(self, .{ .path = crt_file.full_object_path }, false); + parseInputReportingFailure(self, crt_file.full_object_path, false, false); } else if (comp.compiler_rt_obj) |crt_file| { - try parseObjectReportingFailure(self, crt_file.full_object_path); + parseObjectReportingFailure(self, crt_file.full_object_path); } // csu postlude - if (csu.crtend) |path| try parseObjectReportingFailure(self, path); - if (csu.crtn) |path| try parseObjectReportingFailure(self, path); + if (csu.crtend) |path| parseObjectReportingFailure(self, path); + if (csu.crtn) |path| parseObjectReportingFailure(self, path); if (diags.hasErrors()) return error.FlushFailure; - // Dedup shared objects - { - var seen_dsos = std.StringHashMap(void).init(gpa); - defer seen_dsos.deinit(); - try seen_dsos.ensureTotalCapacity(@as(u32, @intCast(self.shared_objects.items.len))); - - var i: usize = 0; - while (i < self.shared_objects.items.len) { - const index = self.shared_objects.items[i]; - const shared_object = self.file(index).?.shared_object; - const soname = shared_object.soname(); - const gop = seen_dsos.getOrPutAssumeCapacity(soname); - if (gop.found_existing) { - _ = self.shared_objects.orderedRemove(i); - } else i += 1; - } - } - // If we haven't already, create a linker-generated input file comprising of // linker-defined synthetic symbols only such as `_DYNAMIC`, etc. if (self.linker_defined_index == null) { @@ -1372,42 +1341,51 @@ pub const ParseError = error{ UnknownFileType, } || LdScript.Error || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError; -fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CrtFile) error{OutOfMemory}!void { - if (crt_file.isObject()) { - try parseObjectReportingFailure(self, crt_file.full_object_path); - } else { - try parseLibraryReportingFailure(self, .{ .path = crt_file.full_object_path }, false); +fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CrtFile) void { + parseInputReportingFailure(self, crt_file.full_object_path, false, false); +} + +pub fn parseInputReportingFailure(self: *Elf, path: Path, needed: bool, must_link: bool) void { + const gpa = self.base.comp.gpa; + const diags = &self.base.comp.link_diags; + const target = self.getTarget(); + + switch (Compilation.classifyFileExt(path.sub_path)) { + .object => parseObjectReportingFailure(self, path), + .shared_library => parseSharedObject(gpa, diags, .{ + .path = path, + .needed = needed, + }, &self.shared_objects, &self.files, target) catch |err| switch (err) { + error.LinkFailure => return, // already reported + error.BadMagic, error.UnexpectedEndOfFile => { + // It could be a linker script. + self.parseLdScript(.{ .path = path, .needed = needed }) catch |err2| switch (err2) { + error.LinkFailure => return, // already reported + else => |e| diags.addParseError(path, "failed to parse linker script: {s}", .{@errorName(e)}), + }; + }, + else => |e| diags.addParseError(path, "failed to parse shared object: {s}", .{@errorName(e)}), + }, + .static_library => parseArchive(self, path, must_link) catch |err| switch (err) { + error.LinkFailure => return, // already reported + else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}), + }, + .unknown => self.parseLdScript(.{ .path = path, .needed = needed }) catch |err| switch (err) { + error.LinkFailure => return, // already reported + else => |e| diags.addParseError(path, "failed to parse linker script: {s}", .{@errorName(e)}), + }, + else => diags.addParseError(path, "unrecognized file type", .{}), } } -pub fn parseObjectReportingFailure(self: *Elf, path: Path) error{OutOfMemory}!void { +pub fn parseObjectReportingFailure(self: *Elf, path: Path) void { + const diags = &self.base.comp.link_diags; self.parseObject(path) catch |err| switch (err) { error.LinkFailure => return, // already reported - error.OutOfMemory => return error.OutOfMemory, - else => |e| try self.addParseError(path, "unable to parse object: {s}", .{@errorName(e)}), + else => |e| diags.addParseError(path, "unable to parse object: {s}", .{@errorName(e)}), }; } -pub fn parseLibraryReportingFailure(self: *Elf, lib: SystemLib, must_link: bool) error{OutOfMemory}!void { - self.parseLibrary(lib, must_link) catch |err| switch (err) { - error.LinkFailure => return, // already reported - error.OutOfMemory => return error.OutOfMemory, - else => |e| try self.addParseError(lib.path, "unable to parse library: {s}", .{@errorName(e)}), - }; -} - -fn parseLibrary(self: *Elf, lib: SystemLib, must_link: bool) ParseError!void { - const tracy = trace(@src()); - defer tracy.end(); - if (try Archive.isArchive(lib.path)) { - try self.parseArchive(lib.path, must_link); - } else if (try SharedObject.isSharedObject(lib.path)) { - try self.parseSharedObject(lib); - } else { - try self.parseLdScript(lib); - } -} - fn parseObject(self: *Elf, path: Path) ParseError!void { const tracy = trace(@src()); defer tracy.end(); @@ -1457,28 +1435,80 @@ fn parseArchive(self: *Elf, path: Path, must_link: bool) ParseError!void { } } -fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void { +fn parseSharedObject( + gpa: Allocator, + diags: *Diags, + lib: SystemLib, + shared_objects: *std.StringArrayHashMapUnmanaged(File.Index), + files: *std.MultiArrayList(File.Entry), + target: std.Target, +) !void { const tracy = trace(@src()); defer tracy.end(); - const gpa = self.base.comp.gpa; const handle = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{}); defer handle.close(); - const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); - self.files.set(index, .{ .shared_object = .{ - .path = .{ - .root_dir = lib.path.root_dir, - .sub_path = try gpa.dupe(u8, lib.path.sub_path), + const stat = Stat.fromFs(try handle.stat()); + var header = try SharedObject.parseHeader(gpa, diags, lib.path, handle, stat, target); + defer header.deinit(gpa); + + const soname = header.soname() orelse lib.path.basename(); + + const gop = try shared_objects.getOrPut(gpa, soname); + if (gop.found_existing) { + header.deinit(gpa); + return; + } + errdefer _ = shared_objects.pop(); + + const index: File.Index = @intCast(try files.addOne(gpa)); + errdefer _ = files.pop(); + + gop.value_ptr.* = index; + + var parsed = try SharedObject.parse(gpa, &header, handle); + errdefer parsed.deinit(gpa); + + const duped_path: Path = .{ + .root_dir = lib.path.root_dir, + .sub_path = try gpa.dupe(u8, lib.path.sub_path), + }; + errdefer gpa.free(duped_path.sub_path); + + files.set(index, .{ + .shared_object = .{ + .parsed = parsed, + .path = duped_path, + .index = index, + .needed = lib.needed, + .alive = lib.needed, + .aliases = null, + .symbols = .empty, + .symbols_extra = .empty, + .symbols_resolver = .empty, + .output_symtab_ctx = .{}, }, - .index = index, - .needed = lib.needed, - .alive = lib.needed, - } }); - try self.shared_objects.append(gpa, index); + }); + const so = fileLookup(files.*, index).?.shared_object; - const shared_object = self.file(index).?.shared_object; - try shared_object.parse(self, handle); + // TODO: save this work for later + const nsyms = parsed.symbols.len; + try so.symbols.ensureTotalCapacityPrecise(gpa, nsyms); + try so.symbols_extra.ensureTotalCapacityPrecise(gpa, nsyms * @typeInfo(Symbol.Extra).@"struct".fields.len); + try so.symbols_resolver.ensureTotalCapacityPrecise(gpa, nsyms); + so.symbols_resolver.appendNTimesAssumeCapacity(0, nsyms); + + for (parsed.symtab, parsed.symbols, parsed.versyms, 0..) |esym, sym, versym, i| { + const out_sym_index = so.addSymbolAssumeCapacity(); + const out_sym = &so.symbols.items[out_sym_index]; + out_sym.value = @intCast(esym.st_value); + out_sym.name_offset = sym.mangled_name; + out_sym.ref = .{ .index = 0, .file = 0 }; + out_sym.esym_index = @intCast(i); + out_sym.version_index = versym; + out_sym.extra_index = so.addSymbolExtraAssumeCapacity(.{}); + } } fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { @@ -1537,7 +1567,7 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { } } - try diags.reportMissingLibraryError( + diags.addMissingLibraryError( checked_paths.items, "missing library dependency: GNU ld script '{}' requires '{s}', but file not found", .{ @as(Path, lib.path), script_arg.path }, @@ -1546,26 +1576,16 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { } const full_path = Path.initCwd(test_path.items); - self.parseLibrary(.{ - .needed = script_arg.needed, - .path = full_path, - }, false) catch |err| switch (err) { - error.LinkFailure => continue, // already reported - else => |e| try self.addParseError( - full_path, - "unexpected error: parsing library failed with error {s}", - .{@errorName(e)}, - ), - }; + parseInputReportingFailure(self, full_path, script_arg.needed, false); } } -pub fn validateEFlags(self: *Elf, file_index: File.Index, e_flags: elf.Elf64_Word) !void { +pub fn validateEFlags(self: *Elf, file_index: File.Index, e_flags: elf.Word) !void { if (self.first_eflags == null) { self.first_eflags = e_flags; return; // there isn't anything to conflict with yet } - const self_eflags: *elf.Elf64_Word = &self.first_eflags.?; + const self_eflags: *elf.Word = &self.first_eflags.?; switch (self.getTarget().cpu.arch) { .riscv64 => { @@ -1641,11 +1661,14 @@ fn accessLibPath( /// 5. Remove references to dead objects/shared objects /// 6. Re-run symbol resolution on pruned objects and shared objects sets. pub fn resolveSymbols(self: *Elf) !void { + // This function mutates `shared_objects`. + const shared_objects = &self.shared_objects; + // Resolve symbols in the ZigObject. For now, we assume that it's always live. if (self.zigObjectPtr()) |zo| try zo.asFile().resolveSymbols(self); // Resolve symbols on the set of all objects and shared objects (even if some are unneeded). for (self.objects.items) |index| try self.file(index).?.resolveSymbols(self); - for (self.shared_objects.items) |index| try self.file(index).?.resolveSymbols(self); + for (shared_objects.values()) |index| try self.file(index).?.resolveSymbols(self); if (self.linkerDefinedPtr()) |obj| try obj.asFile().resolveSymbols(self); // Mark live objects. @@ -1662,11 +1685,14 @@ pub fn resolveSymbols(self: *Elf) !void { _ = self.objects.orderedRemove(i); } else i += 1; } + // TODO This loop has 2 major flaws: + // 1. It is O(N^2) which is never allowed in the codebase. + // 2. It mutates shared_objects, which is a non-starter for incremental compilation. i = 0; - while (i < self.shared_objects.items.len) { - const index = self.shared_objects.items[i]; + while (i < shared_objects.values().len) { + const index = shared_objects.values()[i]; if (!self.file(index).?.isAlive()) { - _ = self.shared_objects.orderedRemove(i); + _ = shared_objects.orderedRemoveAt(i); } else i += 1; } @@ -1687,7 +1713,7 @@ pub fn resolveSymbols(self: *Elf) !void { // Re-resolve the symbols. if (self.zigObjectPtr()) |zo| try zo.asFile().resolveSymbols(self); for (self.objects.items) |index| try self.file(index).?.resolveSymbols(self); - for (self.shared_objects.items) |index| try self.file(index).?.resolveSymbols(self); + for (shared_objects.values()) |index| try self.file(index).?.resolveSymbols(self); if (self.linkerDefinedPtr()) |obj| try obj.asFile().resolveSymbols(self); } @@ -1696,12 +1722,13 @@ pub fn resolveSymbols(self: *Elf) !void { /// This routine will prune unneeded objects extracted from archives and /// unneeded shared objects. fn markLive(self: *Elf) void { + const shared_objects = self.shared_objects.values(); if (self.zigObjectPtr()) |zig_object| zig_object.asFile().markLive(self); for (self.objects.items) |index| { const file_ptr = self.file(index).?; if (file_ptr.isAlive()) file_ptr.markLive(self); } - for (self.shared_objects.items) |index| { + for (shared_objects) |index| { const file_ptr = self.file(index).?; if (file_ptr.isAlive()) file_ptr.markLive(self); } @@ -1716,6 +1743,7 @@ pub fn markEhFrameAtomsDead(self: *Elf) void { } fn markImportsExports(self: *Elf) void { + const shared_objects = self.shared_objects.values(); if (self.zigObjectPtr()) |zo| { zo.markImportsExports(self); } @@ -1723,7 +1751,7 @@ fn markImportsExports(self: *Elf) void { self.file(index).?.object.markImportsExports(self); } if (!self.isEffectivelyDynLib()) { - for (self.shared_objects.items) |index| { + for (shared_objects) |index| { self.file(index).?.shared_object.markImportExports(self); } } @@ -1744,6 +1772,7 @@ fn claimUnresolved(self: *Elf) void { /// alloc sections. fn scanRelocs(self: *Elf) !void { const gpa = self.base.comp.gpa; + const shared_objects = self.shared_objects.values(); var undefs = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)).init(gpa); defer { @@ -1787,7 +1816,7 @@ fn scanRelocs(self: *Elf) !void { for (self.objects.items) |index| { try self.file(index).?.createSymbolIndirection(self); } - for (self.shared_objects.items) |index| { + for (shared_objects) |index| { try self.file(index).?.createSymbolIndirection(self); } if (self.linkerDefinedPtr()) |obj| { @@ -1905,10 +1934,10 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD. const id_symlink_basename = "lld.id"; - var man: Cache.Manifest = undefined; + var man: std.Build.Cache.Manifest = undefined; defer if (!self.base.disable_lld_caching) man.deinit(); - var digest: [Cache.hex_digest_len]u8 = undefined; + var digest: [std.Build.Cache.hex_digest_len]u8 = undefined; if (!self.base.disable_lld_caching) { man = comp.cache_parent.obtain(); @@ -1988,7 +2017,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s digest = man.final(); var prev_digest_buf: [digest.len]u8 = undefined; - const prev_digest: []u8 = Cache.readSmallFile( + const prev_digest: []u8 = std.Build.Cache.readSmallFile( directory.handle, id_symlink_basename, &prev_digest_buf, @@ -2442,7 +2471,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s if (!self.base.disable_lld_caching) { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. - Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { + std.Build.Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); }; // Again failure here only means an unnecessary cache miss. @@ -2899,6 +2928,7 @@ fn initSyntheticSections(self: *Elf) !void { const comp = self.base.comp; const target = self.getTarget(); const ptr_size = self.ptrWidthBytes(); + const shared_objects = self.shared_objects.values(); const needs_eh_frame = blk: { if (self.zigObjectPtr()) |zo| @@ -3023,7 +3053,7 @@ fn initSyntheticSections(self: *Elf) !void { }); } - if (self.isEffectivelyDynLib() or self.shared_objects.items.len > 0 or comp.config.pie) { + if (self.isEffectivelyDynLib() or shared_objects.len > 0 or comp.config.pie) { if (self.section_indexes.dynstrtab == null) { self.section_indexes.dynstrtab = try self.addSection(.{ .name = try self.insertShString(".dynstr"), @@ -3072,7 +3102,7 @@ fn initSyntheticSections(self: *Elf) !void { const needs_versions = for (self.dynsym.entries.items) |entry| { const sym = self.symbol(entry.ref).?; - if (sym.flags.import and sym.version_index & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) break true; + if (sym.flags.import and sym.version_index.VERSION > elf.Versym.GLOBAL.VERSION) break true; } else false; if (needs_versions) { if (self.section_indexes.versym == null) { @@ -3080,8 +3110,8 @@ fn initSyntheticSections(self: *Elf) !void { .name = try self.insertShString(".gnu.version"), .flags = elf.SHF_ALLOC, .type = elf.SHT_GNU_VERSYM, - .addralign = @alignOf(elf.Elf64_Versym), - .entsize = @sizeOf(elf.Elf64_Versym), + .addralign = @alignOf(elf.Versym), + .entsize = @sizeOf(elf.Versym), }); } if (self.section_indexes.verneed == null) { @@ -3259,7 +3289,9 @@ fn sortInitFini(self: *Elf) !void { fn setDynamicSection(self: *Elf, rpaths: []const []const u8) !void { if (self.section_indexes.dynamic == null) return; - for (self.shared_objects.items) |index| { + const shared_objects = self.shared_objects.values(); + + for (shared_objects) |index| { const shared_object = self.file(index).?.shared_object; if (!shared_object.alive) continue; try self.dynamic.addNeeded(shared_object, self); @@ -3283,7 +3315,7 @@ fn setVersionSymtab(self: *Elf) !void { const gpa = self.base.comp.gpa; if (self.section_indexes.versym == null) return; try self.versym.resize(gpa, self.dynsym.count()); - self.versym.items[0] = elf.VER_NDX_LOCAL; + self.versym.items[0] = .LOCAL; for (self.dynsym.entries.items, 1..) |entry, i| { const sym = self.symbol(entry.ref).?; self.versym.items[i] = sym.version_index; @@ -3653,7 +3685,7 @@ fn updateSectionSizes(self: *Elf) !void { } if (self.section_indexes.versym) |index| { - shdrs[index].sh_size = self.versym.items.len * @sizeOf(elf.Elf64_Versym); + shdrs[index].sh_size = self.versym.items.len * @sizeOf(elf.Versym); } if (self.section_indexes.verneed) |index| { @@ -4055,13 +4087,15 @@ pub fn updateSymtabSize(self: *Elf) !void { var strsize: u32 = 0; const gpa = self.base.comp.gpa; + const shared_objects = self.shared_objects.values(); + var files = std.ArrayList(File.Index).init(gpa); defer files.deinit(); - try files.ensureTotalCapacityPrecise(self.objects.items.len + self.shared_objects.items.len + 2); + try files.ensureTotalCapacityPrecise(self.objects.items.len + shared_objects.len + 2); if (self.zig_object_index) |index| files.appendAssumeCapacity(index); for (self.objects.items) |index| files.appendAssumeCapacity(index); - for (self.shared_objects.items) |index| files.appendAssumeCapacity(index); + for (shared_objects) |index| files.appendAssumeCapacity(index); if (self.linker_defined_index) |index| files.appendAssumeCapacity(index); // Section symbols @@ -4284,6 +4318,8 @@ pub fn writeShStrtab(self: *Elf) !void { pub fn writeSymtab(self: *Elf) !void { const gpa = self.base.comp.gpa; + const shared_objects = self.shared_objects.values(); + const slice = self.sections.slice(); const symtab_shdr = slice.items(.shdr)[self.section_indexes.symtab.?]; const strtab_shdr = slice.items(.shdr)[self.section_indexes.strtab.?]; @@ -4335,7 +4371,7 @@ pub fn writeSymtab(self: *Elf) !void { file_ptr.writeSymtab(self); } - for (self.shared_objects.items) |index| { + for (shared_objects) |index| { const file_ptr = self.file(index).?; file_ptr.writeSymtab(self); } @@ -4368,8 +4404,8 @@ pub fn writeSymtab(self: *Elf) !void { .st_info = sym.st_info, .st_other = sym.st_other, .st_shndx = sym.st_shndx, - .st_value = @as(u32, @intCast(sym.st_value)), - .st_size = @as(u32, @intCast(sym.st_size)), + .st_value = @intCast(sym.st_value), + .st_size = @intCast(sym.st_size), }; if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out); } @@ -4925,18 +4961,6 @@ fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void { }); } -pub fn addParseError( - self: *Elf, - path: Path, - comptime format: []const u8, - args: anytype, -) error{OutOfMemory}!void { - const diags = &self.base.comp.link_diags; - var err = try diags.addErrorWithNotes(1); - try err.addMsg(format, args); - try err.addNote("while parsing {}", .{path}); -} - pub fn addFileError( self: *Elf, file_index: File.Index, @@ -4959,16 +4983,6 @@ pub fn failFile( return error.LinkFailure; } -pub fn failParse( - self: *Elf, - path: Path, - comptime format: []const u8, - args: anytype, -) error{ OutOfMemory, LinkFailure } { - try addParseError(self, path, format, args); - return error.LinkFailure; -} - const FormatShdrCtx = struct { elf_file: *Elf, shdr: elf.Elf64_Shdr, @@ -5113,6 +5127,8 @@ fn fmtDumpState( _ = unused_fmt_string; _ = options; + const shared_objects = self.shared_objects.values(); + if (self.zigObjectPtr()) |zig_object| { try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.basename }); try writer.print("{}{}", .{ @@ -5136,11 +5152,11 @@ fn fmtDumpState( }); } - for (self.shared_objects.items) |index| { + for (shared_objects) |index| { const shared_object = self.file(index).?.shared_object; - try writer.print("shared_object({d}) : ", .{index}); - try writer.print("{}", .{shared_object.path}); - try writer.print(" : needed({})", .{shared_object.needed}); + try writer.print("shared_object({d}) : {} : needed({})", .{ + index, shared_object.path, shared_object.needed, + }); if (!shared_object.alive) try writer.writeAll(" : [*]"); try writer.writeByte('\n'); try writer.print("{}\n", .{shared_object.fmtSymtab(self)}); @@ -5204,10 +5220,7 @@ pub fn preadAllAlloc(allocator: Allocator, handle: fs.File, offset: u64, size: u } /// Binary search -pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize { - if (!@hasDecl(@TypeOf(predicate), "predicate")) - @compileError("Predicate is required to define fn predicate(@This(), T) bool"); - +pub fn bsearch(comptime T: type, haystack: []const T, predicate: anytype) usize { var min: usize = 0; var max: usize = haystack.len; while (min < max) { @@ -5223,10 +5236,7 @@ pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytyp } /// Linear search -pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize { - if (!@hasDecl(@TypeOf(predicate), "predicate")) - @compileError("Predicate is required to define fn predicate(@This(), T) bool"); - +pub fn lsearch(comptime T: type, haystack: []const T, predicate: anytype) usize { var i: usize = 0; while (i < haystack.len) : (i += 1) { if (predicate.predicate(haystack[i])) break; @@ -5569,6 +5579,11 @@ fn createThunks(elf_file: *Elf, atom_list: *AtomList) !void { } } +pub fn stringTableLookup(strtab: []const u8, off: u32) [:0]const u8 { + const slice = strtab[off..]; + return slice[0..mem.indexOfScalar(u8, slice, 0).? :0]; +} + const std = @import("std"); const build_options = @import("build_options"); const builtin = @import("builtin"); @@ -5581,8 +5596,9 @@ const state_log = std.log.scoped(.link_state); const math = std.math; const mem = std.mem; const Allocator = std.mem.Allocator; -const Cache = std.Build.Cache; const Hash = std.hash.Wyhash; +const Path = std.Build.Cache.Path; +const Stat = std.Build.Cache.File.Stat; const codegen = @import("../codegen.zig"); const dev = @import("../dev.zig"); @@ -5601,10 +5617,10 @@ const Merge = @import("Elf/Merge.zig"); const Air = @import("../Air.zig"); const Archive = @import("Elf/Archive.zig"); const AtomList = @import("Elf/AtomList.zig"); -const Path = Cache.Path; const Compilation = @import("../Compilation.zig"); const ComdatGroupSection = synthetic_sections.ComdatGroupSection; const CopyRelSection = synthetic_sections.CopyRelSection; +const Diags = @import("../link.zig").Diags; const DynamicSection = synthetic_sections.DynamicSection; const DynsymSection = synthetic_sections.DynsymSection; const Dwarf = @import("Dwarf.zig"); diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index 7cc454f10c8affcee4869a597f06c4390ebdfdb2..50fffb0c19f5f7fb5f4bc5917812641dc127d1ad 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -1,15 +1,6 @@ objects: std.ArrayListUnmanaged(Object) = .empty, strtab: std.ArrayListUnmanaged(u8) = .empty, -pub fn isArchive(path: Path) !bool { - const file = try path.root_dir.handle.openFile(path.sub_path, .{}); - defer file.close(); - const reader = file.reader(); - const magic = reader.readBytesNoEof(elf.ARMAG.len) catch return false; - if (!mem.eql(u8, &magic, elf.ARMAG)) return false; - return true; -} - pub fn deinit(self: *Archive, allocator: Allocator) void { self.objects.deinit(allocator); self.strtab.deinit(allocator); @@ -18,6 +9,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void { pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.HandleIndex) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; + const diags = &comp.link_diags; const handle = elf_file.fileHandle(handle_index); const size = (try handle.stat()).size; @@ -35,7 +27,7 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.Hand pos += @sizeOf(elf.ar_hdr); if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) { - return elf_file.failParse(path, "invalid archive header delimiter: {s}", .{ + return diags.failParse(path, "invalid archive header delimiter: {s}", .{ std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag), }); } diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 8580f27d2db580f65de438e5768481af6471f750..4d8c23d2ff136952ae5d3fe3d9e70f7b832ed917 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -592,6 +592,7 @@ fn reportUndefined( const file_ptr = self.file(elf_file).?; const rel_esym = switch (file_ptr) { .zig_object => |x| x.symbol(rel.r_sym()).elfSym(elf_file), + .shared_object => |so| so.parsed.symtab[rel.r_sym()], inline else => |x| x.symtab.items[rel.r_sym()], }; const esym = sym.elfSym(elf_file); diff --git a/src/link/Elf/LdScript.zig b/src/link/Elf/LdScript.zig index 7bb1f62104404d74b3909b2363faa7ab24e8f0f8..349011a20ac9cd4df6c6018aa5dca04c29385e0e 100644 --- a/src/link/Elf/LdScript.zig +++ b/src/link/Elf/LdScript.zig @@ -21,6 +21,8 @@ pub const Error = error{ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void { const comp = elf_file.base.comp; const gpa = comp.gpa; + const diags = &comp.link_diags; + var tokenizer = Tokenizer{ .source = data }; var tokens = std.ArrayList(Token).init(gpa); defer tokens.deinit(); @@ -37,7 +39,7 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void { try line_col.append(.{ .line = line, .column = column }); switch (tok.id) { .invalid => { - return elf_file.failParse(scr.path, "invalid token in LD script: '{s}' ({d}:{d})", .{ + return diags.failParse(scr.path, "invalid token in LD script: '{s}' ({d}:{d})", .{ std.fmt.fmtSliceEscapeLower(tok.get(data)), line, column, }); }, @@ -61,7 +63,7 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void { const last_token_id = parser.it.pos - 1; const last_token = parser.it.get(last_token_id); const lcol = line_col.items[last_token_id]; - return elf_file.failParse(scr.path, "unexpected token in LD script: {s}: '{s}' ({d}:{d})", .{ + return diags.failParse(scr.path, "unexpected token in LD script: {s}: '{s}' ({d}:{d})", .{ @tagName(last_token.id), last_token.get(data), lcol.line, diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 2c0313609f930341877389adc8d4d99f149191a8..5aca15a205462b8e206f9228db618dc923cf51f4 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -310,7 +310,7 @@ fn initSymbols(self: *Object, allocator: Allocator, elf_file: *Elf) !void { sym_ptr.name_offset = sym.st_name; sym_ptr.esym_index = @intCast(i); sym_ptr.extra_index = self.addSymbolExtraAssumeCapacity(.{}); - sym_ptr.version_index = if (i >= first_global) elf_file.default_sym_version else elf.VER_NDX_LOCAL; + sym_ptr.version_index = if (i >= first_global) elf_file.default_sym_version else .LOCAL; sym_ptr.flags.weak = sym.st_bind() == elf.STB_WEAK; if (sym.st_shndx != elf.SHN_ABS and sym.st_shndx != elf.SHN_COMMON) { sym_ptr.ref = .{ .index = self.atoms_indexes.items[sym.st_shndx], .file = self.index }; @@ -536,7 +536,7 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void { sym.ref = .{ .index = 0, .file = 0 }; sym.esym_index = esym_index; sym.file_index = self.index; - sym.version_index = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version; + sym.version_index = if (is_import) .LOCAL else elf_file.default_sym_version; sym.flags.import = is_import; const idx = self.symbols_resolver.items[i]; @@ -598,8 +598,9 @@ pub fn markImportsExports(self: *Object, elf_file: *Elf) void { const ref = self.resolveSymbol(@intCast(idx), elf_file); const sym = elf_file.symbol(ref) orelse continue; const file = sym.file(elf_file).?; - if (sym.version_index == elf.VER_NDX_LOCAL) continue; - const vis = @as(elf.STV, @enumFromInt(sym.elfSym(elf_file).st_other)); + // https://github.com/ziglang/zig/issues/21678 + if (@as(u16, @bitCast(sym.version_index)) == @as(u16, @bitCast(elf.Versym.LOCAL))) continue; + const vis: elf.STV = @enumFromInt(sym.elfSym(elf_file).st_other); if (vis == .HIDDEN) continue; if (file == .shared_object and !sym.isAbs(elf_file)) { sym.flags.import = true; diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig index 7e6aabea381744be5a72690025d6f16565cc5e16..aa4c5f431766fe77526b49bfed33381b21f921b8 100644 --- a/src/link/Elf/SharedObject.zig +++ b/src/link/Elf/SharedObject.zig @@ -1,236 +1,316 @@ path: Path, index: File.Index, -header: ?elf.Elf64_Ehdr = null, -shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .empty, +parsed: Parsed, -symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .empty, -strtab: std.ArrayListUnmanaged(u8) = .empty, -/// Version symtab contains version strings of the symbols if present. -versyms: std.ArrayListUnmanaged(elf.Elf64_Versym) = .empty, -verstrings: std.ArrayListUnmanaged(u32) = .empty, +symbols: std.ArrayListUnmanaged(Symbol), +symbols_extra: std.ArrayListUnmanaged(u32), +symbols_resolver: std.ArrayListUnmanaged(Elf.SymbolResolver.Index), -symbols: std.ArrayListUnmanaged(Symbol) = .empty, -symbols_extra: std.ArrayListUnmanaged(u32) = .empty, -symbols_resolver: std.ArrayListUnmanaged(Elf.SymbolResolver.Index) = .empty, - -aliases: ?std.ArrayListUnmanaged(u32) = null, -dynamic_table: std.ArrayListUnmanaged(elf.Elf64_Dyn) = .empty, +aliases: ?std.ArrayListUnmanaged(u32), needed: bool, alive: bool, -output_symtab_ctx: Elf.SymtabCtx = .{}, - -pub fn isSharedObject(path: Path) !bool { - const file = try path.root_dir.handle.openFile(path.sub_path, .{}); - defer file.close(); - const reader = file.reader(); - const header = reader.readStruct(elf.Elf64_Ehdr) catch return false; - if (!mem.eql(u8, header.e_ident[0..4], "\x7fELF")) return false; - if (header.e_ident[elf.EI_VERSION] != 1) return false; - if (header.e_type != elf.ET.DYN) return false; - return true; -} +output_symtab_ctx: Elf.SymtabCtx, -pub fn deinit(self: *SharedObject, allocator: Allocator) void { - allocator.free(self.path.sub_path); - self.shdrs.deinit(allocator); - self.symtab.deinit(allocator); - self.strtab.deinit(allocator); - self.versyms.deinit(allocator); - self.verstrings.deinit(allocator); - self.symbols.deinit(allocator); - self.symbols_extra.deinit(allocator); - self.symbols_resolver.deinit(allocator); - if (self.aliases) |*aliases| aliases.deinit(allocator); - self.dynamic_table.deinit(allocator); +pub fn deinit(so: *SharedObject, gpa: Allocator) void { + gpa.free(so.path.sub_path); + so.parsed.deinit(gpa); + so.symbols.deinit(gpa); + so.symbols_extra.deinit(gpa); + so.symbols_resolver.deinit(gpa); + if (so.aliases) |*aliases| aliases.deinit(gpa); + so.* = undefined; } -pub fn parse(self: *SharedObject, elf_file: *Elf, handle: std.fs.File) !void { - const comp = elf_file.base.comp; - const gpa = comp.gpa; - const file_size = (try handle.stat()).size; +pub const Header = struct { + dynamic_table: []const elf.Elf64_Dyn, + soname_index: ?u32, + verdefnum: ?u32, + + sections: []const elf.Elf64_Shdr, + dynsym_sect_index: ?u32, + versym_sect_index: ?u32, + verdef_sect_index: ?u32, - 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)).*; + stat: Stat, + strtab: std.ArrayListUnmanaged(u8), - const em = elf_file.base.comp.root_mod.resolved_target.result.toElfMachine(); - if (em != self.header.?.e_machine) { - return elf_file.failFile(self.index, "invalid ELF machine type: {s}", .{ - @tagName(self.header.?.e_machine), - }); + pub fn deinit(header: *Header, gpa: Allocator) void { + gpa.free(header.sections); + gpa.free(header.dynamic_table); + header.strtab.deinit(gpa); + header.* = undefined; } - 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) { - return elf_file.failFile(self.index, "corrupted header: section header table extends past the end of file", .{}); + pub fn soname(header: Header) ?[]const u8 { + const i = header.soname_index orelse return null; + return Elf.stringTableLookup(header.strtab.items, i); } +}; + +pub const Parsed = struct { + stat: Stat, + strtab: []const u8, + soname_index: ?u32, + sections: []const elf.Elf64_Shdr, + + /// Nonlocal symbols only. + symtab: []const elf.Elf64_Sym, + /// Version symtab contains version strings of the symbols if present. + /// Nonlocal symbols only. + versyms: []const elf.Versym, + /// Nonlocal symbols only. + symbols: []const Parsed.Symbol, + + verstrings: []const u32, - 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); + const Symbol = struct { + mangled_name: u32, + }; + + pub fn deinit(p: *Parsed, gpa: Allocator) void { + gpa.free(p.strtab); + gpa.free(p.symtab); + gpa.free(p.versyms); + gpa.free(p.symbols); + gpa.free(p.verstrings); + p.* = undefined; + } + + pub fn versionString(p: Parsed, index: elf.Versym) [:0]const u8 { + return versionStringLookup(p.strtab, p.verstrings, index); + } + + pub fn soname(p: Parsed) ?[]const u8 { + const i = p.soname_index orelse return null; + return Elf.stringTableLookup(p.strtab, i); + } +}; + +pub fn parseHeader( + gpa: Allocator, + diags: *Diags, + file_path: Path, + fs_file: std.fs.File, + stat: Stat, + target: std.Target, +) !Header { + var ehdr: elf.Elf64_Ehdr = undefined; + { + const buf = mem.asBytes(&ehdr); + const amt = try fs_file.preadAll(buf, 0); + if (amt != buf.len) return error.UnexpectedEndOfFile; + } + if (!mem.eql(u8, ehdr.e_ident[0..4], "\x7fELF")) return error.BadMagic; + if (ehdr.e_ident[elf.EI_VERSION] != 1) return error.BadElfVersion; + if (ehdr.e_type != elf.ET.DYN) return error.NotSharedObject; + + if (target.toElfMachine() != ehdr.e_machine) + return diags.failParse(file_path, "invalid ELF machine type: {s}", .{@tagName(ehdr.e_machine)}); + + const shoff = std.math.cast(usize, ehdr.e_shoff) orelse return error.Overflow; + const shnum = std.math.cast(u32, ehdr.e_shnum) orelse return error.Overflow; + + const sections = try gpa.alloc(elf.Elf64_Shdr, shnum); + errdefer gpa.free(sections); + { + const buf = mem.sliceAsBytes(sections); + const amt = try fs_file.preadAll(buf, shoff); + if (amt != buf.len) return error.UnexpectedEndOfFile; + } 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 (file_size < shdr.sh_offset or file_size < shdr.sh_offset + shdr.sh_size) { - return elf_file.failFile(self.index, "corrupted section header", .{}); - } - } + for (sections, 0..) |shdr, i_usize| { + const i: u32 = @intCast(i_usize); switch (shdr.sh_type) { - 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 => {}, + elf.SHT_DYNSYM => dynsym_sect_index = i, + elf.SHT_DYNAMIC => dynamic_sect_index = i, + elf.SHT_GNU_VERSYM => versym_sect_index = i, + elf.SHT_GNU_VERDEF => verdef_sect_index = i, + else => continue, } } - 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 dynamic_table: []elf.Elf64_Dyn = if (dynamic_sect_index) |index| dt: { + const shdr = sections[index]; + const n = shdr.sh_size / @sizeOf(elf.Elf64_Dyn); + const dynamic_table = try gpa.alloc(elf.Elf64_Dyn, n); + errdefer gpa.free(dynamic_table); + const buf = mem.sliceAsBytes(dynamic_table); + const amt = try fs_file.preadAll(buf, shdr.sh_offset); + if (amt != buf.len) return error.UnexpectedEndOfFile; + break :dt dynamic_table; + } else &.{}; + errdefer gpa.free(dynamic_table); + + var strtab: std.ArrayListUnmanaged(u8) = .empty; + errdefer strtab.deinit(gpa); + + if (dynsym_sect_index) |index| { + const dynsym_shdr = sections[index]; + if (dynsym_shdr.sh_link >= sections.len) return error.BadStringTableIndex; + const strtab_shdr = sections[dynsym_shdr.sh_link]; + const buf = try strtab.addManyAsSlice(gpa, strtab_shdr.sh_size); + const amt = try fs_file.preadAll(buf, strtab_shdr.sh_offset); + if (amt != buf.len) return error.UnexpectedEndOfFile; } - 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); + var soname_index: ?u32 = null; + var verdefnum: ?u32 = null; + for (dynamic_table) |entry| switch (entry.d_tag) { + elf.DT_SONAME => { + if (entry.d_val >= strtab.items.len) return error.BadSonameIndex; + soname_index = @intCast(entry.d_val); + }, + elf.DT_VERDEFNUM => { + verdefnum = @intCast(entry.d_val); + }, + else => continue, + }; - 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, + return .{ + .dynamic_table = dynamic_table, + .soname_index = soname_index, + .verdefnum = verdefnum, + .sections = sections, + .dynsym_sect_index = dynsym_sect_index, .versym_sect_index = versym_sect_index, - }); - - try self.initSymbols(elf_file, .{ - .symtab = symtab, + .verdef_sect_index = verdef_sect_index, .strtab = strtab, - }); + .stat = stat, + }; } -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; +pub fn parse( + gpa: Allocator, + /// Moves resources from header. Caller may unconditionally deinit. + header: *Header, + fs_file: std.fs.File, +) !Parsed { + const symtab = if (header.dynsym_sect_index) |index| st: { + const shdr = header.sections[index]; + const n = shdr.sh_size / @sizeOf(elf.Elf64_Sym); + const symtab = try gpa.alloc(elf.Elf64_Sym, n); + errdefer gpa.free(symtab); + const buf = mem.sliceAsBytes(symtab); + const amt = try fs_file.preadAll(buf, shdr.sh_offset); + if (amt != buf.len) return error.UnexpectedEndOfFile; + break :st symtab; + } else &.{}; + defer gpa.free(symtab); - try self.verstrings.resize(gpa, 2); - self.verstrings.items[elf.VER_NDX_LOCAL] = 0; - self.verstrings.items[elf.VER_NDX_GLOBAL] = 0; + var verstrings: std.ArrayListUnmanaged(u32) = .empty; + defer verstrings.deinit(gpa); - 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); + if (header.verdef_sect_index) |shndx| { + const shdr = header.sections[shndx]; + const verdefs = try Elf.preadAllAlloc(gpa, fs_file, shdr.sh_offset, shdr.sh_size); defer gpa.free(verdefs); - const nverdefs = self.verdefNum(); - try self.verstrings.resize(gpa, self.verstrings.items.len + nverdefs); - var i: u32 = 0; var offset: u32 = 0; - while (i < nverdefs) : (i += 1) { - const verdef = @as(*align(1) const elf.Elf64_Verdef, @ptrCast(verdefs.ptr + offset)).*; - defer offset += verdef.vd_next; - if (verdef.vd_flags == elf.VER_FLG_BASE) continue; // Skip BASE entry - const vda_name = if (verdef.vd_cnt > 0) - @as(*align(1) const elf.Elf64_Verdaux, @ptrCast(verdefs.ptr + offset + verdef.vd_aux)).vda_name - else - 0; - self.verstrings.items[verdef.vd_ndx] = vda_name; - } - } + while (true) { + const verdef = mem.bytesAsValue(elf.Verdef, verdefs[offset..][0..@sizeOf(elf.Verdef)]); + if (verdef.ndx == .UNSPECIFIED) return error.VerDefSymbolTooLarge; + + if (verstrings.items.len <= @intFromEnum(verdef.ndx)) + try verstrings.appendNTimes(gpa, 0, @intFromEnum(verdef.ndx) + 1 - verstrings.items.len); - try self.versyms.ensureTotalCapacityPrecise(gpa, opts.symtab.len); - - 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| { - const normalized_ver = if (ver & elf.VERSYM_VERSION >= self.verstrings.items.len - 1) - elf.VER_NDX_GLOBAL - else - ver; - self.versyms.appendAssumeCapacity(normalized_ver); + const aux = mem.bytesAsValue(elf.Verdaux, verdefs[offset + verdef.aux ..][0..@sizeOf(elf.Verdaux)]); + verstrings.items[@intFromEnum(verdef.ndx)] = aux.name; + + if (verdef.next == 0) break; + offset += verdef.next; } - } else for (0..opts.symtab.len) |_| { - self.versyms.appendAssumeCapacity(elf.VER_NDX_GLOBAL); } -} - -fn initSymbols(self: *SharedObject, elf_file: *Elf, opts: struct { - symtab: []align(1) const elf.Elf64_Sym, - strtab: []const u8, -}) !void { - const gpa = elf_file.base.comp.gpa; - const nsyms = opts.symtab.len; - - try self.strtab.appendSlice(gpa, opts.strtab); - try self.symtab.ensureTotalCapacityPrecise(gpa, nsyms); - try self.symbols.ensureTotalCapacityPrecise(gpa, nsyms); - try self.symbols_extra.ensureTotalCapacityPrecise(gpa, nsyms * @sizeOf(Symbol.Extra)); - try self.symbols_resolver.ensureTotalCapacityPrecise(gpa, nsyms); - self.symbols_resolver.resize(gpa, nsyms) catch unreachable; - @memset(self.symbols_resolver.items, 0); - - 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 - // during symbol resolution. Thank you GNU! - const name_off = if (hidden) blk: { - const mangled = try std.fmt.allocPrint(gpa, "{s}@{s}", .{ - name, - self.versionString(self.versyms.items[i]), - }); - defer gpa.free(mangled); - break :blk try self.addString(gpa, mangled); - } else sym.st_name; - const out_esym_index: u32 = @intCast(self.symtab.items.len); - const out_esym = self.symtab.addOneAssumeCapacity(); - out_esym.* = sym; - out_esym.st_name = name_off; - const out_sym_index = self.addSymbolAssumeCapacity(); - const out_sym = &self.symbols.items[out_sym_index]; - out_sym.value = @intCast(out_esym.st_value); - out_sym.name_offset = name_off; - out_sym.ref = .{ .index = 0, .file = 0 }; - out_sym.esym_index = out_esym_index; - out_sym.version_index = self.versyms.items[out_esym_index]; - out_sym.extra_index = self.addSymbolExtraAssumeCapacity(.{}); + + const versyms = if (header.versym_sect_index) |versym_sect_index| vs: { + const shdr = header.sections[versym_sect_index]; + if (shdr.sh_size != symtab.len * @sizeOf(elf.Versym)) return error.BadVerSymSectionSize; + + const versyms = try gpa.alloc(elf.Versym, symtab.len); + errdefer gpa.free(versyms); + const buf = mem.sliceAsBytes(versyms); + const amt = try fs_file.preadAll(buf, shdr.sh_offset); + if (amt != buf.len) return error.UnexpectedEndOfFile; + break :vs versyms; + } else &.{}; + defer gpa.free(versyms); + + var nonlocal_esyms: std.ArrayListUnmanaged(elf.Elf64_Sym) = .empty; + defer nonlocal_esyms.deinit(gpa); + + var nonlocal_versyms: std.ArrayListUnmanaged(elf.Versym) = .empty; + defer nonlocal_versyms.deinit(gpa); + + var nonlocal_symbols: std.ArrayListUnmanaged(Parsed.Symbol) = .empty; + defer nonlocal_symbols.deinit(gpa); + + var strtab = header.strtab; + header.strtab = .empty; + defer strtab.deinit(gpa); + + for (symtab, 0..) |sym, i| { + const ver: elf.Versym = if (versyms.len == 0 or sym.st_shndx == elf.SHN_UNDEF) + .GLOBAL + else + .{ .VERSION = versyms[i].VERSION, .HIDDEN = false }; + + // https://github.com/ziglang/zig/issues/21678 + //if (ver == .LOCAL) continue; + if (@as(u16, @bitCast(ver)) == 0) continue; + + try nonlocal_esyms.ensureUnusedCapacity(gpa, 1); + try nonlocal_versyms.ensureUnusedCapacity(gpa, 1); + try nonlocal_symbols.ensureUnusedCapacity(gpa, 1); + + const name = Elf.stringTableLookup(strtab.items, sym.st_name); + const is_default = versyms.len == 0 or !versyms[i].HIDDEN; + const mangled_name = if (is_default) sym.st_name else mn: { + const off: u32 = @intCast(strtab.items.len); + const version_string = versionStringLookup(strtab.items, verstrings.items, versyms[i]); + try strtab.ensureUnusedCapacity(gpa, name.len + version_string.len + 2); + // Reload since the string table might have been resized. + const name2 = Elf.stringTableLookup(strtab.items, sym.st_name); + const version_string2 = versionStringLookup(strtab.items, verstrings.items, versyms[i]); + strtab.appendSliceAssumeCapacity(name2); + strtab.appendAssumeCapacity('@'); + strtab.appendSliceAssumeCapacity(version_string2); + strtab.appendAssumeCapacity(0); + break :mn off; + }; + + nonlocal_esyms.appendAssumeCapacity(sym); + nonlocal_versyms.appendAssumeCapacity(ver); + nonlocal_symbols.appendAssumeCapacity(.{ + .mangled_name = mangled_name, + }); } + + const sections = header.sections; + header.sections = &.{}; + errdefer gpa.free(sections); + + return .{ + .sections = sections, + .stat = header.stat, + .soname_index = header.soname_index, + .strtab = try strtab.toOwnedSlice(gpa), + .symtab = try nonlocal_esyms.toOwnedSlice(gpa), + .versyms = try nonlocal_versyms.toOwnedSlice(gpa), + .symbols = try nonlocal_symbols.toOwnedSlice(gpa), + .verstrings = try verstrings.toOwnedSlice(gpa), + }; } pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) !void { const gpa = elf_file.base.comp.gpa; - for (self.symtab.items, self.symbols_resolver.items, 0..) |esym, *resolv, i| { + for (self.parsed.symtab, self.symbols_resolver.items, 0..) |esym, *resolv, i| { const gop = try elf_file.resolver.getOrPut(gpa, .{ .index = @intCast(i), .file = self.index, @@ -253,7 +333,7 @@ pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) !void { } pub fn markLive(self: *SharedObject, elf_file: *Elf) void { - for (self.symtab.items, 0..) |esym, i| { + for (self.parsed.symtab, 0..) |esym, i| { if (esym.st_shndx != elf.SHN_UNDEF) continue; const ref = self.resolveSymbol(@intCast(i), elf_file); @@ -308,29 +388,21 @@ pub fn writeSymtab(self: *SharedObject, elf_file: *Elf) void { } } -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); +pub fn versionString(self: SharedObject, index: elf.Versym) [:0]const u8 { + return self.parsed.versionString(index); +} + +fn versionStringLookup(strtab: []const u8, verstrings: []const u32, index: elf.Versym) [:0]const u8 { + const off = verstrings[index.VERSION]; + return Elf.stringTableLookup(strtab, off); } pub fn asFile(self: *SharedObject) File { return .{ .shared_object = self }; } -fn verdefNum(self: *SharedObject) u32 { - for (self.dynamic_table.items) |entry| switch (entry.d_tag) { - elf.DT_VERDEFNUM => return @intCast(entry.d_val), - else => {}, - }; - return 0; -} - pub fn soname(self: *SharedObject) []const u8 { - for (self.dynamic_table.items) |entry| switch (entry.d_tag) { - elf.DT_SONAME => return self.getString(@intCast(entry.d_val)), - else => {}, - }; - return std.fs.path.basename(self.path.sub_path); + return self.parsed.soname() orelse self.path.basename(); } pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void { @@ -360,7 +432,7 @@ pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void { aliases.appendAssumeCapacity(@intCast(index)); } - std.mem.sort(u32, aliases.items, SortAlias{ .so = self, .ef = elf_file }, SortAlias.lessThan); + mem.sort(u32, aliases.items, SortAlias{ .so = self, .ef = elf_file }, SortAlias.lessThan); self.aliases = aliases.moveToUnmanaged(); } @@ -384,17 +456,8 @@ pub fn symbolAliases(self: *SharedObject, index: u32, elf_file: *Elf) []const u3 return aliases.items[start..end]; } -fn addString(self: *SharedObject, allocator: Allocator, str: []const u8) !u32 { - const off: u32 = @intCast(self.strtab.items.len); - try self.strtab.ensureUnusedCapacity(allocator, str.len + 1); - self.strtab.appendSliceAssumeCapacity(str); - self.strtab.appendAssumeCapacity(0); - return off; -} - pub fn getString(self: SharedObject, off: u32) [:0]const u8 { - assert(off < self.strtab.items.len); - return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); + return Elf.stringTableLookup(self.parsed.strtab, off); } pub fn resolveSymbol(self: SharedObject, index: Symbol.Index, elf_file: *Elf) Elf.Ref { @@ -402,25 +465,14 @@ pub fn resolveSymbol(self: SharedObject, index: Symbol.Index, elf_file: *Elf) El return elf_file.resolver.get(resolv).?; } -fn addSymbol(self: *SharedObject, allocator: Allocator) !Symbol.Index { - try self.symbols.ensureUnusedCapacity(allocator, 1); - return self.addSymbolAssumeCapacity(); -} - -fn addSymbolAssumeCapacity(self: *SharedObject) Symbol.Index { +pub fn addSymbolAssumeCapacity(self: *SharedObject) Symbol.Index { const index: Symbol.Index = @intCast(self.symbols.items.len); self.symbols.appendAssumeCapacity(.{ .file_index = self.index }); return index; } -pub fn addSymbolExtra(self: *SharedObject, allocator: Allocator, extra: Symbol.Extra) !u32 { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; - try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len); - return self.addSymbolExtraAssumeCapacity(extra); -} - pub fn addSymbolExtraAssumeCapacity(self: *SharedObject, extra: Symbol.Extra) u32 { - const index = @as(u32, @intCast(self.symbols_extra.items.len)); + const index: u32 = @intCast(self.symbols_extra.items.len); const fields = @typeInfo(Symbol.Extra).@"struct".fields; inline for (fields) |field| { self.symbols_extra.appendAssumeCapacity(switch (field.type) { @@ -465,7 +517,7 @@ pub fn format( _ = unused_fmt_string; _ = options; _ = writer; - @compileError("do not format shared objects directly"); + @compileError("unreachable"); } pub fn fmtSymtab(self: SharedObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { @@ -509,8 +561,10 @@ const elf = std.elf; const log = std.log.scoped(.elf); const mem = std.mem; const Path = std.Build.Cache.Path; - +const Stat = std.Build.Cache.File.Stat; const Allocator = mem.Allocator; + const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; const Symbol = @import("Symbol.zig"); +const Diags = @import("../../link.zig").Diags; diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index c01fa1988499b25a5b9171a6276e72e9152fd037..31584ca406ce2416ce1f2113063041dad628e290 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -22,7 +22,7 @@ esym_index: Index = 0, /// Index of the source version symbol this symbol references if any. /// If the symbol is unversioned it will have either VER_NDX_LOCAL or VER_NDX_GLOBAL. -version_index: elf.Elf64_Versym = elf.VER_NDX_LOCAL, +version_index: elf.Versym = .LOCAL, /// Misc flags for the symbol packaged as packed struct for compression. flags: Flags = .{}, @@ -87,6 +87,7 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File { pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { return switch (symbol.file(elf_file).?) { .zig_object => |x| x.symtab.items(.elf_sym)[symbol.esym_index], + .shared_object => |so| so.parsed.symtab[symbol.esym_index], inline else => |x| x.symtab.items[symbol.esym_index], }; } @@ -235,7 +236,7 @@ pub fn dsoAlignment(symbol: Symbol, elf_file: *Elf) !u64 { assert(file_ptr == .shared_object); const shared_object = file_ptr.shared_object; const esym = symbol.elfSym(elf_file); - const shdr = shared_object.shdrs.items[esym.st_shndx]; + const shdr = shared_object.parsed.sections[esym.st_shndx]; const alignment = @max(1, shdr.sh_addralign); return if (esym.st_value == 0) alignment @@ -351,8 +352,8 @@ fn formatName( const elf_file = ctx.elf_file; const symbol = ctx.symbol; try writer.writeAll(symbol.name(elf_file)); - switch (symbol.version_index & elf.VERSYM_VERSION) { - elf.VER_NDX_LOCAL, elf.VER_NDX_GLOBAL => {}, + switch (symbol.version_index.VERSION) { + @intFromEnum(elf.VER_NDX.LOCAL), @intFromEnum(elf.VER_NDX.GLOBAL) => {}, else => { const file_ptr = symbol.file(elf_file).?; assert(file_ptr == .shared_object); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index d97b5699e51317287a3a9e2ce1de4e42e6a95363..893d3ce336f29b9f1b450038b540ea96d392ff84 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -264,7 +264,7 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { } } -pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void { +pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void { // Handle any lazy symbols that were emitted by incremental compilation. if (self.lazy_syms.getPtr(.anyerror_type)) |metadata| { const pt: Zcu.PerThread = .{ .zcu = elf_file.base.comp.zcu.?, .tid = tid }; @@ -623,7 +623,7 @@ pub fn claimUnresolved(self: *ZigObject, elf_file: *Elf) void { global.ref = .{ .index = 0, .file = 0 }; global.esym_index = @intCast(index); global.file_index = self.index; - global.version_index = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version; + global.version_index = if (is_import) .LOCAL else elf_file.default_sym_version; global.flags.import = is_import; const idx = self.symbols_resolver.items[i]; @@ -689,8 +689,9 @@ pub fn markImportsExports(self: *ZigObject, elf_file: *Elf) void { const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file); const sym = elf_file.symbol(ref) orelse continue; const file = sym.file(elf_file).?; - if (sym.version_index == elf.VER_NDX_LOCAL) continue; - const vis = @as(elf.STV, @enumFromInt(sym.elfSym(elf_file).st_other)); + // https://github.com/ziglang/zig/issues/21678 + if (@as(u16, @bitCast(sym.version_index)) == @as(u16, @bitCast(elf.Versym.LOCAL))) continue; + const vis: elf.STV = @enumFromInt(sym.elfSym(elf_file).st_other); if (vis == .HIDDEN) continue; if (file == .shared_object and !sym.isAbs(elf_file)) { sym.flags.import = true; diff --git a/src/link/Elf/relocatable.zig b/src/link/Elf/relocatable.zig index 3af2d919d6e5aaf6e1f2cb1a5ba2eea7bf892f0b..c88e95fec02be8078b878ffef2ce0e4524875024 100644 --- a/src/link/Elf/relocatable.zig +++ b/src/link/Elf/relocatable.zig @@ -4,22 +4,22 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path for (comp.objects) |obj| { switch (Compilation.classifyFileExt(obj.path.sub_path)) { - .object => try parseObjectStaticLibReportingFailure(elf_file, obj.path), - .static_library => try parseArchiveStaticLibReportingFailure(elf_file, obj.path), - else => try elf_file.addParseError(obj.path, "unrecognized file extension", .{}), + .object => parseObjectStaticLibReportingFailure(elf_file, obj.path), + .static_library => parseArchiveStaticLibReportingFailure(elf_file, obj.path), + else => diags.addParseError(obj.path, "unrecognized file extension", .{}), } } for (comp.c_object_table.keys()) |key| { - try parseObjectStaticLibReportingFailure(elf_file, key.status.success.object_path); + parseObjectStaticLibReportingFailure(elf_file, key.status.success.object_path); } if (module_obj_path) |path| { - try parseObjectStaticLibReportingFailure(elf_file, path); + parseObjectStaticLibReportingFailure(elf_file, path); } if (comp.include_compiler_rt) { - try parseObjectStaticLibReportingFailure(elf_file, comp.compiler_rt_obj.?.full_object_path); + parseObjectStaticLibReportingFailure(elf_file, comp.compiler_rt_obj.?.full_object_path); } if (diags.hasErrors()) return error.FlushFailure; @@ -154,21 +154,17 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) l const diags = &comp.link_diags; for (comp.objects) |obj| { - if (obj.isObject()) { - try elf_file.parseObjectReportingFailure(obj.path); - } else { - try elf_file.parseLibraryReportingFailure(.{ .path = obj.path }, obj.must_link); - } + elf_file.parseInputReportingFailure(obj.path, false, obj.must_link); } // 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 elf_file.parseObjectReportingFailure(key.status.success.object_path); + elf_file.parseObjectReportingFailure(key.status.success.object_path); } - if (module_obj_path) |path| try elf_file.parseObjectReportingFailure(path); + if (module_obj_path) |path| elf_file.parseObjectReportingFailure(path); if (diags.hasErrors()) return error.FlushFailure; @@ -219,19 +215,19 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) l if (diags.hasErrors()) return error.FlushFailure; } -fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: Path) error{OutOfMemory}!void { +fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: Path) void { + const diags = &elf_file.base.comp.link_diags; parseObjectStaticLib(elf_file, path) catch |err| switch (err) { error.LinkFailure => return, - error.OutOfMemory => return error.OutOfMemory, - else => |e| try elf_file.addParseError(path, "parsing object failed: {s}", .{@errorName(e)}), + else => |e| diags.addParseError(path, "parsing object failed: {s}", .{@errorName(e)}), }; } -fn parseArchiveStaticLibReportingFailure(elf_file: *Elf, path: Path) error{OutOfMemory}!void { +fn parseArchiveStaticLibReportingFailure(elf_file: *Elf, path: Path) void { + const diags = &elf_file.base.comp.link_diags; parseArchiveStaticLib(elf_file, path) catch |err| switch (err) { error.LinkFailure => return, - error.OutOfMemory => return error.OutOfMemory, - else => |e| try elf_file.addParseError(path, "parsing static library failed: {s}", .{@errorName(e)}), + else => |e| diags.addParseError(path, "parsing static library failed: {s}", .{@errorName(e)}), }; } diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index 95bcb4c1a0df37f62f16ae7675a33da075b27525..6d26ec5d1ffda8c59e5c9d47d2360bc983bfff34 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -1345,8 +1345,8 @@ pub const GnuHashSection = struct { pub const VerneedSection = struct { verneed: std.ArrayListUnmanaged(elf.Elf64_Verneed) = .empty, - vernaux: std.ArrayListUnmanaged(elf.Elf64_Vernaux) = .empty, - index: elf.Elf64_Versym = elf.VER_NDX_GLOBAL + 1, + vernaux: std.ArrayListUnmanaged(elf.Vernaux) = .empty, + index: elf.Versym = .{ .VERSION = elf.Versym.GLOBAL.VERSION + 1, .HIDDEN = false }, pub fn deinit(vern: *VerneedSection, allocator: Allocator) void { vern.verneed.deinit(allocator); @@ -1363,7 +1363,7 @@ pub const VerneedSection = struct { /// Index of the defining this symbol version shared object file shared_object: File.Index, /// Version index - version_index: elf.Elf64_Versym, + version_index: elf.Versym, fn soname(this: @This(), ctx: *Elf) []const u8 { const shared_object = ctx.file(this.shared_object).?.shared_object; @@ -1376,7 +1376,8 @@ pub const VerneedSection = struct { } pub fn lessThan(ctx: *Elf, lhs: @This(), rhs: @This()) bool { - if (lhs.shared_object == rhs.shared_object) return lhs.version_index < rhs.version_index; + if (lhs.shared_object == rhs.shared_object) + return @as(u16, @bitCast(lhs.version_index)) < @as(u16, @bitCast(rhs.version_index)); return mem.lessThan(u8, lhs.soname(ctx), rhs.soname(ctx)); } }; @@ -1389,7 +1390,7 @@ pub const VerneedSection = struct { for (dynsyms, 1..) |entry, i| { const symbol = elf_file.symbol(entry.ref).?; - if (symbol.flags.import and symbol.version_index & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) { + if (symbol.flags.import and symbol.version_index.VERSION > elf.Versym.GLOBAL.VERSION) { const shared_object = symbol.file(elf_file).?.shared_object; verneed.appendAssumeCapacity(.{ .index = i, @@ -1404,11 +1405,12 @@ pub const VerneedSection = struct { var last = verneed.items[0]; var last_verneed = try vern.addVerneed(last.soname(elf_file), elf_file); var last_vernaux = try vern.addVernaux(last_verneed, last.versionString(elf_file), elf_file); - versyms[last.index] = last_vernaux.vna_other; + versyms[last.index] = @bitCast(last_vernaux.other); for (verneed.items[1..]) |ver| { if (ver.shared_object == last.shared_object) { - if (ver.version_index != last.version_index) { + // https://github.com/ziglang/zig/issues/21678 + if (@as(u16, @bitCast(ver.version_index)) != @as(u16, @bitCast(last.version_index))) { last_vernaux = try vern.addVernaux(last_verneed, ver.versionString(elf_file), elf_file); } } else { @@ -1416,7 +1418,7 @@ pub const VerneedSection = struct { last_vernaux = try vern.addVernaux(last_verneed, ver.versionString(elf_file), elf_file); } last = ver; - versyms[ver.index] = last_vernaux.vna_other; + versyms[ver.index] = @bitCast(last_vernaux.other); } // Fixup offsets @@ -1428,8 +1430,8 @@ pub const VerneedSection = struct { vsym.vn_aux = vernaux_off - verneed_off; var inner_off: u32 = 0; for (vern.vernaux.items[count..][0..vsym.vn_cnt], 0..) |*vaux, vaux_i| { - if (vaux_i < vsym.vn_cnt - 1) vaux.vna_next = @sizeOf(elf.Elf64_Vernaux); - inner_off += @sizeOf(elf.Elf64_Vernaux); + if (vaux_i < vsym.vn_cnt - 1) vaux.next = @sizeOf(elf.Vernaux); + inner_off += @sizeOf(elf.Vernaux); } vernaux_off += inner_off; verneed_off += @sizeOf(elf.Elf64_Verneed); @@ -1456,24 +1458,24 @@ pub const VerneedSection = struct { verneed_sym: *elf.Elf64_Verneed, version: [:0]const u8, elf_file: *Elf, - ) !elf.Elf64_Vernaux { + ) !elf.Vernaux { const comp = elf_file.base.comp; const gpa = comp.gpa; const sym = try vern.vernaux.addOne(gpa); sym.* = .{ - .vna_hash = HashSection.hasher(version), - .vna_flags = 0, - .vna_other = vern.index, - .vna_name = try elf_file.insertDynString(version), - .vna_next = 0, + .hash = HashSection.hasher(version), + .flags = 0, + .other = @bitCast(vern.index), + .name = try elf_file.insertDynString(version), + .next = 0, }; verneed_sym.vn_cnt += 1; - vern.index += 1; + vern.index.VERSION += 1; return sym.*; } pub fn size(vern: VerneedSection) usize { - return vern.verneed.items.len * @sizeOf(elf.Elf64_Verneed) + vern.vernaux.items.len * @sizeOf(elf.Elf64_Vernaux); + return vern.verneed.items.len * @sizeOf(elf.Elf64_Verneed) + vern.vernaux.items.len * @sizeOf(elf.Vernaux); } pub fn write(vern: VerneedSection, writer: anytype) !void { diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 10bae046da6591dfcaa4d86d71440909b2a9883a..d17cbb92b76d94e962e29f9f1f0f61f0f57d16fa 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -396,14 +396,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n } for (positionals.items) |obj| { - self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) { - error.UnknownFileType => try diags.reportParseError(obj.path, "unknown file type for an input file", .{}), - else => |e| try diags.reportParseError( - obj.path, - "unexpected error: reading input file failed with error {s}", - .{@errorName(e)}, - ), - }; + self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| + diags.addParseError(obj.path, "failed to read input file: {s}", .{@errorName(err)}); } var system_libs = std.ArrayList(SystemLib).init(gpa); @@ -443,14 +437,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n }; for (system_libs.items) |lib| { - self.classifyInputFile(lib.path, lib, false) catch |err| switch (err) { - error.UnknownFileType => try diags.reportParseError(lib.path, "unknown file type for an input file", .{}), - else => |e| try diags.reportParseError( - lib.path, - "unexpected error: parsing input file failed with error {s}", - .{@errorName(e)}, - ), - }; + self.classifyInputFile(lib.path, lib, false) catch |err| + diags.addParseError(lib.path, "failed to parse input file: {s}", .{@errorName(err)}); } // Finally, link against compiler_rt. @@ -460,14 +448,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n break :blk null; }; if (compiler_rt_path) |path| { - self.classifyInputFile(path, .{ .path = path }, false) catch |err| switch (err) { - error.UnknownFileType => try diags.reportParseError(path, "unknown file type for an input file", .{}), - else => |e| try diags.reportParseError( - path, - "unexpected error: parsing input file failed with error {s}", - .{@errorName(e)}, - ), - }; + self.classifyInputFile(path, .{ .path = path }, false) catch |err| + diags.addParseError(path, "failed to parse input file: {s}", .{@errorName(err)}); } try self.parseInputFiles(); @@ -796,7 +778,7 @@ pub fn resolveLibSystem( if (try accessLibPath(arena, &test_path, &checked_paths, dir, "System")) break :success; } - try diags.reportMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{}); + diags.addMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{}); return error.MissingLibSystem; } @@ -847,10 +829,7 @@ fn parseFatFile(self: *MachO, file: std.fs.File, path: Path) !?fat.Arch { for (fat_archs) |arch| { if (arch.tag == cpu_arch) return arch; } - try diags.reportParseError(path, "missing arch in universal file: expected {s}", .{ - @tagName(cpu_arch), - }); - return error.MissingCpuArch; + return diags.failParse(path, "missing arch in universal file: expected {s}", .{@tagName(cpu_arch)}); } pub fn readMachHeader(file: std.fs.File, offset: usize) !macho.mach_header_64 { diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index 8375abbc16e3d221388395fa03591bf908aad4b0..9ad25d77c7c29b1a68e7eb9ccd017e6c47236984 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -29,10 +29,9 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File pos += @sizeOf(ar_hdr); if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) { - try diags.reportParseError(path, "invalid header delimiter: expected '{s}', found '{s}'", .{ + return diags.failParse(path, "invalid header delimiter: expected '{s}', found '{s}'", .{ std.fmt.fmtSliceEscapeLower(ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag), }); - return error.MalformedArchive; } var hdr_size = try hdr.size(); diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index e45ff0f3c28582f0b5fb382723a1b3b55f853c2d..0ac07a85f904b7beb8a8e1fd99ead780a0e4c356 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -29,14 +29,8 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat } for (positionals.items) |obj| { - macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) { - error.UnknownFileType => try diags.reportParseError(obj.path, "unknown file type for an input file", .{}), - else => |e| try diags.reportParseError( - obj.path, - "unexpected error: reading input file failed with error {s}", - .{@errorName(e)}, - ), - }; + macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| + diags.addParseError(obj.path, "failed to read input file: {s}", .{@errorName(err)}); } if (diags.hasErrors()) return error.FlushFailure; @@ -95,14 +89,8 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? } for (positionals.items) |obj| { - macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) { - error.UnknownFileType => try diags.reportParseError(obj.path, "unknown file type for an input file", .{}), - else => |e| try diags.reportParseError( - obj.path, - "unexpected error: reading input file failed with error {s}", - .{@errorName(e)}, - ), - }; + macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| + diags.addParseError(obj.path, "failed to read input file: {s}", .{@errorName(err)}); } if (diags.hasErrors()) return error.FlushFailure; -- 2.54.0 From 9b0a3942ef22e35a08377265daf4bc3168a1ae21 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 12 Oct 2024 01:50:01 -0700 Subject: [PATCH 2/5] objcopy: update for std.elf type safety the new types make this code seem a bit strange --- lib/compiler/objcopy.zig | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/compiler/objcopy.zig b/lib/compiler/objcopy.zig index 9794671e6f9aeb13a98d4abb6147099f0e553129..a46dcab9c8a8e6c3f070c479c4086a236835436b 100644 --- a/lib/compiler/objcopy.zig +++ b/lib/compiler/objcopy.zig @@ -832,7 +832,6 @@ fn ElfFile(comptime is_64: bool) type { const Elf_Shdr = if (is_64) elf.Elf64_Shdr else elf.Elf32_Shdr; const Elf_Chdr = if (is_64) elf.Elf64_Chdr else elf.Elf32_Chdr; const Elf_Sym = if (is_64) elf.Elf64_Sym else elf.Elf32_Sym; - const Elf_Verdef = if (is_64) elf.Elf64_Verdef else elf.Elf32_Verdef; const Elf_OffSize = if (is_64) elf.Elf64_Off else elf.Elf32_Off; return struct { @@ -1179,10 +1178,13 @@ fn ElfFile(comptime is_64: bool) type { const data = try allocator.alignedAlloc(u8, section_memory_align, src_data.len); @memcpy(data, src_data); - const defs = @as([*]Elf_Verdef, @ptrCast(data))[0 .. @as(usize, @intCast(src.sh_size)) / @sizeOf(Elf_Verdef)]; + const defs = @as([*]elf.Verdef, @ptrCast(data))[0 .. @as(usize, @intCast(src.sh_size)) / @sizeOf(elf.Verdef)]; for (defs) |*def| { - if (def.vd_ndx != elf.SHN_UNDEF) - def.vd_ndx = sections_update[src.sh_info].remap_idx; + // Original author of this next line had elf.SHN_UNDEF + // here which does not make sense given that this field + // is elf.VER_NDX + if (def.ndx != .LOCAL) + def.ndx = @enumFromInt(sections_update[src.sh_info].remap_idx); } break :dst_data data; -- 2.54.0 From 344a21b14f8ddcd30d376953e2f4c6a81eae2ccb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 12 Oct 2024 01:54:56 -0700 Subject: [PATCH 3/5] fix 32-bit build --- src/link/Elf/SharedObject.zig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig index aa4c5f431766fe77526b49bfed33381b21f921b8..8b4f482226098f951639e2b0a7c7fb48b499971f 100644 --- a/src/link/Elf/SharedObject.zig +++ b/src/link/Elf/SharedObject.zig @@ -138,7 +138,7 @@ pub fn parseHeader( const dynamic_table: []elf.Elf64_Dyn = if (dynamic_sect_index) |index| dt: { const shdr = sections[index]; - const n = shdr.sh_size / @sizeOf(elf.Elf64_Dyn); + const n = std.math.cast(usize, shdr.sh_size / @sizeOf(elf.Elf64_Dyn)) orelse return error.Overflow; const dynamic_table = try gpa.alloc(elf.Elf64_Dyn, n); errdefer gpa.free(dynamic_table); const buf = mem.sliceAsBytes(dynamic_table); @@ -155,7 +155,8 @@ pub fn parseHeader( const dynsym_shdr = sections[index]; if (dynsym_shdr.sh_link >= sections.len) return error.BadStringTableIndex; const strtab_shdr = sections[dynsym_shdr.sh_link]; - const buf = try strtab.addManyAsSlice(gpa, strtab_shdr.sh_size); + const n = std.math.cast(usize, strtab_shdr.sh_size) orelse return error.Overflow; + const buf = try strtab.addManyAsSlice(gpa, n); const amt = try fs_file.preadAll(buf, strtab_shdr.sh_offset); if (amt != buf.len) return error.UnexpectedEndOfFile; } @@ -194,7 +195,7 @@ pub fn parse( ) !Parsed { const symtab = if (header.dynsym_sect_index) |index| st: { const shdr = header.sections[index]; - const n = shdr.sh_size / @sizeOf(elf.Elf64_Sym); + const n = std.math.cast(usize, shdr.sh_size / @sizeOf(elf.Elf64_Sym)) orelse return error.Overflow; const symtab = try gpa.alloc(elf.Elf64_Sym, n); errdefer gpa.free(symtab); const buf = mem.sliceAsBytes(symtab); -- 2.54.0 From 49637f537269ed98efb95a82e63ef73f6a9606a9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 12 Oct 2024 10:42:29 -0700 Subject: [PATCH 4/5] objcopy: check both global and local when remapping --- lib/compiler/objcopy.zig | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/compiler/objcopy.zig b/lib/compiler/objcopy.zig index a46dcab9c8a8e6c3f070c479c4086a236835436b..bf031bb39120ab44fe041e3f24e1ab9db96e5598 100644 --- a/lib/compiler/objcopy.zig +++ b/lib/compiler/objcopy.zig @@ -1179,13 +1179,10 @@ fn ElfFile(comptime is_64: bool) type { @memcpy(data, src_data); const defs = @as([*]elf.Verdef, @ptrCast(data))[0 .. @as(usize, @intCast(src.sh_size)) / @sizeOf(elf.Verdef)]; - for (defs) |*def| { - // Original author of this next line had elf.SHN_UNDEF - // here which does not make sense given that this field - // is elf.VER_NDX - if (def.ndx != .LOCAL) - def.ndx = @enumFromInt(sections_update[src.sh_info].remap_idx); - } + for (defs) |*def| switch (def.ndx) { + .LOCAL, .GLOBAL => {}, + else => def.ndx = @enumFromInt(sections_update[src.sh_info].remap_idx), + }; break :dst_data data; }, -- 2.54.0 From de04a8a1ef0ea37df7c09a997f60a3c1138de124 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 12 Oct 2024 10:43:58 -0700 Subject: [PATCH 5/5] std.dynamic_library: update to new elf API --- lib/std/dynamic_library.zig | 30 ++++++++++++++---------------- lib/std/os/linux/vdso.zig | 17 ++++++++--------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index 7fd231aba7fbef9e1c4318a74020d4506bc2c9da..8f07db68dab271757dfc7e95a2252d74e12f5205 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -141,7 +141,7 @@ pub const ElfDynLib = struct { strings: [*:0]u8, syms: [*]elf.Sym, hashtab: [*]posix.Elf_Symndx, - versym: ?[*]u16, + versym: ?[*]elf.Versym, verdef: ?*elf.Verdef, memory: []align(mem.page_size) u8, @@ -319,7 +319,7 @@ pub const ElfDynLib = struct { var maybe_strings: ?[*:0]u8 = null; var maybe_syms: ?[*]elf.Sym = null; var maybe_hashtab: ?[*]posix.Elf_Symndx = null; - var maybe_versym: ?[*]u16 = null; + var maybe_versym: ?[*]elf.Versym = null; var maybe_verdef: ?*elf.Verdef = null; { @@ -327,11 +327,11 @@ pub const ElfDynLib = struct { while (dynv[i] != 0) : (i += 2) { const p = base + dynv[i + 1]; switch (dynv[i]) { - elf.DT_STRTAB => maybe_strings = @as([*:0]u8, @ptrFromInt(p)), - elf.DT_SYMTAB => maybe_syms = @as([*]elf.Sym, @ptrFromInt(p)), - elf.DT_HASH => maybe_hashtab = @as([*]posix.Elf_Symndx, @ptrFromInt(p)), - elf.DT_VERSYM => maybe_versym = @as([*]u16, @ptrFromInt(p)), - elf.DT_VERDEF => maybe_verdef = @as(*elf.Verdef, @ptrFromInt(p)), + elf.DT_STRTAB => maybe_strings = @ptrFromInt(p), + elf.DT_SYMTAB => maybe_syms = @ptrFromInt(p), + elf.DT_HASH => maybe_hashtab = @ptrFromInt(p), + elf.DT_VERSYM => maybe_versym = @ptrFromInt(p), + elf.DT_VERDEF => maybe_verdef = @ptrFromInt(p), else => {}, } } @@ -399,18 +399,16 @@ pub const ElfDynLib = struct { } }; -fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [*:0]u8) bool { +fn checkver(def_arg: *elf.Verdef, vsym_arg: elf.Versym, vername: []const u8, strings: [*:0]u8) bool { var def = def_arg; - const vsym = @as(u32, @bitCast(vsym_arg)) & 0x7fff; + const vsym_index = vsym_arg.VERSION; while (true) { - if (0 == (def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym) - break; - if (def.vd_next == 0) - return false; - def = @as(*elf.Verdef, @ptrFromInt(@intFromPtr(def) + def.vd_next)); + if (0 == (def.flags & elf.VER_FLG_BASE) and @intFromEnum(def.ndx) == vsym_index) break; + if (def.next == 0) return false; + def = @ptrFromInt(@intFromPtr(def) + def.next); } - const aux = @as(*elf.Verdaux, @ptrFromInt(@intFromPtr(def) + def.vd_aux)); - return mem.eql(u8, vername, mem.sliceTo(strings + aux.vda_name, 0)); + const aux: *elf.Verdaux = @ptrFromInt(@intFromPtr(def) + def.aux); + return mem.eql(u8, vername, mem.sliceTo(strings + aux.name, 0)); } test "ElfDynLib" { diff --git a/lib/std/os/linux/vdso.zig b/lib/std/os/linux/vdso.zig index 516976098db2abc084dbe31658a5705414f88256..3d418994a6efdf17aada0fce0aa44d6b2fd25c8f 100644 --- a/lib/std/os/linux/vdso.zig +++ b/lib/std/os/linux/vdso.zig @@ -34,7 +34,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { const dynv = maybe_dynv orelse return 0; if (base == maxInt(usize)) return 0; - var maybe_strings: ?[*]u8 = null; + var maybe_strings: ?[*:0]u8 = null; var maybe_syms: ?[*]elf.Sym = null; var maybe_hashtab: ?[*]linux.Elf_Symndx = null; var maybe_versym: ?[*]elf.Versym = null; @@ -45,11 +45,11 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { while (dynv[i] != 0) : (i += 2) { const p = base +% dynv[i + 1]; switch (dynv[i]) { - elf.DT_STRTAB => maybe_strings = @as([*]u8, @ptrFromInt(p)), - elf.DT_SYMTAB => maybe_syms = @as([*]elf.Sym, @ptrFromInt(p)), - elf.DT_HASH => maybe_hashtab = @as([*]linux.Elf_Symndx, @ptrFromInt(p)), - elf.DT_VERSYM => maybe_versym = @as([*]elf.Versym, @ptrFromInt(p)), - elf.DT_VERDEF => maybe_verdef = @as(*elf.Verdef, @ptrFromInt(p)), + elf.DT_STRTAB => maybe_strings = @ptrFromInt(p), + elf.DT_SYMTAB => maybe_syms = @ptrFromInt(p), + elf.DT_HASH => maybe_hashtab = @ptrFromInt(p), + elf.DT_VERSYM => maybe_versym = @ptrFromInt(p), + elf.DT_VERDEF => maybe_verdef = @ptrFromInt(p), else => {}, } } @@ -80,7 +80,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { return 0; } -fn checkver(def_arg: *elf.Verdef, vsym_arg: elf.Versym, vername: []const u8, strings: [*]u8) bool { +fn checkver(def_arg: *elf.Verdef, vsym_arg: elf.Versym, vername: []const u8, strings: [*:0]u8) bool { var def = def_arg; const vsym_index = vsym_arg.VERSION; while (true) { @@ -89,6 +89,5 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: elf.Versym, vername: []const u8, str def = @ptrFromInt(@intFromPtr(def) + def.next); } const aux: *elf.Verdaux = @ptrFromInt(@intFromPtr(def) + def.aux); - const vda_name: [*:0]u8 = @ptrCast(strings + aux.name); - return mem.eql(u8, vername, mem.sliceTo(vda_name, 0)); + return mem.eql(u8, vername, mem.sliceTo(strings + aux.name, 0)); } -- 2.54.0