mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
Merge pull request #21065 from ziglang/elf-zig-got
elf: replace .got.zig with a zig jump table
This commit is contained in:
commit
90989be0e3
@ -218,7 +218,7 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
|
||||
|
||||
if (opts.is_test) {
|
||||
try buffer.appendSlice(
|
||||
\\pub var test_functions: []const std.builtin.TestFn = undefined; // overwritten later
|
||||
\\pub var test_functions: []const std.builtin.TestFn = &.{}; // overwritten later
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
@ -3092,6 +3092,10 @@ pub fn totalErrorCount(comp: *Compilation) u32 {
|
||||
if (zcu.intern_pool.global_error_set.getNamesFromMainThread().len > zcu.error_limit) {
|
||||
total += 1;
|
||||
}
|
||||
|
||||
for (zcu.failed_codegen.keys()) |_| {
|
||||
total += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 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| {
|
||||
try addModuleErrorMsg(zcu, &bundle, value.*, &all_references);
|
||||
}
|
||||
|
||||
@ -4352,24 +4352,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
|
||||
// on linking.
|
||||
if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) {
|
||||
.func => |func| {
|
||||
if (self.bin_file.cast(.elf)) |elf_file| {
|
||||
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 = @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,
|
||||
// },
|
||||
// });
|
||||
if (self.bin_file.cast(.elf)) |_| {
|
||||
return self.fail("TODO implement calling functions for Elf", .{});
|
||||
} else if (self.bin_file.cast(.macho)) |_| {
|
||||
return self.fail("TODO implement calling functions for MachO", .{});
|
||||
} else if (self.bin_file.cast(.coff)) |coff_file| {
|
||||
const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav);
|
||||
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"| {
|
||||
const nav_name = ip.getNav(@"extern".owner_nav).name.toSlice(ip);
|
||||
const lib_name = @"extern".lib_name.toSlice(ip);
|
||||
if (self.bin_file.cast(.macho)) |macho_file| {
|
||||
_ = macho_file;
|
||||
@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,
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
if (self.bin_file.cast(.macho)) |_| {
|
||||
return self.fail("TODO implement calling extern functions for MachO", .{});
|
||||
} else if (self.bin_file.cast(.coff)) |coff_file| {
|
||||
const sym_index = try coff_file.getGlobalSymbol(nav_name, lib_name);
|
||||
try self.genSetReg(Type.u64, .x30, .{
|
||||
@ -6234,7 +6207,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
|
||||
.memory => |addr| .{ .memory = addr },
|
||||
.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_symbol, .load_tlv => unreachable, // TODO
|
||||
.load_symbol, .load_tlv, .lea_symbol => unreachable, // TODO
|
||||
},
|
||||
.fail => |msg| {
|
||||
self.err_msg = msg;
|
||||
|
||||
@ -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
|
||||
// on linking.
|
||||
if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) {
|
||||
.func => |func| {
|
||||
if (self.bin_file.cast(.elf)) |elf_file| {
|
||||
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),
|
||||
});
|
||||
}
|
||||
.func => {
|
||||
return self.fail("TODO implement calling functions", .{});
|
||||
},
|
||||
.@"extern" => {
|
||||
return self.fail("TODO implement calling extern functions", .{});
|
||||
@ -6184,7 +6170,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
|
||||
.mcv => |mcv| switch (mcv) {
|
||||
.none => .none,
|
||||
.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) },
|
||||
.memory => |addr| .{ .memory = addr },
|
||||
},
|
||||
|
||||
@ -4937,7 +4937,7 @@ fn genCall(
|
||||
if (func.mod.pic) {
|
||||
return func.fail("TODO: genCall pic", .{});
|
||||
} 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(.{
|
||||
.tag = .jalr,
|
||||
.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
|
||||
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);
|
||||
break :blk .{ .sym = sym_off };
|
||||
} 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,
|
||||
src_mcv,
|
||||
),
|
||||
.load_tlv => {
|
||||
.load_symbol, .load_tlv => {
|
||||
const addr_reg, const addr_lock = try func.allocReg(.int);
|
||||
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));
|
||||
}
|
||||
},
|
||||
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,
|
||||
.data = .{
|
||||
.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,
|
||||
.rs2 = .zero,
|
||||
},
|
||||
@ -8026,6 +8026,7 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {
|
||||
.mcv => |mcv| switch (mcv) {
|
||||
.none => .none,
|
||||
.undef => unreachable,
|
||||
.lea_symbol => |sym_index| .{ .lea_symbol = .{ .sym = sym_index } },
|
||||
.load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } },
|
||||
.load_tlv => |sym_index| .{ .lea_tlv = sym_index },
|
||||
.immediate => |imm| .{ .immediate = imm },
|
||||
|
||||
@ -43,31 +43,19 @@ pub fn emitMir(emit: *Emit) Error!void {
|
||||
.fmt = std.meta.activeTag(lowered_inst),
|
||||
}),
|
||||
.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 zo = elf_file.zigObjectPtr().?;
|
||||
|
||||
const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
|
||||
const sym = zo.symbol(symbol.sym_index);
|
||||
|
||||
var hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20);
|
||||
var lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I);
|
||||
|
||||
if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {
|
||||
_ = try sym.getOrCreateZigGotEntry(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
|
||||
if (sym.flags.is_extern_ptr and emit.lower.pic) {
|
||||
return emit.fail("emit GOT relocation for symbol '{s}'", .{sym.name(elf_file)});
|
||||
}
|
||||
|
||||
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, .{
|
||||
.r_offset = start_offset,
|
||||
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type,
|
||||
|
||||
@ -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
|
||||
// on linking.
|
||||
if (try self.air.value(callee, pt)) |func_value| switch (ip.indexToKey(func_value.toIntern())) {
|
||||
.func => |func| {
|
||||
const got_addr = if (self.bin_file.cast(.elf)) |elf_file| blk: {
|
||||
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 = {} },
|
||||
});
|
||||
.func => {
|
||||
return self.fail("TODO implement calling functions", .{});
|
||||
},
|
||||
.@"extern" => {
|
||||
return self.fail("TODO implement calling extern functions", .{});
|
||||
@ -4153,7 +4127,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
|
||||
.mcv => |mcv| switch (mcv) {
|
||||
.none => .none,
|
||||
.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 },
|
||||
.memory => |addr| .{ .memory = addr },
|
||||
},
|
||||
|
||||
@ -1379,14 +1379,22 @@ fn asmImmediate(self: *Self, tag: Mir.Inst.FixedTag, imm: Immediate) !void {
|
||||
.ops = switch (imm) {
|
||||
.signed => .i_s,
|
||||
.unsigned => .i_u,
|
||||
.reloc => .rel,
|
||||
},
|
||||
.data = .{ .i = .{
|
||||
.data = switch (imm) {
|
||||
.reloc => |x| reloc: {
|
||||
assert(tag[0] == ._);
|
||||
break :reloc .{ .reloc = x };
|
||||
},
|
||||
.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) {
|
||||
.signed => .ri_s,
|
||||
.unsigned => |u| if (math.cast(u32, u)) |_| .ri_u else .ri64,
|
||||
.reloc => unreachable,
|
||||
};
|
||||
_ = try self.addInst(.{
|
||||
.tag = tag[1],
|
||||
@ -1417,6 +1426,7 @@ fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.FixedTag, reg: Register, imm:
|
||||
.i = switch (imm) {
|
||||
.signed => |s| @bitCast(s),
|
||||
.unsigned => |u| @intCast(u),
|
||||
.reloc => unreachable,
|
||||
},
|
||||
} },
|
||||
.ri64 => .{ .rx = .{
|
||||
@ -1488,6 +1498,7 @@ fn asmRegisterRegisterRegisterImmediate(
|
||||
.i = switch (imm) {
|
||||
.signed => |s| @bitCast(@as(i8, @intCast(s))),
|
||||
.unsigned => |u| @intCast(u),
|
||||
.reloc => unreachable,
|
||||
},
|
||||
} },
|
||||
});
|
||||
@ -1505,6 +1516,7 @@ fn asmRegisterRegisterImmediate(
|
||||
.ops = switch (imm) {
|
||||
.signed => .rri_s,
|
||||
.unsigned => .rri_u,
|
||||
.reloc => unreachable,
|
||||
},
|
||||
.data = .{ .rri = .{
|
||||
.fixes = tag[0],
|
||||
@ -1513,6 +1525,7 @@ fn asmRegisterRegisterImmediate(
|
||||
.i = switch (imm) {
|
||||
.signed => |s| @bitCast(s),
|
||||
.unsigned => |u| @intCast(u),
|
||||
.reloc => unreachable,
|
||||
},
|
||||
} },
|
||||
});
|
||||
@ -1610,6 +1623,7 @@ fn asmRegisterMemoryImmediate(
|
||||
if (switch (imm) {
|
||||
.signed => |s| if (math.cast(i16, s)) |x| @as(u16, @bitCast(x)) else null,
|
||||
.unsigned => |u| math.cast(u16, u),
|
||||
.reloc => unreachable,
|
||||
}) |small_imm| {
|
||||
_ = try self.addInst(.{
|
||||
.tag = tag[1],
|
||||
@ -1625,6 +1639,7 @@ fn asmRegisterMemoryImmediate(
|
||||
const payload = try self.addExtra(Mir.Imm32{ .imm = switch (imm) {
|
||||
.signed => |s| @bitCast(s),
|
||||
.unsigned => unreachable,
|
||||
.reloc => unreachable,
|
||||
} });
|
||||
assert(payload + 1 == try self.addExtra(Mir.Memory.encode(m)));
|
||||
_ = try self.addInst(.{
|
||||
@ -1632,6 +1647,7 @@ fn asmRegisterMemoryImmediate(
|
||||
.ops = switch (imm) {
|
||||
.signed => .rmi_s,
|
||||
.unsigned => .rmi_u,
|
||||
.reloc => unreachable,
|
||||
},
|
||||
.data = .{ .rx = .{
|
||||
.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) {
|
||||
.signed => |s| @bitCast(s),
|
||||
.unsigned => |u| @intCast(u),
|
||||
.reloc => unreachable,
|
||||
} });
|
||||
assert(payload + 1 == try self.addExtra(Mir.Memory.encode(m)));
|
||||
_ = try self.addInst(.{
|
||||
@ -1686,6 +1703,7 @@ fn asmMemoryImmediate(self: *Self, tag: Mir.Inst.FixedTag, m: Memory, imm: Immed
|
||||
.ops = switch (imm) {
|
||||
.signed => .mi_s,
|
||||
.unsigned => .mi_u,
|
||||
.reloc => unreachable,
|
||||
},
|
||||
.data = .{ .x = .{
|
||||
.fixes = tag[0],
|
||||
@ -12310,33 +12328,10 @@ fn genCall(self: *Self, info: union(enum) {
|
||||
if (self.bin_file.cast(.elf)) |elf_file| {
|
||||
const zo = elf_file.zigObjectPtr().?;
|
||||
const sym_index = try zo.getOrCreateMetadataForNav(elf_file, func.owner_nav);
|
||||
if (self.mod.pic) {
|
||||
const callee_reg: Register = switch (resolved_cc) {
|
||||
.SysV => callee: {
|
||||
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 = .{
|
||||
try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{
|
||||
.atom_index = try self.owner.getSymbolIndex(self),
|
||||
.sym_index = sym_index,
|
||||
} },
|
||||
.mod = .{ .rm = .{ .size = .qword } },
|
||||
});
|
||||
}));
|
||||
} else if (self.bin_file.cast(.coff)) |coff_file| {
|
||||
const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav);
|
||||
const sym_index = coff_file.getAtom(atom).getSymbolIndex().?;
|
||||
@ -12365,7 +12360,16 @@ fn genCall(self: *Self, info: union(enum) {
|
||||
});
|
||||
} 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,
|
||||
@"extern".lib_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.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;
|
||||
}
|
||||
@ -14096,8 +14106,8 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
.{ .reg = try self.copyToTmpRegister(Type.usize, .{ .lea_got = sym_index }) }
|
||||
else
|
||||
return self.fail("invalid modifier: '{s}'", .{modifier}),
|
||||
.load_symbol => |sym_off| if (mem.eql(u8, modifier, "P"))
|
||||
.{ .reg = try self.copyToTmpRegister(Type.usize, .{ .load_symbol = sym_off }) }
|
||||
.lea_symbol => |sym_off| if (mem.eql(u8, modifier, "P"))
|
||||
.{ .reg = try self.copyToTmpRegister(Type.usize, .{ .lea_symbol = sym_off }) }
|
||||
else
|
||||
return self.fail("invalid modifier: '{s}'", .{modifier}),
|
||||
else => return self.fail("invalid constraint: '{s}'", .{op_str}),
|
||||
@ -15253,16 +15263,7 @@ fn genExternSymbolRef(
|
||||
callee: []const u8,
|
||||
) InnerError!void {
|
||||
const atom_index = try self.owner.getSymbolIndex(self);
|
||||
if (self.bin_file.cast(.elf)) |elf_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| {
|
||||
if (self.bin_file.cast(.coff)) |coff_file| {
|
||||
const global_index = try coff_file.getGlobalSymbol(callee, lib);
|
||||
_ = try self.addInst(.{
|
||||
.tag = .mov,
|
||||
@ -15306,7 +15307,7 @@ fn genLazySymbolRef(
|
||||
if (self.mod.pic) {
|
||||
switch (tag) {
|
||||
.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, .{
|
||||
.load_symbol = .{ .sym = sym_index },
|
||||
@ -15324,14 +15325,11 @@ fn genLazySymbolRef(
|
||||
.sym_index = sym_index,
|
||||
};
|
||||
switch (tag) {
|
||||
.lea, .mov => try self.asmRegisterMemory(.{ ._, .mov }, reg.to64(), .{
|
||||
.base = .{ .reloc = reloc },
|
||||
.mod = .{ .rm = .{ .size = .qword } },
|
||||
}),
|
||||
.call => try self.asmMemory(.{ ._, .call }, .{
|
||||
.lea, .mov => try self.asmRegisterMemory(.{ ._, tag }, reg.to64(), .{
|
||||
.base = .{ .reloc = reloc },
|
||||
.mod = .{ .rm = .{ .size = .qword } },
|
||||
}),
|
||||
.call => try self.asmImmediate(.{ ._, .call }, Immediate.rel(reloc)),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
@ -18790,6 +18788,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
|
||||
.immediate => |imm| .{ .immediate = imm },
|
||||
.memory => |addr| .{ .memory = addr },
|
||||
.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_got => |sym_index| .{ .lea_got = sym_index },
|
||||
.load_tlv => |sym_index| .{ .lea_tlv = sym_index },
|
||||
|
||||
@ -8,7 +8,7 @@ const bits = @import("bits.zig");
|
||||
const encoder = @import("encoder.zig");
|
||||
|
||||
const Encoding = @import("Encoding.zig");
|
||||
const Immediate = bits.Immediate;
|
||||
const Immediate = Instruction.Immediate;
|
||||
const Instruction = encoder.Instruction;
|
||||
const LegacyPrefixes = encoder.LegacyPrefixes;
|
||||
const Memory = Instruction.Memory;
|
||||
|
||||
@ -110,21 +110,11 @@ pub fn emitMir(emit: *Emit) Error!void {
|
||||
});
|
||||
},
|
||||
.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 atom = zo.symbol(data.atom_index).atom(elf_file).?;
|
||||
const sym = zo.symbol(data.sym_index);
|
||||
if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {
|
||||
_ = try sym.getOrCreateZigGotEntry(data.sym_index, elf_file);
|
||||
}
|
||||
if (emit.lower.pic) {
|
||||
const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib)
|
||||
link.File.Elf.R_ZIG_GOTPCREL
|
||||
else if (sym.flags.needs_got)
|
||||
const r_type: u32 = if (sym.flags.is_extern_ptr)
|
||||
@intFromEnum(std.elf.R_X86_64.GOTPCREL)
|
||||
else
|
||||
@intFromEnum(std.elf.R_X86_64.PC32);
|
||||
@ -134,19 +124,7 @@ pub fn emitMir(emit: *Emit) Error!void {
|
||||
.r_addend = -4,
|
||||
});
|
||||
} else {
|
||||
if (lowered_inst.encoding.mnemonic == .call and sym.flags.needs_zig_got and is_obj_or_static_lib) {
|
||||
const r_type = @intFromEnum(std.elf.R_X86_64.PC32);
|
||||
try atom.addReloc(elf_file, .{
|
||||
.r_offset = end_offset - 4,
|
||||
.r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type,
|
||||
.r_addend = -4,
|
||||
});
|
||||
} else {
|
||||
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)
|
||||
const r_type: u32 = if (sym.flags.is_tls)
|
||||
@intFromEnum(std.elf.R_X86_64.TPOFF32)
|
||||
else
|
||||
@intFromEnum(std.elf.R_X86_64.@"32");
|
||||
@ -156,7 +134,6 @@ pub fn emitMir(emit: *Emit) Error!void {
|
||||
.r_addend = 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (emit.lower.bin_file.cast(.macho)) |macho_file| {
|
||||
const is_obj_or_static_lib = switch (emit.lower.output_mode) {
|
||||
.Exe => false,
|
||||
|
||||
@ -397,33 +397,40 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
|
||||
}
|
||||
|
||||
_ = lower.reloc(.{ .linker_reloc = sym });
|
||||
break :op if (lower.pic) switch (mnemonic) {
|
||||
if (lower.pic) switch (mnemonic) {
|
||||
.lea => {
|
||||
if (elf_sym.flags.is_extern_ptr) emit_mnemonic = .mov;
|
||||
break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
|
||||
},
|
||||
.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) };
|
||||
},
|
||||
else => unreachable,
|
||||
} else switch (mnemonic) {
|
||||
.call => break :op if (is_obj_or_static_lib and elf_sym.flags.needs_zig_got) .{
|
||||
.imm = Immediate.s(0),
|
||||
} else .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
|
||||
.call => break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
|
||||
.base = .{ .reg = .ds },
|
||||
}) },
|
||||
.lea => {
|
||||
emit_mnemonic = .mov;
|
||||
break :op .{ .imm = Immediate.s(0) };
|
||||
},
|
||||
.mov => {
|
||||
if (is_obj_or_static_lib and elf_sym.flags.needs_zig_got) emit_mnemonic = .lea;
|
||||
break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
|
||||
.mov => break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
|
||||
.base = .{ .reg = .ds },
|
||||
}) };
|
||||
},
|
||||
}) },
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
} else if (lower.bin_file.cast(.macho)) |macho_file| {
|
||||
const zo = macho_file.getZigObject().?;
|
||||
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,
|
||||
.mi_u, .mi_s => 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)}),
|
||||
};
|
||||
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) },
|
||||
.{ .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 }) },
|
||||
},
|
||||
.got_reloc, .direct_reloc, .import_reloc => ops: {
|
||||
@ -660,7 +667,7 @@ const std = @import("std");
|
||||
const Air = @import("../../Air.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ErrorMsg = Zcu.ErrorMsg;
|
||||
const Immediate = bits.Immediate;
|
||||
const Immediate = Instruction.Immediate;
|
||||
const Instruction = encoder.Instruction;
|
||||
const Lower = @This();
|
||||
const Memory = Instruction.Memory;
|
||||
|
||||
@ -769,7 +769,7 @@ pub const Inst = struct {
|
||||
/// Uses `imm` payload.
|
||||
i_u,
|
||||
/// Relative displacement operand.
|
||||
/// Uses `imm` payload.
|
||||
/// Uses `reloc` payload.
|
||||
rel,
|
||||
/// Register, memory operands.
|
||||
/// Uses `rx` payload with extra data of type `Memory`.
|
||||
|
||||
@ -569,6 +569,7 @@ pub const Memory = struct {
|
||||
pub const Immediate = union(enum) {
|
||||
signed: i32,
|
||||
unsigned: u64,
|
||||
reloc: Symbol,
|
||||
|
||||
pub fn u(x: u64) Immediate {
|
||||
return .{ .unsigned = x };
|
||||
@ -578,39 +579,19 @@ pub const Immediate = union(enum) {
|
||||
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 rel(symbol: Symbol) Immediate {
|
||||
return .{ .reloc = symbol };
|
||||
}
|
||||
|
||||
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 fn format(
|
||||
imm: Immediate,
|
||||
comptime fmt: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) @TypeOf(writer).Error!void {
|
||||
switch (imm) {
|
||||
.reloc => |x| try std.fmt.formatType(x, fmt, options, writer, 0),
|
||||
inline else => |x| try writer.print("{d}", .{x}),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -7,7 +7,6 @@ const testing = std.testing;
|
||||
const bits = @import("bits.zig");
|
||||
const Encoding = @import("Encoding.zig");
|
||||
const FrameIndex = bits.FrameIndex;
|
||||
const Immediate = bits.Immediate;
|
||||
const Register = bits.Register;
|
||||
const Symbol = bits.Symbol;
|
||||
|
||||
@ -28,6 +27,55 @@ pub const Instruction = struct {
|
||||
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) {
|
||||
sib: Sib,
|
||||
rip: Rip,
|
||||
@ -1119,7 +1167,7 @@ test "encode" {
|
||||
|
||||
const inst = try Instruction.new(.none, .mov, &.{
|
||||
.{ .reg = .rbx },
|
||||
.{ .imm = Immediate.u(4) },
|
||||
.{ .imm = Instruction.Immediate.u(4) },
|
||||
});
|
||||
try inst.encode(buf.writer(), .{});
|
||||
try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items);
|
||||
@ -1129,47 +1177,47 @@ test "lower I encoding" {
|
||||
var enc = TestEncode{};
|
||||
|
||||
try enc.encode(.push, &.{
|
||||
.{ .imm = Immediate.u(0x10) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x6A\x10", enc.code(), "push 0x10");
|
||||
|
||||
try enc.encode(.push, &.{
|
||||
.{ .imm = Immediate.u(0x1000) },
|
||||
.{ .imm = Instruction.Immediate.u(0x1000) },
|
||||
});
|
||||
try expectEqualHexStrings("\x66\x68\x00\x10", enc.code(), "push 0x1000");
|
||||
|
||||
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 enc.encode(.adc, &.{
|
||||
.{ .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 enc.encode(.add, &.{
|
||||
.{ .reg = .al },
|
||||
.{ .imm = Immediate.u(0x10) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x04\x10", enc.code(), "add al, 0x10");
|
||||
|
||||
try enc.encode(.add, &.{
|
||||
.{ .reg = .rax },
|
||||
.{ .imm = Immediate.u(0x10) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
|
||||
|
||||
try enc.encode(.sbb, &.{
|
||||
.{ .reg = .ax },
|
||||
.{ .imm = Immediate.u(0x10) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x66\x1D\x10\x00", enc.code(), "sbb ax, 0x10");
|
||||
|
||||
try enc.encode(.xor, &.{
|
||||
.{ .reg = .al },
|
||||
.{ .imm = Immediate.u(0x10) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x34\x10", enc.code(), "xor al, 0x10");
|
||||
}
|
||||
@ -1179,43 +1227,43 @@ test "lower MI encoding" {
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .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 enc.encode(.mov, &.{
|
||||
.{ .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 enc.encode(.mov, &.{
|
||||
.{ .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 enc.encode(.mov, &.{
|
||||
.{ .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 enc.encode(.mov, &.{
|
||||
.{ .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 enc.encode(.mov, &.{
|
||||
.{ .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 enc.encode(.mov, &.{
|
||||
.{ .mem = Instruction.Memory.rip(.qword, 0x10) },
|
||||
.{ .imm = Immediate.u(0x10) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
"\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00",
|
||||
@ -1225,19 +1273,19 @@ test "lower MI encoding" {
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .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 enc.encode(.mov, &.{
|
||||
.{ .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 enc.encode(.mov, &.{
|
||||
.{ .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");
|
||||
|
||||
@ -1247,7 +1295,7 @@ test "lower MI encoding" {
|
||||
.disp = 0x10000000,
|
||||
.scale_index = .{ .scale = 2, .index = .rcx },
|
||||
}) },
|
||||
.{ .imm = Immediate.u(0x10) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
"\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00",
|
||||
@ -1257,43 +1305,43 @@ test "lower MI encoding" {
|
||||
|
||||
try enc.encode(.adc, &.{
|
||||
.{ .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 enc.encode(.adc, &.{
|
||||
.{ .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 enc.encode(.adc, &.{
|
||||
.{ .reg = .rax },
|
||||
.{ .imm = Immediate.u(0x10) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10");
|
||||
|
||||
try enc.encode(.add, &.{
|
||||
.{ .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 enc.encode(.add, &.{
|
||||
.{ .reg = .rax },
|
||||
.{ .imm = Immediate.u(0x10) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
|
||||
|
||||
try enc.encode(.add, &.{
|
||||
.{ .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 enc.encode(.@"and", &.{
|
||||
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
|
||||
.{ .imm = Immediate.u(0x10) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
"\x83\x24\x25\x00\x00\x00\x10\x10",
|
||||
@ -1303,7 +1351,7 @@ test "lower MI encoding" {
|
||||
|
||||
try enc.encode(.@"and", &.{
|
||||
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) },
|
||||
.{ .imm = Immediate.u(0x10) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
"\x26\x83\x24\x25\x00\x00\x00\x10\x10",
|
||||
@ -1313,7 +1361,7 @@ test "lower MI encoding" {
|
||||
|
||||
try enc.encode(.@"and", &.{
|
||||
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
|
||||
.{ .imm = Immediate.u(0x10) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
"\x41\x83\xA4\x24\x00\x00\x00\x10\x10",
|
||||
@ -1323,7 +1371,7 @@ test "lower MI encoding" {
|
||||
|
||||
try enc.encode(.sub, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
|
||||
.{ .imm = Immediate.u(0x10) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
"\x41\x83\xAB\x00\x00\x00\x10\x10",
|
||||
@ -1542,14 +1590,14 @@ test "lower RMI encoding" {
|
||||
try enc.encode(.imul, &.{
|
||||
.{ .reg = .r11 },
|
||||
.{ .reg = .r12 },
|
||||
.{ .imm = Immediate.s(-2) },
|
||||
.{ .imm = Instruction.Immediate.s(-2) },
|
||||
});
|
||||
try expectEqualHexStrings("\x4D\x6B\xDC\xFE", enc.code(), "imul r11, r12, -2");
|
||||
|
||||
try enc.encode(.imul, &.{
|
||||
.{ .reg = .r11 },
|
||||
.{ .mem = Instruction.Memory.rip(.qword, -16) },
|
||||
.{ .imm = Immediate.s(-1024) },
|
||||
.{ .imm = Instruction.Immediate.s(-1024) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
"\x4C\x69\x1D\xF0\xFF\xFF\xFF\x00\xFC\xFF\xFF",
|
||||
@ -1560,7 +1608,7 @@ test "lower RMI encoding" {
|
||||
try enc.encode(.imul, &.{
|
||||
.{ .reg = .bx },
|
||||
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
|
||||
.{ .imm = Immediate.s(-1024) },
|
||||
.{ .imm = Instruction.Immediate.s(-1024) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
"\x66\x69\x5D\xF0\x00\xFC",
|
||||
@ -1571,7 +1619,7 @@ test "lower RMI encoding" {
|
||||
try enc.encode(.imul, &.{
|
||||
.{ .reg = .bx },
|
||||
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
|
||||
.{ .imm = Immediate.u(1024) },
|
||||
.{ .imm = Instruction.Immediate.u(1024) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
"\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 enc.encode(.call, &.{
|
||||
.{ .imm = Immediate.s(0) },
|
||||
.{ .imm = Instruction.Immediate.s(0) },
|
||||
});
|
||||
try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0");
|
||||
|
||||
@ -1746,7 +1794,7 @@ test "lower OI encoding" {
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .reg = .rax },
|
||||
.{ .imm = Immediate.u(0x1000000000000000) },
|
||||
.{ .imm = Instruction.Immediate.u(0x1000000000000000) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
"\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10",
|
||||
@ -1756,7 +1804,7 @@ test "lower OI encoding" {
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .reg = .r11 },
|
||||
.{ .imm = Immediate.u(0x1000000000000000) },
|
||||
.{ .imm = Instruction.Immediate.u(0x1000000000000000) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
"\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10",
|
||||
@ -1766,19 +1814,19 @@ test "lower OI encoding" {
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .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 enc.encode(.mov, &.{
|
||||
.{ .reg = .r11w },
|
||||
.{ .imm = Immediate.u(0x1000) },
|
||||
.{ .imm = Instruction.Immediate.u(0x1000) },
|
||||
});
|
||||
try expectEqualHexStrings("\x66\x41\xBB\x00\x10", enc.code(), "mov r11w, 0x1000");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .reg = .r11b },
|
||||
.{ .imm = Immediate.u(0x10) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x41\xB3\x10", enc.code(), "mov r11b, 0x10");
|
||||
}
|
||||
@ -1900,7 +1948,7 @@ test "invalid instruction" {
|
||||
.{ .reg = .r12d },
|
||||
});
|
||||
try invalidInstruction(.push, &.{
|
||||
.{ .imm = Immediate.u(0x1000000000000000) },
|
||||
.{ .imm = Instruction.Immediate.u(0x1000000000000000) },
|
||||
});
|
||||
}
|
||||
|
||||
@ -2213,7 +2261,7 @@ const Assembler = struct {
|
||||
.immediate => {
|
||||
const is_neg = if (as.expect(.minus)) |_| true else |_| false;
|
||||
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);
|
||||
break :blk .{ .signed = imm * -1 };
|
||||
} else .{ .unsigned = try std.fmt.parseInt(u64, as.source(imm_tok), 0) };
|
||||
|
||||
@ -828,6 +828,9 @@ pub const GenResult = union(enum) {
|
||||
/// Reference to memory location but deferred until linker allocated the Decl in memory.
|
||||
/// Traditionally, this corresponds to emitting a relocation in a relocatable object file.
|
||||
load_symbol: u32,
|
||||
/// 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 {
|
||||
@ -895,16 +898,15 @@ fn genNavRef(
|
||||
if (lf.cast(.elf)) |elf_file| {
|
||||
const zo = elf_file.zigObjectPtr().?;
|
||||
if (is_extern) {
|
||||
// TODO audit this
|
||||
const sym_index = try elf_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip));
|
||||
zo.symbol(sym_index).flags.needs_got = true;
|
||||
return GenResult.mcv(.{ .load_symbol = sym_index });
|
||||
zo.symbol(sym_index).flags.is_extern_ptr = true;
|
||||
return GenResult.mcv(.{ .lea_symbol = sym_index });
|
||||
}
|
||||
const sym_index = try zo.getOrCreateMetadataForNav(elf_file, nav_index);
|
||||
if (!single_threaded and is_threadlocal) {
|
||||
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| {
|
||||
const zo = macho_file.getZigObject().?;
|
||||
if (is_extern) {
|
||||
|
||||
115
src/link/Elf.zig
115
src/link/Elf.zig
@ -64,9 +64,6 @@ phdrs: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{},
|
||||
/// Tracked loadable segments during incremental linking.
|
||||
/// The index into the program headers of a PT_LOAD program header with Read and Execute flags
|
||||
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
|
||||
phdr_zig_load_ro_index: ?u16 = null,
|
||||
/// 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 = .{},
|
||||
/// .rela.plt section
|
||||
rela_plt: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{},
|
||||
/// .got.zig section
|
||||
zig_got: ZigGotSection = .{},
|
||||
/// SHT_GROUP sections
|
||||
/// Applies only to a relocatable.
|
||||
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_section_index: ?u32 = null,
|
||||
zig_bss_section_index: ?u32 = null,
|
||||
zig_got_section_index: ?u32 = null,
|
||||
|
||||
debug_info_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.rela_dyn.deinit(gpa);
|
||||
self.rela_plt.deinit(gpa);
|
||||
self.zig_got.deinit(gpa);
|
||||
self.comdat_group_sections.deinit(gpa);
|
||||
}
|
||||
|
||||
@ -611,28 +604,13 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void {
|
||||
.type = elf.PT_LOAD,
|
||||
.offset = off,
|
||||
.filesz = filesz,
|
||||
.addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000,
|
||||
.addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000,
|
||||
.memsz = filesz,
|
||||
.@"align" = self.page_size,
|
||||
.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) {
|
||||
const alignment = self.page_size;
|
||||
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.?, .{});
|
||||
}
|
||||
|
||||
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) {
|
||||
self.zig_data_rel_ro_section_index = try self.addSection(.{
|
||||
.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 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;
|
||||
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) {
|
||||
const existing_size = shdr.sh_size;
|
||||
@ -3156,8 +3118,8 @@ fn initSyntheticSections(self: *Elf) !void {
|
||||
});
|
||||
|
||||
const needs_rela_dyn = blk: {
|
||||
if (self.got.flags.needs_rela or self.got.flags.needs_tlsld or
|
||||
self.zig_got.flags.needs_rela or self.copy_rel.symbols.items.len > 0) break :blk true;
|
||||
if (self.got.flags.needs_rela or self.got.flags.needs_tlsld or self.copy_rel.symbols.items.len > 0)
|
||||
break :blk true;
|
||||
if (self.zigObjectPtr()) |zig_object| {
|
||||
if (zig_object.num_dynrelocs > 0) break :blk true;
|
||||
}
|
||||
@ -3562,7 +3524,6 @@ fn sortPhdrs(self: *Elf) error{OutOfMemory}!void {
|
||||
|
||||
for (&[_]*?u16{
|
||||
&self.phdr_zig_load_re_index,
|
||||
&self.phdr_zig_got_index,
|
||||
&self.phdr_zig_load_ro_index,
|
||||
&self.phdr_zig_load_zerofill_index,
|
||||
&self.phdr_table_index,
|
||||
@ -3694,7 +3655,6 @@ fn resetShdrIndexes(self: *Elf, backlinks: []const u32) !void {
|
||||
&self.versym_section_index,
|
||||
&self.verneed_section_index,
|
||||
&self.zig_text_section_index,
|
||||
&self.zig_got_section_index,
|
||||
&self.zig_data_rel_ro_section_index,
|
||||
&self.zig_data_section_index,
|
||||
&self.zig_bss_section_index,
|
||||
@ -3893,7 +3853,7 @@ fn updateSectionSizes(self: *Elf) !void {
|
||||
}
|
||||
|
||||
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| {
|
||||
num += zig_object.num_dynrelocs;
|
||||
}
|
||||
@ -4431,15 +4391,6 @@ pub fn updateSymtabSize(self: *Elf) !void {
|
||||
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) |_| {
|
||||
self.got.output_symtab_ctx.ilocal = nlocals + 1;
|
||||
self.got.updateSymtabSize(self);
|
||||
@ -4576,9 +4527,6 @@ fn writeSyntheticSections(self: *Elf) !void {
|
||||
const shdr = self.shdrs.items[shndx];
|
||||
try self.got.addRela(self);
|
||||
try self.copy_rel.addRela(self);
|
||||
if (self.zigObjectPtr()) |_| {
|
||||
try self.zig_got.addRela(self);
|
||||
}
|
||||
self.sortRelaDyn();
|
||||
try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_dyn.items), shdr.sh_offset);
|
||||
}
|
||||
@ -4674,10 +4622,6 @@ pub fn writeSymtab(self: *Elf) !void {
|
||||
obj.asFile().writeSymtab(self);
|
||||
}
|
||||
|
||||
if (self.zig_got_section_index) |_| {
|
||||
self.zig_got.writeSymtab(self);
|
||||
}
|
||||
|
||||
if (self.got_section_index) |_| {
|
||||
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_section_index,
|
||||
self.zig_bss_section_index,
|
||||
self.zig_got_section_index,
|
||||
}) |maybe_index| {
|
||||
if (maybe_index) |index| {
|
||||
if (index == shndx) return true;
|
||||
@ -5657,10 +5600,11 @@ fn fmtDumpState(
|
||||
|
||||
if (self.zigObjectPtr()) |zig_object| {
|
||||
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.fmtSymtab(self),
|
||||
});
|
||||
try writer.writeByte('\n');
|
||||
}
|
||||
|
||||
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.plt.fmt(self)});
|
||||
|
||||
@ -5991,41 +5934,6 @@ const RelaSection = struct {
|
||||
};
|
||||
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 {
|
||||
return switch (cpu_arch) {
|
||||
.mips, .mipsel, .mips64, .mips64el => "__start",
|
||||
@ -6095,6 +6003,5 @@ const StringTable = @import("StringTable.zig");
|
||||
const Thunk = thunks.Thunk;
|
||||
const Value = @import("../Value.zig");
|
||||
const VerneedSection = synthetic_sections.VerneedSection;
|
||||
const ZigGotSection = synthetic_sections.ZigGotSection;
|
||||
const ZigObject = @import("Elf/ZigObject.zig");
|
||||
const riscv = @import("riscv.zig");
|
||||
|
||||
@ -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));
|
||||
// Addend from the relocation.
|
||||
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);
|
||||
// Address of the global offset table.
|
||||
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.
|
||||
const G = target.gotAddress(elf_file) - GOT;
|
||||
// // 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.
|
||||
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),
|
||||
r_offset,
|
||||
P,
|
||||
S + A,
|
||||
G + GOT + A,
|
||||
ZIG_GOT + A,
|
||||
target.name(elf_file),
|
||||
});
|
||||
|
||||
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) {
|
||||
.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.
|
||||
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})", .{
|
||||
relocation.fmtRelocType(rel.r_type(), cpu_arch),
|
||||
@ -1025,7 +1022,7 @@ pub fn format(
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
_ = 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) {
|
||||
@ -1052,7 +1049,7 @@ fn format2(
|
||||
const elf_file = ctx.elf_file;
|
||||
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.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) {
|
||||
try writer.writeAll(" : fdes{ ");
|
||||
@ -1180,16 +1177,7 @@ const x86_64 = struct {
|
||||
.TLSDESC_CALL,
|
||||
=> {},
|
||||
|
||||
else => |x| switch (@intFromEnum(x)) {
|
||||
// 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 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) {
|
||||
.NONE => unreachable,
|
||||
@ -1224,9 +1212,8 @@ const x86_64 = struct {
|
||||
);
|
||||
},
|
||||
|
||||
.PLT32,
|
||||
.PC32,
|
||||
=> try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little),
|
||||
.PLT32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little),
|
||||
.PC32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + 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),
|
||||
@ -1327,15 +1314,9 @@ const x86_64 = struct {
|
||||
}
|
||||
},
|
||||
|
||||
.GOT32 => try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A)), .little),
|
||||
|
||||
else => |x| switch (@intFromEnum(x)) {
|
||||
// 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),
|
||||
.GOT32 => try cwriter.writeInt(i32, @as(i32, @intCast(G + A)), .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 cwriter = stream.writer();
|
||||
|
||||
_, const A, const S, const GOT, _, _, const DTP, _ = args;
|
||||
_, const A, const S, const GOT, _, _, const DTP = args;
|
||||
|
||||
switch (r_type) {
|
||||
.NONE => unreachable,
|
||||
@ -1629,7 +1610,7 @@ const x86_64 = struct {
|
||||
const bits = @import("../../arch/x86_64/bits.zig");
|
||||
const encoder = @import("../../arch/x86_64/encoder.zig");
|
||||
const Disassembler = @import("../../arch/x86_64/Disassembler.zig");
|
||||
const Immediate = bits.Immediate;
|
||||
const Immediate = Instruction.Immediate;
|
||||
const Instruction = encoder.Instruction;
|
||||
};
|
||||
|
||||
@ -1738,9 +1719,8 @@ const aarch64 = struct {
|
||||
const code = code_buffer[r_offset..][0..4];
|
||||
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;
|
||||
_ = ZIG_GOT;
|
||||
|
||||
switch (r_type) {
|
||||
.NONE => unreachable,
|
||||
@ -1942,7 +1922,7 @@ const aarch64 = struct {
|
||||
const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type());
|
||||
const cwriter = stream.writer();
|
||||
|
||||
_, const A, const S, _, _, _, _, _ = args;
|
||||
_, const A, const S, _, _, _, _ = args;
|
||||
|
||||
switch (r_type) {
|
||||
.NONE => unreachable,
|
||||
@ -1998,19 +1978,7 @@ const riscv = struct {
|
||||
.SUB32,
|
||||
=> {},
|
||||
|
||||
else => |x| switch (@intFromEnum(x)) {
|
||||
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 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;
|
||||
_ = DTP;
|
||||
|
||||
@ -2147,34 +2115,7 @@ const riscv = struct {
|
||||
// TODO: annotates an ADD instruction that can be removed when TPREL is relaxed
|
||||
},
|
||||
|
||||
else => |x| switch (@intFromEnum(x)) {
|
||||
// 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 cwriter = stream.writer();
|
||||
|
||||
_, const A, const S, const GOT, _, _, const DTP, _ = args;
|
||||
_, const A, const S, const GOT, _, _, const DTP = args;
|
||||
_ = GOT;
|
||||
_ = DTP;
|
||||
|
||||
@ -2230,7 +2171,7 @@ const riscv = struct {
|
||||
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{
|
||||
Overflow,
|
||||
|
||||
@ -101,7 +101,7 @@ pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 {
|
||||
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 (!msub.alive) return 0;
|
||||
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) {
|
||||
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.is_canonical and symbol.flags.has_got) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
const GetOrCreateZigGotEntryResult = struct {
|
||||
found_existing: bool,
|
||||
index: ZigGotSection.Index,
|
||||
};
|
||||
|
||||
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 trampolineAddress(symbol: Symbol, elf_file: *Elf) i64 {
|
||||
if (!symbol.flags.has_trampoline) return 0;
|
||||
const zo = elf_file.zigObjectPtr().?;
|
||||
const index = symbol.extra(elf_file).trampoline;
|
||||
return zo.symbol(index).address(.{}, elf_file);
|
||||
}
|
||||
|
||||
pub fn dsoAlignment(symbol: Symbol, elf_file: *Elf) !u64 {
|
||||
@ -259,7 +250,7 @@ const AddExtraOpts = struct {
|
||||
tlsgd: ?u32 = null,
|
||||
gottp: ?u32 = null,
|
||||
tlsdesc: ?u32 = null,
|
||||
zig_got: ?u32 = null,
|
||||
trampoline: ?u32 = null,
|
||||
};
|
||||
|
||||
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];
|
||||
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);
|
||||
break :blk symbol.address(.{ .plt = false, .trampoline = false }, elf_file);
|
||||
};
|
||||
out.st_info = (st_bind << 4) | st_type;
|
||||
out.st_other = esym.st_other;
|
||||
@ -331,7 +322,7 @@ pub fn format(
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
_ = writer;
|
||||
@compileError("do not format symbols directly");
|
||||
@compileError("do not format Symbol directly");
|
||||
}
|
||||
|
||||
const FormatContext = struct {
|
||||
@ -388,7 +379,7 @@ fn format2(
|
||||
try writer.print("%{d} : {s} : @{x}", .{
|
||||
symbol.esym_index,
|
||||
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.isAbs(elf_file)) {
|
||||
@ -456,17 +447,18 @@ pub const Flags = packed struct {
|
||||
needs_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.
|
||||
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 {
|
||||
@ -479,8 +471,8 @@ pub const Extra = struct {
|
||||
tlsgd: u32 = 0,
|
||||
gottp: u32 = 0,
|
||||
tlsdesc: u32 = 0,
|
||||
zig_got: u32 = 0,
|
||||
merge_section: u32 = 0,
|
||||
trampoline: u32 = 0,
|
||||
};
|
||||
|
||||
pub const Index = u32;
|
||||
|
||||
@ -102,23 +102,17 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
|
||||
}
|
||||
self.relocs.deinit(allocator);
|
||||
|
||||
{
|
||||
var it = self.navs.iterator();
|
||||
while (it.next()) |entry| {
|
||||
entry.value_ptr.exports.deinit(allocator);
|
||||
for (self.navs.values()) |*meta| {
|
||||
meta.exports.deinit(allocator);
|
||||
}
|
||||
self.navs.deinit(allocator);
|
||||
}
|
||||
|
||||
self.lazy_syms.deinit(allocator);
|
||||
|
||||
{
|
||||
var it = self.uavs.iterator();
|
||||
while (it.next()) |entry| {
|
||||
entry.value_ptr.exports.deinit(allocator);
|
||||
for (self.uavs.values()) |*meta| {
|
||||
meta.exports.deinit(allocator);
|
||||
}
|
||||
self.uavs.deinit(allocator);
|
||||
}
|
||||
|
||||
for (self.tls_variables.values()) |*tlv| {
|
||||
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 (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| {
|
||||
const pt: Zcu.PerThread = .{ .zcu = elf_file.base.comp.module.?, .tid = tid };
|
||||
try dw.flushModule(pt);
|
||||
@ -698,6 +702,7 @@ pub fn lowerUav(
|
||||
else => explicit_alignment,
|
||||
};
|
||||
if (self.uavs.get(uav)) |metadata| {
|
||||
assert(metadata.allocated);
|
||||
const sym = self.symbol(metadata.symbol_index);
|
||||
const existing_alignment = sym.atom(elf_file).?.alignment;
|
||||
if (uav_alignment.order(existing_alignment).compare(.lte))
|
||||
@ -729,7 +734,7 @@ pub fn lowerUav(
|
||||
.ok => |sym_index| sym_index,
|
||||
.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 } };
|
||||
}
|
||||
|
||||
@ -747,13 +752,7 @@ pub fn getOrCreateMetadataForLazySymbol(
|
||||
.const_data => .{ &gop.value_ptr.rodata_symbol_index, &gop.value_ptr.rodata_state },
|
||||
};
|
||||
switch (state_ptr.*) {
|
||||
.unused => {
|
||||
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;
|
||||
},
|
||||
.unused => symbol_index_ptr.* = try self.newSymbolWithAtom(pt.zcu.gpa, 0),
|
||||
.pending_flush => return symbol_index_ptr.*,
|
||||
.flushed => {},
|
||||
}
|
||||
@ -807,9 +806,6 @@ pub fn getOrCreateMetadataForNav(
|
||||
sym.flags.is_tls = true;
|
||||
}
|
||||
}
|
||||
if (!sym.flags.is_tls) {
|
||||
sym.flags.needs_zig_got = true;
|
||||
}
|
||||
gop.value_ptr.* = .{ .symbol_index = symbol_index };
|
||||
}
|
||||
return gop.value_ptr.symbol_index;
|
||||
@ -820,9 +816,9 @@ fn getNavShdrIndex(
|
||||
elf_file: *Elf,
|
||||
zcu: *Zcu,
|
||||
nav_index: InternPool.Nav.Index,
|
||||
sym_index: Symbol.Index,
|
||||
code: []const u8,
|
||||
) error{OutOfMemory}!u32 {
|
||||
_ = self;
|
||||
const ip = &zcu.intern_pool;
|
||||
const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded;
|
||||
const nav_val = zcu.navValue(nav_index);
|
||||
@ -832,10 +828,12 @@ fn getNavShdrIndex(
|
||||
.@"extern" => |@"extern"| .{ @"extern".is_const, @"extern".is_threadlocal, .none },
|
||||
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) {
|
||||
for (code) |byte| {
|
||||
if (byte != 0) break;
|
||||
} else return elf_file.sectionByName(".tbss") orelse try elf_file.addSection(.{
|
||||
const is_bss = !has_relocs and for (code) |byte| {
|
||||
if (byte != 0) break false;
|
||||
} else true;
|
||||
if (is_bss) return elf_file.sectionByName(".tbss") orelse try elf_file.addSection(.{
|
||||
.type = elf.SHT_NOBITS,
|
||||
.flags = elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_TLS,
|
||||
.name = try elf_file.insertShString(".tbss"),
|
||||
@ -854,9 +852,10 @@ fn getNavShdrIndex(
|
||||
.Debug, .ReleaseSafe => elf_file.zig_data_section_index.?,
|
||||
.ReleaseFast, .ReleaseSmall => elf_file.zig_bss_section_index.?,
|
||||
};
|
||||
for (code) |byte| {
|
||||
if (byte != 0) break;
|
||||
} else return elf_file.zig_bss_section_index.?;
|
||||
const is_bss = !has_relocs and for (code) |byte| {
|
||||
if (byte != 0) break false;
|
||||
} else true;
|
||||
if (is_bss) return elf_file.zig_bss_section_index.?;
|
||||
return elf_file.zig_data_section_index.?;
|
||||
}
|
||||
|
||||
@ -909,13 +908,6 @@ fn updateNavCode(
|
||||
if (old_vaddr != atom_ptr.value) {
|
||||
sym.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) {
|
||||
atom_ptr.shrink(elf_file);
|
||||
@ -925,14 +917,10 @@ fn updateNavCode(
|
||||
errdefer self.freeNavMetadata(elf_file, sym_index);
|
||||
|
||||
sym.value = 0;
|
||||
sym.flags.needs_zig_got = true;
|
||||
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| {
|
||||
switch (builtin.os.tag) {
|
||||
@ -959,6 +947,7 @@ fn updateNavCode(
|
||||
if (shdr.sh_type != elf.SHT_NOBITS) {
|
||||
const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value));
|
||||
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.size = code.len;
|
||||
|
||||
self.navs.getPtr(nav_index).?.allocated = true;
|
||||
|
||||
{
|
||||
const gop = try self.tls_variables.getOrPut(gpa, atom_ptr.atom_index);
|
||||
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);
|
||||
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| {
|
||||
const sym = self.symbol(sym_index);
|
||||
@ -1080,6 +1084,41 @@ pub fn updateFunc(
|
||||
}
|
||||
|
||||
// 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(
|
||||
@ -1102,13 +1141,12 @@ pub fn updateNav(
|
||||
.variable => |variable| Value.fromInterned(variable.init),
|
||||
.@"extern" => |@"extern"| {
|
||||
if (ip.isFunctionType(@"extern".ty)) return;
|
||||
// Extern variable gets a .got entry only.
|
||||
const sym_index = try self.getGlobalSymbol(
|
||||
elf_file,
|
||||
nav.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;
|
||||
},
|
||||
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)
|
||||
try self.updateTlv(elf_file, pt, nav_index, sym_index, shndx, code)
|
||||
else
|
||||
@ -1225,14 +1268,8 @@ fn updateLazySymbol(
|
||||
errdefer self.freeNavMetadata(elf_file, symbol_index);
|
||||
|
||||
local_sym.value = 0;
|
||||
local_sym.flags.needs_zig_got = true;
|
||||
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 file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value));
|
||||
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.*;
|
||||
}
|
||||
|
||||
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 {
|
||||
return .{ .zig_object = self };
|
||||
}
|
||||
@ -1662,6 +1745,8 @@ const AvMetadata = struct {
|
||||
symbol_index: Symbol.Index,
|
||||
/// A list of all exports aliases of this Av.
|
||||
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 {
|
||||
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 {
|
||||
symbol_index: Symbol.Index,
|
||||
code: []const u8 = &[0]u8{},
|
||||
@ -1682,12 +1793,26 @@ const TlsVariable = struct {
|
||||
};
|
||||
|
||||
const AtomList = std.ArrayListUnmanaged(Atom.Index);
|
||||
const NavTable = std.AutoHashMapUnmanaged(InternPool.Nav.Index, AvMetadata);
|
||||
const UavTable = std.AutoHashMapUnmanaged(InternPool.Index, AvMetadata);
|
||||
const NavTable = std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, AvMetadata);
|
||||
const UavTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, AvMetadata);
|
||||
const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, LazySymbolMetadata);
|
||||
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 build_options = @import("build_options");
|
||||
const builtin = @import("builtin");
|
||||
const codegen = @import("../../codegen.zig");
|
||||
const elf = std.elf;
|
||||
|
||||
@ -112,19 +112,11 @@ fn formatRelocType(
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
const r_type = ctx.r_type;
|
||||
switch (r_type) {
|
||||
Elf.R_ZIG_GOT32 => try writer.writeAll("R_ZIG_GOT32"),
|
||||
Elf.R_ZIG_GOTPCREL => try writer.writeAll("R_ZIG_GOTPCREL"),
|
||||
Elf.R_ZIG_GOT_HI20 => try writer.writeAll("R_ZIG_GOT_HI20"),
|
||||
Elf.R_ZIG_GOT_LO12 => try writer.writeAll("R_ZIG_GOT_LO12"),
|
||||
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) {
|
||||
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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
entries: std.ArrayListUnmanaged(Entry) = .{},
|
||||
output_symtab_ctx: Elf.SymtabCtx = .{},
|
||||
|
||||
@ -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_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_riscv64) return error.SkipZigTest;
|
||||
|
||||
@export(var_to_export, .{ .name = "opaque_extern_var" });
|
||||
try expect(@as(*align(1) u32, @ptrCast(&opaque_extern_var)).* == 42);
|
||||
|
||||
@ -48,7 +48,6 @@ test "exporting using field access" {
|
||||
test "exporting comptime-known value" {
|
||||
if (builtin.zig_backend == .stage2_wasm) 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
|
||||
(builtin.target.ofmt != .elf and
|
||||
builtin.target.ofmt != .macho and
|
||||
@ -68,7 +67,6 @@ test "exporting comptime-known value" {
|
||||
test "exporting comptime var" {
|
||||
if (builtin.zig_backend == .stage2_wasm) 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
|
||||
(builtin.target.ofmt != .elf and
|
||||
builtin.target.ofmt != .macho and
|
||||
|
||||
@ -7,7 +7,6 @@ test "anyopaque extern symbol" {
|
||||
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_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||
|
||||
const a = @extern(*anyopaque, .{ .name = "a_mystery_symbol" });
|
||||
const b: *i32 = @alignCast(@ptrCast(a));
|
||||
|
||||
@ -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_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_riscv64) return error.SkipZigTest;
|
||||
|
||||
const S1 = struct {
|
||||
export fn someFunctionThatReturnsAValue() c_int {
|
||||
|
||||
@ -45,7 +45,6 @@ test "pointer-integer arithmetic" {
|
||||
|
||||
test "pointer subtraction" {
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||
|
||||
{
|
||||
const a: *u8 = @ptrFromInt(100);
|
||||
|
||||
@ -1773,25 +1773,41 @@ fn testImportingDataDynamic(b: *Build, opts: Options) *Step {
|
||||
.use_llvm = true,
|
||||
}, .{
|
||||
.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, .{
|
||||
.name = "main",
|
||||
.zig_source_bytes =
|
||||
\\const std = @import("std");
|
||||
\\extern var foo: i32;
|
||||
\\extern fn printFoo() 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
|
||||
});
|
||||
main.pie = true;
|
||||
main.linkLibrary(dso);
|
||||
main.linkLibC();
|
||||
|
||||
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);
|
||||
|
||||
return test_step;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user