Merge pull request #21681 from ziglang/reduce-flush

link.Elf: eliminate an O(N^2) algorithm in flush()
This commit is contained in:
Andrew Kelley 2024-10-12 14:39:17 -07:00 committed by GitHub
commit f7c588286d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 808 additions and 742 deletions

View File

@ -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;
},

View File

@ -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 {

View File

@ -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" {

View File

@ -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

View File

@ -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));
}

View File

@ -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<lib>`, 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);
}

View File

@ -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,

View File

@ -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");

View File

@ -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),
});
}

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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)}),
};
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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();

View File

@ -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;