diff --git a/lib/compiler/objcopy.zig b/lib/compiler/objcopy.zig index 9794671e6f..bf031bb391 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,11 +1178,11 @@ 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)]; - for (defs) |*def| { - if (def.vd_ndx != elf.SHN_UNDEF) - def.vd_ndx = sections_update[src.sh_info].remap_idx; - } + const defs = @as([*]elf.Verdef, @ptrCast(data))[0 .. @as(usize, @intCast(src.sh_size)) / @sizeOf(elf.Verdef)]; + for (defs) |*def| switch (def.ndx) { + .LOCAL, .GLOBAL => {}, + else => def.ndx = @enumFromInt(sections_update[src.sh_info].remap_idx), + }; break :dst_data data; }, diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 53f1dcff29..bd98634ba8 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/dynamic_library.zig b/lib/std/dynamic_library.zig index 7fd231aba7..8f07db68da 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/elf.zig b/lib/std/elf.zig index 792694dffb..f61114b53d 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 4e62679b26..3d418994a6 100644 --- a/lib/std/os/linux/vdso.zig +++ b/lib/std/os/linux/vdso.zig @@ -34,10 +34,10 @@ 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: ?[*]u16 = null; + var maybe_versym: ?[*]elf.Versym = null; var maybe_verdef: ?*elf.Verdef = 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([*]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 => {}, } } @@ -80,17 +80,14 @@ 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: [*: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)); - const vda_name = @as([*:0]u8, @ptrCast(strings + aux.vda_name)); - return mem.eql(u8, vername, mem.sliceTo(vda_name, 0)); + const aux: *elf.Verdaux = @ptrFromInt(@intFromPtr(def) + def.aux); + return mem.eql(u8, vername, mem.sliceTo(strings + aux.name, 0)); } diff --git a/src/Compilation.zig b/src/Compilation.zig index 74da4b9fe2..1451c2adc3 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 ebfee34c4b..7280ce3df7 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(), + }; } - pub fn reportParseError( + 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 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 526c2932d9..0b1439ec80 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), - }, - .index = index, - .needed = lib.needed, - .alive = lib.needed, - } }); - try self.shared_objects.append(gpa, index); + const stat = Stat.fromFs(try handle.stat()); + var header = try SharedObject.parseHeader(gpa, diags, lib.path, handle, stat, target); + defer header.deinit(gpa); - const shared_object = self.file(index).?.shared_object; - try shared_object.parse(self, handle); + 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 = .{}, + }, + }); + const so = fileLookup(files.*, index).?.shared_object; + + // 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 7cc454f10c..50fffb0c19 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 8580f27d2d..4d8c23d2ff 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 7bb1f62104..349011a20a 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 2c0313609f..5aca15a205 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 7e6aabea38..8b4f482226 100644 --- a/src/link/Elf/SharedObject.zig +++ b/src/link/Elf/SharedObject.zig @@ -1,236 +1,317 @@ 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 = .{}, +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; +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 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 const Header = struct { + dynamic_table: []const elf.Elf64_Dyn, + soname_index: ?u32, + verdefnum: ?u32, -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; + 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 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; } - 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); + 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 = 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); + 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 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; } - 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{}; + 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, + }; + + 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, + .verdef_sect_index = verdef_sect_index, + .strtab = strtab, + .stat = stat, + }; +} + +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 = 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); + 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); - 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); + var verstrings: std.ArrayListUnmanaged(u32) = .empty; + defer verstrings.deinit(gpa); - try self.parseVersions(elf_file, handle, .{ - .symtab = symtab, - .verdef_sect_index = verdef_sect_index, - .versym_sect_index = versym_sect_index, - }); - - try self.initSymbols(elf_file, .{ - .symtab = symtab, - .strtab = strtab, - }); -} - -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; - - try self.verstrings.resize(gpa, 2); - self.verstrings.items[elf.VER_NDX_LOCAL] = 0; - self.verstrings.items[elf.VER_NDX_GLOBAL] = 0; - - 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); + + 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; } } - try self.versyms.ensureTotalCapacityPrecise(gpa, opts.symtab.len); + 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; - 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); - } - } else for (0..opts.symtab.len) |_| { - self.versyms.appendAssumeCapacity(elf.VER_NDX_GLOBAL); + 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, + }); } -} -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; + const sections = header.sections; + header.sections = &.{}; + errdefer gpa.free(sections); - 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(.{}); - } + 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 +334,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 +389,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 +433,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 +457,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 +466,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 +518,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 +562,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 c01fa19884..31584ca406 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 d97b5699e5..893d3ce336 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 3af2d919d6..c88e95fec0 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 95bcb4c1a0..6d26ec5d1f 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 10bae046da..d17cbb92b7 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 8375abbc16..9ad25d77c7 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 e45ff0f3c2..0ac07a85f9 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;