Merge pull request #21065 from ziglang/elf-zig-got

elf: replace .got.zig with a zig jump table
This commit is contained in:
Jakub Konka 2024-08-16 21:19:44 +02:00 committed by GitHub
commit 90989be0e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 490 additions and 789 deletions

View File

@ -218,7 +218,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
if (opts.is_test) { if (opts.is_test) {
try buffer.appendSlice( try buffer.appendSlice(
\\pub var test_functions: []const std.builtin.TestFn = undefined; // overwritten later \\pub var test_functions: []const std.builtin.TestFn = &.{}; // overwritten later
\\ \\
); );
} }

View File

@ -3092,6 +3092,10 @@ pub fn totalErrorCount(comp: *Compilation) u32 {
if (zcu.intern_pool.global_error_set.getNamesFromMainThread().len > zcu.error_limit) { if (zcu.intern_pool.global_error_set.getNamesFromMainThread().len > zcu.error_limit) {
total += 1; total += 1;
} }
for (zcu.failed_codegen.keys()) |_| {
total += 1;
}
} }
// The "no entry point found" error only counts if there are no semantic analysis errors. // The "no entry point found" error only counts if there are no semantic analysis errors.
@ -3237,6 +3241,9 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
} }
} }
} }
for (zcu.failed_codegen.values()) |error_msg| {
try addModuleErrorMsg(zcu, &bundle, error_msg.*, &all_references);
}
for (zcu.failed_exports.values()) |value| { for (zcu.failed_exports.values()) |value| {
try addModuleErrorMsg(zcu, &bundle, value.*, &all_references); try addModuleErrorMsg(zcu, &bundle, value.*, &all_references);
} }

View File

