commit 27be3b069a8f577184ca8d2c41f1001ca46af646 (tree)
parent 1deaf5a085308b7a48eadd7f4f22de6096681e94
Author: mlugg <mlugg@noreply.codeberg.org>
Date: Mon, 18 May 2026 08:22:47 +0200
Merge pull request 'Elf2: various enhancements' (#35332) from elf2 into master
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/35332
Diffstat:
19 files changed, 2489 insertions(+), 1491 deletions(-)
diff --git a/lib/std/elf.zig b/lib/std/elf.zig
@@ -1043,7 +1043,7 @@ pub const Elf32 = struct {
pub const Addr = u32;
pub const Off = u32;
pub const Ehdr = extern struct {
- ident: [EI.NIDENT]u8,
+ ident: Ident,
type: ET,
machine: EM,
version: Word,
@@ -1133,7 +1133,7 @@ pub const Elf64 = struct {
pub const Addr = u64;
pub const Off = u64;
pub const Ehdr = extern struct {
- ident: [EI.NIDENT]u8,
+ ident: Ident,
type: ET,
machine: EM,
version: Word,
@@ -1611,6 +1611,20 @@ pub const Sym = switch (@sizeOf(usize)) {
/// Deprecated, use `std.elf.ElfN.Addr`
pub const Addr = ElfN.Addr;
+pub const Ident = extern struct {
+ magic: [MAGIC.len]u8 = MAGIC.*,
+ class: CLASS,
+ data: DATA,
+ version: u8,
+ osabi: OSABI,
+ abiversion: u8,
+ pad: [7]u8 = @splat(0),
+
+ comptime {
+ assert(@sizeOf(Ident) == EI.NIDENT);
+ }
+};
+
/// Deprecated, use `@intFromEnum(std.elf.CLASS.NONE)`
pub const ELFCLASSNONE = @intFromEnum(CLASS.NONE);
/// Deprecated, use `@intFromEnum(std.elf.CLASS.@"32")`
diff --git a/lib/std/posix.zig b/lib/std/posix.zig
@@ -834,7 +834,7 @@ pub fn dl_iterate_phdr(
while (it.next()) |entry| {
const phdrs: []elf.ElfN.Phdr = if (entry.addr != 0) phdrs: {
const ehdr: *elf.ElfN.Ehdr = @ptrFromInt(entry.addr);
- assert(mem.eql(u8, ehdr.ident[0..4], elf.MAGIC));
+ assert(mem.eql(u8, &ehdr.ident.magic, elf.MAGIC));
const phdrs: [*]elf.ElfN.Phdr = @ptrFromInt(entry.addr + ehdr.phoff);
break :phdrs phdrs[0..ehdr.phnum];
} else getSelfPhdrs();
diff --git a/src/codegen.zig b/src/codegen.zig
@@ -178,7 +178,7 @@ pub fn emitFunction(
pt: Zcu.PerThread,
src_loc: Zcu.LazySrcLoc,
func_index: InternPool.Index,
- atom_index: u32,
+ atom_id: link.File.AtomId,
any_mir: *const AnyMir,
w: *std.Io.Writer,
debug_output: link.File.DebugInfoOutput,
@@ -195,7 +195,7 @@ pub fn emitFunction(
=> |backend| {
dev.check(devFeatureForBackend(backend));
const mir = &@field(any_mir, AnyMir.tag(backend));
- return mir.emit(lf, pt, src_loc, func_index, atom_index, w, debug_output);
+ return mir.emit(lf, pt, src_loc, func_index, atom_id, w, debug_output);
},
}
}
@@ -205,7 +205,7 @@ pub fn generateLazyFunction(
pt: Zcu.PerThread,
src_loc: Zcu.LazySrcLoc,
lazy_sym: link.File.LazySymbol,
- atom_index: u32,
+ atom_id: link.File.AtomId,
w: *std.Io.Writer,
debug_output: link.File.DebugInfoOutput,
) (CodeGenError || std.Io.Writer.Error)!void {
@@ -218,7 +218,7 @@ pub fn generateLazyFunction(
else => unreachable,
inline .stage2_riscv64, .stage2_x86_64 => |backend| {
dev.check(devFeatureForBackend(backend));
- return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, atom_index, w, debug_output);
+ return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, atom_id, w, debug_output);
},
}
}
@@ -852,20 +852,7 @@ fn lowerNavRef(
}
}
-/// Helper struct to denote that the value is in memory but requires a linker relocation fixup:
-/// * got - the value is referenced indirectly via GOT entry index (the linker emits a got-type reloc)
-/// * direct - the value is referenced directly via symbol index index (the linker emits a displacement reloc)
-/// * import - the value is referenced indirectly via import entry index (the linker emits an import-type reloc)
-pub const LinkerLoad = struct {
- type: enum {
- got,
- direct,
- import,
- },
- sym_index: u32,
-};
-
-pub const SymbolResult = union(enum) { sym_index: u32, fail: *ErrorMsg };
+pub const SymbolResult = union(enum) { sym_index: link.File.SymbolId, fail: *ErrorMsg };
pub fn genNavRef(
lf: *link.File,
@@ -890,7 +877,7 @@ pub fn genNavRef(
.internal => {
const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index);
if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true;
- return .{ .sym_index = sym_index };
+ return .{ .sym_index = @enumFromInt(sym_index) };
},
.strong, .weak => {
const sym_index = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), lib_name.toSlice(ip));
@@ -901,12 +888,12 @@ pub fn genNavRef(
.link_once => unreachable,
}
if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true;
- return .{ .sym_index = sym_index };
+ return .{ .sym_index = @enumFromInt(sym_index) };
},
.link_once => unreachable,
}
} else if (lf.cast(.elf2)) |elf| {
- return .{ .sym_index = @intFromEnum(elf.navSymbol(zcu, nav_index) catch |err| switch (err) {
+ return .{ .sym_index = elf.navSymbol(nav_index) catch |err| switch (err) {
error.OutOfMemory => |e| return e,
else => |e| return .{ .fail = try ErrorMsg.create(
zcu.gpa,
@@ -914,14 +901,14 @@ pub fn genNavRef(
"linker failed to create a nav: {t}",
.{e},
) },
- }) };
+ } };
} else if (lf.cast(.macho)) |macho_file| {
const zo = macho_file.getZigObject().?;
switch (linkage) {
.internal => {
const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index);
if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true;
- return .{ .sym_index = sym_index };
+ return .{ .sym_index = @enumFromInt(sym_index) };
},
.strong, .weak => {
const sym_index = try macho_file.getGlobalSymbol(nav.name.toSlice(ip), lib_name.toSlice(ip));
@@ -932,12 +919,12 @@ pub fn genNavRef(
.link_once => unreachable,
}
if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true;
- return .{ .sym_index = sym_index };
+ return .{ .sym_index = @enumFromInt(sym_index) };
},
.link_once => unreachable,
}
} else if (lf.cast(.coff2)) |coff| {
- return .{ .sym_index = @intFromEnum(try coff.navSymbol(zcu, nav_index)) };
+ return .{ .sym_index = @enumFromInt(@intFromEnum(try coff.navSymbol(zcu, nav_index))) };
} else {
const msg = try ErrorMsg.create(zcu.gpa, src_loc, "TODO genNavRef for target {}", .{target});
return .{ .fail = msg };
@@ -957,22 +944,22 @@ pub const GenResult = union(enum) {
immediate: u64,
/// Decl with address deferred until the linker allocates everything in virtual memory.
/// Payload is a symbol index.
- load_direct: u32,
+ load_direct: link.File.SymbolId,
/// Decl with address deferred until the linker allocates everything in virtual memory.
/// Payload is a symbol index.
- lea_direct: u32,
+ lea_direct: link.File.SymbolId,
/// Decl referenced via GOT with address deferred until the linker allocates
/// everything in virtual memory.
/// Payload is a symbol index.
- load_got: u32,
+ load_got: link.File.SymbolId,
/// Direct by-address reference to memory location.
memory: u64,
/// Reference to memory location but deferred until linker allocated the Decl in memory.
/// Traditionally, this corresponds to emitting a relocation in a relocatable object file.
- load_symbol: u32,
+ load_symbol: link.File.SymbolId,
/// Reference to memory location but deferred until linker allocated the Decl in memory.
/// Traditionally, this corresponds to emitting a relocation in a relocatable object file.
- lea_symbol: u32,
+ lea_symbol: link.File.SymbolId,
};
};
diff --git a/src/codegen/aarch64/Mir.zig b/src/codegen/aarch64/Mir.zig
@@ -56,7 +56,7 @@ pub fn emit(
pt: Zcu.PerThread,
src_loc: Zcu.LazySrcLoc,
func_index: InternPool.Index,
- atom_index: u32,
+ atom_index: link.File.AtomId,
w: *std.Io.Writer,
debug_output: link.File.DebugInfoOutput,
) !void {
@@ -132,11 +132,11 @@ pub fn emit(
zcu,
atom_index,
if (lf.cast(.elf)) |ef|
- ef.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(ef, pt, lazy_reloc.symbol) catch |err|
- return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)})
+ @enumFromInt(ef.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(ef, pt, lazy_reloc.symbol) catch |err|
+ return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)}))
else if (lf.cast(.macho)) |mf|
- mf.getZigObject().?.getOrCreateMetadataForLazySymbol(mf, pt, lazy_reloc.symbol) catch |err|
- return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)})
+ @enumFromInt(mf.getZigObject().?.getOrCreateMetadataForLazySymbol(mf, pt, lazy_reloc.symbol) catch |err|
+ return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)}))
else
return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {t}", .{lf.tag}),
mir.body[lazy_reloc.reloc.label],
@@ -149,9 +149,9 @@ pub fn emit(
zcu,
atom_index,
if (lf.cast(.elf)) |ef|
- try ef.getGlobalSymbol(std.mem.span(global_reloc.name), null)
+ @enumFromInt(try ef.getGlobalSymbol(std.mem.span(global_reloc.name), null))
else if (lf.cast(.macho)) |mf|
- try mf.getGlobalSymbol(std.mem.span(global_reloc.name), null)
+ @enumFromInt(try mf.getGlobalSymbol(std.mem.span(global_reloc.name), null))
else
return zcu.codegenFail(func.owner_nav, "external symbols unimplemented for {t}", .{lf.tag}),
mir.body[global_reloc.reloc.label],
@@ -187,8 +187,8 @@ fn emitInstruction(w: *std.Io.Writer, instruction: Instruction) !void {
fn emitReloc(
lf: *link.File,
zcu: *Zcu,
- atom_index: u32,
- sym_index: u32,
+ atom_index: link.File.AtomId,
+ sym_index: link.File.SymbolId,
instruction: Instruction,
offset: u32,
addend: u64,
@@ -199,7 +199,7 @@ fn emitReloc(
else => unreachable,
.data_processing_immediate => |decoded| if (lf.cast(.elf)) |ef| {
const zo = ef.zigObjectPtr().?;
- const atom = zo.symbol(atom_index).atom(ef).?;
+ const atom = zo.symbol(@intFromEnum(atom_index)).atom(ef).?;
const r_type: std.elf.R_AARCH64 = switch (decoded.decode()) {
else => unreachable,
.pc_relative_addressing => |pc_relative_addressing| switch (pc_relative_addressing.group.op) {
@@ -222,12 +222,12 @@ fn emitReloc(
};
try atom.addReloc(gpa, .{
.r_offset = offset,
- .r_info = @as(u64, sym_index) << 32 | @intFromEnum(r_type),
+ .r_info = @as(u64, @intFromEnum(sym_index)) << 32 | @intFromEnum(r_type),
.r_addend = @bitCast(addend),
}, zo);
} else if (lf.cast(.macho)) |mf| {
const zo = mf.getZigObject().?;
- const atom = zo.symbols.items[atom_index].getAtom(mf).?;
+ const atom = zo.symbols.items[@intFromEnum(atom_index)].getAtom(mf).?;
switch (decoded.decode()) {
else => unreachable,
.pc_relative_addressing => |pc_relative_addressing| switch (pc_relative_addressing.group.op) {
@@ -235,7 +235,7 @@ fn emitReloc(
.adrp => try atom.addReloc(mf, .{
.tag = .@"extern",
.offset = offset,
- .target = sym_index,
+ .target = @intFromEnum(sym_index),
.addend = @bitCast(addend),
.type = switch (kind) {
.direct => .page,
@@ -245,7 +245,7 @@ fn emitReloc(
.pcrel = true,
.has_subtractor = false,
.length = 2,
- .symbolnum = @intCast(sym_index),
+ .symbolnum = @intCast(@intFromEnum(sym_index)),
},
}),
},
@@ -253,7 +253,7 @@ fn emitReloc(
.add => try atom.addReloc(mf, .{
.tag = .@"extern",
.offset = offset,
- .target = sym_index,
+ .target = @intFromEnum(sym_index),
.addend = @bitCast(addend),
.type = switch (kind) {
.direct => .pageoff,
@@ -263,7 +263,7 @@ fn emitReloc(
.pcrel = false,
.has_subtractor = false,
.length = 2,
- .symbolnum = @intCast(sym_index),
+ .symbolnum = @intCast(@intFromEnum(sym_index)),
},
}),
.sub => unreachable,
@@ -272,36 +272,36 @@ fn emitReloc(
},
.branch_exception_generating_system => |decoded| if (lf.cast(.elf)) |ef| {
const zo = ef.zigObjectPtr().?;
- const atom = zo.symbol(atom_index).atom(ef).?;
+ const atom = zo.symbol(@intFromEnum(atom_index)).atom(ef).?;
const r_type: std.elf.R_AARCH64 = switch (decoded.decode().unconditional_branch_immediate.group.op) {
.b => .JUMP26,
.bl => .CALL26,
};
try atom.addReloc(gpa, .{
.r_offset = offset,
- .r_info = @as(u64, sym_index) << 32 | @intFromEnum(r_type),
+ .r_info = @as(u64, @intFromEnum(sym_index)) << 32 | @intFromEnum(r_type),
.r_addend = @bitCast(addend),
}, zo);
} else if (lf.cast(.macho)) |mf| {
const zo = mf.getZigObject().?;
- const atom = zo.symbols.items[atom_index].getAtom(mf).?;
+ const atom = zo.symbols.items[@intFromEnum(atom_index)].getAtom(mf).?;
try atom.addReloc(mf, .{
.tag = .@"extern",
.offset = offset,
- .target = sym_index,
+ .target = @intFromEnum(sym_index),
.addend = @bitCast(addend),
.type = .branch,
.meta = .{
.pcrel = true,
.has_subtractor = false,
.length = 2,
- .symbolnum = @intCast(sym_index),
+ .symbolnum = @intCast(@intFromEnum(sym_index)),
},
});
},
.load_store => |decoded| if (lf.cast(.elf)) |ef| {
const zo = ef.zigObjectPtr().?;
- const atom = zo.symbol(atom_index).atom(ef).?;
+ const atom = zo.symbol(@intFromEnum(atom_index)).atom(ef).?;
const r_type: std.elf.R_AARCH64 = switch (decoded.decode().register_unsigned_immediate.decode()) {
.integer => |integer| switch (integer.decode()) {
.unallocated, .prfm => unreachable,
@@ -342,16 +342,16 @@ fn emitReloc(
};
try atom.addReloc(gpa, .{
.r_offset = offset,
- .r_info = @as(u64, sym_index) << 32 | @intFromEnum(r_type),
+ .r_info = @as(u64, @intFromEnum(sym_index)) << 32 | @intFromEnum(r_type),
.r_addend = @bitCast(addend),
}, zo);
} else if (lf.cast(.macho)) |mf| {
const zo = mf.getZigObject().?;
- const atom = zo.symbols.items[atom_index].getAtom(mf).?;
+ const atom = zo.symbols.items[@intFromEnum(atom_index)].getAtom(mf).?;
try atom.addReloc(mf, .{
.tag = .@"extern",
.offset = offset,
- .target = sym_index,
+ .target = @intFromEnum(sym_index),
.addend = @bitCast(addend),
.type = switch (kind) {
.direct => .pageoff,
@@ -361,7 +361,7 @@ fn emitReloc(
.pcrel = false,
.has_subtractor = false,
.length = 2,
- .symbolnum = @intCast(sym_index),
+ .symbolnum = @intCast(@intFromEnum(sym_index)),
},
});
},
diff --git a/src/codegen/riscv64/CodeGen.zig b/src/codegen/riscv64/CodeGen.zig
@@ -130,7 +130,7 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
-const SymbolOffset = struct { sym: u32, off: i32 = 0 };
+const SymbolOffset = struct { sym: link.File.SymbolId, off: i32 = 0 };
const RegisterOffset = struct { reg: Register, off: i32 = 0 };
pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 };
@@ -166,7 +166,7 @@ const MCValue = union(enum) {
dead: u32,
/// The value is undefined. Contains a symbol index to an undefined constant. Null means
/// set the undefined value via immediate instead of a load.
- undef: ?u32,
+ undef: ?link.File.SymbolId,
/// A pointer-sized integer that fits in a register.
/// If the type is a pointer, this is the pointer address in virtual address space.
immediate: u64,
@@ -859,7 +859,7 @@ pub fn generateLazy(
pt: Zcu.PerThread,
src_loc: Zcu.LazySrcLoc,
lazy_sym: link.File.LazySymbol,
- atom_index: u32,
+ atom_index: link.File.AtomId,
w: *std.Io.Writer,
debug_output: link.File.DebugInfoOutput,
) (CodeGenError || std.Io.Writer.Error)!void {
@@ -1305,7 +1305,7 @@ fn genLazy(func: *Func, lazy_sym: link.File.LazySymbol) InnerError!void {
}) catch |err|
return func.fail("{s} creating lazy symbol", .{@errorName(err)});
- try func.genSetReg(Type.u64, data_reg, .{ .lea_symbol = .{ .sym = sym_index } });
+ try func.genSetReg(Type.u64, data_reg, .{ .lea_symbol = .{ .sym = @enumFromInt(sym_index) } });
const cmp_reg, const cmp_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(cmp_lock);
@@ -3595,8 +3595,8 @@ fn airRuntimeNavPtr(func: *Func, inst: Air.Inst.Index) !void {
.tag = .pseudo_load_tlv,
.data = .{ .reloc = .{
.register = dest_mcv.getReg().?,
- .atom_index = try func.owner.getSymbolIndex(func),
- .sym_index = tlv_sym_index,
+ .atom_index = @enumFromInt(try func.owner.getSymbolIndex(func)),
+ .sym_index = @enumFromInt(tlv_sym_index),
} },
});
} else {
@@ -3606,8 +3606,8 @@ fn airRuntimeNavPtr(func: *Func, inst: Air.Inst.Index) !void {
.tag = .pseudo_load_tlv,
.data = .{ .reloc = .{
.register = tmp_reg,
- .atom_index = try func.owner.getSymbolIndex(func),
- .sym_index = tlv_sym_index,
+ .atom_index = @enumFromInt(try func.owner.getSymbolIndex(func)),
+ .sym_index = @enumFromInt(tlv_sym_index),
} },
});
try func.genCopy(ptr_ty, dest_mcv, .{ .register = tmp_reg });
@@ -4958,7 +4958,7 @@ fn genCall(
.func => |func_val| {
if (func.bin_file.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?;
- const sym_index = try zo.getOrCreateMetadataForNav(zcu, func_val.owner_nav);
+ const sym_index: link.File.SymbolId = @enumFromInt(try zo.getOrCreateMetadataForNav(zcu, func_val.owner_nav));
if (func.mod.pic) {
return func.fail("TODO: genCall pic", .{});
@@ -4978,7 +4978,7 @@ fn genCall(
.@"extern" => |@"extern"| {
const lib_name = @"extern".lib_name.toSlice(&zcu.intern_pool);
const name = @"extern".name.toSlice(&zcu.intern_pool);
- const atom_index = try func.owner.getSymbolIndex(func);
+ const atom_index: link.File.AtomId = @enumFromInt(try func.owner.getSymbolIndex(func));
const elf_file = func.bin_file.cast(.elf).?;
_ = try func.addInst(.{
@@ -4986,7 +4986,7 @@ fn genCall(
.data = .{ .reloc = .{
.register = .ra,
.atom_index = atom_index,
- .sym_index = try elf_file.getGlobalSymbol(name, lib_name),
+ .sym_index = @enumFromInt(try elf_file.getGlobalSymbol(name, lib_name)),
} },
});
},
@@ -6405,7 +6405,7 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void {
.tag = .pseudo_extern_fn_reloc,
.data = .{ .reloc = .{
.register = random_link_reg,
- .atom_index = try func.owner.getSymbolIndex(func),
+ .atom_index = @enumFromInt(try func.owner.getSymbolIndex(func)),
.sym_index = sym_offset.sym,
} },
});
@@ -7036,7 +7036,7 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
},
.lea_symbol => |sym_off| {
assert(sym_off.off == 0);
- const atom_index = try func.owner.getSymbolIndex(func);
+ const atom_index: link.File.AtomId = @enumFromInt(try func.owner.getSymbolIndex(func));
_ = try func.addInst(.{
.tag = .pseudo_load_symbol,
diff --git a/src/codegen/riscv64/Emit.zig b/src/codegen/riscv64/Emit.zig
@@ -47,8 +47,8 @@ pub fn emitMir(emit: *Emit) Error!void {
const elf_file = emit.bin_file.cast(.elf).?;
const zo = elf_file.zigObjectPtr().?;
- const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
- const sym = zo.symbol(symbol.sym_index);
+ const atom_ptr = zo.symbol(@intFromEnum(symbol.atom_index)).atom(elf_file).?;
+ const sym = zo.symbol(@intFromEnum(symbol.sym_index));
if (emit.lower.pic) {
return emit.fail("know when to emit GOT relocation for symbol '{s}'", .{sym.name(elf_file)});
@@ -59,13 +59,13 @@ pub fn emitMir(emit: *Emit) Error!void {
try atom_ptr.addReloc(gpa, .{
.r_offset = start_offset,
- .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type,
+ .r_info = (@as(u64, @intFromEnum(symbol.sym_index)) << 32) | hi_r_type,
.r_addend = 0,
}, zo);
try atom_ptr.addReloc(gpa, .{
.r_offset = start_offset + 4,
- .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type,
+ .r_info = (@as(u64, @intFromEnum(symbol.sym_index)) << 32) | lo_r_type,
.r_addend = 0,
}, zo);
},
@@ -73,38 +73,38 @@ pub fn emitMir(emit: *Emit) Error!void {
const elf_file = emit.bin_file.cast(.elf).?;
const zo = elf_file.zigObjectPtr().?;
- const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
+ const atom_ptr = zo.symbol(@intFromEnum(symbol.atom_index)).atom(elf_file).?;
const R_RISCV = std.elf.R_RISCV;
try atom_ptr.addReloc(gpa, .{
.r_offset = start_offset,
- .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_HI20),
+ .r_info = (@as(u64, @intFromEnum(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_HI20),
.r_addend = 0,
}, zo);
try atom_ptr.addReloc(gpa, .{
.r_offset = start_offset + 4,
- .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_ADD),
+ .r_info = (@as(u64, @intFromEnum(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_ADD),
.r_addend = 0,
}, zo);
try atom_ptr.addReloc(gpa, .{
.r_offset = start_offset + 8,
- .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_LO12_I),
+ .r_info = (@as(u64, @intFromEnum(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_LO12_I),
.r_addend = 0,
}, zo);
},
.call_extern_fn_reloc => |symbol| {
const elf_file = emit.bin_file.cast(.elf).?;
const zo = elf_file.zigObjectPtr().?;
- const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
+ const atom_ptr = zo.symbol(@intFromEnum(symbol.atom_index)).atom(elf_file).?;
const r_type: u32 = @intFromEnum(std.elf.R_RISCV.CALL_PLT);
try atom_ptr.addReloc(gpa, .{
.r_offset = start_offset,
- .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type,
+ .r_info = (@as(u64, @intFromEnum(symbol.sym_index)) << 32) | r_type,
.r_addend = 0,
}, zo);
},
diff --git a/src/codegen/riscv64/Mir.zig b/src/codegen/riscv64/Mir.zig
@@ -67,8 +67,8 @@ pub const Inst = struct {
},
reloc: struct {
register: Register,
- atom_index: u32,
- sym_index: u32,
+ atom_index: link.File.AtomId,
+ sym_index: link.File.SymbolId,
},
fence: struct {
pred: Barrier,
@@ -109,7 +109,7 @@ pub fn emit(
pt: Zcu.PerThread,
src_loc: Zcu.LazySrcLoc,
func_index: InternPool.Index,
- atom_index: u32,
+ atom_index: link.File.AtomId,
w: *std.Io.Writer,
debug_output: link.File.DebugInfoOutput,
) (codegen.CodeGenError || std.Io.Writer.Error)!void {
@@ -183,8 +183,8 @@ pub const FcvtOp = enum(u5) {
pub const LoadSymbolPayload = struct {
register: u32,
- atom_index: u32,
- sym_index: u32,
+ atom_index: link.File.AtomId,
+ sym_index: link.File.SymbolId,
};
/// Used in conjunction with payload to transfer a list of used registers in a compact manner.
diff --git a/src/codegen/riscv64/bits.zig b/src/codegen/riscv64/bits.zig
@@ -4,6 +4,7 @@ const testing = std.testing;
const Target = std.Target;
const Zcu = @import("../../Zcu.zig");
+const link = @import("../../link.zig");
const Mir = @import("Mir.zig");
const abi = @import("abi.zig");
@@ -260,9 +261,9 @@ pub const FrameIndex = enum(u32) {
/// A linker symbol not yet allocated in VM.
pub const Symbol = struct {
/// Index of the containing atom.
- atom_index: u32,
+ atom_index: link.File.AtomId,
/// Index into the linker's symbol table.
- sym_index: u32,
+ sym_index: link.File.SymbolId,
};
pub const VType = packed struct(u8) {
diff --git a/src/codegen/sparc64/Mir.zig b/src/codegen/sparc64/Mir.zig
@@ -380,7 +380,7 @@ pub fn emit(
pt: Zcu.PerThread,
src_loc: Zcu.LazySrcLoc,
func_index: InternPool.Index,
- atom_index: u32,
+ atom_index: link.File.AtomId,
w: *std.Io.Writer,
debug_output: link.File.DebugInfoOutput,
) (codegen.CodeGenError || std.Io.Writer.Error)!void {
diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig
@@ -1029,7 +1029,7 @@ pub fn generateLazy(
pt: Zcu.PerThread,
src_loc: Zcu.LazySrcLoc,
lazy_sym: link.File.LazySymbol,
- atom_index: u32,
+ atom_id: link.File.AtomId,
w: *std.Io.Writer,
debug_output: link.File.DebugInfoOutput,
) codegen.CodeGenError!void {
@@ -1073,7 +1073,7 @@ pub fn generateLazy(
else => |e| return e,
};
- try function.getTmpMir().emitLazy(bin_file, pt, src_loc, lazy_sym, atom_index, w, debug_output);
+ try function.getTmpMir().emitLazy(bin_file, pt, src_loc, lazy_sym, atom_id, w, debug_output);
}
const FormatNavData = struct {
diff --git a/src/codegen/x86_64/Emit.zig b/src/codegen/x86_64/Emit.zig
@@ -4,7 +4,7 @@ lower: Lower,
bin_file: *link.File,
pt: Zcu.PerThread,
pic: bool,
-atom_index: u32,
+atom_id: link.File.AtomId,
debug_output: link.File.DebugInfoOutput,
w: *std.Io.Writer,
@@ -98,53 +98,48 @@ pub fn emitMir(emit: *Emit) Error!void {
.op_index = lowered_relocs[0].op_index,
.off = lowered_relocs[0].off,
.target = target: switch (lowered_relocs[0].target) {
- .inst => |inst| .{ .index = inst, .is_extern = false, .type = .inst },
- .table => .{ .index = undefined, .is_extern = false, .type = .table },
+ .inst => |inst| .{ .inst = inst },
+ .table => .table,
.nav => |nav| {
- const sym_index = switch (try codegen.genNavRef(
+ const symbol_id = switch (try codegen.genNavRef(
emit.bin_file,
emit.pt,
emit.lower.src_loc,
nav,
emit.lower.target,
)) {
- .sym_index => |sym_index| sym_index,
+ .sym_index => |symbol_id| symbol_id,
.fail => |em| {
assert(emit.lower.err_msg == null);
emit.lower.err_msg = em;
return error.EmitFail;
},
};
- const resolved_nav = ip.getNav(nav).resolved.?;
- if (resolved_nav.value != .none) switch (ip.indexToKey(resolved_nav.value)) {
- .@"extern" => |@"extern"| break :target .{
- .index = sym_index,
- .is_extern = switch (@"extern".visibility) {
- .default => true,
- .hidden, .protected => false,
- },
- .type = if (resolved_nav.@"threadlocal" and comp.config.any_non_single_threaded) .tlv else .symbol,
- .force_pcrel_direct = switch (@"extern".relocation) {
- .any => false,
- .pcrel => true,
- },
+ const target_symbol: RelocInfo.Target.Symbol = if (ip.getNav(nav).getExtern(ip)) |@"extern"| .{
+ .symbol = symbol_id,
+ .is_extern = switch (@"extern".visibility) {
+ .default => true,
+ .hidden, .protected => false,
},
- else => {},
- };
- break :target .{
- .index = sym_index,
- .is_extern = false,
- .type = if (resolved_nav.@"threadlocal" and comp.config.any_non_single_threaded) .tlv else .symbol,
- };
+ .force_pcrel_direct = switch (@"extern".relocation) {
+ .any => false,
+ .pcrel => true,
+ },
+ } else .{ .symbol = symbol_id, .is_extern = false };
+ if (ip.getNav(nav).resolved.?.@"threadlocal" and comp.config.any_non_single_threaded) {
+ break :target .{ .tlv = target_symbol };
+ } else {
+ break :target .{ .symbol = target_symbol };
+ }
},
- .uav => |uav| .{
- .index = switch (try emit.bin_file.lowerUav(
+ .uav => |uav| .{ .symbol = .{
+ .symbol = switch (try emit.bin_file.lowerUav(
emit.pt,
uav.val,
Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu),
emit.lower.src_loc,
)) {
- .sym_index => |sym_index| sym_index,
+ .sym_index => |symbol_id| symbol_id,
.fail => |em| {
assert(emit.lower.err_msg == null);
emit.lower.err_msg = em;
@@ -152,55 +147,57 @@ pub fn emitMir(emit: *Emit) Error!void {
},
},
.is_extern = false,
- .type = .symbol,
- },
- .lazy_sym => |lazy_sym| .{
- .index = if (emit.bin_file.cast(.elf)) |elf_file|
- elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, emit.pt, lazy_sym) catch |err|
- return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
+ } },
+ .lazy_sym => |lazy_sym| .{ .symbol = .{
+ .symbol = if (emit.bin_file.cast(.elf)) |elf_file|
+ @enumFromInt(
+ elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, emit.pt, lazy_sym) catch |err|
+ return emit.fail("{s} creating lazy symbol", .{@errorName(err)}),
+ )
else if (emit.bin_file.cast(.elf2)) |elf|
- @intFromEnum(try elf.lazySymbol(lazy_sym))
+ try elf.lazySymbol(lazy_sym)
else if (emit.bin_file.cast(.macho)) |macho_file|
- macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, emit.pt, lazy_sym) catch |err|
- return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
- else if (emit.bin_file.cast(.coff2)) |elf|
- @intFromEnum(try elf.lazySymbol(lazy_sym))
+ @enumFromInt(macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, emit.pt, lazy_sym) catch |err|
+ return emit.fail("{s} creating lazy symbol", .{@errorName(err)}))
+ else if (emit.bin_file.cast(.coff2)) |coff|
+ @enumFromInt(@intFromEnum(try coff.lazySymbol(lazy_sym)))
else
return emit.fail("lazy symbols unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
.is_extern = false,
- .type = .symbol,
- },
- .extern_func => |extern_func| .{
- .index = if (emit.bin_file.cast(.elf)) |elf_file|
- try elf_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)
- else if (emit.bin_file.cast(.elf2)) |elf| @intFromEnum(try elf.globalSymbol(.{
+ } },
+ .extern_func => |extern_func| .{ .symbol = .{
+ .symbol = if (emit.bin_file.cast(.elf)) |elf_file|
+ @enumFromInt(try elf_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null))
+ else if (emit.bin_file.cast(.elf2)) |elf| elf.externSymbol(.{
.name = extern_func.toSlice(&emit.lower.mir).?,
.lib_name = switch (comp.compiler_rt_strat) {
.none, .lib, .obj, .zcu => null,
.dyn_lib => "compiler_rt",
},
.type = .FUNC,
- })) else if (emit.bin_file.cast(.macho)) |macho_file|
- try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)
- else if (emit.bin_file.cast(.coff2)) |coff| @intFromEnum(try coff.globalSymbol(
+ }) catch |err| switch (err) {
+ error.LinkOnceUnsupported => unreachable,
+ else => |e| return e,
+ } else if (emit.bin_file.cast(.macho)) |macho_file|
+ @enumFromInt(try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null))
+ else if (emit.bin_file.cast(.coff2)) |coff| @enumFromInt(@intFromEnum(try coff.globalSymbol(
extern_func.toSlice(&emit.lower.mir).?,
switch (comp.compiler_rt_strat) {
.none, .lib, .obj, .zcu => null,
.dyn_lib => "compiler_rt",
},
- )) else return emit.fail("external symbol unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
+ ))) else return emit.fail("external symbol unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
.is_extern = true,
- .type = .symbol,
- },
+ } },
},
};
const reloc_info = reloc_info_buf[0..reloc_info_index];
- for (reloc_info) |*reloc| switch (reloc.target.type) {
+ for (reloc_info) |*reloc| switch (reloc.target) {
.inst, .table => {},
- .symbol => {
+ .symbol => |target| {
switch (lowered_inst.encoding.mnemonic) {
.call => {
- reloc.target.type = .branch;
+ reloc.target = .{ .branch = target };
try emit.encodeInst(lowered_inst, reloc_info);
continue :lowered_inst;
},
@@ -217,7 +214,7 @@ pub fn emitMir(emit: *Emit) Error!void {
.{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{}) },
}, emit.lower.target), reloc_info),
else => unreachable,
- } else if (reloc.target.is_extern) switch (lowered_inst.encoding.mnemonic) {
+ } else if (target.is_extern) switch (lowered_inst.encoding.mnemonic) {
.lea => try emit.encodeInst(try .new(.none, .mov, &.{
lowered_inst.ops[0],
.{ .mem = .initRip(.ptr, 0) },
@@ -247,7 +244,7 @@ pub fn emitMir(emit: *Emit) Error!void {
else => unreachable,
}
} else if (emit.bin_file.cast(.macho)) |_| {
- if (reloc.target.is_extern) switch (lowered_inst.encoding.mnemonic) {
+ if (target.is_extern) switch (lowered_inst.encoding.mnemonic) {
.lea => try emit.encodeInst(try .new(.none, .mov, &.{
lowered_inst.ops[0],
.{ .mem = .initRip(.ptr, 0) },
@@ -294,7 +291,7 @@ pub fn emitMir(emit: *Emit) Error!void {
continue :lowered_inst;
},
.branch, .tls => unreachable,
- .tlv => {
+ .tlv => |target| {
if (emit.bin_file.cast(.elf) != null or emit.bin_file.cast(.elf2) != null) {
// TODO handle extern TLS vars, i.e., emit GD model
if (emit.pic) switch (lowered_inst.encoding.mnemonic) {
@@ -306,28 +303,26 @@ pub fn emitMir(emit: *Emit) Error!void {
.{ .mem = .initRip(.none, 0) },
}, emit.lower.target), &.{.{
.op_index = 1,
- .target = .{
- .index = reloc.target.index,
- .is_extern = false,
- .type = .tls,
- },
+ .target = .{ .tls = target.symbol },
}});
try emit.encodeInst(try .new(.none, .call, &.{
.{ .imm = .s(0) },
}, emit.lower.target), &.{.{
.op_index = 0,
- .target = .{
- .index = if (emit.bin_file.cast(.elf)) |elf_file| try elf_file.getGlobalSymbol(
+ .target = .{ .branch = .{
+ .symbol = if (emit.bin_file.cast(.elf)) |elf_file| @enumFromInt(try elf_file.getGlobalSymbol(
"__tls_get_addr",
if (comp.config.link_libc) "c" else null,
- ) else if (emit.bin_file.cast(.elf2)) |elf| @intFromEnum(try elf.globalSymbol(.{
+ )) else if (emit.bin_file.cast(.elf2)) |elf| elf.externSymbol(.{
.name = "__tls_get_addr",
.lib_name = if (comp.config.link_libc) "c" else null,
.type = .FUNC,
- })) else unreachable,
+ }) catch |err| switch (err) {
+ error.LinkOnceUnsupported => unreachable,
+ else => |e| return e,
+ } else unreachable,
.is_extern = true,
- .type = .branch,
- },
+ } },
}});
try emit.encodeInst(try .new(.none, lowered_inst.encoding.mnemonic, &.{
lowered_inst.ops[0],
@@ -399,13 +394,12 @@ pub fn emitMir(emit: *Emit) Error!void {
.{ .mem = .initSib(.dword, .{}) },
}, emit.lower.target), &.{.{
.op_index = 1,
- .target = .{
- .index = @intFromEnum(
+ .target = .{ .symbol = .{
+ .symbol = @enumFromInt(@intFromEnum(
try coff.globalSymbol("__tls_index", null),
- ),
+ )),
.is_extern = false,
- .type = .symbol,
- },
+ } },
}});
try emit.encodeInst(try .new(.none, .mov, &.{
.{ .reg = .eax },
@@ -435,13 +429,12 @@ pub fn emitMir(emit: *Emit) Error!void {
.{ .mem = .initRip(.dword, 0) },
}, emit.lower.target), &.{.{
.op_index = 1,
- .target = .{
- .index = @intFromEnum(
+ .target = .{ .symbol = .{
+ .symbol = @enumFromInt(@intFromEnum(
try coff.globalSymbol("_tls_index", null),
- ),
+ )),
.is_extern = false,
- .type = .symbol,
- },
+ } },
}});
try emit.encodeInst(try .new(.none, .mov, &.{
.{ .reg = .rax },
@@ -713,17 +706,17 @@ pub fn emitMir(emit: *Emit) Error!void {
var table_offset = std.mem.alignForward(u32, @intCast(emit.w.end), ptr_size);
if (emit.bin_file.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?;
- const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
+ const atom = zo.symbol(@intFromEnum(emit.atom_id)).atom(elf_file).?;
for (emit.table_relocs.items) |table_reloc| try atom.addReloc(gpa, .{
.r_offset = table_reloc.source_offset,
- .r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"32S"),
+ .r_info = @as(u64, @intFromEnum(emit.atom_id)) << 32 | @intFromEnum(std.elf.R_X86_64.@"32S"),
.r_addend = @as(i64, table_offset) + table_reloc.target_offset,
}, zo);
for (emit.lower.mir.table) |entry| {
try atom.addReloc(gpa, .{
.r_offset = table_offset,
- .r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"64"),
+ .r_info = @as(u64, @intFromEnum(emit.atom_id)) << 32 | @intFromEnum(std.elf.R_X86_64.@"64"),
.r_addend = emit.code_offset_mapping.items[entry],
}, zo);
table_offset += ptr_size;
@@ -731,17 +724,17 @@ pub fn emitMir(emit: *Emit) Error!void {
try emit.w.splatByteAll(0, table_offset - emit.w.end);
} else if (emit.bin_file.cast(.elf2)) |elf| {
for (emit.table_relocs.items) |table_reloc| try elf.addReloc(
- @enumFromInt(emit.atom_index),
+ emit.atom_id,
table_reloc.source_offset,
- @enumFromInt(emit.atom_index),
+ elf.symbolForAtom(emit.atom_id),
@as(i64, table_offset) + table_reloc.target_offset,
.{ .X86_64 = .@"32S" },
);
for (emit.lower.mir.table) |entry| {
try elf.addReloc(
- @enumFromInt(emit.atom_index),
+ emit.atom_id,
table_offset,
- @enumFromInt(emit.atom_index),
+ elf.symbolForAtom(emit.atom_id),
emit.code_offset_mapping.items[entry],
.{ .X86_64 = .@"64" },
);
@@ -765,13 +758,19 @@ const RelocInfo = struct {
off: i32 = 0,
target: Target,
- const Target = struct {
- index: u32,
- is_extern: bool,
- type: Target.Type,
- force_pcrel_direct: bool = false,
+ const Target = union(enum) {
+ inst: Mir.Inst.Index,
+ table,
+ branch: Symbol,
+ symbol: Symbol,
+ tlv: Symbol,
+ tls: link.File.SymbolId,
- const Type = enum { inst, table, symbol, branch, tls, tlv };
+ const Symbol = struct {
+ symbol: link.File.SymbolId,
+ is_extern: bool,
+ force_pcrel_direct: bool = false,
+ };
};
};
@@ -784,8 +783,8 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
else => |e| return e,
};
const end_offset: u32 = @intCast(emit.w.end);
- for (reloc_info) |reloc| switch (reloc.target.type) {
- .inst => {
+ for (reloc_info) |reloc| switch (reloc.target) {
+ .inst => |target_inst| {
const inst_length: u4 = @intCast(end_offset - start_offset);
const reloc_offset, const reloc_length = reloc_offset_length: {
var reloc_offset = inst_length;
@@ -809,7 +808,7 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
.inst_length = inst_length,
.source_offset = reloc_offset,
.source_length = reloc_length,
- .target = reloc.target.index,
+ .target = target_inst,
.target_offset = reloc.off,
});
},
@@ -817,146 +816,146 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
.source_offset = end_offset - 4,
.target_offset = reloc.off,
}),
- .symbol => if (emit.bin_file.cast(.elf)) |elf_file| {
+ .symbol => |target| if (emit.bin_file.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?;
- const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
+ const atom = zo.symbol(@intFromEnum(emit.atom_id)).atom(elf_file).?;
const r_type: std.elf.R_X86_64 = if (!emit.pic)
.@"32S"
- else if (reloc.target.is_extern and !reloc.target.force_pcrel_direct)
+ else if (target.is_extern and !target.force_pcrel_direct)
.GOTPCREL
else
.PC32;
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
- .r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type),
+ .r_info = @as(u64, @intFromEnum(target.symbol)) << 32 | @intFromEnum(r_type),
.r_addend = if (emit.pic) reloc.off - 4 else reloc.off,
}, zo);
} else if (emit.bin_file.cast(.macho)) |macho_file| {
const zo = macho_file.getZigObject().?;
- const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?;
+ const atom = zo.symbols.items[@intFromEnum(emit.atom_id)].getAtom(macho_file).?;
try atom.addReloc(macho_file, .{
.tag = .@"extern",
.offset = end_offset - 4,
- .target = reloc.target.index,
+ .target = @intFromEnum(target.symbol),
.addend = reloc.off,
- .type = if (reloc.target.is_extern and !reloc.target.force_pcrel_direct) .got_load else .signed,
+ .type = if (target.is_extern and !target.force_pcrel_direct) .got_load else .signed,
.meta = .{
.pcrel = true,
.has_subtractor = false,
.length = 2,
- .symbolnum = @intCast(reloc.target.index),
+ .symbolnum = @intCast(@intFromEnum(target.symbol)),
},
});
} else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc(
- @enumFromInt(emit.atom_index),
+ emit.atom_id,
end_offset - 4,
- @enumFromInt(reloc.target.index),
+ target.symbol,
reloc.off,
.{ .X86_64 = .@"32S" },
) else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(
- @enumFromInt(emit.atom_index),
+ @enumFromInt(@intFromEnum(emit.atom_id)),
end_offset - 4,
- @enumFromInt(reloc.target.index),
+ @enumFromInt(@intFromEnum(target.symbol)),
reloc.off,
.{ .AMD64 = .REL32 },
) else unreachable,
- .branch => if (emit.bin_file.cast(.elf)) |elf_file| {
+ .branch => |target| if (emit.bin_file.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?;
- const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
+ const atom = zo.symbol(@intFromEnum(emit.atom_id)).atom(elf_file).?;
const r_type: std.elf.R_X86_64 = .PLT32;
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
- .r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type),
+ .r_info = @as(u64, @intFromEnum(target.symbol)) << 32 | @intFromEnum(r_type),
.r_addend = reloc.off - 4,
}, zo);
} else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc(
- @enumFromInt(emit.atom_index),
+ emit.atom_id,
end_offset - 4,
- @enumFromInt(reloc.target.index),
+ target.symbol,
reloc.off - 4,
.{ .X86_64 = .PLT32 },
) else if (emit.bin_file.cast(.macho)) |macho_file| {
const zo = macho_file.getZigObject().?;
- const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?;
+ const atom = zo.symbols.items[@intFromEnum(emit.atom_id)].getAtom(macho_file).?;
try atom.addReloc(macho_file, .{
.tag = .@"extern",
.offset = end_offset - 4,
- .target = reloc.target.index,
+ .target = @intFromEnum(target.symbol),
.addend = reloc.off,
.type = .branch,
.meta = .{
.pcrel = true,
.has_subtractor = false,
.length = 2,
- .symbolnum = @intCast(reloc.target.index),
+ .symbolnum = @intCast(@intFromEnum(target.symbol)),
},
});
} else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(
- @enumFromInt(emit.atom_index),
+ @enumFromInt(@intFromEnum(emit.atom_id)),
end_offset - 4,
- @enumFromInt(reloc.target.index),
+ @enumFromInt(@intFromEnum(target.symbol)),
reloc.off,
.{ .AMD64 = .REL32 },
) else return emit.fail("TODO implement {s} reloc for {s}", .{
- @tagName(reloc.target.type), @tagName(emit.bin_file.tag),
+ @tagName(reloc.target), @tagName(emit.bin_file.tag),
}),
- .tls => if (emit.bin_file.cast(.elf)) |elf_file| {
+ .tls => |target_symbol| if (emit.bin_file.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?;
- const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
+ const atom = zo.symbol(@intFromEnum(emit.atom_id)).atom(elf_file).?;
const r_type: std.elf.R_X86_64 = if (emit.pic) .TLSLD else unreachable;
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
- .r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type),
+ .r_info = @as(u64, @intFromEnum(target_symbol)) << 32 | @intFromEnum(r_type),
.r_addend = reloc.off - 4,
}, zo);
} else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc(
- @enumFromInt(emit.atom_index),
+ emit.atom_id,
end_offset - 4,
- @enumFromInt(reloc.target.index),
+ target_symbol,
reloc.off - 4,
.{ .X86_64 = if (emit.pic) .TLSLD else unreachable },
) else return emit.fail("TODO implement {s} reloc for {s}", .{
- @tagName(reloc.target.type), @tagName(emit.bin_file.tag),
+ @tagName(reloc.target), @tagName(emit.bin_file.tag),
}),
- .tlv => if (emit.bin_file.cast(.elf)) |elf_file| {
+ .tlv => |target| if (emit.bin_file.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?;
- const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
+ const atom = zo.symbol(@intFromEnum(emit.atom_id)).atom(elf_file).?;
const r_type: std.elf.R_X86_64 = if (emit.pic) .DTPOFF32 else .TPOFF32;
try atom.addReloc(gpa, .{
.r_offset = end_offset - 4,
- .r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type),
+ .r_info = @as(u64, @intFromEnum(target.symbol)) << 32 | @intFromEnum(r_type),
.r_addend = reloc.off,
}, zo);
} else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc(
- @enumFromInt(emit.atom_index),
+ emit.atom_id,
end_offset - 4,
- @enumFromInt(reloc.target.index),
+ target.symbol,
reloc.off,
.{ .X86_64 = if (emit.pic) .DTPOFF32 else .TPOFF32 },
) else if (emit.bin_file.cast(.macho)) |macho_file| {
const zo = macho_file.getZigObject().?;
- const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?;
+ const atom = zo.symbols.items[@intFromEnum(emit.atom_id)].getAtom(macho_file).?;
try atom.addReloc(macho_file, .{
.tag = .@"extern",
.offset = end_offset - 4,
- .target = reloc.target.index,
+ .target = @intFromEnum(target.symbol),
.addend = reloc.off,
.type = .tlv,
.meta = .{
.pcrel = true,
.has_subtractor = false,
.length = 2,
- .symbolnum = @intCast(reloc.target.index),
+ .symbolnum = @intCast(@intFromEnum(target.symbol)),
},
});
} else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(
- @enumFromInt(emit.atom_index),
+ @enumFromInt(@intFromEnum(emit.atom_id)),
end_offset - 4,
- @enumFromInt(reloc.target.index),
+ @enumFromInt(@intFromEnum(target.symbol)),
reloc.off,
.{ .AMD64 = .SECREL },
) else return emit.fail("TODO implement {s} reloc for {s}", .{
- @tagName(reloc.target.type), @tagName(emit.bin_file.tag),
+ @tagName(reloc.target), @tagName(emit.bin_file.tag),
}),
};
}
diff --git a/src/codegen/x86_64/Mir.zig b/src/codegen/x86_64/Mir.zig
@@ -1976,7 +1976,7 @@ pub fn emit(
pt: Zcu.PerThread,
src_loc: Zcu.LazySrcLoc,
func_index: InternPool.Index,
- atom_index: u32,
+ atom_id: link.File.AtomId,
w: *std.Io.Writer,
debug_output: link.File.DebugInfoOutput,
) codegen.CodeGenError!void {
@@ -1998,7 +1998,7 @@ pub fn emit(
.bin_file = lf,
.pt = pt,
.pic = mod.pic,
- .atom_index = atom_index,
+ .atom_id = atom_id,
.debug_output = debug_output,
.w = w,
@@ -2030,7 +2030,7 @@ pub fn emitLazy(
pt: Zcu.PerThread,
src_loc: Zcu.LazySrcLoc,
lazy_sym: link.File.LazySymbol,
- atom_index: u32,
+ atom_id: link.File.AtomId,
w: *std.Io.Writer,
debug_output: link.File.DebugInfoOutput,
) codegen.CodeGenError!void {
@@ -2049,7 +2049,7 @@ pub fn emitLazy(
.bin_file = lf,
.pt = pt,
.pic = mod.pic,
- .atom_index = atom_index,
+ .atom_id = atom_id,
.debug_output = debug_output,
.w = w,
diff --git a/src/link.zig b/src/link.zig
@@ -759,12 +759,24 @@ pub const File = struct {
/// must be attached to `Zcu.failed_codegen` rather than `Compilation.link_diags`.
pub const UpdateNavError = codegen.CodeGenError;
+ /// Opaque identifier for a function currently being emitted.
+ ///
+ /// The function may be an interned function with a NAV, or it may be a lazy function.
+ ///
+ /// This type exists for type-safe interaction between codegen and link.
+ pub const AtomId = enum(u32) { _ };
+
+ /// Opaque identifier for some symbol in the output binary.
+ ///
+ /// This type exists for type-safe interaction between codegen and link.
+ pub const SymbolId = enum(u32) { _ };
+
/// Called from within CodeGen to retrieve the symbol index of a global symbol.
/// If no symbol exists yet with this name, a new undefined global symbol will
/// be created. This symbol may get resolved once all relocatables are (re-)linked.
/// Optionally, it is possible to specify where to expect the symbol defined if it
/// is an import.
- pub fn getGlobalSymbol(base: *File, name: []const u8, lib_name: ?[]const u8) UpdateNavError!u32 {
+ pub fn getGlobalSymbol(base: *File, name: []const u8, lib_name: ?[]const u8) UpdateNavError!SymbolId {
log.debug("getGlobalSymbol '{s}' (expected in '{?s}')", .{ name, lib_name });
switch (base.tag) {
.lld => unreachable,
@@ -1008,7 +1020,7 @@ pub const File = struct {
pub const Parent = union(enum) {
none,
- atom_index: u32,
+ atom_index: AtomId,
debug_output: DebugInfoOutput,
};
};
diff --git a/src/link/Coff.zig b/src/link/Coff.zig
@@ -1328,7 +1328,7 @@ pub fn getUavVAddr(
pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.Index) !u64 {
try coff.addReloc(
- @enumFromInt(reloc_info.parent.atom_index),
+ @enumFromInt(@intFromEnum(reloc_info.parent.atom_index)),
reloc_info.offset,
target_si,
reloc_info.addend,
@@ -1581,7 +1581,7 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
zcu.navSrcLoc(nav_index),
.fromInterned(nav.resolved.?.value),
&nw.interface,
- .{ .atom_index = @intFromEnum(si) },
+ .{ .atom_index = @enumFromInt(@intFromEnum(si)) },
) catch |err| switch (err) {
error.WriteFailed => return error.OutOfMemory,
else => |e| return e,
@@ -1637,7 +1637,7 @@ pub fn lowerUav(
coff.const_prog_node.increaseEstimatedTotalItems(1);
}
}
- return .{ .sym_index = @intFromEnum(si) };
+ return .{ .sym_index = @enumFromInt(@intFromEnum(si)) };
}
pub fn updateFunc(
@@ -1715,7 +1715,7 @@ fn updateFuncInner(
pt,
zcu.navSrcLoc(func.owner_nav),
func_index,
- @intFromEnum(si),
+ @enumFromInt(@intFromEnum(si)),
mir,
&nw.interface,
.none,
@@ -1930,7 +1930,7 @@ fn flushUav(
src_loc,
.fromInterned(uav_val),
&nw.interface,
- .{ .atom_index = @intFromEnum(si) },
+ .{ .atom_index = @enumFromInt(@intFromEnum(si)) },
) catch |err| switch (err) {
error.WriteFailed => return error.OutOfMemory,
else => |e| return e,
@@ -2146,7 +2146,7 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
&required_alignment,
&nw.interface,
.none,
- .{ .atom_index = @intFromEnum(si) },
+ .{ .atom_index = @enumFromInt(@intFromEnum(si)) },
);
si.get(coff).size = @intCast(nw.interface.end);
si.applyLocationRelocs(coff);
@@ -2339,7 +2339,7 @@ fn updateExportsInner(
try coff.symbol_table.ensureUnusedCapacity(gpa, export_indices.len);
const exported_si: Symbol.Index = switch (exported) {
.nav => |nav| try coff.navSymbol(zcu, nav),
- .uav => |uav| @enumFromInt(switch (try coff.lowerUav(
+ .uav => |uav| @enumFromInt(@intFromEnum(switch (try coff.lowerUav(
pt,
uav,
Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
@@ -2350,7 +2350,7 @@ fn updateExportsInner(
defer em.destroy(gpa);
return coff.base.comp.link_diags.fail("{s}", .{em.msg});
},
- }),
+ })),
};
while (try coff.idle(pt.tid)) {}
const exported_ni = exported_si.node(coff);
diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig
@@ -1151,13 +1151,13 @@ const CrossSectionReloc = struct {
};
const ExternalReloc = struct {
source_off: u32 = 0,
- target_sym: u32,
+ target_sym: link.File.SymbolId,
target_off: u64 = 0,
};
pub const Loc = union(enum) {
empty,
- addr_reloc: u32,
+ addr_reloc: link.File.SymbolId,
deref: *const Loc,
constu: u64,
consts: i64,
@@ -1506,7 +1506,7 @@ pub const WipNav = struct {
entry: Entry.Index,
any_children: bool,
func: InternPool.Index,
- func_sym_index: u32,
+ func_sym_index: link.File.SymbolId,
func_high_pc: u32,
blocks: std.ArrayList(struct {
abbrev_code: u32,
@@ -1966,7 +1966,7 @@ pub const WipNav = struct {
fn endian(_: ExprLocCounter) std.lang.Endian {
return @import("builtin").cpu.arch.endian();
}
- fn addrSym(counter: *ExprLocCounter, _: u32) Writer.Error!void {
+ fn addrSym(counter: *ExprLocCounter, _: link.File.SymbolId) Writer.Error!void {
try counter.dw.writer.splatByteAll(undefined, @intFromEnum(counter.address_size));
}
fn infoEntry(counter: *ExprLocCounter, _: Unit.Index, _: Entry.Index) Writer.Error!void {
@@ -1987,7 +1987,7 @@ pub const WipNav = struct {
fn endian(ctx: @This()) std.lang.Endian {
return ctx.wip_nav.dwarf.endian;
}
- fn addrSym(ctx: @This(), sym_index: u32) (UpdateError || Writer.Error)!void {
+ fn addrSym(ctx: @This(), sym_index: link.File.SymbolId) (UpdateError || Writer.Error)!void {
try ctx.wip_nav.infoAddrSym(sym_index, 0);
}
fn infoEntry(
@@ -2004,7 +2004,7 @@ pub const WipNav = struct {
fn infoAddrSym(
wip_nav: *WipNav,
- sym_index: u32,
+ sym_index: link.File.SymbolId,
sym_off: u64,
) (UpdateError || Writer.Error)!void {
const diw = &wip_nav.debug_info.writer;
@@ -2029,7 +2029,7 @@ pub const WipNav = struct {
fn endian(ctx: @This()) std.lang.Endian {
return ctx.wip_nav.dwarf.endian;
}
- fn addrSym(ctx: @This(), sym_index: u32) (UpdateError || Writer.Error)!void {
+ fn addrSym(ctx: @This(), sym_index: link.File.SymbolId) (UpdateError || Writer.Error)!void {
try ctx.wip_nav.frameAddrSym(sym_index, 0);
}
fn infoEntry(
@@ -2046,7 +2046,7 @@ pub const WipNav = struct {
fn frameAddrSym(
wip_nav: *WipNav,
- sym_index: u32,
+ sym_index: link.File.SymbolId,
sym_off: u64,
) (UpdateError || Writer.Error)!void {
const dfw = &wip_nav.debug_frame.writer;
@@ -2591,7 +2591,7 @@ pub fn initWipNav(
dwarf: *Dwarf,
pt: Zcu.PerThread,
nav_index: InternPool.Nav.Index,
- sym_index: u32,
+ sym_index: link.File.SymbolId,
) error{ OutOfMemory, CodegenFail }!WipNav {
return initWipNavInner(dwarf, pt, nav_index, sym_index) catch |err| switch (err) {
error.OutOfMemory => error.OutOfMemory,
@@ -2603,7 +2603,7 @@ fn initWipNavInner(
dwarf: *Dwarf,
pt: Zcu.PerThread,
nav_index: InternPool.Nav.Index,
- sym_index: u32,
+ sym_index: link.File.SymbolId,
) !WipNav {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig
@@ -461,14 +461,14 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {
}, self);
}
for (entry.external_relocs.items) |reloc| {
- const target_sym = self.symbol(reloc.target_sym);
+ const target_sym = self.symbol(@intFromEnum(reloc.target_sym));
const r_offset = entry_off + reloc.source_off;
const r_addend: i64 = @intCast(reloc.target_off);
const r_type = relocation.dwarf.externalRelocType(target_sym.*, sect_index, dwarf.address_size, cpu_arch);
atom_ptr.addRelocAssumeCapacity(.{
.r_offset = r_offset,
.r_addend = r_addend,
- .r_info = (@as(u64, @intCast(reloc.target_sym)) << 32) | r_type,
+ .r_info = (@as(u64, @intCast(@intFromEnum(reloc.target_sym))) << 32) | r_type,
}, self);
}
}
@@ -941,7 +941,7 @@ pub fn getNavVAddr(
switch (reloc_info.parent) {
.none => unreachable,
.atom_index => |atom_index| {
- const parent_atom = self.symbol(atom_index).atom(elf_file).?;
+ const parent_atom = self.symbol(@intFromEnum(atom_index)).atom(elf_file).?;
const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);
try parent_atom.addReloc(elf_file.base.comp.gpa, .{
.r_offset = reloc_info.offset,
@@ -952,7 +952,7 @@ pub fn getNavVAddr(
.debug_output => |debug_output| switch (debug_output) {
.dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{
.source_off = @intCast(reloc_info.offset),
- .target_sym = this_sym_index,
+ .target_sym = @enumFromInt(this_sym_index),
.target_off = reloc_info.addend,
}),
.none => unreachable,
@@ -973,7 +973,7 @@ pub fn getUavVAddr(
switch (reloc_info.parent) {
.none => unreachable,
.atom_index => |atom_index| {
- const parent_atom = self.symbol(atom_index).atom(elf_file).?;
+ const parent_atom = self.symbol(@intFromEnum(atom_index)).atom(elf_file).?;
const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);
try parent_atom.addReloc(elf_file.base.comp.gpa, .{
.r_offset = reloc_info.offset,
@@ -984,7 +984,7 @@ pub fn getUavVAddr(
.debug_output => |debug_output| switch (debug_output) {
.dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{
.source_off = @intCast(reloc_info.offset),
- .target_sym = sym_index,
+ .target_sym = @enumFromInt(sym_index),
.target_off = reloc_info.addend,
}),
.none => unreachable,
@@ -1013,7 +1013,7 @@ pub fn lowerUav(
const sym = self.symbol(metadata.symbol_index);
const existing_alignment = sym.atom(elf_file).?.alignment;
if (uav_alignment.order(existing_alignment).compare(.lte))
- return .{ .sym_index = metadata.symbol_index };
+ return .{ .sym_index = @enumFromInt(metadata.symbol_index) };
}
const osec = if (self.data_relro_index) |sym_index|
@@ -1051,7 +1051,10 @@ pub fn lowerUav(
) },
};
switch (res) {
- .sym_index => |sym_index| try self.uavs.put(gpa, uav, .{ .symbol_index = sym_index, .allocated = true }),
+ .sym_index => |sym_index| try self.uavs.put(gpa, uav, .{
+ .symbol_index = @intFromEnum(sym_index),
+ .allocated = true,
+ }),
.fail => {},
}
return res;
@@ -1545,7 +1548,11 @@ pub fn updateFunc(
var aw: std.Io.Writer.Allocating = .init(gpa);
defer aw.deinit();
- var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null;
+ var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(
+ pt,
+ func.owner_nav,
+ @enumFromInt(sym_index),
+ ) else null;
defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
codegen.emitFunction(
@@ -1553,7 +1560,7 @@ pub fn updateFunc(
pt,
zcu.navSrcLoc(func.owner_nav),
func_index,
- sym_index,
+ @enumFromInt(sym_index),
mir,
&aw.writer,
if (debug_wip_nav) |*dn| .{ .dwarf = dn } else .none,
@@ -1660,7 +1667,7 @@ pub fn updateNav(
self.symbol(sym_index).flags.is_tls = true;
}
if (self.dwarf) |*dwarf| {
- var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index);
+ var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, @enumFromInt(sym_index));
defer debug_wip_nav.deinit();
dwarf.finishWipNav(pt, nav_index, &debug_wip_nav) catch |err| switch (err) {
error.OutOfMemory, error.Overflow => |e| return e,
@@ -1678,7 +1685,7 @@ pub fn updateNav(
var aw: std.Io.Writer.Allocating = .init(zcu.gpa);
defer aw.deinit();
- var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null;
+ var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, @enumFromInt(sym_index)) else null;
defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
codegen.generateSymbol(
@@ -1687,7 +1694,7 @@ pub fn updateNav(
zcu.navSrcLoc(nav_index),
.fromInterned(nav.resolved.?.value),
&aw.writer,
- .{ .atom_index = sym_index },
+ .{ .atom_index = @enumFromInt(sym_index) },
) catch |err| switch (err) {
error.WriteFailed => return error.OutOfMemory,
else => |e| return e,
@@ -1757,7 +1764,7 @@ fn updateLazySymbol(
&required_alignment,
&aw.writer,
.none,
- .{ .atom_index = symbol_index },
+ .{ .atom_index = @enumFromInt(symbol_index) },
) catch |err| switch (err) {
error.WriteFailed => return error.OutOfMemory,
else => |e| return e,
@@ -1836,7 +1843,7 @@ fn lowerConst(
src_loc,
val,
&aw.writer,
- .{ .atom_index = sym_index },
+ .{ .atom_index = @enumFromInt(sym_index) },
) catch |err| switch (err) {
error.WriteFailed => return error.OutOfMemory,
else => |e| return e,
@@ -1858,7 +1865,7 @@ fn lowerConst(
try elf_file.pwriteAll(code, atom_ptr.offset(elf_file));
- return .{ .sym_index = sym_index };
+ return .{ .sym_index = @enumFromInt(sym_index) };
}
pub fn updateExports(
diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig
@@ -25,55 +25,102 @@ ni: Node.Known,
nodes: std.MultiArrayList(Node),
shdrs: std.ArrayList(Section),
phdrs: std.ArrayList(MappedFile.Node.Index),
-si: Symbol.Known,
+shndx: struct {
+ got: Section.Index,
+ got_plt: Section.Index,
+ plt: Section.Index,
+ plt_sec: Section.Index,
+ dynsym: Section.Index,
+ dynstr: Section.Index,
+ dynamic: Section.Index,
+ tdata: Section.Index,
+},
symtab: std.ArrayList(Symbol),
+globals: struct {
+ strong_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
+ weak_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
+ strong_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
+ weak_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
+},
+/// Key is a node which is a valid `Symbol.node` value, value is the name of the first global symbol
+/// in that node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.
+///
+/// Value is never `.empty`.
+///
+/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,
+/// because the vast majority of nodes which can export global symbols actually will not.
+node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, String(.strtab)),
shstrtab: StringTable,
strtab: StringTable,
-dynsym: std.AutoArrayHashMapUnmanaged(Symbol.Index, void),
dynstr: StringTable,
got: struct {
len: u32,
tlsld: GotIndex,
- plt: std.AutoArrayHashMapUnmanaged(Symbol.Index, void),
+ plt: std.AutoArrayHashMapUnmanaged(Symbol.Id, void),
},
-needed: std.AutoArrayHashMapUnmanaged(u32, void),
+first_plt_reloc: Reloc.Index,
+first_dynamic_reloc: Reloc.Index,
+needed: std.AutoArrayHashMapUnmanaged(String(.dynstr), void),
inputs: std.ArrayList(struct {
path: std.Build.Cache.Path,
member: ?[]const u8,
- si: Symbol.Index,
-}),
-input_sections: std.ArrayList(struct {
- ii: Node.InputIndex,
- file_location: MappedFile.Node.FileLocation,
- si: Symbol.Index,
+ file_symbol: Symbol.LocalIndex,
}),
+input_sections: std.ArrayList(InputSection),
input_section_pending_index: u32,
-globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index),
-navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Symbol.Index),
-uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Symbol.Index),
+navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, struct {
+ /// The start index of the contiguous sequence of relocations in this NAV.
+ first_reloc: Reloc.Index,
+ lsi: Symbol.LocalIndex,
+}),
+uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct {
+ /// The start index of the contiguous sequence of relocations in this UAV.
+ first_reloc: Reloc.Index,
+ lsi: Symbol.LocalIndex,
+}),
lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
- map: std.AutoArrayHashMapUnmanaged(InternPool.Index, Symbol.Index),
+ map: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct {
+ /// The start index of the contiguous sequence of relocations in this lazy code/data.
+ first_reloc: Reloc.Index,
+ lsi: Symbol.LocalIndex,
+ }),
pending_index: u32,
}),
-pending_uavs: std.AutoArrayHashMapUnmanaged(Node.UavMapIndex, struct {
- alignment: InternPool.Alignment,
- src_loc: Zcu.LazySrcLoc,
-}),
+pending_uavs: std.ArrayList(Node.UavMapIndex),
relocs: std.ArrayList(Reloc),
+
+/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation
+/// entries which target that symbol must be updated to reference the correct symbol index.
+changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),
+
const_prog_node: std.Progress.Node,
synth_prog_node: std.Progress.Node,
input_prog_node: std.Progress.Node,
-pub const Node = union(enum) {
+const Node = union(enum) {
+ /// Cannot contain relocations.
file,
+ /// Cannot contain relocations.
ehdr,
+ /// Cannot contain relocations.
shdr,
+ /// Cannot contain relocations.
segment: u32,
- section: Symbol.Index,
- input_section: InputSectionIndex,
+ /// The section '.plt' may contain relocations via `elf.first_plt_reloc`.
+ ///
+ /// The section '.dynamic' may contain relocations via `elf.first_dynamic_reloc`.
+ ///
+ /// Otherwise, cannot contain relocations.
+ section: Section.Index,
+ /// May contain relocations through the `first_reloc` field in `elf.input_sections`.
+ input_section: InputSection.Index,
+ /// May contain relocations through the `first_reloc` field in `elf.navs`.
nav: NavMapIndex,
+ /// May contain relocations through the `first_reloc` field in `elf.uavs`.
uav: UavMapIndex,
+ /// May contain relocations through the `first_reloc` field in `elf.lazy.map`.
lazy_code: LazyMapRef.Index(.code),
+ /// May contain relocations through the `first_reloc` field in `elf.lazy.map`.
lazy_const_data: LazyMapRef.Index(.const_data),
pub const InputIndex = enum(u32) {
@@ -87,32 +134,20 @@ pub const Node = union(enum) {
return elf.inputs.items[@intFromEnum(ii)].member;
}
- pub fn symbol(ii: InputIndex, elf: *const Elf) Symbol.Index {
- return elf.inputs.items[@intFromEnum(ii)].si;
- }
-
- pub fn endSymbol(ii: InputIndex, elf: *const Elf) Symbol.Index {
- const next_ii = @intFromEnum(ii) + 1;
- return if (next_ii < elf.inputs.items.len)
- @as(InputIndex, @enumFromInt(next_ii)).symbol(elf)
- else
- @enumFromInt(elf.symtab.items.len);
- }
- };
-
- pub const InputSectionIndex = enum(u32) {
- _,
-
- pub fn input(isi: InputSectionIndex, elf: *const Elf) InputIndex {
- return elf.input_sections.items[@intFromEnum(isi)].ii;
+ pub fn fileSymbol(ii: InputIndex, elf: *const Elf) Symbol.LocalIndex {
+ return elf.inputs.items[@intFromEnum(ii)].file_symbol;
}
- pub fn fileLocation(isi: InputSectionIndex, elf: *const Elf) MappedFile.Node.FileLocation {
- return elf.input_sections.items[@intFromEnum(isi)].file_location;
- }
-
- pub fn symbol(isi: InputSectionIndex, elf: *const Elf) Symbol.Index {
- return elf.input_sections.items[@intFromEnum(isi)].si;
+ pub fn localSymbolRange(ii: InputIndex, elf: *Elf) [2]Symbol.LocalIndex {
+ if (@intFromEnum(ii) + 1 < elf.inputs.items.len) {
+ const next_ii: InputIndex = @enumFromInt(@intFromEnum(ii) + 1);
+ return .{ ii.fileSymbol(elf), next_ii.fileSymbol(elf) };
+ } else {
+ const local_symbols_len = switch (elf.shdrPtr(.symtab)) {
+ inline else => |shdr| elf.targetLoad(&shdr.info),
+ };
+ return .{ ii.fileSymbol(elf), @enumFromInt(local_symbols_len) };
+ }
}
};
@@ -123,8 +158,12 @@ pub const Node = union(enum) {
return elf.navs.keys()[@intFromEnum(nmi)];
}
- pub fn symbol(nmi: NavMapIndex, elf: *const Elf) Symbol.Index {
- return elf.navs.values()[@intFromEnum(nmi)];
+ pub fn symbol(nmi: NavMapIndex, elf: *const Elf) Symbol.LocalIndex {
+ return elf.navs.values()[@intFromEnum(nmi)].lsi;
+ }
+
+ fn firstReloc(nmi: NavMapIndex, elf: *const Elf) Reloc.Index {
+ return elf.navs.values()[@intFromEnum(nmi)].first_reloc;
}
};
@@ -135,8 +174,12 @@ pub const Node = union(enum) {
return elf.uavs.keys()[@intFromEnum(umi)];
}
- pub fn symbol(umi: UavMapIndex, elf: *const Elf) Symbol.Index {
- return elf.uavs.values()[@intFromEnum(umi)];
+ pub fn symbol(umi: UavMapIndex, elf: *const Elf) Symbol.LocalIndex {
+ return elf.uavs.values()[@intFromEnum(umi)].lsi;
+ }
+
+ fn firstReloc(umi: UavMapIndex, elf: *const Elf) Reloc.Index {
+ return elf.uavs.values()[@intFromEnum(umi)].first_reloc;
}
};
@@ -156,9 +199,13 @@ pub const Node = union(enum) {
return lmi.ref().lazySymbol(elf);
}
- pub fn symbol(lmi: @This(), elf: *const Elf) Symbol.Index {
+ pub fn symbol(lmi: @This(), elf: *const Elf) Symbol.LocalIndex {
return lmi.ref().symbol(elf);
}
+
+ fn firstReloc(lmi: @This(), elf: *const Elf) Reloc.Index {
+ return lmi.ref().firstReloc(elf);
+ }
};
}
@@ -166,8 +213,12 @@ pub const Node = union(enum) {
return .{ .kind = lmr.kind, .ty = elf.lazy.getPtrConst(lmr.kind).map.keys()[lmr.index] };
}
- pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.Index {
- return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index];
+ pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.LocalIndex {
+ return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].lsi;
+ }
+
+ fn firstReloc(lmr: LazyMapRef, elf: *const Elf) Reloc.Index {
+ return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].first_reloc;
}
};
@@ -180,17 +231,66 @@ pub const Node = union(enum) {
comptime text: MappedFile.Node.Index = @enumFromInt(5),
comptime data: MappedFile.Node.Index = @enumFromInt(6),
comptime data_rel_ro: MappedFile.Node.Index = @enumFromInt(7),
+
tls: MappedFile.Node.Index,
};
comptime {
if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Node) == 8);
}
+
+ /// In this linker implementation, `link.File.AtomId` is a type-erased `MappedFile.Node.Index`.
+ fn toAtom(ni: MappedFile.Node.Index) link.File.AtomId {
+ return @enumFromInt(@intFromEnum(ni));
+ }
+ /// In this linker implementation, `link.File.AtomId` is a type-erased `MappedFile.Node.Index`.
+ fn fromAtom(atom: link.File.AtomId) MappedFile.Node.Index {
+ return @enumFromInt(@intFromEnum(atom));
+ }
};
-pub const Section = struct {
- si: Symbol.Index,
- rela_si: Symbol.Index,
+const InputSection = struct {
+ input: Node.InputIndex,
+ file_location: MappedFile.Node.FileLocation,
+ vaddr: u64,
+ /// The node corresponding to this input section.
+ node: MappedFile.Node.Index,
+ /// The start index of the contiguous sequence of relocations in this input section.
+ first_reloc: Reloc.Index,
+
+ const Index = enum(u32) {
+ _,
+
+ fn ptr(isi: InputSection.Index, elf: *Elf) *InputSection {
+ return &elf.input_sections.items[@intFromEnum(isi)];
+ }
+
+ fn ptrConst(isi: InputSection.Index, elf: *const Elf) *const InputSection {
+ return &elf.input_sections.items[@intFromEnum(isi)];
+ }
+
+ fn input(isi: InputSection.Index, elf: *const Elf) Node.InputIndex {
+ return isi.ptrConst(elf).input;
+ }
+
+ fn fileLocation(isi: InputSection.Index, elf: *const Elf) MappedFile.Node.FileLocation {
+ return isi.ptrConst(elf).file_location;
+ }
+
+ fn node(isi: InputSection.Index, elf: *const Elf) MappedFile.Node.Index {
+ return isi.ptrConst(elf).node;
+ }
+ };
+};
+
+const Section = struct {
+ /// The node corresponding to this section.
+ ni: MappedFile.Node.Index,
+ /// A symbol which is exactly at the start of this section.
+ ///
+ /// If the section does not have flag `std.elf.SHF.ALLOC`, this is `.null`.
+ lsi: Symbol.LocalIndex,
+ rela_shndx: Section.Index,
rela_free: RelIndex,
pub const RelIndex = enum(u32) {
@@ -207,394 +307,1165 @@ pub const Section = struct {
};
}
};
-};
-pub const Symbol = struct {
- ni: MappedFile.Node.Index,
- /// Relocations contained within this symbol
- loc_relocs: Reloc.Index,
- /// Relocations targeting this symbol
- target_relocs: Reloc.Index,
- unused: u32,
+ pub const Index = enum(Tag) {
+ UNDEF = std.elf.SHN_UNDEF,
+ LIVEPATCH = reserve(std.elf.SHN_LIVEPATCH),
+ ABS = reserve(std.elf.SHN_ABS),
+ COMMON = reserve(std.elf.SHN_COMMON),
- pub const Index = enum(u32) {
- null,
- symtab,
+ symtab = 1,
shstrtab,
strtab,
rodata,
text,
data,
data_rel_ro,
- got,
- got_plt,
- plt,
- plt_sec,
+
_,
- pub fn get(si: Symbol.Index, elf: *Elf) *Symbol {
- return &elf.symtab.items[@intFromEnum(si)];
+ pub const Tag = u32;
+
+ pub const LORESERVE: Index = .fromSection(std.elf.SHN_LORESERVE);
+ pub const HIRESERVE: Index = .fromSection(std.elf.SHN_HIRESERVE);
+ comptime {
+ assert(@intFromEnum(HIRESERVE) == std.math.maxInt(Tag));
}
- pub fn node(si: Symbol.Index, elf: *Elf) MappedFile.Node.Index {
- const ni = si.get(elf).ni;
- assert(ni != .none);
- return ni;
+ fn reserve(sec: std.elf.Section) Tag {
+ assert(sec >= std.elf.SHN_LORESERVE and sec <= std.elf.SHN_HIRESERVE);
+ return @as(Tag, std.math.maxInt(Tag) - std.elf.SHN_HIRESERVE) + sec;
}
- pub fn next(si: Symbol.Index) Symbol.Index {
- return @enumFromInt(@intFromEnum(si) + 1);
+ pub fn fromSection(sec: std.elf.Section) Index {
+ return switch (sec) {
+ std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => @enumFromInt(sec),
+ std.elf.SHN_LORESERVE...std.elf.SHN_HIRESERVE => @enumFromInt(reserve(sec)),
+ };
+ }
+ pub fn toSection(s: Index) ?std.elf.Section {
+ return switch (@intFromEnum(s)) {
+ std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => |sec| @intCast(sec),
+ std.elf.SHN_LORESERVE...reserve(std.elf.SHN_LORESERVE) - 1 => null,
+ reserve(std.elf.SHN_LORESERVE)...reserve(std.elf.SHN_HIRESERVE) => |sec| @intCast(
+ sec - reserve(std.elf.SHN_LORESERVE) + std.elf.SHN_LORESERVE,
+ ),
+ };
}
- pub const Shndx = enum(Tag) {
- UNDEF = std.elf.SHN_UNDEF,
- LIVEPATCH = reserve(std.elf.SHN_LIVEPATCH),
- ABS = reserve(std.elf.SHN_ABS),
- COMMON = reserve(std.elf.SHN_COMMON),
- _,
+ fn get(s: Index, elf: *Elf) *Section {
+ return &elf.shdrs.items[@intFromEnum(s)];
+ }
- pub const Tag = u32;
+ fn name(s: Index, elf: *Elf) [:0]const u8 {
+ const str: String(.shstrtab) = switch (elf.shdrPtr(s)) {
+ inline else => |shdr| @enumFromInt(elf.targetLoad(&shdr.name)),
+ };
+ return str.slice(elf);
+ }
- pub const LORESERVE: Shndx = .fromSection(std.elf.SHN_LORESERVE);
- pub const HIRESERVE: Shndx = .fromSection(std.elf.SHN_HIRESERVE);
- comptime {
- assert(@intFromEnum(HIRESERVE) == std.math.maxInt(Tag));
- }
+ fn vaddr(s: Index, elf: *Elf) u64 {
+ return switch (s.get(elf).lsi) {
+ .null => 0,
+ else => |lsi| Symbol.Id.local(lsi).value(elf),
+ };
+ }
- fn reserve(sec: std.elf.Section) Tag {
- assert(sec >= std.elf.SHN_LORESERVE and sec <= std.elf.SHN_HIRESERVE);
- return @as(Tag, std.math.maxInt(Tag) - std.elf.SHN_HIRESERVE) + sec;
+ fn rename(shndx: Index, elf: *Elf, new_name: []const u8) !void {
+ const shstrtab_entry = try elf.string(.shstrtab, new_name);
+ switch (elf.shdrPtr(shndx)) {
+ inline else => |shdr| elf.targetStore(&shdr.name, @intFromEnum(shstrtab_entry)),
}
+ }
+ };
+};
+
+fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) !void {
+ const gpa = elf.base.comp.gpa;
+
+ try elf.symtab.ensureUnusedCapacity(gpa, len);
+
+ // If adding locals, we may need to move one global out of the way for each local. If adding
+ // globals, they could all get demoted to STB_LOCAL, which would mean we move those N globals
+ // *and* we move up to N other globals out of their way.
+ try elf.changed_symtab_index.ensureUnusedCapacity(gpa, switch (kind) {
+ .all_local => len,
+ .maybe_global => len * 2,
+ });
+
+ {
+ // Ensure the symtab section's node is big enough
+ const need_node_size: u64 = switch (elf.shdrPtr(.symtab)) {
+ inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym),
+ };
+ _, const cur_node_size = Section.Index.symtab.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
+ if (cur_node_size < need_node_size) {
+ const new_node_size = need_node_size +| need_node_size / MappedFile.growth_factor;
+ try Section.Index.symtab.get(elf).ni.resize(&elf.mf, gpa, new_node_size);
+ }
+ }
+
+ switch (kind) {
+ .all_local => {},
+ .maybe_global => {
+ try elf.globals.strong_def.ensureUnusedCapacity(gpa, len);
+ try elf.globals.weak_def.ensureUnusedCapacity(gpa, len);
+ try elf.globals.strong_undef.ensureUnusedCapacity(gpa, len);
+ try elf.globals.weak_undef.ensureUnusedCapacity(gpa, len);
+
+ try elf.node_global_symbols.ensureUnusedCapacity(gpa, len);
- pub fn fromSection(sec: std.elf.Section) Shndx {
- return switch (sec) {
- std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => @enumFromInt(sec),
- std.elf.SHN_LORESERVE...std.elf.SHN_HIRESERVE => @enumFromInt(reserve(sec)),
+ if (elf.shndx.dynsym != .UNDEF) {
+ // Ensure the `.dynsym` section's node is big enough
+ const dynsym_need_size: u64 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
+ inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym),
};
+ _, const dynsym_cur_size = elf.shndx.dynsym.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
+ if (dynsym_cur_size < dynsym_need_size) {
+ const new_size = dynsym_need_size +| dynsym_need_size / MappedFile.growth_factor;
+ try elf.shndx.dynsym.get(elf).ni.resize(&elf.mf, gpa, new_size);
+ }
+
+ try elf.got.plt.ensureUnusedCapacity(gpa, len);
+ const need_plt_capacity = elf.got.plt.count() + len;
+
+ switch (elf.ehdrField(.machine)) {
+ else => |machine| @panic(@tagName(machine)),
+ .X86_64 => {
+ // Ensure the `.plt` section's node is big enough
+ const plt_need_size: usize = 16 * (1 + need_plt_capacity);
+ _, const plt_cur_size = elf.shndx.plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
+ if (plt_cur_size < plt_need_size) {
+ const new_size = plt_need_size +| plt_need_size / MappedFile.growth_factor;
+ try elf.shndx.plt.get(elf).ni.resize(&elf.mf, gpa, new_size);
+ }
+
+ // Ensure the `.got.plt` section's node is big enough
+ const got_plt_need_size: usize = switch (elf.identClass()) {
+ .NONE, _ => unreachable,
+ inline else => |class| @sizeOf(class.ElfN().Addr) * (3 + need_plt_capacity),
+ };
+ _, const got_plt_cur_size = elf.shndx.got_plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
+ if (got_plt_cur_size < got_plt_need_size) {
+ const new_size = got_plt_need_size +| got_plt_need_size / MappedFile.growth_factor;
+ try elf.shndx.got_plt.get(elf).ni.resize(&elf.mf, gpa, new_size);
+ }
+
+ // Ensure the `.plt.sec` section's node is big enough
+ const plt_sec_need_size: usize = 16 * need_plt_capacity;
+ _, const plt_sec_cur_size = elf.shndx.plt_sec.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
+ if (plt_sec_cur_size < plt_sec_need_size) {
+ const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor;
+ try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size);
+ }
+
+ // Ensure the `.rela.plt` section's node is big enough
+ const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx;
+ const rela_plt_need_size: usize = switch (elf.shdrPtr(rela_plt_shndx)) {
+ inline else => |shdr| @intCast(elf.targetLoad(&shdr.entsize) * need_plt_capacity),
+ };
+ _, const rela_plt_cur_size = rela_plt_shndx.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
+ if (rela_plt_cur_size < rela_plt_need_size) {
+ const new_size = rela_plt_need_size +| rela_plt_need_size / MappedFile.growth_factor;
+ try rela_plt_shndx.get(elf).ni.resize(&elf.mf, gpa, new_size);
+ } else {
+ // Still mark `.rela.plt` as resized so that the DT_PLTRELSZ entry can
+ // be updated if we do indeed add a PLT entry.
+ try rela_plt_shndx.get(elf).ni.resized(gpa, &elf.mf);
+ }
+ },
+ }
}
- pub fn toSection(s: Shndx) ?std.elf.Section {
- return switch (@intFromEnum(s)) {
- std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => |sec| @intCast(sec),
- std.elf.SHN_LORESERVE...reserve(std.elf.SHN_LORESERVE) - 1 => null,
- reserve(std.elf.SHN_LORESERVE)...reserve(std.elf.SHN_HIRESERVE) => |sec| @intCast(
- sec - reserve(std.elf.SHN_LORESERVE) + std.elf.SHN_LORESERVE,
- ),
- };
+ },
+ }
+}
+
+const AddLocalSymbolOptions = struct {
+ node: MappedFile.Node.Index,
+ name: String(.strtab),
+ value: u64,
+ size: u64,
+ type: std.elf.STT,
+ shndx: Section.Index,
+};
+fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.LocalIndex {
+ switch (elf.shdrPtr(.symtab)) {
+ inline else => |shdr, class| {
+ const ent_size = @sizeOf(class.ElfN().Sym);
+
+ // `shdr.info` stores the index of the first global symbol. We will replace it with our
+ // new local symbol, and move the global symbol to a new index at the end of the symtab.
+ const target_index: Symbol.Index = @enumFromInt(elf.targetLoad(&shdr.info));
+
+ const old_size = elf.targetLoad(&shdr.size);
+ const new_size = old_size + ent_size;
+
+ assert(elf.symtab.items.len == @divExact(old_size, ent_size));
+
+ elf.targetStore(&shdr.info, @intFromEnum(target_index) + 1);
+ elf.targetStore(&shdr.size, new_size);
+
+ const new_index: Symbol.Index = @enumFromInt(elf.symtab.items.len);
+ elf.symtab.appendAssumeCapacity(undefined);
+
+ const target_sym = @field(elf.symPtr(target_index), @tagName(class));
+
+ if (target_index != new_index) {
+ // Move the global at `target_index` to `new_index`. First the symtab entry...
+ const new_sym = @field(elf.symPtr(new_index), @tagName(class));
+ new_sym.* = target_sym.*;
+ // ...then the `elf.symtab` metadata...
+ new_index.ptr(elf).* = target_index.ptr(elf).*;
+ // ...then update the `elf.globals` tracking.
+ const global_name: String(.strtab) = @enumFromInt(elf.targetLoad(&new_sym.name));
+ elf.globalByName(global_name).?.symtab_index = new_index;
+
+ if (target_index.ptr(elf).first_target_reloc != .none) {
+ // This symbol's index is changing, so queue an update of relocs targeting it.
+ elf.changed_symtab_index.putAssumeCapacity(global_name, {});
+ }
}
- pub fn get(s: Shndx, elf: *Elf) *Section {
- return &elf.shdrs.items[@intFromEnum(s)];
+ target_index.ptr(elf).* = .{
+ .node = opts.node,
+ .first_target_reloc = .none,
+ };
+
+ target_sym.* = .{
+ .name = @intFromEnum(opts.name),
+ .value = @intCast(opts.value),
+ .size = @intCast(opts.size),
+ .info = .{ .type = opts.type, .bind = .LOCAL },
+ .other = .{ .visibility = .DEFAULT },
+ .shndx = opts.shndx.toSection().?,
+ };
+ if (elf.targetEndian() != native_endian) {
+ std.mem.byteSwapAllFields(class.ElfN().Sym, target_sym);
}
- };
- pub fn shndx(si: Symbol.Index, elf: *Elf) Shndx {
- return .fromSection(switch (elf.symPtr(si)) {
- inline else => |sym| elf.targetLoad(&sym.shndx),
- });
- }
- pub const InitOptions = struct {
- name: []const u8 = "",
- lib_name: ?[]const u8 = null,
- value: u64 = 0,
- size: u64 = 0,
- type: std.elf.STT,
- bind: std.elf.STB = .LOCAL,
- visibility: std.elf.STV = .DEFAULT,
- shndx: Shndx = .UNDEF,
- };
- pub fn init(si: Symbol.Index, elf: *Elf, opts: InitOptions) !void {
- const comp = elf.base.comp;
- const gpa = comp.gpa;
- const target_endian = elf.targetEndian();
- const name_strtab_entry = try elf.string(.strtab, opts.name);
- switch (elf.shdrPtr(elf.si.symtab.shndx(elf))) {
- inline else => |shdr| {
- const old_size = elf.targetLoad(&shdr.size);
- const ent_size = elf.targetLoad(&shdr.entsize);
- const new_size = ent_size * elf.symtab.items.len;
- if (new_size > old_size) {
- elf.targetStore(&shdr.size, @intCast(new_size));
- const symtab_ni = elf.si.symtab.node(elf);
- _, const node_size = symtab_ni.location(&elf.mf).resolve(&elf.mf);
- if (new_size > node_size) try symtab_ni.resize(
- &elf.mf,
- gpa,
- new_size +| new_size / MappedFile.growth_factor,
- );
- }
+ return @enumFromInt(@intFromEnum(target_index));
+ },
+ }
+}
+
+const AddGlobalSymbolOptions = struct {
+ const Name = struct {
+ strtab: String(.strtab),
+ dynstr: String(.dynstr),
+ fn string(elf: *Elf, slice: []const u8) !Name {
+ return .{
+ .strtab = try elf.string(.strtab, slice),
+ .dynstr = switch (elf.shndx.dynsym) {
+ .UNDEF => .empty,
+ else => try elf.string(.dynstr, slice),
},
+ };
+ }
+ };
+
+ node: MappedFile.Node.Index,
+ name: Name,
+ lib_name: ?[]const u8 = null,
+ value: u64,
+ size: u64,
+ type: std.elf.STT,
+ bind: enum { strong, weak },
+ visibility: std.elf.STV,
+ shndx: Section.Index,
+};
+fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{MultipleDefinitions}!Symbol.Id {
+ _ = opts.lib_name; // TODO
+
+ if (elf.shndx.dynsym == .UNDEF) {
+ assert(opts.name.dynstr == .empty);
+ } else {
+ assert(std.mem.eql(u8, opts.name.dynstr.slice(elf), opts.name.strtab.slice(elf)));
+ }
+
+ // We break from this `switch` only if this symbol name did not previously exist at all and so
+ // we have added a new entry to one of the maps in `elf.globals`. In that case we actually need
+ // a new symtab entry.
+ const new_global_ptr: *Symbol.Global = if (opts.shndx != .UNDEF) switch (opts.bind) {
+ .strong => new_global: {
+ const gop = elf.globals.strong_def.getOrPutAssumeCapacity(opts.name.strtab);
+ if (gop.found_existing) return error.MultipleDefinitions;
+ const old_kv = elf.globals.weak_def.fetchSwapRemove(opts.name.strtab) orelse
+ elf.globals.strong_undef.fetchSwapRemove(opts.name.strtab) orelse
+ elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {
+ // The symbol did not already exist, so we'll use the "new global" path.
+ break :new_global gop.value_ptr;
+ };
+ gop.value_ptr.* = old_kv.value;
+ elf.setGlobalSymbolValue(opts.name.strtab, gop.value_ptr, .{
+ .node = opts.node,
+ .value = opts.value,
+ .size = opts.size,
+ .type = opts.type,
+ .shndx = opts.shndx,
+ });
+ elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);
+ return .global(opts.name.strtab);
+ },
+ .weak => new_global: {
+ if (elf.globals.strong_def.getPtr(opts.name.strtab)) |global| {
+ // The existing definition holds, we just merge our visibility in.
+ elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);
+ return .global(opts.name.strtab);
}
- switch (elf.symPtr(si)) {
- inline else => |sym, class| {
+ const gop = elf.globals.weak_def.getOrPutAssumeCapacity(opts.name.strtab);
+ if (gop.found_existing) {
+ // The existing definition holds, we just merge our visibility in.
+ elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);
+ return .global(opts.name.strtab);
+ }
+ const old_kv = elf.globals.strong_undef.fetchSwapRemove(opts.name.strtab) orelse
+ elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {
+ // The symbol did not already exist, so we'll use the "new global" path.
+ break :new_global gop.value_ptr;
+ };
+ gop.value_ptr.* = old_kv.value;
+ elf.setGlobalSymbolValue(opts.name.strtab, gop.value_ptr, .{
+ .node = opts.node,
+ .value = opts.value,
+ .size = opts.size,
+ .type = opts.type,
+ .shndx = opts.shndx,
+ });
+ elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);
+ return .global(opts.name.strtab);
+ },
+ } else switch (opts.bind) {
+ .strong => new_global: {
+ if (elf.globals.strong_def.getPtr(opts.name.strtab)) |global| {
+ // The existing definition holds, we just merge our visibility in.
+ elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);
+ return .global(opts.name.strtab);
+ }
+ if (elf.globals.weak_def.getPtr(opts.name.strtab)) |global| {
+ // The existing definition holds, we just merge our visibility in.
+ elf.mergeGlobalSymbolVisibility(global, opts.visibility, .weak);
+ return .global(opts.name.strtab);
+ }
+ const gop = elf.globals.strong_undef.getOrPutAssumeCapacity(opts.name.strtab);
+ if (gop.found_existing) {
+ // The existing symbol is okay, we just merge our visibility in.
+ elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);
+ return .global(opts.name.strtab);
+ }
+ const old_kv = elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {
+ // The symbol did not already exist, so we'll use the "new global" path.
+ break :new_global gop.value_ptr;
+ };
+ gop.value_ptr.* = old_kv.value;
+ elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);
+ return .global(opts.name.strtab);
+ },
+ .weak => new_global: {
+ if (elf.globals.strong_def.getPtr(opts.name.strtab) orelse
+ elf.globals.strong_undef.getPtr(opts.name.strtab)) |global|
+ {
+ // The existing symbol is okay, we just merge our visibility in.
+ elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);
+ return .global(opts.name.strtab);
+ }
+ if (elf.globals.weak_def.getPtr(opts.name.strtab)) |global| {
+ // The existing symbol is okay, we just merge our visibility in.
+ elf.mergeGlobalSymbolVisibility(global, opts.visibility, .weak);
+ return .global(opts.name.strtab);
+ }
+ const gop = elf.globals.weak_undef.getOrPutAssumeCapacity(opts.name.strtab);
+ if (gop.found_existing) {
+ // The existing symbol is okay, we just merge our visibility in.
+ elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);
+ return .global(opts.name.strtab);
+ }
+ break :new_global gop.value_ptr;
+ },
+ };
+
+ const force_local_bind: bool = switch (opts.visibility) {
+ .HIDDEN, .INTERNAL => elf.ehdrField(.type) != .REL,
+ .PROTECTED, .DEFAULT => false,
+ };
+
+ const bind: std.elf.STB = if (force_local_bind) b: {
+ break :b .LOCAL;
+ } else switch (opts.bind) {
+ .strong => .GLOBAL,
+ .weak => .WEAK,
+ };
+
+ const sym_index: Symbol.Index = @enumFromInt(elf.symtab.items.len);
+ elf.symtab.appendAssumeCapacity(.{
+ .node = opts.node,
+ .first_target_reloc = .none,
+ });
+ switch (elf.shdrPtr(.symtab)) {
+ inline else => |shdr, class| {
+ const Sym = class.ElfN().Sym;
+ // Increase the symtab size...
+ const old_size = elf.targetLoad(&shdr.size);
+ assert(old_size == @intFromEnum(sym_index) * @sizeOf(Sym));
+ elf.targetStore(&shdr.size, old_size + @sizeOf(Sym));
+ // ...then populate the newly-valid symbol pointer
+ const sym = @field(elf.symPtr(sym_index), @tagName(class));
+ sym.* = .{
+ .name = @intFromEnum(opts.name.strtab),
+ .value = @intCast(opts.value),
+ .size = @intCast(opts.size),
+ .info = .{ .type = opts.type, .bind = bind },
+ .other = .{ .visibility = opts.visibility },
+ .shndx = opts.shndx.toSection().?,
+ };
+ if (elf.targetEndian() != native_endian) {
+ std.mem.byteSwapAllFields(Sym, sym);
+ }
+ },
+ }
+
+ const old_head: String(.strtab) = old_head: {
+ if (opts.node == .none) break :old_head .empty;
+ const gop = elf.node_global_symbols.getOrPutAssumeCapacity(opts.node);
+ const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;
+ gop.value_ptr.* = opts.name.strtab;
+ break :old_head old_head;
+ };
+
+ new_global_ptr.* = .{
+ .symtab_index = sym_index,
+ .dynsym_index = dynsym_index: {
+ if (elf.shndx.dynsym == .UNDEF) break :dynsym_index 0;
+ if (force_local_bind) break :dynsym_index 0;
+ switch (elf.shdrPtr(elf.shndx.dynsym)) {
+ inline else => |shdr, class| {
const Sym = class.ElfN().Sym;
+ // Increase the dynamic symbol table size...
+ const old_size = elf.targetLoad(&shdr.size);
+ elf.targetStore(&shdr.size, old_size + @sizeOf(Sym));
+ const dynsym_index: u32 = @intCast(@divExact(old_size, @sizeOf(Sym)));
+ // ...then populate the newly-valid symbol pointer
+ const sym = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
sym.* = .{
- .name = name_strtab_entry,
+ .name = @intFromEnum(opts.name.dynstr),
.value = @intCast(opts.value),
.size = @intCast(opts.size),
- .info = .{ .type = opts.type, .bind = opts.bind },
+ .info = .{ .type = opts.type, .bind = bind },
.other = .{ .visibility = opts.visibility },
.shndx = opts.shndx.toSection().?,
};
- if (target_endian != native_endian) std.mem.byteSwapAllFields(Sym, sym);
+ if (elf.targetEndian() != native_endian) {
+ std.mem.byteSwapAllFields(Sym, sym);
+ }
+ break :dynsym_index dynsym_index;
},
}
- switch (elf.shdrPtr(elf.si.symtab.shndx(elf))) {
- inline else => |shdr| elf.targetStore(&shdr.info, @max(
- elf.targetLoad(&shdr.info),
- @intFromEnum(si) + 1,
- )),
- }
+ },
+ .prev_in_node = .empty,
+ .next_in_node = old_head,
+ };
- if (opts.bind == .LOCAL) return;
- no_entry: {
- if (std.mem.eql(u8, opts.name, entry: switch (elf.options.entry) {
- .default => switch (comp.config.output_mode) {
- .Exe => continue :entry .enabled,
- .Lib, .Obj => continue :entry .disabled,
- },
- .disabled => break :no_entry,
- .enabled => "_start",
- .named => |named| named,
- })) {
- elf.si.entry = si;
- switch (elf.ehdrPtr()) {
- inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(opts.value)),
- }
- }
+ if (old_head != .empty) {
+ const old_head_ptr = elf.globalByName(old_head).?;
+ assert(old_head_ptr.symtab_index.ptr(elf).node == opts.node);
+ assert(old_head_ptr.prev_in_node == .empty);
+ old_head_ptr.prev_in_node = opts.name.strtab;
+ }
+
+ if (force_local_bind) {
+ elf.moveDemotedGlobal(new_global_ptr);
+ }
+
+ if (new_global_ptr.dynsym_index != 0 and
+ opts.visibility == .DEFAULT and
+ opts.shndx == .UNDEF and
+ opts.type == .FUNC)
+ {
+ // We're adding an undefined global STT_FUNC symbol which could be resolved by another DSO.
+ // We therefore might need a PLT entry, so let's add one now. TODO: it'd be good to remove
+ // the PLT entry if we later discover a link inpu which resolves this reference.
+ elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index);
+ }
+
+ return .global(opts.name.strtab);
+}
+fn setGlobalSymbolValue(
+ elf: *Elf,
+ global_name: String(.strtab),
+ global_ptr: *Symbol.Global,
+ new: struct {
+ node: MappedFile.Node.Index,
+ value: u64,
+ size: u64,
+ type: std.elf.STT,
+ shndx: Section.Index,
+ },
+) void {
+ const old_node = global_ptr.symtab_index.ptr(elf).node;
+ if (old_node != .none) {
+ if (global_ptr.next_in_node != .empty) {
+ const next = elf.globalByName(global_ptr.next_in_node).?;
+ assert(next.prev_in_node == global_name);
+ assert(next.symtab_index.ptr(elf).node == old_node);
+ next.prev_in_node = global_ptr.prev_in_node;
+ }
+ if (global_ptr.prev_in_node != .empty) {
+ const prev = elf.globalByName(global_ptr.prev_in_node).?;
+ assert(prev.next_in_node == global_name);
+ assert(prev.symtab_index.ptr(elf).node == old_node);
+ prev.next_in_node = global_ptr.next_in_node;
+ } else {
+ // We're the start of the linked list, so we need to change the head.
+ if (global_ptr.next_in_node == .empty) {
+ assert(elf.node_global_symbols.fetchSwapRemove(old_node).?.value == global_name);
+ } else {
+ elf.node_global_symbols.getPtr(old_node).?.* = global_ptr.next_in_node;
}
+ }
+ } else {
+ assert(global_ptr.next_in_node == .empty);
+ assert(global_ptr.prev_in_node == .empty);
+ }
- if (elf.si.dynsym == .null) return;
- const dsi = elf.dynsym.count();
- try elf.dynsym.putNoClobber(gpa, si, {});
- const name_dynstr_entry = try elf.string(.dynstr, opts.name);
- switch (elf.shdrPtr(elf.si.dynsym.shndx(elf))) {
- inline else => |shdr| {
- const old_size = elf.targetLoad(&shdr.size);
- const ent_size = elf.targetLoad(&shdr.entsize);
- const new_size = ent_size * elf.dynsym.count();
- if (new_size > old_size) {
- elf.targetStore(&shdr.size, @intCast(new_size));
- const dynsym_ni = elf.si.dynsym.node(elf);
- _, const node_size = dynsym_ni.location(&elf.mf).resolve(&elf.mf);
- if (new_size > node_size) try dynsym_ni.resize(
- &elf.mf,
- gpa,
- new_size +| new_size / MappedFile.growth_factor,
- );
- }
+ global_ptr.symtab_index.ptr(elf).node = new.node;
+
+ const old_head: String(.strtab) = old_head: {
+ if (new.node == .none) break :old_head .empty;
+ const gop = elf.node_global_symbols.getOrPutAssumeCapacity(new.node);
+ const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;
+ gop.value_ptr.* = global_name;
+ break :old_head old_head;
+ };
+
+ global_ptr.prev_in_node = .empty;
+ global_ptr.next_in_node = old_head;
+
+ if (old_head != .empty) {
+ const old_head_ptr = elf.globalByName(old_head).?;
+ assert(old_head_ptr.symtab_index.ptr(elf).node == new.node);
+ assert(old_head_ptr.prev_in_node == .empty);
+ old_head_ptr.prev_in_node = global_name;
+ }
+
+ // Now for the easy bit where we actually update the symtab entry.
+ switch (elf.symPtr(global_ptr.symtab_index)) {
+ inline else => |sym| {
+ // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.
+ elf.targetStore(&sym.size, @intCast(new.size));
+ elf.targetStore(&sym.shndx, new.shndx.toSection().?);
+ const old_bind = elf.targetLoad(&sym.info).bind;
+ elf.targetStore(&sym.info, .{
+ .type = new.type,
+ .bind = old_bind,
+ });
+ },
+ }
+
+ // ...and also the dynsym entry if there is one.
+ if (global_ptr.dynsym_index != 0) switch (elf.dynsymPtr(global_ptr.dynsym_index)) {
+ inline else => |sym| {
+ // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.
+ elf.targetStore(&sym.size, @intCast(new.size));
+ elf.targetStore(&sym.shndx, new.shndx.toSection().?);
+ const old_bind = elf.targetLoad(&sym.info).bind;
+ elf.targetStore(&sym.info, .{
+ .type = new.type,
+ .bind = old_bind,
+ });
+ },
+ };
+
+ global_ptr.flushMoved(elf, new.value);
+}
+/// When the same global symbol appears in two inputs---even if one symbol is defined and the other
+/// undefined---their visibility values are combined to determine the resulting visibility, which
+/// can also affect the bind of the symbol we output.
+fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visibility: std.elf.STV, bind: enum { strong, weak }) void {
+ const old_visibility: std.elf.STV = switch (elf.symPtr(global_ptr.symtab_index)) {
+ inline else => |sym| elf.targetLoad(&sym.other).visibility,
+ };
+ // The combined visibility is essentially the "strictest" of the two, with most strict being
+ // INTERNAL, followed by HIDDEN, PROTECTED, DEFAULT.
+ const new_visibility: std.elf.STV, const newly_hidden: bool = switch (old_visibility) {
+ .INTERNAL => .{ .INTERNAL, false },
+ .HIDDEN => switch (other_visibility) {
+ .INTERNAL => .{ .INTERNAL, false },
+ .HIDDEN, .PROTECTED, .DEFAULT => .{ .HIDDEN, false },
+ },
+ .PROTECTED => switch (other_visibility) {
+ .INTERNAL => .{ .INTERNAL, true },
+ .HIDDEN => .{ .HIDDEN, true },
+ .PROTECTED, .DEFAULT => .{ .PROTECTED, false },
+ },
+ .DEFAULT => switch (other_visibility) {
+ .INTERNAL => .{ .INTERNAL, true },
+ .HIDDEN => .{ .HIDDEN, true },
+ .PROTECTED => .{ .PROTECTED, false },
+ .DEFAULT => .{ .DEFAULT, false },
+ },
+ };
+ // If the symbol is HIDDEN/INTERNAL and we're emitting an ELF module (executable or shared
+ // object), then the symbol should have binding STB_LOCAL in the output. Therefore, if we are
+ // putting the global in this state for the first time---let's call it "demoting" the global to
+ // STB_LOCAL---we need to update its bind in the symtab.
+ const demote_to_local = newly_hidden and elf.ehdrField(.type) != .REL;
+ switch (elf.symPtr(global_ptr.symtab_index)) {
+ inline else => |sym, class| {
+ const old_info = elf.targetLoad(&sym.info);
+ const new_info: class.ElfN().Sym.Info = .{
+ .type = old_info.type,
+ .bind = if (demote_to_local) b: {
+ assert(old_info.bind != .LOCAL);
+ break :b .LOCAL;
+ } else if (old_info.bind == .LOCAL) .LOCAL else switch (bind) {
+ .strong => .GLOBAL,
+ .weak => .WEAK,
},
+ };
+ elf.targetStore(&sym.other, .{ .visibility = new_visibility });
+ elf.targetStore(&sym.info, new_info);
+ // also update dynsym
+ if (global_ptr.dynsym_index != 0) {
+ const dynsym = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));
+ elf.targetStore(&dynsym.other, .{ .visibility = new_visibility });
+ elf.targetStore(&dynsym.info, new_info);
}
- switch (elf.dynsymSlice()) {
- inline else => |dynsyms, class| {
- const Sym = class.ElfN().Sym;
- const dynsym = &dynsyms[dsi];
- dynsym.* = .{
- .name = name_dynstr_entry,
- .value = @intCast(opts.value),
- .size = @intCast(opts.size),
- .info = .{ .type = opts.type, .bind = opts.bind },
- .other = .{ .visibility = opts.visibility },
- .shndx = opts.shndx.toSection().?,
- };
- if (target_endian != native_endian) std.mem.byteSwapAllFields(Sym, dynsym);
- },
+ },
+ }
+ if (demote_to_local) {
+ // When demoting a global to STB_LOCAL, we need to move its symtab index so that it is with
+ // the STB_LOCAL symbols instead of the global symbols.
+ elf.moveDemotedGlobal(global_ptr);
+ }
+}
+/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),
+/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF
+/// requires that all STB_LOCAL symbols in a symbol table appear before any global symbols.
+fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
+ assert(elf.ehdrField(.type) != .REL); // demotion only happens when emitting an ELF module
+ switch (elf.shdrPtr(.symtab)) {
+ inline else => |shdr, class| {
+ // `shdr.info` stores the index of the first global symbol. We are going to swap the
+ // demoted symbol with that first global symbol, then increment that start index.
+ const dest_index: Symbol.Index = @enumFromInt(elf.targetLoad(&shdr.info));
+ const src_index = global_ptr.symtab_index;
+
+ // This global should currently be in the "global symbols" part of the symtab, since our
+ // job is to move it *out* of that part:
+ assert(@intFromEnum(src_index) >= @intFromEnum(dest_index));
+
+ elf.targetStore(&shdr.info, @intFromEnum(dest_index) + 1);
+
+ if (src_index == dest_index) {
+ // The demoted global was already the first global, so we don't need to do any swap.
+ return;
}
- if (opts.type != .FUNC or opts.shndx != .UNDEF) return;
- const plt_index: u32 = @intCast(elf.got.plt.count());
- try elf.got.plt.putNoClobber(gpa, si, {});
- switch (elf.ehdrField(.machine)) {
- else => |machine| @panic(@tagName(machine)),
- .X86_64 => {
- const plt_ni = elf.si.plt.node(elf);
- _, const plt_node_size = plt_ni.location(&elf.mf).resolve(&elf.mf);
- const plt_addr = plt_addr: switch (elf.shdrPtr(elf.si.plt.shndx(elf))) {
- inline else => |shdr| {
- const old_size = 16 * (1 + plt_index);
- const new_size = old_size + 16;
- elf.targetStore(&shdr.size, new_size);
- if (new_size > plt_node_size) try plt_ni.resize(
- &elf.mf,
- gpa,
- new_size +| new_size / MappedFile.growth_factor,
- );
- const plt_slice = plt_ni.slice(&elf.mf)[old_size..new_size];
- @memcpy(plt_slice, &[16]u8{
- 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
- 0x68, 0x00, 0x00, 0x00, 0x00, // push $0x0
- 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp 0
- 0x66, 0x90, // xchg %ax,%ax
- });
- std.mem.writeInt(u32, plt_slice[5..][0..4], plt_index, target_endian);
- std.mem.writeInt(
- i32,
- plt_slice[10..][0..4],
- 2 - @as(i32, @intCast(new_size)),
- target_endian,
- );
- break :plt_addr elf.targetLoad(&shdr.addr) + old_size;
- },
- };
+ const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
+ const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));
- const got_plt_shndx = elf.si.got_plt.shndx(elf);
- const got_plt_ni = elf.si.got_plt.node(elf);
- _, const got_plt_node_size = got_plt_ni.location(&elf.mf).resolve(&elf.mf);
- const got_plt_addr = got_plt_addr: switch (elf.shdrPtr(got_plt_shndx)) {
- inline else => |shdr, class| {
- const Addr = class.ElfN().Addr;
- const addr_size = @sizeOf(Addr);
- const old_size = addr_size * (3 + plt_index);
- const new_size = old_size + addr_size;
- elf.targetStore(&shdr.size, new_size);
- if (new_size > got_plt_node_size) try got_plt_ni.resize(
- &elf.mf,
- gpa,
- new_size +| new_size / MappedFile.growth_factor,
- );
- std.mem.writeInt(
- Addr,
- got_plt_ni.slice(&elf.mf)[old_size..][0..addr_size],
- @intCast(plt_addr),
- target_endian,
- );
- break :got_plt_addr elf.targetLoad(&shdr.addr) + old_size;
- },
- };
+ const this_name: String(.strtab) = @enumFromInt(elf.targetLoad(&src_sym_ptr.name));
+ assert(elf.globalByName(this_name).? == global_ptr);
+ if (global_ptr.symtab_index.ptr(elf).first_target_reloc != .none) {
+ // This symbol's index is changing, so queue an update of relocs targeting it.
+ elf.changed_symtab_index.putAssumeCapacity(this_name, {});
+ }
+
+ const other_name: String(.strtab) = @enumFromInt(elf.targetLoad(&dest_sym_ptr.name));
+ const other_global_ptr = elf.globalByName(other_name).?;
+ assert(other_global_ptr.symtab_index == dest_index);
+ if (other_global_ptr.symtab_index.ptr(elf).first_target_reloc != .none) {
+ // This other symbol's index is changing, so queue an update of relocs targeting it.
+ elf.changed_symtab_index.putAssumeCapacity(other_name, {});
+ }
+
+ // First swap the symtab entries...
+ std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr);
+ // ...then the `elf.symtab` metadata...
+ std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));
+ // ...then update the `elf.globals` tracking.
+ global_ptr.symtab_index = dest_index;
+ other_global_ptr.symtab_index = src_index;
+
+ // We also need to get rid of the dynsym entry if there is one. For simplicity, just
+ // replace it with a dummy entry which will never be used and will not cause problems.
+ // TODO: we should have a free-list of dynsym slots so that other symbols can go here.
+ // TODO: it would also be best to just avoid having gaps in the dynsym altogether.
+ if (global_ptr.dynsym_index != 0) {
+ const dynsym = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));
+ dynsym.* = .{
+ .name = @intFromEnum(String(.dynstr).empty),
+ .value = 0,
+ .size = 0,
+ .info = .{
+ .type = .NOTYPE,
+ // STB_WEAK is important: we mustn't cause a dynamic linker error if the
+ // symbol can't be resolved.
+ .bind = .WEAK,
+ },
+ // SHN_UNDEF is important: we mustn't define this symbol for other DSOs.
+ .shndx = std.elf.SHN_UNDEF,
+ .other = .{ .visibility = .DEFAULT },
+ };
+ if (elf.targetEndian() != native_endian) {
+ std.mem.byteSwapAllFields(class.ElfN().Sym, dynsym);
+ }
+ }
+ },
+ }
+}
+fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {
+ const target_endian = elf.targetEndian();
+ const plt_index: u32 = @intCast(elf.got.plt.count());
+ elf.got.plt.putAssumeCapacityNoClobber(.global(global_name), {});
+ switch (elf.ehdrField(.machine)) {
+ else => |machine| @panic(@tagName(machine)),
+ .X86_64 => {
+ const plt_ni = elf.shndx.plt.get(elf).ni;
+ const plt_addr = plt_addr: switch (elf.shdrPtr(elf.shndx.plt)) {
+ inline else => |shdr| {
+ const old_size = 16 * (1 + plt_index);
+ assert(elf.targetLoad(&shdr.size) == old_size);
+ elf.targetStore(&shdr.size, old_size + 16);
+ const plt_slice = plt_ni.slice(&elf.mf)[old_size..][0..16];
+ @memcpy(plt_slice, &[16]u8{
+ 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
+ 0x68, 0x00, 0x00, 0x00, 0x00, // push $0x0
+ 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp 0
+ 0x66, 0x90, // xchg %ax,%ax
+ });
+ std.mem.writeInt(u32, plt_slice[5..][0..4], plt_index, target_endian);
+ std.mem.writeInt(
+ i32,
+ plt_slice[10..][0..4],
+ -@as(i32, @intCast(old_size + 14)),
+ target_endian,
+ );
+ break :plt_addr elf.targetLoad(&shdr.addr) + old_size;
+ },
+ };
+
+ const got_plt_shndx = elf.shndx.got_plt;
+ const got_plt_ni = elf.shndx.got_plt.get(elf).ni;
+ const got_plt_addr = got_plt_addr: switch (elf.shdrPtr(got_plt_shndx)) {
+ inline else => |shdr, class| {
+ const ent_size = @sizeOf(class.ElfN().Addr);
+ const old_size = ent_size * (3 + plt_index);
+ elf.targetStore(&shdr.size, old_size + ent_size);
+ std.mem.writeInt(
+ class.ElfN().Addr,
+ got_plt_ni.slice(&elf.mf)[old_size..][0..ent_size],
+ @intCast(plt_addr),
+ target_endian,
+ );
+ break :got_plt_addr elf.targetLoad(&shdr.addr) + old_size;
+ },
+ };
- const plt_sec_ni = elf.si.plt_sec.node(elf);
- _, const plt_sec_node_size = plt_sec_ni.location(&elf.mf).resolve(&elf.mf);
- switch (elf.shdrPtr(elf.si.plt_sec.shndx(elf))) {
- inline else => |shdr| {
- const old_size = 16 * plt_index;
- const new_size = old_size + 16;
- elf.targetStore(&shdr.size, new_size);
- if (new_size > plt_sec_node_size) try plt_sec_ni.resize(
- &elf.mf,
- gpa,
- new_size +| new_size / MappedFile.growth_factor,
- );
- const plt_sec_slice = plt_sec_ni.slice(&elf.mf)[old_size..new_size];
- @memcpy(plt_sec_slice, &[16]u8{
- 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
- 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip)
- 0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00, // nopw 0x0(%rax,%rax,1)
- });
- std.mem.writeInt(
- i32,
- plt_sec_slice[6..][0..4],
- @intCast(@as(i64, @bitCast(
- got_plt_addr -% (elf.targetLoad(&shdr.addr) + old_size + 10),
- ))),
- target_endian,
- );
- },
- }
+ const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni;
+ switch (elf.shdrPtr(elf.shndx.plt_sec)) {
+ inline else => |shdr| {
+ const old_size = 16 * plt_index;
+ elf.targetStore(&shdr.size, old_size + 16);
+ const plt_sec_slice = plt_sec_ni.slice(&elf.mf)[old_size..][0..16];
+ @memcpy(plt_sec_slice, &[16]u8{
+ 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
+ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip)
+ 0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00, // nopw 0x0(%rax,%rax,1)
+ });
+ std.mem.writeInt(
+ i32,
+ plt_sec_slice[6..][0..4],
+ @intCast(@as(i64, @bitCast(
+ got_plt_addr -% (elf.targetLoad(&shdr.addr) + old_size + 10),
+ ))),
+ target_endian,
+ );
+ },
+ }
- const rela_plt_si = got_plt_shndx.get(elf).rela_si;
- const rela_plt_ni = rela_plt_si.node(elf);
- _, const rela_plt_node_size = rela_plt_ni.location(&elf.mf).resolve(&elf.mf);
- switch (elf.shdrPtr(rela_plt_si.shndx(elf))) {
- inline else => |shdr, class| {
- const Rela = class.ElfN().Rela;
- const rela_size = elf.targetLoad(&shdr.entsize);
- const old_size = rela_size * plt_index;
- const new_size = old_size + rela_size;
- elf.targetStore(&shdr.size, new_size);
- if (new_size > rela_plt_node_size) try rela_plt_ni.resize(
- &elf.mf,
- gpa,
- new_size +| new_size / MappedFile.growth_factor,
- );
- const rela: *Rela = @ptrCast(@alignCast(
- rela_plt_ni.slice(&elf.mf)[@intCast(old_size)..@intCast(new_size)],
- ));
- rela.* = .{
- .offset = @intCast(got_plt_addr),
- .info = .{
- .type = @intFromEnum(std.elf.R_X86_64.JUMP_SLOT),
- .sym = @intCast(dsi),
- },
- .addend = 0,
- };
- if (target_endian != native_endian) std.mem.byteSwapAllFields(Rela, rela);
+ const rela_plt_shndx = got_plt_shndx.get(elf).rela_shndx;
+ const rela_plt_ni = rela_plt_shndx.get(elf).ni;
+ switch (elf.shdrPtr(rela_plt_shndx)) {
+ inline else => |shdr, class| {
+ const Rela = class.ElfN().Rela;
+ const rela_size = elf.targetLoad(&shdr.entsize);
+ const old_size = rela_size * plt_index;
+ const new_size = old_size + rela_size;
+ elf.targetStore(&shdr.size, new_size);
+ const rela: *Rela = @ptrCast(@alignCast(
+ rela_plt_ni.slice(&elf.mf)[@intCast(old_size)..@intCast(new_size)],
+ ));
+ rela.* = .{
+ .offset = @intCast(got_plt_addr),
+ .info = .{
+ .type = @intFromEnum(std.elf.R_X86_64.JUMP_SLOT),
+ .sym = @intCast(dynsym_index),
},
- }
- try rela_plt_ni.resized(gpa, &elf.mf);
+ .addend = 0,
+ };
+ if (target_endian != native_endian) std.mem.byteSwapAllFields(Rela, rela);
},
}
- }
+ },
+ }
+}
- pub fn flushMoved(si: Symbol.Index, elf: *Elf, value: u64) void {
- switch (elf.symPtr(si)) {
- inline else => |sym, class| {
- elf.targetStore(&sym.value, @intCast(value));
- if (si == elf.si.entry) {
- @branchHint(.unlikely);
- @field(elf.ehdrPtr(), @tagName(class)).entry = sym.value;
- }
- },
+const Symbol = struct {
+ /// The node which this symbol's value is defined relative to. Possible values are:
+ /// * `.none` for a SHN_ABS or SHN_UNDEF symbol
+ /// * A section (the symbol's value is that section's vaddr)
+ /// * An input section (the symbol's value is some vaddr in that input section)
+ /// * A NAV, UAV, or lazy code/data (the symbol's value is exactly the vaddr of that node)
+ node: MappedFile.Node.Index,
+
+ /// The head of a linked list of relocations targeting this symbol.
+ first_target_reloc: Reloc.Index,
+
+ const Global = struct {
+ /// The current index of the symtab entry for this global symbol.
+ symtab_index: Symbol.Index,
+ /// The current index of the dynsym entry for this global symbol. If the global has been
+ /// demoted to STB_LOCAL, it does not have a dynsym entry and this field is set to 0.
+ dynsym_index: u32,
+
+ /// The next entry in a linked list of global symbols with the same `Symbol.node` value.
+ ///
+ /// If `node` is `.none`, this is `.empty`.
+ next_in_node: String(.strtab),
+ /// The previous entry in a linked list of global symbols with the same `Symbol.node` value.
+ ///
+ /// If `node` is `.none`, this is `.empty`.
+ prev_in_node: String(.strtab),
+
+ /// Like `Symbol.Index.flushMoved`, but also updates the dynamic symbol table if necessary.
+ fn flushMoved(g: *const Global, elf: *Elf, value: u64) void {
+ g.symtab_index.flushMoved(elf, value);
+ if (g.dynsym_index != 0) {
+ switch (elf.dynsymPtr(g.dynsym_index)) {
+ inline else => |sym| elf.targetStore(&sym.value, @intCast(value)),
+ }
}
- si.applyLocationRelocs(elf);
- si.applyTargetRelocs(elf);
}
+ };
+
+ /// An index directly into the symtab. These values are not stable (global symbols are sometimes
+ /// moved to new locations in the symtab) and therefore should only be used ephemerally.
+ ///
+ /// Local symbols *do* have stable indices into the symtab; see `LocalIndex`.
+ ///
+ /// For a stable reference to an arbitrary symbol, see `Id`.
+ const Index = enum(u32) {
+ null = 0,
+ _,
- pub fn applyLocationRelocs(si: Symbol.Index, elf: *Elf) void {
- if (elf.ehdrField(.type) == .REL) return;
- switch (si.get(elf).loc_relocs) {
- .none => {},
- else => |loc_relocs| for (elf.relocs.items[@intFromEnum(loc_relocs)..]) |*reloc| {
- if (reloc.loc != si) break;
+ fn flushMoved(si: Symbol.Index, elf: *Elf, value: u64) void {
+ switch (elf.symPtr(si)) {
+ inline else => |sym| elf.targetStore(&sym.value, @intCast(value)),
+ }
+ if (elf.ehdrField(.type) != .REL) {
+ var ri = si.ptr(elf).first_target_reloc;
+ while (ri != .none) {
+ const reloc = ri.get(elf);
+ assert(reloc.target.index(elf) == si);
reloc.apply(elf);
- },
+ ri = reloc.next;
+ }
}
}
+ fn ptr(si: Symbol.Index, elf: *Elf) *Symbol {
+ return &elf.symtab.items[@intFromEnum(si)];
+ }
+ };
- pub fn applyTargetRelocs(si: Symbol.Index, elf: *Elf) void {
- if (elf.ehdrField(.type) == .REL) return;
- var ri = si.get(elf).target_relocs;
- while (ri != .none) {
- const reloc = ri.get(elf);
- assert(reloc.target == si);
- reloc.apply(elf);
- ri = reloc.next;
- }
+ /// A `LocalIndex` is a raw index into the symtab like `Index`, but it guarantees that the
+ /// symbol in question has STB_LOCAL binding, which guarantees that its symtab index is stable
+ /// so can be stored long-term without needing to be updated
+ ///
+ /// This is because symbols which have STB_LOCAL binding in the output file gain fixed symtab
+ /// indices, thanks to a combination of a few factors:
+ /// * We never remove STB_LOCAL symbols
+ /// * There is no symbol ordering requirement *within* the leading range of STB_LOCAL symbols
+ /// * A symbol visibility which demotes a global to STB_LOCAL binding can never be reverted by
+ /// a subsequent operation (different visibilities resolve to the "strictest" one)
+ const LocalIndex = enum(u32) {
+ null = 0,
+ _,
+
+ fn index(li: LocalIndex) Index {
+ return @enumFromInt(@intFromEnum(li));
}
+ };
- pub fn deleteLocationRelocs(si: Symbol.Index, elf: *Elf) void {
- const sym = si.get(elf);
- for (elf.relocs.items[@intFromEnum(sym.loc_relocs)..]) |*reloc| {
- if (reloc.loc != si) break;
- reloc.delete(elf);
- }
- sym.loc_relocs = .none;
+ /// Opaque, stable identifier for a symbol. Does not necessarily equal the index into the symtab.
+ const Id = packed struct(u32) {
+ kind: enum(u1) { local, global },
+ raw: u31,
+
+ const @"null": Symbol.Id = .local(.null);
+
+ fn local(lsi: Symbol.LocalIndex) Symbol.Id {
+ return .{ .kind = .local, .raw = @intCast(@intFromEnum(lsi)) };
+ }
+ fn global(name: String(.strtab)) Symbol.Id {
+ return .{ .kind = .global, .raw = @intCast(@intFromEnum(name)) };
+ }
+ fn unwrap(s: Symbol.Id) union(enum) {
+ local: Symbol.LocalIndex,
+ global: String(.strtab),
+ } {
+ return switch (s.kind) {
+ .local => .{ .local = @enumFromInt(s.raw) },
+ .global => .{ .global = @enumFromInt(s.raw) },
+ };
+ }
+
+ fn toTypeErased(s: Symbol.Id) link.File.SymbolId {
+ return @enumFromInt(@as(u32, @bitCast(s)));
+ }
+ fn fromTypeErased(s: link.File.SymbolId) Symbol.Id {
+ return @bitCast(@intFromEnum(s));
+ }
+
+ fn index(s: Symbol.Id, elf: *const Elf) Symbol.Index {
+ return switch (s.unwrap()) {
+ .local => |lsi| lsi.index(),
+ .global => |name| elf.globalByName(name).?.symtab_index,
+ };
+ }
+
+ fn value(s: Symbol.Id, elf: *Elf) u64 {
+ return switch (elf.symPtr(s.index(elf))) {
+ inline else => |sym| elf.targetLoad(&sym.value),
+ };
+ }
+
+ /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
+ /// some point due to a call to `flushMoved`.
+ fn hasMoved(s: Symbol.Id, elf: *Elf) bool {
+ const node = s.index(elf).ptr(elf).node;
+ if (node == .none) return false;
+ return node.hasMoved(&elf.mf);
}
};
+};
- pub const Known = struct {
- comptime symtab: Symbol.Index = .symtab,
- comptime shstrtab: Symbol.Index = .shstrtab,
- comptime strtab: Symbol.Index = .strtab,
- comptime rodata: Symbol.Index = .rodata,
- comptime text: Symbol.Index = .text,
- comptime data: Symbol.Index = .data,
- comptime data_rel_ro: Symbol.Index = .data_rel_ro,
- comptime got: Symbol.Index = .got,
- comptime got_plt: Symbol.Index = .got_plt,
- comptime plt: Symbol.Index = .plt,
- comptime plt_sec: Symbol.Index = .plt_sec,
- dynsym: Symbol.Index,
- dynstr: Symbol.Index,
- dynamic: Symbol.Index,
- tdata: Symbol.Index,
- entry: Symbol.Index,
+fn globalByName(elf: *const Elf, name: String(.strtab)) ?*Symbol.Global {
+ if (elf.globals.strong_def.getPtr(name)) |ptr| return ptr;
+ if (elf.globals.weak_def.getPtr(name)) |ptr| return ptr;
+ if (elf.globals.strong_undef.getPtr(name)) |ptr| return ptr;
+ if (elf.globals.weak_undef.getPtr(name)) |ptr| return ptr;
+ return null;
+}
+
+pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
+ const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) {
+ .file,
+ .ehdr,
+ .shdr,
+ .segment,
+ .section,
+ .input_section,
+ => unreachable,
+
+ inline .nav,
+ .uav,
+ .lazy_code,
+ .lazy_const_data,
+ => |i| i.symbol(elf),
};
+ const s: Symbol.Id = .local(lsi);
+ return s.toTypeErased();
+}
+pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !link.File.SymbolId {
+ const gpa = elf.base.comp.gpa;
- comptime {
- if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Symbol) == 16);
+ try elf.ensureUnusedSymbolCapacity(1, .all_local);
+ try elf.nodes.ensureUnusedCapacity(gpa, 1);
+ try elf.lazy.getPtr(lazy.kind).map.ensureUnusedCapacity(gpa, 1);
+
+ const gop = elf.lazy.getPtr(lazy.kind).map.getOrPutAssumeCapacity(lazy.ty);
+ if (!gop.found_existing) {
+ const shndx: Section.Index, const sym_type: std.elf.STT = switch (lazy.kind) {
+ .code => .{ .text, .FUNC },
+ .const_data => .{ .rodata, .OBJECT },
+ };
+ const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{});
+ var name_buf: [64]u8 = undefined;
+ const name = std.fmt.bufPrint(
+ &name_buf,
+ "__lazy_{t}_{d}",
+ .{ lazy.kind, @intFromEnum(lazy.ty) },
+ ) catch unreachable;
+ gop.value_ptr.* = .{
+ .lsi = elf.addLocalSymbolAssumeCapacity(.{
+ .node = node,
+ .name = try elf.string(.strtab, name),
+ .value = 0,
+ .size = 0,
+ .type = sym_type,
+ .shndx = shndx,
+ }),
+ .first_reloc = .none,
+ };
+ elf.nodes.appendAssumeCapacity(switch (lazy.kind) {
+ .code => .{ .lazy_code = @enumFromInt(gop.index) },
+ .const_data => .{ .lazy_const_data = @enumFromInt(gop.index) },
+ });
+ elf.synth_prog_node.increaseEstimatedTotalItems(1);
+ }
+ const s: Symbol.Id = .local(gop.value_ptr.lsi);
+ return s.toTypeErased();
+}
+pub fn externSymbol(elf: *Elf, opts: struct {
+ name: []const u8,
+ lib_name: ?[]const u8,
+ type: std.elf.STT,
+ linkage: std.lang.GlobalLinkage = .strong,
+ visibility: std.lang.SymbolVisibility = .default,
+}) !link.File.SymbolId {
+ try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
+ const symbol = elf.addGlobalSymbolAssumeCapacity(.{
+ .node = .none,
+ .name = try .string(elf, opts.name),
+ .lib_name = opts.lib_name,
+ .value = 0,
+ .size = 0,
+ .type = opts.type,
+ .bind = switch (opts.linkage) {
+ .internal => @panic("TODO internal extern symbol"),
+ .strong => .strong,
+ .weak => .weak,
+ .link_once => return error.LinkOnceUnsupported,
+ },
+ .visibility = switch (opts.visibility) {
+ .default => .DEFAULT,
+ .hidden => .HIDDEN,
+ .protected => .PROTECTED,
+ },
+ .shndx = .UNDEF,
+ }) catch |err| switch (err) {
+ error.MultipleDefinitions => unreachable, // shndx is undef
+ };
+ return symbol.toTypeErased();
+}
+pub fn addReloc(
+ elf: *Elf,
+ atom: link.File.AtomId,
+ offset: u64,
+ target: link.File.SymbolId,
+ addend: i64,
+ @"type": Reloc.Type,
+) !void {
+ const node: MappedFile.Node.Index = Node.fromAtom(atom);
+ try elf.ensureUnusedRelocCapacity(node, 1);
+ elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type");
+}
+pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId {
+ const zcu = elf.base.comp.zcu.?;
+ const ip = &zcu.intern_pool;
+ const nav = ip.getNav(nav_index);
+ if (nav.getExtern(ip)) |@"extern"| {
+ return elf.externSymbol(.{
+ .name = @"extern".name.toSlice(ip),
+ .lib_name = @"extern".lib_name.toSlice(ip),
+ .type = navType(ip, nav.resolved.?, elf.base.comp.config.any_non_single_threaded),
+ .linkage = @"extern".linkage,
+ .visibility = @"extern".visibility,
+ });
+ }
+ const nmi = try elf.navMapIndex(zcu, nav_index);
+ const s: Symbol.Id = .local(nmi.symbol(elf));
+ return s.toTypeErased();
+}
+pub fn uavSymbol(
+ elf: *Elf,
+ uav_val: InternPool.Index,
+ uav_align: InternPool.Alignment,
+) !link.File.SymbolId {
+ const umi = try elf.uavMapIndex(uav_val, uav_align);
+ const s: Symbol.Id = .local(umi.symbol(elf));
+ return s.toTypeErased();
+}
+pub fn getNavVAddr(
+ elf: *Elf,
+ pt: Zcu.PerThread,
+ nav: InternPool.Nav.Index,
+ reloc_info: link.File.RelocInfo,
+) !u64 {
+ _ = pt;
+ return elf.getVAddr(reloc_info, try elf.navSymbol(nav));
+}
+pub fn getUavVAddr(
+ elf: *Elf,
+ uav_val: InternPool.Index,
+ reloc_info: link.File.RelocInfo,
+) !u64 {
+ return elf.getVAddr(reloc_info, try elf.uavSymbol(uav_val, .none));
+}
+pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.SymbolId) !u64 {
+ const node: MappedFile.Node.Index = Node.fromAtom(reloc_info.parent.atom_index);
+ const target_sym: Symbol.Id = .fromTypeErased(target);
+ try elf.ensureUnusedRelocCapacity(node, 1);
+ elf.addRelocAssumeCapacity(
+ node,
+ reloc_info.offset,
+ target_sym,
+ reloc_info.addend,
+ .absAddr(elf),
+ );
+ return target_sym.value(elf);
+}
+pub fn lowerUav(
+ elf: *Elf,
+ pt: Zcu.PerThread,
+ uav_val: InternPool.Index,
+ uav_align: InternPool.Alignment,
+ src_loc: Zcu.LazySrcLoc,
+) !codegen.SymbolResult {
+ _ = pt;
+ const umi = elf.uavMapIndex(uav_val, uav_align) catch |err| switch (err) {
+ error.OutOfMemory => |e| return e,
+ else => |e| return .{ .fail = try Zcu.ErrorMsg.create(
+ elf.base.comp.gpa,
+ src_loc,
+ "linker failed to update constant: {s}",
+ .{@errorName(e)},
+ ) },
+ };
+ const s: Symbol.Id = .local(umi.symbol(elf));
+ return .{ .sym_index = s.toTypeErased() };
+}
+
+const StringSection = enum {
+ shstrtab,
+ strtab,
+ dynstr,
+ fn shndx(s: StringSection, elf: *const Elf) Section.Index {
+ return switch (s) {
+ .strtab => .strtab,
+ .shstrtab => .shstrtab,
+ .dynstr => elf.shndx.dynstr,
+ };
}
};
+fn String(section: StringSection) type {
+ return enum(u32) {
+ empty = 0,
+ _,
+
+ fn slice(str: @This(), elf: *Elf) [:0]const u8 {
+ const section_node = section.shndx(elf).get(elf).ni;
+ const overlong = section_node.sliceConst(&elf.mf)[@intFromEnum(str)..];
+ return overlong[0..std.mem.findScalar(u8, overlong, 0).? :0];
+ }
+ };
+}
+fn string(elf: *Elf, comptime section: StringSection, key: []const u8) !String(section) {
+ const st: *StringTable = &@field(elf, @tagName(section));
+ return @enumFromInt(try st.get(elf, section.shndx(elf), key));
+}
-pub const StringTable = struct {
+const StringTable = struct {
map: std.HashMapUnmanaged(u32, void, StringTable.Context, std.hash_map.default_max_load_percentage),
const Context = struct {
@@ -623,9 +1494,13 @@ pub const StringTable = struct {
}
};
- pub fn get(st: *StringTable, elf: *Elf, si: Symbol.Index, key: []const u8) !u32 {
+ pub fn get(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) !u32 {
+ // If we are in `initHeaders` the strtab might not be initalized yet, so we need to special
+ // case the empty string.
+ if (key.len == 0) return 0;
+
const gpa = elf.base.comp.gpa;
- const ni = si.node(elf);
+ const ni = shndx.get(elf).ni;
const slice_const = ni.sliceConst(&elf.mf);
const gop = try st.map.getOrPutContextAdapted(
gpa,
@@ -635,7 +1510,7 @@ pub const StringTable = struct {
);
if (gop.found_existing) return gop.key_ptr.*;
try ni.resized(gpa, &elf.mf);
- const old_size, const new_size = size: switch (elf.shdrPtr(si.shndx(elf))) {
+ const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) {
inline else => |shdr| {
const old_size: u32 = @intCast(elf.targetLoad(&shdr.size));
const new_size: u32 = @intCast(old_size + key.len + 1);
@@ -654,7 +1529,7 @@ pub const StringTable = struct {
}
};
-pub const GotIndex = enum(u32) {
+const GotIndex = enum(u32) {
none = std.math.maxInt(u32),
_,
@@ -671,12 +1546,12 @@ pub const GotIndex = enum(u32) {
}
};
-pub const Reloc = extern struct {
+const Reloc = extern struct {
type: Reloc.Type,
prev: Reloc.Index,
next: Reloc.Index,
- loc: Symbol.Index,
- target: Symbol.Index,
+ node: MappedFile.Node.Index,
+ target: Symbol.Id,
index: Section.RelIndex,
offset: u64,
addend: i64,
@@ -745,85 +1620,86 @@ pub const Reloc = extern struct {
pub fn apply(reloc: *const Reloc, elf: *Elf) void {
assert(elf.ehdrField(.type) != .REL);
- const loc_ni = reloc.loc.get(elf).ni;
- switch (loc_ni) {
- .none => return,
- else => |ni| if (ni.hasMoved(&elf.mf)) return,
- }
- switch (reloc.target.get(elf).ni) {
- .none => {},
- else => |ni| if (ni.hasMoved(&elf.mf)) return,
+ assert(reloc.node != .none);
+ if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {
+ // There's no point applying the relocation now, because it will be re-applied by
+ // `flushMoved` at some point anyway.
+ return;
}
- const loc_slice = loc_ni.slice(&elf.mf)[@intCast(reloc.offset)..];
+ const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
+ .file => unreachable,
+ .ehdr => unreachable,
+ .shdr => unreachable,
+ .segment => unreachable,
+ .section => |shndx| shndx.vaddr(elf),
+ .input_section => |isi| isi.ptrConst(elf).vaddr,
+ inline .nav,
+ .uav,
+ .lazy_code,
+ .lazy_const_data,
+ => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
+ };
+ const dest_vaddr = node_vaddr + reloc.offset;
+ const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
const target_endian = elf.targetEndian();
- switch (elf.symtabSlice()) {
- inline else => |symtab, class| {
- const loc_sym = &symtab[@intFromEnum(reloc.loc)];
- const loc_shndx = elf.targetLoad(&loc_sym.shndx);
- assert(loc_shndx != std.elf.SHN_UNDEF);
- const target_sym = &symtab[@intFromEnum(reloc.target)];
- const target_value =
- elf.targetLoad(&target_sym.value) +% @as(u64, @bitCast(reloc.addend));
+ switch (elf.symPtr(reloc.target.index(elf))) {
+ inline else => |target_sym, class| {
+ const target_value = elf.targetLoad(&target_sym.value) +% @as(u64, @bitCast(reloc.addend));
switch (elf.ehdrField(.machine)) {
else => |machine| @panic(@tagName(machine)),
.X86_64 => switch (reloc.type.X86_64) {
else => |kind| @panic(@tagName(kind)),
.@"64" => std.mem.writeInt(
u64,
- loc_slice[0..8],
+ dest_slice[0..8],
target_value,
target_endian,
),
.PC32 => std.mem.writeInt(
i32,
- loc_slice[0..4],
- @intCast(@as(i64, @bitCast(target_value -%
- (elf.targetLoad(&loc_sym.value) + reloc.offset)))),
+ dest_slice[0..4],
+ @intCast(@as(i64, @bitCast(target_value -% dest_vaddr))),
target_endian,
),
.PLT32 => std.mem.writeInt(
i32,
- loc_slice[0..4],
- @intCast(@as(i64, @bitCast(
- if (elf.got.plt.getIndex(reloc.target)) |plt_index|
- elf.targetLoad(&@field(
- elf.shdrPtr(elf.si.plt_sec.shndx(elf)),
- @tagName(class),
- ).addr) +% 16 * plt_index +%
- @as(u64, @bitCast(reloc.addend)) -%
- (elf.targetLoad(&loc_sym.value) + reloc.offset)
- else
- target_value -%
- (elf.targetLoad(&loc_sym.value) + reloc.offset),
- ))),
+ dest_slice[0..4],
+ @intCast(@as(i64, @bitCast(if (elf.got.plt.getIndex(reloc.target)) |plt_index|
+ elf.targetLoad(&@field(
+ elf.shdrPtr(elf.shndx.plt_sec),
+ @tagName(class),
+ ).addr) +% 16 * plt_index +%
+ @as(u64, @bitCast(reloc.addend)) -% dest_vaddr
+ else
+ target_value -% dest_vaddr))),
target_endian,
),
.@"32" => std.mem.writeInt(
u32,
- loc_slice[0..4],
+ dest_slice[0..4],
@intCast(target_value),
target_endian,
),
.@"32S" => std.mem.writeInt(
i32,
- loc_slice[0..4],
+ dest_slice[0..4],
@intCast(@as(i64, @bitCast(target_value))),
target_endian,
),
.TLSLD => std.mem.writeInt(
i32,
- loc_slice[0..4],
+ dest_slice[0..4],
@intCast(@as(i64, @bitCast(
- elf.targetLoad(&symtab[@intFromEnum(elf.si.got)].value) +%
+ elf.shndx.got.vaddr(elf) +%
@as(u64, @bitCast(reloc.addend)) +%
@as(u64, 8) * elf.got.tlsld.unwrap().? -%
- (elf.targetLoad(&loc_sym.value) + reloc.offset),
+ dest_vaddr,
))),
target_endian,
),
.DTPOFF32 => std.mem.writeInt(
i32,
- loc_slice[0..4],
+ dest_slice[0..4],
@intCast(@as(i64, @bitCast(target_value))),
target_endian,
),
@@ -833,14 +1709,14 @@ pub const Reloc = extern struct {
assert(elf.targetLoad(&ph.type) == .TLS);
std.mem.writeInt(
i32,
- loc_slice[0..4],
+ dest_slice[0..4],
@intCast(@as(i64, @bitCast(target_value -% elf.targetLoad(&ph.memsz)))),
target_endian,
);
},
.SIZE32 => std.mem.writeInt(
u32,
- loc_slice[0..4],
+ dest_slice[0..4],
@intCast(
elf.targetLoad(&target_sym.size) +% @as(u64, @bitCast(reloc.addend)),
),
@@ -848,7 +1724,7 @@ pub const Reloc = extern struct {
),
.SIZE64 => std.mem.writeInt(
u64,
- loc_slice[0..8],
+ dest_slice[0..8],
elf.targetLoad(&target_sym.size) +% @as(u64, @bitCast(reloc.addend)),
target_endian,
),
@@ -861,9 +1737,9 @@ pub const Reloc = extern struct {
pub fn delete(reloc: *Reloc, elf: *Elf) void {
switch (reloc.prev) {
.none => {
- const target = reloc.target.get(elf);
- assert(target.target_relocs.get(elf) == reloc);
- target.target_relocs = reloc.next;
+ const target_ptr = reloc.target.index(elf).ptr(elf);
+ assert(target_ptr.first_target_reloc.get(elf) == reloc);
+ target_ptr.first_target_reloc = reloc.next;
},
else => |prev| prev.get(elf).next = reloc.next,
}
@@ -874,13 +1750,13 @@ pub const Reloc = extern struct {
switch (elf.ehdrField(.type)) {
.NONE, .CORE, _ => unreachable,
.REL => {
- const sh = reloc.loc.shndx(elf).get(elf);
- switch (elf.shdrPtr(sh.rela_si.shndx(elf))) {
+ const sh = elf.getNodeShndx(reloc.node).get(elf);
+ switch (elf.shdrPtr(sh.rela_shndx)) {
inline else => |shdr, class| {
const Rela = class.ElfN().Rela;
const ent_size = elf.targetLoad(&shdr.entsize);
const start = ent_size * reloc.index.unwrap().?;
- const rela_slice = sh.rela_si.node(elf).slice(&elf.mf);
+ const rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf);
const rela: *Rela = @ptrCast(@alignCast(
rela_slice[@intCast(start)..][0..@intCast(ent_size)],
));
@@ -901,6 +1777,38 @@ pub const Reloc = extern struct {
reloc.* = undefined;
}
+ fn updateTargetIndex(reloc: *const Reloc, elf: *Elf) void {
+ assert(elf.ehdrField(.type) == .REL);
+ const sh = elf.getNodeShndx(reloc.node).get(elf);
+ switch (elf.shdrPtr(sh.rela_shndx)) {
+ inline else => |shdr, class| {
+ assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
+ const size = elf.targetLoad(&shdr.size);
+ const raw_rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf);
+ const rela_slice: []class.ElfN().Rela = @ptrCast(@alignCast(raw_rela_slice[0..@intCast(size)]));
+ elf.targetStore(&rela_slice[reloc.index.unwrap().?].info, .{
+ .type = @intCast(reloc.type.unwrap(elf)),
+ .sym = @intCast(@intFromEnum(reloc.target.index(elf))),
+ });
+ },
+ }
+ }
+
+ fn updateNodeOffset(reloc: *const Reloc, elf: *Elf, node_offset: u64) void {
+ assert(elf.ehdrField(.type) == .REL);
+ const total_offset = node_offset + reloc.offset;
+ const sh = elf.getNodeShndx(reloc.node).get(elf);
+ switch (elf.shdrPtr(sh.rela_shndx)) {
+ inline else => |shdr, class| {
+ assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
+ const size = elf.targetLoad(&shdr.size);
+ const raw_rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf);
+ const rela_slice: []class.ElfN().Rela = @ptrCast(@alignCast(raw_rela_slice[0..@intCast(size)]));
+ elf.targetStore(&rela_slice[reloc.index.unwrap().?].offset, @intCast(total_offset));
+ },
+ }
+ }
+
comptime {
if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Reloc) == 40);
}
@@ -1001,34 +1909,38 @@ fn create(
.nodes = .empty,
.shdrs = .empty,
.phdrs = .empty,
- .si = .{
- .dynsym = .null,
- .dynstr = .null,
- .dynamic = .null,
- .tdata = .null,
- .entry = .null,
+ .shndx = .{
+ .got = .UNDEF,
+ .got_plt = .UNDEF,
+ .plt = .UNDEF,
+ .plt_sec = .UNDEF,
+ .dynsym = .UNDEF,
+ .dynstr = .UNDEF,
+ .dynamic = .UNDEF,
+ .tdata = .UNDEF,
},
.symtab = .empty,
- .shstrtab = .{
- .map = .empty,
- },
- .strtab = .{
- .map = .empty,
- },
- .dynsym = .empty,
- .dynstr = .{
- .map = .empty,
+ .globals = .{
+ .strong_def = .empty,
+ .weak_def = .empty,
+ .strong_undef = .empty,
+ .weak_undef = .empty,
},
+ .node_global_symbols = .empty,
+ .shstrtab = .{ .map = .empty },
+ .strtab = .{ .map = .empty },
+ .dynstr = .{ .map = .empty },
.got = .{
.len = 0,
.tlsld = .none,
.plt = .empty,
},
+ .first_plt_reloc = .none,
+ .first_dynamic_reloc = .none,
.needed = .empty,
.inputs = .empty,
.input_sections = .empty,
.input_section_pending_index = 0,
- .globals = .empty,
.navs = .empty,
.uavs = .empty,
.lazy = comptime .initFill(.{
@@ -1037,6 +1949,7 @@ fn create(
}),
.pending_uavs = .empty,
.relocs = .empty,
+ .changed_symtab_index = .empty,
.const_prog_node = .none,
.synth_prog_node = .none,
.input_prog_node = .none,
@@ -1054,21 +1967,25 @@ pub fn deinit(elf: *Elf) void {
elf.shdrs.deinit(gpa);
elf.phdrs.deinit(gpa);
elf.symtab.deinit(gpa);
+ elf.globals.strong_def.deinit(gpa);
+ elf.globals.weak_def.deinit(gpa);
+ elf.globals.strong_undef.deinit(gpa);
+ elf.globals.weak_undef.deinit(gpa);
+ elf.node_global_symbols.deinit(gpa);
elf.shstrtab.map.deinit(gpa);
elf.strtab.map.deinit(gpa);
- elf.dynsym.deinit(gpa);
elf.dynstr.map.deinit(gpa);
elf.got.plt.deinit(gpa);
elf.needed.deinit(gpa);
for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);
elf.inputs.deinit(gpa);
elf.input_sections.deinit(gpa);
- elf.globals.deinit(gpa);
elf.navs.deinit(gpa);
elf.uavs.deinit(gpa);
for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);
elf.pending_uavs.deinit(gpa);
elf.relocs.deinit(gpa);
+ elf.changed_symtab_index.deinit(gpa);
elf.* = undefined;
}
@@ -1123,19 +2040,18 @@ fn initHeaders(
const expected_nodes_len = expected_nodes_len: switch (@"type") {
.NONE, .CORE, _ => unreachable,
.REL => {
+ // Each phdr is actually going to be an shdr.
defer phnum = 0;
break :expected_nodes_len 5 + phnum;
},
- .EXEC, .DYN => break :expected_nodes_len 8 + phnum * 2 +
- @intFromBool(maybe_interp != null) +
- @as(usize, 4) * @intFromBool(have_dynamic_section) +
- @intFromBool(comp.config.any_non_single_threaded),
+ .EXEC, .DYN => break :expected_nodes_len 10 +
+ phnum * 2 - 1 + // each phdr also has a matching shdr, except for the PT_PHDR phdr
+ @as(usize, 4) * @intFromBool(have_dynamic_section), // .dynstr, .dynsym, .rela.dyn, .rela.plt
};
try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
try elf.shdrs.ensureTotalCapacity(gpa, shnum);
try elf.phdrs.resize(gpa, phnum);
try elf.symtab.ensureTotalCapacity(gpa, 1);
- if (have_dynamic_section) try elf.dynsym.ensureTotalCapacity(gpa, 1);
elf.nodes.appendAssumeCapacity(.file);
switch (class) {
@@ -1150,14 +2066,13 @@ fn initHeaders(
elf.nodes.appendAssumeCapacity(.ehdr);
const ehdr: *ElfN.Ehdr = @ptrCast(@alignCast(elf.ni.ehdr.slice(&elf.mf)));
- const EI = std.elf.EI;
- @memcpy(ehdr.ident[0..std.elf.MAGIC.len], std.elf.MAGIC);
- ehdr.ident[EI.CLASS] = @intFromEnum(class);
- ehdr.ident[EI.DATA] = @intFromEnum(data);
- ehdr.ident[EI.VERSION] = 1;
- ehdr.ident[EI.OSABI] = @intFromEnum(osabi);
- ehdr.ident[EI.ABIVERSION] = 0;
- @memset(ehdr.ident[EI.PAD..], 0);
+ ehdr.ident = .{
+ .class = class,
+ .data = data,
+ .version = 1,
+ .osabi = osabi,
+ .abiversion = 0,
+ };
ehdr.type = @"type";
ehdr.machine = machine;
ehdr.version = 1;
@@ -1176,7 +2091,7 @@ fn initHeaders(
}
assert(elf.ni.shdr == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
- .size = elf.ehdrField(.shentsize) * elf.ehdrField(.shnum),
+ .size = @as(u64, elf.ehdrField(.shentsize)) * @as(u64, elf.ehdrField(.shnum)),
.alignment = elf.mf.flags.block_size,
.moved = true,
.resized = true,
@@ -1366,7 +2281,7 @@ fn initHeaders(
const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf)));
sh_undef.* = .{
- .name = try elf.string(.shstrtab, ""),
+ .name = @intFromEnum(String(.shstrtab).empty),
.type = .NULL,
.flags = .{ .shf = .{} },
.addr = 0,
@@ -1378,24 +2293,23 @@ fn initHeaders(
.entsize = 0,
};
if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef);
- elf.shdrs.appendAssumeCapacity(.{ .si = .null, .rela_si = .null, .rela_free = .none });
+ elf.shdrs.appendAssumeCapacity(.{ .lsi = .null, .ni = .none, .rela_shndx = .UNDEF, .rela_free = .none });
elf.symtab.addOneAssumeCapacity().* = .{
- .ni = .none,
- .loc_relocs = .none,
- .target_relocs = .none,
- .unused = 0,
+ .node = .none,
+ .first_target_reloc = .none,
};
- assert(elf.si.symtab == try elf.addSection(elf.ni.file, .{
+ assert(.symtab == try elf.addSection(elf.ni.file, .{
.type = .SYMTAB,
.size = @sizeOf(ElfN.Sym) * 1,
.addralign = addr_align,
.entsize = @sizeOf(ElfN.Sym),
.node_align = elf.mf.flags.block_size,
+ .info = 1, // index of first non-local symbol
}));
const symtab_null = @field(elf.symPtr(.null), @tagName(ct_class));
symtab_null.* = .{
- .name = try elf.string(.strtab, ""),
+ .name = @intFromEnum(String(.strtab).empty),
.value = 0,
.size = 0,
.info = .{ .type = .NOTYPE, .bind = .LOCAL },
@@ -1408,55 +2322,56 @@ fn initHeaders(
ehdr.shstrndx = ehdr.shnum;
},
}
- assert(elf.si.shstrtab == try elf.addSection(elf.ni.file, .{
+ assert(.shstrtab == try elf.addSection(elf.ni.file, .{
.type = .STRTAB,
.size = 1,
.entsize = 1,
.node_align = elf.mf.flags.block_size,
}));
- try elf.renameSection(.symtab, ".symtab");
- try elf.renameSection(.shstrtab, ".shstrtab");
- elf.si.shstrtab.node(elf).slice(&elf.mf)[0] = 0;
+ Section.Index.get(.shstrtab, elf).ni.slice(&elf.mf)[0] = 0;
- assert(elf.si.strtab == try elf.addSection(elf.ni.file, .{
+ try Section.Index.symtab.rename(elf, ".symtab");
+ try Section.Index.shstrtab.rename(elf, ".shstrtab");
+
+ assert(.strtab == try elf.addSection(elf.ni.file, .{
.name = ".strtab",
.type = .STRTAB,
.size = 1,
.entsize = 1,
.node_align = elf.mf.flags.block_size,
}));
- switch (elf.shdrPtr(elf.si.symtab.shndx(elf))) {
- inline else => |shdr| elf.targetStore(&shdr.link, @intFromEnum(elf.si.strtab.shndx(elf))),
+ Section.Index.get(.strtab, elf).ni.slice(&elf.mf)[0] = 0;
+ switch (elf.shdrPtr(.symtab)) {
+ inline else => |shdr| elf.targetStore(&shdr.link, @intFromEnum(Section.Index.strtab)),
}
- elf.si.strtab.node(elf).slice(&elf.mf)[0] = 0;
- assert(elf.si.rodata == try elf.addSection(elf.ni.rodata, .{
+ assert(.rodata == try elf.addSection(elf.ni.rodata, .{
.name = ".rodata",
.flags = .{ .ALLOC = true },
.addralign = elf.mf.flags.block_size,
}));
- assert(elf.si.text == try elf.addSection(elf.ni.text, .{
+ assert(.text == try elf.addSection(elf.ni.text, .{
.name = ".text",
.flags = .{ .ALLOC = true, .EXECINSTR = true },
.addralign = elf.mf.flags.block_size,
}));
- assert(elf.si.data == try elf.addSection(elf.ni.data, .{
+ assert(.data == try elf.addSection(elf.ni.data, .{
.name = ".data",
.flags = .{ .WRITE = true, .ALLOC = true },
.addralign = elf.mf.flags.block_size,
}));
- assert(elf.si.data_rel_ro == try elf.addSection(elf.ni.data_rel_ro, .{
+ assert(.data_rel_ro == try elf.addSection(elf.ni.data_rel_ro, .{
.name = ".data.rel.ro",
.flags = .{ .WRITE = true, .ALLOC = true },
.addralign = elf.mf.flags.block_size,
}));
if (@"type" != .REL) {
- assert(elf.si.got == try elf.addSection(elf.ni.data_rel_ro, .{
+ elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{
.name = ".got",
.flags = .{ .WRITE = true, .ALLOC = true },
.addralign = addr_align,
- }));
- assert(elf.si.got_plt == try elf.addSection(
+ });
+ elf.shndx.got_plt = try elf.addSection(
if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data,
.{
.name = ".got.plt",
@@ -1469,26 +2384,26 @@ fn initHeaders(
},
.addralign = addr_align,
},
- ));
+ );
const plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec =
switch (machine) {
else => @panic(@tagName(machine)),
.X86_64 => .{ 16, .@"16", true },
};
- assert(elf.si.plt == try elf.addSection(elf.ni.text, .{
+ elf.shndx.plt = try elf.addSection(elf.ni.text, .{
.name = ".plt",
.type = .PROGBITS,
.flags = .{ .ALLOC = true, .EXECINSTR = true },
.size = plt_size,
.addralign = plt_align,
.node_align = elf.mf.flags.block_size,
- }));
- if (plt_sec) assert(elf.si.plt_sec == try elf.addSection(elf.ni.text, .{
+ });
+ if (plt_sec) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{
.name = ".plt.sec",
.flags = .{ .ALLOC = true, .EXECINSTR = true },
.addralign = plt_align,
.node_align = elf.mf.flags.block_size,
- }));
+ });
if (maybe_interp) |interp| {
const interp_ni = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{
.size = interp.len + 1,
@@ -1499,13 +2414,13 @@ fn initHeaders(
elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx });
elf.phdrs.items[interp_phndx] = interp_ni;
- const sec_interp_si = try elf.addSection(interp_ni, .{
+ const sec_interp_shndx = try elf.addSection(interp_ni, .{
.name = ".interp",
.type = .PROGBITS,
.flags = .{ .ALLOC = true },
.size = @intCast(interp.len + 1),
});
- const sec_interp = sec_interp_si.node(elf).slice(&elf.mf);
+ const sec_interp = sec_interp_shndx.get(elf).ni.slice(&elf.mf);
@memcpy(sec_interp[0..interp.len], interp);
sec_interp[interp.len] = 0;
}
@@ -1518,7 +2433,7 @@ fn initHeaders(
elf.nodes.appendAssumeCapacity(.{ .segment = dynamic_phndx });
elf.phdrs.items[dynamic_phndx] = dynamic_ni;
- elf.si.dynstr = try elf.addSection(elf.ni.rodata, .{
+ const dynstr_shndx = try elf.addSection(elf.ni.rodata, .{
.name = ".dynstr",
.type = .STRTAB,
.flags = .{ .ALLOC = true },
@@ -1526,26 +2441,27 @@ fn initHeaders(
.entsize = 1,
.node_align = elf.mf.flags.block_size,
});
- const dynstr_shndx = elf.si.dynstr.shndx(elf);
- elf.dynsym.putAssumeCapacityNoClobber(.null, {});
+ dynstr_shndx.get(elf).ni.slice(&elf.mf)[0] = 0;
+ elf.shndx.dynstr = dynstr_shndx;
+
switch (class) {
.NONE, _ => unreachable,
inline else => |ct_class| {
const Sym = ct_class.ElfN().Sym;
- elf.si.dynsym = try elf.addSection(elf.ni.rodata, .{
+ elf.shndx.dynsym = try elf.addSection(elf.ni.rodata, .{
.name = ".dynsym",
.type = .DYNSYM,
.flags = .{ .ALLOC = true },
.size = @sizeOf(Sym) * 1,
- .link = @intFromEnum(dynstr_shndx),
+ .link = dynstr_shndx.toSection().?,
.info = 1,
.addralign = addr_align,
.entsize = @sizeOf(Sym),
.node_align = elf.mf.flags.block_size,
});
- const dynsym_null = &@field(elf.dynsymSlice(), @tagName(ct_class))[0];
+ const dynsym_null = @field(elf.dynsymPtr(0), @tagName(ct_class));
dynsym_null.* = .{
- .name = try elf.string(.dynstr, ""),
+ .name = @intFromEnum(String(.dynstr).empty),
.value = 0,
.size = 0,
.info = .{ .type = .NOTYPE, .bind = .LOCAL },
@@ -1562,57 +2478,57 @@ fn initHeaders(
.NONE, _ => unreachable,
inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela),
};
- elf.si.got.shndx(elf).get(elf).rela_si = try elf.addSection(elf.ni.rodata, .{
+ elf.shndx.got.get(elf).rela_shndx = try elf.addSection(elf.ni.rodata, .{
.name = ".rela.dyn",
.type = .RELA,
.flags = .{ .ALLOC = true },
- .link = @intFromEnum(elf.si.dynsym.shndx(elf)),
+ .link = elf.shndx.dynsym.toSection().?,
.addralign = addr_align,
.entsize = rela_size,
.node_align = elf.mf.flags.block_size,
});
- const got_plt_shndx = elf.si.got_plt.shndx(elf);
- got_plt_shndx.get(elf).rela_si = try elf.addSection(elf.ni.rodata, .{
+ const got_plt_shndx = elf.shndx.got_plt;
+ got_plt_shndx.get(elf).rela_shndx = try elf.addSection(elf.ni.rodata, .{
.name = ".rela.plt",
.type = .RELA,
.flags = .{ .ALLOC = true, .INFO_LINK = true },
- .link = @intFromEnum(elf.si.dynsym.shndx(elf)),
- .info = @intFromEnum(got_plt_shndx),
+ .link = elf.shndx.dynsym.toSection().?,
+ .info = got_plt_shndx.toSection().?,
.addralign = addr_align,
.entsize = rela_size,
.node_align = elf.mf.flags.block_size,
});
- elf.si.dynamic = try elf.addSection(dynamic_ni, .{
+ elf.shndx.dynamic = try elf.addSection(dynamic_ni, .{
.name = ".dynamic",
.type = .DYNAMIC,
.flags = .{ .ALLOC = true, .WRITE = true },
- .link = @intFromEnum(dynstr_shndx),
+ .link = dynstr_shndx.toSection().?,
.entsize = @intCast(addr_align.toByteUnits() * 2),
.node_align = addr_align,
});
switch (machine) {
else => @panic(@tagName(machine)),
.X86_64 => {
- @memcpy(elf.si.plt.node(elf).slice(&elf.mf)[0..16], &[16]u8{
+ const plt_ni = elf.shndx.plt.get(elf).ni;
+ const got_plt_sym: Symbol.Id = .local(elf.shndx.got_plt.get(elf).lsi);
+ @memcpy(plt_ni.slice(&elf.mf)[0..16], &[16]u8{
0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // push 0x0(%rip)
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip)
0x0f, 0x1f, 0x40, 0x00, // nopl 0x0(%rax)
});
- const plt_sym = elf.si.plt.get(elf);
- assert(plt_sym.loc_relocs == .none);
- plt_sym.loc_relocs = @enumFromInt(elf.relocs.items.len);
- try elf.ensureUnusedRelocCapacity(elf.si.plt, 2);
+ elf.first_plt_reloc = @enumFromInt(elf.relocs.items.len);
+ try elf.ensureUnusedRelocCapacity(plt_ni, 2);
elf.addRelocAssumeCapacity(
- elf.si.plt,
+ plt_ni,
2,
- elf.si.got_plt,
+ got_plt_sym,
8 * 1 - 4,
.{ .X86_64 = .PC32 },
);
elf.addRelocAssumeCapacity(
- elf.si.plt,
+ plt_ni,
8,
- elf.si.got_plt,
+ got_plt_sym,
8 * 2 - 4,
.{ .X86_64 = .PC32 },
);
@@ -1632,7 +2548,7 @@ fn initHeaders(
assert(maybe_interp == null);
assert(!have_dynamic_section);
}
- if (comp.config.any_non_single_threaded) elf.si.tdata = try elf.addSection(elf.ni.tls, .{
+ if (comp.config.any_non_single_threaded) elf.shndx.tdata = try elf.addSection(elf.ni.tls, .{
.name = ".tdata",
.flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
.addralign = elf.mf.flags.block_size,
@@ -1642,7 +2558,7 @@ fn initHeaders(
pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {
prog_node.increaseEstimatedTotalItems(4);
- elf.const_prog_node = prog_node.start("Constants", elf.pending_uavs.count());
+ elf.const_prog_node = prog_node.start("Constants", elf.pending_uavs.items.len);
elf.synth_prog_node = prog_node.start("Synthetics", count: {
var count: usize = 0;
for (&elf.lazy.values) |*lazy| count += lazy.map.count() - lazy.pending_index;
@@ -1669,35 +2585,105 @@ pub fn endProgress(elf: *Elf) void {
fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {
return elf.nodes.get(@intFromEnum(ni));
}
+/// Asserts that `ni` is a section, input section, NAV, UAV, or lazy code/data.
+fn getNodeShndx(elf: *Elf, ni: MappedFile.Node.Index) Section.Index {
+ return switch (elf.getNode(ni)) {
+ .file => unreachable,
+ .ehdr => unreachable,
+ .shdr => unreachable,
+ .segment => unreachable,
+
+ .section => |shndx| shndx,
+
+ .input_section,
+ .nav,
+ .uav,
+ .lazy_code,
+ .lazy_const_data,
+ => elf.getNode(ni.parent(&elf.mf)).section,
+ };
+}
fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
- const parent_vaddr = parent_vaddr: {
- const parent_ni = ni.parent(&elf.mf);
- const parent_si = switch (elf.getNode(parent_ni)) {
- .file => return 0,
- .ehdr, .shdr => unreachable,
- .segment => |phndx| break :parent_vaddr switch (elf.phdrSlice()) {
- inline else => |phdr| elf.targetLoad(&phdr[phndx].vaddr),
- },
- .section => |si| si,
- .input_section => unreachable,
- inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| mi.symbol(elf),
- };
- break :parent_vaddr if (parent_si == elf.si.tdata) 0 else switch (elf.symPtr(parent_si)) {
- inline else => |sym| elf.targetLoad(&sym.value),
- };
+ const parent_vaddr = switch (elf.getNode(ni.parent(&elf.mf))) {
+ .file => return 0,
+ .ehdr, .shdr => unreachable,
+ .segment => |phndx| switch (elf.phdrSlice()) {
+ inline else => |phdr| elf.targetLoad(&phdr[phndx].vaddr),
+ },
+ .section => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf),
+ .input_section => unreachable,
+ inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
};
const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
return parent_vaddr + offset;
}
-pub fn identClass(elf: *const Elf) std.elf.CLASS {
+/// Deletes any existing relocations in the given node, and marks the start of the node's contiguous
+/// sequence of relocations, so that the caller may append the node's updated relocations.
+///
+/// Asserts that `ni` must be a node which supports relocations (see `Elf.Node`). Does not support
+/// the special-case sections '.plt' and '.dynamic'.
+fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
+ const first_reloc_ptr: *Reloc.Index = switch (elf.getNode(ni)) {
+ .file => unreachable, // cannot contain relocs
+ .ehdr => unreachable, // cannot contain relocs
+ .shdr => unreachable, // cannot contain relocs
+ .segment => unreachable, // cannot contain relocs
+ .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported)
+ .input_section => |isi| &elf.input_sections.items[@intFromEnum(isi)].first_reloc,
+ .nav => |nmi| &elf.navs.values()[@intFromEnum(nmi)].first_reloc,
+ .uav => |umi| &elf.uavs.values()[@intFromEnum(umi)].first_reloc,
+ inline .lazy_code, .lazy_const_data => |lmi| &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_reloc,
+ };
+ if (first_reloc_ptr.* != .none) {
+ for (elf.relocs.items[@intFromEnum(first_reloc_ptr.*)..]) |*reloc| {
+ if (reloc.node != ni) break;
+ reloc.delete(elf);
+ }
+ }
+ first_reloc_ptr.* = @enumFromInt(elf.relocs.items.len);
+}
+
+/// Given that `node` has moved, updates all relocations in `node` (starting from `first_reloc`) as
+/// needed. In relocatables, this means updating the offsets of those relocations. In ELF modules,
+/// this means applying the relocations.
+fn flushMovedNodeRelocs(
+ elf: *Elf,
+ node: MappedFile.Node.Index,
+ node_vaddr: u64,
+ first_reloc: Reloc.Index,
+) void {
+ if (first_reloc == .none) return;
+ switch (elf.ehdrField(.type)) {
+ .NONE, .CORE, _ => unreachable,
+ .REL => {
+ // In a relocatable, we're not actually applying any relocations ourselves, but we need
+ // to update the offsets of the relocation entries since the node they're in has moved.
+ for (elf.relocs.items[@intFromEnum(first_reloc)..]) |*reloc| {
+ if (reloc.node != node) break;
+ reloc.updateNodeOffset(elf, node_vaddr);
+ }
+ },
+ .EXEC, .DYN => {
+ // For an ELF module, we just need to apply relocations.
+ for (elf.relocs.items[@intFromEnum(first_reloc)..]) |*reloc| {
+ if (reloc.node != node) break;
+ reloc.apply(elf);
+ }
+ // TODO: once we're emitting runtime relocation entries, we need to update their offsets
+ // too, like the logic for relocatables above.
+ },
+ }
+}
+
+fn identClass(elf: *const Elf) std.elf.CLASS {
return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.CLASS]);
}
-pub fn identData(elf: *const Elf) std.elf.DATA {
+fn identData(elf: *const Elf) std.elf.DATA {
return @enumFromInt(elf.mf.memory_map.memory[std.elf.EI.DATA]);
}
-pub fn targetEndian(elf: *const Elf) std.lang.Endian {
+fn targetEndian(elf: *const Elf) std.lang.Endian {
return switch (elf.identData()) {
.NONE, _ => unreachable,
.@"2LSB" => .little,
@@ -1731,12 +2717,12 @@ fn targetStore(elf: *const Elf, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).point
};
}
-pub const EhdrPtr = union(std.elf.CLASS) {
+const EhdrPtr = union(std.elf.CLASS) {
NONE: noreturn,
@"32": *std.elf.Elf32.Ehdr,
@"64": *std.elf.Elf64.Ehdr,
};
-pub fn ehdrPtr(elf: *Elf) EhdrPtr {
+fn ehdrPtr(elf: *Elf) EhdrPtr {
const slice = elf.ni.ehdr.slice(&elf.mf);
return switch (elf.identClass()) {
.NONE, _ => unreachable,
@@ -1747,7 +2733,7 @@ pub fn ehdrPtr(elf: *Elf) EhdrPtr {
),
};
}
-pub fn ehdrField(
+fn ehdrField(
elf: *Elf,
comptime field: std.meta.FieldEnum(std.elf.Elf64.Ehdr),
) @FieldType(std.elf.Elf64.Ehdr, @tagName(field)) {
@@ -1756,12 +2742,12 @@ pub fn ehdrField(
};
}
-pub const PhdrSlice = union(std.elf.CLASS) {
+const PhdrSlice = union(std.elf.CLASS) {
NONE: noreturn,
@"32": []std.elf.Elf32.Phdr,
@"64": []std.elf.Elf64.Phdr,
};
-pub fn phdrSlice(elf: *Elf) PhdrSlice {
+fn phdrSlice(elf: *Elf) PhdrSlice {
assert(elf.ehdrField(.type) != .REL);
const slice = elf.ni.phdr.slice(&elf.mf);
return switch (elf.identClass()) {
@@ -1774,104 +2760,47 @@ pub fn phdrSlice(elf: *Elf) PhdrSlice {
};
}
-pub const ShdrSlice = union(std.elf.CLASS) {
- NONE: noreturn,
- @"32": []std.elf.Elf32.Shdr,
- @"64": []std.elf.Elf64.Shdr,
-};
-pub fn shdrSlice(elf: *Elf) ShdrSlice {
- const slice = elf.ni.shdr.slice(&elf.mf);
- return switch (elf.identClass()) {
- .NONE, _ => unreachable,
- inline else => |class| @unionInit(
- ShdrSlice,
- @tagName(class),
- @ptrCast(@alignCast(slice)),
- ),
- };
-}
-
-pub const ShdrPtr = union(std.elf.CLASS) {
+const ShdrPtr = union(std.elf.CLASS) {
NONE: noreturn,
@"32": *std.elf.Elf32.Shdr,
@"64": *std.elf.Elf64.Shdr,
};
-pub fn shdrPtr(elf: *Elf, shndx: Symbol.Index.Shndx) ShdrPtr {
- return switch (elf.shdrSlice()) {
- inline else => |shdrs, class| @unionInit(ShdrPtr, @tagName(class), &shdrs[@intFromEnum(shndx)]),
- };
-}
-
-pub const SymtabSlice = union(std.elf.CLASS) {
- NONE: noreturn,
- @"32": []std.elf.Elf32.Sym,
- @"64": []std.elf.Elf64.Sym,
-};
-pub fn symtabSlice(elf: *Elf) SymtabSlice {
- const slice = elf.si.symtab.node(elf).slice(&elf.mf);
- return switch (elf.identClass()) {
+fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {
+ const raw_slice = elf.ni.shdr.slice(&elf.mf);
+ switch (elf.identClass()) {
.NONE, _ => unreachable,
- inline else => |class| @unionInit(SymtabSlice, @tagName(class), @ptrCast(@alignCast(
- slice[0..std.mem.alignBackwardAnyAlign(usize, slice.len, @sizeOf(class.ElfN().Sym))],
- ))),
- };
+ inline else => |class| {
+ const shdr_slice: []class.ElfN().Shdr = @ptrCast(@alignCast(raw_slice));
+ const shdr_ptr = &shdr_slice[@intFromEnum(shndx)];
+ return @unionInit(ShdrPtr, @tagName(class), shdr_ptr);
+ },
+ }
}
-pub const SymPtr = union(std.elf.CLASS) {
+const SymPtr = union(std.elf.CLASS) {
NONE: noreturn,
@"32": *std.elf.Elf32.Sym,
@"64": *std.elf.Elf64.Sym,
-};
-pub fn symPtr(elf: *Elf, si: Symbol.Index) SymPtr {
- return switch (elf.symtabSlice()) {
- inline else => |syms, class| @unionInit(SymPtr, @tagName(class), &syms[@intFromEnum(si)]),
- };
-}
-
-pub fn dynsymSlice(elf: *Elf) SymtabSlice {
- const slice = elf.si.dynsym.node(elf).slice(&elf.mf);
- return switch (elf.identClass()) {
- .NONE, _ => unreachable,
- inline else => |class| @unionInit(SymtabSlice, @tagName(class), @ptrCast(@alignCast(
- slice[0..std.mem.alignBackwardAnyAlign(usize, slice.len, @sizeOf(class.ElfN().Sym))],
- ))),
- };
-}
-
-fn addSymbolAssumeCapacity(elf: *Elf) Symbol.Index {
- defer elf.symtab.addOneAssumeCapacity().* = .{
- .ni = .none,
- .loc_relocs = .none,
- .target_relocs = .none,
- .unused = 0,
- };
- return @enumFromInt(elf.symtab.items.len);
-}
-
-fn initSymbolAssumeCapacity(elf: *Elf, opts: Symbol.Index.InitOptions) !Symbol.Index {
- const si = elf.addSymbolAssumeCapacity();
- try si.init(elf, opts);
- return si;
-}
-
-pub fn globalSymbol(elf: *Elf, opts: struct {
- name: []const u8,
- lib_name: ?[]const u8 = null,
- type: std.elf.STT,
- bind: std.elf.STB = .GLOBAL,
- visibility: std.elf.STV = .DEFAULT,
-}) !Symbol.Index {
- const gpa = elf.base.comp.gpa;
- try elf.symtab.ensureUnusedCapacity(gpa, 1);
- const global_gop = try elf.globals.getOrPut(gpa, try elf.string(.strtab, opts.name));
- if (!global_gop.found_existing) global_gop.value_ptr.* = try elf.initSymbolAssumeCapacity(.{
- .name = opts.name,
- .lib_name = opts.lib_name,
- .type = opts.type,
- .bind = opts.bind,
- .visibility = opts.visibility,
- });
- return global_gop.value_ptr.*;
+};
+fn symPtr(elf: *Elf, index: Symbol.Index) SymPtr {
+ const raw_slice = Section.Index.symtab.get(elf).ni.slice(&elf.mf);
+ switch (elf.shdrPtr(.symtab)) {
+ inline else => |shdr, class| {
+ const size = elf.targetLoad(&shdr.size);
+ const slice: []class.ElfN().Sym = @ptrCast(@alignCast(raw_slice[0..@intCast(size)]));
+ return @unionInit(SymPtr, @tagName(class), &slice[@intFromEnum(index)]);
+ },
+ }
+}
+fn dynsymPtr(elf: *Elf, index: u32) SymPtr {
+ const raw_slice = elf.shndx.dynsym.get(elf).ni.slice(&elf.mf);
+ switch (elf.shdrPtr(elf.shndx.dynsym)) {
+ inline else => |shdr, class| {
+ const size = elf.targetLoad(&shdr.size);
+ const slice: []class.ElfN().Sym = @ptrCast(@alignCast(raw_slice[0..@intCast(size)]));
+ return @unionInit(SymPtr, @tagName(class), &slice[index]);
+ },
+ }
}
fn navType(
@@ -1886,97 +2815,135 @@ fn navType(
else
.OBJECT;
}
-fn namedSection(elf: *const Elf, name: []const u8) ?Symbol.Index {
+fn namedSection(elf: *const Elf, name: []const u8) ?Section.Index {
if (std.mem.eql(u8, name, ".rodata") or
- std.mem.startsWith(u8, name, ".rodata.")) return elf.si.rodata;
+ std.mem.startsWith(u8, name, ".rodata.")) return .rodata;
if (std.mem.eql(u8, name, ".text") or
- std.mem.startsWith(u8, name, ".text.")) return elf.si.text;
+ std.mem.startsWith(u8, name, ".text.")) return .text;
if (std.mem.eql(u8, name, ".data") or
- std.mem.startsWith(u8, name, ".data.")) return elf.si.data;
+ std.mem.startsWith(u8, name, ".data.")) return .data;
if (std.mem.eql(u8, name, ".tdata") or
- std.mem.startsWith(u8, name, ".tdata.")) return elf.si.tdata;
+ std.mem.startsWith(u8, name, ".tdata.")) return elf.shndx.tdata;
return null;
}
-fn navSection(
- elf: *Elf,
- ip: *const InternPool,
- nav_resolved: @typeInfo(@FieldType(InternPool.Nav, "resolved")).optional.child,
-) Symbol.Index {
- if (nav_resolved.@"linksection".toSlice(ip)) |@"linksection"|
- if (elf.namedSection(@"linksection")) |si| return si;
- return switch (navType(
- ip,
- nav_resolved,
- elf.base.comp.config.any_non_single_threaded,
- )) {
- else => unreachable,
- .FUNC => elf.si.text,
- .OBJECT => elf.si.data,
- .TLS => elf.si.tdata,
- };
-}
fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex {
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
- try elf.symtab.ensureUnusedCapacity(gpa, 1);
- const nav_gop = try elf.navs.getOrPut(gpa, nav_index);
- if (!nav_gop.found_existing) nav_gop.value_ptr.* = try elf.initSymbolAssumeCapacity(.{
- .name = nav.fqn.toSlice(ip),
- .type = navType(ip, nav.resolved.?, elf.base.comp.config.any_non_single_threaded),
- });
- return @enumFromInt(nav_gop.index);
-}
-pub fn navSymbol(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
- const ip = &zcu.intern_pool;
- const nav = ip.getNav(nav_index);
- if (nav.getExtern(ip)) |@"extern"| return elf.globalSymbol(.{
- .name = @"extern".name.toSlice(ip),
- .lib_name = @"extern".lib_name.toSlice(ip),
- .type = navType(ip, nav.resolved.?, elf.base.comp.config.any_non_single_threaded),
- .bind = switch (@"extern".linkage) {
- .internal => .LOCAL,
- .strong => .GLOBAL,
- .weak => .WEAK,
- .link_once => return error.LinkOnceUnsupported,
- },
- .visibility = switch (@"extern".visibility) {
- .default => .DEFAULT,
- .hidden => .HIDDEN,
- .protected => .PROTECTED,
- },
- });
- const nmi = try elf.navMapIndex(zcu, nav_index);
- return nmi.symbol(elf);
-}
-fn uavMapIndex(elf: *Elf, uav_val: InternPool.Index) !Node.UavMapIndex {
- const gpa = elf.base.comp.gpa;
- try elf.symtab.ensureUnusedCapacity(gpa, 1);
- const uav_gop = try elf.uavs.getOrPut(gpa, uav_val);
- if (!uav_gop.found_existing)
- uav_gop.value_ptr.* = try elf.initSymbolAssumeCapacity(.{ .type = .OBJECT });
- return @enumFromInt(uav_gop.index);
-}
-pub fn uavSymbol(elf: *Elf, uav_val: InternPool.Index) !Symbol.Index {
- const umi = try elf.uavMapIndex(uav_val);
- return umi.symbol(elf);
+ try elf.ensureUnusedSymbolCapacity(1, .all_local);
+ try elf.nodes.ensureUnusedCapacity(gpa, 1);
+ try elf.navs.ensureUnusedCapacity(gpa, 1);
+
+ const nav_gop = elf.navs.getOrPutAssumeCapacity(nav_index);
+ const nmi: Node.NavMapIndex = @enumFromInt(nav_gop.index);
+ if (!nav_gop.found_existing) {
+ const sym_type = navType(ip, nav.resolved.?, elf.base.comp.config.any_non_single_threaded);
+ const shndx: Section.Index = section: {
+ if (nav.resolved.?.@"linksection".toSlice(ip)) |@"linksection"| {
+ if (elf.namedSection(@"linksection")) |shndx| break :section shndx;
+ }
+ break :section switch (sym_type) {
+ else => unreachable,
+ .FUNC => .text,
+ .OBJECT => .data,
+ .TLS => elf.shndx.tdata,
+ };
+ };
+ const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) {
+ .@"fn" => a: {
+ const mod = zcu.navFileScope(nav_index).mod.?;
+ const target = &mod.resolved_target.result;
+ const min = target_util.minFunctionAlignment(target);
+ break :a switch (nav.resolved.?.@"align") {
+ else => |a| a.maxStrict(min),
+ .none => switch (mod.optimize_mode) {
+ .Debug,
+ .ReleaseSafe,
+ .ReleaseFast,
+ => target_util.defaultFunctionAlignment(target),
+ .ReleaseSmall => min,
+ },
+ };
+ },
+ else => switch (nav.resolved.?.@"align") {
+ .none => Type.fromInterned(nav.resolved.?.type).abiAlignment(zcu),
+ else => |a| a,
+ },
+ };
+ const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{
+ .alignment = alignment.toStdMem(),
+ });
+ nav_gop.value_ptr.* = .{
+ .lsi = elf.addLocalSymbolAssumeCapacity(.{
+ .node = node,
+ .name = try elf.string(.strtab, nav.fqn.toSlice(ip)),
+ .value = 0,
+ .size = 0,
+ .type = sym_type,
+ .shndx = shndx,
+ }),
+ .first_reloc = .none,
+ };
+ elf.nodes.appendAssumeCapacity(.{ .nav = nmi });
+ }
+ return nmi;
}
-pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !Symbol.Index {
+fn uavMapIndex(
+ elf: *Elf,
+ uav_val: InternPool.Index,
+ uav_align: InternPool.Alignment,
+) !Node.UavMapIndex {
const gpa = elf.base.comp.gpa;
- try elf.symtab.ensureUnusedCapacity(gpa, 1);
- const lazy_gop = try elf.lazy.getPtr(lazy.kind).map.getOrPut(gpa, lazy.ty);
- if (!lazy_gop.found_existing) {
- lazy_gop.value_ptr.* = try elf.initSymbolAssumeCapacity(.{
- .type = switch (lazy.kind) {
- .code => .FUNC,
- .const_data => .OBJECT,
- },
+ const zcu = elf.base.comp.zcu.?;
+
+ try elf.ensureUnusedSymbolCapacity(1, .all_local);
+ try elf.nodes.ensureUnusedCapacity(gpa, 1);
+ try elf.uavs.ensureUnusedCapacity(gpa, 1);
+ try elf.pending_uavs.ensureUnusedCapacity(gpa, 1);
+
+ const abi_align = Value.fromInterned(uav_val).typeOf(zcu).abiAlignment(zcu);
+ const resolved_align: InternPool.Alignment = switch (uav_align) {
+ .none => abi_align,
+ else => |a| a.minStrict(abi_align),
+ };
+
+ const uav_gop = elf.uavs.getOrPutAssumeCapacity(uav_val);
+ const umi: Node.UavMapIndex = @enumFromInt(uav_gop.index);
+ if (!uav_gop.found_existing) {
+ const shndx: Section.Index = .data;
+ const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{
+ .moved = true, // see assert at end of `flushUav`
+ .alignment = resolved_align.toStdMem(),
});
- elf.synth_prog_node.increaseEstimatedTotalItems(1);
+ var name_buf: [32]u8 = undefined;
+ const name = std.fmt.bufPrint(
+ &name_buf,
+ "__anon_{d}",
+ .{@intFromEnum(uav_val)},
+ ) catch unreachable;
+ uav_gop.value_ptr.* = .{
+ .lsi = elf.addLocalSymbolAssumeCapacity(.{
+ .node = node,
+ .name = try elf.string(.strtab, name),
+ .value = 0,
+ .size = 0,
+ .type = .OBJECT,
+ .shndx = shndx,
+ }),
+ .first_reloc = .none,
+ };
+ elf.nodes.appendAssumeCapacity(.{ .uav = umi });
+ elf.const_prog_node.increaseEstimatedTotalItems(1);
+ elf.pending_uavs.appendAssumeCapacity(umi);
+ } else {
+ const node = uav_gop.value_ptr.lsi.index().ptr(elf).node;
+ if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) {
+ node.realign(&elf.mf, resolved_align.toStdMem());
+ }
}
- return lazy_gop.value_ptr.*;
+ return umi;
}
pub fn loadInput(elf: *Elf, input: link.Input) (Io.File.Reader.SizeError ||
@@ -2081,22 +3048,23 @@ fn loadObject(
const diags = &comp.link_diags;
const r = &fr.interface;
- const ii: Node.InputIndex = @enumFromInt(elf.inputs.items.len);
+ const input_index: Node.InputIndex = @enumFromInt(elf.inputs.items.len);
log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberString(member) });
- const ident = try r.peek(std.elf.EI.OSABI);
- if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic;
- if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.memory_map.memory[std.elf.MAGIC.len..ident.len]))
- return diags.failParse(path, "bad ident", .{});
- try elf.symtab.ensureUnusedCapacity(gpa, 1);
+ try elf.checkInputIdent(path, r);
+ try elf.ensureUnusedSymbolCapacity(1, .all_local);
try elf.inputs.ensureUnusedCapacity(gpa, 1);
+ const file_symbol = elf.addLocalSymbolAssumeCapacity(.{
+ .node = .none,
+ .name = try elf.string(.strtab, std.fs.path.stem(member orelse path.sub_path)),
+ .value = 0,
+ .size = 0,
+ .type = .FILE,
+ .shndx = .ABS,
+ });
elf.inputs.addOneAssumeCapacity().* = .{
.path = path,
.member = if (member) |m| try gpa.dupe(u8, m) else null,
- .si = try elf.initSymbolAssumeCapacity(.{
- .name = std.fs.path.stem(member orelse path.sub_path),
- .type = .FILE,
- .shndx = .ABS,
- }),
+ .file_symbol = file_symbol,
};
const target_endian = elf.targetEndian();
switch (elf.identClass()) {
@@ -2108,17 +3076,17 @@ fn loadObject(
if (ehdr.machine != elf.ehdrField(.machine))
return diags.failParse(path, "bad machine", .{});
if (ehdr.shoff == 0 or ehdr.shnum <= 1) return;
- if (ehdr.shoff + ehdr.shentsize * ehdr.shnum > fl.size)
+ if (ehdr.shoff + @as(u64, ehdr.shentsize) * @as(u64, ehdr.shnum) > fl.size)
return diags.failParse(path, "bad section header location", .{});
if (ehdr.shentsize < @sizeOf(ElfN.Shdr))
return diags.failParse(path, "unsupported shentsize", .{});
- const sections = try gpa.alloc(struct { shdr: ElfN.Shdr, si: Symbol.Index }, ehdr.shnum);
+ const sections = try gpa.alloc(struct { shdr: ElfN.Shdr, isi: ?InputSection.Index }, ehdr.shnum);
defer gpa.free(sections);
try fr.seekTo(fl.offset + ehdr.shoff);
for (sections) |*section| {
section.* = .{
.shdr = try r.peekStruct(ElfN.Shdr, target_endian),
- .si = .null,
+ .isi = null,
};
try r.discardAll(ehdr.shentsize);
switch (section.shdr.type) {
@@ -2140,42 +3108,49 @@ fn loadObject(
};
defer gpa.free(shstrtab);
try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
- try elf.symtab.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
for (sections[1..]) |*section| switch (section.shdr.type) {
else => {},
.PROGBITS, .NOBITS => {
if (section.shdr.name >= shstrtab.len) continue;
const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
- const parent_si = elf.namedSection(name) orelse continue;
- const ni = try elf.mf.addLastChildNode(gpa, parent_si.node(elf), .{
+ const shndx: Section.Index = elf.namedSection(name) orelse shndx: {
+ // TODO: actually generate a .bss section. For now, just throw it into `.data`.
+ if (std.mem.eql(u8, name, ".bss") or
+ std.mem.startsWith(u8, name, ".bss.")) break :shndx .data;
+ if (std.mem.eql(u8, name, ".tbss") or
+ std.mem.startsWith(u8, name, ".tbss.")) break :shndx elf.shndx.tdata;
+ break :shndx .UNDEF;
+ };
+ if (shndx == .UNDEF) continue;
+ const ni = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{
.size = section.shdr.size,
.alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
usize,
@intCast(@max(section.shdr.addralign, 1)),
)),
- .moved = true,
+ .moved = true, // see assert at end of `flushInputSection`
});
elf.nodes.appendAssumeCapacity(.{
.input_section = @enumFromInt(elf.input_sections.items.len),
});
- section.si = try elf.initSymbolAssumeCapacity(.{
- .type = .SECTION,
- .shndx = parent_si.shndx(elf),
- });
- section.si.get(elf).ni = ni;
+ section.isi = @enumFromInt(elf.input_sections.items.len);
elf.input_sections.addOneAssumeCapacity().* = .{
- .ii = ii,
- .si = section.si,
+ .input = input_index,
.file_location = .{
.offset = fl.offset + section.shdr.offset,
- .size = section.shdr.size,
+ .size = if (section.shdr.type == .NOBITS) 0 else section.shdr.size,
},
+ // The section vaddr is initially 0, because the symbol addresses are
+ // zero-based. This will eventually be updated by `flushMoved`.
+ .vaddr = 0,
+ .node = ni,
+ .first_reloc = .none,
};
elf.synth_prog_node.increaseEstimatedTotalItems(1);
},
};
- var symmap: std.ArrayList(Symbol.Index) = .empty;
+ var symmap: std.ArrayList(Symbol.Id) = .empty;
defer symmap.deinit(gpa);
for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) {
else => {},
@@ -2206,105 +3181,132 @@ fn loadObject(
), 1) catch continue;
symmap.clearRetainingCapacity();
try symmap.resize(gpa, symnum);
- try elf.symtab.ensureUnusedCapacity(gpa, symnum);
- try elf.globals.ensureUnusedCapacity(gpa, symnum);
+ try elf.ensureUnusedSymbolCapacity(symnum, .maybe_global);
try fr.seekTo(fl.offset + symtab.shdr.offset + symtab.shdr.entsize);
for (symmap.items) |*si| {
si.* = .null;
const input_sym = try r.peekStruct(ElfN.Sym, target_endian);
try r.discardAll64(symtab.shdr.entsize);
- if (input_sym.name >= strtab.len or input_sym.shndx == std.elf.SHN_UNDEF or
- input_sym.shndx >= ehdr.shnum) continue;
- switch (input_sym.info.type) {
- .NOTYPE, .OBJECT, .FUNC => {},
- .SECTION => {
- const section = §ions[input_sym.shndx];
- if (input_sym.value == section.shdr.addr) si.* = section.si;
+ if (input_sym.name >= strtab.len or input_sym.shndx >= ehdr.shnum) continue;
+
+ const name = std.mem.sliceTo(strtab[input_sym.name..], 0);
+
+ const sym_type: std.elf.STT = switch (input_sym.info.type) {
+ .NOTYPE, .OBJECT, .FUNC, .TLS => |t| t,
+ .SECTION => .NOTYPE,
+ .FILE, .COMMON, _ => continue,
+ };
+
+ if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) {
+ _ => |bind| return diags.failParse(
+ path,
+ "symbol '{s}' has unsupported binding (0x{x})",
+ .{ name, bind },
+ ),
+ .LOCAL => continue,
+ .GLOBAL, .WEAK => |bind| {
+ si.* = elf.addGlobalSymbolAssumeCapacity(.{
+ .node = .none,
+ .name = try .string(elf, name),
+ .value = input_sym.value,
+ .size = input_sym.size,
+ .type = sym_type,
+ .bind = if (bind == .WEAK) .weak else .strong,
+ .visibility = input_sym.other.visibility,
+ .shndx = .UNDEF,
+ }) catch |err| switch (err) {
+ error.MultipleDefinitions => unreachable, // shndx is .UNDEF
+ };
continue;
},
- else => continue,
- }
- const name = std.mem.sliceTo(strtab[input_sym.name..], 0);
- const parent_si = sections[input_sym.shndx].si;
- si.* = try elf.initSymbolAssumeCapacity(.{
- .name = name,
- .value = input_sym.value,
- .size = input_sym.size,
- .type = input_sym.info.type,
- .bind = input_sym.info.bind,
- .visibility = input_sym.other.visibility,
- .shndx = parent_si.shndx(elf),
- });
- si.get(elf).ni = parent_si.get(elf).ni;
+ };
+
+ const input_section_node = (sections[input_sym.shndx].isi orelse continue).node(elf);
+
switch (input_sym.info.bind) {
- else => {},
- .GLOBAL => {
- const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad(
- &@field(elf.symPtr(si.*), @tagName(class)).name,
- ));
- if (gop.found_existing) switch (elf.targetLoad(
- switch (elf.symPtr(gop.value_ptr.*)) {
- inline else => |sym| &sym.info,
- },
- ).bind) {
- else => unreachable,
- .GLOBAL => return diags.failParse(
+ _ => |bind| return diags.failParse(
+ path,
+ "symbol '{s}' has unsupported binding (0x{x})",
+ .{ name, bind },
+ ),
+ .LOCAL => {
+ const lsi = elf.addLocalSymbolAssumeCapacity(.{
+ .node = input_section_node,
+ .name = try elf.string(.strtab, name),
+ .value = input_sym.value,
+ .size = input_sym.size,
+ .type = sym_type,
+ .shndx = elf.getNodeShndx(input_section_node),
+ });
+ si.* = .local(lsi);
+ },
+ .GLOBAL, .WEAK => |bind| {
+ si.* = elf.addGlobalSymbolAssumeCapacity(.{
+ .node = input_section_node,
+ .name = try .string(elf, name),
+ .value = input_sym.value,
+ .size = input_sym.size,
+ .type = sym_type,
+ .bind = if (bind == .WEAK) .weak else .strong,
+ .visibility = input_sym.other.visibility,
+ .shndx = elf.getNodeShndx(input_section_node),
+ }) catch |err| switch (err) {
+ error.MultipleDefinitions => return diags.failParse(
path,
"multiple definitions of '{s}'",
.{name},
),
- .WEAK => {},
};
- gop.value_ptr.* = si.*;
- },
- .WEAK => {
- const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad(
- &@field(elf.symPtr(si.*), @tagName(class)).name,
- ));
- if (!gop.found_existing) gop.value_ptr.* = si.*;
},
}
}
- for (sections[1..]) |*rels| switch (rels.shdr.type) {
+ for (sections[1..]) |*rel_sec| switch (rel_sec.shdr.type) {
else => {},
inline .REL, .RELA => |sht| {
- if (rels.shdr.link != symtab_shndx or rels.shdr.info == std.elf.SHN_UNDEF or
- rels.shdr.info >= ehdr.shnum) continue;
+ if (rel_sec.shdr.link != symtab_shndx or rel_sec.shdr.info == std.elf.SHN_UNDEF or
+ rel_sec.shdr.info >= ehdr.shnum) continue;
const Rel = switch (sht) {
else => comptime unreachable,
.REL => ElfN.Rel,
.RELA => ElfN.Rela,
};
- if (rels.shdr.entsize < @sizeOf(Rel))
+ if (rel_sec.shdr.entsize < @sizeOf(Rel))
return diags.failParse(path, "unsupported rel entsize", .{});
- const loc_sec = §ions[rels.shdr.info];
- if (loc_sec.si == .null) continue;
- const loc_sym = loc_sec.si.get(elf);
- assert(loc_sym.loc_relocs == .none);
- loc_sym.loc_relocs = @enumFromInt(elf.relocs.items.len);
+ const loc_sec = §ions[rel_sec.shdr.info];
+ const loc_node = (loc_sec.isi orelse continue).node(elf);
+ elf.resetNodeRelocs(loc_node);
const relnum = std.math.divExact(
u32,
- @intCast(rels.shdr.size),
- @intCast(rels.shdr.entsize),
+ @intCast(rel_sec.shdr.size),
+ @intCast(rel_sec.shdr.entsize),
) catch return diags.failParse(
path,
"relocation section size (0x{x}) is not a multiple of entsize (0x{x})",
- .{ rels.shdr.size, rels.shdr.entsize },
+ .{ rel_sec.shdr.size, rel_sec.shdr.entsize },
);
- try elf.ensureUnusedRelocCapacity(loc_sec.si, relnum);
- try fr.seekTo(fl.offset + rels.shdr.offset);
+ try elf.ensureUnusedRelocCapacity(loc_node, relnum);
+ try fr.seekTo(fl.offset + rel_sec.shdr.offset);
for (0..relnum) |_| {
const rel = try r.peekStruct(Rel, target_endian);
- try r.discardAll64(rels.shdr.entsize);
- if (rel.info.sym == 0 or rel.info.sym > symnum) continue;
- const target_si = symmap.items[rel.info.sym - 1];
- if (target_si == .null) continue;
+ try r.discardAll64(rel_sec.shdr.entsize);
+ if (rel.info.sym == 0) continue;
+ if (rel.info.sym > symnum) return diags.failParse(
+ path,
+ "relocation target symbol index {d} exceeds symtab size",
+ .{rel.info.sym},
+ );
+ const target = symmap.items[rel.info.sym - 1];
+ if (target == Symbol.Id.null) return diags.failParse(
+ path,
+ "unsupported symbol at index {d} required for relocation",
+ .{rel.info.sym},
+ );
elf.addRelocAssumeCapacity(
- loc_sec.si,
+ loc_node,
rel.offset - loc_sec.shdr.addr,
- target_si,
+ target,
rel.addend,
.wrap(rel.info.type, elf),
);
@@ -2322,10 +3324,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
const r = &fr.interface;
log.debug("loadDso({f})", .{path.fmtEscapeString()});
- const ident = try r.peek(std.elf.EI.NIDENT);
- if (!std.mem.eql(u8, ident[0..std.elf.MAGIC.len], std.elf.MAGIC)) return error.BadMagic;
- if (!std.mem.eql(u8, ident[std.elf.MAGIC.len..], elf.mf.memory_map.memory[std.elf.MAGIC.len..ident.len]))
- return diags.failParse(path, "bad ident", .{});
+ try elf.checkInputIdent(path, r);
const target_endian = elf.targetEndian();
switch (elf.identClass()) {
.NONE, _ => unreachable,
@@ -2393,7 +3392,62 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
}
fn loadDsoExact(elf: *Elf, name: []const u8) !void {
log.debug("loadDsoExact({f})", .{std.zig.fmtString(name)});
- try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, name), {});
+ if (elf.shndx.dynamic != .UNDEF) {
+ try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, name), {});
+ }
+}
+
+/// Validates that the `std.elf.Ident` present at the start of `r` is a compatible link input.
+///
+/// Returns an error if it is incompatible, or if the ident is broken or missing.
+///
+/// Does not advance the position of `r`. Requires `r` to have a 16-byte buffer.
+fn checkInputIdent(
+ elf: *const Elf,
+ path: std.Build.Cache.Path,
+ r: *Io.Reader,
+) !void {
+ const diags = &elf.base.comp.link_diags;
+
+ const ident = try r.peekStructPointer(std.elf.Ident);
+ const target: *const std.elf.Ident = @ptrCast(elf.mf.memory_map.memory[0..@sizeOf(std.elf.Ident)]);
+
+ if (!std.mem.eql(u8, &ident.magic, std.elf.MAGIC)) {
+ return error.BadMagic;
+ }
+
+ if (ident.class != target.class) return diags.failParse(
+ path,
+ "bad ELF class ({?s})",
+ .{std.enums.tagName(std.elf.CLASS, ident.class)},
+ );
+ if (ident.data != target.data) return diags.failParse(
+ path,
+ "bad ELF data encoding ({?s})",
+ .{std.enums.tagName(std.elf.DATA, ident.data)},
+ );
+ if (ident.version != target.version) return diags.failParse(
+ path,
+ "bad ELF version ({d})",
+ .{ident.version},
+ );
+
+ // OSABI is a bit more complex. On Linux, `.NONE` and `.GNU` are both valid and both common.
+ // It sounds reasonable to allow the value we chose *and* allow `.NONE`.
+ const expect_abiversion: u8 = abiver: {
+ if (ident.osabi == .NONE) break :abiver 0;
+ if (ident.osabi == target.osabi) break :abiver target.abiversion;
+ return diags.failParse(
+ path,
+ "bad ELF OS/ABI ({?s})",
+ .{std.enums.tagName(std.elf.OSABI, ident.osabi)},
+ );
+ };
+ if (ident.abiversion != expect_abiversion) return diags.failParse(
+ path,
+ "bad ELF ABI version ({d})",
+ .{ident.abiversion},
+ );
}
pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void {
@@ -2406,20 +3460,27 @@ pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void {
fn prelinkInner(elf: *Elf) !void {
const comp = elf.base.comp;
const gpa = comp.gpa;
- try elf.symtab.ensureUnusedCapacity(gpa, 1);
+ try elf.ensureUnusedSymbolCapacity(1, .all_local);
try elf.inputs.ensureUnusedCapacity(gpa, 1);
const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{
std.fs.path.stem(elf.base.emit.sub_path),
});
defer gpa.free(zcu_name);
- const si = try elf.initSymbolAssumeCapacity(.{ .name = zcu_name, .type = .FILE, .shndx = .ABS });
+ const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{
+ .node = .none,
+ .name = try elf.string(.strtab, zcu_name),
+ .value = 0,
+ .size = 0,
+ .type = .FILE,
+ .shndx = .ABS,
+ });
elf.inputs.addOneAssumeCapacity().* = .{
.path = elf.base.emit,
.member = null,
- .si = si,
+ .file_symbol = zcu_file_symbol,
};
- if (elf.si.dynamic != .null) switch (elf.identClass()) {
+ if (elf.shndx.dynamic != .UNDEF) switch (elf.identClass()) {
.NONE, _ => unreachable,
inline else => |ct_class| {
const ElfN = ct_class.ElfN();
@@ -2430,9 +3491,9 @@ fn prelinkInner(elf: *Elf) !void {
@intFromBool(flags != 0) + @intFromBool(flags_1 != 0) +
@intFromBool(comp.config.output_mode == .Exe) + 12;
const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len);
- const dynamic_ni = elf.si.dynamic.node(elf);
+ const dynamic_ni = elf.shndx.dynamic.get(elf).ni;
try dynamic_ni.resize(&elf.mf, gpa, dynamic_size);
- switch (elf.shdrPtr(elf.si.dynamic.shndx(elf))) {
+ switch (elf.shdrPtr(elf.shndx.dynamic)) {
inline else => |shdr| elf.targetStore(&shdr.size, dynamic_size),
}
const sec_dynamic = dynamic_ni.slice(&elf.mf);
@@ -2441,10 +3502,10 @@ fn prelinkInner(elf: *Elf) !void {
for (
dynamic_entries[dynamic_index..][0..needed_len],
elf.needed.keys(),
- ) |*dynamic_entry, needed| dynamic_entry.* = .{ std.elf.DT_NEEDED, needed };
+ ) |*dynamic_entry, needed| dynamic_entry.* = .{ std.elf.DT_NEEDED, @intFromEnum(needed) };
dynamic_index += needed_len;
if (elf.options.soname) |soname| {
- dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, try elf.string(.dynstr, soname) };
+ dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(try elf.string(.dynstr, soname)) };
dynamic_index += 1;
}
if (flags != 0) {
@@ -2459,25 +3520,25 @@ fn prelinkInner(elf: *Elf) !void {
dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 };
dynamic_index += 1;
}
- const rela_dyn_si = elf.si.got.shndx(elf).get(elf).rela_si;
- const rela_plt_si = elf.si.got_plt.shndx(elf).get(elf).rela_si;
+ const rela_dyn_shndx = elf.shndx.got.get(elf).rela_shndx;
+ const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx;
dynamic_entries[dynamic_index..][0..12].* = .{
- .{ std.elf.DT_RELA, @intCast(elf.computeNodeVAddr(rela_dyn_si.node(elf))) },
+ .{ std.elf.DT_RELA, @intCast(elf.computeNodeVAddr(rela_dyn_shndx.get(elf).ni)) },
.{ std.elf.DT_RELASZ, elf.targetLoad(
- &@field(elf.shdrPtr(rela_dyn_si.shndx(elf)), @tagName(ct_class)).size,
+ &@field(elf.shdrPtr(rela_dyn_shndx), @tagName(ct_class)).size,
) },
.{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
- .{ std.elf.DT_JMPREL, @intCast(elf.computeNodeVAddr(rela_plt_si.node(elf))) },
+ .{ std.elf.DT_JMPREL, @intCast(elf.computeNodeVAddr(rela_plt_shndx.get(elf).ni)) },
.{ std.elf.DT_PLTRELSZ, elf.targetLoad(
- &@field(elf.shdrPtr(rela_plt_si.shndx(elf)), @tagName(ct_class)).size,
+ &@field(elf.shdrPtr(rela_plt_shndx), @tagName(ct_class)).size,
) },
- .{ std.elf.DT_PLTGOT, @intCast(elf.computeNodeVAddr(elf.si.got_plt.node(elf))) },
+ .{ std.elf.DT_PLTGOT, @intCast(elf.computeNodeVAddr(elf.shndx.got_plt.get(elf).ni)) },
.{ std.elf.DT_PLTREL, std.elf.DT_RELA },
- .{ std.elf.DT_SYMTAB, @intCast(elf.computeNodeVAddr(elf.si.dynsym.node(elf))) },
+ .{ std.elf.DT_SYMTAB, @intCast(elf.computeNodeVAddr(elf.shndx.dynsym.get(elf).ni)) },
.{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
- .{ std.elf.DT_STRTAB, @intCast(elf.computeNodeVAddr(elf.si.dynstr.node(elf))) },
+ .{ std.elf.DT_STRTAB, @intCast(elf.computeNodeVAddr(elf.shndx.dynstr.get(elf).ni)) },
.{ std.elf.DT_STRSZ, elf.targetLoad(
- &@field(elf.shdrPtr(elf.si.dynstr.shndx(elf)), @tagName(ct_class)).size,
+ &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size,
) },
.{ std.elf.DT_NULL, 0 },
};
@@ -2486,42 +3547,40 @@ fn prelinkInner(elf: *Elf) !void {
if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|
std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);
- const dynamic_sym = elf.si.dynamic.get(elf);
- assert(dynamic_sym.loc_relocs == .none);
- dynamic_sym.loc_relocs = @enumFromInt(elf.relocs.items.len);
- try elf.ensureUnusedRelocCapacity(elf.si.dynamic, 5);
+ elf.first_dynamic_reloc = @enumFromInt(elf.relocs.items.len);
+ try elf.ensureUnusedRelocCapacity(dynamic_ni, 5);
elf.addRelocAssumeCapacity(
- elf.si.dynamic,
+ dynamic_ni,
@sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1),
- rela_dyn_si,
+ .local(rela_dyn_shndx.get(elf).lsi),
0,
.absAddr(elf),
);
elf.addRelocAssumeCapacity(
- elf.si.dynamic,
+ dynamic_ni,
@sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1),
- rela_plt_si,
+ .local(rela_plt_shndx.get(elf).lsi),
0,
.absAddr(elf),
);
elf.addRelocAssumeCapacity(
- elf.si.dynamic,
+ dynamic_ni,
@sizeOf(ElfN.Addr) * (2 * (dynamic_len - 7) + 1),
- elf.si.got_plt,
+ .local(elf.shndx.got_plt.get(elf).lsi),
0,
.absAddr(elf),
);
elf.addRelocAssumeCapacity(
- elf.si.dynamic,
+ dynamic_ni,
@sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1),
- elf.si.dynsym,
+ .local(elf.shndx.dynsym.get(elf).lsi),
0,
.absAddr(elf),
);
elf.addRelocAssumeCapacity(
- elf.si.dynamic,
+ dynamic_ni,
@sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1),
- elf.si.dynstr,
+ .local(elf.shndx.dynstr.get(elf).lsi),
0,
.absAddr(elf),
);
@@ -2529,36 +3588,6 @@ fn prelinkInner(elf: *Elf) !void {
};
}
-pub fn getNavVAddr(
- elf: *Elf,
- pt: Zcu.PerThread,
- nav: InternPool.Nav.Index,
- reloc_info: link.File.RelocInfo,
-) !u64 {
- return elf.getVAddr(reloc_info, try elf.navSymbol(pt.zcu, nav));
-}
-
-pub fn getUavVAddr(
- elf: *Elf,
- uav: InternPool.Index,
- reloc_info: link.File.RelocInfo,
-) !u64 {
- return elf.getVAddr(reloc_info, try elf.uavSymbol(uav));
-}
-
-pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target_si: Symbol.Index) !u64 {
- try elf.addReloc(
- @enumFromInt(reloc_info.parent.atom_index),
- reloc_info.offset,
- target_si,
- reloc_info.addend,
- .absAddr(elf),
- );
- return switch (elf.symPtr(target_si)) {
- inline else => |sym| elf.targetLoad(&sym.value),
- };
-}
-
fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
name: []const u8 = "",
type: std.elf.SHT = .NULL,
@@ -2570,19 +3599,22 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
entsize: std.elf.Word = 0,
node_align: std.mem.Alignment = .@"1",
fixed: bool = false,
-}) !Symbol.Index {
+}) !Section.Index {
switch (opts.type) {
.NULL => assert(opts.size == 0),
.PROGBITS => assert(opts.size > 0),
else => {},
}
+ if (opts.flags.ALLOC and elf.ehdrField(.type) != .REL) {
+ assert(elf.getNode(segment_ni) == .segment);
+ }
const gpa = elf.base.comp.gpa;
try elf.nodes.ensureUnusedCapacity(gpa, 1);
try elf.shdrs.ensureUnusedCapacity(gpa, 1);
- try elf.symtab.ensureUnusedCapacity(gpa, 1);
+ if (opts.flags.ALLOC) try elf.ensureUnusedSymbolCapacity(1, .all_local);
const shstrtab_entry = try elf.string(.shstrtab, opts.name);
- const shndx: Symbol.Index.Shndx, const new_shdr_size = shndx: switch (elf.ehdrPtr()) {
+ const shndx: Section.Index, const new_shdr_size = shndx: switch (elf.ehdrPtr()) {
inline else => |ehdr, class| {
const shndx, const shnum = alloc_shndx: switch (elf.targetLoad(&ehdr.shnum)) {
1...std.elf.SHN_LORESERVE - 2 => |shndx| {
@@ -2605,8 +3637,8 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
break :alloc_shndx .{ shndx, shnum };
},
};
- assert(shndx < @intFromEnum(Symbol.Index.Shndx.LORESERVE));
- break :shndx .{ @enumFromInt(shndx), elf.targetLoad(&ehdr.shentsize) * shnum };
+ assert(shndx < @intFromEnum(Section.Index.LORESERVE));
+ break :shndx .{ @enumFromInt(shndx), @as(u64, elf.targetLoad(&ehdr.shentsize)) * @as(u64, shnum) };
},
};
_, const shdr_node_size = elf.ni.shdr.location(&elf.mf).resolve(&elf.mf);
@@ -2622,17 +3654,22 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
.fixed = opts.fixed,
.resized = opts.size > 0,
});
- const si = elf.addSymbolAssumeCapacity();
- elf.nodes.appendAssumeCapacity(.{ .section = si });
- elf.shdrs.appendAssumeCapacity(.{ .si = si, .rela_si = .null, .rela_free = .none });
- si.get(elf).ni = ni;
const addr = elf.computeNodeVAddr(ni);
+ const lsi: Symbol.LocalIndex = if (opts.flags.ALLOC) elf.addLocalSymbolAssumeCapacity(.{
+ .node = ni,
+ .name = .empty,
+ .value = addr,
+ .size = 0,
+ .type = .SECTION,
+ .shndx = shndx,
+ }) else .null;
+ elf.shdrs.appendAssumeCapacity(.{ .lsi = lsi, .ni = ni, .rela_shndx = .UNDEF, .rela_free = .none });
+ elf.nodes.appendAssumeCapacity(.{ .section = shndx });
const offset = ni.fileLocation(&elf.mf, false).offset;
- try si.init(elf, .{ .value = addr, .type = .SECTION, .shndx = shndx });
switch (elf.shdrPtr(shndx)) {
inline else => |shdr, class| {
shdr.* = .{
- .name = shstrtab_entry,
+ .name = @intFromEnum(shstrtab_entry),
.type = opts.type,
.flags = .{ .shf = opts.flags },
.addr = @intCast(addr),
@@ -2646,63 +3683,31 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(class.ElfN().Shdr, shdr);
},
}
- return si;
-}
-
-fn renameSection(elf: *Elf, si: Symbol.Index, name: []const u8) !void {
- const shstrtab_entry = try elf.string(.shstrtab, name);
- switch (elf.shdrPtr(si.shndx(elf))) {
- inline else => |shdr| elf.targetStore(&shdr.name, shstrtab_entry),
- }
-}
-
-fn sectionName(elf: *Elf, si: Symbol.Index) [:0]const u8 {
- const name = elf.si.shstrtab.node(elf).slice(&elf.mf)[switch (elf.shdrPtr(si.shndx(elf))) {
- inline else => |shdr| elf.targetLoad(&shdr.name),
- }..];
- return name[0..std.mem.indexOfScalar(u8, name, 0).? :0];
-}
-
-fn string(elf: *Elf, comptime section: enum { shstrtab, strtab, dynstr }, key: []const u8) !u32 {
- if (key.len == 0) return 0;
- return @field(elf, @tagName(section)).get(elf, @field(elf.si, @tagName(section)), key);
+ return shndx;
}
-pub fn addReloc(
- elf: *Elf,
- loc_si: Symbol.Index,
- offset: u64,
- target_si: Symbol.Index,
- addend: i64,
- @"type": Reloc.Type,
-) !void {
- try elf.ensureUnusedRelocCapacity(loc_si, 1);
- elf.addRelocAssumeCapacity(loc_si, offset, target_si, addend, @"type");
-}
-pub fn ensureUnusedRelocCapacity(elf: *Elf, loc_si: Symbol.Index, len: usize) !void {
+fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) !void {
if (len == 0) return;
const gpa = elf.base.comp.gpa;
try elf.relocs.ensureUnusedCapacity(gpa, len);
const class = elf.identClass();
- const rela_si, const rela_len = rela: switch (elf.ehdrField(.type)) {
+ const rela_shndx, const rela_len = rela: switch (elf.ehdrField(.type)) {
.NONE, .CORE, _ => unreachable,
.REL => {
- const shndx = loc_si.shndx(elf);
- const sh = shndx.get(elf);
- if (sh.rela_si == .null) {
+ const shndx = elf.getNodeShndx(node);
+ if (shndx.get(elf).rela_shndx == .UNDEF) {
var bfa_buf: [32]u8 = undefined;
var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa);
const allocator = bfa.allocator();
- const rela_name =
- try std.fmt.allocPrint(allocator, ".rela{s}", .{elf.sectionName(sh.si)});
+ const rela_name = try std.fmt.allocPrint(allocator, ".rela{s}", .{shndx.name(elf)});
defer allocator.free(rela_name);
- sh.rela_si = try elf.addSection(.none, .{
+ const rela_shndx = try elf.addSection(.none, .{
.name = rela_name,
.type = .RELA,
- .link = @intFromEnum(elf.si.symtab.shndx(elf)),
- .info = @intFromEnum(shndx),
+ .link = @intFromEnum(Section.Index.symtab),
+ .info = shndx.toSection().?,
.addralign = switch (class) {
.NONE, _ => unreachable,
.@"32" => .@"4",
@@ -2714,14 +3719,15 @@ pub fn ensureUnusedRelocCapacity(elf: *Elf, loc_si: Symbol.Index, len: usize) !v
},
.node_align = elf.mf.flags.block_size,
});
+ shndx.get(elf).rela_shndx = rela_shndx;
}
- break :rela .{ sh.rela_si, len };
+ break :rela .{ shndx.get(elf).rela_shndx, len };
},
.EXEC, .DYN => switch (elf.got.tlsld) {
_ => return,
- .none => if (elf.si.dynamic != .null) {
+ .none => if (elf.shndx.dynamic != .UNDEF) {
try elf.mf.updates.ensureUnusedCapacity(gpa, 1);
- const got_ni = elf.si.got.node(elf);
+ const got_ni = elf.shndx.got.get(elf).ni;
_, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf);
const got_size = switch (class) {
.NONE, _ => unreachable,
@@ -2729,43 +3735,52 @@ pub fn ensureUnusedRelocCapacity(elf: *Elf, loc_si: Symbol.Index, len: usize) !v
};
if (got_size > got_node_size)
try got_ni.resize(&elf.mf, gpa, got_size +| got_size / MappedFile.growth_factor);
- break :rela .{ elf.si.got.shndx(elf).get(elf).rela_si, 1 };
+ break :rela .{ elf.shndx.got.get(elf).rela_shndx, 1 };
} else return,
},
};
- const rela_ni = rela_si.node(elf);
+ const rela_ni = rela_shndx.get(elf).ni;
_, const rela_node_size = rela_ni.location(&elf.mf).resolve(&elf.mf);
- const rela_size = switch (elf.shdrPtr(rela_si.shndx(elf))) {
+ const rela_size = switch (elf.shdrPtr(rela_shndx)) {
inline else => |shdr| elf.targetLoad(&shdr.size) + elf.targetLoad(&shdr.entsize) * rela_len,
};
if (rela_size > rela_node_size)
try rela_ni.resize(&elf.mf, gpa, rela_size +| rela_size / MappedFile.growth_factor);
}
-pub fn addRelocAssumeCapacity(
+fn addRelocAssumeCapacity(
elf: *Elf,
- loc_si: Symbol.Index,
+ node: MappedFile.Node.Index,
offset: u64,
- target_si: Symbol.Index,
+ target: Symbol.Id,
addend: i64,
@"type": Reloc.Type,
) void {
- const target = target_si.get(elf);
+ assert(node != .none);
const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len);
+ const next: Reloc.Index = next: {
+ const target_ptr = target.index(elf).ptr(elf);
+ const next = target_ptr.first_target_reloc;
+ target_ptr.first_target_reloc = ri;
+ break :next next;
+ };
+ if (next != .none) {
+ next.get(elf).prev = ri;
+ }
elf.relocs.addOneAssumeCapacity().* = .{
.type = @"type",
.prev = .none,
- .next = target.target_relocs,
- .loc = loc_si,
- .target = target_si,
+ .next = next,
+ .node = node,
+ .target = target,
.index = index: switch (elf.ehdrField(.type)) {
.NONE, .CORE, _ => unreachable,
.REL => {
- const sh = loc_si.shndx(elf).get(elf);
- switch (elf.shdrPtr(sh.rela_si.shndx(elf))) {
+ const sh = elf.getNodeShndx(node).get(elf);
+ switch (elf.shdrPtr(sh.rela_shndx)) {
inline else => |shdr, class| {
const Rela = class.ElfN().Rela;
const ent_size = elf.targetLoad(&shdr.entsize);
- const rela_slice = sh.rela_si.node(elf).slice(&elf.mf);
+ const rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf);
const index: u32 = if (sh.rela_free.unwrap()) |index| alloc_index: {
const rela: *Rela = @ptrCast(@alignCast(
rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)],
@@ -2781,11 +3796,15 @@ pub fn addRelocAssumeCapacity(
const rela: *Rela = @ptrCast(@alignCast(
rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)],
));
+ // The `offset` field here needs to equal the offset into the section, which
+ // is *not* the same as our `offset` which is the offset into `node`. We
+ // could calculate it now, but there's no point since `flushMovedNodeRelocs`
+ // will eventually do that for us anyway. So for now, just set offset to 0.
rela.* = .{
- .offset = @intCast(offset),
+ .offset = 0,
.info = .{
.type = @intCast(@"type".unwrap(elf)),
- .sym = @intCast(@intFromEnum(target_si)),
+ .sym = @intCast(@intFromEnum(target.index(elf))),
},
.addend = @intCast(addend),
};
@@ -2802,25 +3821,25 @@ pub fn addRelocAssumeCapacity(
else => {},
.TLSLD => switch (elf.got.tlsld) {
_ => {},
- .none => if (elf.si.dynamic != .null) {
+ .none => if (elf.shndx.dynamic != .UNDEF) {
const tlsld_index = elf.got.len;
elf.got.tlsld = .wrap(tlsld_index);
elf.got.len = tlsld_index + 2;
- const got_addr = got_addr: switch (elf.shdrPtr(elf.si.got.shndx(elf))) {
+ const got_addr = got_addr: switch (elf.shdrPtr(elf.shndx.got)) {
inline else => |shdr, class| {
const addr_size = @sizeOf(class.ElfN().Addr);
const old_size = addr_size * tlsld_index;
const new_size = old_size + addr_size * 2;
@memset(
- elf.si.got.node(elf).slice(&elf.mf)[old_size..new_size],
+ elf.shndx.got.get(elf).ni.slice(&elf.mf)[old_size..new_size],
0,
);
break :got_addr elf.targetLoad(&shdr.addr) + old_size;
},
};
- const rela_dyn_si = elf.si.got.shndx(elf).get(elf).rela_si;
- const rela_dyn_ni = rela_dyn_si.node(elf);
- switch (elf.shdrPtr(rela_dyn_si.shndx(elf))) {
+ const rela_dyn_shndx = elf.shndx.got.get(elf).rela_shndx;
+ const rela_dyn_ni = rela_dyn_shndx.get(elf).ni;
+ switch (elf.shdrPtr(rela_dyn_shndx)) {
inline else => |shdr, class| {
const Rela = class.ElfN().Rela;
const old_size = elf.targetLoad(&shdr.size);
@@ -2851,11 +3870,6 @@ pub fn addRelocAssumeCapacity(
.offset = offset,
.addend = addend,
};
- switch (target.target_relocs) {
- .none => {},
- else => |target_ri| target_ri.get(elf).prev = ri,
- }
- target.target_relocs = ri;
}
pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
@@ -2877,30 +3891,12 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
if (!Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) return;
const nmi = try elf.navMapIndex(zcu, nav_index);
- const si = nmi.symbol(elf);
- const ni = ni: {
- const sym = si.get(elf);
- switch (sym.ni) {
- .none => {
- try elf.nodes.ensureUnusedCapacity(gpa, 1);
- const sec_si = elf.navSection(ip, nav.resolved.?);
- const ni = try elf.mf.addLastChildNode(gpa, sec_si.node(elf), .{
- .alignment = zcu.navAlignment(nav_index).toStdMem(),
- .moved = true,
- });
- elf.nodes.appendAssumeCapacity(.{ .nav = nmi });
- sym.ni = ni;
- switch (elf.symPtr(si)) {
- inline else => |sym_ptr, class| sym_ptr.shndx =
- @field(elf.symPtr(sec_si), @tagName(class)).shndx,
- }
- },
- else => si.deleteLocationRelocs(elf),
- }
- assert(sym.loc_relocs == .none);
- sym.loc_relocs = @enumFromInt(elf.relocs.items.len);
- break :ni sym.ni;
- };
+ const ni = nmi.symbol(elf).index().ptr(elf).node;
+ elf.resetNodeRelocs(ni);
+
+ // Ensure the NAV is marked as moved so that once we're done, `flushMoved` will eventually be
+ // called to apply the NAV's new relocations.
+ try ni.moved(gpa, &elf.mf);
var nw: MappedFile.Node.Writer = undefined;
ni.writer(&elf.mf, gpa, &nw);
@@ -2911,54 +3907,14 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
zcu.navSrcLoc(nav_index),
.fromInterned(nav.resolved.?.value),
&nw.interface,
- .{ .atom_index = @intFromEnum(si) },
+ .{ .atom_index = Node.toAtom(ni) },
) catch |err| switch (err) {
error.WriteFailed => return error.OutOfMemory,
else => |e| return e,
};
- switch (elf.symPtr(si)) {
+ switch (elf.symPtr(nmi.symbol(elf).index())) {
inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
}
- si.applyLocationRelocs(elf);
-}
-
-pub fn lowerUav(
- elf: *Elf,
- pt: Zcu.PerThread,
- uav_val: InternPool.Index,
- uav_align: InternPool.Alignment,
- src_loc: Zcu.LazySrcLoc,
-) !codegen.SymbolResult {
- const zcu = pt.zcu;
- const gpa = zcu.gpa;
-
- try elf.pending_uavs.ensureUnusedCapacity(gpa, 1);
- const umi = elf.uavMapIndex(uav_val) catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- else => |e| return .{ .fail = try Zcu.ErrorMsg.create(
- gpa,
- src_loc,
- "linker failed to update constant: {s}",
- .{@errorName(e)},
- ) },
- };
- const si = umi.symbol(elf);
- if (switch (si.get(elf).ni) {
- .none => true,
- else => |ni| uav_align.toStdMem().order(ni.alignment(&elf.mf)).compare(.gt),
- }) {
- const gop = elf.pending_uavs.getOrPutAssumeCapacity(umi);
- if (gop.found_existing) {
- gop.value_ptr.alignment = gop.value_ptr.alignment.max(uav_align);
- } else {
- gop.value_ptr.* = .{
- .alignment = uav_align,
- .src_loc = src_loc,
- };
- elf.const_prog_node.increaseEstimatedTotalItems(1);
- }
- }
- return .{ .sym_index = @intFromEnum(si) };
}
pub fn updateFunc(
@@ -2993,42 +3949,13 @@ fn updateFuncInner(
const nav = ip.getNav(func.owner_nav);
const nmi = try elf.navMapIndex(zcu, func.owner_nav);
- const si = nmi.symbol(elf);
- log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), si });
- const ni = ni: {
- const sym = si.get(elf);
- switch (sym.ni) {
- .none => {
- try elf.nodes.ensureUnusedCapacity(gpa, 1);
- const sec_si = elf.navSection(ip, nav.resolved.?);
- const mod = zcu.navFileScope(func.owner_nav).mod.?;
- const target = &mod.resolved_target.result;
- const ni = try elf.mf.addLastChildNode(gpa, sec_si.node(elf), .{
- .alignment = switch (nav.resolved.?.@"align") {
- .none => switch (mod.optimize_mode) {
- .Debug,
- .ReleaseSafe,
- .ReleaseFast,
- => target_util.defaultFunctionAlignment(target),
- .ReleaseSmall => target_util.minFunctionAlignment(target),
- },
- else => |a| a.maxStrict(target_util.minFunctionAlignment(target)),
- }.toStdMem(),
- .moved = true,
- });
- elf.nodes.appendAssumeCapacity(.{ .nav = nmi });
- sym.ni = ni;
- switch (elf.symPtr(si)) {
- inline else => |sym_ptr, class| sym_ptr.shndx =
- @field(elf.symPtr(sec_si), @tagName(class)).shndx,
- }
- },
- else => si.deleteLocationRelocs(elf),
- }
- assert(sym.loc_relocs == .none);
- sym.loc_relocs = @enumFromInt(elf.relocs.items.len);
- break :ni sym.ni;
- };
+ log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), nmi.symbol(elf) });
+ const ni = nmi.symbol(elf).index().ptr(elf).node;
+ elf.resetNodeRelocs(ni);
+
+ // Ensure the NAV is marked as moved so that once we're done, `flushMoved` will eventually be
+ // called to apply the NAV's new relocations.
+ try ni.moved(gpa, &elf.mf);
var nw: MappedFile.Node.Writer = undefined;
ni.writer(&elf.mf, gpa, &nw);
@@ -3038,7 +3965,7 @@ fn updateFuncInner(
pt,
zcu.navSrcLoc(func.owner_nav),
func_index,
- @intFromEnum(si),
+ Node.toAtom(ni),
mir,
&nw.interface,
.none,
@@ -3046,10 +3973,9 @@ fn updateFuncInner(
error.WriteFailed => return nw.err.?,
else => |e| return e,
};
- switch (elf.symPtr(si)) {
+ switch (elf.symPtr(nmi.symbol(elf).index())) {
inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
}
- si.applyLocationRelocs(elf);
}
pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) !void {
@@ -3072,7 +3998,42 @@ pub fn flush(
const comp = elf.base.comp;
_ = arena;
_ = prog_node;
+
+ if (elf.ehdrField(.type) != .REL and
+ elf.shndx.dynamic == .UNDEF and
+ elf.globals.strong_undef.count() > 0)
+ {
+ for (elf.globals.strong_undef.keys()) |name| {
+ comp.link_diags.addError("undefined global symbol '{s}'", .{name.slice(elf)});
+ }
+ return error.LinkFailure;
+ }
+
while (try elf.idle(tid)) {}
+
+ const entry_addr: u64 = entry: {
+ const sym_name_slice: []const u8 = name: switch (elf.options.entry) {
+ .default => switch (comp.config.output_mode) {
+ .Exe => continue :name .enabled,
+ .Lib, .Obj => continue :name .disabled,
+ },
+ .disabled => break :entry 0,
+ .enabled => "_start",
+ .named => |named| named,
+ };
+ const sym_name_strtab = elf.string(.strtab, sym_name_slice) catch |err| switch (err) {
+ error.Canceled => |e| return e,
+ else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),
+ };
+ const global = elf.globalByName(sym_name_strtab) orelse break :entry 0;
+ switch (elf.symPtr(global.symtab_index)) {
+ inline else => |sym| break :entry elf.targetLoad(&sym.value),
+ }
+ };
+ switch (elf.ehdrPtr()) {
+ inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),
+ }
+
elf.mf.flush() catch |err| switch (err) {
error.Canceled => |e| return e,
else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),
@@ -3082,15 +4043,10 @@ pub fn flush(
pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
const comp = elf.base.comp;
task: {
- while (elf.pending_uavs.pop()) |pending_uav| {
- const sub_prog_node = elf.idleProgNode(tid, elf.const_prog_node, .{ .uav = pending_uav.key });
+ while (elf.pending_uavs.pop()) |umi| {
+ const sub_prog_node = elf.idleProgNode(tid, elf.const_prog_node, .{ .uav = umi });
defer sub_prog_node.end();
- elf.flushUav(
- .{ .zcu = comp.zcu.?, .tid = tid },
- pending_uav.key,
- pending_uav.value.alignment,
- pending_uav.value.src_loc,
- ) catch |err| switch (err) {
+ elf.flushUav(.{ .zcu = comp.zcu.?, .tid = tid }, umi) catch |err| switch (err) {
error.OutOfMemory => |e| return e,
else => |e| return comp.link_diags.fail(
"linker failed to lower constant: {t}",
@@ -3127,9 +4083,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
break :task;
};
if (elf.input_section_pending_index < elf.input_sections.items.len) {
- const isi: Node.InputSectionIndex = @enumFromInt(elf.input_section_pending_index);
+ const isi: InputSection.Index = @enumFromInt(elf.input_section_pending_index);
elf.input_section_pending_index += 1;
- const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.symbol(elf).node(elf)));
+ const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.node(elf)));
defer sub_prog_node.end();
elf.flushInputSection(isi) catch |err| switch (err) {
else => |e| {
@@ -3137,9 +4093,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
return comp.link_diags.fail(
"linker failed to read input section '{s}' from \"{f}{f}\": {t}",
.{
- elf.sectionName(
- elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section,
- ),
+ elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf),
ii.path(elf).fmtEscapeString(),
fmtMemberString(ii.member(elf)),
e,
@@ -3149,6 +4103,20 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
};
break :task;
}
+ if (elf.changed_symtab_index.pop()) |kv| {
+ if (elf.ehdrField(.type) == .REL) {
+ const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);
+ defer sub_prog_node.end();
+ const sym = elf.globalByName(kv.key).?.symtab_index.ptr(elf);
+ var ri = sym.first_target_reloc;
+ while (ri != .none) {
+ const reloc = ri.get(elf);
+ reloc.updateTargetIndex(elf);
+ ri = reloc.next;
+ }
+ break :task;
+ }
+ }
while (elf.mf.updates.pop()) |ni| {
const clean_moved = ni.cleanMoved(&elf.mf);
const clean_resized = ni.cleanResized(&elf.mf);
@@ -3161,9 +4129,10 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
} else elf.mf.update_prog_node.completeOne();
}
}
- if (elf.pending_uavs.count() > 0) return true;
+ if (elf.pending_uavs.items.len > 0) return true;
for (&elf.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
if (elf.input_sections.items.len > elf.input_section_pending_index) return true;
+ if (elf.changed_symtab_index.count() > 0) return true;
if (elf.mf.updates.items.len > 0) return true;
return false;
}
@@ -3177,13 +4146,13 @@ fn idleProgNode(
var name: [std.Progress.Node.max_name_len]u8 = undefined;
return prog_node.start(name: switch (node) {
else => |tag| @tagName(tag),
- .section => |si| elf.sectionName(si),
+ .section => |shndx| shndx.name(elf),
.input_section => |isi| {
const ii = isi.input(elf);
break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{
ii.path(elf).fmtEscapeString(),
fmtMemberString(ii.member(elf)),
- elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section),
+ elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf),
}) catch &name;
},
.nav => |nmi| {
@@ -3200,59 +4169,43 @@ fn flushUav(
elf: *Elf,
pt: Zcu.PerThread,
umi: Node.UavMapIndex,
- uav_align: InternPool.Alignment,
- src_loc: Zcu.LazySrcLoc,
) !void {
+ const comp = elf.base.comp;
+ const gpa = comp.gpa;
const zcu = pt.zcu;
- const gpa = zcu.gpa;
const uav_val = umi.uavValue(elf);
- const si = umi.symbol(elf);
- const ni = ni: {
- const sym = si.get(elf);
- switch (sym.ni) {
- .none => {
- try elf.nodes.ensureUnusedCapacity(gpa, 1);
- const sec_si = elf.si.data;
- const ni = try elf.mf.addLastChildNode(gpa, sec_si.node(elf), .{
- .alignment = uav_align.toStdMem(),
- .moved = true,
- });
- elf.nodes.appendAssumeCapacity(.{ .uav = umi });
- sym.ni = ni;
- switch (elf.symPtr(si)) {
- inline else => |sym_ptr, class| sym_ptr.shndx =
- @field(elf.symPtr(sec_si), @tagName(class)).shndx,
- }
- },
- else => {
- if (sym.ni.alignment(&elf.mf).order(uav_align.toStdMem()).compare(.gte)) return;
- si.deleteLocationRelocs(elf);
- },
- }
- assert(sym.loc_relocs == .none);
- sym.loc_relocs = @enumFromInt(elf.relocs.items.len);
- break :ni sym.ni;
- };
+ const ni = umi.symbol(elf).index().ptr(elf).node;
+ elf.resetNodeRelocs(ni);
var nw: MappedFile.Node.Writer = undefined;
ni.writer(&elf.mf, gpa, &nw);
defer nw.deinit();
+ // TODO: UAV lowering should never require source locations.
+ const dummy_src_loc: Zcu.LazySrcLoc = .{
+ .base_node_inst = try zcu.intern_pool.trackZir(gpa, comp.io, pt.tid, .{
+ .file = zcu.module_roots.get(zcu.std_mod).?.unwrap().?,
+ .inst = .main_struct_inst,
+ }),
+ .offset = .{ .byte_abs = 0 },
+ };
codegen.generateSymbol(
&elf.base,
pt,
- src_loc,
+ dummy_src_loc,
.fromInterned(uav_val),
&nw.interface,
- .{ .atom_index = @intFromEnum(si) },
+ .{ .atom_index = Node.toAtom(ni) },
) catch |err| switch (err) {
error.WriteFailed => return error.OutOfMemory,
else => |e| return e,
};
- switch (elf.symPtr(si)) {
+ switch (elf.symPtr(umi.symbol(elf).index())) {
inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
}
- si.applyLocationRelocs(elf);
+ // The UAV should already be considered to have moved, because it is created as moved and
+ // pending calls to `flushUav` always happen before pending calls to `flushMoved`.
+ assert(ni.hasMoved(&elf.mf));
}
fn flushLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
@@ -3260,33 +4213,12 @@ fn flushLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
const gpa = zcu.gpa;
const lazy = lmr.lazySymbol(elf);
- const si = lmr.symbol(elf);
- const ni = ni: {
- const sym = si.get(elf);
- switch (sym.ni) {
- .none => {
- try elf.nodes.ensureUnusedCapacity(gpa, 1);
- const sec_si: Symbol.Index = switch (lazy.kind) {
- .code => .text,
- .const_data => .rodata,
- };
- const ni = try elf.mf.addLastChildNode(gpa, sec_si.node(elf), .{ .moved = true });
- elf.nodes.appendAssumeCapacity(switch (lazy.kind) {
- .code => .{ .lazy_code = @enumFromInt(lmr.index) },
- .const_data => .{ .lazy_const_data = @enumFromInt(lmr.index) },
- });
- sym.ni = ni;
- switch (elf.symPtr(si)) {
- inline else => |sym_ptr, class| sym_ptr.shndx =
- @field(elf.symPtr(sec_si), @tagName(class)).shndx,
- }
- },
- else => si.deleteLocationRelocs(elf),
- }
- assert(sym.loc_relocs == .none);
- sym.loc_relocs = @enumFromInt(elf.relocs.items.len);
- break :ni sym.ni;
- };
+ const ni = lmr.symbol(elf).index().ptr(elf).node;
+ elf.resetNodeRelocs(ni);
+
+ // Ensure the lazy node is marked as moved so that once we're done, `flushMoved` will eventually
+ // be called to apply the lazy node's new relocations.
+ try ni.moved(gpa, &elf.mf);
var required_alignment: InternPool.Alignment = .none;
var nw: MappedFile.Node.Writer = undefined;
@@ -3300,15 +4232,14 @@ fn flushLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
&required_alignment,
&nw.interface,
.none,
- .{ .atom_index = @intFromEnum(si) },
+ .{ .atom_index = Node.toAtom(ni) },
);
- switch (elf.symPtr(si)) {
+ switch (elf.symPtr(lmr.symbol(elf).index())) {
inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
}
- si.applyLocationRelocs(elf);
}
-fn flushInputSection(elf: *Elf, isi: Node.InputSectionIndex) !void {
+fn flushInputSection(elf: *Elf, isi: InputSection.Index) !void {
const file_loc = isi.fileLocation(elf);
if (file_loc.size == 0) return;
const comp = elf.base.comp;
@@ -3321,12 +4252,13 @@ fn flushInputSection(elf: *Elf, isi: Node.InputSectionIndex) !void {
var fr = file.reader(io, &.{});
try fr.seekTo(file_loc.offset);
var nw: MappedFile.Node.Writer = undefined;
- const si = isi.symbol(elf);
- si.node(elf).writer(&elf.mf, gpa, &nw);
+ isi.node(elf).writer(&elf.mf, gpa, &nw);
defer nw.deinit();
if (try nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) != file_loc.size)
return error.EndOfStream;
- si.applyLocationRelocs(elf);
+ // The input section should already be considered to have moved, because it is created as moved
+ // and pending calls to `flushInputSection` always happen before pending calls to `flushMoved`.
+ assert(isi.node(elf).hasMoved(&elf.mf));
}
fn flushFileOffset(elf: *Elf, ni: MappedFile.Node.Index) !void {
@@ -3349,7 +4281,7 @@ fn flushFileOffset(elf: *Elf, ni: MappedFile.Node.Index) !void {
var child_it = ni.children(&elf.mf);
while (child_it.next()) |child_ni| try elf.flushFileOffset(child_ni);
},
- .section => |si| switch (elf.shdrPtr(si.shndx(elf))) {
+ .section => |shndx| switch (elf.shdrPtr(shndx)) {
inline else => |shdr| elf.targetStore(&shdr.offset, @intCast(
ni.fileLocation(&elf.mf, false).offset,
)),
@@ -3378,22 +4310,21 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
},
}
},
- .section => |si| {
+ .section => |shndx| {
try elf.flushFileOffset(ni);
const addr = elf.computeNodeVAddr(ni);
- const shndx = si.shndx(elf);
switch (elf.shdrPtr(shndx)) {
inline else => |shdr, class| {
const flags = elf.targetLoad(&shdr.flags).shf;
if (flags.ALLOC) {
- if (elf.si.dynamic != .null) {
- if (si == elf.si.got) {
+ if (elf.shndx.dynamic != .UNDEF) {
+ if (shndx == elf.shndx.got) {
const old_addr = elf.targetLoad(&shdr.addr);
- const rela_dyn_si = shndx.get(elf).rela_si;
+ const rela_dyn_shndx = shndx.get(elf).rela_shndx;
const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
- rela_dyn_si.node(elf).slice(&elf.mf)[0..@intCast(
+ rela_dyn_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(
elf.targetLoad(&@field(
- elf.shdrPtr(rela_dyn_si.shndx(elf)),
+ elf.shdrPtr(rela_dyn_shndx),
@tagName(class),
).size),
)],
@@ -3413,19 +4344,19 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
),
},
}
- } else if (si == elf.si.got_plt) {
+ } else if (shndx == elf.shndx.got_plt) {
const target_endian = elf.targetEndian();
const old_addr = elf.targetLoad(&shdr.addr);
- const rela_plt_si = shndx.get(elf).rela_si;
+ const rela_plt_shndx = shndx.get(elf).rela_shndx;
const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
- rela_plt_si.node(elf).slice(&elf.mf)[0..@intCast(
+ rela_plt_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(
elf.targetLoad(&@field(
- elf.shdrPtr(rela_plt_si.shndx(elf)),
+ elf.shdrPtr(rela_plt_shndx),
@tagName(class),
).size),
)],
));
- const plt_sec_slice = elf.si.plt_sec.node(elf).slice(&elf.mf);
+ const plt_sec_slice = elf.shndx.plt_sec.get(elf).ni.slice(&elf.mf);
switch (elf.ehdrField(.machine)) {
else => |machine| @panic(@tagName(machine)),
.AARCH64, .PPC64, .RISCV => {},
@@ -3454,7 +4385,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
}
},
}
- } else if (si == elf.si.plt_sec) {
+ } else if (shndx == elf.shndx.plt_sec) {
const target_endian = elf.targetEndian();
const old_addr = elf.targetLoad(&shdr.addr);
const plt_sec_slice = ni.slice(&elf.mf);
@@ -3477,34 +4408,72 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
}
}
elf.targetStore(&shdr.addr, @intCast(addr));
- @field(elf.symPtr(si), @tagName(class)).value = shdr.addr;
+ shndx.get(elf).lsi.index().flushMoved(elf, addr);
+ }
+
+ if (shndx == elf.shndx.plt) {
+ elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_plt_reloc);
+ } else if (shndx == elf.shndx.dynamic) {
+ elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_dynamic_reloc);
}
},
}
- si.flushMoved(elf, addr);
},
.input_section => |isi| {
- const old_addr = switch (elf.symPtr(isi.symbol(elf))) {
- inline else => |sym| elf.targetLoad(&sym.value),
- };
- const new_addr = elf.computeNodeVAddr(ni);
+ const old_section_addr = isi.ptr(elf).vaddr;
+ const new_section_addr = elf.computeNodeVAddr(ni);
+ isi.ptr(elf).vaddr = new_section_addr;
+
+ // Update local symbols
const ii = isi.input(elf);
- var si = ii.symbol(elf);
- const end_si = ii.endSymbol(elf);
- while (cond: {
- si = si.next();
- break :cond si != end_si;
- }) {
- if (si.get(elf).ni != ni) continue;
- si.flushMoved(elf, switch (elf.symPtr(si)) {
- inline else => |sym| elf.targetLoad(&sym.value),
- } - old_addr + new_addr);
+ var lsi, const end_lsi = ii.localSymbolRange(elf);
+ while (lsi != end_lsi) : (lsi = @enumFromInt(@intFromEnum(lsi) + 1)) {
+ if (lsi.index().ptr(elf).node != ni) continue;
+ const old_sym_addr: u64 = switch (elf.symPtr(lsi.index())) {
+ inline else => |sym| switch (elf.targetLoad(&sym.other).visibility) {
+ .HIDDEN, .INTERNAL => {
+ // This is actually a global symbol which got demoted to STB_LOCAL due
+ // to its visibility. It will be handled in the global symbols pass
+ // below; don't touch it now.
+ continue;
+ },
+ .PROTECTED => unreachable, // not allowed for an STB_LOCAL symbol
+ .DEFAULT => elf.targetLoad(&sym.value),
+ },
+ };
+ lsi.index().flushMoved(elf, old_sym_addr - old_section_addr + new_section_addr);
+ }
+
+ // Update global symbols
+ if (elf.node_global_symbols.get(ni)) |first_name| {
+ assert(first_name != .empty);
+ var name = first_name;
+ while (name != .empty) {
+ const global = elf.globalByName(name).?;
+ const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) {
+ inline else => |sym| elf.targetLoad(&sym.value),
+ };
+ global.flushMoved(elf, old_sym_addr - old_section_addr + new_section_addr);
+ name = global.next_in_node;
+ }
}
+
+ elf.flushMovedNodeRelocs(ni, new_section_addr, isi.ptrConst(elf).first_reloc);
+ },
+ inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| {
+ const new_addr = elf.computeNodeVAddr(ni);
+ mi.symbol(elf).index().flushMoved(elf, new_addr);
+ if (elf.node_global_symbols.get(ni)) |first_name| {
+ assert(first_name != .empty);
+ var name = first_name;
+ while (name != .empty) {
+ const global = elf.globalByName(name).?;
+ global.flushMoved(elf, new_addr);
+ name = global.next_in_node;
+ }
+ }
+ elf.flushMovedNodeRelocs(ni, new_addr, mi.firstReloc(elf));
},
- inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| mi.symbol(elf).flushMoved(
- elf,
- elf.computeNodeVAddr(ni),
- ),
}
try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
}
@@ -3542,7 +4511,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
switch (elf.targetLoad(&next_ph.type)) {
else => unreachable,
.NULL, .LOAD => {},
- .DYNAMIC, .INTERP, .PHDR, .TLS => break,
+ .DYNAMIC, .INTERP, .PHDR, .TLS, std.elf.PT.GNU_RELRO => break,
}
const next_vaddr = elf.targetLoad(&next_ph.vaddr);
if (vaddr + memsz <= next_vaddr) break;
@@ -3564,7 +4533,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
}
},
},
- .section => |si| switch (elf.shdrPtr(si.shndx(elf))) {
+ .section => |shndx| switch (elf.shdrPtr(shndx)) {
inline else => |shdr, class| {
switch (elf.targetLoad(&shdr.type)) {
else => unreachable,
@@ -3572,10 +4541,10 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
.PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL),
.SYMTAB, .DYNAMIC, .REL, .DYNSYM => return,
.STRTAB => {
- if (elf.si.dynamic != .null) {
- if (si == elf.si.dynstr) {
+ if (elf.shndx.dynamic != .UNDEF) {
+ if (shndx == elf.shndx.dynstr) {
const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
- elf.si.dynamic.node(elf).slice(&elf.mf),
+ elf.shndx.dynamic.get(elf).ni.slice(&elf.mf),
));
for (dynamic_entries) |*dynamic_entry|
switch (elf.targetLoad(&dynamic_entry[0])) {
@@ -3587,19 +4556,19 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
return;
},
.RELA => {
- if (elf.si.dynamic != .null) {
- if (si == elf.si.got.shndx(elf).get(elf).rela_si) {
+ if (elf.shndx.dynamic != .UNDEF) {
+ if (shndx == elf.shndx.got.get(elf).rela_shndx) {
const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
- elf.si.dynamic.node(elf).slice(&elf.mf),
+ elf.shndx.dynamic.get(elf).ni.slice(&elf.mf),
));
for (dynamic_entries) |*dynamic_entry|
switch (elf.targetLoad(&dynamic_entry[0])) {
else => {},
std.elf.DT_RELASZ => dynamic_entry[1] = shdr.size,
};
- } else if (si == elf.si.got_plt.shndx(elf).get(elf).rela_si) {
+ } else if (shndx == elf.shndx.got_plt.get(elf).rela_shndx) {
const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
- elf.si.dynamic.node(elf).slice(&elf.mf),
+ elf.shndx.dynamic.get(elf).ni.slice(&elf.mf),
));
for (dynamic_entries) |*dynamic_entry|
switch (elf.targetLoad(&dynamic_entry[0])) {
@@ -3611,7 +4580,9 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
return;
},
}
- elf.targetStore(&shdr.size, @intCast(size));
+ if (shndx != elf.shndx.plt) {
+ elf.targetStore(&shdr.size, @intCast(size));
+ }
},
},
.input_section, .nav, .uav, .lazy_code, .lazy_const_data => {},
@@ -3642,7 +4613,6 @@ fn updateExportsInner(
export_indices: []const Zcu.Export.Index,
) !void {
const zcu = pt.zcu;
- const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
switch (exported) {
@@ -3652,44 +4622,35 @@ fn updateExportsInner(
Value.fromInterned(uav).fmtValue(pt),
}),
}
- try elf.symtab.ensureUnusedCapacity(gpa, export_indices.len);
- const exported_si: Symbol.Index, const @"type": std.elf.STT = switch (exported) {
+ try elf.ensureUnusedSymbolCapacity(@intCast(export_indices.len), .maybe_global);
+ const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (exported) {
.nav => |nav| .{
- try elf.navSymbol(zcu, nav),
+ (try elf.navMapIndex(zcu, nav)).symbol(elf),
navType(ip, ip.getNav(nav).resolved.?, elf.base.comp.config.any_non_single_threaded),
},
- .uav => |uav| .{ @enumFromInt(switch (try elf.lowerUav(
- pt,
- uav,
- Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
- export_indices[0].ptr(zcu).src,
- )) {
- .sym_index => |si| si,
- .fail => |em| {
- defer em.destroy(gpa);
- return elf.base.comp.link_diags.fail("{s}", .{em.msg});
- },
- }), .OBJECT },
+ .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT },
};
while (try elf.idle(pt.tid)) {}
- const exported_ni = exported_si.node(elf);
- const value, const size, const shndx = switch (elf.symPtr(exported_si)) {
+ const value: u64, const size: u64, const shndx: Section.Index = switch (elf.symPtr(exported_lsi.index())) {
inline else => |exported_sym| .{
elf.targetLoad(&exported_sym.value),
- exported_sym.size,
- exported_sym.shndx,
+ elf.targetLoad(&exported_sym.size),
+ .fromSection(elf.targetLoad(&exported_sym.shndx)),
},
};
for (export_indices) |export_index| {
const @"export" = export_index.ptr(zcu);
const name = @"export".opts.name.toSlice(ip);
- const export_si = try elf.globalSymbol(.{
- .name = name,
+ _ = elf.addGlobalSymbolAssumeCapacity(.{
+ .node = .none,
+ .name = try .string(elf, name),
+ .value = value,
+ .size = @intCast(size),
.type = @"type",
.bind = switch (@"export".opts.linkage) {
- .internal => .LOCAL,
- .strong => .GLOBAL,
- .weak => .WEAK,
+ .internal => @panic("TODO internal linkage"),
+ .strong => .strong,
+ .weak => .weak,
.link_once => return error.LinkOnceUnsupported,
},
.visibility = switch (@"export".opts.visibility) {
@@ -3697,15 +4658,23 @@ fn updateExportsInner(
.hidden => .HIDDEN,
.protected => .PROTECTED,
},
- });
- export_si.get(elf).ni = exported_ni;
- switch (elf.symPtr(export_si)) {
- inline else => |export_sym| {
- export_sym.size = @intCast(size);
- export_sym.shndx = shndx;
+ .shndx = shndx,
+ }) catch |err| switch (err) {
+ error.MultipleDefinitions => {
+ // HACK: because we currently don't/can't delete these exports, we would typically
+ // get these errors on every non-initial incremental update. Hack around that by
+ // only emitting this error if the symbol we're conflicting with comes from an input
+ // section (as opposed to the ZCU).
+ const conflicting_global = elf.globalByName(try elf.string(.strtab, name)).?;
+ const conflicting_node = conflicting_global.symtab_index.ptr(elf).node;
+ if (elf.getNode(conflicting_node) == .input_section) {
+ return elf.base.comp.link_diags.fail(
+ "multiple definitions of '{s}'",
+ .{name},
+ );
+ }
},
- }
- export_si.flushMoved(elf, value);
+ };
}
}
@@ -3759,13 +4728,13 @@ pub fn printNode(
try w.writeByte(')');
},
},
- .section => |si| try w.print("({s})", .{elf.sectionName(si)}),
+ .section => |shndx| try w.print("({s})", .{shndx.name(elf)}),
.input_section => |isi| {
const ii = isi.input(elf);
try w.print("({f}{f}, {s})", .{
ii.path(elf).fmtEscapeString(),
fmtMemberString(ii.member(elf)),
- elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section),
+ elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf),
});
},
.nav => |nmi| {
diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig
@@ -632,7 +632,7 @@ pub fn getNavVAddr(
switch (reloc_info.parent) {
.none => unreachable,
.atom_index => |atom_index| {
- const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?;
+ const parent_atom = self.symbols.items[@intFromEnum(atom_index)].getAtom(macho_file).?;
try parent_atom.addReloc(macho_file, .{
.tag = .@"extern",
.offset = @intCast(reloc_info.offset),
@@ -650,7 +650,7 @@ pub fn getNavVAddr(
.debug_output => |debug_output| switch (debug_output) {
.dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{
.source_off = @intCast(reloc_info.offset),
- .target_sym = sym_index,
+ .target_sym = @enumFromInt(sym_index),
.target_off = reloc_info.addend,
}),
.none => unreachable,
@@ -671,7 +671,7 @@ pub fn getUavVAddr(
switch (reloc_info.parent) {
.none => unreachable,
.atom_index => |atom_index| {
- const parent_atom = self.symbols.items[atom_index].getAtom(macho_file).?;
+ const parent_atom = self.symbols.items[@intFromEnum(atom_index)].getAtom(macho_file).?;
try parent_atom.addReloc(macho_file, .{
.tag = .@"extern",
.offset = @intCast(reloc_info.offset),
@@ -689,7 +689,7 @@ pub fn getUavVAddr(
.debug_output => |debug_output| switch (debug_output) {
.dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{
.source_off = @intCast(reloc_info.offset),
- .target_sym = sym_index,
+ .target_sym = @enumFromInt(sym_index),
.target_off = reloc_info.addend,
}),
.none => unreachable,
@@ -717,7 +717,7 @@ pub fn lowerUav(
const sym = self.symbols.items[metadata.symbol_index];
const existing_alignment = sym.getAtom(macho_file).?.alignment;
if (uav_alignment.order(existing_alignment).compare(.lte))
- return .{ .sym_index = metadata.symbol_index };
+ return .{ .sym_index = @enumFromInt(metadata.symbol_index) };
}
var name_buf: [32]u8 = undefined;
@@ -742,7 +742,7 @@ pub fn lowerUav(
) },
};
switch (res) {
- .sym_index => |sym_index| try self.uavs.put(gpa, uav, .{ .symbol_index = sym_index }),
+ .sym_index => |sym_index| try self.uavs.put(gpa, uav, .{ .symbol_index = @intFromEnum(sym_index) }),
.fail => {},
}
return res;
@@ -790,7 +790,7 @@ pub fn updateFunc(
var aw: std.Io.Writer.Allocating = .init(gpa);
defer aw.deinit();
- var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null;
+ var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, @enumFromInt(sym_index)) else null;
defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
codegen.emitFunction(
@@ -798,7 +798,7 @@ pub fn updateFunc(
pt,
zcu.navSrcLoc(func.owner_nav),
func_index,
- sym_index,
+ @enumFromInt(sym_index),
mir,
&aw.writer,
if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none,
@@ -884,7 +884,7 @@ pub fn updateNav(
const sym_index = try self.getGlobalSymbol(macho_file, name, lib_name);
if (nav.resolved.?.@"threadlocal" and macho_file.base.comp.config.any_non_single_threaded) self.symbols.items[sym_index].flags.tlv = true;
if (self.dwarf) |*dwarf| {
- var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, sym_index);
+ var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, @enumFromInt(sym_index));
defer debug_wip_nav.deinit();
dwarf.finishWipNav(pt, nav_index, &debug_wip_nav) catch |err| switch (err) {
error.OutOfMemory, error.Overflow => |e| return e,
@@ -902,7 +902,7 @@ pub fn updateNav(
var aw: std.Io.Writer.Allocating = .init(zcu.gpa);
defer aw.deinit();
- var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null;
+ var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, @enumFromInt(sym_index)) else null;
defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
codegen.generateSymbol(
@@ -911,7 +911,7 @@ pub fn updateNav(
zcu.navSrcLoc(nav_index),
.fromInterned(nav.resolved.?.value),
&aw.writer,
- .{ .atom_index = sym_index },
+ .{ .atom_index = @enumFromInt(sym_index) },
) catch |err| switch (err) {
error.WriteFailed => return error.OutOfMemory,
else => |e| return e,
@@ -1214,7 +1214,7 @@ fn lowerConst(
src_loc,
val,
&aw.writer,
- .{ .atom_index = sym_index },
+ .{ .atom_index = @enumFromInt(sym_index) },
) catch |err| switch (err) {
error.WriteFailed => return error.OutOfMemory,
else => |e| return e,
@@ -1242,7 +1242,7 @@ fn lowerConst(
const file_offset = sect.offset + atom.value;
try macho_file.pwriteAll(code, file_offset);
- return .{ .sym_index = sym_index };
+ return .{ .sym_index = @enumFromInt(sym_index) };
}
pub fn updateExports(
@@ -1377,7 +1377,7 @@ fn updateLazySymbol(
&required_alignment,
&aw.writer,
.none,
- .{ .atom_index = symbol_index },
+ .{ .atom_index = @enumFromInt(symbol_index) },
);
const code = aw.written();
diff --git a/src/link/MappedFile.zig b/src/link/MappedFile.zig
@@ -305,6 +305,15 @@ pub const Node = extern struct {
}
}
+ pub fn realign(ni: Node.Index, mf: *MappedFile, new_alignment: std.mem.Alignment) void {
+ ni.get(mf).flags.alignment = new_alignment;
+
+ const old_offset, const old_size = ni.location(mf).resolve(mf);
+ if (!new_alignment.check(@intCast(old_offset)) or !new_alignment.check(@intCast(old_size))) {
+ @panic("TODO MappedFile.realign");
+ }
+ }
+
pub fn writer(ni: Node.Index, mf: *MappedFile, gpa: std.mem.Allocator, w: *Writer) void {
w.* = .{
.gpa = gpa,