diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 8a020fc521..695bd4211b 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4354,8 +4354,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (try self.air.value(callee, pt)) |func_value| { if (func_value.getFunction(mod)) |func| { if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl); - const sym = elf_file.symbol(sym_index); + const zo = elf_file.zigObjectPtr().?; + const sym_index = try zo.getOrCreateMetadataForDecl(elf_file, func.owner_decl); + const sym = zo.symbol(sym_index); _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); const got_addr = @as(u32, @intCast(sym.zigGotAddress(elf_file))); try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr }); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index f923c001e1..19bef2f991 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -4336,8 +4336,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (try self.air.value(callee, pt)) |func_value| { if (func_value.getFunction(mod)) |func| { if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl); - const sym = elf_file.symbol(sym_index); + const zo = elf_file.zigObjectPtr().?; + const sym_index = try zo.getOrCreateMetadataForDecl(elf_file, func.owner_decl); + const sym = zo.symbol(sym_index); _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); const got_addr: u32 = @intCast(sym.zigGotAddress(elf_file)); try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr }); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 040afd3160..c4adb82ee6 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1409,14 +1409,14 @@ fn genLazy(func: *Func, lazy_sym: link.File.LazySymbol) InnerError!void { defer func.register_manager.unlockReg(data_lock); const elf_file = func.bin_file.cast(link.File.Elf).?; - const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, .{ + const zo = elf_file.zigObjectPtr().?; + const sym_index = zo.getOrCreateMetadataForLazySymbol(elf_file, pt, .{ .kind = .const_data, .ty = enum_ty, }) catch |err| return func.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym = elf_file.symbol(sym_index); - try func.genSetReg(Type.u64, data_reg, .{ .lea_symbol = .{ .sym = sym.esym_index } }); + try func.genSetReg(Type.u64, data_reg, .{ .lea_symbol = .{ .sym = sym_index } }); const cmp_reg, const cmp_lock = try func.allocReg(.int); defer func.register_manager.unlockReg(cmp_lock); @@ -4946,13 +4946,13 @@ fn genCall( }) { .func => |func_val| { if (func.bin_file.cast(link.File.Elf)) |elf_file| { - const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func_val.owner_decl); - const sym = elf_file.symbol(sym_index); + const zo = elf_file.zigObjectPtr().?; + const sym_index = try zo.getOrCreateMetadataForDecl(elf_file, func_val.owner_decl); if (func.mod.pic) { return func.fail("TODO: genCall pic", .{}); } else { - try func.genSetReg(Type.u64, .ra, .{ .load_symbol = .{ .sym = sym.esym_index } }); + try func.genSetReg(Type.u64, .ra, .{ .load_symbol = .{ .sym = sym_index } }); _ = try func.addInst(.{ .tag = .jalr, .data = .{ .i_type = .{ @@ -7822,14 +7822,14 @@ fn airTagName(func: *Func, inst: Air.Inst.Index) !void { const lazy_sym = link.File.LazySymbol.initDecl(.code, enum_ty.getOwnerDecl(zcu), zcu); const elf_file = func.bin_file.cast(link.File.Elf).?; - const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err| + const zo = elf_file.zigObjectPtr().?; + const sym_index = zo.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err| return func.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym = elf_file.symbol(sym_index); if (func.mod.pic) { return func.fail("TODO: airTagName pic", .{}); } else { - try func.genSetReg(Type.u64, .ra, .{ .load_symbol = .{ .sym = sym.esym_index } }); + try func.genSetReg(Type.u64, .ra, .{ .load_symbol = .{ .sym = sym_index } }); _ = try func.addInst(.{ .tag = .jalr, .data = .{ .i_type = .{ @@ -8047,11 +8047,7 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue { return error.CodegenFail; }; switch (lf.tag) { - .elf => { - const elf_file = lf.cast(link.File.Elf).?; - const local = elf_file.symbol(local_sym_index); - return MCValue{ .undef = local.esym_index }; - }, + .elf => return MCValue{ .undef = local_sym_index }, else => unreachable, } } diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index beb232b776..9345f5dacc 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -50,16 +50,16 @@ pub fn emitMir(emit: *Emit) Error!void { }; const elf_file = emit.bin_file.cast(link.File.Elf).?; + const zo = elf_file.zigObjectPtr().?; - const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; - const sym_index = elf_file.zigObjectPtr().?.symbol(symbol.sym_index); - const sym = elf_file.symbol(sym_index); + const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?; + const sym = zo.symbol(symbol.sym_index); var hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20); var lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I); if (sym.flags.needs_zig_got and !is_obj_or_static_lib) { - _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); + _ = try sym.getOrCreateZigGotEntry(symbol.sym_index, elf_file); hi_r_type = Elf.R_ZIG_GOT_HI20; lo_r_type = Elf.R_ZIG_GOT_LO12; @@ -82,8 +82,9 @@ pub fn emitMir(emit: *Emit) Error!void { }, .load_tlv_reloc => |symbol| { const elf_file = emit.bin_file.cast(link.File.Elf).?; + const zo = elf_file.zigObjectPtr().?; - const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; + const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?; const R_RISCV = std.elf.R_RISCV; @@ -107,7 +108,8 @@ pub fn emitMir(emit: *Emit) Error!void { }, .call_extern_fn_reloc => |symbol| { const elf_file = emit.bin_file.cast(link.File.Elf).?; - const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; + const zo = elf_file.zigObjectPtr().?; + const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?; const r_type: u32 = @intFromEnum(std.elf.R_RISCV.CALL_PLT); diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 1c3b2327b6..f9bcfe7003 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1354,8 +1354,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier switch (mod.intern_pool.indexToKey(func_value.ip_index)) { .func => |func| { const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { - const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl); - const sym = elf_file.symbol(sym_index); + const zo = elf_file.zigObjectPtr().?; + const sym_index = try zo.getOrCreateMetadataForDecl(elf_file, func.owner_decl); + const sym = zo.symbol(sym_index); _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); break :blk @as(u32, @intCast(sym.zigGotAddress(elf_file))); } else unreachable; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 4e227e2253..924de9d25a 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -12327,8 +12327,8 @@ fn genCall(self: *Self, info: union(enum) { }) { .func => |func| { if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl); - const sym = elf_file.symbol(sym_index); + const zo = elf_file.zigObjectPtr().?; + const sym_index = try zo.getOrCreateMetadataForDecl(elf_file, func.owner_decl); if (self.mod.pic) { const callee_reg: Register = switch (resolved_cc) { .SysV => callee: { @@ -12345,14 +12345,14 @@ fn genCall(self: *Self, info: union(enum) { try self.genSetReg( callee_reg, Type.usize, - .{ .load_symbol = .{ .sym = sym.esym_index } }, + .{ .load_symbol = .{ .sym = sym_index } }, .{}, ); try self.asmRegister(.{ ._, .call }, callee_reg); } else try self.asmMemory(.{ ._, .call }, .{ .base = .{ .reloc = .{ .atom_index = try self.owner.getSymbolIndex(self), - .sym_index = sym.esym_index, + .sym_index = sym_index, } }, .mod = .{ .rm = .{ .size = .qword } }, }); @@ -15320,16 +15320,16 @@ fn genLazySymbolRef( ) InnerError!void { const pt = self.pt; if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err| + const zo = elf_file.zigObjectPtr().?; + const sym_index = zo.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const sym = elf_file.symbol(sym_index); if (self.mod.pic) { switch (tag) { .lea, .call => try self.genSetReg(reg, Type.usize, .{ - .load_symbol = .{ .sym = sym.esym_index }, + .load_symbol = .{ .sym = sym_index }, }, .{}), .mov => try self.genSetReg(reg, Type.usize, .{ - .load_symbol = .{ .sym = sym.esym_index }, + .load_symbol = .{ .sym = sym_index }, }, .{}), else => unreachable, } @@ -15341,7 +15341,7 @@ fn genLazySymbolRef( } else { const reloc = bits.Symbol{ .atom_index = try self.owner.getSymbolIndex(self), - .sym_index = sym.esym_index, + .sym_index = sym_index, }; switch (tag) { .lea, .mov => try self.asmRegisterMemory(.{ ._, .mov }, reg.to64(), .{ diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index fd7a87f50d..bc439bd7ab 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -42,7 +42,8 @@ pub fn emitMir(emit: *Emit) Error!void { }), .linker_extern_fn => |symbol| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| { // Add relocation to the decl. - const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; + const zo = elf_file.zigObjectPtr().?; + const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?; const r_type = @intFromEnum(std.elf.R_X86_64.PLT32); try atom_ptr.addReloc(elf_file, .{ .r_offset = end_offset - 4, @@ -88,7 +89,8 @@ pub fn emitMir(emit: *Emit) Error!void { }), .linker_tlsld => |data| { const elf_file = emit.lower.bin_file.cast(link.File.Elf).?; - const atom = elf_file.symbol(data.atom_index).atom(elf_file).?; + const zo = elf_file.zigObjectPtr().?; + const atom = zo.symbol(data.atom_index).atom(elf_file).?; const r_type = @intFromEnum(std.elf.R_X86_64.TLSLD); try atom.addReloc(elf_file, .{ .r_offset = end_offset - 4, @@ -98,7 +100,8 @@ pub fn emitMir(emit: *Emit) Error!void { }, .linker_dtpoff => |data| { const elf_file = emit.lower.bin_file.cast(link.File.Elf).?; - const atom = elf_file.symbol(data.atom_index).atom(elf_file).?; + const zo = elf_file.zigObjectPtr().?; + const atom = zo.symbol(data.atom_index).atom(elf_file).?; const r_type = @intFromEnum(std.elf.R_X86_64.DTPOFF32); try atom.addReloc(elf_file, .{ .r_offset = end_offset - 4, @@ -112,11 +115,11 @@ pub fn emitMir(emit: *Emit) Error!void { .Obj => true, .Lib => emit.lower.link_mode == .static, }; - const atom = elf_file.symbol(data.atom_index).atom(elf_file).?; - const sym_index = elf_file.zigObjectPtr().?.symbol(data.sym_index); - const sym = elf_file.symbol(sym_index); + const zo = elf_file.zigObjectPtr().?; + const atom = zo.symbol(data.atom_index).atom(elf_file).?; + const sym = zo.symbol(data.sym_index); if (sym.flags.needs_zig_got and !is_obj_or_static_lib) { - _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); + _ = try sym.getOrCreateZigGotEntry(data.sym_index, elf_file); } if (emit.lower.pic) { const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib) diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index 2a19186176..1efcacdc2a 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -349,8 +349,8 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) assert(mem_op.sib.scale_index.scale == 0); if (lower.bin_file.cast(link.File.Elf)) |elf_file| { - const sym_index = elf_file.zigObjectPtr().?.symbol(sym.sym_index); - const elf_sym = elf_file.symbol(sym_index); + const zo = elf_file.zigObjectPtr().?; + const elf_sym = zo.symbol(sym.sym_index); if (elf_sym.flags.is_tls) { // TODO handle extern TLS vars, i.e., emit GD model diff --git a/src/codegen.zig b/src/codegen.zig index 9c3fd1914b..50688151ed 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -906,20 +906,20 @@ fn genDeclRef( const is_extern = decl.isExtern(zcu); if (lf.cast(link.File.Elf)) |elf_file| { + const zo = elf_file.zigObjectPtr().?; if (is_extern) { const name = decl.name.toSlice(ip); // TODO audit this const lib_name = if (decl.getOwnedVariable(zcu)) |ov| ov.lib_name.toSlice(ip) else null; const sym_index = try elf_file.getGlobalSymbol(name, lib_name); - elf_file.symbol(elf_file.zigObjectPtr().?.symbol(sym_index)).flags.needs_got = true; + zo.symbol(sym_index).flags.needs_got = true; return GenResult.mcv(.{ .load_symbol = sym_index }); } - const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); - const sym = elf_file.symbol(sym_index); + const sym_index = try zo.getOrCreateMetadataForDecl(elf_file, decl_index); if (is_threadlocal) { - return GenResult.mcv(.{ .load_tlv = sym.esym_index }); + return GenResult.mcv(.{ .load_tlv = sym_index }); } - return GenResult.mcv(.{ .load_symbol = sym.esym_index }); + return GenResult.mcv(.{ .load_symbol = sym_index }); } else if (lf.cast(link.File.MachO)) |macho_file| { const zo = macho_file.getZigObject().?; if (is_extern) { @@ -971,9 +971,7 @@ fn genUnnamedConst( }; switch (lf.tag) { .elf => { - const elf_file = lf.cast(link.File.Elf).?; - const local = elf_file.symbol(local_sym_index); - return GenResult.mcv(.{ .load_symbol = local.esym_index }); + return GenResult.mcv(.{ .load_symbol = local_sym_index }); }, .macho => { const macho_file = lf.cast(link.File.MachO).?; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index a8590c18d3..e5885cefc4 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -173,12 +173,7 @@ shstrtab_section_index: ?u32 = null, strtab_section_index: ?u32 = null, symtab_section_index: ?u32 = null, -/// An array of symbols parsed across all input files. -symbols: std.ArrayListUnmanaged(Symbol) = .{}, -symbols_extra: std.ArrayListUnmanaged(u32) = .{}, -symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{}, - -resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, +resolver: SymbolResolver = .{}, has_text_reloc: bool = false, num_ifunc_dynrelocs: usize = 0, @@ -192,10 +187,6 @@ merge_sections: std.ArrayListUnmanaged(MergeSection) = .{}, /// Table of last atom index in a section and matching atom free list if any. last_atom_and_free_list_table: LastAtomAndFreeListTable = .{}, -/// Global string table used to provide quick access to global symbol resolvers -/// such as `resolver`. -strings: StringTable = .{}, - first_eflags: ?elf.Elf64_Word = null, /// When allocating, the ideal_capacity is calculated by @@ -341,10 +332,6 @@ pub fn createEmpty( const gpa = comp.gpa; - // Index 0 is always a null symbol. - try self.symbols.append(gpa, .{}); - // Index 0 is always a null symbol. - try self.symbols_extra.append(gpa, 0); // Append null file at index 0 try self.files.append(gpa, .null); // Append null byte to string tables @@ -460,9 +447,6 @@ pub fn deinit(self: *Elf) void { self.shstrtab.deinit(gpa); self.symtab.deinit(gpa); self.strtab.deinit(gpa); - self.symbols.deinit(gpa); - self.symbols_extra.deinit(gpa); - self.symbols_free_list.deinit(gpa); self.resolver.deinit(gpa); for (self.thunks.items) |*th| { @@ -478,8 +462,6 @@ pub fn deinit(self: *Elf) void { } self.last_atom_and_free_list_table.deinit(gpa); - self.strings.deinit(gpa); - self.got.deinit(gpa); self.plt.deinit(gpa); self.plt_got.deinit(gpa); @@ -1300,6 +1282,9 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod try self.finalizeMergeSections(); try self.initOutputSections(); try self.initMergeSections(); + if (self.linkerDefinedPtr()) |obj| { + try obj.initStartStopSymbols(self); + } self.claimUnresolved(); // Scan and create missing synthetic entries such as GOT indirection. @@ -1922,20 +1907,17 @@ fn accessLibPath( /// 6. Re-run symbol resolution on pruned objects and shared objects sets. pub fn resolveSymbols(self: *Elf) !void { // Resolve symbols in the ZigObject. For now, we assume that it's always live. - if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resolveSymbols(self); + 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| self.file(index).?.resolveSymbols(self); - for (self.shared_objects.items) |index| self.file(index).?.resolveSymbols(self); - if (self.linkerDefinedPtr()) |obj| obj.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); + if (self.linkerDefinedPtr()) |obj| try obj.asFile().resolveSymbols(self); // Mark live objects. self.markLive(); // Reset state of all globals after marking live objects. - if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resetGlobals(self); - for (self.objects.items) |index| self.file(index).?.resetGlobals(self); - for (self.shared_objects.items) |index| self.file(index).?.resetGlobals(self); - if (self.linkerDefinedPtr()) |obj| obj.asFile().resetGlobals(self); + self.resolver.reset(); // Prune dead objects and shared objects. var i: usize = 0; @@ -1968,10 +1950,10 @@ pub fn resolveSymbols(self: *Elf) !void { } // Re-resolve the symbols. - if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resolveSymbols(self); - for (self.objects.items) |index| self.file(index).?.resolveSymbols(self); - for (self.shared_objects.items) |index| self.file(index).?.resolveSymbols(self); - if (self.linkerDefinedPtr()) |obj| obj.asFile().resolveSymbols(self); + 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); + if (self.linkerDefinedPtr()) |obj| try obj.asFile().resolveSymbols(self); } /// Traverses all objects and shared objects marking any object referenced by @@ -2005,46 +1987,17 @@ fn convertCommonSymbols(self: *Elf) !void { } fn markImportsExports(self: *Elf) void { - const mark = struct { - fn mark(elf_file: *Elf, file_index: File.Index) void { - for (elf_file.file(file_index).?.globals()) |global_index| { - const global = elf_file.symbol(global_index); - if (global.version_index == elf.VER_NDX_LOCAL) continue; - const file_ptr = global.file(elf_file) orelse continue; - const vis = @as(elf.STV, @enumFromInt(global.elfSym(elf_file).st_other)); - if (vis == .HIDDEN) continue; - if (file_ptr == .shared_object and !global.isAbs(elf_file)) { - global.flags.import = true; - continue; - } - if (file_ptr.index() == file_index) { - global.flags.@"export" = true; - if (elf_file.isEffectivelyDynLib() and vis != .PROTECTED) { - global.flags.import = true; - } - } - } - } - }.mark; - + if (self.zigObjectPtr()) |zo| { + zo.markImportsExports(self); + } + for (self.objects.items) |index| { + self.file(index).?.object.markImportsExports(self); + } if (!self.isEffectivelyDynLib()) { for (self.shared_objects.items) |index| { - for (self.file(index).?.globals()) |global_index| { - const global = self.symbol(global_index); - const file_ptr = global.file(self) orelse continue; - const vis = @as(elf.STV, @enumFromInt(global.elfSym(self).st_other)); - if (file_ptr != .shared_object and vis != .HIDDEN) global.flags.@"export" = true; - } + self.file(index).?.shared_object.markImportExports(self); } } - - if (self.zig_object_index) |index| { - mark(self, index); - } - - for (self.objects.items) |index| { - mark(self, index); - } } fn claimUnresolved(self: *Elf) void { @@ -2063,22 +2016,27 @@ fn claimUnresolved(self: *Elf) void { fn scanRelocs(self: *Elf) !void { const gpa = self.base.comp.gpa; - var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Ref)).init(gpa); + var undefs = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)).init(gpa); defer { - var it = undefs.iterator(); - while (it.next()) |entry| { - entry.value_ptr.deinit(); + for (undefs.values()) |*refs| { + refs.deinit(); } undefs.deinit(); } - var objects = try std.ArrayList(File.Index).initCapacity(gpa, self.objects.items.len + 1); - defer objects.deinit(); - if (self.zigObjectPtr()) |zo| objects.appendAssumeCapacity(zo.index); - objects.appendSliceAssumeCapacity(self.objects.items); - var has_reloc_errors = false; - for (objects.items) |index| { + if (self.zigObjectPtr()) |zo| { + zo.asFile().scanRelocs(self, &undefs) catch |err| switch (err) { + error.RelaxFailure => unreachable, + error.UnsupportedCpuArch => { + try self.reportUnsupportedCpuArch(); + return error.FlushFailure; + }, + error.RelocFailure => has_reloc_errors = true, + else => |e| return e, + }; + } + for (self.objects.items) |index| { self.file(index).?.scanRelocs(self, &undefs) catch |err| switch (err) { error.RelaxFailure => unreachable, error.UnsupportedCpuArch => { @@ -2094,47 +2052,18 @@ fn scanRelocs(self: *Elf) !void { if (has_reloc_errors) return error.FlushFailure; - for (self.symbols.items, 0..) |*sym, i| { - const index = @as(u32, @intCast(i)); - if (!sym.isLocal(self) and !sym.flags.has_dynamic) { - log.debug("'{s}' is non-local", .{sym.name(self)}); - try self.dynsym.addSymbol(index, self); - } - if (sym.flags.needs_got) { - log.debug("'{s}' needs GOT", .{sym.name(self)}); - _ = try self.got.addGotSymbol(index, self); - } - if (sym.flags.needs_plt) { - if (sym.flags.is_canonical) { - log.debug("'{s}' needs CPLT", .{sym.name(self)}); - sym.flags.@"export" = true; - try self.plt.addSymbol(index, self); - } else if (sym.flags.needs_got) { - log.debug("'{s}' needs PLTGOT", .{sym.name(self)}); - try self.plt_got.addSymbol(index, self); - } else { - log.debug("'{s}' needs PLT", .{sym.name(self)}); - try self.plt.addSymbol(index, self); - } - } - if (sym.flags.needs_copy_rel and !sym.flags.has_copy_rel) { - log.debug("'{s}' needs COPYREL", .{sym.name(self)}); - try self.copy_rel.addSymbol(index, self); - } - if (sym.flags.needs_tlsgd) { - log.debug("'{s}' needs TLSGD", .{sym.name(self)}); - try self.got.addTlsGdSymbol(index, self); - } - if (sym.flags.needs_gottp) { - log.debug("'{s}' needs GOTTP", .{sym.name(self)}); - try self.got.addGotTpSymbol(index, self); - } - if (sym.flags.needs_tlsdesc) { - log.debug("'{s}' needs TLSDESC", .{sym.name(self)}); - try self.got.addTlsDescSymbol(index, self); - } + if (self.zigObjectPtr()) |zo| { + try zo.asFile().createSymbolIndirection(self); + } + for (self.objects.items) |index| { + try self.file(index).?.createSymbolIndirection(self); + } + for (self.shared_objects.items) |index| { + try self.file(index).?.createSymbolIndirection(self); + } + if (self.linkerDefinedPtr()) |obj| { + try obj.asFile().createSymbolIndirection(self); } - if (self.got.flags.needs_tlsld) { log.debug("program needs TLSLD", .{}); try self.got.addTlsLdSymbol(self); @@ -2911,8 +2840,8 @@ pub fn writeElfHeader(self: *Elf) !void { index += 4; const e_entry: u64 = if (self.linkerDefinedPtr()) |obj| blk: { - const entry_index = obj.entry_index orelse break :blk 0; - break :blk @intCast(self.symbol(entry_index).address(.{}, self)); + const entry_sym = obj.entrySymbol(self) orelse break :blk 0; + break :blk @intCast(entry_sym.address(.{}, self)); } else 0; const phdr_table_offset = if (self.phdr_table_index) |phndx| self.phdrs.items[phndx].p_offset else 0; switch (self.ptr_width) { @@ -3043,7 +2972,7 @@ pub fn deleteExport( fn checkDuplicates(self: *Elf) !void { const gpa = self.base.comp.gpa; - var dupes = std.AutoArrayHashMap(Symbol.Index, std.ArrayListUnmanaged(File.Index)).init(gpa); + var dupes = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)).init(gpa); defer { for (dupes.values()) |*list| { list.deinit(gpa); @@ -3351,7 +3280,7 @@ fn initSyntheticSections(self: *Elf) !void { }); const needs_versions = for (self.dynsym.entries.items) |entry| { - const sym = self.symbol(entry.symbol_index); + const sym = self.symbol(entry.ref).?; if (sym.flags.import and sym.version_index & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) break true; } else false; if (needs_versions) { @@ -3565,7 +3494,7 @@ fn setVersionSymtab(self: *Elf) !void { try self.versym.resize(gpa, self.dynsym.count()); self.versym.items[0] = elf.VER_NDX_LOCAL; for (self.dynsym.entries.items, 1..) |entry, i| { - const sym = self.symbol(entry.symbol_index); + const sym = self.symbol(entry.ref).?; self.versym.items[i] = sym.version_index; } @@ -4357,11 +4286,10 @@ fn allocateSpecialPhdrs(self: *Elf) void { fn writeAtoms(self: *Elf) !void { const gpa = self.base.comp.gpa; - var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Ref)).init(gpa); + var undefs = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)).init(gpa); defer { - var it = undefs.iterator(); - while (it.next()) |entry| { - entry.value_ptr.deinit(); + for (undefs.values()) |*refs| { + refs.deinit(); } undefs.deinit(); } @@ -4507,11 +4435,13 @@ pub fn updateSymtabSize(self: *Elf) !void { strsize += ctx.strsize; } - if (self.zig_got_section_index) |_| { - self.zig_got.output_symtab_ctx.ilocal = nlocals + 1; - self.zig_got.updateSymtabSize(self); - nlocals += self.zig_got.output_symtab_ctx.nlocals; - strsize += self.zig_got.output_symtab_ctx.strsize; + if (self.zigObjectPtr()) |_| { + if (self.zig_got_section_index) |_| { + self.zig_got.output_symtab_ctx.ilocal = nlocals + 1; + self.zig_got.updateSymtabSize(self); + nlocals += self.zig_got.output_symtab_ctx.nlocals; + strsize += self.zig_got.output_symtab_ctx.strsize; + } } if (self.got_section_index) |_| { @@ -4650,7 +4580,9 @@ fn writeSyntheticSections(self: *Elf) !void { const shdr = self.shdrs.items[shndx]; try self.got.addRela(self); try self.copy_rel.addRela(self); - try self.zig_got.addRela(self); + if (self.zigObjectPtr()) |_| { + try self.zig_got.addRela(self); + } self.sortRelaDyn(); try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_dyn.items), shdr.sh_offset); } @@ -5311,13 +5243,21 @@ pub fn calcNumIRelativeRelocs(self: *Elf) usize { for (self.got.entries.items) |entry| { if (entry.tag != .got) continue; - const sym = self.symbol(entry.symbol_index); + const sym = self.symbol(entry.ref).?; if (sym.isIFunc(self)) count += 1; } return count; } +pub fn getStartStopBasename(self: Elf, shdr: elf.Elf64_Shdr) ?[]const u8 { + const name = self.getShString(shdr.sh_name); + if (shdr.sh_flags & elf.SHF_ALLOC != 0 and name.len > 0) { + if (Elf.isCIdentifier(name)) return name; + } + return null; +} + pub fn isCIdentifier(name: []const u8) bool { if (name.len == 0) return false; const first_c = name[0]; @@ -5328,14 +5268,6 @@ pub fn isCIdentifier(name: []const u8) bool { return true; } -pub fn getStartStopBasename(self: *Elf, shdr: elf.Elf64_Shdr) ?[]const u8 { - const name = self.getShString(shdr.sh_name); - if (shdr.sh_flags & elf.SHF_ALLOC != 0 and name.len > 0) { - if (isCIdentifier(name)) return name; - } - return null; -} - pub fn addThunk(self: *Elf) !Thunk.Index { const index = @as(Thunk.Index, @intCast(self.thunks.items.len)); const th = try self.thunks.addOne(self.base.comp.gpa); @@ -5381,95 +5313,9 @@ pub fn comdatGroup(self: *Elf, ref: Ref) *ComdatGroup { return self.file(ref.file).?.comdatGroup(ref.index); } -/// Returns pointer-to-symbol described at sym_index. -pub fn symbol(self: *Elf, sym_index: Symbol.Index) *Symbol { - return &self.symbols.items[sym_index]; -} - -pub fn addSymbol(self: *Elf) !Symbol.Index { - const gpa = self.base.comp.gpa; - try self.symbols.ensureUnusedCapacity(gpa, 1); - const index = blk: { - if (self.symbols_free_list.popOrNull()) |index| { - log.debug(" (reusing symbol index {d})", .{index}); - break :blk index; - } else { - log.debug(" (allocating symbol index {d})", .{self.symbols.items.len}); - const index: Symbol.Index = @intCast(self.symbols.items.len); - _ = self.symbols.addOneAssumeCapacity(); - break :blk index; - } - }; - self.symbols.items[index] = .{}; - return index; -} - -pub fn addSymbolExtra(self: *Elf, extra: Symbol.Extra) !u32 { - const gpa = self.base.comp.gpa; - const fields = @typeInfo(Symbol.Extra).Struct.fields; - try self.symbols_extra.ensureUnusedCapacity(gpa, fields.len); - return self.addSymbolExtraAssumeCapacity(extra); -} - -pub fn addSymbolExtraAssumeCapacity(self: *Elf, extra: Symbol.Extra) u32 { - const index = @as(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) { - u32 => @field(extra, field.name), - else => @compileError("bad field type"), - }); - } - return index; -} - -pub fn symbolExtra(self: *Elf, index: u32) ?Symbol.Extra { - if (index == 0) return null; - const fields = @typeInfo(Symbol.Extra).Struct.fields; - var i: usize = index; - var result: Symbol.Extra = undefined; - inline for (fields) |field| { - @field(result, field.name) = switch (field.type) { - u32 => self.symbols_extra.items[i], - else => @compileError("bad field type"), - }; - i += 1; - } - return result; -} - -pub fn setSymbolExtra(self: *Elf, index: u32, extra: Symbol.Extra) void { - assert(index > 0); - const fields = @typeInfo(Symbol.Extra).Struct.fields; - inline for (fields, 0..) |field, i| { - self.symbols_extra.items[index + i] = switch (field.type) { - u32 => @field(extra, field.name), - else => @compileError("bad field type"), - }; - } -} - -const GetOrPutGlobalResult = struct { - found_existing: bool, - index: Symbol.Index, -}; - -pub fn getOrPutGlobal(self: *Elf, name: []const u8) !GetOrPutGlobalResult { - const gpa = self.base.comp.gpa; - const name_off = try self.strings.insert(gpa, name); - const gop = try self.resolver.getOrPut(gpa, name_off); - if (!gop.found_existing) { - const index = try self.addSymbol(); - log.debug("added symbol '{s}' at index {d}", .{ name, index }); - const global = self.symbol(index); - global.name_offset = name_off; - global.flags.global = true; - gop.value_ptr.* = index; - } - return .{ - .found_existing = gop.found_existing, - .index = gop.value_ptr.*, - }; +pub fn symbol(self: *Elf, ref: Ref) ?*Symbol { + const file_ptr = self.file(ref.file) orelse return null; + return file_ptr.symbol(ref.index); } pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 { @@ -5579,36 +5425,34 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void { try self.base.comp.link_errors.ensureUnusedCapacity(gpa, undefs.count()); - var it = undefs.iterator(); - while (it.next()) |entry| { - const undef_index = entry.key_ptr.*; - const atoms = entry.value_ptr.*.items; - const natoms = @min(atoms.len, max_notes); - const nnotes = natoms + @intFromBool(atoms.len > max_notes); + for (undefs.keys(), undefs.values()) |key, refs| { + const undef_sym = self.resolver.keys.items[key - 1]; + const nrefs = @min(refs.items.len, max_notes); + const nnotes = nrefs + @intFromBool(refs.items.len > max_notes); var err = try self.base.addErrorWithNotesAssumeCapacity(nnotes); - try err.addMsg("undefined symbol: {s}", .{self.symbol(undef_index).name(self)}); + try err.addMsg("undefined symbol: {s}", .{undef_sym.name(self)}); - for (atoms[0..natoms]) |ref| { + for (refs.items[0..nrefs]) |ref| { const atom_ptr = self.atom(ref).?; - const file_ptr = self.file(ref.file).?; + const file_ptr = atom_ptr.file(self).?; try err.addNote("referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) }); } - if (atoms.len > max_notes) { - const remaining = atoms.len - max_notes; + if (refs.items.len > max_notes) { + const remaining = refs.items.len - max_notes; try err.addNote("referenced {d} more times", .{remaining}); } } } fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemory }!void { + if (dupes.keys().len == 0) return; // Nothing to do + const max_notes = 3; - var has_dupes = false; - var it = dupes.iterator(); - while (it.next()) |entry| { - const sym = self.symbol(entry.key_ptr.*); - const notes = entry.value_ptr.*; + + for (dupes.keys(), dupes.values()) |key, notes| { + const sym = self.resolver.keys.items[key - 1]; const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes); var err = try self.base.addErrorWithNotes(nnotes + 1); @@ -5625,11 +5469,9 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor const remaining = notes.items.len - max_notes; try err.addNote("defined {d} more times", .{remaining}); } - - has_dupes = true; } - if (has_dupes) return error.HasDuplicates; + return error.HasDuplicates; } fn reportMissingLibraryError( @@ -6018,6 +5860,10 @@ pub const Ref = struct { index: u32, file: u32, + pub fn eql(ref: Ref, other: Ref) bool { + return ref.index == other.index and ref.file == other.file; + } + pub fn format( ref: Ref, comptime unused_fmt_string: []const u8, @@ -6030,6 +5876,96 @@ pub const Ref = struct { } }; +pub const SymbolResolver = struct { + keys: std.ArrayListUnmanaged(Key) = .{}, + values: std.ArrayListUnmanaged(Ref) = .{}, + table: std.AutoArrayHashMapUnmanaged(void, void) = .{}, + + const Result = struct { + found_existing: bool, + index: Index, + ref: *Ref, + }; + + pub fn deinit(resolver: *SymbolResolver, allocator: Allocator) void { + resolver.keys.deinit(allocator); + resolver.values.deinit(allocator); + resolver.table.deinit(allocator); + } + + pub fn getOrPut( + resolver: *SymbolResolver, + allocator: Allocator, + ref: Ref, + elf_file: *Elf, + ) !Result { + const adapter = Adapter{ .keys = resolver.keys.items, .elf_file = elf_file }; + const key = Key{ .index = ref.index, .file_index = ref.file }; + const gop = try resolver.table.getOrPutAdapted(allocator, key, adapter); + if (!gop.found_existing) { + try resolver.keys.append(allocator, key); + _ = try resolver.values.addOne(allocator); + } + return .{ + .found_existing = gop.found_existing, + .index = @intCast(gop.index + 1), + .ref = &resolver.values.items[gop.index], + }; + } + + pub fn get(resolver: SymbolResolver, index: Index) ?Ref { + if (index == 0) return null; + return resolver.values.items[index - 1]; + } + + pub fn reset(resolver: *SymbolResolver) void { + resolver.keys.clearRetainingCapacity(); + resolver.values.clearRetainingCapacity(); + resolver.table.clearRetainingCapacity(); + } + + const Key = struct { + index: Symbol.Index, + file_index: File.Index, + + fn name(key: Key, elf_file: *Elf) [:0]const u8 { + const ref = Ref{ .index = key.index, .file = key.file_index }; + return elf_file.symbol(ref).?.name(elf_file); + } + + fn file(key: Key, elf_file: *Elf) ?File { + return elf_file.file(key.file_index); + } + + fn eql(key: Key, other: Key, elf_file: *Elf) bool { + const key_name = key.name(elf_file); + const other_name = other.name(elf_file); + return mem.eql(u8, key_name, other_name); + } + + fn hash(key: Key, elf_file: *Elf) u32 { + return @truncate(Hash.hash(0, key.name(elf_file))); + } + }; + + const Adapter = struct { + keys: []const Key, + elf_file: *Elf, + + pub fn eql(ctx: @This(), key: Key, b_void: void, b_map_index: usize) bool { + _ = b_void; + const other = ctx.keys[b_map_index]; + return key.eql(other, ctx.elf_file); + } + + pub fn hash(ctx: @This(), key: Key) u32 { + return key.hash(ctx.elf_file); + } + }; + + pub const Index = u32; +}; + const LastAtomAndFreeList = struct { /// Index of the last allocated atom in this section. last_atom_index: Atom.Index = 0, @@ -6143,6 +6079,7 @@ const File = @import("Elf/file.zig").File; const GnuHashSection = synthetic_sections.GnuHashSection; const GotSection = synthetic_sections.GotSection; const GotPltSection = synthetic_sections.GotPltSection; +const Hash = std.hash.Wyhash; const HashSection = synthetic_sections.HashSection; const InputMergeSection = merge_section.InputMergeSection; const LdScript = @import("Elf/LdScript.zig"); diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index b91b546103..f2757f570b 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -326,12 +326,8 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El const cpu_arch = elf_file.getTarget().cpu.arch; const file_ptr = self.file(elf_file).?; for (self.relocs(elf_file)) |rel| { - const target_index = switch (file_ptr) { - .zig_object => |x| x.symbol(rel.r_sym()), - .object => |x| x.symbols.items[rel.r_sym()], - else => unreachable, - }; - const target = elf_file.symbol(target_index); + const target_ref = file_ptr.resolveSymbol(rel.r_sym(), elf_file); + const target = elf_file.symbol(target_ref).?; const r_type = rel.r_type(); const r_offset: u64 = @intCast(self.value + @as(i64, @intCast(rel.r_offset))); var r_addend = rel.r_addend; @@ -422,12 +418,21 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype const r_kind = relocation.decode(rel.r_type(), cpu_arch); if (r_kind == .none) continue; - const symbol_index = switch (file_ptr) { - .zig_object => |x| x.symbol(rel.r_sym()), - .object => |x| x.symbols.items[rel.r_sym()], - else => unreachable, + const symbol_ref = file_ptr.resolveSymbol(rel.r_sym(), elf_file); + const symbol = elf_file.symbol(symbol_ref) orelse { + const sym_name = switch (file_ptr) { + .zig_object => |x| x.symbol(rel.r_sym()).name(elf_file), + inline else => |x| x.symbols.items[rel.r_sym()].name(elf_file), + }; + // Violation of One Definition Rule for COMDATs. + // TODO convert into an error + log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{ + file_ptr.fmtPath(), + self.name(elf_file), + sym_name, + }); + continue; }; - const symbol = elf_file.symbol(symbol_index); const is_synthetic_symbol = switch (file_ptr) { .zig_object => false, // TODO: implement this once we support merge sections in ZigObject @@ -435,19 +440,8 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype else => unreachable, }; - // Check for violation of One Definition Rule for COMDATs. - if (symbol.file(elf_file) == null) { - // TODO convert into an error - log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{ - file_ptr.fmtPath(), - self.name(elf_file), - symbol.name(elf_file), - }); - continue; - } - // Report an undefined symbol. - if (!is_synthetic_symbol and (try self.reportUndefined(elf_file, symbol, symbol_index, rel, undefs))) + if (!is_synthetic_symbol and (try self.reportUndefined(elf_file, symbol, rel, undefs))) continue; if (symbol.isIFunc(elf_file)) { @@ -694,16 +688,15 @@ fn reportUndefined( self: Atom, elf_file: *Elf, sym: *const Symbol, - sym_index: Symbol.Index, rel: elf.Elf64_Rela, undefs: anytype, ) !bool { const comp = elf_file.base.comp; const gpa = comp.gpa; - const rel_esym = switch (self.file(elf_file).?) { - .zig_object => |x| x.elfSym(rel.r_sym()).*, - .object => |x| x.symtab.items[rel.r_sym()], - else => unreachable, + const file_ptr = self.file(elf_file).?; + const rel_esym = switch (file_ptr) { + .zig_object => |x| x.symbol(rel.r_sym()).elfSym(elf_file), + inline else => |x| x.symtab.items[rel.r_sym()], }; const esym = sym.elfSym(elf_file); if (rel_esym.st_shndx == elf.SHN_UNDEF and @@ -712,7 +705,12 @@ fn reportUndefined( !sym.flags.import and esym.st_shndx == elf.SHN_UNDEF) { - const gop = try undefs.getOrPut(sym_index); + const idx = switch (file_ptr) { + .zig_object => |x| x.symbols_resolver.items[rel.r_sym() & ZigObject.symbol_mask], + .object => |x| x.symbols_resolver.items[rel.r_sym() - x.first_global.?], + inline else => |x| x.symbols_resolver.items[rel.r_sym()], + }; + const gop = try undefs.getOrPut(idx); if (!gop.found_existing) { gop.value_ptr.* = std.ArrayList(Elf.Ref).init(gpa); } @@ -737,11 +735,8 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) RelocError!voi const r_kind = relocation.decode(rel.r_type(), cpu_arch); if (r_kind == .none) continue; - const target = switch (file_ptr) { - .zig_object => |x| elf_file.symbol(x.symbol(rel.r_sym())), - .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]), - else => unreachable, - }; + const target_ref = file_ptr.resolveSymbol(rel.r_sym(), elf_file); + const target = elf_file.symbol(target_ref).?; const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; // We will use equation format to resolve relocations: @@ -845,7 +840,7 @@ fn resolveDynAbsReloc( if (is_writeable or elf_file.z_nocopyreloc) { elf_file.addRelaDynAssumeCapacity(.{ .offset = P, - .sym = target.extra(elf_file).?.dynamic, + .sym = target.extra(elf_file).dynamic, .type = relocation.encode(.abs, cpu_arch), .addend = A, }); @@ -859,7 +854,7 @@ fn resolveDynAbsReloc( if (is_writeable) { elf_file.addRelaDynAssumeCapacity(.{ .offset = P, - .sym = target.extra(elf_file).?.dynamic, + .sym = target.extra(elf_file).dynamic, .type = relocation.encode(.abs, cpu_arch), .addend = A, }); @@ -872,7 +867,7 @@ fn resolveDynAbsReloc( .dynrel => { elf_file.addRelaDynAssumeCapacity(.{ .offset = P, - .sym = target.extra(elf_file).?.dynamic, + .sym = target.extra(elf_file).dynamic, .type = relocation.encode(.abs, cpu_arch), .addend = A, }); @@ -923,31 +918,29 @@ pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: any const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; - const target_index = switch (file_ptr) { - .zig_object => |x| x.symbol(rel.r_sym()), - .object => |x| x.symbols.items[rel.r_sym()], - else => unreachable, + const target_ref = file_ptr.resolveSymbol(rel.r_sym(), elf_file); + const target = elf_file.symbol(target_ref) orelse { + const sym_name = switch (file_ptr) { + .zig_object => |x| x.symbol(rel.r_sym()).name(elf_file), + inline else => |x| x.symbols.items[rel.r_sym()].name(elf_file), + }; + // Violation of One Definition Rule for COMDATs. + // TODO convert into an error + log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{ + file_ptr.fmtPath(), + self.name(elf_file), + sym_name, + }); + continue; }; - const target = elf_file.symbol(target_index); const is_synthetic_symbol = switch (file_ptr) { .zig_object => false, // TODO: implement this once we support merge sections in ZigObject .object => |x| rel.r_sym() >= x.symtab.items.len, else => unreachable, }; - // Check for violation of One Definition Rule for COMDATs. - if (target.file(elf_file) == null) { - // TODO convert into an error - log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{ - file_ptr.fmtPath(), - self.name(elf_file), - target.name(elf_file), - }); - continue; - } - // Report an undefined symbol. - if (!is_synthetic_symbol and (try self.reportUndefined(elf_file, target, target_index, rel, undefs))) + if (!is_synthetic_symbol and (try self.reportUndefined(elf_file, target, rel, undefs))) continue; // We will use equation format to resolve relocations: @@ -1766,11 +1759,7 @@ const aarch64 = struct { => { const disp: i28 = math.cast(i28, S + A - P) orelse blk: { const th = atom.thunk(elf_file); - const target_index = switch (file_ptr) { - .zig_object => |x| x.symbol(rel.r_sym()), - .object => |x| x.symbols.items[rel.r_sym()], - else => unreachable, - }; + const target_index = file_ptr.resolveSymbol(rel.r_sym(), elf_file); const S_ = th.targetAddress(target_index, elf_file); break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow; }; @@ -2106,11 +2095,8 @@ const riscv = struct { return error.RelocFailure; }; it.pos = pos; - const target_ = switch (file_ptr) { - .zig_object => |x| elf_file.symbol(x.symbol(pair.r_sym())), - .object => |x| elf_file.symbol(x.symbols.items[pair.r_sym()]), - else => unreachable, - }; + const target_ref_ = file_ptr.resolveSymbol(pair.r_sym(), elf_file); + const target_ = elf_file.symbol(target_ref_).?; const S_ = target_.address(.{}, elf_file); const A_ = pair.r_addend; const P_ = atom_addr + @as(i64, @intCast(pair.r_offset)); @@ -2313,4 +2299,5 @@ const File = @import("file.zig").File; const Object = @import("Object.zig"); const Symbol = @import("Symbol.zig"); const Thunk = @import("thunks.zig").Thunk; +const ZigObject = @import("ZigObject.zig"); const dev = @import("../../dev.zig"); diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig index 676c49cc7d..0a3a96d95e 100644 --- a/src/link/Elf/LinkerDefined.zig +++ b/src/link/Elf/LinkerDefined.zig @@ -2,7 +2,10 @@ index: File.Index, symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, -symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, + +symbols: std.ArrayListUnmanaged(Symbol) = .{}, +symbols_extra: std.ArrayListUnmanaged(u32) = .{}, +symbols_resolver: std.ArrayListUnmanaged(Elf.SymbolResolver.Index) = .{}, entry_index: ?Symbol.Index = null, dynamic_index: ?Symbol.Index = null, @@ -29,6 +32,8 @@ pub fn deinit(self: *LinkerDefined, allocator: Allocator) void { self.symtab.deinit(allocator); self.strtab.deinit(allocator); self.symbols.deinit(allocator); + self.symbols_extra.deinit(allocator); + self.symbols_resolver.deinit(allocator); self.start_stop_indexes.deinit(allocator); } @@ -37,86 +42,150 @@ pub fn init(self: *LinkerDefined, allocator: Allocator) !void { try self.strtab.append(allocator, 0); } -pub fn initSymbols(self: *LinkerDefined, elf_file: *Elf) !void { - const gpa = elf_file.base.comp.gpa; - - if (elf_file.entry_name) |name| { - self.entry_index = try self.addGlobal(name, elf_file); - } - - self.dynamic_index = try self.addGlobal("_DYNAMIC", elf_file); - self.ehdr_start_index = try self.addGlobal("__ehdr_start", elf_file); - self.init_array_start_index = try self.addGlobal("__init_array_start", elf_file); - self.init_array_end_index = try self.addGlobal("__init_array_end", elf_file); - self.fini_array_start_index = try self.addGlobal("__fini_array_start", elf_file); - self.fini_array_end_index = try self.addGlobal("__fini_array_end", elf_file); - self.preinit_array_start_index = try self.addGlobal("__preinit_array_start", elf_file); - self.preinit_array_end_index = try self.addGlobal("__preinit_array_end", elf_file); - self.got_index = try self.addGlobal("_GLOBAL_OFFSET_TABLE_", elf_file); - self.plt_index = try self.addGlobal("_PROCEDURE_LINKAGE_TABLE_", elf_file); - self.end_index = try self.addGlobal("_end", elf_file); - - if (elf_file.base.comp.link_eh_frame_hdr) { - self.gnu_eh_frame_hdr_index = try self.addGlobal("__GNU_EH_FRAME_HDR", elf_file); - } - - self.dso_handle_index = try self.addGlobal("__dso_handle", elf_file); - self.rela_iplt_start_index = try self.addGlobal("__rela_iplt_start", elf_file); - self.rela_iplt_end_index = try self.addGlobal("__rela_iplt_end", elf_file); - - for (elf_file.shdrs.items) |shdr| { - if (elf_file.getStartStopBasename(shdr)) |name| { - try self.start_stop_indexes.ensureUnusedCapacity(gpa, 2); - - const start = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name}); - defer gpa.free(start); - const stop = try std.fmt.allocPrintZ(gpa, "__stop_{s}", .{name}); - defer gpa.free(stop); - - self.start_stop_indexes.appendAssumeCapacity(try self.addGlobal(start, elf_file)); - self.start_stop_indexes.appendAssumeCapacity(try self.addGlobal(stop, elf_file)); - } - } - - if (elf_file.getTarget().cpu.arch.isRISCV() and elf_file.isEffectivelyDynLib()) { - self.global_pointer_index = try self.addGlobal("__global_pointer$", elf_file); - } -} - -fn addGlobal(self: *LinkerDefined, name: []const u8, elf_file: *Elf) !u32 { - const comp = elf_file.base.comp; - const gpa = comp.gpa; - try self.symtab.ensureUnusedCapacity(gpa, 1); - try self.symbols.ensureUnusedCapacity(gpa, 1); - const name_off = @as(u32, @intCast(self.strtab.items.len)); - try self.strtab.writer(gpa).print("{s}\x00", .{name}); - self.symtab.appendAssumeCapacity(.{ +fn newSymbolAssumeCapacity(self: *LinkerDefined, name_off: u32, elf_file: *Elf) Symbol.Index { + const esym_index: u32 = @intCast(self.symtab.items.len); + const esym = self.symtab.addOneAssumeCapacity(); + esym.* = .{ .st_name = name_off, .st_info = elf.STB_GLOBAL << 4, .st_other = @intFromEnum(elf.STV.HIDDEN), .st_shndx = elf.SHN_ABS, .st_value = 0, .st_size = 0, - }); - const gop = try elf_file.getOrPutGlobal(name); - self.symbols.addOneAssumeCapacity().* = gop.index; - return gop.index; + }; + const index = self.addSymbolAssumeCapacity(); + const symbol = &self.symbols.items[index]; + symbol.name_offset = name_off; + symbol.extra_index = self.addSymbolExtraAssumeCapacity(.{}); + symbol.ref = .{ .index = 0, .file = 0 }; + symbol.esym_index = esym_index; + symbol.version_index = elf_file.default_sym_version; + return index; } -pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void { - for (self.symbols.items, 0..) |index, i| { - const sym_idx = @as(Symbol.Index, @intCast(i)); - const this_sym = self.symtab.items[sym_idx]; +pub fn initSymbols(self: *LinkerDefined, elf_file: *Elf) !void { + const gpa = elf_file.base.comp.gpa; - if (this_sym.st_shndx == elf.SHN_UNDEF) continue; + var nsyms: usize = 0; + if (elf_file.entry_name) |_| { + nsyms += 1; // entry + } + nsyms += 1; // _DYNAMIC + nsyms += 1; // __ehdr_start + nsyms += 1; // __init_array_start + nsyms += 1; // __init_array_end + nsyms += 1; // __fini_array_start + nsyms += 1; // __fini_array_end + nsyms += 1; // __preinit_array_start + nsyms += 1; // __preinit_array_end + nsyms += 1; // _GLOBAL_OFFSET_TABLE_ + nsyms += 1; // _PROCEDURE_LINKAGE_TABLE_ + nsyms += 1; // _end + if (elf_file.base.comp.link_eh_frame_hdr) { + nsyms += 1; // __GNU_EH_FRAME_HDR + } + nsyms += 1; // __dso_handle + nsyms += 1; // __rela_iplt_start + nsyms += 1; // __rela_iplt_end + if (elf_file.getTarget().cpu.arch.isRISCV() and elf_file.isEffectivelyDynLib()) { + nsyms += 1; // __global_pointer$ + } - const global = elf_file.symbol(index); - if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) { - global.value = 0; - global.ref = .{ .index = 0, .file = 0 }; - global.file_index = self.index; - global.esym_index = sym_idx; - global.version_index = elf_file.default_sym_version; + 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); + + if (elf_file.entry_name) |name| { + self.entry_index = self.newSymbolAssumeCapacity(try self.addString(gpa, name), elf_file); + } + + self.dynamic_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "_DYNAMIC"), elf_file); + self.ehdr_start_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__ehdr_start"), elf_file); + self.init_array_start_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__init_array_start"), elf_file); + self.init_array_end_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__init_array_end"), elf_file); + self.fini_array_start_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__fini_array_start"), elf_file); + self.fini_array_end_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__fini_array_end"), elf_file); + self.preinit_array_start_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__preinit_array_start"), elf_file); + self.preinit_array_end_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__preinit_array_end"), elf_file); + self.got_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "_GLOBAL_OFFSET_TABLE_"), elf_file); + self.plt_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "_PROCEDURE_LINKAGE_TABLE_"), elf_file); + self.end_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "_end"), elf_file); + + if (elf_file.base.comp.link_eh_frame_hdr) { + self.gnu_eh_frame_hdr_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__GNU_EH_FRAME_HDR"), elf_file); + } + + self.dso_handle_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__dso_handle"), elf_file); + self.rela_iplt_start_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__rela_iplt_start"), elf_file); + self.rela_iplt_end_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__rela_iplt_end"), elf_file); + + if (elf_file.getTarget().cpu.arch.isRISCV() and elf_file.isEffectivelyDynLib()) { + self.global_pointer_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__global_pointer$"), elf_file); + } +} + +pub fn initStartStopSymbols(self: *LinkerDefined, elf_file: *Elf) !void { + const gpa = elf_file.base.comp.gpa; + + var nsyms: usize = 0; + for (elf_file.shdrs.items) |shdr| { + if (elf_file.getStartStopBasename(shdr)) |_| { + nsyms += 2; // __start_, __stop_ + } + } + + try self.start_stop_indexes.ensureTotalCapacityPrecise(gpa, nsyms); + try self.symtab.ensureUnusedCapacity(gpa, nsyms); + try self.symbols.ensureUnusedCapacity(gpa, nsyms); + try self.symbols_extra.ensureUnusedCapacity(gpa, nsyms * @sizeOf(Symbol.Extra)); + try self.symbols_resolver.ensureUnusedCapacity(gpa, nsyms); + + for (elf_file.shdrs.items) |shdr| { + if (elf_file.getStartStopBasename(shdr)) |name| { + const start_name = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name}); + defer gpa.free(start_name); + const stop_name = try std.fmt.allocPrintZ(gpa, "__stop_{s}", .{name}); + defer gpa.free(stop_name); + + for (&[_][]const u8{ start_name, stop_name }) |nn| { + const index = self.newSymbolAssumeCapacity(try self.addString(gpa, nn), elf_file); + self.start_stop_indexes.appendAssumeCapacity(index); + const gop = try elf_file.resolver.getOrPut(gpa, .{ + .index = index, + .file = self.index, + }, elf_file); + assert(!gop.found_existing); + gop.ref.* = .{ .index = index, .file = self.index }; + self.symbols_resolver.appendAssumeCapacity(gop.index); + } + } + } +} + +pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) !void { + const gpa = elf_file.base.comp.gpa; + + for (self.symtab.items, self.symbols_resolver.items, 0..) |esym, *resolv, i| { + const gop = try elf_file.resolver.getOrPut(gpa, .{ + .index = @intCast(i), + .file = self.index, + }, elf_file); + if (!gop.found_existing) { + gop.ref.* = .{ .index = 0, .file = 0 }; + } + resolv.* = gop.index; + + if (esym.st_shndx == elf.SHN_UNDEF) continue; + if (elf_file.symbol(gop.ref.*) == null) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; + continue; + } + + if (self.asFile().symbolRank(esym, false) < elf_file.symbol(gop.ref.*).?.symbolRank(elf_file)) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; } } } @@ -125,93 +194,73 @@ pub fn allocateSymbols(self: *LinkerDefined, elf_file: *Elf) void { const comp = elf_file.base.comp; const link_mode = comp.config.link_mode; + const allocSymbol = struct { + fn allocSymbol(ld: *LinkerDefined, index: Symbol.Index, value: u64, osec: u32, ef: *Elf) void { + const sym = ef.symbol(ld.resolveSymbol(index, ef)).?; + sym.value = @intCast(value); + sym.output_section_index = osec; + } + }.allocSymbol; + // _DYNAMIC if (elf_file.dynamic_section_index) |shndx| { const shdr = &elf_file.shdrs.items[shndx]; - const symbol_ptr = elf_file.symbol(self.dynamic_index.?); - symbol_ptr.value = @intCast(shdr.sh_addr); - symbol_ptr.output_section_index = shndx; + allocSymbol(self, self.dynamic_index.?, shdr.sh_addr, shndx, elf_file); } // __ehdr_start - { - const symbol_ptr = elf_file.symbol(self.ehdr_start_index.?); - symbol_ptr.value = @intCast(elf_file.image_base); - symbol_ptr.output_section_index = 1; - } + allocSymbol(self, self.ehdr_start_index.?, elf_file.image_base, 1, elf_file); // __init_array_start, __init_array_end if (elf_file.sectionByName(".init_array")) |shndx| { - const start_sym = elf_file.symbol(self.init_array_start_index.?); - const end_sym = elf_file.symbol(self.init_array_end_index.?); const shdr = &elf_file.shdrs.items[shndx]; - start_sym.output_section_index = shndx; - start_sym.value = @intCast(shdr.sh_addr); - end_sym.output_section_index = shndx; - end_sym.value = @intCast(shdr.sh_addr + shdr.sh_size); + allocSymbol(self, self.init_array_start_index.?, shdr.sh_addr, shndx, elf_file); + allocSymbol(self, self.init_array_end_index.?, shdr.sh_addr + shdr.sh_size, shndx, elf_file); } // __fini_array_start, __fini_array_end if (elf_file.sectionByName(".fini_array")) |shndx| { - const start_sym = elf_file.symbol(self.fini_array_start_index.?); - const end_sym = elf_file.symbol(self.fini_array_end_index.?); const shdr = &elf_file.shdrs.items[shndx]; - start_sym.output_section_index = shndx; - start_sym.value = @intCast(shdr.sh_addr); - end_sym.output_section_index = shndx; - end_sym.value = @intCast(shdr.sh_addr + shdr.sh_size); + allocSymbol(self, self.fini_array_start_index.?, shdr.sh_addr, shndx, elf_file); + allocSymbol(self, self.fini_array_end_index.?, shdr.sh_addr + shdr.sh_size, shndx, elf_file); } // __preinit_array_start, __preinit_array_end if (elf_file.sectionByName(".preinit_array")) |shndx| { - const start_sym = elf_file.symbol(self.preinit_array_start_index.?); - const end_sym = elf_file.symbol(self.preinit_array_end_index.?); const shdr = &elf_file.shdrs.items[shndx]; - start_sym.output_section_index = shndx; - start_sym.value = @intCast(shdr.sh_addr); - end_sym.output_section_index = shndx; - end_sym.value = @intCast(shdr.sh_addr + shdr.sh_size); + allocSymbol(self, self.preinit_array_start_index.?, shdr.sh_addr, shndx, elf_file); + allocSymbol(self, self.preinit_array_end_index.?, shdr.sh_addr + shdr.sh_size, shndx, elf_file); } // _GLOBAL_OFFSET_TABLE_ if (elf_file.getTarget().cpu.arch == .x86_64) { if (elf_file.got_plt_section_index) |shndx| { const shdr = elf_file.shdrs.items[shndx]; - const sym = elf_file.symbol(self.got_index.?); - sym.value = @intCast(shdr.sh_addr); - sym.output_section_index = shndx; + allocSymbol(self, self.got_index.?, shdr.sh_addr, shndx, elf_file); } } else { if (elf_file.got_section_index) |shndx| { const shdr = elf_file.shdrs.items[shndx]; - const sym = elf_file.symbol(self.got_index.?); - sym.value = @intCast(shdr.sh_addr); - sym.output_section_index = shndx; + allocSymbol(self, self.got_index.?, shdr.sh_addr, shndx, elf_file); } } // _PROCEDURE_LINKAGE_TABLE_ if (elf_file.plt_section_index) |shndx| { const shdr = &elf_file.shdrs.items[shndx]; - const symbol_ptr = elf_file.symbol(self.plt_index.?); - symbol_ptr.value = @intCast(shdr.sh_addr); - symbol_ptr.output_section_index = shndx; + allocSymbol(self, self.plt_index.?, shdr.sh_addr, shndx, elf_file); } // __dso_handle if (self.dso_handle_index) |index| { const shdr = &elf_file.shdrs.items[1]; - const symbol_ptr = elf_file.symbol(index); - symbol_ptr.value = @intCast(shdr.sh_addr); - symbol_ptr.output_section_index = 0; + allocSymbol(self, index, shdr.sh_addr, 0, elf_file); } // __GNU_EH_FRAME_HDR if (elf_file.eh_frame_hdr_section_index) |shndx| { const shdr = &elf_file.shdrs.items[shndx]; - const symbol_ptr = elf_file.symbol(self.gnu_eh_frame_hdr_index.?); - symbol_ptr.value = @intCast(shdr.sh_addr); - symbol_ptr.output_section_index = shndx; + allocSymbol(self, self.gnu_eh_frame_hdr_index.?, shdr.sh_addr, shndx, elf_file); } // __rela_iplt_start, __rela_iplt_end @@ -220,32 +269,41 @@ pub fn allocateSymbols(self: *LinkerDefined, elf_file: *Elf) void { const shdr = &elf_file.shdrs.items[shndx]; const end_addr = shdr.sh_addr + shdr.sh_size; const start_addr = end_addr - elf_file.calcNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela); - const start_sym = elf_file.symbol(self.rela_iplt_start_index.?); - const end_sym = elf_file.symbol(self.rela_iplt_end_index.?); - start_sym.value = @intCast(start_addr); - start_sym.output_section_index = shndx; - end_sym.value = @intCast(end_addr); - end_sym.output_section_index = shndx; + allocSymbol(self, self.rela_iplt_start_index.?, start_addr, shndx, elf_file); + allocSymbol(self, self.rela_iplt_end_index.?, end_addr, shndx, elf_file); } // _end { - const end_symbol = elf_file.symbol(self.end_index.?); + var value: u64 = 0; + var osec: u32 = 0; for (elf_file.shdrs.items, 0..) |shdr, shndx| { if (shdr.sh_flags & elf.SHF_ALLOC != 0) { - end_symbol.value = @intCast(shdr.sh_addr + shdr.sh_size); - end_symbol.output_section_index = @intCast(shndx); + value = shdr.sh_addr + shdr.sh_size; + osec = @intCast(shndx); } } + allocSymbol(self, self.end_index.?, value, osec, elf_file); + } + + // __global_pointer$ + if (self.global_pointer_index) |index| { + const value, const osec = if (elf_file.sectionByName(".sdata")) |shndx| .{ + elf_file.shdrs.items[shndx].sh_addr + 0x800, + shndx, + } else .{ 0, 0 }; + allocSymbol(self, index, value, osec, elf_file); } // __start_*, __stop_* { var index: usize = 0; while (index < self.start_stop_indexes.items.len) : (index += 2) { - const start = elf_file.symbol(self.start_stop_indexes.items[index]); + const start_ref = self.resolveSymbol(self.start_stop_indexes.items[index], elf_file); + const start = elf_file.symbol(start_ref).?; const name = start.name(elf_file); - const stop = elf_file.symbol(self.start_stop_indexes.items[index + 1]); + const stop_ref = self.resolveSymbol(self.start_stop_indexes.items[index + 1], elf_file); + const stop = elf_file.symbol(stop_ref).?; const shndx = elf_file.sectionByName(name["__start_".len..]).?; const shdr = &elf_file.shdrs.items[shndx]; start.value = @intCast(shdr.sh_addr); @@ -254,47 +312,30 @@ pub fn allocateSymbols(self: *LinkerDefined, elf_file: *Elf) void { stop.output_section_index = shndx; } } - - // __global_pointer$ - if (self.global_pointer_index) |index| { - const sym = elf_file.symbol(index); - if (elf_file.sectionByName(".sdata")) |shndx| { - const shdr = elf_file.shdrs.items[shndx]; - sym.value = @intCast(shdr.sh_addr + 0x800); - sym.output_section_index = shndx; - } else { - sym.value = 0; - sym.output_section_index = 0; - } - } } -pub fn globals(self: LinkerDefined) []const Symbol.Index { - return self.symbols.items; -} - -pub fn updateSymtabSize(self: *LinkerDefined, elf_file: *Elf) !void { - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - const file_ptr = global.file(elf_file) orelse continue; - if (file_ptr.index() != self.index) continue; +pub fn updateSymtabSize(self: *LinkerDefined, elf_file: *Elf) void { + for (self.symbols.items, self.symbols_resolver.items) |*global, resolv| { + const ref = elf_file.resolver.get(resolv).?; + const ref_sym = elf_file.symbol(ref) orelse continue; + if (ref_sym.file(elf_file).?.index() != self.index) continue; global.flags.output_symtab = true; if (global.isLocal(elf_file)) { - try global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file); + global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file); self.output_symtab_ctx.nlocals += 1; } else { - try global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file); + global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file); self.output_symtab_ctx.nglobals += 1; } self.output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1; } } -pub fn writeSymtab(self: LinkerDefined, elf_file: *Elf) void { - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - const file_ptr = global.file(elf_file) orelse continue; - if (file_ptr.index() != self.index) continue; +pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf) void { + for (self.symbols.items, self.symbols_resolver.items) |global, resolv| { + const ref = elf_file.resolver.get(resolv).?; + const ref_sym = elf_file.symbol(ref) orelse continue; + if (ref_sym.file(elf_file).?.index() != self.index) continue; const idx = global.outputSymtabIndex(elf_file) orelse continue; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file)); @@ -305,15 +346,93 @@ pub fn writeSymtab(self: LinkerDefined, elf_file: *Elf) void { } } +pub fn dynamicSymbol(self: LinkerDefined, elf_file: *Elf) ?*Symbol { + const index = self.dynamic_index orelse return null; + const resolv = self.resolveSymbol(index, elf_file); + return elf_file.symbol(resolv); +} + +pub fn entrySymbol(self: LinkerDefined, elf_file: *Elf) ?*Symbol { + const index = self.entry_index orelse return null; + const resolv = self.resolveSymbol(index, elf_file); + return elf_file.symbol(resolv); +} + pub fn asFile(self: *LinkerDefined) File { return .{ .linker_defined = self }; } +fn addString(self: *LinkerDefined, 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: LinkerDefined, 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); } +pub fn resolveSymbol(self: LinkerDefined, index: Symbol.Index, elf_file: *Elf) Elf.Ref { + const resolv = self.symbols_resolver.items[index]; + return elf_file.resolver.get(resolv).?; +} + +fn addSymbol(self: *LinkerDefined, allocator: Allocator) !Symbol.Index { + try self.symbols.ensureUnusedCapacity(allocator, 1); + return self.addSymbolAssumeCapacity(); +} + +fn addSymbolAssumeCapacity(self: *LinkerDefined) Symbol.Index { + const index: Symbol.Index = @intCast(self.symbols.items.len); + self.symbols.appendAssumeCapacity(.{ .file_index = self.index }); + return index; +} + +pub fn addSymbolExtra(self: *LinkerDefined, 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: *LinkerDefined, extra: Symbol.Extra) u32 { + const index = @as(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) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn symbolExtra(self: *LinkerDefined, index: u32) Symbol.Extra { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + var i: usize = index; + var result: Symbol.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.symbols_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setSymbolExtra(self: *LinkerDefined, index: u32, extra: Symbol.Extra) void { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.symbols_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + pub fn fmtSymtab(self: *LinkerDefined, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { return .{ .data = .{ .self = self, @@ -334,10 +453,16 @@ fn formatSymtab( ) !void { _ = unused_fmt_string; _ = options; + const self = ctx.self; + const elf_file = ctx.elf_file; try writer.writeAll(" globals\n"); - for (ctx.self.globals()) |index| { - const global = ctx.elf_file.symbol(index); - try writer.print(" {}\n", .{global.fmt(ctx.elf_file)}); + for (self.symbols.items, 0..) |sym, i| { + const ref = self.resolveSymbol(@intCast(i), elf_file); + if (elf_file.symbol(ref)) |ref_sym| { + try writer.print(" {}\n", .{ref_sym.fmt(elf_file)}); + } else { + try writer.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); + } } } diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 9185ca0ef7..a6f9c4ac18 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -9,7 +9,9 @@ shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{}, symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, first_global: ?Symbol.Index = null, -symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +symbols: std.ArrayListUnmanaged(Symbol) = .{}, +symbols_extra: std.ArrayListUnmanaged(u32) = .{}, +symbols_resolver: std.ArrayListUnmanaged(Elf.SymbolResolver.Index) = .{}, relocs: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, atoms: std.ArrayListUnmanaged(Atom) = .{}, @@ -51,6 +53,8 @@ pub fn deinit(self: *Object, allocator: Allocator) void { self.symtab.deinit(allocator); self.strtab.deinit(allocator); self.symbols.deinit(allocator); + self.symbols_extra.deinit(allocator); + self.symbols_resolver.deinit(allocator); self.atoms.deinit(allocator); self.atoms_indexes.deinit(allocator); self.atoms_extra.deinit(allocator); @@ -80,7 +84,7 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { try self.atoms.append(gpa, .{ .extra_index = try self.addAtomExtra(gpa, .{}) }); try self.initAtoms(gpa, handle, elf_file); - try self.initSymtab(gpa, elf_file); + try self.initSymbols(gpa, elf_file); for (self.shdrs.items, 0..) |shdr, i| { const atom_ptr = self.atom(self.atoms_indexes.items[i]) orelse continue; @@ -374,29 +378,29 @@ fn skipShdr(self: *Object, index: u32, elf_file: *Elf) bool { return ignore; } -fn initSymtab(self: *Object, allocator: Allocator, elf_file: *Elf) !void { +fn initSymbols(self: *Object, allocator: Allocator, elf_file: *Elf) !void { const first_global = self.first_global orelse self.symtab.items.len; + const nglobals = self.symtab.items.len - first_global; try self.symbols.ensureTotalCapacityPrecise(allocator, self.symtab.items.len); + try self.symbols_extra.ensureTotalCapacityPrecise(allocator, self.symtab.items.len * @sizeOf(Symbol.Extra)); + try self.symbols_resolver.ensureTotalCapacityPrecise(allocator, nglobals); + self.symbols_resolver.resize(allocator, nglobals) catch unreachable; + @memset(self.symbols_resolver.items, 0); - for (self.symtab.items[0..first_global], 0..) |sym, i| { - const index = try elf_file.addSymbol(); - self.symbols.appendAssumeCapacity(index); - const sym_ptr = elf_file.symbol(index); + for (self.symtab.items, 0..) |sym, i| { + const index = self.addSymbolAssumeCapacity(); + const sym_ptr = &self.symbols.items[index]; sym_ptr.value = @intCast(sym.st_value); sym_ptr.name_offset = sym.st_name; - sym_ptr.esym_index = @as(u32, @intCast(i)); - sym_ptr.file_index = self.index; - if (sym.st_shndx != elf.SHN_ABS) { + 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.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 }; } } - - for (self.symtab.items[first_global..]) |sym| { - const name = self.getString(sym.st_name); - const gop = try elf_file.getOrPutGlobal(name); - self.symbols.addOneAssumeCapacity().* = gop.index; - } } fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx: u32, elf_file: *Elf) !void { @@ -543,7 +547,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void { for (self.cies.items) |cie| { for (cie.relocs(elf_file)) |rel| { - const sym = elf_file.symbol(self.symbols.items[rel.r_sym()]); + const sym = elf_file.symbol(self.resolveSymbol(rel.r_sym(), elf_file)).?; if (sym.flags.import) { if (sym.type(elf_file) != elf.STT_FUNC) // TODO convert into an error @@ -557,49 +561,47 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void { } } -pub fn resolveSymbols(self: *Object, elf_file: *Elf) void { +pub fn resolveSymbols(self: *Object, elf_file: *Elf) !void { + const gpa = elf_file.base.comp.gpa; + const first_global = self.first_global orelse return; - for (self.globals(), 0..) |index, i| { - const esym_index = @as(Symbol.Index, @intCast(first_global + i)); - const esym = self.symtab.items[esym_index]; - - if (esym.st_shndx == elf.SHN_UNDEF) continue; - - if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) { + for (self.globals(), first_global..) |_, i| { + const esym = self.symtab.items[i]; + if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON and esym.st_shndx != elf.SHN_UNDEF) { const atom_index = self.atoms_indexes.items[esym.st_shndx]; const atom_ptr = self.atom(atom_index) orelse continue; if (!atom_ptr.alive) continue; } - const global = elf_file.symbol(index); - if (self.asFile().symbolRank(esym, !self.alive) < global.symbolRank(elf_file)) { - switch (esym.st_shndx) { - elf.SHN_ABS, elf.SHN_COMMON => {}, - else => global.ref = .{ - .index = self.atoms_indexes.items[esym.st_shndx], - .file = self.index, - }, - } - global.value = @intCast(esym.st_value); - global.esym_index = esym_index; - global.file_index = self.index; - global.version_index = elf_file.default_sym_version; - if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true; + const resolv = &self.symbols_resolver.items[i - first_global]; + const gop = try elf_file.resolver.getOrPut(gpa, .{ + .index = @intCast(i), + .file = self.index, + }, elf_file); + if (!gop.found_existing) { + gop.ref.* = .{ .index = 0, .file = 0 }; + } + resolv.* = gop.index; + + if (esym.st_shndx == elf.SHN_UNDEF) continue; + if (elf_file.symbol(gop.ref.*) == null) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; + continue; + } + + if (self.asFile().symbolRank(esym, !self.alive) < elf_file.symbol(gop.ref.*).?.symbolRank(elf_file)) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; } } } pub fn claimUnresolved(self: *Object, elf_file: *Elf) void { const first_global = self.first_global orelse return; - for (self.globals(), 0..) |index, i| { + for (self.globals(), 0..) |*sym, i| { const esym_index = @as(u32, @intCast(first_global + i)); const esym = self.symtab.items[esym_index]; if (esym.st_shndx != elf.SHN_UNDEF) continue; - - const global = elf_file.symbol(index); - if (global.file(elf_file)) |_| { - if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF) continue; - } + if (elf_file.symbol(self.resolveSymbol(esym_index, elf_file)) != null) continue; const is_import = blk: { if (!elf_file.isEffectivelyDynLib()) break :blk false; @@ -608,45 +610,48 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void { break :blk true; }; - global.value = 0; - global.ref = .{ .index = 0, .file = 0 }; - global.esym_index = esym_index; - global.file_index = self.index; - global.version_index = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version; - global.flags.import = is_import; + sym.value = 0; + 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.flags.import = is_import; + + const idx = self.symbols_resolver.items[i]; + elf_file.resolver.values.items[idx - 1] = .{ .index = esym_index, .file = self.index }; } } pub fn claimUnresolvedObject(self: *Object, elf_file: *Elf) void { const first_global = self.first_global orelse return; - for (self.globals(), 0..) |index, i| { + for (self.globals(), 0..) |*sym, i| { const esym_index = @as(u32, @intCast(first_global + i)); const esym = self.symtab.items[esym_index]; if (esym.st_shndx != elf.SHN_UNDEF) continue; + if (elf_file.symbol(self.resolveSymbol(esym_index, elf_file)) != null) continue; - const global = elf_file.symbol(index); - if (global.file(elf_file)) |file| { - if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF or file.index() <= self.index) continue; - } + sym.value = 0; + sym.ref = .{ .index = 0, .file = 0 }; + sym.esym_index = esym_index; + sym.file_index = self.index; - global.value = 0; - global.ref = .{ .index = 0, .file = 0 }; - global.esym_index = esym_index; - global.file_index = self.index; + const idx = self.symbols_resolver.items[i]; + elf_file.resolver.values.items[idx - 1] = .{ .index = esym_index, .file = self.index }; } } pub fn markLive(self: *Object, elf_file: *Elf) void { const first_global = self.first_global orelse return; - for (self.globals(), 0..) |index, i| { - const sym_idx = first_global + i; - const sym = self.symtab.items[sym_idx]; - if (sym.st_bind() == elf.STB_WEAK) continue; + for (0..self.globals().len) |i| { + const esym_idx = first_global + i; + const esym = self.symtab.items[esym_idx]; + if (esym.st_bind() == elf.STB_WEAK) continue; - const global = elf_file.symbol(index); - const file = global.file(elf_file) orelse continue; - const should_keep = sym.st_shndx == elf.SHN_UNDEF or - (sym.st_shndx == elf.SHN_COMMON and global.elfSym(elf_file).st_shndx != elf.SHN_COMMON); + const ref = self.resolveSymbol(@intCast(esym_idx), elf_file); + const sym = elf_file.symbol(ref) orelse continue; + const file = sym.file(elf_file).?; + const should_keep = esym.st_shndx == elf.SHN_UNDEF or + (esym.st_shndx == elf.SHN_COMMON and sym.elfSym(elf_file).st_shndx != elf.SHN_COMMON); if (should_keep and !file.isAlive()) { file.setAlive(); file.markLive(elf_file); @@ -664,26 +669,50 @@ pub fn markEhFrameAtomsDead(self: *Object, elf_file: *Elf) void { } } +pub fn markImportsExports(self: *Object, elf_file: *Elf) void { + const first_global = self.first_global orelse return; + for (0..self.globals().len) |i| { + const idx = first_global + i; + 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)); + if (vis == .HIDDEN) continue; + if (file == .shared_object and !sym.isAbs(elf_file)) { + sym.flags.import = true; + continue; + } + if (file.index() == self.index) { + sym.flags.@"export" = true; + if (elf_file.isEffectivelyDynLib() and vis != .PROTECTED) { + sym.flags.import = true; + } + } + } +} + pub fn checkDuplicates(self: *Object, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void { const first_global = self.first_global orelse return; - for (self.globals(), 0..) |index, i| { - const sym_idx = first_global + i; - const sym = self.symtab.items[sym_idx]; - const global = elf_file.symbol(index); - const global_file = global.file(elf_file) orelse continue; + for (0..self.globals().len) |i| { + const esym_idx = first_global + i; + const esym = self.symtab.items[esym_idx]; + const ref = self.resolveSymbol(@intCast(esym_idx), elf_file); + const ref_sym = elf_file.symbol(ref) orelse continue; + const ref_file = ref_sym.file(elf_file).?; - if (self.index == global_file.index() or - sym.st_shndx == elf.SHN_UNDEF or - sym.st_bind() == elf.STB_WEAK or - sym.st_shndx == elf.SHN_COMMON) continue; + if (self.index == ref_file.index() or + esym.st_shndx == elf.SHN_UNDEF or + esym.st_bind() == elf.STB_WEAK or + esym.st_shndx == elf.SHN_COMMON) continue; - if (sym.st_shndx != elf.SHN_ABS) { - const atom_index = self.atoms_indexes.items[sym.st_shndx]; + if (esym.st_shndx != elf.SHN_ABS) { + const atom_index = self.atoms_indexes.items[esym.st_shndx]; const atom_ptr = self.atom(atom_index) orelse continue; if (!atom_ptr.alive) continue; } - const gop = try dupes.getOrPut(index); + const gop = try dupes.getOrPut(self.symbols_resolver.items[i]); if (!gop.found_existing) { gop.value_ptr.* = .{}; } @@ -813,9 +842,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { } for (self.symtab.items, 0..) |*esym, idx| { - const sym_index = self.symbols.items[idx]; - const sym = elf_file.symbol(sym_index); - + const sym = &self.symbols.items[idx]; if (esym.st_shndx == elf.SHN_COMMON or esym.st_shndx == elf.SHN_UNDEF or esym.st_shndx == elf.SHN_ABS) continue; const imsec_index = self.input_merge_sections_indexes.items[esym.st_shndx]; @@ -854,22 +881,20 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { return error.MalformedObject; }; - const out_sym_idx: u64 = @intCast(self.symbols.items.len); - try self.symbols.ensureUnusedCapacity(gpa, 1); + const sym_index = try self.addSymbol(gpa); + const sym = &self.symbols.items[sym_index]; const name = try std.fmt.allocPrint(gpa, "{s}$subsection{d}", .{ msec.name(elf_file), res.msub_index }); defer gpa.free(name); - const sym_index = try elf_file.addSymbol(); - const sym = elf_file.symbol(sym_index); sym.* = .{ .value = @bitCast(@as(i64, @intCast(res.offset)) - rel.r_addend), .name_offset = try self.addString(gpa, name), .esym_index = rel.r_sym(), .file_index = self.index, + .extra_index = try self.addSymbolExtra(gpa, .{}), }; sym.ref = .{ .index = res.msub_index, .file = imsec.merge_section_index }; sym.flags.merge_subsection = true; - self.symbols.addOneAssumeCapacity().* = sym_index; - rel.r_info = (out_sym_idx << 32) | rel.r_type(); + rel.r_info = (@as(u64, @intCast(sym_index)) << 32) | rel.r_type(); } } } @@ -878,27 +903,16 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { /// play nicely with the rest of the system. pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { const first_global = self.first_global orelse return; - for (self.globals(), 0..) |index, i| { - const sym_idx = @as(u32, @intCast(first_global + i)); - const this_sym = self.symtab.items[sym_idx]; - if (this_sym.st_shndx != elf.SHN_COMMON) continue; - - const global = elf_file.symbol(index); - const global_file = global.file(elf_file).?; - if (global_file.index() != self.index) { - // if (elf_file.options.warn_common) { - // elf_file.base.warn("{}: multiple common symbols: {s}", .{ - // self.fmtPath(), - // global.getName(elf_file), - // }); - // } - continue; - } + for (self.globals(), self.symbols_resolver.items, 0..) |*sym, resolv, i| { + const esym_idx = @as(u32, @intCast(first_global + i)); + const esym = self.symtab.items[esym_idx]; + if (esym.st_shndx != elf.SHN_COMMON) continue; + if (elf_file.resolver.get(resolv).?.file != self.index) continue; const comp = elf_file.base.comp; const gpa = comp.gpa; - const is_tls = global.type(elf_file) == elf.STT_TLS; + const is_tls = sym.type(elf_file) == elf.STT_TLS; const name = if (is_tls) ".tls_common" else ".common"; const name_offset = @as(u32, @intCast(self.strtab.items.len)); try self.strtab.writer(gpa).print("{s}\x00", .{name}); @@ -907,7 +921,7 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { if (is_tls) sh_flags |= elf.SHF_TLS; const shndx = @as(u32, @intCast(self.shdrs.items.len)); const shdr = try self.shdrs.addOne(gpa); - const sh_size = math.cast(usize, this_sym.st_size) orelse return error.Overflow; + const sh_size = math.cast(usize, esym.st_size) orelse return error.Overflow; shdr.* = .{ .sh_name = name_offset, .sh_type = elf.SHT_NOBITS, @@ -917,21 +931,21 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { .sh_size = sh_size, .sh_link = 0, .sh_info = 0, - .sh_addralign = this_sym.st_value, + .sh_addralign = esym.st_value, .sh_entsize = 0, }; const atom_index = try self.addAtom(gpa, .{ .name = name_offset, .shndx = shndx, - .size = this_sym.st_size, - .alignment = Alignment.fromNonzeroByteUnits(this_sym.st_value), + .size = esym.st_size, + .alignment = Alignment.fromNonzeroByteUnits(esym.st_value), }); try self.atoms_indexes.append(gpa, atom_index); - global.value = 0; - global.ref = .{ .index = atom_index, .file = self.index }; - global.flags.weak = false; + sym.value = 0; + sym.ref = .{ .index = atom_index, .file = self.index }; + sym.flags.weak = false; } } @@ -1073,7 +1087,7 @@ pub fn writeAr(self: Object, elf_file: *Elf, writer: anytype) !void { try writer.writeAll(data); } -pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void { +pub fn updateSymtabSize(self: *Object, elf_file: *Elf) void { const isAlive = struct { fn isAlive(sym: *const Symbol, ctx: *Elf) bool { if (sym.mergeSubsection(ctx)) |msub| return msub.alive; @@ -1082,8 +1096,7 @@ pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void { } }.isAlive; - for (self.locals()) |local_index| { - const local = elf_file.symbol(local_index); + for (self.locals()) |*local| { if (!isAlive(local, elf_file)) continue; const esym = local.elfSym(elf_file); switch (esym.st_type()) { @@ -1092,31 +1105,30 @@ pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void { else => {}, } local.flags.output_symtab = true; - try local.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file); + local.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file); self.output_symtab_ctx.nlocals += 1; self.output_symtab_ctx.strsize += @as(u32, @intCast(local.name(elf_file).len)) + 1; } - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - const file_ptr = global.file(elf_file) orelse continue; - if (file_ptr.index() != self.index) continue; + for (self.globals(), self.symbols_resolver.items) |*global, resolv| { + const ref = elf_file.resolver.values.items[resolv - 1]; + const ref_sym = elf_file.symbol(ref) orelse continue; + if (ref_sym.file(elf_file).?.index() != self.index) continue; if (!isAlive(global, elf_file)) continue; global.flags.output_symtab = true; if (global.isLocal(elf_file)) { - try global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file); + global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file); self.output_symtab_ctx.nlocals += 1; } else { - try global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file); + global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file); self.output_symtab_ctx.nglobals += 1; } self.output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1; } } -pub fn writeSymtab(self: Object, elf_file: *Elf) void { - for (self.locals()) |local_index| { - const local = elf_file.symbol(local_index); +pub fn writeSymtab(self: *Object, elf_file: *Elf) void { + for (self.locals()) |local| { const idx = local.outputSymtabIndex(elf_file) orelse continue; const out_sym = &elf_file.symtab.items[idx]; out_sym.st_name = @intCast(elf_file.strtab.items.len); @@ -1125,10 +1137,10 @@ pub fn writeSymtab(self: Object, elf_file: *Elf) void { local.setOutputSym(elf_file, out_sym); } - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - const file_ptr = global.file(elf_file) orelse continue; - if (file_ptr.index() != self.index) continue; + for (self.globals(), self.symbols_resolver.items) |global, resolv| { + const ref = elf_file.resolver.values.items[resolv - 1]; + const ref_sym = elf_file.symbol(ref) orelse continue; + if (ref_sym.file(elf_file).?.index() != self.index) continue; const idx = global.outputSymtabIndex(elf_file) orelse continue; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file)); @@ -1139,20 +1151,6 @@ pub fn writeSymtab(self: Object, elf_file: *Elf) void { } } -pub fn locals(self: Object) []const Symbol.Index { - if (self.symbols.items.len == 0) return &[0]Symbol.Index{}; - assert(self.symbols.items.len >= self.symtab.items.len); - const end = self.first_global orelse self.symtab.items.len; - return self.symbols.items[0..end]; -} - -pub fn globals(self: Object) []const Symbol.Index { - if (self.symbols.items.len == 0) return &[0]Symbol.Index{}; - assert(self.symbols.items.len >= self.symtab.items.len); - const start = self.first_global orelse self.symtab.items.len; - return self.symbols.items[start..self.symtab.items.len]; -} - /// Returns atom's code and optionally uncompresses data if required (for compressed sections). /// Caller owns the memory. pub fn codeDecompressAlloc(self: *Object, elf_file: *Elf, atom_index: Atom.Index) ![]u8 { @@ -1185,6 +1183,81 @@ pub fn codeDecompressAlloc(self: *Object, elf_file: *Elf, atom_index: Atom.Index return data; } +fn locals(self: *Object) []Symbol { + if (self.symbols.items.len == 0) return &[0]Symbol{}; + assert(self.symbols.items.len >= self.symtab.items.len); + const end = self.first_global orelse self.symtab.items.len; + return self.symbols.items[0..end]; +} + +pub fn globals(self: *Object) []Symbol { + if (self.symbols.items.len == 0) return &[0]Symbol{}; + assert(self.symbols.items.len >= self.symtab.items.len); + const start = self.first_global orelse self.symtab.items.len; + return self.symbols.items[start..self.symtab.items.len]; +} + +pub fn resolveSymbol(self: Object, index: Symbol.Index, elf_file: *Elf) Elf.Ref { + const start = self.first_global orelse self.symtab.items.len; + const end = self.symtab.items.len; + if (index < start or index >= end) return .{ .index = index, .file = self.index }; + const resolv = self.symbols_resolver.items[index - start]; + return elf_file.resolver.get(resolv).?; +} + +fn addSymbol(self: *Object, allocator: Allocator) !Symbol.Index { + try self.symbols.ensureUnusedCapacity(allocator, 1); + return self.addSymbolAssumeCapacity(); +} + +fn addSymbolAssumeCapacity(self: *Object) Symbol.Index { + const index: Symbol.Index = @intCast(self.symbols.items.len); + self.symbols.appendAssumeCapacity(.{ .file_index = self.index }); + return index; +} + +pub fn addSymbolExtra(self: *Object, 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: *Object, extra: Symbol.Extra) u32 { + const index = @as(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) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn symbolExtra(self: *Object, index: u32) Symbol.Extra { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + var i: usize = index; + var result: Symbol.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.symbols_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setSymbolExtra(self: *Object, index: u32, extra: Symbol.Extra) void { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.symbols_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + pub fn asFile(self: *Object) File { return .{ .object = self }; } @@ -1352,15 +1425,20 @@ fn formatSymtab( _ = unused_fmt_string; _ = options; const object = ctx.object; + const elf_file = ctx.elf_file; try writer.writeAll(" locals\n"); - for (object.locals()) |index| { - const local = ctx.elf_file.symbol(index); - try writer.print(" {}\n", .{local.fmt(ctx.elf_file)}); + for (object.locals()) |sym| { + try writer.print(" {}\n", .{sym.fmt(elf_file)}); } try writer.writeAll(" globals\n"); - for (object.globals()) |index| { - const global = ctx.elf_file.symbol(index); - try writer.print(" {}\n", .{global.fmt(ctx.elf_file)}); + for (object.globals(), 0..) |sym, i| { + const first_global = object.first_global.?; + const ref = object.resolveSymbol(@intCast(i + first_global), elf_file); + if (elf_file.symbol(ref)) |ref_sym| { + try writer.print(" {}\n", .{ref_sym.fmt(elf_file)}); + } else { + try writer.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); + } } } diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig index 461a56ae39..b33853d23c 100644 --- a/src/link/Elf/SharedObject.zig +++ b/src/link/Elf/SharedObject.zig @@ -10,7 +10,10 @@ strtab: std.ArrayListUnmanaged(u8) = .{}, versyms: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{}, verstrings: std.ArrayListUnmanaged(u32) = .{}, -symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +symbols: std.ArrayListUnmanaged(Symbol) = .{}, +symbols_extra: std.ArrayListUnmanaged(u32) = .{}, +symbols_resolver: std.ArrayListUnmanaged(Elf.SymbolResolver.Index) = .{}, + aliases: ?std.ArrayListUnmanaged(u32) = null, dynamic_table: std.ArrayListUnmanaged(elf.Elf64_Dyn) = .{}, @@ -38,6 +41,8 @@ pub fn deinit(self: *SharedObject, allocator: Allocator) void { 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); } @@ -129,7 +134,7 @@ pub fn parse(self: *SharedObject, elf_file: *Elf, handle: std.fs.File) !void { .versym_sect_index = versym_sect_index, }); - try self.initSymtab(elf_file, .{ + try self.initSymbols(elf_file, .{ .symtab = symtab, .strtab = strtab, }); @@ -188,16 +193,20 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf, handle: std.fs.File, opts: } } -fn initSymtab(self: *SharedObject, elf_file: *Elf, opts: struct { +fn initSymbols(self: *SharedObject, elf_file: *Elf, opts: struct { symtab: []align(1) const elf.Elf64_Sym, strtab: []const u8, }) !void { - const comp = elf_file.base.comp; - const gpa = comp.gpa; + const gpa = elf_file.base.comp.gpa; + const nsyms = opts.symtab.len; try self.strtab.appendSlice(gpa, opts.strtab); - try self.symtab.ensureTotalCapacityPrecise(gpa, opts.symtab.len); - try self.symbols.ensureTotalCapacityPrecise(gpa, opts.symtab.len); + 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; @@ -210,45 +219,57 @@ fn initSymtab(self: *SharedObject, elf_file: *Elf, opts: struct { self.versionString(self.versyms.items[i]), }); defer gpa.free(mangled); - const name_off = @as(u32, @intCast(self.strtab.items.len)); - try self.strtab.writer(gpa).print("{s}\x00", .{mangled}); - break :blk name_off; + break :blk try self.addString(gpa, mangled); } else sym.st_name; - const out_sym = self.symtab.addOneAssumeCapacity(); - out_sym.* = sym; - out_sym.st_name = name_off; - const gop = try elf_file.getOrPutGlobal(self.getString(name_off)); - self.symbols.addOneAssumeCapacity().* = gop.index; + 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(.{}); } } -pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) void { - for (self.globals(), 0..) |index, i| { - const esym_index = @as(u32, @intCast(i)); - const this_sym = self.symtab.items[esym_index]; +pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) !void { + const gpa = elf_file.base.comp.gpa; - if (this_sym.st_shndx == elf.SHN_UNDEF) continue; + for (self.symtab.items, self.symbols_resolver.items, 0..) |esym, *resolv, i| { + const gop = try elf_file.resolver.getOrPut(gpa, .{ + .index = @intCast(i), + .file = self.index, + }, elf_file); + if (!gop.found_existing) { + gop.ref.* = .{ .index = 0, .file = 0 }; + } + resolv.* = gop.index; - const global = elf_file.symbol(index); - if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) { - global.value = @intCast(this_sym.st_value); - global.ref = .{ .index = 0, .file = 0 }; - global.esym_index = esym_index; - global.version_index = self.versyms.items[esym_index]; - global.file_index = self.index; + if (esym.st_shndx == elf.SHN_UNDEF) continue; + if (elf_file.symbol(gop.ref.*) == null) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; + continue; + } + + if (self.asFile().symbolRank(esym, false) < elf_file.symbol(gop.ref.*).?.symbolRank(elf_file)) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; } } } pub fn markLive(self: *SharedObject, elf_file: *Elf) void { - for (self.globals(), 0..) |index, i| { - const sym = self.symtab.items[i]; - if (sym.st_shndx != elf.SHN_UNDEF) continue; + for (self.symtab.items, 0..) |esym, i| { + if (esym.st_shndx != elf.SHN_UNDEF) continue; - const global = elf_file.symbol(index); - const file = global.file(elf_file) orelse continue; + const ref = self.resolveSymbol(@intCast(i), elf_file); + const sym = elf_file.symbol(ref) orelse continue; + const file = sym.file(elf_file).?; const should_drop = switch (file) { - .shared_object => |sh| !sh.needed and sym.st_bind() == elf.STB_WEAK, + .shared_object => |sh| !sh.needed and esym.st_bind() == elf.STB_WEAK, else => false, }; if (!should_drop and !file.isAlive()) { @@ -258,28 +279,34 @@ pub fn markLive(self: *SharedObject, elf_file: *Elf) void { } } -pub fn globals(self: SharedObject) []const Symbol.Index { - return self.symbols.items; +pub fn markImportExports(self: *SharedObject, elf_file: *Elf) void { + for (0..self.symbols.items.len) |i| { + const ref = self.resolveSymbol(@intCast(i), elf_file); + const ref_sym = elf_file.symbol(ref) orelse continue; + const ref_file = ref_sym.file(elf_file).?; + const vis = @as(elf.STV, @enumFromInt(ref_sym.elfSym(elf_file).st_other)); + if (ref_file != .shared_object and vis != .HIDDEN) ref_sym.flags.@"export" = true; + } } -pub fn updateSymtabSize(self: *SharedObject, elf_file: *Elf) !void { - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - const file_ptr = global.file(elf_file) orelse continue; - if (file_ptr.index() != self.index) continue; +pub fn updateSymtabSize(self: *SharedObject, elf_file: *Elf) void { + for (self.symbols.items, self.symbols_resolver.items) |*global, resolv| { + const ref = elf_file.resolver.get(resolv).?; + const ref_sym = elf_file.symbol(ref) orelse continue; + if (ref_sym.file(elf_file).?.index() != self.index) continue; if (global.isLocal(elf_file)) continue; global.flags.output_symtab = true; - try global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file); + global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file); self.output_symtab_ctx.nglobals += 1; self.output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1; } } -pub fn writeSymtab(self: SharedObject, elf_file: *Elf) void { - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - const file_ptr = global.file(elf_file) orelse continue; - if (file_ptr.index() != self.index) continue; +pub fn writeSymtab(self: *SharedObject, elf_file: *Elf) void { + for (self.symbols.items, self.symbols_resolver.items) |global, resolv| { + const ref = elf_file.resolver.get(resolv).?; + const ref_sym = elf_file.symbol(ref) orelse continue; + if (ref_sym.file(elf_file).?.index() != self.index) continue; const idx = global.outputSymtabIndex(elf_file) orelse continue; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file)); @@ -319,9 +346,12 @@ pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void { assert(self.aliases == null); const SortAlias = struct { - pub fn lessThan(ctx: *Elf, lhs: Symbol.Index, rhs: Symbol.Index) bool { - const lhs_sym = ctx.symbol(lhs).elfSym(ctx); - const rhs_sym = ctx.symbol(rhs).elfSym(ctx); + so: *SharedObject, + ef: *Elf, + + pub fn lessThan(ctx: @This(), lhs: Symbol.Index, rhs: Symbol.Index) bool { + const lhs_sym = ctx.so.symbols.items[lhs].elfSym(ctx.ef); + const rhs_sym = ctx.so.symbols.items[rhs].elfSym(ctx.ef); return lhs_sym.st_value < rhs_sym.st_value; } }; @@ -330,16 +360,16 @@ pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void { const gpa = comp.gpa; var aliases = std.ArrayList(Symbol.Index).init(gpa); defer aliases.deinit(); - try aliases.ensureTotalCapacityPrecise(self.globals().len); + try aliases.ensureTotalCapacityPrecise(self.symbols.items.len); - for (self.globals()) |index| { - const global = elf_file.symbol(index); - const global_file = global.file(elf_file) orelse continue; - if (global_file.index() != self.index) continue; - aliases.appendAssumeCapacity(index); + for (self.symbols_resolver.items, 0..) |resolv, index| { + const ref = elf_file.resolver.get(resolv).?; + const ref_sym = elf_file.symbol(ref) orelse continue; + if (ref_sym.file(elf_file).?.index() != self.index) continue; + aliases.appendAssumeCapacity(@intCast(index)); } - std.mem.sort(u32, aliases.items, elf_file, SortAlias.lessThan); + std.mem.sort(u32, aliases.items, SortAlias{ .so = self, .ef = elf_file }, SortAlias.lessThan); self.aliases = aliases.moveToUnmanaged(); } @@ -347,27 +377,93 @@ pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void { pub fn symbolAliases(self: *SharedObject, index: u32, elf_file: *Elf) []const u32 { assert(self.aliases != null); - const symbol = elf_file.symbol(index).elfSym(elf_file); + const symbol = self.symbols.items[index].elfSym(elf_file); const aliases = self.aliases.?; const start = for (aliases.items, 0..) |alias, i| { - const alias_sym = elf_file.symbol(alias).elfSym(elf_file); + const alias_sym = self.symbols.items[alias].elfSym(elf_file); if (symbol.st_value == alias_sym.st_value) break i; } else aliases.items.len; const end = for (aliases.items[start..], 0..) |alias, i| { - const alias_sym = elf_file.symbol(alias).elfSym(elf_file); + const alias_sym = self.symbols.items[alias].elfSym(elf_file); if (symbol.st_value < alias_sym.st_value) break i + start; } else aliases.items.len; 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); } +pub fn resolveSymbol(self: SharedObject, index: Symbol.Index, elf_file: *Elf) Elf.Ref { + const resolv = self.symbols_resolver.items[index]; + 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 { + 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 fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields) |field| { + self.symbols_extra.appendAssumeCapacity(switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn symbolExtra(self: *SharedObject, index: u32) Symbol.Extra { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + var i: usize = index; + var result: Symbol.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.symbols_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setSymbolExtra(self: *SharedObject, index: u32, extra: Symbol.Extra) void { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.symbols_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + pub fn format( self: SharedObject, comptime unused_fmt_string: []const u8, @@ -402,10 +498,15 @@ fn formatSymtab( _ = unused_fmt_string; _ = options; const shared = ctx.shared; + const elf_file = ctx.elf_file; try writer.writeAll(" globals\n"); - for (shared.symbols.items) |index| { - const global = ctx.elf_file.symbol(index); - try writer.print(" {}\n", .{global.fmt(ctx.elf_file)}); + for (shared.symbols.items, 0..) |sym, i| { + const ref = shared.resolveSymbol(@intCast(i), elf_file); + if (elf_file.symbol(ref)) |ref_sym| { + try writer.print(" {}\n", .{ref_sym.fmt(elf_file)}); + } else { + try writer.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); + } } } diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 68d23cb74d..ce6b94a185 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -63,9 +63,7 @@ pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 { } pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 { - if (symbol.flags.global) return elf_file.strings.getAssumeExists(symbol.name_offset); - const file_ptr = symbol.file(elf_file).?; - return switch (file_ptr) { + return switch (symbol.file(elf_file).?) { inline else => |x| x.getString(symbol.name_offset), }; } @@ -87,9 +85,8 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File { } pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { - const file_ptr = symbol.file(elf_file).?; - return switch (file_ptr) { - .zig_object => |x| x.elfSym(symbol.esym_index).*, + return switch (symbol.file(elf_file).?) { + .zig_object => |x| x.symtab.items(.elf_sym)[symbol.esym_index], inline else => |x| x.symtab.items[symbol.esym_index], }; } @@ -159,20 +156,20 @@ pub fn outputSymtabIndex(symbol: Symbol, elf_file: *Elf) ?u32 { const symtab_ctx = switch (file_ptr) { inline else => |x| x.output_symtab_ctx, }; - const idx = symbol.extra(elf_file).?.symtab; + const idx = symbol.extra(elf_file).symtab; return if (symbol.isLocal(elf_file)) idx + symtab_ctx.ilocal else idx + symtab_ctx.iglobal; } pub fn gotAddress(symbol: Symbol, elf_file: *Elf) i64 { if (!symbol.flags.has_got) return 0; - const extras = symbol.extra(elf_file).?; + const extras = symbol.extra(elf_file); const entry = elf_file.got.entries.items[extras.got]; return entry.address(elf_file); } pub fn pltGotAddress(symbol: Symbol, elf_file: *Elf) i64 { if (!(symbol.flags.has_plt and symbol.flags.has_got)) return 0; - const extras = symbol.extra(elf_file).?; + const extras = symbol.extra(elf_file); const shdr = elf_file.shdrs.items[elf_file.plt_got_section_index.?]; const cpu_arch = elf_file.getTarget().cpu.arch; return @intCast(shdr.sh_addr + extras.plt_got * PltGotSection.entrySize(cpu_arch)); @@ -180,7 +177,7 @@ pub fn pltGotAddress(symbol: Symbol, elf_file: *Elf) i64 { pub fn pltAddress(symbol: Symbol, elf_file: *Elf) i64 { if (!symbol.flags.has_plt) return 0; - const extras = symbol.extra(elf_file).?; + const extras = symbol.extra(elf_file); const shdr = elf_file.shdrs.items[elf_file.plt_section_index.?]; const cpu_arch = elf_file.getTarget().cpu.arch; return @intCast(shdr.sh_addr + extras.plt * PltSection.entrySize(cpu_arch) + PltSection.preambleSize(cpu_arch)); @@ -188,7 +185,7 @@ pub fn pltAddress(symbol: Symbol, elf_file: *Elf) i64 { pub fn gotPltAddress(symbol: Symbol, elf_file: *Elf) i64 { if (!symbol.flags.has_plt) return 0; - const extras = symbol.extra(elf_file).?; + const extras = symbol.extra(elf_file); const shdr = elf_file.shdrs.items[elf_file.got_plt_section_index.?]; return @intCast(shdr.sh_addr + extras.plt * 8 + GotPltSection.preamble_size); } @@ -201,21 +198,21 @@ pub fn copyRelAddress(symbol: Symbol, elf_file: *Elf) i64 { pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) i64 { if (!symbol.flags.has_tlsgd) return 0; - const extras = symbol.extra(elf_file).?; + const extras = symbol.extra(elf_file); const entry = elf_file.got.entries.items[extras.tlsgd]; return entry.address(elf_file); } pub fn gotTpAddress(symbol: Symbol, elf_file: *Elf) i64 { if (!symbol.flags.has_gottp) return 0; - const extras = symbol.extra(elf_file).?; + const extras = symbol.extra(elf_file); const entry = elf_file.got.entries.items[extras.gottp]; return entry.address(elf_file); } pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) i64 { if (!symbol.flags.has_tlsdesc) return 0; - const extras = symbol.extra(elf_file).?; + const extras = symbol.extra(elf_file); const entry = elf_file.got.entries.items[extras.tlsdesc]; return entry.address(elf_file); } @@ -228,14 +225,14 @@ const GetOrCreateZigGotEntryResult = struct { pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !GetOrCreateZigGotEntryResult { assert(!elf_file.base.isRelocatable()); assert(symbol.flags.needs_zig_got); - if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.zig_got }; + if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).zig_got }; const index = try elf_file.zig_got.addSymbol(symbol_index, elf_file); return .{ .found_existing = false, .index = index }; } pub fn zigGotAddress(symbol: Symbol, elf_file: *Elf) i64 { if (!symbol.flags.has_zig_got) return 0; - const extras = symbol.extra(elf_file).?; + const extras = symbol.extra(elf_file); return elf_file.zig_got.entryAddress(extras.zig_got, elf_file); } @@ -265,11 +262,8 @@ const AddExtraOpts = struct { zig_got: ?u32 = null, }; -pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) !void { - if (symbol.extra(elf_file) == null) { - symbol.extra_index = try elf_file.addSymbolExtra(.{}); - } - var extras = symbol.extra(elf_file).?; +pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) void { + var extras = symbol.extra(elf_file); inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| { if (@field(opts, field.name)) |x| { @field(extras, field.name) = x; @@ -278,12 +272,16 @@ pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) !void { symbol.setExtra(extras, elf_file); } -pub fn extra(symbol: Symbol, elf_file: *Elf) ?Extra { - return elf_file.symbolExtra(symbol.extra_index); +pub fn extra(symbol: Symbol, elf_file: *Elf) Extra { + return switch (symbol.file(elf_file).?) { + inline else => |x| x.symbolExtra(symbol.extra_index), + }; } pub fn setExtra(symbol: Symbol, extras: Extra, elf_file: *Elf) void { - elf_file.setSymbolExtra(symbol.extra_index, extras); + return switch (symbol.file(elf_file).?) { + inline else => |x| x.setSymbolExtra(symbol.extra_index, extras), + }; } pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { @@ -426,12 +424,6 @@ pub const Flags = packed struct { /// Whether this symbol is weak. weak: bool = false, - /// Whether the symbol has its name interned in global symbol - /// resolver table. - /// This happens for any symbol that is considered a global - /// symbol, but is not necessarily an import or export. - global: bool = false, - /// Whether the symbol makes into the output symtab. output_symtab: bool = false, diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 454646bfeb..e01c820f89 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -8,9 +8,11 @@ data: std.ArrayListUnmanaged(u8) = .{}, path: []const u8, index: File.Index, -local_esyms: std.MultiArrayList(ElfSym) = .{}, -global_esyms: std.MultiArrayList(ElfSym) = .{}, +symtab: std.MultiArrayList(ElfSym) = .{}, strtab: StringTable = .{}, +symbols: std.ArrayListUnmanaged(Symbol) = .{}, +symbols_extra: std.ArrayListUnmanaged(u32) = .{}, +symbols_resolver: std.ArrayListUnmanaged(Elf.SymbolResolver.Index) = .{}, local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{}, @@ -87,18 +89,11 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { try self.strtab.buffer.append(gpa, 0); const name_off = try self.strtab.insert(gpa, self.path); - const symbol_index = try elf_file.addSymbol(); - try self.local_symbols.append(gpa, symbol_index); - const symbol_ptr = elf_file.symbol(symbol_index); - symbol_ptr.file_index = self.index; - symbol_ptr.name_offset = name_off; - - const esym_index = try self.addLocalEsym(gpa); - const esym = &self.local_esyms.items(.elf_sym)[esym_index]; - esym.st_name = name_off; + const symbol_index = try self.newLocalSymbol(gpa, name_off); + const sym = self.symbol(symbol_index); + const esym = &self.symtab.items(.elf_sym)[sym.esym_index]; esym.st_info = elf.STT_FILE; esym.st_shndx = elf.SHN_ABS; - symbol_ptr.esym_index = esym_index; switch (comp.config.debug_format) { .strip => {}, @@ -112,9 +107,11 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { pub fn deinit(self: *ZigObject, allocator: Allocator) void { self.data.deinit(allocator); - self.local_esyms.deinit(allocator); - self.global_esyms.deinit(allocator); + self.symtab.deinit(allocator); self.strtab.deinit(allocator); + self.symbols.deinit(allocator); + self.symbols_extra.deinit(allocator); + self.symbols_resolver.deinit(allocator); self.local_symbols.deinit(allocator); self.global_symbols.deinit(allocator); self.globals_lookup.deinit(allocator); @@ -262,50 +259,75 @@ fn saveDebugSectionsSizes(self: *ZigObject, elf_file: *Elf) void { } } -pub fn addLocalEsym(self: *ZigObject, allocator: Allocator) !Symbol.Index { - try self.local_esyms.ensureUnusedCapacity(allocator, 1); - const index = @as(Symbol.Index, @intCast(self.local_esyms.addOneAssumeCapacity())); - var esym = ElfSym{ .elf_sym = Elf.null_sym }; - esym.elf_sym.st_info = elf.STB_LOCAL << 4; - self.local_esyms.set(index, esym); +fn newSymbol(self: *ZigObject, allocator: Allocator, name_off: u32, st_bind: u4) !Symbol.Index { + try self.symtab.ensureUnusedCapacity(allocator, 1); + try self.symbols.ensureUnusedCapacity(allocator, 1); + try self.symbols_extra.ensureUnusedCapacity(allocator, @sizeOf(Symbol.Extra)); + + const index = self.addSymbolAssumeCapacity(); + const sym = &self.symbols.items[index]; + sym.name_offset = name_off; + sym.extra_index = self.addSymbolExtraAssumeCapacity(.{}); + + const esym_idx: u32 = @intCast(self.symtab.addOneAssumeCapacity()); + const esym = ElfSym{ .elf_sym = .{ + .st_value = 0, + .st_name = name_off, + .st_info = @as(u8, @intCast(st_bind)) << 4, + .st_other = 0, + .st_size = 0, + .st_shndx = 0, + } }; + self.symtab.set(index, esym); + sym.esym_index = esym_idx; + return index; } -pub fn addGlobalEsym(self: *ZigObject, allocator: Allocator) !Symbol.Index { - try self.global_esyms.ensureUnusedCapacity(allocator, 1); - const index = @as(Symbol.Index, @intCast(self.global_esyms.addOneAssumeCapacity())); - var esym = ElfSym{ .elf_sym = Elf.null_sym }; - esym.elf_sym.st_info = elf.STB_GLOBAL << 4; - self.global_esyms.set(index, esym); - return index | global_symbol_bit; +fn newLocalSymbol(self: *ZigObject, allocator: Allocator, name_off: u32) !Symbol.Index { + try self.local_symbols.ensureUnusedCapacity(allocator, 1); + const fake_index: Symbol.Index = @intCast(self.local_symbols.items.len); + const index = try self.newSymbol(allocator, name_off, elf.STB_LOCAL); + self.local_symbols.appendAssumeCapacity(index); + return fake_index; } -pub fn newAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index { - const gpa = elf_file.base.comp.gpa; - const atom_index = try self.addAtom(gpa); - const symbol_index = try elf_file.addSymbol(); - const esym_index = try self.addLocalEsym(gpa); +fn newGlobalSymbol(self: *ZigObject, allocator: Allocator, name_off: u32) !Symbol.Index { + try self.global_symbols.ensureUnusedCapacity(allocator, 1); + try self.symbols_resolver.ensureUnusedCapacity(allocator, 1); + const fake_index: Symbol.Index = @intCast(self.global_symbols.items.len); + const index = try self.newSymbol(allocator, name_off, elf.STB_GLOBAL); + self.global_symbols.appendAssumeCapacity(index); + self.symbols_resolver.addOneAssumeCapacity().* = 0; + return fake_index | global_symbol_bit; +} - try self.atoms_indexes.append(gpa, atom_index); - try self.local_symbols.append(gpa, symbol_index); +fn newAtom(self: *ZigObject, allocator: Allocator, name_off: u32) !Atom.Index { + try self.atoms.ensureUnusedCapacity(allocator, 1); + try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra)); + try self.atoms_indexes.ensureUnusedCapacity(allocator, 1); + try self.relocs.ensureUnusedCapacity(allocator, 1); - const symbol_ptr = elf_file.symbol(symbol_index); - symbol_ptr.file_index = self.index; - symbol_ptr.ref = .{ .index = atom_index, .file = self.index }; + const index = self.addAtomAssumeCapacity(); + self.atoms_indexes.appendAssumeCapacity(index); + const atom_ptr = self.atom(index).?; + atom_ptr.name_offset = name_off; - self.local_esyms.items(.shndx)[esym_index] = atom_index; - self.local_esyms.items(.elf_sym)[esym_index].st_shndx = SHN_ATOM; - symbol_ptr.esym_index = esym_index; - - // TODO I'm thinking that maybe we shouldn' set this value unless it's actually needed? - const relocs_index = @as(u32, @intCast(self.relocs.items.len)); - const relocs = try self.relocs.addOne(gpa); - relocs.* = .{}; - - const atom_ptr = self.atom(atom_index).?; + const relocs_index: u32 = @intCast(self.relocs.items.len); + self.relocs.addOneAssumeCapacity().* = .{}; atom_ptr.relocs_section_index = relocs_index; - return symbol_index; + return index; +} + +fn newSymbolWithAtom(self: *ZigObject, allocator: Allocator, name_off: u32) !Symbol.Index { + const atom_index = try self.newAtom(allocator, name_off); + const sym_index = try self.newLocalSymbol(allocator, name_off); + const sym = self.symbol(sym_index); + sym.ref = .{ .index = atom_index, .file = self.index }; + self.symtab.items(.shndx)[sym.esym_index] = atom_index; + self.symtab.items(.elf_sym)[sym.esym_index].st_shndx = SHN_ATOM; + return sym_index; } /// TODO actually create fake input shdrs and return that instead. @@ -320,48 +342,47 @@ pub fn inputShdr(self: *ZigObject, atom_index: Atom.Index, elf_file: *Elf) elf.E return shdr; } -pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) void { - for (self.globals(), 0..) |index, i| { - const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit; - const esym = self.global_esyms.items(.elf_sym)[i]; - const shndx = self.global_esyms.items(.shndx)[i]; +pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) !void { + const gpa = elf_file.base.comp.gpa; - if (esym.st_shndx == elf.SHN_UNDEF) continue; - - if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) { + for (self.global_symbols.items, 0..) |index, i| { + const global = &self.symbols.items[index]; + const esym = global.elfSym(elf_file); + const shndx = self.symtab.items(.shndx)[global.esym_index]; + if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON and esym.st_shndx != elf.SHN_UNDEF) { assert(esym.st_shndx == SHN_ATOM); const atom_ptr = self.atom(shndx) orelse continue; if (!atom_ptr.alive) continue; } - const global = elf_file.symbol(index); - if (self.asFile().symbolRank(esym, false) < global.symbolRank(elf_file)) { - const atom_index = switch (esym.st_shndx) { - elf.SHN_ABS, elf.SHN_COMMON => 0, - SHN_ATOM => shndx, - else => unreachable, - }; - global.value = @intCast(esym.st_value); - global.ref = .{ .index = atom_index, .file = self.index }; - global.esym_index = esym_index; - global.file_index = self.index; - global.version_index = elf_file.default_sym_version; - if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true; + const resolv = &self.symbols_resolver.items[i]; + const gop = try elf_file.resolver.getOrPut(gpa, .{ + .index = @intCast(i | global_symbol_bit), + .file = self.index, + }, elf_file); + if (!gop.found_existing) { + gop.ref.* = .{ .index = 0, .file = 0 }; + } + resolv.* = gop.index; + + if (esym.st_shndx == elf.SHN_UNDEF) continue; + if (elf_file.symbol(gop.ref.*) == null) { + gop.ref.* = .{ .index = @intCast(i | global_symbol_bit), .file = self.index }; + continue; + } + + if (self.asFile().symbolRank(esym, false) < elf_file.symbol(gop.ref.*).?.symbolRank(elf_file)) { + gop.ref.* = .{ .index = @intCast(i | global_symbol_bit), .file = self.index }; } } } -pub fn claimUnresolved(self: ZigObject, elf_file: *Elf) void { - for (self.globals(), 0..) |index, i| { - const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit; - const esym = self.global_esyms.items(.elf_sym)[i]; - +pub fn claimUnresolved(self: *ZigObject, elf_file: *Elf) void { + for (self.global_symbols.items, 0..) |index, i| { + const global = &self.symbols.items[index]; + const esym = self.symtab.items(.elf_sym)[index]; if (esym.st_shndx != elf.SHN_UNDEF) continue; - - const global = elf_file.symbol(index); - if (global.file(elf_file)) |_| { - if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF) continue; - } + if (elf_file.symbol(self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file)) != null) continue; const is_import = blk: { if (!elf_file.isEffectivelyDynLib()) break :blk false; @@ -372,29 +393,30 @@ pub fn claimUnresolved(self: ZigObject, elf_file: *Elf) void { global.value = 0; global.ref = .{ .index = 0, .file = 0 }; - global.esym_index = esym_index; + 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.flags.import = is_import; + + const idx = self.symbols_resolver.items[i]; + elf_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i | global_symbol_bit), .file = self.index }; } } pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void { - for (self.globals(), 0..) |index, i| { - const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit; - const esym = self.global_esyms.items(.elf_sym)[i]; - + for (self.global_symbols.items, 0..) |index, i| { + const global = &self.symbols.items[index]; + const esym = self.symtab.items(.elf_sym)[index]; if (esym.st_shndx != elf.SHN_UNDEF) continue; - - const global = elf_file.symbol(index); - if (global.file(elf_file)) |file| { - if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF or file.index() <= self.index) continue; - } + if (elf_file.symbol(self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file)) != null) continue; global.value = 0; global.ref = .{ .index = 0, .file = 0 }; - global.esym_index = esym_index; + global.esym_index = @intCast(index); global.file_index = self.index; + + const idx = self.symbols_resolver.items[i]; + elf_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i | global_symbol_bit), .file = self.index }; } } @@ -417,12 +439,14 @@ pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void { } pub fn markLive(self: *ZigObject, elf_file: *Elf) void { - for (self.globals(), 0..) |index, i| { - const esym = self.global_esyms.items(.elf_sym)[i]; + for (self.global_symbols.items, 0..) |index, i| { + const global = self.symbols.items[index]; + const esym = self.symtab.items(.elf_sym)[index]; if (esym.st_bind() == elf.STB_WEAK) continue; - const global = elf_file.symbol(index); - const file = global.file(elf_file) orelse continue; + 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).?; const should_keep = esym.st_shndx == elf.SHN_UNDEF or (esym.st_shndx == elf.SHN_COMMON and global.elfSym(elf_file).st_shndx != elf.SHN_COMMON); if (should_keep and !file.isAlive()) { @@ -432,14 +456,36 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void { } } -pub fn checkDuplicates(self: *ZigObject, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void { - for (self.globals(), 0..) |index, i| { - const esym = self.global_esyms.items(.elf_sym)[i]; - const shndx = self.global_esyms.items(.shndx)[i]; - const global = elf_file.symbol(index); - const global_file = global.file(elf_file) orelse continue; +pub fn markImportsExports(self: *ZigObject, elf_file: *Elf) void { + for (0..self.global_symbols.items.len) |i| { + 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)); + if (vis == .HIDDEN) continue; + if (file == .shared_object and !sym.isAbs(elf_file)) { + sym.flags.import = true; + continue; + } + if (file.index() == self.index) { + sym.flags.@"export" = true; + if (elf_file.isEffectivelyDynLib() and vis != .PROTECTED) { + sym.flags.import = true; + } + } + } +} - if (self.index == global_file.index() or +pub fn checkDuplicates(self: *ZigObject, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void { + for (self.global_symbols.items, 0..) |index, i| { + const esym = self.symtab.items(.elf_sym)[index]; + const shndx = self.symtab.items(.shndx)[index]; + const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file); + const ref_sym = elf_file.symbol(ref) orelse continue; + const ref_file = ref_sym.file(elf_file).?; + + if (self.index == ref_file.index() or esym.st_shndx == elf.SHN_UNDEF or esym.st_bind() == elf.STB_WEAK or esym.st_shndx == elf.SHN_COMMON) continue; @@ -449,7 +495,7 @@ pub fn checkDuplicates(self: *ZigObject, dupes: anytype, elf_file: *Elf) error{O if (!atom_ptr.alive) continue; } - const gop = try dupes.getOrPut(index); + const gop = try dupes.getOrPut(self.symbols_resolver.items[i]); if (!gop.found_existing) { gop.value_ptr.* = .{}; } @@ -481,12 +527,13 @@ pub fn readFileContents(self: *ZigObject, elf_file: *Elf) !void { pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void { const gpa = elf_file.base.comp.gpa; - try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.globals().len); + try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.global_symbols.items.len); - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - const file_ptr = global.file(elf_file).?; - assert(file_ptr.index() == self.index); + for (self.global_symbols.items, 0..) |index, i| { + const global = self.symbols.items[index]; + const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file); + const sym = elf_file.symbol(ref).?; + assert(sym.file(elf_file).?.index() == self.index); if (global.outputShndx(elf_file) == null) continue; const off = try ar_symtab.strtab.insert(gpa, global.name(elf_file)); @@ -528,33 +575,9 @@ pub fn addAtomsToRelaSections(self: *ZigObject, elf_file: *Elf) !void { } } -inline fn isGlobal(index: Symbol.Index) bool { - return index & global_symbol_bit != 0; -} - -pub fn symbol(self: ZigObject, index: Symbol.Index) Symbol.Index { - const actual_index = index & symbol_mask; - if (isGlobal(index)) return self.globals()[actual_index]; - return self.locals()[actual_index]; -} - -pub fn elfSym(self: *ZigObject, index: Symbol.Index) *elf.Elf64_Sym { - const actual_index = index & symbol_mask; - if (isGlobal(index)) return &self.global_esyms.items(.elf_sym)[actual_index]; - return &self.local_esyms.items(.elf_sym)[actual_index]; -} - -pub fn locals(self: ZigObject) []const Symbol.Index { - return self.local_symbols.items; -} - -pub fn globals(self: ZigObject) []const Symbol.Index { - return self.global_symbols.items; -} - pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void { - for (self.locals()) |local_index| { - const local = elf_file.symbol(local_index); + for (self.local_symbols.items) |index| { + const local = &self.symbols.items[index]; if (local.atom(elf_file)) |atom_ptr| if (!atom_ptr.alive) continue; const esym = local.elfSym(elf_file); switch (esym.st_type()) { @@ -562,22 +585,23 @@ pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void { else => {}, } local.flags.output_symtab = true; - try local.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file); + local.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file); self.output_symtab_ctx.nlocals += 1; self.output_symtab_ctx.strsize += @as(u32, @intCast(local.name(elf_file).len)) + 1; } - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - const file_ptr = global.file(elf_file) orelse continue; - if (file_ptr.index() != self.index) continue; + for (self.global_symbols.items, self.symbols_resolver.items) |index, resolv| { + const global = &self.symbols.items[index]; + const ref = elf_file.resolver.values.items[resolv - 1]; + const ref_sym = elf_file.symbol(ref) orelse continue; + if (ref_sym.file(elf_file).?.index() != self.index) continue; if (global.atom(elf_file)) |atom_ptr| if (!atom_ptr.alive) continue; global.flags.output_symtab = true; if (global.isLocal(elf_file)) { - try global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file); + global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file); self.output_symtab_ctx.nlocals += 1; } else { - try global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file); + global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file); self.output_symtab_ctx.nglobals += 1; } self.output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1; @@ -585,8 +609,8 @@ pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void { } pub fn writeSymtab(self: ZigObject, elf_file: *Elf) void { - for (self.locals()) |local_index| { - const local = elf_file.symbol(local_index); + for (self.local_symbols.items) |index| { + const local = &self.symbols.items[index]; const idx = local.outputSymtabIndex(elf_file) orelse continue; const out_sym = &elf_file.symtab.items[idx]; out_sym.st_name = @intCast(elf_file.strtab.items.len); @@ -595,10 +619,11 @@ pub fn writeSymtab(self: ZigObject, elf_file: *Elf) void { local.setOutputSym(elf_file, out_sym); } - for (self.globals()) |global_index| { - const global = elf_file.symbol(global_index); - const file_ptr = global.file(elf_file) orelse continue; - if (file_ptr.index() != self.index) continue; + for (self.global_symbols.items, self.symbols_resolver.items) |index, resolv| { + const global = self.symbols.items[index]; + const ref = elf_file.resolver.values.items[resolv - 1]; + const ref_sym = elf_file.symbol(ref) orelse continue; + if (ref_sym.file(elf_file).?.index() != self.index) continue; const idx = global.outputSymtabIndex(elf_file) orelse continue; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file)); @@ -609,10 +634,6 @@ pub fn writeSymtab(self: ZigObject, elf_file: *Elf) void { } } -pub fn asFile(self: *ZigObject) File { - return .{ .zig_object = self }; -} - /// Returns atom's code. /// Caller owns the memory. pub fn codeAlloc(self: *ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8 { @@ -645,13 +666,13 @@ pub fn getDeclVAddr( reloc_info: link.File.RelocInfo, ) !u64 { const this_sym_index = try self.getOrCreateMetadataForDecl(elf_file, decl_index); - const this_sym = elf_file.symbol(this_sym_index); + const this_sym = self.symbol(this_sym_index); const vaddr = this_sym.address(.{}, elf_file); - const parent_atom = elf_file.symbol(reloc_info.parent_atom_index).atom(elf_file).?; + const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(elf_file).?; const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch); try parent_atom.addReloc(elf_file, .{ .r_offset = reloc_info.offset, - .r_info = (@as(u64, @intCast(this_sym.esym_index)) << 32) | r_type, + .r_info = (@as(u64, @intCast(this_sym_index)) << 32) | r_type, .r_addend = reloc_info.addend, }); return @intCast(vaddr); @@ -664,13 +685,13 @@ pub fn getAnonDeclVAddr( reloc_info: link.File.RelocInfo, ) !u64 { const sym_index = self.anon_decls.get(decl_val).?.symbol_index; - const sym = elf_file.symbol(sym_index); + const sym = self.symbol(sym_index); const vaddr = sym.address(.{}, elf_file); - const parent_atom = elf_file.symbol(reloc_info.parent_atom_index).atom(elf_file).?; + const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(elf_file).?; const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch); try parent_atom.addReloc(elf_file, .{ .r_offset = reloc_info.offset, - .r_info = (@as(u64, @intCast(sym.esym_index)) << 32) | r_type, + .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, .r_addend = reloc_info.addend, }); return @intCast(vaddr); @@ -692,7 +713,7 @@ pub fn lowerAnonDecl( else => explicit_alignment, }; if (self.anon_decls.get(decl_val)) |metadata| { - const existing_alignment = elf_file.symbol(metadata.symbol_index).atom(elf_file).?.alignment; + const existing_alignment = self.symbol(metadata.symbol_index).atom(elf_file).?.alignment; if (decl_alignment.order(existing_alignment).compare(.lte)) return .ok; } @@ -753,8 +774,8 @@ pub fn getOrCreateMetadataForLazySymbol( }; switch (metadata.state.*) { .unused => { - const symbol_index = try self.newAtom(elf_file); - const sym = elf_file.symbol(symbol_index); + const symbol_index = try self.newSymbolWithAtom(gpa, 0); + const sym = self.symbol(symbol_index); sym.flags.needs_zig_got = true; metadata.symbol_index.* = symbol_index; }, @@ -778,13 +799,10 @@ fn freeUnnamedConsts(self: *ZigObject, elf_file: *Elf, decl_index: InternPool.De } fn freeDeclMetadata(self: *ZigObject, elf_file: *Elf, sym_index: Symbol.Index) void { - _ = self; - const gpa = elf_file.base.comp.gpa; - const sym = elf_file.symbol(sym_index); + const sym = self.symbol(sym_index); sym.atom(elf_file).?.free(elf_file); log.debug("adding %{d} to local symbols free list", .{sym_index}); - elf_file.symbols_free_list.append(gpa, sym_index) catch {}; - elf_file.symbols.items[sym_index] = .{}; + self.symbols.items[sym_index] = .{}; // TODO free GOT entry here } @@ -817,10 +835,10 @@ pub fn getOrCreateMetadataForDecl( const gop = try self.decls.getOrPut(gpa, decl_index); if (!gop.found_existing) { const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded; - const symbol_index = try self.newAtom(elf_file); + const symbol_index = try self.newSymbolWithAtom(gpa, 0); const mod = elf_file.base.comp.module.?; const decl = mod.declPtr(decl_index); - const sym = elf_file.symbol(symbol_index); + const sym = self.symbol(symbol_index); if (decl.getOwnedVariable(mod)) |variable| { if (variable.is_threadlocal and any_non_single_threaded) { sym.flags.is_tls = true; @@ -909,8 +927,8 @@ fn updateDeclCode( target_util.minFunctionAlignment(mod.getTarget()), ); - const sym = elf_file.symbol(sym_index); - const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index]; + const sym = self.symbol(sym_index); + const esym = &self.symtab.items(.elf_sym)[sym.esym_index]; const atom_ptr = sym.atom(elf_file).?; const name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip)); @@ -940,7 +958,7 @@ fn updateDeclCode( if (!elf_file.base.isRelocatable()) { log.debug(" (writing new offset table entry)", .{}); assert(sym.flags.has_zig_got); - const extra = sym.extra(elf_file).?; + const extra = sym.extra(elf_file); try elf_file.zig_got.writeOne(elf_file, extra.zig_got); } } @@ -1007,8 +1025,8 @@ fn updateTlv( const required_alignment = decl.getAlignment(pt); - const sym = elf_file.symbol(sym_index); - const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index]; + const sym = self.symbol(sym_index); + const esym = &self.symtab.items(.elf_sym)[sym.esym_index]; const atom_ptr = sym.atom(elf_file).?; const name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip)); @@ -1064,7 +1082,7 @@ pub fn updateFunc( const sym_index = try self.getOrCreateMetadataForDecl(elf_file, decl_index); self.freeUnnamedConsts(elf_file, decl_index); - elf_file.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file); + self.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file); var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); @@ -1096,7 +1114,7 @@ pub fn updateFunc( try self.updateDeclCode(elf_file, pt, decl_index, sym_index, shndx, code, elf.STT_FUNC); if (decl_state) |*ds| { - const sym = elf_file.symbol(sym_index); + const sym = self.symbol(sym_index); try self.dwarf.?.commitDeclState( pt, decl_index, @@ -1130,13 +1148,13 @@ pub fn updateDecl( const variable = decl.getOwnedVariable(mod).?; const name = decl.name.toSlice(&mod.intern_pool); const lib_name = variable.lib_name.toSlice(&mod.intern_pool); - const esym_index = try self.getGlobalSymbol(elf_file, name, lib_name); - elf_file.symbol(self.symbol(esym_index)).flags.needs_got = true; + const sym_index = try self.getGlobalSymbol(elf_file, name, lib_name); + self.symbol(sym_index).flags.needs_got = true; return; } const sym_index = try self.getOrCreateMetadataForDecl(elf_file, decl_index); - elf_file.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file); + self.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file); const gpa = elf_file.base.comp.gpa; var code_buffer = std.ArrayList(u8).init(gpa); @@ -1174,7 +1192,7 @@ pub fn updateDecl( try self.updateDeclCode(elf_file, pt, decl_index, sym_index, shndx, code, elf.STT_OBJECT); if (decl_state) |*ds| { - const sym = elf_file.symbol(sym_index); + const sym = self.symbol(sym_index); try self.dwarf.?.commitDeclState( pt, decl_index, @@ -1233,9 +1251,9 @@ fn updateLazySymbol( .code => elf_file.zig_text_section_index.?, .const_data => elf_file.zig_data_rel_ro_section_index.?, }; - const local_sym = elf_file.symbol(symbol_index); + const local_sym = self.symbol(symbol_index); local_sym.name_offset = name_str_index; - const local_esym = &self.local_esyms.items(.elf_sym)[local_sym.esym_index]; + const local_esym = &self.symtab.items(.elf_sym)[local_sym.esym_index]; local_esym.st_name = name_str_index; local_esym.st_info |= elf.STT_OBJECT; local_esym.st_size = code.len; @@ -1323,7 +1341,8 @@ fn lowerConst( var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); - const sym_index = try self.newAtom(elf_file); + const name_off = try self.addString(gpa, name); + const sym_index = try self.newSymbolWithAtom(gpa, name_off); const res = try codegen.generateSymbol( &elf_file.base, @@ -1339,27 +1358,19 @@ fn lowerConst( .fail => |em| return .{ .fail = em }, }; - const local_sym = elf_file.symbol(sym_index); - const name_str_index = try self.strtab.insert(gpa, name); - local_sym.name_offset = name_str_index; - const local_esym = &self.local_esyms.items(.elf_sym)[local_sym.esym_index]; - local_esym.st_name = name_str_index; + const local_sym = self.symbol(sym_index); + const local_esym = &self.symtab.items(.elf_sym)[local_sym.esym_index]; local_esym.st_info |= elf.STT_OBJECT; local_esym.st_size = code.len; const atom_ptr = local_sym.atom(elf_file).?; atom_ptr.alive = true; - atom_ptr.name_offset = name_str_index; atom_ptr.alignment = required_alignment; atom_ptr.size = code.len; atom_ptr.output_section_index = output_section_index; try atom_ptr.allocate(elf_file); - // TODO rename and re-audit this method errdefer self.freeDeclMetadata(elf_file, sym_index); - local_sym.value = 0; - local_esym.st_value = 0; - const shdr = elf_file.shdrs.items[output_section_index]; const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value)); try elf_file.base.file.?.pwriteAll(code, file_offset); @@ -1401,9 +1412,9 @@ pub fn updateExports( }, }; const sym_index = metadata.symbol_index; - const esym_index = elf_file.symbol(sym_index).esym_index; - const esym = self.local_esyms.items(.elf_sym)[esym_index]; - const esym_shndx = self.local_esyms.items(.shndx)[esym_index]; + const esym_index = self.symbol(sym_index).esym_index; + const esym = self.symtab.items(.elf_sym)[esym_index]; + const esym_shndx = self.symtab.items(.shndx)[esym_index]; for (export_indices) |export_idx| { const exp = mod.all_exports.items[export_idx]; @@ -1437,22 +1448,27 @@ pub fn updateExports( const stt_bits: u8 = @as(u4, @truncate(esym.st_info)); const exp_name = exp.opts.name.toSlice(&mod.intern_pool); const name_off = try self.strtab.insert(gpa, exp_name); - const global_esym_index = if (metadata.@"export"(self, exp_name)) |exp_index| + const global_sym_index = if (metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: { - const global_esym_index = try self.getGlobalSymbol(elf_file, exp_name, null); - try metadata.exports.append(gpa, global_esym_index); - break :blk global_esym_index; + const global_sym_index = try self.getGlobalSymbol(elf_file, exp_name, null); + try metadata.exports.append(gpa, global_sym_index); + break :blk global_sym_index; }; - const actual_esym_index = global_esym_index & symbol_mask; - const global_esym = &self.global_esyms.items(.elf_sym)[actual_esym_index]; - global_esym.st_value = @intCast(elf_file.symbol(sym_index).value); + const value = self.symbol(sym_index).value; + const global_sym = self.symbol(global_sym_index); + global_sym.value = value; + global_sym.flags.weak = exp.opts.linkage == .weak; + global_sym.version_index = elf_file.default_sym_version; + global_sym.ref = .{ .index = esym_shndx, .file = self.index }; + const global_esym = &self.symtab.items(.elf_sym)[global_sym.esym_index]; + global_esym.st_value = @intCast(value); global_esym.st_shndx = esym.st_shndx; global_esym.st_info = (stb_bits << 4) | stt_bits; global_esym.st_name = name_off; global_esym.st_size = esym.st_size; - self.global_esyms.items(.shndx)[actual_esym_index] = esym_shndx; + self.symtab.items(.shndx)[global_sym.esym_index] = esym_shndx; } } @@ -1488,16 +1504,10 @@ pub fn deleteExport( const exp_name = name.toSlice(&mod.intern_pool); const esym_index = metadata.@"export"(self, exp_name) orelse return; log.debug("deleting export '{s}'", .{exp_name}); - const esym = &self.global_esyms.items(.elf_sym)[esym_index.*]; + const esym = &self.symtab.items(.elf_sym)[esym_index.*]; _ = self.globals_lookup.remove(esym.st_name); - const sym_index = elf_file.resolver.get(esym.st_name).?; - const sym = elf_file.symbol(sym_index); - if (sym.file_index == self.index) { - _ = elf_file.resolver.swapRemove(esym.st_name); - sym.* = .{}; - } esym.* = Elf.null_sym; - self.global_esyms.items(.shndx)[esym_index.*] = elf.SHN_UNDEF; + self.symtab.items(.shndx)[esym_index.*] = elf.SHN_UNDEF; } pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 { @@ -1506,16 +1516,19 @@ pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_n const off = try self.strtab.insert(gpa, name); const lookup_gop = try self.globals_lookup.getOrPut(gpa, off); if (!lookup_gop.found_existing) { - const esym_index = try self.addGlobalEsym(gpa); - const esym = self.elfSym(esym_index); - esym.st_name = off; - lookup_gop.value_ptr.* = esym_index; - const gop = try elf_file.getOrPutGlobal(name); - try self.global_symbols.append(gpa, gop.index); + lookup_gop.value_ptr.* = try self.newGlobalSymbol(gpa, off); } return lookup_gop.value_ptr.*; } +pub fn asFile(self: *ZigObject) File { + return .{ .zig_object = self }; +} + +fn addString(self: *ZigObject, allocator: Allocator, string: []const u8) !u32 { + return self.strtab.insert(allocator, string); +} + pub fn getString(self: ZigObject, off: u32) [:0]const u8 { return self.strtab.getAssumeExists(off); } @@ -1586,6 +1599,77 @@ pub fn setAtomExtra(self: *ZigObject, index: u32, extra: Atom.Extra) void { } } +inline fn isGlobal(index: Symbol.Index) bool { + return index & global_symbol_bit != 0; +} + +pub fn symbol(self: *ZigObject, index: Symbol.Index) *Symbol { + const actual_index = index & symbol_mask; + if (isGlobal(index)) return &self.symbols.items[self.global_symbols.items[actual_index]]; + return &self.symbols.items[self.local_symbols.items[actual_index]]; +} + +pub fn resolveSymbol(self: ZigObject, index: Symbol.Index, elf_file: *Elf) Elf.Ref { + if (isGlobal(index)) { + const resolv = self.symbols_resolver.items[index & symbol_mask]; + return elf_file.resolver.get(resolv).?; + } + return .{ .index = index, .file = self.index }; +} + +fn addSymbol(self: *ZigObject, allocator: Allocator) !Symbol.Index { + try self.symbols.ensureUnusedCapacity(allocator, 1); + return self.addSymbolAssumeCapacity(); +} + +fn addSymbolAssumeCapacity(self: *ZigObject) Symbol.Index { + const index: Symbol.Index = @intCast(self.symbols.items.len); + self.symbols.appendAssumeCapacity(.{ .file_index = self.index }); + return index; +} + +pub fn addSymbolExtra(self: *ZigObject, 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: *ZigObject, extra: Symbol.Extra) u32 { + const index = @as(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) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn symbolExtra(self: *ZigObject, index: u32) Symbol.Extra { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + var i: usize = index; + var result: Symbol.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.symbols_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.symbols_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { return .{ .data = .{ .self = self, @@ -1606,15 +1690,17 @@ fn formatSymtab( ) !void { _ = unused_fmt_string; _ = options; + const self = ctx.self; + const elf_file = ctx.elf_file; try writer.writeAll(" locals\n"); - for (ctx.self.locals()) |index| { - const local = ctx.elf_file.symbol(index); - try writer.print(" {}\n", .{local.fmt(ctx.elf_file)}); + for (self.local_symbols.items) |index| { + const local = self.symbols.items[index]; + try writer.print(" {}\n", .{local.fmt(elf_file)}); } try writer.writeAll(" globals\n"); - for (ctx.self.globals()) |index| { - const global = ctx.elf_file.symbol(index); - try writer.print(" {}\n", .{global.fmt(ctx.elf_file)}); + for (ctx.self.global_symbols.items) |index| { + const global = self.symbols.items[index]; + try writer.print(" {}\n", .{global.fmt(elf_file)}); } } @@ -1658,9 +1744,9 @@ const DeclMetadata = struct { /// A list of all exports aliases of this Decl. exports: std.ArrayListUnmanaged(Symbol.Index) = .{}, - fn @"export"(m: DeclMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 { + fn @"export"(m: DeclMetadata, zo: *ZigObject, name: []const u8) ?*u32 { for (m.exports.items) |*exp| { - const exp_name = zig_object.getString(zig_object.elfSym(exp.*).st_name); + const exp_name = zo.getString(zo.symbol(exp.*).name_offset); if (mem.eql(u8, name, exp_name)) return exp; } return null; diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index 1ca1b90906..f090cf0820 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -145,10 +145,10 @@ pub const Cie = struct { if (cie_rel.r_addend != other_rel.r_addend) return false; const cie_object = elf_file.file(cie.file_index).?.object; + const cie_ref = cie_object.resolveSymbol(cie_rel.r_sym(), elf_file); const other_object = elf_file.file(other.file_index).?.object; - const cie_sym = cie_object.symbols.items[cie_rel.r_sym()]; - const other_sym = other_object.symbols.items[other_rel.r_sym()]; - if (!std.mem.eql(u8, std.mem.asBytes(&cie_sym), std.mem.asBytes(&other_sym))) return false; + const other_ref = other_object.resolveSymbol(other_rel.r_sym(), elf_file); + if (!cie_ref.eql(other_ref)) return false; } return true; } @@ -339,7 +339,8 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { const contents = cie.data(elf_file); for (cie.relocs(elf_file)) |rel| { - const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); + const ref = object.resolveSymbol(rel.r_sym(), elf_file); + const sym = elf_file.symbol(ref).?; resolveReloc(cie, sym, rel, elf_file, contents) catch |err| switch (err) { error.RelocFailure => has_reloc_errors = true, else => |e| return e, @@ -366,7 +367,8 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { ); for (fde.relocs(elf_file)) |rel| { - const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); + const ref = object.resolveSymbol(rel.r_sym(), elf_file); + const sym = elf_file.symbol(ref).?; resolveReloc(fde, sym, rel, elf_file, contents) catch |err| switch (err) { error.RelocFailure => has_reloc_errors = true, else => |e| return e, @@ -452,7 +454,8 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void { for (object.cies.items) |cie| { if (!cie.alive) continue; for (cie.relocs(elf_file)) |rel| { - const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); + const ref = object.resolveSymbol(rel.r_sym(), elf_file); + const sym = elf_file.symbol(ref).?; const out_rel = emitReloc(elf_file, cie, sym, rel); try writer.writeStruct(out_rel); } @@ -461,7 +464,8 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void { for (object.fdes.items) |fde| { if (!fde.alive) continue; for (fde.relocs(elf_file)) |rel| { - const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); + const ref = object.resolveSymbol(rel.r_sym(), elf_file); + const sym = elf_file.symbol(ref).?; const out_rel = emitReloc(elf_file, fde, sym, rel); try writer.writeStruct(out_rel); } @@ -513,7 +517,8 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void { const relocs = fde.relocs(elf_file); assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips... const rel = relocs[0]; - const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); + const ref = object.resolveSymbol(rel.r_sym(), elf_file); + const sym = elf_file.symbol(ref).?; const P = @as(i64, @intCast(fde.address(elf_file))); const S = @as(i64, @intCast(sym.address(.{}, elf_file))); const A = rel.r_addend; diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 9d2560e11b..88dc807274 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -61,20 +61,10 @@ pub const File = union(enum) { return (@as(u32, base) << 24) + file.index(); } - pub fn resolveSymbols(file: File, elf_file: *Elf) void { - switch (file) { + pub fn resolveSymbols(file: File, elf_file: *Elf) !void { + return switch (file) { inline else => |x| x.resolveSymbols(elf_file), - } - } - - pub fn resetGlobals(file: File, elf_file: *Elf) void { - for (file.globals()) |global_index| { - const global = elf_file.symbol(global_index); - const name_offset = global.name_offset; - global.* = .{}; - global.name_offset = name_offset; - global.flags.global = true; - } + }; } pub fn setAlive(file: File) void { @@ -98,6 +88,77 @@ pub const File = union(enum) { } } + pub fn createSymbolIndirection(file: File, elf_file: *Elf) !void { + const impl = struct { + fn impl(sym: *Symbol, ref: Elf.Ref, ef: *Elf) !void { + if (!sym.isLocal(ef) and !sym.flags.has_dynamic) { + log.debug("'{s}' is non-local", .{sym.name(ef)}); + try ef.dynsym.addSymbol(ref, ef); + } + if (sym.flags.needs_got) { + log.debug("'{s}' needs GOT", .{sym.name(ef)}); + _ = try ef.got.addGotSymbol(ref, ef); + } + if (sym.flags.needs_plt) { + if (sym.flags.is_canonical) { + log.debug("'{s}' needs CPLT", .{sym.name(ef)}); + sym.flags.@"export" = true; + try ef.plt.addSymbol(ref, ef); + } else if (sym.flags.needs_got) { + log.debug("'{s}' needs PLTGOT", .{sym.name(ef)}); + try ef.plt_got.addSymbol(ref, ef); + } else { + log.debug("'{s}' needs PLT", .{sym.name(ef)}); + try ef.plt.addSymbol(ref, ef); + } + } + if (sym.flags.needs_copy_rel and !sym.flags.has_copy_rel) { + log.debug("'{s}' needs COPYREL", .{sym.name(ef)}); + try ef.copy_rel.addSymbol(ref, ef); + } + if (sym.flags.needs_tlsgd) { + log.debug("'{s}' needs TLSGD", .{sym.name(ef)}); + try ef.got.addTlsGdSymbol(ref, ef); + } + if (sym.flags.needs_gottp) { + log.debug("'{s}' needs GOTTP", .{sym.name(ef)}); + try ef.got.addGotTpSymbol(ref, ef); + } + if (sym.flags.needs_tlsdesc) { + log.debug("'{s}' needs TLSDESC", .{sym.name(ef)}); + try ef.got.addTlsDescSymbol(ref, ef); + } + } + }.impl; + + switch (file) { + .zig_object => |x| { + for (x.local_symbols.items, 0..) |idx, i| { + const sym = &x.symbols.items[idx]; + const ref = x.resolveSymbol(@intCast(i), elf_file); + const ref_sym = elf_file.symbol(ref) orelse continue; + if (ref_sym.file(elf_file).?.index() != x.index) continue; + try impl(sym, ref, elf_file); + } + for (x.global_symbols.items, 0..) |idx, i| { + const sym = &x.symbols.items[idx]; + const ref = x.resolveSymbol(@intCast(i | ZigObject.global_symbol_bit), elf_file); + const ref_sym = elf_file.symbol(ref) orelse continue; + if (ref_sym.file(elf_file).?.index() != x.index) continue; + try impl(sym, ref, elf_file); + } + }, + inline else => |x| { + for (x.symbols.items, 0..) |*sym, i| { + const ref = x.resolveSymbol(@intCast(i), elf_file); + const ref_sym = elf_file.symbol(ref) orelse continue; + if (ref_sym.file(elf_file).?.index() != x.index) continue; + try impl(sym, ref, elf_file); + } + }, + } + } + pub fn atom(file: File, atom_index: Atom.Index) ?*Atom { return switch (file) { .shared_object => unreachable, @@ -144,23 +205,16 @@ pub const File = union(enum) { }; } - pub fn symbol(file: File, ind: Symbol.Index) Symbol.Index { + pub fn resolveSymbol(file: File, ind: Symbol.Index, elf_file: *Elf) Elf.Ref { + return switch (file) { + inline else => |x| x.resolveSymbol(ind, elf_file), + }; + } + + pub fn symbol(file: File, ind: Symbol.Index) *Symbol { return switch (file) { .zig_object => |x| x.symbol(ind), - inline else => |x| x.symbols.items[ind], - }; - } - - pub fn locals(file: File) []const Symbol.Index { - return switch (file) { - .linker_defined, .shared_object => &[0]Symbol.Index{}, - inline else => |x| x.locals(), - }; - } - - pub fn globals(file: File) []const Symbol.Index { - return switch (file) { - inline else => |x| x.globals(), + inline else => |x| &x.symbols.items[ind], }; } @@ -237,6 +291,7 @@ pub const File = union(enum) { const std = @import("std"); const elf = std.elf; +const log = std.log.scoped(.link); const Allocator = std.mem.Allocator; const Archive = @import("Archive.zig"); diff --git a/src/link/Elf/gc.zig b/src/link/Elf/gc.zig index de2d43f738..e0680d5db6 100644 --- a/src/link/Elf/gc.zig +++ b/src/link/Elf/gc.zig @@ -1,72 +1,85 @@ pub fn gcAtoms(elf_file: *Elf) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; - const num_files = elf_file.objects.items.len + @intFromBool(elf_file.zig_object_index != null); - var files = try std.ArrayList(File.Index).initCapacity(gpa, num_files); - defer files.deinit(); - if (elf_file.zig_object_index) |index| files.appendAssumeCapacity(index); - for (elf_file.objects.items) |index| files.appendAssumeCapacity(index); - var roots = std.ArrayList(*Atom).init(gpa); defer roots.deinit(); - try collectRoots(&roots, files.items, elf_file); - + try collectRoots(&roots, elf_file); mark(roots, elf_file); - prune(files.items, elf_file); + prune(elf_file); } -fn collectRoots(roots: *std.ArrayList(*Atom), files: []const File.Index, elf_file: *Elf) !void { +fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void { if (elf_file.linkerDefinedPtr()) |obj| { - if (obj.entry_index) |index| { - const global = elf_file.symbol(index); - try markSymbol(global, roots, elf_file); + if (obj.entrySymbol(elf_file)) |sym| { + try markSymbol(sym, roots, elf_file); } } - for (files) |index| { - for (elf_file.file(index).?.globals()) |global_index| { - const global = elf_file.symbol(global_index); - if (global.file(elf_file)) |file| { - if (file.index() == index and global.flags.@"export") - try markSymbol(global, roots, elf_file); - } - } - } - - for (files) |index| { - const file = elf_file.file(index).?; - - for (file.atoms()) |atom_index| { - const atom = file.atom(atom_index) orelse continue; - if (!atom.alive) continue; - - const shdr = atom.inputShdr(elf_file); - const name = atom.name(elf_file); - const is_gc_root = blk: { - if (shdr.sh_flags & elf.SHF_GNU_RETAIN != 0) break :blk true; - if (shdr.sh_type == elf.SHT_NOTE) break :blk true; - if (shdr.sh_type == elf.SHT_PREINIT_ARRAY) break :blk true; - if (shdr.sh_type == elf.SHT_INIT_ARRAY) break :blk true; - if (shdr.sh_type == elf.SHT_FINI_ARRAY) break :blk true; - if (mem.startsWith(u8, name, ".ctors")) break :blk true; - if (mem.startsWith(u8, name, ".dtors")) break :blk true; - if (mem.startsWith(u8, name, ".init")) break :blk true; - if (mem.startsWith(u8, name, ".fini")) break :blk true; - if (Elf.isCIdentifier(name)) break :blk true; - break :blk false; - }; - if (is_gc_root and markAtom(atom)) try roots.append(atom); - if (shdr.sh_flags & elf.SHF_ALLOC == 0) atom.visited = true; - } - - // Mark every atom referenced by CIE as alive. - for (file.cies()) |cie| { - for (cie.relocs(elf_file)) |rel| { - const sym = elf_file.symbol(file.symbol(rel.r_sym())); + if (elf_file.zigObjectPtr()) |zo| { + for (0..zo.global_symbols.items.len) |i| { + const ref = zo.resolveSymbol(@intCast(i | ZigObject.global_symbol_bit), elf_file); + const sym = elf_file.symbol(ref) orelse continue; + if (sym.file(elf_file).?.index() != zo.index) continue; + if (sym.flags.@"export") { try markSymbol(sym, roots, elf_file); } } } + + for (elf_file.objects.items) |index| { + const object = elf_file.file(index).?.object; + for (0..object.globals().len) |i| { + const ref = object.resolveSymbol(@intCast(i), elf_file); + const sym = elf_file.symbol(ref) orelse continue; + if (sym.file(elf_file).?.index() != object.index) continue; + if (sym.flags.@"export") { + try markSymbol(sym, roots, elf_file); + } + } + } + + const atomRoots = struct { + fn atomRoots(file: File, rs: anytype, ef: *Elf) !void { + for (file.atoms()) |atom_index| { + const atom = file.atom(atom_index) orelse continue; + if (!atom.alive) continue; + + const shdr = atom.inputShdr(ef); + const name = atom.name(ef); + const is_gc_root = blk: { + if (shdr.sh_flags & elf.SHF_GNU_RETAIN != 0) break :blk true; + if (shdr.sh_type == elf.SHT_NOTE) break :blk true; + if (shdr.sh_type == elf.SHT_PREINIT_ARRAY) break :blk true; + if (shdr.sh_type == elf.SHT_INIT_ARRAY) break :blk true; + if (shdr.sh_type == elf.SHT_FINI_ARRAY) break :blk true; + if (mem.startsWith(u8, name, ".ctors")) break :blk true; + if (mem.startsWith(u8, name, ".dtors")) break :blk true; + if (mem.startsWith(u8, name, ".init")) break :blk true; + if (mem.startsWith(u8, name, ".fini")) break :blk true; + if (Elf.isCIdentifier(name)) break :blk true; + break :blk false; + }; + if (is_gc_root and markAtom(atom)) try rs.append(atom); + if (shdr.sh_flags & elf.SHF_ALLOC == 0) atom.visited = true; + } + + // Mark every atom referenced by CIE as alive. + for (file.cies()) |cie| { + for (cie.relocs(ef)) |rel| { + const ref = file.resolveSymbol(rel.r_sym(), ef); + const sym = ef.symbol(ref) orelse continue; + try markSymbol(sym, rs, ef); + } + } + } + }.atomRoots; + + if (elf_file.zigObjectPtr()) |zo| { + try atomRoots(zo.asFile(), roots, elf_file); + } + for (elf_file.objects.items) |index| { + try atomRoots(elf_file.file(index).?, roots, elf_file); + } } fn markSymbol(sym: *Symbol, roots: *std.ArrayList(*Atom), elf_file: *Elf) !void { @@ -92,7 +105,8 @@ fn markLive(atom: *Atom, elf_file: *Elf) void { for (atom.fdes(elf_file)) |fde| { for (fde.relocs(elf_file)[1..]) |rel| { - const target_sym = elf_file.symbol(file.symbol(rel.r_sym())); + const ref = file.resolveSymbol(rel.r_sym(), elf_file); + const target_sym = elf_file.symbol(ref) orelse continue; const target_atom = target_sym.atom(elf_file) orelse continue; target_atom.alive = true; gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index }); @@ -101,7 +115,8 @@ fn markLive(atom: *Atom, elf_file: *Elf) void { } for (atom.relocs(elf_file)) |rel| { - const target_sym = elf_file.symbol(file.symbol(rel.r_sym())); + const ref = file.resolveSymbol(rel.r_sym(), elf_file); + const target_sym = elf_file.symbol(ref) orelse continue; if (target_sym.mergeSubsection(elf_file)) |msub| { msub.alive = true; continue; @@ -120,16 +135,23 @@ fn mark(roots: std.ArrayList(*Atom), elf_file: *Elf) void { } } -fn prune(files: []const File.Index, elf_file: *Elf) void { - for (files) |index| { - const file = elf_file.file(index).?; - for (file.atoms()) |atom_index| { - const atom = file.atom(atom_index) orelse continue; - if (atom.alive and !atom.visited) { - atom.alive = false; - atom.markFdesDead(elf_file); +fn prune(elf_file: *Elf) void { + const pruneInFile = struct { + fn pruneInFile(file: File, ef: *Elf) void { + for (file.atoms()) |atom_index| { + const atom = file.atom(atom_index) orelse continue; + if (atom.alive and !atom.visited) { + atom.alive = false; + atom.markFdesDead(ef); + } } } + }.pruneInFile; + if (elf_file.zigObjectPtr()) |zo| { + pruneInFile(zo.asFile(), elf_file); + } + for (elf_file.objects.items) |index| { + pruneInFile(elf_file.file(index).?, elf_file); } } @@ -181,3 +203,4 @@ const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; const Symbol = @import("Symbol.zig"); +const ZigObject = @import("ZigObject.zig"); diff --git a/src/link/Elf/relocatable.zig b/src/link/Elf/relocatable.zig index ce65b8eb72..92eace8501 100644 --- a/src/link/Elf/relocatable.zig +++ b/src/link/Elf/relocatable.zig @@ -37,7 +37,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co // First, we flush relocatable object file generated with our backends. if (elf_file.zigObjectPtr()) |zig_object| { - zig_object.resolveSymbols(elf_file); + try zig_object.resolveSymbols(elf_file); try elf_file.addCommentString(); try elf_file.finalizeMergeSections(); zig_object.claimUnresolvedObject(elf_file); @@ -383,7 +383,7 @@ fn updateComdatGroupsSizes(elf_file: *Elf) void { shdr.sh_size = cg.size(elf_file); shdr.sh_link = elf_file.symtab_section_index.?; - const sym = elf_file.symbol(cg.symbol(elf_file)); + const sym = cg.symbol(elf_file); shdr.sh_info = sym.outputSymtabIndex(elf_file) orelse elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx(elf_file).?); } diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index c5c2359890..e1ec90139e 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -251,15 +251,16 @@ pub const ZigGotSection = struct { pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index { const comp = elf_file.base.comp; const gpa = comp.gpa; + const zo = elf_file.zigObjectPtr().?; const index = try zig_got.allocateEntry(gpa); const entry = &zig_got.entries.items[index]; entry.* = sym_index; - const symbol = elf_file.symbol(sym_index); + const symbol = zo.symbol(sym_index); symbol.flags.has_zig_got = true; if (elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) { zig_got.flags.needs_rela = true; } - try symbol.addExtra(.{ .zig_got = index }, elf_file); + symbol.addExtra(.{ .zig_got = index }, elf_file); return index; } @@ -282,6 +283,7 @@ pub const ZigGotSection = struct { } pub fn writeOne(zig_got: *ZigGotSection, elf_file: *Elf, index: Index) !void { + const zo = elf_file.zigObjectPtr().?; if (zig_got.flags.dirty) { const needed_size = zig_got.size(elf_file); try elf_file.growAllocSection(elf_file.zig_got_section_index.?, needed_size); @@ -293,7 +295,7 @@ pub const ZigGotSection = struct { const off = zig_got.entryOffset(index, elf_file); const vaddr: u64 = @intCast(zig_got.entryAddress(index, elf_file)); const entry = zig_got.entries.items[index]; - const value = elf_file.symbol(entry).address(.{}, elf_file); + const value = zo.symbol(entry).address(.{}, elf_file); switch (entry_size) { 2 => { var buf: [2]u8 = undefined; @@ -336,8 +338,9 @@ pub const ZigGotSection = struct { } pub fn writeAll(zig_got: ZigGotSection, elf_file: *Elf, writer: anytype) !void { + const zo = elf_file.zigObjectPtr().?; for (zig_got.entries.items) |entry| { - const symbol = elf_file.symbol(entry); + const symbol = zo.symbol(entry); const value = symbol.address(.{ .plt = false }, elf_file); try writeInt(value, elf_file, writer); } @@ -351,9 +354,10 @@ pub const ZigGotSection = struct { const comp = elf_file.base.comp; const gpa = comp.gpa; const cpu_arch = elf_file.getTarget().cpu.arch; + const zo = elf_file.zigObjectPtr().?; try elf_file.rela_dyn.ensureUnusedCapacity(gpa, zig_got.numRela()); for (zig_got.entries.items) |entry| { - const symbol = elf_file.symbol(entry); + const symbol = zo.symbol(entry); const offset = symbol.zigGotAddress(elf_file); elf_file.addRelaDynAssumeCapacity(.{ .offset = @intCast(offset), @@ -364,16 +368,18 @@ pub const ZigGotSection = struct { } pub fn updateSymtabSize(zig_got: *ZigGotSection, elf_file: *Elf) void { + const zo = elf_file.zigObjectPtr().?; zig_got.output_symtab_ctx.nlocals = @as(u32, @intCast(zig_got.entries.items.len)); for (zig_got.entries.items) |entry| { - const name = elf_file.symbol(entry).name(elf_file); + const name = zo.symbol(entry).name(elf_file); zig_got.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$ziggot".len)) + 1; } } pub fn writeSymtab(zig_got: ZigGotSection, elf_file: *Elf) void { + const zo = elf_file.zigObjectPtr().?; for (zig_got.entries.items, zig_got.output_symtab_ctx.ilocal.., 0..) |entry, ilocal, index| { - const symbol = elf_file.symbol(entry); + const symbol = zo.symbol(entry); const symbol_name = symbol.name(elf_file); const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(symbol_name); @@ -409,15 +415,18 @@ pub const ZigGotSection = struct { ) !void { _ = options; _ = unused_fmt_string; + const zig_got = ctx.zig_got; + const elf_file = ctx.elf_file; try writer.writeAll(".zig.got\n"); - for (ctx.zig_got.entries.items, 0..) |entry, index| { - const symbol = ctx.elf_file.symbol(entry); + for (zig_got.entries.items, 0..) |entry, index| { + const zo = elf_file.zigObjectPtr().?; + const symbol = zo.symbol(entry); try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ index, - ctx.zig_got.entryAddress(@intCast(index), ctx.elf_file), + zig_got.entryAddress(@intCast(index), elf_file), entry, - symbol.address(.{}, ctx.elf_file), - symbol.name(ctx.elf_file), + symbol.address(.{}, elf_file), + symbol.name(elf_file), }); } } @@ -446,7 +455,7 @@ pub const GotSection = struct { const Entry = struct { tag: Tag, - symbol_index: Symbol.Index, + ref: Elf.Ref, cell_index: Index, /// Returns how many indexes in the GOT this entry uses. @@ -477,25 +486,25 @@ pub const GotSection = struct { const last = got.entries.items[index - 1]; break :blk last.cell_index + @as(Index, @intCast(last.len())); } else 0; - entry.* = .{ .tag = undefined, .symbol_index = undefined, .cell_index = cell_index }; + entry.* = .{ .tag = undefined, .ref = undefined, .cell_index = cell_index }; return index; } - pub fn addGotSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index { + pub fn addGotSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !Index { const comp = elf_file.base.comp; const gpa = comp.gpa; const index = try got.allocateEntry(gpa); const entry = &got.entries.items[index]; entry.tag = .got; - entry.symbol_index = sym_index; - const symbol = elf_file.symbol(sym_index); + entry.ref = ref; + const symbol = elf_file.symbol(ref).?; symbol.flags.has_got = true; if (symbol.flags.import or symbol.isIFunc(elf_file) or ((elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) and !symbol.isAbs(elf_file))) { got.flags.needs_rela = true; } - try symbol.addExtra(.{ .got = index }, elf_file); + symbol.addExtra(.{ .got = index }, elf_file); return index; } @@ -506,48 +515,48 @@ pub const GotSection = struct { const index = try got.allocateEntry(gpa); const entry = &got.entries.items[index]; entry.tag = .tlsld; - entry.symbol_index = undefined; // unused + entry.ref = .{ .index = 0, .file = 0 }; // unused got.flags.needs_rela = true; got.tlsld_index = index; } - pub fn addTlsGdSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + pub fn addTlsGdSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; const index = try got.allocateEntry(gpa); const entry = &got.entries.items[index]; entry.tag = .tlsgd; - entry.symbol_index = sym_index; - const symbol = elf_file.symbol(sym_index); + entry.ref = ref; + const symbol = elf_file.symbol(ref).?; symbol.flags.has_tlsgd = true; if (symbol.flags.import or elf_file.isEffectivelyDynLib()) got.flags.needs_rela = true; - try symbol.addExtra(.{ .tlsgd = index }, elf_file); + symbol.addExtra(.{ .tlsgd = index }, elf_file); } - pub fn addGotTpSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + pub fn addGotTpSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; const index = try got.allocateEntry(gpa); const entry = &got.entries.items[index]; entry.tag = .gottp; - entry.symbol_index = sym_index; - const symbol = elf_file.symbol(sym_index); + entry.ref = ref; + const symbol = elf_file.symbol(ref).?; symbol.flags.has_gottp = true; if (symbol.flags.import or elf_file.isEffectivelyDynLib()) got.flags.needs_rela = true; - try symbol.addExtra(.{ .gottp = index }, elf_file); + symbol.addExtra(.{ .gottp = index }, elf_file); } - pub fn addTlsDescSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + pub fn addTlsDescSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; const index = try got.allocateEntry(gpa); const entry = &got.entries.items[index]; entry.tag = .tlsdesc; - entry.symbol_index = sym_index; - const symbol = elf_file.symbol(sym_index); + entry.ref = ref; + const symbol = elf_file.symbol(ref).?; symbol.flags.has_tlsdesc = true; got.flags.needs_rela = true; - try symbol.addExtra(.{ .tlsdesc = index }, elf_file); + symbol.addExtra(.{ .tlsdesc = index }, elf_file); } pub fn size(got: GotSection, elf_file: *Elf) usize { @@ -564,10 +573,7 @@ pub const GotSection = struct { const apply_relocs = true; // TODO add user option for this for (got.entries.items) |entry| { - const symbol = switch (entry.tag) { - .tlsld => null, - inline else => elf_file.symbol(entry.symbol_index), - }; + const symbol = elf_file.symbol(entry.ref); switch (entry.tag) { .got => { const value = blk: { @@ -637,11 +643,8 @@ pub const GotSection = struct { try elf_file.rela_dyn.ensureUnusedCapacity(gpa, got.numRela(elf_file)); for (got.entries.items) |entry| { - const symbol = switch (entry.tag) { - .tlsld => null, - inline else => elf_file.symbol(entry.symbol_index), - }; - const extra = if (symbol) |s| s.extra(elf_file).? else null; + const symbol = elf_file.symbol(entry.ref); + const extra = if (symbol) |s| s.extra(elf_file) else null; switch (entry.tag) { .got => { @@ -740,10 +743,7 @@ pub const GotSection = struct { const is_dyn_lib = elf_file.isEffectivelyDynLib(); var num: usize = 0; for (got.entries.items) |entry| { - const symbol = switch (entry.tag) { - .tlsld => null, - inline else => elf_file.symbol(entry.symbol_index), - }; + const symbol = elf_file.symbol(entry.ref); switch (entry.tag) { .got => if (symbol.?.flags.import or symbol.?.isIFunc(elf_file) or ((elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) and @@ -775,24 +775,15 @@ pub const GotSection = struct { pub fn updateSymtabSize(got: *GotSection, elf_file: *Elf) void { got.output_symtab_ctx.nlocals = @as(u32, @intCast(got.entries.items.len)); for (got.entries.items) |entry| { - const symbol_name = switch (entry.tag) { - .tlsld => "", - inline else => elf_file.symbol(entry.symbol_index).name(elf_file), - }; + const symbol_name = if (elf_file.symbol(entry.ref)) |sym| sym.name(elf_file) else ""; got.output_symtab_ctx.strsize += @as(u32, @intCast(symbol_name.len + @tagName(entry.tag).len)) + 1 + 1; } } pub fn writeSymtab(got: GotSection, elf_file: *Elf) void { for (got.entries.items, got.output_symtab_ctx.ilocal..) |entry, ilocal| { - const symbol = switch (entry.tag) { - .tlsld => null, - inline else => elf_file.symbol(entry.symbol_index), - }; - const symbol_name = switch (entry.tag) { - .tlsld => "", - inline else => symbol.?.name(elf_file), - }; + const symbol = elf_file.symbol(entry.ref); + const symbol_name = if (symbol) |s| s.name(elf_file) else ""; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(symbol_name); elf_file.strtab.appendAssumeCapacity('$'); @@ -828,36 +819,38 @@ pub const GotSection = struct { ) !void { _ = options; _ = unused_fmt_string; + const got = ctx.got; + const elf_file = ctx.elf_file; try writer.writeAll("GOT\n"); - for (ctx.got.entries.items) |entry| { - const symbol = ctx.elf_file.symbol(entry.symbol_index); - try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ + for (got.entries.items) |entry| { + const symbol = elf_file.symbol(entry.ref).?; + try writer.print(" {d}@0x{x} => {}@0x{x} ({s})\n", .{ entry.cell_index, - entry.address(ctx.elf_file), - entry.symbol_index, - symbol.address(.{}, ctx.elf_file), - symbol.name(ctx.elf_file), + entry.address(elf_file), + entry.ref, + symbol.address(.{}, elf_file), + symbol.name(elf_file), }); } } }; pub const PltSection = struct { - symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, + symbols: std.ArrayListUnmanaged(Elf.Ref) = .{}, output_symtab_ctx: Elf.SymtabCtx = .{}, pub fn deinit(plt: *PltSection, allocator: Allocator) void { plt.symbols.deinit(allocator); } - pub fn addSymbol(plt: *PltSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + pub fn addSymbol(plt: *PltSection, ref: Elf.Ref, elf_file: *Elf) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; const index = @as(u32, @intCast(plt.symbols.items.len)); - const symbol = elf_file.symbol(sym_index); + const symbol = elf_file.symbol(ref).?; symbol.flags.has_plt = true; - try symbol.addExtra(.{ .plt = index }, elf_file); - try plt.symbols.append(gpa, sym_index); + symbol.addExtra(.{ .plt = index }, elf_file); + try plt.symbols.append(gpa, ref); } pub fn size(plt: PltSection, elf_file: *Elf) usize { @@ -895,10 +888,10 @@ pub const PltSection = struct { const gpa = comp.gpa; const cpu_arch = elf_file.getTarget().cpu.arch; try elf_file.rela_plt.ensureUnusedCapacity(gpa, plt.numRela()); - for (plt.symbols.items) |sym_index| { - const sym = elf_file.symbol(sym_index); + for (plt.symbols.items) |ref| { + const sym = elf_file.symbol(ref).?; assert(sym.flags.import); - const extra = sym.extra(elf_file).?; + const extra = sym.extra(elf_file); const r_offset: u64 = @intCast(sym.gotPltAddress(elf_file)); const r_sym: u64 = extra.dynamic; const r_type = relocation.encode(.jump_slot, cpu_arch); @@ -916,16 +909,16 @@ pub const PltSection = struct { pub fn updateSymtabSize(plt: *PltSection, elf_file: *Elf) void { plt.output_symtab_ctx.nlocals = @as(u32, @intCast(plt.symbols.items.len)); - for (plt.symbols.items) |sym_index| { - const name = elf_file.symbol(sym_index).name(elf_file); + for (plt.symbols.items) |ref| { + const name = elf_file.symbol(ref).?.name(elf_file); plt.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$plt".len)) + 1; } } pub fn writeSymtab(plt: PltSection, elf_file: *Elf) void { const cpu_arch = elf_file.getTarget().cpu.arch; - for (plt.symbols.items, plt.output_symtab_ctx.ilocal..) |sym_index, ilocal| { - const sym = elf_file.symbol(sym_index); + for (plt.symbols.items, plt.output_symtab_ctx.ilocal..) |ref, ilocal| { + const sym = elf_file.symbol(ref).?; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file)); elf_file.strtab.appendSliceAssumeCapacity("$plt"); @@ -958,15 +951,17 @@ pub const PltSection = struct { ) !void { _ = options; _ = unused_fmt_string; + const plt = ctx.plt; + const elf_file = ctx.elf_file; try writer.writeAll("PLT\n"); - for (ctx.plt.symbols.items, 0..) |symbol_index, i| { - const symbol = ctx.elf_file.symbol(symbol_index); - try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ + for (plt.symbols.items, 0..) |ref, i| { + const symbol = elf_file.symbol(ref).?; + try writer.print(" {d}@0x{x} => {}@0x{x} ({s})\n", .{ i, - symbol.pltAddress(ctx.elf_file), - symbol_index, - symbol.address(.{}, ctx.elf_file), - symbol.name(ctx.elf_file), + symbol.pltAddress(elf_file), + ref, + symbol.address(.{}, elf_file), + symbol.name(elf_file), }); } } @@ -988,8 +983,8 @@ pub const PltSection = struct { try writer.writeAll(&preamble); try writer.writeByteNTimes(0xcc, preambleSize(.x86_64) - preamble.len); - for (plt.symbols.items, 0..) |sym_index, i| { - const sym = elf_file.symbol(sym_index); + for (plt.symbols.items, 0..) |ref, i| { + const sym = elf_file.symbol(ref).?; const target_addr = sym.gotPltAddress(elf_file); const source_addr = sym.pltAddress(elf_file); disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 12)) - 4; @@ -1037,8 +1032,8 @@ pub const PltSection = struct { } } - for (plt.symbols.items) |sym_index| { - const sym = elf_file.symbol(sym_index); + for (plt.symbols.items) |ref| { + const sym = elf_file.symbol(ref).?; const target_addr = sym.gotPltAddress(elf_file); const source_addr = sym.pltAddress(elf_file); const pages = try aarch64_util.calcNumberOfPages(source_addr, target_addr); @@ -1075,7 +1070,7 @@ pub const GotPltSection = struct { _ = got_plt; { // [0]: _DYNAMIC - const symbol = elf_file.symbol(elf_file.linkerDefinedPtr().?.dynamic_index.?); + const symbol = elf_file.linkerDefinedPtr().?.dynamicSymbol(elf_file).?; try writer.writeInt(u64, @intCast(symbol.address(.{}, elf_file)), .little); } // [1]: 0x0 @@ -1093,22 +1088,22 @@ pub const GotPltSection = struct { }; pub const PltGotSection = struct { - symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, + symbols: std.ArrayListUnmanaged(Elf.Ref) = .{}, output_symtab_ctx: Elf.SymtabCtx = .{}, pub fn deinit(plt_got: *PltGotSection, allocator: Allocator) void { plt_got.symbols.deinit(allocator); } - pub fn addSymbol(plt_got: *PltGotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + pub fn addSymbol(plt_got: *PltGotSection, ref: Elf.Ref, elf_file: *Elf) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; const index = @as(u32, @intCast(plt_got.symbols.items.len)); - const symbol = elf_file.symbol(sym_index); + const symbol = elf_file.symbol(ref).?; symbol.flags.has_plt = true; symbol.flags.has_got = true; - try symbol.addExtra(.{ .plt_got = index }, elf_file); - try plt_got.symbols.append(gpa, sym_index); + symbol.addExtra(.{ .plt_got = index }, elf_file); + try plt_got.symbols.append(gpa, ref); } pub fn size(plt_got: PltGotSection, elf_file: *Elf) usize { @@ -1134,15 +1129,15 @@ pub const PltGotSection = struct { pub fn updateSymtabSize(plt_got: *PltGotSection, elf_file: *Elf) void { plt_got.output_symtab_ctx.nlocals = @as(u32, @intCast(plt_got.symbols.items.len)); - for (plt_got.symbols.items) |sym_index| { - const name = elf_file.symbol(sym_index).name(elf_file); + for (plt_got.symbols.items) |ref| { + const name = elf_file.symbol(ref).?.name(elf_file); plt_got.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$pltgot".len)) + 1; } } pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf) void { - for (plt_got.symbols.items, plt_got.output_symtab_ctx.ilocal..) |sym_index, ilocal| { - const sym = elf_file.symbol(sym_index); + for (plt_got.symbols.items, plt_got.output_symtab_ctx.ilocal..) |ref, ilocal| { + const sym = elf_file.symbol(ref).?; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file)); elf_file.strtab.appendSliceAssumeCapacity("$pltgot"); @@ -1160,8 +1155,8 @@ pub const PltGotSection = struct { const x86_64 = struct { pub fn write(plt_got: PltGotSection, elf_file: *Elf, writer: anytype) !void { - for (plt_got.symbols.items) |sym_index| { - const sym = elf_file.symbol(sym_index); + for (plt_got.symbols.items) |ref| { + const sym = elf_file.symbol(ref).?; const target_addr = sym.gotAddress(elf_file); const source_addr = sym.pltGotAddress(elf_file); const disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 6)) - 4; @@ -1178,8 +1173,8 @@ pub const PltGotSection = struct { const aarch64 = struct { fn write(plt_got: PltGotSection, elf_file: *Elf, writer: anytype) !void { - for (plt_got.symbols.items) |sym_index| { - const sym = elf_file.symbol(sym_index); + for (plt_got.symbols.items) |ref| { + const sym = elf_file.symbol(ref).?; const target_addr = sym.gotAddress(elf_file); const source_addr = sym.pltGotAddress(elf_file); const pages = try aarch64_util.calcNumberOfPages(source_addr, target_addr); @@ -1204,56 +1199,56 @@ pub const PltGotSection = struct { }; pub const CopyRelSection = struct { - symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, + symbols: std.ArrayListUnmanaged(Elf.Ref) = .{}, pub fn deinit(copy_rel: *CopyRelSection, allocator: Allocator) void { copy_rel.symbols.deinit(allocator); } - pub fn addSymbol(copy_rel: *CopyRelSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + pub fn addSymbol(copy_rel: *CopyRelSection, ref: Elf.Ref, elf_file: *Elf) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; const index = @as(u32, @intCast(copy_rel.symbols.items.len)); - const symbol = elf_file.symbol(sym_index); + const symbol = elf_file.symbol(ref).?; symbol.flags.import = true; symbol.flags.@"export" = true; symbol.flags.has_copy_rel = true; symbol.flags.weak = false; - try symbol.addExtra(.{ .copy_rel = index }, elf_file); - try copy_rel.symbols.append(gpa, sym_index); + symbol.addExtra(.{ .copy_rel = index }, elf_file); + try copy_rel.symbols.append(gpa, ref); const shared_object = symbol.file(elf_file).?.shared_object; if (shared_object.aliases == null) { try shared_object.initSymbolAliases(elf_file); } - const aliases = shared_object.symbolAliases(sym_index, elf_file); + const aliases = shared_object.symbolAliases(ref.index, elf_file); for (aliases) |alias| { - if (alias == sym_index) continue; - const alias_sym = elf_file.symbol(alias); + if (alias == ref.index) continue; + const alias_sym = &shared_object.symbols.items[alias]; alias_sym.flags.import = true; alias_sym.flags.@"export" = true; alias_sym.flags.has_copy_rel = true; alias_sym.flags.needs_copy_rel = true; alias_sym.flags.weak = false; - try elf_file.dynsym.addSymbol(alias, elf_file); + try elf_file.dynsym.addSymbol(.{ .index = alias, .file = shared_object.index }, elf_file); } } pub fn updateSectionSize(copy_rel: CopyRelSection, shndx: u32, elf_file: *Elf) !void { const shdr = &elf_file.shdrs.items[shndx]; - for (copy_rel.symbols.items) |sym_index| { - const symbol = elf_file.symbol(sym_index); + for (copy_rel.symbols.items) |ref| { + const symbol = elf_file.symbol(ref).?; const shared_object = symbol.file(elf_file).?.shared_object; const alignment = try symbol.dsoAlignment(elf_file); symbol.value = @intCast(mem.alignForward(u64, shdr.sh_size, alignment)); shdr.sh_addralign = @max(shdr.sh_addralign, alignment); shdr.sh_size = @as(u64, @intCast(symbol.value)) + symbol.elfSym(elf_file).st_size; - const aliases = shared_object.symbolAliases(sym_index, elf_file); + const aliases = shared_object.symbolAliases(ref.index, elf_file); for (aliases) |alias| { - if (alias == sym_index) continue; - const alias_sym = elf_file.symbol(alias); + if (alias == ref.index) continue; + const alias_sym = &shared_object.symbols.items[alias]; alias_sym.value = symbol.value; } } @@ -1264,10 +1259,10 @@ pub const CopyRelSection = struct { const gpa = comp.gpa; const cpu_arch = elf_file.getTarget().cpu.arch; try elf_file.rela_dyn.ensureUnusedCapacity(gpa, copy_rel.numRela()); - for (copy_rel.symbols.items) |sym_index| { - const sym = elf_file.symbol(sym_index); + for (copy_rel.symbols.items) |ref| { + const sym = elf_file.symbol(ref).?; assert(sym.flags.import and sym.flags.has_copy_rel); - const extra = sym.extra(elf_file).?; + const extra = sym.extra(elf_file); elf_file.addRelaDynAssumeCapacity(.{ .offset = @intCast(sym.address(.{}, elf_file)), .sym = extra.dynamic, @@ -1285,8 +1280,8 @@ pub const DynsymSection = struct { entries: std.ArrayListUnmanaged(Entry) = .{}, pub const Entry = struct { - /// Index of the symbol which gets privilege of getting a dynamic treatment - symbol_index: Symbol.Index, + /// Ref of the symbol which gets privilege of getting a dynamic treatment + ref: Elf.Ref, /// Offset into .dynstrtab off: u32, }; @@ -1295,22 +1290,22 @@ pub const DynsymSection = struct { dynsym.entries.deinit(allocator); } - pub fn addSymbol(dynsym: *DynsymSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + pub fn addSymbol(dynsym: *DynsymSection, ref: Elf.Ref, elf_file: *Elf) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; const index = @as(u32, @intCast(dynsym.entries.items.len + 1)); - const sym = elf_file.symbol(sym_index); + const sym = elf_file.symbol(ref).?; sym.flags.has_dynamic = true; - try sym.addExtra(.{ .dynamic = index }, elf_file); + sym.addExtra(.{ .dynamic = index }, elf_file); const off = try elf_file.insertDynString(sym.name(elf_file)); - try dynsym.entries.append(gpa, .{ .symbol_index = sym_index, .off = off }); + try dynsym.entries.append(gpa, .{ .ref = ref, .off = off }); } pub fn sort(dynsym: *DynsymSection, elf_file: *Elf) void { const Sort = struct { pub fn lessThan(ctx: *Elf, lhs: Entry, rhs: Entry) bool { - const lhs_sym = ctx.symbol(lhs.symbol_index); - const rhs_sym = ctx.symbol(rhs.symbol_index); + const lhs_sym = ctx.symbol(lhs.ref).?; + const rhs_sym = ctx.symbol(rhs.ref).?; if (lhs_sym.flags.@"export" != rhs_sym.flags.@"export") { return rhs_sym.flags.@"export"; @@ -1322,14 +1317,14 @@ pub const DynsymSection = struct { const rhs_hash = GnuHashSection.hasher(rhs_sym.name(ctx)) % nbuckets; if (lhs_hash == rhs_hash) - return lhs_sym.extra(ctx).?.dynamic < rhs_sym.extra(ctx).?.dynamic; + return lhs_sym.extra(ctx).dynamic < rhs_sym.extra(ctx).dynamic; return lhs_hash < rhs_hash; } }; var num_exports: u32 = 0; for (dynsym.entries.items) |entry| { - const sym = elf_file.symbol(entry.symbol_index); + const sym = elf_file.symbol(entry.ref).?; if (sym.flags.@"export") num_exports += 1; } @@ -1338,8 +1333,8 @@ pub const DynsymSection = struct { std.mem.sort(Entry, dynsym.entries.items, elf_file, Sort.lessThan); for (dynsym.entries.items, 1..) |entry, index| { - const sym = elf_file.symbol(entry.symbol_index); - var extra = sym.extra(elf_file).?; + const sym = elf_file.symbol(entry.ref).?; + var extra = sym.extra(elf_file); extra.dynamic = @as(u32, @intCast(index)); sym.setExtra(extra, elf_file); } @@ -1356,7 +1351,7 @@ pub const DynsymSection = struct { pub fn write(dynsym: DynsymSection, elf_file: *Elf, writer: anytype) !void { try writer.writeStruct(Elf.null_sym); for (dynsym.entries.items) |entry| { - const sym = elf_file.symbol(entry.symbol_index); + const sym = elf_file.symbol(entry.ref).?; var out_sym: elf.Elf64_Sym = Elf.null_sym; sym.setOutputSym(elf_file, &out_sym); out_sym.st_name = entry.off; @@ -1429,7 +1424,7 @@ pub const GnuHashSection = struct { fn getExports(elf_file: *Elf) []const DynsymSection.Entry { const start = for (elf_file.dynsym.entries.items, 0..) |entry, i| { - const sym = elf_file.symbol(entry.symbol_index); + const sym = elf_file.symbol(entry.ref).?; if (sym.flags.@"export") break i; } else elf_file.dynsym.entries.items.len; return elf_file.dynsym.entries.items[start..]; @@ -1477,7 +1472,7 @@ pub const GnuHashSection = struct { @memset(bloom, 0); for (exports, 0..) |entry, i| { - const sym = elf_file.symbol(entry.symbol_index); + const sym = elf_file.symbol(entry.ref).?; const h = hasher(sym.name(elf_file)); hashes[i] = h; indices[i] = h % hash.num_buckets; @@ -1574,7 +1569,7 @@ pub const VerneedSection = struct { try verneed.ensureTotalCapacity(dynsyms.len); for (dynsyms, 1..) |entry, i| { - const symbol = elf_file.symbol(entry.symbol_index); + const symbol = elf_file.symbol(entry.ref).?; if (symbol.flags.import and symbol.version_index & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) { const shared_object = symbol.file(elf_file).?.shared_object; verneed.appendAssumeCapacity(.{ @@ -1677,11 +1672,11 @@ pub const ComdatGroupSection = struct { return cg_file.object.comdatGroup(cgs.cg_ref.index); } - pub fn symbol(cgs: ComdatGroupSection, elf_file: *Elf) Symbol.Index { + pub fn symbol(cgs: ComdatGroupSection, elf_file: *Elf) *Symbol { const cg = cgs.comdatGroup(elf_file); const object = cg.file(elf_file).object; const shdr = object.shdrs.items[cg.shndx]; - return object.symbols.items[shdr.sh_info]; + return &object.symbols.items[shdr.sh_info]; } pub fn size(cgs: ComdatGroupSection, elf_file: *Elf) usize { diff --git a/src/link/Elf/thunks.zig b/src/link/Elf/thunks.zig index 8353eea98a..c62c81bd38 100644 --- a/src/link/Elf/thunks.zig +++ b/src/link/Elf/thunks.zig @@ -43,11 +43,7 @@ pub fn createThunks(shndx: u32, elf_file: *Elf) !void { else => @panic("unsupported arch"), }; if (is_reachable) continue; - const target = switch (file) { - .zig_object => |x| x.symbol(rel.r_sym()), - .object => |x| x.symbols.items[rel.r_sym()], - else => unreachable, - }; + const target = file.resolveSymbol(rel.r_sym(), elf_file); try thunk.symbols.put(gpa, target, {}); } atom.addExtra(.{ .thunk = thunk_index }, elf_file); @@ -80,7 +76,7 @@ fn maxAllowedDistance(cpu_arch: std.Target.Cpu.Arch) u32 { pub const Thunk = struct { value: i64 = 0, output_section_index: u32 = 0, - symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, + symbols: std.AutoArrayHashMapUnmanaged(Elf.Ref, void) = .{}, output_symtab_ctx: Elf.SymtabCtx = .{}, pub fn deinit(thunk: *Thunk, allocator: Allocator) void { @@ -97,9 +93,9 @@ pub const Thunk = struct { return @as(i64, @intCast(shdr.sh_addr)) + thunk.value; } - pub fn targetAddress(thunk: Thunk, sym_index: Symbol.Index, elf_file: *Elf) i64 { + pub fn targetAddress(thunk: Thunk, ref: Elf.Ref, elf_file: *Elf) i64 { const cpu_arch = elf_file.getTarget().cpu.arch; - return thunk.address(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(sym_index).? * trampolineSize(cpu_arch))); + return thunk.address(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(ref).? * trampolineSize(cpu_arch))); } pub fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void { @@ -112,16 +108,16 @@ pub const Thunk = struct { pub fn calcSymtabSize(thunk: *Thunk, elf_file: *Elf) void { thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len)); - for (thunk.symbols.keys()) |sym_index| { - const sym = elf_file.symbol(sym_index); + for (thunk.symbols.keys()) |ref| { + const sym = elf_file.symbol(ref).?; thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.name(elf_file).len + "$thunk".len + 1)); } } pub fn writeSymtab(thunk: Thunk, elf_file: *Elf) void { const cpu_arch = elf_file.getTarget().cpu.arch; - for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |sym_index, ilocal| { - const sym = elf_file.symbol(sym_index); + for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| { + const sym = elf_file.symbol(ref).?; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file)); elf_file.strtab.appendSliceAssumeCapacity("$thunk"); @@ -131,7 +127,7 @@ pub const Thunk = struct { .st_info = elf.STT_FUNC, .st_other = 0, .st_shndx = @intCast(thunk.output_section_index), - .st_value = @intCast(thunk.targetAddress(sym_index, elf_file)), + .st_value = @intCast(thunk.targetAddress(ref, elf_file)), .st_size = trampolineSize(cpu_arch), }; } @@ -181,9 +177,9 @@ pub const Thunk = struct { const thunk = ctx.thunk; const elf_file = ctx.elf_file; try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) }); - for (thunk.symbols.keys()) |index| { - const sym = elf_file.symbol(index); - try writer.print(" %{d} : {s} : @{x}\n", .{ index, sym.name(elf_file), sym.value }); + for (thunk.symbols.keys()) |ref| { + const sym = elf_file.symbol(ref).?; + try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value }); } } @@ -195,12 +191,8 @@ const aarch64 = struct { const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type()); if (r_type != .CALL26 and r_type != .JUMP26) return true; const file = atom.file(elf_file).?; - const target_index = switch (file) { - .zig_object => |x| x.symbol(rel.r_sym()), - .object => |x| x.symbols.items[rel.r_sym()], - else => unreachable, - }; - const target = elf_file.symbol(target_index); + const target_ref = file.resolveSymbol(rel.r_sym(), elf_file); + const target = elf_file.symbol(target_ref).?; if (target.flags.has_plt) return false; if (atom.output_section_index != target.output_section_index) return false; const target_atom = target.atom(elf_file).?; @@ -212,8 +204,8 @@ const aarch64 = struct { } fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void { - for (thunk.symbols.keys(), 0..) |sym_index, i| { - const sym = elf_file.symbol(sym_index); + for (thunk.symbols.keys(), 0..) |ref, i| { + const sym = elf_file.symbol(ref).?; const saddr = thunk.address(elf_file) + @as(i64, @intCast(i * trampoline_size)); const taddr = sym.address(.{}, elf_file); const pages = try util.calcNumberOfPages(saddr, taddr);