@ -4352,24 +4352,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
// on linking. // on linking.
if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) { if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) {
.func => |func| { .func => |func| {
if (self.bin_file.cast(.elf)) |elf_file| { if (self.bin_file.cast(.elf)) |_| {
const zo = elf_file.zigObjectPtr().?; return self.fail("TODO implement calling functions for Elf", .{});
const sym_index = try zo.getOrCreateMetadataForNav(elf_file, func.owner_nav); } else if (self.bin_file.cast(.macho)) |_| {
const sym = zo.symbol(sym_index); return self.fail("TODO implement calling functions for MachO", .{});
_ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
const got_addr = @as(u32, @intCast(sym.zigGotAddress(elf_file)));
try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr });
} else if (self.bin_file.cast(.macho)) |macho_file| {
_ = macho_file;
@panic("TODO airCall");
// const atom = try macho_file.getOrCreateAtomForNav(func.owner_nav);
// const sym_index = macho_file.getAtom(atom).getSymbolIndex().?;
// try self.genSetReg(Type.u64, .x30, .{
// .linker_load = .{
// .type = .got,
// .sym_index = sym_index,
// },
// });
} else if (self.bin_file.cast(.coff)) |coff_file| { } else if (self.bin_file.cast(.coff)) |coff_file| {
const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav); const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav);
const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; const sym_index = coff_file.getAtom(atom).getSymbolIndex().?;
@ -4393,21 +4379,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
.@"extern" => |@"extern"| { .@"extern" => |@"extern"| {
const nav_name = ip.getNav(@"extern".owner_nav).name.toSlice(ip); const nav_name = ip.getNav(@"extern".owner_nav).name.toSlice(ip);
const lib_name = @"extern".lib_name.toSlice(ip); const lib_name = @"extern".lib_name.toSlice(ip);
if (self.bin_file.cast(.macho)) |macho_file| { if (self.bin_file.cast(.macho)) |_| {
_ = macho_file; return self.fail("TODO implement calling extern functions for MachO", .{});
@panic("TODO airCall");
// const sym_index = try macho_file.getGlobalSymbol(nav_name, lib_name);
// const atom = try macho_file.getOrCreateAtomForNav(self.owner_nav);
// const atom_index = macho_file.getAtom(atom).getSymbolIndex().?;
// _ = try self.addInst(.{
// .tag = .call_extern,
// .data = .{
// .relocation = .{
// .atom_index = atom_index,
// .sym_index = sym_index,
// },
// },
// });
} else if (self.bin_file.cast(.coff)) |coff_file| { } else if (self.bin_file.cast(.coff)) |coff_file| {
const sym_index = try coff_file.getGlobalSymbol(nav_name, lib_name); const sym_index = try coff_file.getGlobalSymbol(nav_name, lib_name);
try self.genSetReg(Type.u64, .x30, .{ try self.genSetReg(Type.u64, .x30, .{
@ -6234,7 +6207,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
.memory => |addr| .{ .memory = addr }, .memory => |addr| .{ .memory = addr },
.load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } }, .load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } },
.load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } }, .load_direct => |sym_index| .{ .linker_load = .{ .type = .direct, .sym_index = sym_index } },
.load_symbol, .load_tlv => unreachable, // TODO .load_symbol, .load_tlv, .lea_symbol => unreachable, // TODO
}, },
.fail => |msg| { .fail => |msg| {
self.err_msg = msg; self.err_msg = msg;

View File

@ -4333,22 +4333,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
// Due to incremental compilation, how function calls are generated depends // Due to incremental compilation, how function calls are generated depends
// on linking. // on linking.
if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) { if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) {
.func => |func| { .func => {
if (self.bin_file.cast(.elf)) |elf_file| { return self.fail("TODO implement calling functions", .{});
const zo = elf_file.zigObjectPtr().?;
const sym_index = try zo.getOrCreateMetadataForNav(elf_file, func.owner_nav);
const sym = zo.symbol(sym_index);
_ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
const got_addr: u32 = @intCast(sym.zigGotAddress(elf_file));
try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr });
} else if (self.bin_file.cast(.macho)) |_| {
unreachable; // unsupported architecture for MachO
} else {
return self.fail("TODO implement call on {s} for {s}", .{
@tagName(self.bin_file.tag),
@tagName(self.target.cpu.arch),
});
}
}, },
.@"extern" => { .@"extern" => {
return self.fail("TODO implement calling extern functions", .{}); return self.fail("TODO implement calling extern functions", .{});
@ -6184,7 +6170,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
.mcv => |mcv| switch (mcv) { .mcv => |mcv| switch (mcv) {
.none => .none, .none => .none,
.undef => .undef, .undef => .undef,
.load_got, .load_symbol, .load_direct, .load_tlv => unreachable, // TODO .load_got, .load_symbol, .load_direct, .load_tlv, .lea_symbol => unreachable, // TODO
.immediate => |imm| .{ .immediate = @truncate(imm) }, .immediate => |imm| .{ .immediate = @truncate(imm) },
.memory => |addr| .{ .memory = addr }, .memory => |addr| .{ .memory = addr },
}, },

View File

@ -4937,7 +4937,7 @@ fn genCall(
if (func.mod.pic) { if (func.mod.pic) {
return func.fail("TODO: genCall pic", .{}); return func.fail("TODO: genCall pic", .{});
} else { } else {
try func.genSetReg(Type.u64, .ra, .{ .load_symbol = .{ .sym = sym_index } }); try func.genSetReg(Type.u64, .ra, .{ .lea_symbol = .{ .sym = sym_index } });
_ = try func.addInst(.{ _ = try func.addInst(.{
.tag = .jalr, .tag = .jalr,
.data = .{ .i_type = .{ .data = .{ .i_type = .{
@ -6120,7 +6120,7 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void {
arg_map.get(op_str["%[".len .. mod_index orelse op_str.len - "]".len]) orelse arg_map.get(op_str["%[".len .. mod_index orelse op_str.len - "]".len]) orelse
return func.fail("no matching constraint: '{s}'", .{op_str}) return func.fail("no matching constraint: '{s}'", .{op_str})
]) { ]) {
.load_symbol => |sym_off| if (mem.eql(u8, modifier, "plt")) blk: { .lea_symbol => |sym_off| if (mem.eql(u8, modifier, "plt")) blk: {
assert(sym_off.off == 0); assert(sym_off.off == 0);
break :blk .{ .sym = sym_off }; break :blk .{ .sym = sym_off };
} else return func.fail("invalid modifier: '{s}'", .{modifier}), } else return func.fail("invalid modifier: '{s}'", .{modifier}),
@ -6388,7 +6388,7 @@ fn genCopy(func: *Func, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
ty, ty,
src_mcv, src_mcv,
), ),
.load_tlv => { .load_symbol, .load_tlv => {
const addr_reg, const addr_lock = try func.allocReg(.int); const addr_reg, const addr_lock = try func.allocReg(.int);
defer func.register_manager.unlockReg(addr_lock); defer func.register_manager.unlockReg(addr_lock);
@ -6433,7 +6433,7 @@ fn genCopy(func: *Func, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
part_disp += @intCast(dst_ty.abiSize(func.pt)); part_disp += @intCast(dst_ty.abiSize(func.pt));
} }
}, },
else => return func.fail("TODO: genCopy to {s} from {s}", .{ @tagName(dst_mcv), @tagName(src_mcv) }), else => return std.debug.panic("TODO: genCopy to {s} from {s}", .{ @tagName(dst_mcv), @tagName(src_mcv) }),
} }
} }
@ -6594,7 +6594,7 @@ fn genInlineMemset(
.tag = .beq, .tag = .beq,
.data = .{ .data = .{
.b_type = .{ .b_type = .{
.inst = @intCast(func.mir_instructions.len + 4), // points after the last inst .inst = @intCast(func.mir_instructions.len + 3), // points after the last inst
.rs1 = count, .rs1 = count,
.rs2 = .zero, .rs2 = .zero,
}, },
@ -8026,6 +8026,7 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {
.mcv => |mcv| switch (mcv) { .mcv => |mcv| switch (mcv) {
.none => .none, .none => .none,
.undef => unreachable, .undef => unreachable,
.lea_symbol => |sym_index| .{ .lea_symbol = .{ .sym = sym_index } },
.load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } }, .load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } },
.load_tlv => |sym_index| .{ .lea_tlv = sym_index }, .load_tlv => |sym_index| .{ .lea_tlv = sym_index },
.immediate => |imm| .{ .immediate = imm }, .immediate => |imm| .{ .immediate = imm },

View File

@ -43,31 +43,19 @@ pub fn emitMir(emit: *Emit) Error!void {
.fmt = std.meta.activeTag(lowered_inst), .fmt = std.meta.activeTag(lowered_inst),
}), }),
.load_symbol_reloc => |symbol| { .load_symbol_reloc => |symbol| {
const is_obj_or_static_lib = switch (emit.lower.output_mode) {
.Exe => false,
.Obj => true,
.Lib => emit.lower.link_mode == .static,
};
const elf_file = emit.bin_file.cast(.elf).?; const elf_file = emit.bin_file.cast(.elf).?;
const zo = elf_file.zigObjectPtr().?; const zo = elf_file.zigObjectPtr().?;
const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?; const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
const sym = zo.symbol(symbol.sym_index); const sym = zo.symbol(symbol.sym_index);
var hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20); if (sym.flags.is_extern_ptr and emit.lower.pic) {
var lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I); return emit.fail("emit GOT relocation for symbol '{s}'", .{sym.name(elf_file)});
if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {
_ = try sym.getOrCreateZigGotEntry(symbol.sym_index, elf_file);
hi_r_type = Elf.R_ZIG_GOT_HI20;
lo_r_type = Elf.R_ZIG_GOT_LO12;
} else if (sym.flags.needs_got) {
hi_r_type = Elf.R_GOT_HI20_STATIC; // TODO: rework this #20887
lo_r_type = Elf.R_GOT_LO12_I_STATIC; // TODO: rework this #20887
} }
const hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20);
const lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I);
try atom_ptr.addReloc(elf_file, .{ try atom_ptr.addReloc(elf_file, .{
.r_offset = start_offset, .r_offset = start_offset,
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type, .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type,

View File

@ -1349,34 +1349,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
// Due to incremental compilation, how function calls are generated depends // Due to incremental compilation, how function calls are generated depends
// on linking. // on linking.
if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) { if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) {
.func => |func| { .func => {
const got_addr = if (self.bin_file.cast(.elf)) |elf_file| blk: { return self.fail("TODO implement calling functions", .{});
const zo = elf_file.zigObjectPtr().?;
const sym_index = try zo.getOrCreateMetadataForNav(elf_file, func.owner_nav);
const sym = zo.symbol(sym_index);
_ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
break :blk @as(u32, @intCast(sym.zigGotAddress(elf_file)));
} else @panic("TODO SPARCv9 currently does not support non-ELF binaries");
try self.genSetReg(Type.usize, .o7, .{ .memory = got_addr });
_ = try self.addInst(.{
.tag = .jmpl,
.data = .{
.arithmetic_3op = .{
.is_imm = false,
.rd = .o7,
.rs1 = .o7,
.rs2_or_imm = .{ .rs2 = .g0 },
},
},
});
// TODO Find a way to fill this delay slot
_ = try self.addInst(.{
.tag = .nop,
.data = .{ .nop = {} },
});
}, },
.@"extern" => { .@"extern" => {
return self.fail("TODO implement calling extern functions", .{}); return self.fail("TODO implement calling extern functions", .{});
@ -4153,7 +4127,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
.mcv => |mcv| switch (mcv) { .mcv => |mcv| switch (mcv) {
.none => .none, .none => .none,
.undef => .undef, .undef => .undef,
.load_got, .load_symbol, .load_direct, .load_tlv => unreachable, // TODO .load_got, .load_symbol, .load_direct, .load_tlv, .lea_symbol => unreachable, // TODO
.immediate => |imm| .{ .immediate = imm }, .immediate => |imm| .{ .immediate = imm },
.memory => |addr| .{ .memory = addr }, .memory => |addr| .{ .memory = addr },
}, },

View File

@ -1379,14 +1379,22 @@ fn asmImmediate(self: *Self, tag: Mir.Inst.FixedTag, imm: Immediate) !void {
.ops = switch (imm) { .ops = switch (imm) {
.signed => .i_s, .signed => .i_s,
.unsigned => .i_u, .unsigned => .i_u,
.reloc => .rel,
}, },
.data = .{ .i = .{ .data = switch (imm) {
.fixes = tag[0], .reloc => |x| reloc: {
.i = switch (imm) { assert(tag[0] == ._);
.signed => |s| @bitCast(s), break :reloc .{ .reloc = x };
.unsigned => |u| @intCast(u),
}, },
} }, .signed, .unsigned => .{ .i = .{
.fixes = tag[0],
.i = switch (imm) {
.signed => |s| @bitCast(s),
.unsigned => |u| @intCast(u),
.reloc => unreachable,
},
} },
},
}); });
} }
@ -1406,6 +1414,7 @@ fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.FixedTag, reg: Register, imm:
const ops: Mir.Inst.Ops = switch (imm) { const ops: Mir.Inst.Ops = switch (imm) {
.signed => .ri_s, .signed => .ri_s,
.unsigned => |u| if (math.cast(u32, u)) |_| .ri_u else .ri64, .unsigned => |u| if (math.cast(u32, u)) |_| .ri_u else .ri64,
.reloc => unreachable,
}; };
_ = try self.addInst(.{ _ = try self.addInst(.{
.tag = tag[1], .tag = tag[1],
@ -1417,6 +1426,7 @@ fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.FixedTag, reg: Register, imm:
.i = switch (imm) { .i = switch (imm) {
.signed => |s| @bitCast(s), .signed => |s| @bitCast(s),
.unsigned => |u| @intCast(u), .unsigned => |u| @intCast(u),
.reloc => unreachable,
}, },
} }, } },
.ri64 => .{ .rx = .{ .ri64 => .{ .rx = .{
@ -1488,6 +1498,7 @@ fn asmRegisterRegisterRegisterImmediate(
.i = switch (imm) { .i = switch (imm) {
.signed => |s| @bitCast(@as(i8, @intCast(s))), .signed => |s| @bitCast(@as(i8, @intCast(s))),
.unsigned => |u| @intCast(u), .unsigned => |u| @intCast(u),
.reloc => unreachable,
}, },
} }, } },
}); });
@ -1505,6 +1516,7 @@ fn asmRegisterRegisterImmediate(
.ops = switch (imm) { .ops = switch (imm) {
.signed => .rri_s, .signed => .rri_s,
.unsigned => .rri_u, .unsigned => .rri_u,
.reloc => unreachable,
}, },
.data = .{ .rri = .{ .data = .{ .rri = .{
.fixes = tag[0], .fixes = tag[0],
@ -1513,6 +1525,7 @@ fn asmRegisterRegisterImmediate(
.i = switch (imm) { .i = switch (imm) {
.signed => |s| @bitCast(s), .signed => |s| @bitCast(s),
.unsigned => |u| @intCast(u), .unsigned => |u| @intCast(u),
.reloc => unreachable,
}, },
} }, } },
}); });
@ -1610,6 +1623,7 @@ fn asmRegisterMemoryImmediate(
if (switch (imm) { if (switch (imm) {
.signed => |s| if (math.cast(i16, s)) |x| @as(u16, @bitCast(x)) else null, .signed => |s| if (math.cast(i16, s)) |x| @as(u16, @bitCast(x)) else null,
.unsigned => |u| math.cast(u16, u), .unsigned => |u| math.cast(u16, u),
.reloc => unreachable,
}) |small_imm| { }) |small_imm| {
_ = try self.addInst(.{ _ = try self.addInst(.{
.tag = tag[1], .tag = tag[1],
@ -1625,6 +1639,7 @@ fn asmRegisterMemoryImmediate(
const payload = try self.addExtra(Mir.Imm32{ .imm = switch (imm) { const payload = try self.addExtra(Mir.Imm32{ .imm = switch (imm) {
.signed => |s| @bitCast(s), .signed => |s| @bitCast(s),
.unsigned => unreachable, .unsigned => unreachable,
.reloc => unreachable,
} }); } });
assert(payload + 1 == try self.addExtra(Mir.Memory.encode(m))); assert(payload + 1 == try self.addExtra(Mir.Memory.encode(m)));
_ = try self.addInst(.{ _ = try self.addInst(.{
@ -1632,6 +1647,7 @@ fn asmRegisterMemoryImmediate(
.ops = switch (imm) { .ops = switch (imm) {
.signed => .rmi_s, .signed => .rmi_s,
.unsigned => .rmi_u, .unsigned => .rmi_u,
.reloc => unreachable,
}, },
.data = .{ .rx = .{ .data = .{ .rx = .{
.fixes = tag[0], .fixes = tag[0],
@ -1679,6 +1695,7 @@ fn asmMemoryImmediate(self: *Self, tag: Mir.Inst.FixedTag, m: Memory, imm: Immed
const payload = try self.addExtra(Mir.Imm32{ .imm = switch (imm) { const payload = try self.addExtra(Mir.Imm32{ .imm = switch (imm) {
.signed => |s| @bitCast(s), .signed => |s| @bitCast(s),
.unsigned => |u| @intCast(u), .unsigned => |u| @intCast(u),
.reloc => unreachable,
} }); } });
assert(payload + 1 == try self.addExtra(Mir.Memory.encode(m))); assert(payload + 1 == try self.addExtra(Mir.Memory.encode(m)));
_ = try self.addInst(.{ _ = try self.addInst(.{
@ -1686,6 +1703,7 @@ fn asmMemoryImmediate(self: *Self, tag: Mir.Inst.FixedTag, m: Memory, imm: Immed
.ops = switch (imm) { .ops = switch (imm) {
.signed => .mi_s, .signed => .mi_s,
.unsigned => .mi_u, .unsigned => .mi_u,
.reloc => unreachable,
}, },
.data = .{ .x = .{ .data = .{ .x = .{
.fixes = tag[0], .fixes = tag[0],
@ -12310,33 +12328,10 @@ fn genCall(self: *Self, info: union(enum) {
if (self.bin_file.cast(.elf)) |elf_file| { if (self.bin_file.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?; const zo = elf_file.zigObjectPtr().?;
const sym_index = try zo.getOrCreateMetadataForNav(elf_file, func.owner_nav); const sym_index = try zo.getOrCreateMetadataForNav(elf_file, func.owner_nav);
if (self.mod.pic) { try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{
const callee_reg: Register = switch (resolved_cc) { .atom_index = try self.owner.getSymbolIndex(self),
.SysV => callee: { .sym_index = sym_index,
if (!fn_info.is_var_args) break :callee .rax; }));
const param_regs = abi.getCAbiIntParamRegs(resolved_cc);
break :callee if (call_info.gp_count < param_regs.len)
param_regs[call_info.gp_count]
else
.r10;
},
.Win64 => .rax,
else => unreachable,
};
try self.genSetReg(
callee_reg,
Type.usize,
.{ .load_symbol = .{ .sym = sym_index } },
.{},
);
try self.asmRegister(.{ ._, .call }, callee_reg);
} else try self.asmMemory(.{ ._, .call }, .{
.base = .{ .reloc = .{
.atom_index = try self.owner.getSymbolIndex(self),
.sym_index = sym_index,
} },
.mod = .{ .rm = .{ .size = .qword } },
});
} else if (self.bin_file.cast(.coff)) |coff_file| { } else if (self.bin_file.cast(.coff)) |coff_file| {
const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav); const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav);
const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; const sym_index = coff_file.getAtom(atom).getSymbolIndex().?;
@ -12365,7 +12360,16 @@ fn genCall(self: *Self, info: union(enum) {
}); });
} else unreachable; } else unreachable;
}, },
.@"extern" => |@"extern"| try self.genExternSymbolRef( .@"extern" => |@"extern"| if (self.bin_file.cast(.elf)) |elf_file| {
const target_sym_index = try elf_file.getGlobalSymbol(
@"extern".name.toSlice(ip),
@"extern".lib_name.toSlice(ip),
);
try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{
.atom_index = try self.owner.getSymbolIndex(self),
.sym_index = target_sym_index,
}));
} else try self.genExternSymbolRef(
.call, .call,
@"extern".lib_name.toSlice(ip), @"extern".lib_name.toSlice(ip),
@"extern".name.toSlice(ip), @"extern".name.toSlice(ip),
@ -12377,7 +12381,13 @@ fn genCall(self: *Self, info: union(enum) {
try self.genSetReg(.rax, Type.usize, .{ .air_ref = callee }, .{}); try self.genSetReg(.rax, Type.usize, .{ .air_ref = callee }, .{});
try self.asmRegister(.{ ._, .call }, .rax); try self.asmRegister(.{ ._, .call }, .rax);
}, },
.lib => |lib| try self.genExternSymbolRef(.call, lib.lib, lib.callee), .lib => |lib| if (self.bin_file.cast(.elf)) |elf_file| {
const target_sym_index = try elf_file.getGlobalSymbol(lib.callee, lib.lib);
try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{
.atom_index = try self.owner.getSymbolIndex(self),
.sym_index = target_sym_index,
}));
} else try self.genExternSymbolRef(.call, lib.lib, lib.callee),
} }
return call_info.return_value.short; return call_info.return_value.short;
} }
@ -14096,8 +14106,8 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
.{ .reg = try self.copyToTmpRegister(Type.usize, .{ .lea_got = sym_index }) } .{ .reg = try self.copyToTmpRegister(Type.usize, .{ .lea_got = sym_index }) }
else else
return self.fail("invalid modifier: '{s}'", .{modifier}), return self.fail("invalid modifier: '{s}'", .{modifier}),
.load_symbol => |sym_off| if (mem.eql(u8, modifier, "P")) .lea_symbol => |sym_off| if (mem.eql(u8, modifier, "P"))
.{ .reg = try self.copyToTmpRegister(Type.usize, .{ .load_symbol = sym_off }) } .{ .reg = try self.copyToTmpRegister(Type.usize, .{ .lea_symbol = sym_off }) }
else else
return self.fail("invalid modifier: '{s}'", .{modifier}), return self.fail("invalid modifier: '{s}'", .{modifier}),
else => return self.fail("invalid constraint: '{s}'", .{op_str}), else => return self.fail("invalid constraint: '{s}'", .{op_str}),
@ -15253,16 +15263,7 @@ fn genExternSymbolRef(
callee: []const u8, callee: []const u8,
) InnerError!void { ) InnerError!void {
const atom_index = try self.owner.getSymbolIndex(self); const atom_index = try self.owner.getSymbolIndex(self);
if (self.bin_file.cast(.elf)) |elf_file| { if (self.bin_file.cast(.coff)) |coff_file| {
_ = try self.addInst(.{
.tag = tag,
.ops = .extern_fn_reloc,
.data = .{ .reloc = .{
.atom_index = atom_index,
.sym_index = try elf_file.getGlobalSymbol(callee, lib),
} },
});
} else if (self.bin_file.cast(.coff)) |coff_file| {
const global_index = try coff_file.getGlobalSymbol(callee, lib); const global_index = try coff_file.getGlobalSymbol(callee, lib);
_ = try self.addInst(.{ _ = try self.addInst(.{
.tag = .mov, .tag = .mov,
@ -15306,7 +15307,7 @@ fn genLazySymbolRef(
if (self.mod.pic) { if (self.mod.pic) {
switch (tag) { switch (tag) {
.lea, .call => try self.genSetReg(reg, Type.usize, .{ .lea, .call => try self.genSetReg(reg, Type.usize, .{
.load_symbol = .{ .sym = sym_index }, .lea_symbol = .{ .sym = sym_index },
}, .{}), }, .{}),
.mov => try self.genSetReg(reg, Type.usize, .{ .mov => try self.genSetReg(reg, Type.usize, .{
.load_symbol = .{ .sym = sym_index }, .load_symbol = .{ .sym = sym_index },
@ -15324,14 +15325,11 @@ fn genLazySymbolRef(
.sym_index = sym_index, .sym_index = sym_index,
}; };
switch (tag) { switch (tag) {
.lea, .mov => try self.asmRegisterMemory(.{ ._, .mov }, reg.to64(), .{ .lea, .mov => try self.asmRegisterMemory(.{ ._, tag }, reg.to64(), .{
.base = .{ .reloc = reloc },
.mod = .{ .rm = .{ .size = .qword } },
}),
.call => try self.asmMemory(.{ ._, .call }, .{
.base = .{ .reloc = reloc }, .base = .{ .reloc = reloc },
.mod = .{ .rm = .{ .size = .qword } }, .mod = .{ .rm = .{ .size = .qword } },
}), }),
.call => try self.asmImmediate(.{ ._, .call }, Immediate.rel(reloc)),
else => unreachable, else => unreachable,
} }
} }
@ -18790,6 +18788,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
.immediate => |imm| .{ .immediate = imm }, .immediate => |imm| .{ .immediate = imm },
.memory => |addr| .{ .memory = addr }, .memory => |addr| .{ .memory = addr },
.load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } }, .load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } },
.lea_symbol => |sym_index| .{ .lea_symbol = .{ .sym = sym_index } },
.load_direct => |sym_index| .{ .load_direct = sym_index }, .load_direct => |sym_index| .{ .load_direct = sym_index },
.load_got => |sym_index| .{ .lea_got = sym_index }, .load_got => |sym_index| .{ .lea_got = sym_index },
.load_tlv => |sym_index| .{ .lea_tlv = sym_index }, .load_tlv => |sym_index| .{ .lea_tlv = sym_index },

View File

@ -8,7 +8,7 @@ const bits = @import("bits.zig");
const encoder = @import("encoder.zig"); const encoder = @import("encoder.zig");
const Encoding = @import("Encoding.zig"); const Encoding = @import("Encoding.zig");
const Immediate = bits.Immediate; const Immediate = Instruction.Immediate;
const Instruction = encoder.Instruction; const Instruction = encoder.Instruction;
const LegacyPrefixes = encoder.LegacyPrefixes; const LegacyPrefixes = encoder.LegacyPrefixes;
const Memory = Instruction.Memory; const Memory = Instruction.Memory;

View File

@ -110,21 +110,11 @@ pub fn emitMir(emit: *Emit) Error!void {
}); });
}, },
.linker_reloc => |data| if (emit.lower.bin_file.cast(.elf)) |elf_file| { .linker_reloc => |data| if (emit.lower.bin_file.cast(.elf)) |elf_file| {
const is_obj_or_static_lib = switch (emit.lower.output_mode) {
.Exe => false,
.Obj => true,
.Lib => emit.lower.link_mode == .static,
};
const zo = elf_file.zigObjectPtr().?; const zo = elf_file.zigObjectPtr().?;
const atom = zo.symbol(data.atom_index).atom(elf_file).?; const atom = zo.symbol(data.atom_index).atom(elf_file).?;
const sym = zo.symbol(data.sym_index); const sym = zo.symbol(data.sym_index);
if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {
_ = try sym.getOrCreateZigGotEntry(data.sym_index, elf_file);
}
if (emit.lower.pic) { if (emit.lower.pic) {
const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib) const r_type: u32 = if (sym.flags.is_extern_ptr)
link.File.Elf.R_ZIG_GOTPCREL
else if (sym.flags.needs_got)
@intFromEnum(std.elf.R_X86_64.GOTPCREL) @intFromEnum(std.elf.R_X86_64.GOTPCREL)
else else
@intFromEnum(std.elf.R_X86_64.PC32); @intFromEnum(std.elf.R_X86_64.PC32);
@ -134,28 +124,15 @@ pub fn emitMir(emit: *Emit) Error!void {
.r_addend = -4, .r_addend = -4,
}); });
} else { } else {
if (lowered_inst.encoding.mnemonic == .call and sym.flags.needs_zig_got and is_obj_or_static_lib) { const r_type: u32 = if (sym.flags.is_tls)
const r_type = @intFromEnum(std.elf.R_X86_64.PC32); @intFromEnum(std.elf.R_X86_64.TPOFF32)
try atom.addReloc(elf_file, .{ else
.r_offset = end_offset - 4, @intFromEnum(std.elf.R_X86_64.@"32");
.r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type, try atom.addReloc(elf_file, .{
.r_addend = -4, .r_offset = end_offset - 4,
}); .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type,
} else { .r_addend = 0,
const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib) });
link.File.Elf.R_ZIG_GOT32
else if (sym.flags.needs_got)
@intFromEnum(std.elf.R_X86_64.GOT32)
else if (sym.flags.is_tls)
@intFromEnum(std.elf.R_X86_64.TPOFF32)
else
@intFromEnum(std.elf.R_X86_64.@"32");
try atom.addReloc(elf_file, .{
.r_offset = end_offset - 4,
.r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type,
.r_addend = 0,
});
}
} }
} else if (emit.lower.bin_file.cast(.macho)) |macho_file| { } else if (emit.lower.bin_file.cast(.macho)) |macho_file| {
const is_obj_or_static_lib = switch (emit.lower.output_mode) { const is_obj_or_static_lib = switch (emit.lower.output_mode) {

View File

@ -397,33 +397,40 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
} }
_ = lower.reloc(.{ .linker_reloc = sym }); _ = lower.reloc(.{ .linker_reloc = sym });
break :op if (lower.pic) switch (mnemonic) { if (lower.pic) switch (mnemonic) {
.lea => { .lea => {
if (elf_sym.flags.is_extern_ptr) emit_mnemonic = .mov;
break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }; break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
}, },
.mov => { .mov => {
if (is_obj_or_static_lib and elf_sym.flags.needs_zig_got) emit_mnemonic = .lea; if (elf_sym.flags.is_extern_ptr) {
const reg = ops[0].reg;
lower.result_insts[lower.result_insts_len] =
try Instruction.new(.none, .mov, &[_]Operand{
.{ .reg = reg.to64() },
.{ .mem = Memory.rip(.qword, 0) },
});
lower.result_insts_len += 1;
break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ .base = .{
.reg = reg.to64(),
} }) };
}
break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }; break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
}, },
else => unreachable, else => unreachable,
} else switch (mnemonic) { } else switch (mnemonic) {
.call => break :op if (is_obj_or_static_lib and elf_sym.flags.needs_zig_got) .{ .call => break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
.imm = Immediate.s(0),
} else .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
.base = .{ .reg = .ds }, .base = .{ .reg = .ds },
}) }, }) },
.lea => { .lea => {
emit_mnemonic = .mov; emit_mnemonic = .mov;
break :op .{ .imm = Immediate.s(0) }; break :op .{ .imm = Immediate.s(0) };
}, },
.mov => { .mov => break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
if (is_obj_or_static_lib and elf_sym.flags.needs_zig_got) emit_mnemonic = .lea; .base = .{ .reg = .ds },
break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ }) },
.base = .{ .reg = .ds },
}) };
},
else => unreachable, else => unreachable,
}; }
} else if (lower.bin_file.cast(.macho)) |macho_file| { } else if (lower.bin_file.cast(.macho)) |macho_file| {
const zo = macho_file.getZigObject().?; const zo = macho_file.getZigObject().?;
const macho_sym = zo.symbols.items[sym.sym_index]; const macho_sym = zo.symbols.items[sym.sym_index];
@ -485,7 +492,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
.rrmi => inst.data.rrix.fixes, .rrmi => inst.data.rrix.fixes,
.mi_u, .mi_s => inst.data.x.fixes, .mi_u, .mi_s => inst.data.x.fixes,
.m => inst.data.x.fixes, .m => inst.data.x.fixes,
.extern_fn_reloc, .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ._, .extern_fn_reloc, .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc, .rel => ._,
else => return lower.fail("TODO lower .{s}", .{@tagName(inst.ops)}), else => return lower.fail("TODO lower .{s}", .{@tagName(inst.ops)}),
}; };
try lower.emit(switch (fixes) { try lower.emit(switch (fixes) {
@ -617,7 +624,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
.{ .mem = lower.mem(inst.data.rrix.payload) }, .{ .mem = lower.mem(inst.data.rrix.payload) },
.{ .imm = lower.imm(inst.ops, inst.data.rrix.i) }, .{ .imm = lower.imm(inst.ops, inst.data.rrix.i) },
}, },
.extern_fn_reloc => &.{ .extern_fn_reloc, .rel => &.{
.{ .imm = lower.reloc(.{ .linker_extern_fn = inst.data.reloc }) }, .{ .imm = lower.reloc(.{ .linker_extern_fn = inst.data.reloc }) },
}, },
.got_reloc, .direct_reloc, .import_reloc => ops: { .got_reloc, .direct_reloc, .import_reloc => ops: {
@ -660,7 +667,7 @@ const std = @import("std");
const Air = @import("../../Air.zig"); const Air = @import("../../Air.zig");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const ErrorMsg = Zcu.ErrorMsg; const ErrorMsg = Zcu.ErrorMsg;
const Immediate = bits.Immediate; const Immediate = Instruction.Immediate;
const Instruction = encoder.Instruction; const Instruction = encoder.Instruction;
const Lower = @This(); const Lower = @This();
const Memory = Instruction.Memory; const Memory = Instruction.Memory;

View File

@ -769,7 +769,7 @@ pub const Inst = struct {
/// Uses `imm` payload. /// Uses `imm` payload.
i_u, i_u,
/// Relative displacement operand. /// Relative displacement operand.
/// Uses `imm` payload. /// Uses `reloc` payload.
rel, rel,
/// Register, memory operands. /// Register, memory operands.
/// Uses `rx` payload with extra data of type `Memory`. /// Uses `rx` payload with extra data of type `Memory`.

View File

@ -569,6 +569,7 @@ pub const Memory = struct {
pub const Immediate = union(enum) { pub const Immediate = union(enum) {
signed: i32, signed: i32,
unsigned: u64, unsigned: u64,
reloc: Symbol,
pub fn u(x: u64) Immediate { pub fn u(x: u64) Immediate {
return .{ .unsigned = x }; return .{ .unsigned = x };
@ -578,39 +579,19 @@ pub const Immediate = union(enum) {
return .{ .signed = x }; return .{ .signed = x };
} }
pub fn asSigned(imm: Immediate, bit_size: u64) i64 { pub fn rel(symbol: Symbol) Immediate {
return switch (imm) { return .{ .reloc = symbol };
.signed => |x| switch (bit_size) {
1, 8 => @as(i8, @intCast(x)),
16 => @as(i16, @intCast(x)),
32, 64 => x,
else => unreachable,
},
.unsigned => |x| switch (bit_size) {
1, 8 => @as(i8, @bitCast(@as(u8, @intCast(x)))),
16 => @as(i16, @bitCast(@as(u16, @intCast(x)))),
32 => @as(i32, @bitCast(@as(u32, @intCast(x)))),
64 => @bitCast(x),
else => unreachable,
},
};
} }
pub fn asUnsigned(imm: Immediate, bit_size: u64) u64 { pub fn format(
return switch (imm) { imm: Immediate,
.signed => |x| switch (bit_size) { comptime fmt: []const u8,
1, 8 => @as(u8, @bitCast(@as(i8, @intCast(x)))), options: std.fmt.FormatOptions,
16 => @as(u16, @bitCast(@as(i16, @intCast(x)))), writer: anytype,
32, 64 => @as(u32, @bitCast(x)), ) @TypeOf(writer).Error!void {
else => unreachable, switch (imm) {
}, .reloc => |x| try std.fmt.formatType(x, fmt, options, writer, 0),
.unsigned => |x| switch (bit_size) { inline else => |x| try writer.print("{d}", .{x}),
1, 8 => @as(u8, @intCast(x)), }
16 => @as(u16, @intCast(x)),
32 => @as(u32, @intCast(x)),
64 => x,
else => unreachable,
},
};
} }
}; };

View File

@ -7,7 +7,6 @@ const testing = std.testing;
const bits = @import("bits.zig"); const bits = @import("bits.zig");
const Encoding = @import("Encoding.zig"); const Encoding = @import("Encoding.zig");
const FrameIndex = bits.FrameIndex; const FrameIndex = bits.FrameIndex;
const Immediate = bits.Immediate;
const Register = bits.Register; const Register = bits.Register;
const Symbol = bits.Symbol; const Symbol = bits.Symbol;
@ -28,6 +27,55 @@ pub const Instruction = struct {
repnz, repnz,
}; };
pub const Immediate = union(enum) {
signed: i32,
unsigned: u64,
pub fn u(x: u64) Immediate {
return .{ .unsigned = x };
}
pub fn s(x: i32) Immediate {
return .{ .signed = x };
}
pub fn asSigned(imm: Immediate, bit_size: u64) i64 {
return switch (imm) {
.signed => |x| switch (bit_size) {
1, 8 => @as(i8, @intCast(x)),
16 => @as(i16, @intCast(x)),
32, 64 => x,
else => unreachable,
},
.unsigned => |x| switch (bit_size) {
1, 8 => @as(i8, @bitCast(@as(u8, @intCast(x)))),
16 => @as(i16, @bitCast(@as(u16, @intCast(x)))),
32 => @as(i32, @bitCast(@as(u32, @intCast(x)))),
64 => @bitCast(x),
else => unreachable,
},
};
}
pub fn asUnsigned(imm: Immediate, bit_size: u64) u64 {
return switch (imm) {
.signed => |x| switch (bit_size) {
1, 8 => @as(u8, @bitCast(@as(i8, @intCast(x)))),
16 => @as(u16, @bitCast(@as(i16, @intCast(x)))),
32, 64 => @as(u32, @bitCast(x)),
else => unreachable,
},
.unsigned => |x| switch (bit_size) {
1, 8 => @as(u8, @intCast(x)),
16 => @as(u16, @intCast(x)),
32 => @as(u32, @intCast(x)),
64 => x,
else => unreachable,
},
};
}
};
pub const Memory = union(enum) { pub const Memory = union(enum) {
sib: Sib, sib: Sib,
rip: Rip, rip: Rip,
@ -1119,7 +1167,7 @@ test "encode" {
const inst = try Instruction.new(.none, .mov, &.{ const inst = try Instruction.new(.none, .mov, &.{
.{ .reg = .rbx }, .{ .reg = .rbx },
.{ .imm = Immediate.u(4) }, .{ .imm = Instruction.Immediate.u(4) },
}); });
try inst.encode(buf.writer(), .{}); try inst.encode(buf.writer(), .{});
try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items); try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items);
@ -1129,47 +1177,47 @@ test "lower I encoding" {
var enc = TestEncode{}; var enc = TestEncode{};
try enc.encode(.push, &.{ try enc.encode(.push, &.{
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x6A\x10", enc.code(), "push 0x10"); try expectEqualHexStrings("\x6A\x10", enc.code(), "push 0x10");
try enc.encode(.push, &.{ try enc.encode(.push, &.{
.{ .imm = Immediate.u(0x1000) }, .{ .imm = Instruction.Immediate.u(0x1000) },
}); });
try expectEqualHexStrings("\x66\x68\x00\x10", enc.code(), "push 0x1000"); try expectEqualHexStrings("\x66\x68\x00\x10", enc.code(), "push 0x1000");
try enc.encode(.push, &.{ try enc.encode(.push, &.{
.{ .imm = Immediate.u(0x10000000) }, .{ .imm = Instruction.Immediate.u(0x10000000) },
}); });
try expectEqualHexStrings("\x68\x00\x00\x00\x10", enc.code(), "push 0x10000000"); try expectEqualHexStrings("\x68\x00\x00\x00\x10", enc.code(), "push 0x10000000");
try enc.encode(.adc, &.{ try enc.encode(.adc, &.{
.{ .reg = .rax }, .{ .reg = .rax },
.{ .imm = Immediate.u(0x10000000) }, .{ .imm = Instruction.Immediate.u(0x10000000) },
}); });
try expectEqualHexStrings("\x48\x15\x00\x00\x00\x10", enc.code(), "adc rax, 0x10000000"); try expectEqualHexStrings("\x48\x15\x00\x00\x00\x10", enc.code(), "adc rax, 0x10000000");
try enc.encode(.add, &.{ try enc.encode(.add, &.{
.{ .reg = .al }, .{ .reg = .al },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x04\x10", enc.code(), "add al, 0x10"); try expectEqualHexStrings("\x04\x10", enc.code(), "add al, 0x10");
try enc.encode(.add, &.{ try enc.encode(.add, &.{
.{ .reg = .rax }, .{ .reg = .rax },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
try enc.encode(.sbb, &.{ try enc.encode(.sbb, &.{
.{ .reg = .ax }, .{ .reg = .ax },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x66\x1D\x10\x00", enc.code(), "sbb ax, 0x10"); try expectEqualHexStrings("\x66\x1D\x10\x00", enc.code(), "sbb ax, 0x10");
try enc.encode(.xor, &.{ try enc.encode(.xor, &.{
.{ .reg = .al }, .{ .reg = .al },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x34\x10", enc.code(), "xor al, 0x10"); try expectEqualHexStrings("\x34\x10", enc.code(), "xor al, 0x10");
} }
@ -1179,43 +1227,43 @@ test "lower MI encoding" {
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .r12 }, .{ .reg = .r12 },
.{ .imm = Immediate.u(0x1000) }, .{ .imm = Instruction.Immediate.u(0x1000) },
}); });
try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) }, .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10"); try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .r12 }, .{ .reg = .r12 },
.{ .imm = Immediate.u(0x1000) }, .{ .imm = Instruction.Immediate.u(0x1000) },
}); });
try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .r12 }, .{ .reg = .r12 },
.{ .imm = Immediate.u(0x1000) }, .{ .imm = Instruction.Immediate.u(0x1000) },
}); });
try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .rax }, .{ .reg = .rax },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10"); try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 } }) }, .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 } }) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10"); try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Instruction.Memory.rip(.qword, 0x10) }, .{ .mem = Instruction.Memory.rip(.qword, 0x10) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
"\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00", "\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00",
@ -1225,19 +1273,19 @@ test "lower MI encoding" {
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -8 }) }, .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -8 }) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10"); try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -2 }) }, .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -2 }) },
.{ .imm = Immediate.s(-16) }, .{ .imm = Instruction.Immediate.s(-16) },
}); });
try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16"); try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -1 }) }, .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -1 }) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10"); try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10");
@ -1247,7 +1295,7 @@ test "lower MI encoding" {
.disp = 0x10000000, .disp = 0x10000000,
.scale_index = .{ .scale = 2, .index = .rcx }, .scale_index = .{ .scale = 2, .index = .rcx },
}) }, }) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
"\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00", "\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00",
@ -1257,43 +1305,43 @@ test "lower MI encoding" {
try enc.encode(.adc, &.{ try enc.encode(.adc, &.{
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) }, .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10"); try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10");
try enc.encode(.adc, &.{ try enc.encode(.adc, &.{
.{ .mem = Instruction.Memory.rip(.qword, 0) }, .{ .mem = Instruction.Memory.rip(.qword, 0) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x48\x83\x15\x00\x00\x00\x00\x10", enc.code(), "adc QWORD PTR [rip], 0x10"); try expectEqualHexStrings("\x48\x83\x15\x00\x00\x00\x00\x10", enc.code(), "adc QWORD PTR [rip], 0x10");
try enc.encode(.adc, &.{ try enc.encode(.adc, &.{
.{ .reg = .rax }, .{ .reg = .rax },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10"); try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10");
try enc.encode(.add, &.{ try enc.encode(.add, &.{
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .rdx }, .disp = -8 }) }, .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .rdx }, .disp = -8 }) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10"); try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10");
try enc.encode(.add, &.{ try enc.encode(.add, &.{
.{ .reg = .rax }, .{ .reg = .rax },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
try enc.encode(.add, &.{ try enc.encode(.add, &.{
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) }, .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) },
.{ .imm = Immediate.s(-0x10) }, .{ .imm = Instruction.Immediate.s(-0x10) },
}); });
try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10"); try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10");
try enc.encode(.@"and", &.{ try enc.encode(.@"and", &.{
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
"\x83\x24\x25\x00\x00\x00\x10\x10", "\x83\x24\x25\x00\x00\x00\x10\x10",
@ -1303,7 +1351,7 @@ test "lower MI encoding" {
try enc.encode(.@"and", &.{ try enc.encode(.@"and", &.{
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) }, .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
"\x26\x83\x24\x25\x00\x00\x00\x10\x10", "\x26\x83\x24\x25\x00\x00\x00\x10\x10",
@ -1313,7 +1361,7 @@ test "lower MI encoding" {
try enc.encode(.@"and", &.{ try enc.encode(.@"and", &.{
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) }, .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
"\x41\x83\xA4\x24\x00\x00\x00\x10\x10", "\x41\x83\xA4\x24\x00\x00\x00\x10\x10",
@ -1323,7 +1371,7 @@ test "lower MI encoding" {
try enc.encode(.sub, &.{ try enc.encode(.sub, &.{
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) }, .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
"\x41\x83\xAB\x00\x00\x00\x10\x10", "\x41\x83\xAB\x00\x00\x00\x10\x10",
@ -1542,14 +1590,14 @@ test "lower RMI encoding" {
try enc.encode(.imul, &.{ try enc.encode(.imul, &.{
.{ .reg = .r11 }, .{ .reg = .r11 },
.{ .reg = .r12 }, .{ .reg = .r12 },
.{ .imm = Immediate.s(-2) }, .{ .imm = Instruction.Immediate.s(-2) },
}); });
try expectEqualHexStrings("\x4D\x6B\xDC\xFE", enc.code(), "imul r11, r12, -2"); try expectEqualHexStrings("\x4D\x6B\xDC\xFE", enc.code(), "imul r11, r12, -2");
try enc.encode(.imul, &.{ try enc.encode(.imul, &.{
.{ .reg = .r11 }, .{ .reg = .r11 },
.{ .mem = Instruction.Memory.rip(.qword, -16) }, .{ .mem = Instruction.Memory.rip(.qword, -16) },
.{ .imm = Immediate.s(-1024) }, .{ .imm = Instruction.Immediate.s(-1024) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
"\x4C\x69\x1D\xF0\xFF\xFF\xFF\x00\xFC\xFF\xFF", "\x4C\x69\x1D\xF0\xFF\xFF\xFF\x00\xFC\xFF\xFF",
@ -1560,7 +1608,7 @@ test "lower RMI encoding" {
try enc.encode(.imul, &.{ try enc.encode(.imul, &.{
.{ .reg = .bx }, .{ .reg = .bx },
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) }, .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
.{ .imm = Immediate.s(-1024) }, .{ .imm = Instruction.Immediate.s(-1024) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
"\x66\x69\x5D\xF0\x00\xFC", "\x66\x69\x5D\xF0\x00\xFC",
@ -1571,7 +1619,7 @@ test "lower RMI encoding" {
try enc.encode(.imul, &.{ try enc.encode(.imul, &.{
.{ .reg = .bx }, .{ .reg = .bx },
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) }, .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
.{ .imm = Immediate.u(1024) }, .{ .imm = Instruction.Immediate.u(1024) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
"\x66\x69\x5D\xF0\x00\x04", "\x66\x69\x5D\xF0\x00\x04",
@ -1687,7 +1735,7 @@ test "lower M encoding" {
try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0"); try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0");
try enc.encode(.call, &.{ try enc.encode(.call, &.{
.{ .imm = Immediate.s(0) }, .{ .imm = Instruction.Immediate.s(0) },
}); });
try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0"); try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0");
@ -1746,7 +1794,7 @@ test "lower OI encoding" {
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .rax }, .{ .reg = .rax },
.{ .imm = Immediate.u(0x1000000000000000) }, .{ .imm = Instruction.Immediate.u(0x1000000000000000) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
"\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10", "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10",
@ -1756,7 +1804,7 @@ test "lower OI encoding" {
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .r11 }, .{ .reg = .r11 },
.{ .imm = Immediate.u(0x1000000000000000) }, .{ .imm = Instruction.Immediate.u(0x1000000000000000) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
"\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10", "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10",
@ -1766,19 +1814,19 @@ test "lower OI encoding" {
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .r11d }, .{ .reg = .r11d },
.{ .imm = Immediate.u(0x10000000) }, .{ .imm = Instruction.Immediate.u(0x10000000) },
}); });
try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", enc.code(), "mov r11d, 0x10000000"); try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", enc.code(), "mov r11d, 0x10000000");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .r11w }, .{ .reg = .r11w },
.{ .imm = Immediate.u(0x1000) }, .{ .imm = Instruction.Immediate.u(0x1000) },
}); });
try expectEqualHexStrings("\x66\x41\xBB\x00\x10", enc.code(), "mov r11w, 0x1000"); try expectEqualHexStrings("\x66\x41\xBB\x00\x10", enc.code(), "mov r11w, 0x1000");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .r11b }, .{ .reg = .r11b },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Instruction.Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x41\xB3\x10", enc.code(), "mov r11b, 0x10"); try expectEqualHexStrings("\x41\xB3\x10", enc.code(), "mov r11b, 0x10");
} }
@ -1900,7 +1948,7 @@ test "invalid instruction" {
.{ .reg = .r12d }, .{ .reg = .r12d },
}); });
try invalidInstruction(.push, &.{ try invalidInstruction(.push, &.{
.{ .imm = Immediate.u(0x1000000000000000) }, .{ .imm = Instruction.Immediate.u(0x1000000000000000) },
}); });
} }
@ -2213,7 +2261,7 @@ const Assembler = struct {
.immediate => { .immediate => {
const is_neg = if (as.expect(.minus)) |_| true else |_| false; const is_neg = if (as.expect(.minus)) |_| true else |_| false;
const imm_tok = try as.expect(.numeral); const imm_tok = try as.expect(.numeral);
const imm: Immediate = if (is_neg) blk: { const imm: Instruction.Immediate = if (is_neg) blk: {
const imm = try std.fmt.parseInt(i32, as.source(imm_tok), 0); const imm = try std.fmt.parseInt(i32, as.source(imm_tok), 0);
break :blk .{ .signed = imm * -1 }; break :blk .{ .signed = imm * -1 };
} else .{ .unsigned = try std.fmt.parseInt(u64, as.source(imm_tok), 0) }; } else .{ .unsigned = try std.fmt.parseInt(u64, as.source(imm_tok), 0) };

View File

@ -828,6 +828,9 @@ pub const GenResult = union(enum) {
/// Reference to memory location but deferred until linker allocated the Decl in memory. /// 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. /// Traditionally, this corresponds to emitting a relocation in a relocatable object file.
load_symbol: u32, load_symbol: u32,
/// 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,
}; };
fn mcv(val: MCValue) GenResult { fn mcv(val: MCValue) GenResult {
@ -895,16 +898,15 @@ fn genNavRef(
if (lf.cast(.elf)) |elf_file| { if (lf.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?; const zo = elf_file.zigObjectPtr().?;
if (is_extern) { if (is_extern) {
// TODO audit this
const sym_index = try elf_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip)); const sym_index = try elf_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip));
zo.symbol(sym_index).flags.needs_got = true; zo.symbol(sym_index).flags.is_extern_ptr = true;
return GenResult.mcv(.{ .load_symbol = sym_index }); return GenResult.mcv(.{ .lea_symbol = sym_index });
} }
const sym_index = try zo.getOrCreateMetadataForNav(elf_file, nav_index); const sym_index = try zo.getOrCreateMetadataForNav(elf_file, nav_index);
if (!single_threaded and is_threadlocal) { if (!single_threaded and is_threadlocal) {
return GenResult.mcv(.{ .load_tlv = sym_index }); return GenResult.mcv(.{ .load_tlv = sym_index });
} }
return GenResult.mcv(.{ .load_symbol = sym_index }); return GenResult.mcv(.{ .lea_symbol = sym_index });
} else if (lf.cast(.macho)) |macho_file| { } else if (lf.cast(.macho)) |macho_file| {
const zo = macho_file.getZigObject().?; const zo = macho_file.getZigObject().?;
if (is_extern) { if (is_extern) {

View File

@ -64,9 +64,6 @@ phdrs: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{},
/// Tracked loadable segments during incremental linking. /// Tracked loadable segments during incremental linking.
/// The index into the program headers of a PT_LOAD program header with Read and Execute flags /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
phdr_zig_load_re_index: ?u16 = null, phdr_zig_load_re_index: ?u16 = null,
/// The index into the program headers of the global offset table.
/// It needs PT_LOAD and Read flags.
phdr_zig_got_index: ?u16 = null,
/// The index into the program headers of a PT_LOAD program header with Read flag /// The index into the program headers of a PT_LOAD program header with Read flag
phdr_zig_load_ro_index: ?u16 = null, phdr_zig_load_ro_index: ?u16 = null,
/// The index into the program headers of a PT_LOAD program header with Write flag /// The index into the program headers of a PT_LOAD program header with Write flag
@ -130,8 +127,6 @@ plt_got: PltGotSection = .{},
copy_rel: CopyRelSection = .{}, copy_rel: CopyRelSection = .{},
/// .rela.plt section /// .rela.plt section
rela_plt: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, rela_plt: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{},
/// .got.zig section
zig_got: ZigGotSection = .{},
/// SHT_GROUP sections /// SHT_GROUP sections
/// Applies only to a relocatable. /// Applies only to a relocatable.
comdat_group_sections: std.ArrayListUnmanaged(ComdatGroupSection) = .{}, comdat_group_sections: std.ArrayListUnmanaged(ComdatGroupSection) = .{},
@ -142,7 +137,6 @@ zig_text_section_index: ?u32 = null,
zig_data_rel_ro_section_index: ?u32 = null, zig_data_rel_ro_section_index: ?u32 = null,
zig_data_section_index: ?u32 = null, zig_data_section_index: ?u32 = null,
zig_bss_section_index: ?u32 = null, zig_bss_section_index: ?u32 = null,
zig_got_section_index: ?u32 = null,
debug_info_section_index: ?u32 = null, debug_info_section_index: ?u32 = null,
debug_abbrev_section_index: ?u32 = null, debug_abbrev_section_index: ?u32 = null,
@ -474,7 +468,6 @@ pub fn deinit(self: *Elf) void {
self.copy_rel.deinit(gpa); self.copy_rel.deinit(gpa);
self.rela_dyn.deinit(gpa); self.rela_dyn.deinit(gpa);
self.rela_plt.deinit(gpa); self.rela_plt.deinit(gpa);
self.zig_got.deinit(gpa);
self.comdat_group_sections.deinit(gpa); self.comdat_group_sections.deinit(gpa);
} }
@ -611,28 +604,13 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void {
.type = elf.PT_LOAD, .type = elf.PT_LOAD,
.offset = off, .offset = off,
.filesz = filesz, .filesz = filesz,
.addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000, .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000,
.memsz = filesz, .memsz = filesz,
.@"align" = self.page_size, .@"align" = self.page_size,
.flags = elf.PF_X | elf.PF_R | elf.PF_W, .flags = elf.PF_X | elf.PF_R | elf.PF_W,
}); });
} }
if (self.phdr_zig_got_index == null) {
const alignment = self.page_size;
const filesz = @as(u64, ptr_size) * options.symbol_count_hint;
const off = self.findFreeSpace(filesz, alignment);
self.phdr_zig_got_index = try self.addPhdr(.{
.type = elf.PT_LOAD,
.offset = off,
.filesz = filesz,
.addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000,
.memsz = filesz,
.@"align" = alignment,
.flags = elf.PF_R | elf.PF_W,
});
}
if (self.phdr_zig_load_ro_index == null) { if (self.phdr_zig_load_ro_index == null) {
const alignment = self.page_size; const alignment = self.page_size;
const filesz: u64 = 1024; const filesz: u64 = 1024;
@ -701,27 +679,6 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void {
try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_text_section_index.?, .{}); try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_text_section_index.?, .{});
} }
if (self.zig_got_section_index == null and !self.base.isRelocatable()) {
self.zig_got_section_index = try self.addSection(.{
.name = try self.insertShString(".got.zig"),
.type = elf.SHT_PROGBITS,
.addralign = ptr_size,
.flags = elf.SHF_ALLOC | elf.SHF_WRITE,
.offset = std.math.maxInt(u64),
});
const shdr = &self.shdrs.items[self.zig_got_section_index.?];
const phndx = self.phdr_zig_got_index.?;
const phdr = self.phdrs.items[phndx];
shdr.sh_addr = phdr.p_vaddr;
shdr.sh_offset = phdr.p_offset;
shdr.sh_size = phdr.p_memsz;
try self.phdr_to_shdr_table.putNoClobber(
gpa,
self.zig_got_section_index.?,
self.phdr_zig_got_index.?,
);
}
if (self.zig_data_rel_ro_section_index == null) { if (self.zig_data_rel_ro_section_index == null) {
self.zig_data_rel_ro_section_index = try self.addSection(.{ self.zig_data_rel_ro_section_index = try self.addSection(.{
.name = try self.insertShString(".data.rel.ro.zig"), .name = try self.insertShString(".data.rel.ro.zig"),
@ -900,6 +857,11 @@ pub fn growAllocSection(self: *Elf, shdr_index: u32, needed_size: u64) !void {
const shdr = &self.shdrs.items[shdr_index]; const shdr = &self.shdrs.items[shdr_index];
const maybe_phdr = if (self.phdr_to_shdr_table.get(shdr_index)) |phndx| &self.phdrs.items[phndx] else null; const maybe_phdr = if (self.phdr_to_shdr_table.get(shdr_index)) |phndx| &self.phdrs.items[phndx] else null;
const is_zerofill = shdr.sh_type == elf.SHT_NOBITS; const is_zerofill = shdr.sh_type == elf.SHT_NOBITS;
log.debug("allocated size {x} of {s}, needed size {x}", .{
self.allocatedSize(shdr.sh_offset),
self.getShString(shdr.sh_name),
needed_size,
});
if (needed_size > self.allocatedSize(shdr.sh_offset) and !is_zerofill) { if (needed_size > self.allocatedSize(shdr.sh_offset) and !is_zerofill) {
const existing_size = shdr.sh_size; const existing_size = shdr.sh_size;
@ -3156,8 +3118,8 @@ fn initSyntheticSections(self: *Elf) !void {
}); });
const needs_rela_dyn = blk: { const needs_rela_dyn = blk: {
if (self.got.flags.needs_rela or self.got.flags.needs_tlsld or if (self.got.flags.needs_rela or self.got.flags.needs_tlsld or self.copy_rel.symbols.items.len > 0)
self.zig_got.flags.needs_rela or self.copy_rel.symbols.items.len > 0) break :blk true; break :blk true;
if (self.zigObjectPtr()) |zig_object| { if (self.zigObjectPtr()) |zig_object| {
if (zig_object.num_dynrelocs > 0) break :blk true; if (zig_object.num_dynrelocs > 0) break :blk true;
} }
@ -3562,7 +3524,6 @@ fn sortPhdrs(self: *Elf) error{OutOfMemory}!void {
for (&[_]*?u16{ for (&[_]*?u16{
&self.phdr_zig_load_re_index, &self.phdr_zig_load_re_index,
&self.phdr_zig_got_index,
&self.phdr_zig_load_ro_index, &self.phdr_zig_load_ro_index,
&self.phdr_zig_load_zerofill_index, &self.phdr_zig_load_zerofill_index,
&self.phdr_table_index, &self.phdr_table_index,
@ -3694,7 +3655,6 @@ fn resetShdrIndexes(self: *Elf, backlinks: []const u32) !void {
&self.versym_section_index, &self.versym_section_index,
&self.verneed_section_index, &self.verneed_section_index,
&self.zig_text_section_index, &self.zig_text_section_index,
&self.zig_got_section_index,
&self.zig_data_rel_ro_section_index, &self.zig_data_rel_ro_section_index,
&self.zig_data_section_index, &self.zig_data_section_index,
&self.zig_bss_section_index, &self.zig_bss_section_index,
@ -3893,7 +3853,7 @@ fn updateSectionSizes(self: *Elf) !void {
} }
if (self.rela_dyn_section_index) |shndx| { if (self.rela_dyn_section_index) |shndx| {
var num = self.got.numRela(self) + self.copy_rel.numRela() + self.zig_got.numRela(); var num = self.got.numRela(self) + self.copy_rel.numRela();
if (self.zigObjectPtr()) |zig_object| { if (self.zigObjectPtr()) |zig_object| {
num += zig_object.num_dynrelocs; num += zig_object.num_dynrelocs;
} }
@ -4431,15 +4391,6 @@ pub fn updateSymtabSize(self: *Elf) !void {
strsize += ctx.strsize; strsize += ctx.strsize;
} }
if (self.zigObjectPtr()) |_| {
if (self.zig_got_section_index) |_| {
self.zig_got.output_symtab_ctx.ilocal = nlocals + 1;
self.zig_got.updateSymtabSize(self);
nlocals += self.zig_got.output_symtab_ctx.nlocals;
strsize += self.zig_got.output_symtab_ctx.strsize;
}
}
if (self.got_section_index) |_| { if (self.got_section_index) |_| {
self.got.output_symtab_ctx.ilocal = nlocals + 1; self.got.output_symtab_ctx.ilocal = nlocals + 1;
self.got.updateSymtabSize(self); self.got.updateSymtabSize(self);
@ -4576,9 +4527,6 @@ fn writeSyntheticSections(self: *Elf) !void {
const shdr = self.shdrs.items[shndx]; const shdr = self.shdrs.items[shndx];
try self.got.addRela(self); try self.got.addRela(self);
try self.copy_rel.addRela(self); try self.copy_rel.addRela(self);
if (self.zigObjectPtr()) |_| {
try self.zig_got.addRela(self);
}
self.sortRelaDyn(); self.sortRelaDyn();
try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_dyn.items), shdr.sh_offset); try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_dyn.items), shdr.sh_offset);
} }
@ -4674,10 +4622,6 @@ pub fn writeSymtab(self: *Elf) !void {
obj.asFile().writeSymtab(self); obj.asFile().writeSymtab(self);
} }
if (self.zig_got_section_index) |_| {
self.zig_got.writeSymtab(self);
}
if (self.got_section_index) |_| { if (self.got_section_index) |_| {
self.got.writeSymtab(self); self.got.writeSymtab(self);
} }
@ -5085,7 +5029,6 @@ pub fn isZigSection(self: Elf, shndx: u32) bool {
self.zig_data_rel_ro_section_index, self.zig_data_rel_ro_section_index,
self.zig_data_section_index, self.zig_data_section_index,
self.zig_bss_section_index, self.zig_bss_section_index,
self.zig_got_section_index,
}) |maybe_index| { }) |maybe_index| {
if (maybe_index) |index| { if (maybe_index) |index| {
if (index == shndx) return true; if (index == shndx) return true;
@ -5657,10 +5600,11 @@ fn fmtDumpState(
if (self.zigObjectPtr()) |zig_object| { if (self.zigObjectPtr()) |zig_object| {
try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.path }); try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.path });
try writer.print("{}{}\n", .{ try writer.print("{}{}", .{
zig_object.fmtAtoms(self), zig_object.fmtAtoms(self),
zig_object.fmtSymtab(self), zig_object.fmtSymtab(self),
}); });
try writer.writeByte('\n');
} }
for (self.objects.items) |index| { for (self.objects.items) |index| {
@ -5700,7 +5644,6 @@ fn fmtDumpState(
} }
} }
try writer.print("{}\n", .{self.zig_got.fmt(self)});
try writer.print("{}\n", .{self.got.fmt(self)}); try writer.print("{}\n", .{self.got.fmt(self)});
try writer.print("{}\n", .{self.plt.fmt(self)}); try writer.print("{}\n", .{self.plt.fmt(self)});
@ -5991,41 +5934,6 @@ const RelaSection = struct {
}; };
const RelaSectionTable = std.AutoArrayHashMapUnmanaged(u32, RelaSection); const RelaSectionTable = std.AutoArrayHashMapUnmanaged(u32, RelaSection);
pub const R_ZIG_GOT32: u32 = 0xff00;
pub const R_ZIG_GOTPCREL: u32 = 0xff01;
pub const R_ZIG_GOT_HI20: u32 = 0xff02;
pub const R_ZIG_GOT_LO12: u32 = 0xff03;
pub const R_GOT_HI20_STATIC: u32 = 0xff04;
pub const R_GOT_LO12_I_STATIC: u32 = 0xff05;
// Comptime asserts that no Zig relocs overlap with another ISA's reloc number
comptime {
const zig_relocs = .{
R_ZIG_GOT32,
R_ZIG_GOT_HI20,
R_ZIG_GOT_LO12,
R_ZIG_GOTPCREL,
R_GOT_HI20_STATIC,
R_GOT_LO12_I_STATIC,
};
const other_relocs = .{
elf.R_X86_64,
elf.R_AARCH64,
elf.R_RISCV,
elf.R_PPC64,
};
@setEvalBranchQuota(@min(other_relocs.len * zig_relocs.len * 256, 6200));
for (other_relocs) |relocs| {
for (@typeInfo(relocs).Enum.fields) |reloc| {
for (zig_relocs) |zig_reloc| {
assert(reloc.value != zig_reloc);
}
}
}
}
fn defaultEntrySymbolName(cpu_arch: std.Target.Cpu.Arch) []const u8 { fn defaultEntrySymbolName(cpu_arch: std.Target.Cpu.Arch) []const u8 {
return switch (cpu_arch) { return switch (cpu_arch) {
.mips, .mipsel, .mips64, .mips64el => "__start", .mips, .mipsel, .mips64, .mips64el => "__start",
@ -6095,6 +6003,5 @@ const StringTable = @import("StringTable.zig");
const Thunk = thunks.Thunk; const Thunk = thunks.Thunk;
const Value = @import("../Value.zig"); const Value = @import("../Value.zig");
const VerneedSection = synthetic_sections.VerneedSection; const VerneedSection = synthetic_sections.VerneedSection;
const ZigGotSection = synthetic_sections.ZigGotSection;
const ZigObject = @import("Elf/ZigObject.zig"); const ZigObject = @import("Elf/ZigObject.zig");
const riscv = @import("riscv.zig"); const riscv = @import("riscv.zig");

View File

@ -746,12 +746,10 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) RelocError!voi
const P = self.address(elf_file) + @as(i64, @intCast(rel.r_offset)); const P = self.address(elf_file) + @as(i64, @intCast(rel.r_offset));
// Addend from the relocation. // Addend from the relocation.
const A = rel.r_addend; const A = rel.r_addend;
// Address of the target symbol - can be address of the symbol within an atom or address of PLT stub. // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub, or address of a Zig trampoline.
const S = target.address(.{}, elf_file); const S = target.address(.{}, elf_file);
// Address of the global offset table. // Address of the global offset table.
const GOT = elf_file.gotAddress(); const GOT = elf_file.gotAddress();
// Address of the .zig.got table entry if any.
const ZIG_GOT = target.zigGotAddress(elf_file);
// Relative offset to the start of the global offset table. // Relative offset to the start of the global offset table.
const G = target.gotAddress(elf_file) - GOT; const G = target.gotAddress(elf_file) - GOT;
// // Address of the thread pointer. // // Address of the thread pointer.
@ -759,19 +757,18 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) RelocError!voi
// Address of the dynamic thread pointer. // Address of the dynamic thread pointer.
const DTP = elf_file.dtpAddress(); const DTP = elf_file.dtpAddress();
relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ZG({x}) ({s})", .{ relocs_log.debug(" {s}: {x}: [{x} => {x}] GOT({x}) ({s})", .{
relocation.fmtRelocType(rel.r_type(), cpu_arch), relocation.fmtRelocType(rel.r_type(), cpu_arch),
r_offset, r_offset,
P, P,
S + A, S + A,
G + GOT + A, G + GOT + A,
ZIG_GOT + A,
target.name(elf_file), target.name(elf_file),
}); });
try stream.seekTo(r_offset); try stream.seekTo(r_offset);
const args = ResolveArgs{ P, A, S, GOT, G, TP, DTP, ZIG_GOT }; const args = ResolveArgs{ P, A, S, GOT, G, TP, DTP };
switch (cpu_arch) { switch (cpu_arch) {
.x86_64 => x86_64.resolveRelocAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) { .x86_64 => x86_64.resolveRelocAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) {
@ -956,7 +953,7 @@ pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: any
// Address of the dynamic thread pointer. // Address of the dynamic thread pointer.
const DTP = elf_file.dtpAddress(); const DTP = elf_file.dtpAddress();
const args = ResolveArgs{ P, A, S, GOT, 0, 0, DTP, 0 }; const args = ResolveArgs{ P, A, S, GOT, 0, 0, DTP };
relocs_log.debug(" {}: {x}: [{x} => {x}] ({s})", .{ relocs_log.debug(" {}: {x}: [{x} => {x}] ({s})", .{
relocation.fmtRelocType(rel.r_type(), cpu_arch), relocation.fmtRelocType(rel.r_type(), cpu_arch),
@ -1025,7 +1022,7 @@ pub fn format(
_ = unused_fmt_string; _ = unused_fmt_string;
_ = options; _ = options;
_ = writer; _ = writer;
@compileError("do not format symbols directly"); @compileError("do not format Atom directly");
} }
pub fn fmt(atom: Atom, elf_file: *Elf) std.fmt.Formatter(format2) { pub fn fmt(atom: Atom, elf_file: *Elf) std.fmt.Formatter(format2) {
@ -1051,8 +1048,8 @@ fn format2(
const atom = ctx.atom; const atom = ctx.atom;
const elf_file = ctx.elf_file; const elf_file = ctx.elf_file;
try writer.print("atom({d}) : {s} : @{x} : shdr({d}) : align({x}) : size({x})", .{ try writer.print("atom({d}) : {s} : @{x} : shdr({d}) : align({x}) : size({x})", .{
atom.atom_index, atom.name(elf_file), atom.address(elf_file), atom.atom_index, atom.name(elf_file), atom.address(elf_file),
atom.output_section_index, atom.alignment, atom.size, atom.output_section_index, atom.alignment.toByteUnits() orelse 0, atom.size,
}); });
if (atom.fdes(elf_file).len > 0) { if (atom.fdes(elf_file).len > 0) {
try writer.writeAll(" : fdes{ "); try writer.writeAll(" : fdes{ ");
@ -1180,16 +1177,7 @@ const x86_64 = struct {
.TLSDESC_CALL, .TLSDESC_CALL,
=> {}, => {},
else => |x| switch (@intFromEnum(x)) { else => try atom.reportUnhandledRelocError(rel, elf_file),
// Zig custom relocations
Elf.R_ZIG_GOT32,
Elf.R_ZIG_GOTPCREL,
=> {
assert(symbol.flags.has_zig_got);
},
else => try atom.reportUnhandledRelocError(rel, elf_file),
},
} }
} }
@ -1209,7 +1197,7 @@ const x86_64 = struct {
const cwriter = stream.writer(); const cwriter = stream.writer();
const P, const A, const S, const GOT, const G, const TP, const DTP, const ZIG_GOT = args; const P, const A, const S, const GOT, const G, const TP, const DTP = args;
switch (r_type) { switch (r_type) {
.NONE => unreachable, .NONE => unreachable,
@ -1224,9 +1212,8 @@ const x86_64 = struct {
); );
}, },
.PLT32, .PLT32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little),
.PC32, .PC32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little),
=> try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little),
.GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little), .GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little),
.GOTPC32 => try cwriter.writeInt(i32, @as(i32, @intCast(GOT + A - P)), .little), .GOTPC32 => try cwriter.writeInt(i32, @as(i32, @intCast(GOT + A - P)), .little),
@ -1327,15 +1314,9 @@ const x86_64 = struct {
} }
}, },
.GOT32 => try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A)), .little), .GOT32 => try cwriter.writeInt(i32, @as(i32, @intCast(G + A)), .little),
else => |x| switch (@intFromEnum(x)) { else => try atom.reportUnhandledRelocError(rel, elf_file),
// Zig custom relocations
Elf.R_ZIG_GOT32 => try cwriter.writeInt(u32, @as(u32, @intCast(ZIG_GOT + A)), .little),
Elf.R_ZIG_GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(ZIG_GOT + A - P)), .little),
else => try atom.reportUnhandledRelocError(rel, elf_file),
},
} }
} }
@ -1355,7 +1336,7 @@ const x86_64 = struct {
const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type()); const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());
const cwriter = stream.writer(); const cwriter = stream.writer();
_, const A, const S, const GOT, _, _, const DTP, _ = args; _, const A, const S, const GOT, _, _, const DTP = args;
switch (r_type) { switch (r_type) {
.NONE => unreachable, .NONE => unreachable,
@ -1629,7 +1610,7 @@ const x86_64 = struct {
const bits = @import("../../arch/x86_64/bits.zig"); const bits = @import("../../arch/x86_64/bits.zig");
const encoder = @import("../../arch/x86_64/encoder.zig"); const encoder = @import("../../arch/x86_64/encoder.zig");
const Disassembler = @import("../../arch/x86_64/Disassembler.zig"); const Disassembler = @import("../../arch/x86_64/Disassembler.zig");
const Immediate = bits.Immediate; const Immediate = Instruction.Immediate;
const Instruction = encoder.Instruction; const Instruction = encoder.Instruction;
}; };
@ -1738,9 +1719,8 @@ const aarch64 = struct {
const code = code_buffer[r_offset..][0..4]; const code = code_buffer[r_offset..][0..4];
const file_ptr = atom.file(elf_file).?; const file_ptr = atom.file(elf_file).?;
const P, const A, const S, const GOT, const G, const TP, const DTP, const ZIG_GOT = args; const P, const A, const S, const GOT, const G, const TP, const DTP = args;
_ = DTP; _ = DTP;
_ = ZIG_GOT;
switch (r_type) { switch (r_type) {
.NONE => unreachable, .NONE => unreachable,
@ -1942,7 +1922,7 @@ const aarch64 = struct {
const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type()); const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type());
const cwriter = stream.writer(); const cwriter = stream.writer();
_, const A, const S, _, _, _, _, _ = args; _, const A, const S, _, _, _, _ = args;
switch (r_type) { switch (r_type) {
.NONE => unreachable, .NONE => unreachable,
@ -1998,19 +1978,7 @@ const riscv = struct {
.SUB32, .SUB32,
=> {}, => {},
else => |x| switch (@intFromEnum(x)) { else => try atom.reportUnhandledRelocError(rel, elf_file),
Elf.R_ZIG_GOT_HI20,
Elf.R_ZIG_GOT_LO12,
=> {
assert(symbol.flags.has_zig_got);
},
Elf.R_GOT_HI20_STATIC,
Elf.R_GOT_LO12_I_STATIC,
=> symbol.flags.needs_got = true,
else => try atom.reportUnhandledRelocError(rel, elf_file),
},
} }
} }
@ -2028,7 +1996,7 @@ const riscv = struct {
const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
const cwriter = stream.writer(); const cwriter = stream.writer();
const P, const A, const S, const GOT, const G, const TP, const DTP, const ZIG_GOT = args; const P, const A, const S, const GOT, const G, const TP, const DTP = args;
_ = TP; _ = TP;
_ = DTP; _ = DTP;
@ -2147,34 +2115,7 @@ const riscv = struct {
// TODO: annotates an ADD instruction that can be removed when TPREL is relaxed // TODO: annotates an ADD instruction that can be removed when TPREL is relaxed
}, },
else => |x| switch (@intFromEnum(x)) { else => try atom.reportUnhandledRelocError(rel, elf_file),
// Zig custom relocations
Elf.R_ZIG_GOT_HI20 => {
assert(target.flags.has_zig_got);
const disp: u32 = @bitCast(math.cast(i32, ZIG_GOT + A) orelse return error.Overflow);
riscv_util.writeInstU(code[r_offset..][0..4], disp);
},
Elf.R_ZIG_GOT_LO12 => {
assert(target.flags.has_zig_got);
const value: u32 = @bitCast(math.cast(i32, ZIG_GOT + A) orelse return error.Overflow);
riscv_util.writeInstI(code[r_offset..][0..4], value);
},
Elf.R_GOT_HI20_STATIC => {
assert(target.flags.has_got);
const disp: u32 = @bitCast(math.cast(i32, G + GOT + A) orelse return error.Overflow);
riscv_util.writeInstU(code[r_offset..][0..4], disp);
},
Elf.R_GOT_LO12_I_STATIC => {
assert(target.flags.has_got);
const disp: u32 = @bitCast(math.cast(i32, G + GOT + A) orelse return error.Overflow);
riscv_util.writeInstI(code[r_offset..][0..4], disp);
},
else => try atom.reportUnhandledRelocError(rel, elf_file),
},
} }
} }
@ -2194,7 +2135,7 @@ const riscv = struct {
const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
const cwriter = stream.writer(); const cwriter = stream.writer();
_, const A, const S, const GOT, _, _, const DTP, _ = args; _, const A, const S, const GOT, _, _, const DTP = args;
_ = GOT; _ = GOT;
_ = DTP; _ = DTP;
@ -2230,7 +2171,7 @@ const riscv = struct {
const riscv_util = @import("../riscv.zig"); const riscv_util = @import("../riscv.zig");
}; };
const ResolveArgs = struct { i64, i64, i64, i64, i64, i64, i64, i64 }; const ResolveArgs = struct { i64, i64, i64, i64, i64, i64, i64 };
const RelocError = error{ const RelocError = error{
Overflow, Overflow,

View File

@ -101,7 +101,7 @@ pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 {
return file_ptr.symbolRank(sym, in_archive); return file_ptr.symbolRank(sym, in_archive);
} }
pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf) i64 { pub fn address(symbol: Symbol, opts: struct { plt: bool = true, trampoline: bool = true }, elf_file: *Elf) i64 {
if (symbol.mergeSubsection(elf_file)) |msub| { if (symbol.mergeSubsection(elf_file)) |msub| {
if (!msub.alive) return 0; if (!msub.alive) return 0;
return msub.address(elf_file) + symbol.value; return msub.address(elf_file) + symbol.value;
@ -109,6 +109,9 @@ pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf
if (symbol.flags.has_copy_rel) { if (symbol.flags.has_copy_rel) {
return symbol.copyRelAddress(elf_file); return symbol.copyRelAddress(elf_file);
} }
if (symbol.flags.has_trampoline and opts.trampoline) {
return symbol.trampolineAddress(elf_file);
}
if (symbol.flags.has_plt and opts.plt) { if (symbol.flags.has_plt and opts.plt) {
if (!symbol.flags.is_canonical and symbol.flags.has_got) { if (!symbol.flags.is_canonical and symbol.flags.has_got) {
// We have a non-lazy bound function pointer, use that! // We have a non-lazy bound function pointer, use that!
@ -217,23 +220,11 @@ pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) i64 {
return entry.address(elf_file); return entry.address(elf_file);
} }
const GetOrCreateZigGotEntryResult = struct { pub fn trampolineAddress(symbol: Symbol, elf_file: *Elf) i64 {
found_existing: bool, if (!symbol.flags.has_trampoline) return 0;
index: ZigGotSection.Index, const zo = elf_file.zigObjectPtr().?;
}; const index = symbol.extra(elf_file).trampoline;
return zo.symbol(index).address(.{}, elf_file);
pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !GetOrCreateZigGotEntryResult {
assert(!elf_file.base.isRelocatable());
assert(symbol.flags.needs_zig_got);
if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).zig_got };
const index = try elf_file.zig_got.addSymbol(symbol_index, elf_file);
return .{ .found_existing = false, .index = index };
}
pub fn zigGotAddress(symbol: Symbol, elf_file: *Elf) i64 {
if (!symbol.flags.has_zig_got) return 0;
const extras = symbol.extra(elf_file);
return elf_file.zig_got.entryAddress(extras.zig_got, elf_file);
} }
pub fn dsoAlignment(symbol: Symbol, elf_file: *Elf) !u64 { pub fn dsoAlignment(symbol: Symbol, elf_file: *Elf) !u64 {
@ -259,7 +250,7 @@ const AddExtraOpts = struct {
tlsgd: ?u32 = null, tlsgd: ?u32 = null,
gottp: ?u32 = null, gottp: ?u32 = null,
tlsdesc: ?u32 = null, tlsdesc: ?u32 = null,
zig_got: ?u32 = null, trampoline: ?u32 = null,
}; };
pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) void { pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) void {
@ -312,7 +303,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
const shdr = elf_file.shdrs.items[st_shndx]; const shdr = elf_file.shdrs.items[st_shndx];
if (shdr.sh_flags & elf.SHF_TLS != 0 and file_ptr != .linker_defined) if (shdr.sh_flags & elf.SHF_TLS != 0 and file_ptr != .linker_defined)
break :blk symbol.address(.{ .plt = false }, elf_file) - elf_file.tlsAddress(); break :blk symbol.address(.{ .plt = false }, elf_file) - elf_file.tlsAddress();
break :blk symbol.address(.{ .plt = false }, elf_file); break :blk symbol.address(.{ .plt = false, .trampoline = false }, elf_file);
}; };
out.st_info = (st_bind << 4) | st_type; out.st_info = (st_bind << 4) | st_type;
out.st_other = esym.st_other; out.st_other = esym.st_other;
@ -331,7 +322,7 @@ pub fn format(
_ = unused_fmt_string; _ = unused_fmt_string;
_ = options; _ = options;
_ = writer; _ = writer;
@compileError("do not format symbols directly"); @compileError("do not format Symbol directly");
} }
const FormatContext = struct { const FormatContext = struct {
@ -388,7 +379,7 @@ fn format2(
try writer.print("%{d} : {s} : @{x}", .{ try writer.print("%{d} : {s} : @{x}", .{
symbol.esym_index, symbol.esym_index,
symbol.fmtName(elf_file), symbol.fmtName(elf_file),
symbol.address(.{}, elf_file), symbol.address(.{ .plt = false, .trampoline = false }, elf_file),
}); });
if (symbol.file(elf_file)) |file_ptr| { if (symbol.file(elf_file)) |file_ptr| {
if (symbol.isAbs(elf_file)) { if (symbol.isAbs(elf_file)) {
@ -456,17 +447,18 @@ pub const Flags = packed struct {
needs_tlsdesc: bool = false, needs_tlsdesc: bool = false,
has_tlsdesc: bool = false, has_tlsdesc: bool = false,
/// Whether the symbol contains .zig.got indirection.
needs_zig_got: bool = false,
has_zig_got: bool = false,
/// Whether the symbol is a TLS variable.
/// TODO this is really not needed if only we operated on esyms between
/// codegen and ZigObject.
is_tls: bool = false,
/// Whether the symbol is a merge subsection. /// Whether the symbol is a merge subsection.
merge_subsection: bool = false, merge_subsection: bool = false,
/// ZigObject specific flags
/// Whether the symbol has a trampoline.
has_trampoline: bool = false,
/// Whether the symbol is a TLS variable.
is_tls: bool = false,
/// Whether the symbol is an extern pointer (as opposed to function).
is_extern_ptr: bool = false,
}; };
pub const Extra = struct { pub const Extra = struct {
@ -479,8 +471,8 @@ pub const Extra = struct {
tlsgd: u32 = 0, tlsgd: u32 = 0,
gottp: u32 = 0, gottp: u32 = 0,
tlsdesc: u32 = 0, tlsdesc: u32 = 0,
zig_got: u32 = 0,
merge_section: u32 = 0, merge_section: u32 = 0,
trampoline: u32 = 0,
}; };
pub const Index = u32; pub const Index = u32;

View File

@ -102,23 +102,17 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
} }
self.relocs.deinit(allocator); self.relocs.deinit(allocator);
{ for (self.navs.values()) |*meta| {
var it = self.navs.iterator(); meta.exports.deinit(allocator);
while (it.next()) |entry| {
entry.value_ptr.exports.deinit(allocator);
}
self.navs.deinit(allocator);
} }
self.navs.deinit(allocator);
self.lazy_syms.deinit(allocator); self.lazy_syms.deinit(allocator);
{ for (self.uavs.values()) |*meta| {
var it = self.uavs.iterator(); meta.exports.deinit(allocator);
while (it.next()) |entry| {
entry.value_ptr.exports.deinit(allocator);
}
self.uavs.deinit(allocator);
} }
self.uavs.deinit(allocator);
for (self.tls_variables.values()) |*tlv| { for (self.tls_variables.values()) |*tlv| {
tlv.deinit(allocator); tlv.deinit(allocator);
@ -161,6 +155,16 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !voi
if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed; if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed;
} }
if (build_options.enable_logging) {
const pt: Zcu.PerThread = .{ .zcu = elf_file.base.comp.module.?, .tid = tid };
for (self.navs.keys(), self.navs.values()) |nav_index, meta| {
checkNavAllocated(pt, nav_index, meta);
}
for (self.uavs.keys(), self.uavs.values()) |uav_index, meta| {
checkUavAllocated(pt, uav_index, meta);
}
}
if (self.dwarf) |*dw| { if (self.dwarf) |*dw| {
const pt: Zcu.PerThread = .{ .zcu = elf_file.base.comp.module.?, .tid = tid }; const pt: Zcu.PerThread = .{ .zcu = elf_file.base.comp.module.?, .tid = tid };
try dw.flushModule(pt); try dw.flushModule(pt);
@ -698,6 +702,7 @@ pub fn lowerUav(
else => explicit_alignment, else => explicit_alignment,
}; };
if (self.uavs.get(uav)) |metadata| { if (self.uavs.get(uav)) |metadata| {
assert(metadata.allocated);
const sym = self.symbol(metadata.symbol_index); const sym = self.symbol(metadata.symbol_index);
const existing_alignment = sym.atom(elf_file).?.alignment; const existing_alignment = sym.atom(elf_file).?.alignment;
if (uav_alignment.order(existing_alignment).compare(.lte)) if (uav_alignment.order(existing_alignment).compare(.lte))
@ -729,7 +734,7 @@ pub fn lowerUav(
.ok => |sym_index| sym_index, .ok => |sym_index| sym_index,
.fail => |em| return .{ .fail = em }, .fail => |em| return .{ .fail = em },
}; };
try self.uavs.put(gpa, uav, .{ .symbol_index = sym_index }); try self.uavs.put(gpa, uav, .{ .symbol_index = sym_index, .allocated = true });
return .{ .mcv = .{ .load_symbol = sym_index } }; return .{ .mcv = .{ .load_symbol = sym_index } };
} }
@ -747,13 +752,7 @@ pub fn getOrCreateMetadataForLazySymbol(
.const_data => .{ &gop.value_ptr.rodata_symbol_index, &gop.value_ptr.rodata_state }, .const_data => .{ &gop.value_ptr.rodata_symbol_index, &gop.value_ptr.rodata_state },
}; };
switch (state_ptr.*) { switch (state_ptr.*) {
.unused => { .unused => symbol_index_ptr.* = try self.newSymbolWithAtom(pt.zcu.gpa, 0),
const gpa = elf_file.base.comp.gpa;
const symbol_index = try self.newSymbolWithAtom(gpa, 0);
const sym = self.symbol(symbol_index);
sym.flags.needs_zig_got = true;
symbol_index_ptr.* = symbol_index;
},
.pending_flush => return symbol_index_ptr.*, .pending_flush => return symbol_index_ptr.*,
.flushed => {}, .flushed => {},
} }
@ -807,9 +806,6 @@ pub fn getOrCreateMetadataForNav(
sym.flags.is_tls = true; sym.flags.is_tls = true;
} }
} }
if (!sym.flags.is_tls) {
sym.flags.needs_zig_got = true;
}
gop.value_ptr.* = .{ .symbol_index = symbol_index }; gop.value_ptr.* = .{ .symbol_index = symbol_index };
} }
return gop.value_ptr.symbol_index; return gop.value_ptr.symbol_index;
@ -820,9 +816,9 @@ fn getNavShdrIndex(
elf_file: *Elf, elf_file: *Elf,
zcu: *Zcu, zcu: *Zcu,
nav_index: InternPool.Nav.Index, nav_index: InternPool.Nav.Index,
sym_index: Symbol.Index,
code: []const u8, code: []const u8,
) error{OutOfMemory}!u32 { ) error{OutOfMemory}!u32 {
_ = self;
const ip = &zcu.intern_pool; const ip = &zcu.intern_pool;
const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded; const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded;
const nav_val = zcu.navValue(nav_index); const nav_val = zcu.navValue(nav_index);
@ -832,10 +828,12 @@ fn getNavShdrIndex(
.@"extern" => |@"extern"| .{ @"extern".is_const, @"extern".is_threadlocal, .none }, .@"extern" => |@"extern"| .{ @"extern".is_const, @"extern".is_threadlocal, .none },
else => .{ true, false, nav_val.toIntern() }, else => .{ true, false, nav_val.toIntern() },
}; };
const has_relocs = self.symbol(sym_index).atom(elf_file).?.relocs(elf_file).len > 0;
if (any_non_single_threaded and is_threadlocal) { if (any_non_single_threaded and is_threadlocal) {
for (code) |byte| { const is_bss = !has_relocs and for (code) |byte| {
if (byte != 0) break; if (byte != 0) break false;
} else return elf_file.sectionByName(".tbss") orelse try elf_file.addSection(.{ } else true;
if (is_bss) return elf_file.sectionByName(".tbss") orelse try elf_file.addSection(.{
.type = elf.SHT_NOBITS, .type = elf.SHT_NOBITS,
.flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS, .flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS,
.name = try elf_file.insertShString(".tbss"), .name = try elf_file.insertShString(".tbss"),
@ -854,9 +852,10 @@ fn getNavShdrIndex(
.Debug, .ReleaseSafe => elf_file.zig_data_section_index.?, .Debug, .ReleaseSafe => elf_file.zig_data_section_index.?,
.ReleaseFast, .ReleaseSmall => elf_file.zig_bss_section_index.?, .ReleaseFast, .ReleaseSmall => elf_file.zig_bss_section_index.?,
}; };
for (code) |byte| { const is_bss = !has_relocs and for (code) |byte| {
if (byte != 0) break; if (byte != 0) break false;
} else return elf_file.zig_bss_section_index.?; } else true;
if (is_bss) return elf_file.zig_bss_section_index.?;
return elf_file.zig_data_section_index.?; return elf_file.zig_data_section_index.?;
} }
@ -909,13 +908,6 @@ fn updateNavCode(
if (old_vaddr != atom_ptr.value) { if (old_vaddr != atom_ptr.value) {
sym.value = 0; sym.value = 0;
esym.st_value = 0; esym.st_value = 0;
if (!elf_file.base.isRelocatable()) {
log.debug(" (writing new offset table entry)", .{});
assert(sym.flags.has_zig_got);
const extra = sym.extra(elf_file);
try elf_file.zig_got.writeOne(elf_file, extra.zig_got);
}
} }
} else if (code.len < old_size) { } else if (code.len < old_size) {
atom_ptr.shrink(elf_file); atom_ptr.shrink(elf_file);
@ -925,15 +917,11 @@ fn updateNavCode(
errdefer self.freeNavMetadata(elf_file, sym_index); errdefer self.freeNavMetadata(elf_file, sym_index);
sym.value = 0; sym.value = 0;
sym.flags.needs_zig_got = true;
esym.st_value = 0; esym.st_value = 0;
if (!elf_file.base.isRelocatable()) {
const gop = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
try elf_file.zig_got.writeOne(elf_file, gop.index);
}
} }
self.navs.getPtr(nav_index).?.allocated = true;
if (elf_file.base.child_pid) |pid| { if (elf_file.base.child_pid) |pid| {
switch (builtin.os.tag) { switch (builtin.os.tag) {
.linux => { .linux => {
@ -959,6 +947,7 @@ fn updateNavCode(
if (shdr.sh_type != elf.SHT_NOBITS) { if (shdr.sh_type != elf.SHT_NOBITS) {
const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value)); const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value));
try elf_file.base.file.?.pwriteAll(code, file_offset); try elf_file.base.file.?.pwriteAll(code, file_offset);
log.debug("writing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), file_offset, file_offset + code.len });
} }
} }
@ -1001,6 +990,8 @@ fn updateTlv(
atom_ptr.alignment = required_alignment; atom_ptr.alignment = required_alignment;
atom_ptr.size = code.len; atom_ptr.size = code.len;
self.navs.getPtr(nav_index).?.allocated = true;
{ {
const gop = try self.tls_variables.getOrPut(gpa, atom_ptr.atom_index); const gop = try self.tls_variables.getOrPut(gpa, atom_ptr.atom_index);
assert(!gop.found_existing); // TODO incremental updates assert(!gop.found_existing); // TODO incremental updates
@ -1065,8 +1056,21 @@ pub fn updateFunc(
}, },
}; };
const shndx = try self.getNavShdrIndex(elf_file, zcu, func.owner_nav, code); const shndx = try self.getNavShdrIndex(elf_file, zcu, func.owner_nav, sym_index, code);
log.debug("setting shdr({x},{s}) for {}", .{
shndx,
elf_file.getShString(elf_file.shdrs.items[shndx].sh_name),
ip.getNav(func.owner_nav).fqn.fmt(ip),
});
const old_rva, const old_alignment = blk: {
const atom_ptr = self.symbol(sym_index).atom(elf_file).?;
break :blk .{ atom_ptr.value, atom_ptr.alignment };
};
try self.updateNavCode(elf_file, pt, func.owner_nav, sym_index, shndx, code, elf.STT_FUNC); try self.updateNavCode(elf_file, pt, func.owner_nav, sym_index, shndx, code, elf.STT_FUNC);
const new_rva, const new_alignment = blk: {
const atom_ptr = self.symbol(sym_index).atom(elf_file).?;
break :blk .{ atom_ptr.value, atom_ptr.alignment };
};
if (dwarf_state) |*ds| { if (dwarf_state) |*ds| {
const sym = self.symbol(sym_index); const sym = self.symbol(sym_index);
@ -1080,6 +1084,41 @@ pub fn updateFunc(
} }
// Exports will be updated by `Zcu.processExports` after the update. // Exports will be updated by `Zcu.processExports` after the update.
if (old_rva != new_rva and old_rva > 0) {
// If we had to reallocate the function, we re-use the existing slot for a trampoline.
// In the rare case that the function has been further overaligned we skip creating a
// trampoline and update all symbols referring this function.
if (old_alignment.order(new_alignment) == .lt) {
@panic("TODO update all symbols referring this function");
}
// Create a trampoline to the new location at `old_rva`.
if (!self.symbol(sym_index).flags.has_trampoline) {
const name = try std.fmt.allocPrint(gpa, "{s}$trampoline", .{
self.symbol(sym_index).name(elf_file),
});
defer gpa.free(name);
const name_off = try self.addString(gpa, name);
const tr_size = trampolineSize(elf_file.getTarget().cpu.arch);
const tr_sym_index = try self.newSymbolWithAtom(gpa, name_off);
const tr_sym = self.symbol(tr_sym_index);
const tr_esym = &self.symtab.items(.elf_sym)[tr_sym.esym_index];
tr_esym.st_info |= elf.STT_OBJECT;
tr_esym.st_size = tr_size;
const tr_atom_ptr = tr_sym.atom(elf_file).?;
tr_atom_ptr.value = old_rva;
tr_atom_ptr.alive = true;
tr_atom_ptr.alignment = old_alignment;
tr_atom_ptr.output_section_index = elf_file.zig_text_section_index.?;
tr_atom_ptr.size = tr_size;
const target_sym = self.symbol(sym_index);
target_sym.addExtra(.{ .trampoline = tr_sym_index }, elf_file);
target_sym.flags.has_trampoline = true;
}
const target_sym = self.symbol(sym_index);
try writeTrampoline(self.symbol(target_sym.extra(elf_file).trampoline).*, target_sym.*, elf_file);
}
} }
pub fn updateNav( pub fn updateNav(
@ -1102,13 +1141,12 @@ pub fn updateNav(
.variable => |variable| Value.fromInterned(variable.init), .variable => |variable| Value.fromInterned(variable.init),
.@"extern" => |@"extern"| { .@"extern" => |@"extern"| {
if (ip.isFunctionType(@"extern".ty)) return; if (ip.isFunctionType(@"extern".ty)) return;
// Extern variable gets a .got entry only.
const sym_index = try self.getGlobalSymbol( const sym_index = try self.getGlobalSymbol(
elf_file, elf_file,
nav.name.toSlice(ip), nav.name.toSlice(ip),
@"extern".lib_name.toSlice(ip), @"extern".lib_name.toSlice(ip),
); );
self.symbol(sym_index).flags.needs_got = true; self.symbol(sym_index).flags.is_extern_ptr = true;
return; return;
}, },
else => nav_val, else => nav_val,
@ -1142,7 +1180,12 @@ pub fn updateNav(
}, },
}; };
const shndx = try self.getNavShdrIndex(elf_file, zcu, nav_index, code); const shndx = try self.getNavShdrIndex(elf_file, zcu, nav_index, sym_index, code);
log.debug("setting shdr({x},{s}) for {}", .{
shndx,
elf_file.getShString(elf_file.shdrs.items[shndx].sh_name),
nav.fqn.fmt(ip),
});
if (elf_file.shdrs.items[shndx].sh_flags & elf.SHF_TLS != 0) if (elf_file.shdrs.items[shndx].sh_flags & elf.SHF_TLS != 0)
try self.updateTlv(elf_file, pt, nav_index, sym_index, shndx, code) try self.updateTlv(elf_file, pt, nav_index, sym_index, shndx, code)
else else
@ -1225,14 +1268,8 @@ fn updateLazySymbol(
errdefer self.freeNavMetadata(elf_file, symbol_index); errdefer self.freeNavMetadata(elf_file, symbol_index);
local_sym.value = 0; local_sym.value = 0;
local_sym.flags.needs_zig_got = true;
local_esym.st_value = 0; local_esym.st_value = 0;
if (!elf_file.base.isRelocatable()) {
const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, elf_file);
try elf_file.zig_got.writeOne(elf_file, gop.index);
}
const shdr = elf_file.shdrs.items[output_section_index]; const shdr = elf_file.shdrs.items[output_section_index];
const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value)); const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value));
try elf_file.base.file.?.pwriteAll(code, file_offset); try elf_file.base.file.?.pwriteAll(code, file_offset);
@ -1440,6 +1477,52 @@ pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_n
return lookup_gop.value_ptr.*; return lookup_gop.value_ptr.*;
} }
const max_trampoline_len = 12;
fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) u64 {
const len = switch (cpu_arch) {
.x86_64 => 5, // jmp rel32
else => @panic("TODO implement trampoline size for this CPU arch"),
};
comptime assert(len <= max_trampoline_len);
return len;
}
fn writeTrampoline(tr_sym: Symbol, target: Symbol, elf_file: *Elf) !void {
const atom_ptr = tr_sym.atom(elf_file).?;
const shdr = elf_file.shdrs.items[atom_ptr.output_section_index];
const fileoff = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value));
const source_addr = tr_sym.address(.{}, elf_file);
const target_addr = target.address(.{ .trampoline = false }, elf_file);
var buf: [max_trampoline_len]u8 = undefined;
const out = switch (elf_file.getTarget().cpu.arch) {
.x86_64 => try x86_64.writeTrampolineCode(source_addr, target_addr, &buf),
else => @panic("TODO implement write trampoline for this CPU arch"),
};
try elf_file.base.file.?.pwriteAll(out, fileoff);
if (elf_file.base.child_pid) |pid| {
switch (builtin.os.tag) {
.linux => {
var local_vec: [1]std.posix.iovec_const = .{.{
.base = out.ptr,
.len = out.len,
}};
var remote_vec: [1]std.posix.iovec_const = .{.{
.base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(source_addr)))),
.len = out.len,
}};
const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0);
switch (std.os.linux.E.init(rc)) {
.SUCCESS => assert(rc == out.len),
else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
}
},
else => return error.HotSwapUnavailableOnHostOperatingSystem,
}
}
}
pub fn asFile(self: *ZigObject) File { pub fn asFile(self: *ZigObject) File {
return .{ .zig_object = self }; return .{ .zig_object = self };
} }
@ -1662,6 +1745,8 @@ const AvMetadata = struct {
symbol_index: Symbol.Index, symbol_index: Symbol.Index,
/// A list of all exports aliases of this Av. /// A list of all exports aliases of this Av.
exports: std.ArrayListUnmanaged(Symbol.Index) = .{}, exports: std.ArrayListUnmanaged(Symbol.Index) = .{},
/// Set to true if the AV has been initialized and allocated.
allocated: bool = false,
fn @"export"(m: AvMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 { fn @"export"(m: AvMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 {
for (m.exports.items) |*exp| { for (m.exports.items) |*exp| {
@ -1672,6 +1757,32 @@ const AvMetadata = struct {
} }
}; };
fn checkNavAllocated(pt: Zcu.PerThread, index: InternPool.Nav.Index, meta: AvMetadata) void {
if (!meta.allocated) {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const nav = ip.getNav(index);
log.err("NAV {}({d}) assigned symbol {d} but not allocated!", .{
nav.fqn.fmt(ip),
index,
meta.symbol_index,
});
}
}
fn checkUavAllocated(pt: Zcu.PerThread, index: InternPool.Index, meta: AvMetadata) void {
if (!meta.allocated) {
const zcu = pt.zcu;
const uav = Value.fromInterned(index);
const ty = uav.typeOf(zcu);
log.err("UAV {}({d}) assigned symbol {d} but not allocated!", .{
ty.fmt(pt),
index,
meta.symbol_index,
});
}
}
const TlsVariable = struct { const TlsVariable = struct {
symbol_index: Symbol.Index, symbol_index: Symbol.Index,
code: []const u8 = &[0]u8{}, code: []const u8 = &[0]u8{},
@ -1682,12 +1793,26 @@ const TlsVariable = struct {
}; };
const AtomList = std.ArrayListUnmanaged(Atom.Index); const AtomList = std.ArrayListUnmanaged(Atom.Index);
const NavTable = std.AutoHashMapUnmanaged(InternPool.Nav.Index, AvMetadata); const NavTable = std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, AvMetadata);
const UavTable = std.AutoHashMapUnmanaged(InternPool.Index, AvMetadata); const UavTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, AvMetadata);
const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, LazySymbolMetadata); const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, LazySymbolMetadata);
const TlsTable = std.AutoArrayHashMapUnmanaged(Atom.Index, TlsVariable); const TlsTable = std.AutoArrayHashMapUnmanaged(Atom.Index, TlsVariable);
const x86_64 = struct {
fn writeTrampolineCode(source_addr: i64, target_addr: i64, buf: *[max_trampoline_len]u8) ![]u8 {
const disp = @as(i64, @intCast(target_addr)) - source_addr - 5;
var bytes = [_]u8{
0xe9, 0x00, 0x00, 0x00, 0x00, // jmp rel32
};
assert(bytes.len == trampolineSize(.x86_64));
mem.writeInt(i32, bytes[1..][0..4], @intCast(disp), .little);
@memcpy(buf[0..bytes.len], &bytes);
return buf[0..bytes.len];
}
};
const assert = std.debug.assert; const assert = std.debug.assert;
const build_options = @import("build_options");
const builtin = @import("builtin"); const builtin = @import("builtin");
const codegen = @import("../../codegen.zig"); const codegen = @import("../../codegen.zig");
const elf = std.elf; const elf = std.elf;

View File

@ -112,19 +112,11 @@ fn formatRelocType(
_ = unused_fmt_string; _ = unused_fmt_string;
_ = options; _ = options;
const r_type = ctx.r_type; const r_type = ctx.r_type;
switch (r_type) { switch (ctx.cpu_arch) {
Elf.R_ZIG_GOT32 => try writer.writeAll("R_ZIG_GOT32"), .x86_64 => try writer.print("R_X86_64_{s}", .{@tagName(@as(elf.R_X86_64, @enumFromInt(r_type)))}),
Elf.R_ZIG_GOTPCREL => try writer.writeAll("R_ZIG_GOTPCREL"), .aarch64 => try writer.print("R_AARCH64_{s}", .{@tagName(@as(elf.R_AARCH64, @enumFromInt(r_type)))}),
Elf.R_ZIG_GOT_HI20 => try writer.writeAll("R_ZIG_GOT_HI20"), .riscv64 => try writer.print("R_RISCV_{s}", .{@tagName(@as(elf.R_RISCV, @enumFromInt(r_type)))}),
Elf.R_ZIG_GOT_LO12 => try writer.writeAll("R_ZIG_GOT_LO12"), else => unreachable,
Elf.R_GOT_HI20_STATIC => try writer.writeAll("R_GOT_HI20_STATIC"),
Elf.R_GOT_LO12_I_STATIC => try writer.writeAll("R_GOT_LO12_I_STATIC"),
else => switch (ctx.cpu_arch) {
.x86_64 => try writer.print("R_X86_64_{s}", .{@tagName(@as(elf.R_X86_64, @enumFromInt(r_type)))}),
.aarch64 => try writer.print("R_AARCH64_{s}", .{@tagName(@as(elf.R_AARCH64, @enumFromInt(r_type)))}),
.riscv64 => try writer.print("R_RISCV_{s}", .{@tagName(@as(elf.R_RISCV, @enumFromInt(r_type)))}),
else => unreachable,
},
} }
} }

View File

@ -223,215 +223,6 @@ pub const DynamicSection = struct {
} }
}; };
pub const ZigGotSection = struct {
entries: std.ArrayListUnmanaged(Symbol.Index) = .{},
output_symtab_ctx: Elf.SymtabCtx = .{},
flags: Flags = .{},
const Flags = packed struct {
needs_rela: bool = false,
dirty: bool = false,
};
pub const Index = u32;
pub fn deinit(zig_got: *ZigGotSection, allocator: Allocator) void {
zig_got.entries.deinit(allocator);
}
fn allocateEntry(zig_got: *ZigGotSection, allocator: Allocator) !Index {
try zig_got.entries.ensureUnusedCapacity(allocator, 1);
// TODO add free list
const index = @as(Index, @intCast(zig_got.entries.items.len));
_ = zig_got.entries.addOneAssumeCapacity();
zig_got.flags.dirty = true;
return index;
}
pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
const zo = elf_file.zigObjectPtr().?;
const index = try zig_got.allocateEntry(gpa);
const entry = &zig_got.entries.items[index];
entry.* = sym_index;
const symbol = zo.symbol(sym_index);
symbol.flags.has_zig_got = true;
if (elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) {
zig_got.flags.needs_rela = true;
}
symbol.addExtra(.{ .zig_got = index }, elf_file);
return index;
}
pub fn entryOffset(zig_got: ZigGotSection, index: Index, elf_file: *Elf) u64 {
_ = zig_got;
const entry_size = elf_file.archPtrWidthBytes();
const shdr = elf_file.shdrs.items[elf_file.zig_got_section_index.?];
return shdr.sh_offset + @as(u64, entry_size) * index;
}
pub fn entryAddress(zig_got: ZigGotSection, index: Index, elf_file: *Elf) i64 {
_ = zig_got;
const entry_size = elf_file.archPtrWidthBytes();
const shdr = elf_file.shdrs.items[elf_file.zig_got_section_index.?];
return @as(i64, @intCast(shdr.sh_addr)) + entry_size * index;
}
pub fn size(zig_got: ZigGotSection, elf_file: *Elf) usize {
return elf_file.archPtrWidthBytes() * zig_got.entries.items.len;
}
pub fn writeOne(zig_got: *ZigGotSection, elf_file: *Elf, index: Index) !void {
const zo = elf_file.zigObjectPtr().?;
if (zig_got.flags.dirty) {
const needed_size = zig_got.size(elf_file);
try elf_file.growAllocSection(elf_file.zig_got_section_index.?, needed_size);
zig_got.flags.dirty = false;
}
const entry_size: u16 = elf_file.archPtrWidthBytes();
const target = elf_file.getTarget();
const endian = target.cpu.arch.endian();
const off = zig_got.entryOffset(index, elf_file);
const vaddr: u64 = @intCast(zig_got.entryAddress(index, elf_file));
const entry = zig_got.entries.items[index];
const value = zo.symbol(entry).address(.{}, elf_file);
switch (entry_size) {
2 => {
var buf: [2]u8 = undefined;
std.mem.writeInt(u16, &buf, @intCast(value), endian);
try elf_file.base.file.?.pwriteAll(&buf, off);
},
4 => {
var buf: [4]u8 = undefined;
std.mem.writeInt(u32, &buf, @intCast(value), endian);
try elf_file.base.file.?.pwriteAll(&buf, off);
},
8 => {
var buf: [8]u8 = undefined;
std.mem.writeInt(u64, &buf, @intCast(value), endian);
try elf_file.base.file.?.pwriteAll(&buf, off);
if (elf_file.base.child_pid) |pid| {
switch (builtin.os.tag) {
.linux => {
var local_vec: [1]std.posix.iovec_const = .{.{
.base = &buf,
.len = buf.len,
}};
var remote_vec: [1]std.posix.iovec_const = .{.{
.base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(vaddr)))),
.len = buf.len,
}};
const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0);
switch (std.os.linux.E.init(rc)) {
.SUCCESS => assert(rc == buf.len),
else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
}
},
else => return error.HotSwapUnavailableOnHostOperatingSystem,
}
}
},
else => unreachable,
}
}
pub fn writeAll(zig_got: ZigGotSection, elf_file: *Elf, writer: anytype) !void {
const zo = elf_file.zigObjectPtr().?;
for (zig_got.entries.items) |entry| {
const symbol = zo.symbol(entry);
const value = symbol.address(.{ .plt = false }, elf_file);
try writeInt(value, elf_file, writer);
}
}
pub fn numRela(zig_got: ZigGotSection) usize {
return zig_got.entries.items.len;
}
pub fn addRela(zig_got: ZigGotSection, elf_file: *Elf) !void {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
const cpu_arch = elf_file.getTarget().cpu.arch;
const zo = elf_file.zigObjectPtr().?;
try elf_file.rela_dyn.ensureUnusedCapacity(gpa, zig_got.numRela());
for (zig_got.entries.items) |entry| {
const symbol = zo.symbol(entry);
const offset = symbol.zigGotAddress(elf_file);
elf_file.addRelaDynAssumeCapacity(.{
.offset = @intCast(offset),
.type = relocation.encode(.rel, cpu_arch),
.addend = symbol.address(.{ .plt = false }, elf_file),
});
}
}
pub fn updateSymtabSize(zig_got: *ZigGotSection, elf_file: *Elf) void {
const zo = elf_file.zigObjectPtr().?;
zig_got.output_symtab_ctx.nlocals = @as(u32, @intCast(zig_got.entries.items.len));
for (zig_got.entries.items) |entry| {
const name = zo.symbol(entry).name(elf_file);
zig_got.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$ziggot".len)) + 1;
}
}
pub fn writeSymtab(zig_got: ZigGotSection, elf_file: *Elf) void {
const zo = elf_file.zigObjectPtr().?;
for (zig_got.entries.items, zig_got.output_symtab_ctx.ilocal.., 0..) |entry, ilocal, index| {
const symbol = zo.symbol(entry);
const symbol_name = symbol.name(elf_file);
const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
elf_file.strtab.appendSliceAssumeCapacity(symbol_name);
elf_file.strtab.appendSliceAssumeCapacity("$ziggot");
elf_file.strtab.appendAssumeCapacity(0);
const st_value = zig_got.entryAddress(@intCast(index), elf_file);
const st_size = elf_file.archPtrWidthBytes();
elf_file.symtab.items[ilocal] = .{
.st_name = st_name,
.st_info = elf.STT_OBJECT,
.st_other = 0,
.st_shndx = @intCast(elf_file.zig_got_section_index.?),
.st_value = @intCast(st_value),
.st_size = st_size,
};
}
}
const FormatCtx = struct {
zig_got: ZigGotSection,
elf_file: *Elf,
};
pub fn fmt(zig_got: ZigGotSection, elf_file: *Elf) std.fmt.Formatter(format2) {
return .{ .data = .{ .zig_got = zig_got, .elf_file = elf_file } };
}
pub fn format2(
ctx: FormatCtx,
comptime unused_fmt_string: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = options;
_ = unused_fmt_string;
const zig_got = ctx.zig_got;
const elf_file = ctx.elf_file;
try writer.writeAll(".zig.got\n");
for (zig_got.entries.items, 0..) |entry, index| {
const zo = elf_file.zigObjectPtr().?;
const symbol = zo.symbol(entry);
try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{
index,
zig_got.entryAddress(@intCast(index), elf_file),
entry,
symbol.address(.{}, elf_file),
symbol.name(elf_file),
});
}
}
};
pub const GotSection = struct { pub const GotSection = struct {
entries: std.ArrayListUnmanaged(Entry) = .{}, entries: std.ArrayListUnmanaged(Entry) = .{},
output_symtab_ctx: Elf.SymtabCtx = .{}, output_symtab_ctx: Elf.SymtabCtx = .{},

View File

@ -733,7 +733,6 @@ test "extern variable with non-pointer opaque type" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
@export(var_to_export, .{ .name = "opaque_extern_var" }); @export(var_to_export, .{ .name = "opaque_extern_var" });
try expect(@as(*align(1) u32, @ptrCast(&opaque_extern_var)).* == 42); try expect(@as(*align(1) u32, @ptrCast(&opaque_extern_var)).* == 42);

View File

@ -48,7 +48,6 @@ test "exporting using field access" {
test "exporting comptime-known value" { test "exporting comptime-known value" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and if (builtin.zig_backend == .stage2_x86_64 and
(builtin.target.ofmt != .elf and (builtin.target.ofmt != .elf and
builtin.target.ofmt != .macho and builtin.target.ofmt != .macho and
@ -68,7 +67,6 @@ test "exporting comptime-known value" {
test "exporting comptime var" { test "exporting comptime var" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and if (builtin.zig_backend == .stage2_x86_64 and
(builtin.target.ofmt != .elf and (builtin.target.ofmt != .elf and
builtin.target.ofmt != .macho and builtin.target.ofmt != .macho and

View File

@ -7,7 +7,6 @@ test "anyopaque extern symbol" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const a = @extern(*anyopaque, .{ .name = "a_mystery_symbol" }); const a = @extern(*anyopaque, .{ .name = "a_mystery_symbol" });
const b: *i32 = @alignCast(@ptrCast(a)); const b: *i32 = @alignCast(@ptrCast(a));

View File

@ -429,7 +429,6 @@ test "implicit cast function to function ptr" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
const S1 = struct { const S1 = struct {
export fn someFunctionThatReturnsAValue() c_int { export fn someFunctionThatReturnsAValue() c_int {

View File

@ -45,7 +45,6 @@ test "pointer-integer arithmetic" {
test "pointer subtraction" { test "pointer subtraction" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
{ {
const a: *u8 = @ptrFromInt(100); const a: *u8 = @ptrFromInt(100);

View File

@ -1773,25 +1773,41 @@ fn testImportingDataDynamic(b: *Build, opts: Options) *Step {
.use_llvm = true, .use_llvm = true,
}, .{ }, .{
.name = "a", .name = "a",
.c_source_bytes = "int foo = 42;", .c_source_bytes =
\\#include <stdio.h>
\\int foo = 42;
\\void printFoo() { fprintf(stderr, "lib foo=%d\n", foo); }
,
}); });
dso.linkLibC();
const main = addExecutable(b, opts, .{ const main = addExecutable(b, opts, .{
.name = "main", .name = "main",
.zig_source_bytes = .zig_source_bytes =
\\const std = @import("std");
\\extern var foo: i32; \\extern var foo: i32;
\\extern fn printFoo() void;
\\pub fn main() void { \\pub fn main() void {
\\ @import("std").debug.print("{d}\n", .{foo}); \\ std.debug.print("exe foo={d}\n", .{foo});
\\ printFoo();
\\ foo += 1;
\\ std.debug.print("exe foo={d}\n", .{foo});
\\ printFoo();
\\} \\}
, ,
.strip = true, // TODO temp hack .strip = true, // TODO temp hack
}); });
main.pie = true; main.pie = true;
main.linkLibrary(dso); main.linkLibrary(dso);
main.linkLibC();
const run = addRunArtifact(main); const run = addRunArtifact(main);
run.expectStdErrEqual("42\n"); run.expectStdErrEqual(
\\exe foo=42
\\lib foo=42
\\exe foo=43
\\lib foo=43
\\
);
test_step.dependOn(&run.step); test_step.dependOn(&run.step);
return test_step; return test_step;