mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
compiler: avoid field/decl name conflicts
Most of the required renames here are net wins for readaibility, I'd say. The ones in `arch` are a little more verbose, but I think better. I didn't bother renaming the non-conflicting functions in `arch/arm/bits.zig` and `arch/aarch64/bits.zig`, since these backends are pretty bit-rotted anyway AIUI.
This commit is contained in:
parent
ba8d3f69ca
commit
c62487da76
@ -613,7 +613,7 @@ set(ZIG_STAGE2_SOURCES
|
||||
src/link/Elf/relocatable.zig
|
||||
src/link/Elf/relocation.zig
|
||||
src/link/Elf/synthetic_sections.zig
|
||||
src/link/Elf/thunks.zig
|
||||
src/link/Elf/Thunk.zig
|
||||
src/link/MachO.zig
|
||||
src/link/MachO/Archive.zig
|
||||
src/link/MachO/Atom.zig
|
||||
@ -638,7 +638,7 @@ set(ZIG_STAGE2_SOURCES
|
||||
src/link/MachO/load_commands.zig
|
||||
src/link/MachO/relocatable.zig
|
||||
src/link/MachO/synthetic.zig
|
||||
src/link/MachO/thunks.zig
|
||||
src/link/MachO/Thunk.zig
|
||||
src/link/MachO/uuid.zig
|
||||
src/link/NvPtx.zig
|
||||
src/link/Plan9.zig
|
||||
|
||||
@ -1069,7 +1069,7 @@ pub const Instruction = union(enum) {
|
||||
};
|
||||
}
|
||||
|
||||
fn bitfield(
|
||||
fn initBitfield(
|
||||
opc: u2,
|
||||
n: u1,
|
||||
rd: Register,
|
||||
@ -1579,7 +1579,7 @@ pub const Instruction = union(enum) {
|
||||
64 => 0b1,
|
||||
else => unreachable, // unexpected register size
|
||||
};
|
||||
return bitfield(0b00, n, rd, rn, immr, imms);
|
||||
return initBitfield(0b00, n, rd, rn, immr, imms);
|
||||
}
|
||||
|
||||
pub fn bfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction {
|
||||
@ -1588,7 +1588,7 @@ pub const Instruction = union(enum) {
|
||||
64 => 0b1,
|
||||
else => unreachable, // unexpected register size
|
||||
};
|
||||
return bitfield(0b01, n, rd, rn, immr, imms);
|
||||
return initBitfield(0b01, n, rd, rn, immr, imms);
|
||||
}
|
||||
|
||||
pub fn ubfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction {
|
||||
@ -1597,7 +1597,7 @@ pub const Instruction = union(enum) {
|
||||
64 => 0b1,
|
||||
else => unreachable, // unexpected register size
|
||||
};
|
||||
return bitfield(0b10, n, rd, rn, immr, imms);
|
||||
return initBitfield(0b10, n, rd, rn, immr, imms);
|
||||
}
|
||||
|
||||
pub fn asrImmediate(rd: Register, rn: Register, shift: u6) Instruction {
|
||||
|
||||
@ -662,7 +662,7 @@ pub const Instruction = union(enum) {
|
||||
};
|
||||
}
|
||||
|
||||
fn multiply(
|
||||
fn initMultiply(
|
||||
cond: Condition,
|
||||
set_cond: u1,
|
||||
rd: Register,
|
||||
@ -864,7 +864,7 @@ pub const Instruction = union(enum) {
|
||||
};
|
||||
}
|
||||
|
||||
fn branch(cond: Condition, offset: i26, link: u1) Instruction {
|
||||
fn initBranch(cond: Condition, offset: i26, link: u1) Instruction {
|
||||
return Instruction{
|
||||
.branch = .{
|
||||
.cond = @intFromEnum(cond),
|
||||
@ -900,7 +900,7 @@ pub const Instruction = union(enum) {
|
||||
};
|
||||
}
|
||||
|
||||
fn breakpoint(imm: u16) Instruction {
|
||||
fn initBreakpoint(imm: u16) Instruction {
|
||||
return Instruction{
|
||||
.breakpoint = .{
|
||||
.imm12 = @as(u12, @truncate(imm >> 4)),
|
||||
@ -1087,19 +1087,19 @@ pub const Instruction = union(enum) {
|
||||
// Multiply
|
||||
|
||||
pub fn mul(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
|
||||
return multiply(cond, 0, rd, rn, rm, null);
|
||||
return initMultiply(cond, 0, rd, rn, rm, null);
|
||||
}
|
||||
|
||||
pub fn muls(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction {
|
||||
return multiply(cond, 1, rd, rn, rm, null);
|
||||
return initMultiply(cond, 1, rd, rn, rm, null);
|
||||
}
|
||||
|
||||
pub fn mla(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
|
||||
return multiply(cond, 0, rd, rn, rm, ra);
|
||||
return initMultiply(cond, 0, rd, rn, rm, ra);
|
||||
}
|
||||
|
||||
pub fn mlas(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
|
||||
return multiply(cond, 1, rd, rn, rm, ra);
|
||||
return initMultiply(cond, 1, rd, rn, rm, ra);
|
||||
}
|
||||
|
||||
// Multiply long
|
||||
@ -1261,11 +1261,11 @@ pub const Instruction = union(enum) {
|
||||
// Branch
|
||||
|
||||
pub fn b(cond: Condition, offset: i26) Instruction {
|
||||
return branch(cond, offset, 0);
|
||||
return initBranch(cond, offset, 0);
|
||||
}
|
||||
|
||||
pub fn bl(cond: Condition, offset: i26) Instruction {
|
||||
return branch(cond, offset, 1);
|
||||
return initBranch(cond, offset, 1);
|
||||
}
|
||||
|
||||
// Branch and exchange
|
||||
@ -1289,7 +1289,7 @@ pub const Instruction = union(enum) {
|
||||
// Breakpoint
|
||||
|
||||
pub fn bkpt(imm: u16) Instruction {
|
||||
return breakpoint(imm);
|
||||
return initBreakpoint(imm);
|
||||
}
|
||||
|
||||
// Aliases
|
||||
|
||||
@ -15563,7 +15563,7 @@ fn genLazySymbolRef(
|
||||
.mov => try self.asmRegisterMemory(
|
||||
.{ ._, tag },
|
||||
reg.to64(),
|
||||
Memory.sib(.qword, .{ .base = .{ .reg = reg.to64() } }),
|
||||
Memory.initSib(.qword, .{ .base = .{ .reg = reg.to64() } }),
|
||||
),
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction {
|
||||
|
||||
if (modrm.rip()) {
|
||||
return inst(act_enc, .{
|
||||
.op1 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(act_enc.data.ops[0].memBitSize()), disp) },
|
||||
.op1 = .{ .mem = Memory.initRip(Memory.PtrSize.fromBitSize(act_enc.data.ops[0].memBitSize()), disp) },
|
||||
.op2 = op2,
|
||||
});
|
||||
}
|
||||
@ -106,7 +106,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction {
|
||||
else
|
||||
parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, 64);
|
||||
return inst(act_enc, .{
|
||||
.op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(act_enc.data.ops[0].memBitSize()), .{
|
||||
.op1 = .{ .mem = Memory.initSib(Memory.PtrSize.fromBitSize(act_enc.data.ops[0].memBitSize()), .{
|
||||
.base = if (base) |base_reg| .{ .reg = base_reg } else .none,
|
||||
.scale_index = scale_index,
|
||||
.disp = disp,
|
||||
@ -119,14 +119,14 @@ pub fn next(dis: *Disassembler) Error!?Instruction {
|
||||
const offset = try dis.parseOffset();
|
||||
return inst(enc, .{
|
||||
.op1 = .{ .reg = Register.rax.toBitSize(enc.data.ops[0].regBitSize()) },
|
||||
.op2 = .{ .mem = Memory.moffs(seg, offset) },
|
||||
.op2 = .{ .mem = Memory.initMoffs(seg, offset) },
|
||||
});
|
||||
},
|
||||
.td => {
|
||||
const seg = segmentRegister(prefixes.legacy);
|
||||
const offset = try dis.parseOffset();
|
||||
return inst(enc, .{
|
||||
.op1 = .{ .mem = Memory.moffs(seg, offset) },
|
||||
.op1 = .{ .mem = Memory.initMoffs(seg, offset) },
|
||||
.op2 = .{ .reg = Register.rax.toBitSize(enc.data.ops[1].regBitSize()) },
|
||||
});
|
||||
},
|
||||
@ -153,7 +153,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction {
|
||||
|
||||
if (modrm.rip()) {
|
||||
return inst(enc, .{
|
||||
.op1 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(dst_bit_size), disp) },
|
||||
.op1 = .{ .mem = Memory.initRip(Memory.PtrSize.fromBitSize(dst_bit_size), disp) },
|
||||
.op2 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.r, prefixes.rex, src_bit_size) },
|
||||
.op3 = op3,
|
||||
});
|
||||
@ -165,7 +165,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction {
|
||||
else
|
||||
parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, 64);
|
||||
return inst(enc, .{
|
||||
.op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(dst_bit_size), .{
|
||||
.op1 = .{ .mem = Memory.initSib(Memory.PtrSize.fromBitSize(dst_bit_size), .{
|
||||
.base = if (base) |base_reg| .{ .reg = base_reg } else .none,
|
||||
.scale_index = scale_index,
|
||||
.disp = disp,
|
||||
@ -203,7 +203,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction {
|
||||
if (modrm.rip()) {
|
||||
return inst(enc, .{
|
||||
.op1 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.r, prefixes.rex, dst_bit_size) },
|
||||
.op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(src_bit_size), disp) },
|
||||
.op2 = .{ .mem = Memory.initRip(Memory.PtrSize.fromBitSize(src_bit_size), disp) },
|
||||
.op3 = op3,
|
||||
});
|
||||
}
|
||||
@ -215,7 +215,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction {
|
||||
parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, 64);
|
||||
return inst(enc, .{
|
||||
.op1 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.r, prefixes.rex, dst_bit_size) },
|
||||
.op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(src_bit_size), .{
|
||||
.op2 = .{ .mem = Memory.initSib(Memory.PtrSize.fromBitSize(src_bit_size), .{
|
||||
.base = if (base) |base_reg| .{ .reg = base_reg } else .none,
|
||||
.scale_index = scale_index,
|
||||
.disp = disp,
|
||||
|
||||
@ -200,13 +200,13 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
|
||||
});
|
||||
try lower.emit(.none, .lea, &.{
|
||||
.{ .reg = inst.data.ri.r1 },
|
||||
.{ .mem = Memory.sib(.qword, .{
|
||||
.{ .mem = Memory.initSib(.qword, .{
|
||||
.base = .{ .reg = inst.data.ri.r1 },
|
||||
.disp = -page_size,
|
||||
}) },
|
||||
});
|
||||
try lower.emit(.none, .@"test", &.{
|
||||
.{ .mem = Memory.sib(.dword, .{
|
||||
.{ .mem = Memory.initSib(.dword, .{
|
||||
.base = .{ .reg = inst.data.ri.r1 },
|
||||
}) },
|
||||
.{ .reg = inst.data.ri.r1.to32() },
|
||||
@ -220,7 +220,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
|
||||
var offset = page_size;
|
||||
while (offset < @as(i32, @bitCast(inst.data.ri.i))) : (offset += page_size) {
|
||||
try lower.emit(.none, .@"test", &.{
|
||||
.{ .mem = Memory.sib(.dword, .{
|
||||
.{ .mem = Memory.initSib(.dword, .{
|
||||
.base = .{ .reg = inst.data.ri.r1 },
|
||||
.disp = -offset,
|
||||
}) },
|
||||
@ -246,7 +246,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
|
||||
},
|
||||
.pseudo_probe_adjust_loop_rr => {
|
||||
try lower.emit(.none, .@"test", &.{
|
||||
.{ .mem = Memory.sib(.dword, .{
|
||||
.{ .mem = Memory.initSib(.dword, .{
|
||||
.base = .{ .reg = inst.data.rr.r1 },
|
||||
.scale_index = .{ .scale = 1, .index = inst.data.rr.r2 },
|
||||
.disp = -page_size,
|
||||
@ -417,7 +417,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
|
||||
lower.result_insts[lower.result_insts_len] =
|
||||
try Instruction.new(.none, .lea, &[_]Operand{
|
||||
.{ .reg = .rdi },
|
||||
.{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) },
|
||||
.{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) },
|
||||
});
|
||||
lower.result_insts_len += 1;
|
||||
_ = lower.reloc(.{
|
||||
@ -430,7 +430,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
|
||||
lower.result_insts_len += 1;
|
||||
_ = lower.reloc(.{ .linker_dtpoff = sym_index }, 0);
|
||||
emit_mnemonic = .lea;
|
||||
break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
|
||||
break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{
|
||||
.base = .{ .reg = .rax },
|
||||
.disp = std.math.minInt(i32),
|
||||
}) };
|
||||
@ -439,12 +439,12 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
|
||||
lower.result_insts[lower.result_insts_len] =
|
||||
try Instruction.new(.none, .mov, &[_]Operand{
|
||||
.{ .reg = .rax },
|
||||
.{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .fs } }) },
|
||||
.{ .mem = Memory.initSib(.qword, .{ .base = .{ .reg = .fs } }) },
|
||||
});
|
||||
lower.result_insts_len += 1;
|
||||
_ = lower.reloc(.{ .linker_reloc = sym_index }, 0);
|
||||
emit_mnemonic = .lea;
|
||||
break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
|
||||
break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{
|
||||
.base = .{ .reg = .rax },
|
||||
.disp = std.math.minInt(i32),
|
||||
}) };
|
||||
@ -455,7 +455,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
|
||||
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) };
|
||||
break :op .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) };
|
||||
},
|
||||
.mov => {
|
||||
if (elf_sym.flags.is_extern_ptr) {
|
||||
@ -463,25 +463,25 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
|
||||
lower.result_insts[lower.result_insts_len] =
|
||||
try Instruction.new(.none, .mov, &[_]Operand{
|
||||
.{ .reg = reg.to64() },
|
||||
.{ .mem = Memory.rip(.qword, 0) },
|
||||
.{ .mem = Memory.initRip(.qword, 0) },
|
||||
});
|
||||
lower.result_insts_len += 1;
|
||||
break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ .base = .{
|
||||
break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ .base = .{
|
||||
.reg = reg.to64(),
|
||||
} }) };
|
||||
}
|
||||
break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
|
||||
break :op .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) };
|
||||
},
|
||||
else => unreachable,
|
||||
} else switch (mnemonic) {
|
||||
.call => break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
|
||||
.call => break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{
|
||||
.base = .{ .reg = .ds },
|
||||
}) },
|
||||
.lea => {
|
||||
emit_mnemonic = .mov;
|
||||
break :op .{ .imm = Immediate.s(0) };
|
||||
},
|
||||
.mov => break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
|
||||
.mov => break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{
|
||||
.base = .{ .reg = .ds },
|
||||
}) },
|
||||
else => unreachable,
|
||||
@ -495,12 +495,12 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
|
||||
lower.result_insts[lower.result_insts_len] =
|
||||
try Instruction.new(.none, .mov, &[_]Operand{
|
||||
.{ .reg = .rdi },
|
||||
.{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) },
|
||||
.{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) },
|
||||
});
|
||||
lower.result_insts_len += 1;
|
||||
lower.result_insts[lower.result_insts_len] =
|
||||
try Instruction.new(.none, .call, &[_]Operand{
|
||||
.{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .rdi } }) },
|
||||
.{ .mem = Memory.initSib(.qword, .{ .base = .{ .reg = .rdi } }) },
|
||||
});
|
||||
lower.result_insts_len += 1;
|
||||
emit_mnemonic = .mov;
|
||||
@ -511,7 +511,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
|
||||
break :op switch (mnemonic) {
|
||||
.lea => {
|
||||
if (macho_sym.flags.is_extern_ptr) emit_mnemonic = .mov;
|
||||
break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
|
||||
break :op .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) };
|
||||
},
|
||||
.mov => {
|
||||
if (macho_sym.flags.is_extern_ptr) {
|
||||
@ -519,14 +519,14 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
|
||||
lower.result_insts[lower.result_insts_len] =
|
||||
try Instruction.new(.none, .mov, &[_]Operand{
|
||||
.{ .reg = reg.to64() },
|
||||
.{ .mem = Memory.rip(.qword, 0) },
|
||||
.{ .mem = Memory.initRip(.qword, 0) },
|
||||
});
|
||||
lower.result_insts_len += 1;
|
||||
break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ .base = .{
|
||||
break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ .base = .{
|
||||
.reg = reg.to64(),
|
||||
} }) };
|
||||
}
|
||||
break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) };
|
||||
break :op .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) };
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
@ -701,7 +701,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
|
||||
}, extra.off);
|
||||
break :ops &.{
|
||||
.{ .reg = reg },
|
||||
.{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) },
|
||||
.{ .mem = Memory.initRip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) },
|
||||
};
|
||||
},
|
||||
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
|
||||
|
||||
@ -1234,9 +1234,9 @@ pub const Memory = struct {
|
||||
.rm => {
|
||||
if (mem.info.base == .reg and @as(Register, @enumFromInt(mem.base)) == .rip) {
|
||||
assert(mem.info.index == .none and mem.info.scale == .@"1");
|
||||
return encoder.Instruction.Memory.rip(mem.info.size, @bitCast(mem.off));
|
||||
return encoder.Instruction.Memory.initRip(mem.info.size, @bitCast(mem.off));
|
||||
}
|
||||
return encoder.Instruction.Memory.sib(mem.info.size, .{
|
||||
return encoder.Instruction.Memory.initSib(mem.info.size, .{
|
||||
.disp = @bitCast(mem.off),
|
||||
.base = switch (mem.info.base) {
|
||||
.none => .none,
|
||||
@ -1258,7 +1258,7 @@ pub const Memory = struct {
|
||||
},
|
||||
.off => {
|
||||
assert(mem.info.base == .reg);
|
||||
return encoder.Instruction.Memory.moffs(
|
||||
return encoder.Instruction.Memory.initMoffs(
|
||||
@enumFromInt(mem.base),
|
||||
@as(u64, mem.extra) << 32 | mem.off,
|
||||
);
|
||||
|
||||
@ -110,12 +110,12 @@ pub const Instruction = struct {
|
||||
offset: u64,
|
||||
};
|
||||
|
||||
pub fn moffs(reg: Register, offset: u64) Memory {
|
||||
pub fn initMoffs(reg: Register, offset: u64) Memory {
|
||||
assert(reg.class() == .segment);
|
||||
return .{ .moffs = .{ .seg = reg, .offset = offset } };
|
||||
}
|
||||
|
||||
pub fn sib(ptr_size: PtrSize, args: struct {
|
||||
pub fn initSib(ptr_size: PtrSize, args: struct {
|
||||
disp: i32 = 0,
|
||||
base: Base = .none,
|
||||
scale_index: ?ScaleIndex = null,
|
||||
@ -129,7 +129,7 @@ pub const Instruction = struct {
|
||||
} };
|
||||
}
|
||||
|
||||
pub fn rip(ptr_size: PtrSize, displacement: i32) Memory {
|
||||
pub fn initRip(ptr_size: PtrSize, displacement: i32) Memory {
|
||||
return .{ .rip = .{ .ptr_size = ptr_size, .disp = displacement } };
|
||||
}
|
||||
|
||||
@ -1266,7 +1266,7 @@ test "lower MI encoding" {
|
||||
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 } }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .r12 } }) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10");
|
||||
@ -1290,13 +1290,13 @@ test "lower MI encoding" {
|
||||
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 } }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r11 } }) },
|
||||
.{ .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) },
|
||||
.{ .mem = Instruction.Memory.initRip(.qword, 0x10) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
@ -1306,25 +1306,25 @@ test "lower MI encoding" {
|
||||
);
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -8 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -8 }) },
|
||||
.{ .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 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -2 }) },
|
||||
.{ .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 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -1 }) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{
|
||||
.base = .{ .reg = .ds },
|
||||
.disp = 0x10000000,
|
||||
.scale_index = .{ .scale = 2, .index = .rcx },
|
||||
@ -1338,13 +1338,13 @@ test "lower MI encoding" {
|
||||
);
|
||||
|
||||
try enc.encode(.adc, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -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) },
|
||||
.{ .mem = Instruction.Memory.initRip(.qword, 0) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x48\x83\x15\x00\x00\x00\x00\x10", enc.code(), "adc QWORD PTR [rip], 0x10");
|
||||
@ -1356,7 +1356,7 @@ test "lower MI encoding" {
|
||||
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 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .rdx }, .disp = -8 }) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10");
|
||||
@ -1368,13 +1368,13 @@ test "lower MI encoding" {
|
||||
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 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -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 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
@ -1384,7 +1384,7 @@ test "lower MI encoding" {
|
||||
);
|
||||
|
||||
try enc.encode(.@"and", &.{
|
||||
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
@ -1394,7 +1394,7 @@ test "lower MI encoding" {
|
||||
);
|
||||
|
||||
try enc.encode(.@"and", &.{
|
||||
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
@ -1404,7 +1404,7 @@ test "lower MI encoding" {
|
||||
);
|
||||
|
||||
try enc.encode(.sub, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
|
||||
.{ .imm = Instruction.Immediate.u(0x10) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
@ -1419,25 +1419,25 @@ test "lower RM encoding" {
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .reg = .rax },
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r11 } }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r11 } }) },
|
||||
});
|
||||
try expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .reg = .rbx },
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10 }) },
|
||||
});
|
||||
try expectEqualHexStrings("\x48\x8B\x1C\x25\x10\x00\x00\x00", enc.code(), "mov rbx, QWORD PTR ds:0x10");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .reg = .rax },
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) },
|
||||
});
|
||||
try expectEqualHexStrings("\x48\x8B\x45\xFC", enc.code(), "mov rax, QWORD PTR [rbp - 4]");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .reg = .rax },
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{
|
||||
.base = .{ .reg = .rbp },
|
||||
.scale_index = .{ .scale = 1, .index = .rcx },
|
||||
.disp = -8,
|
||||
@ -1447,7 +1447,7 @@ test "lower RM encoding" {
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .reg = .eax },
|
||||
.{ .mem = Instruction.Memory.sib(.dword, .{
|
||||
.{ .mem = Instruction.Memory.initSib(.dword, .{
|
||||
.base = .{ .reg = .rbp },
|
||||
.scale_index = .{ .scale = 4, .index = .rdx },
|
||||
.disp = -4,
|
||||
@ -1457,7 +1457,7 @@ test "lower RM encoding" {
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .reg = .rax },
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{
|
||||
.base = .{ .reg = .rbp },
|
||||
.scale_index = .{ .scale = 8, .index = .rcx },
|
||||
.disp = -8,
|
||||
@ -1467,7 +1467,7 @@ test "lower RM encoding" {
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .reg = .r8b },
|
||||
.{ .mem = Instruction.Memory.sib(.byte, .{
|
||||
.{ .mem = Instruction.Memory.initSib(.byte, .{
|
||||
.base = .{ .reg = .rsi },
|
||||
.scale_index = .{ .scale = 1, .index = .rcx },
|
||||
.disp = -24,
|
||||
@ -1483,7 +1483,7 @@ test "lower RM encoding" {
|
||||
try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
|
||||
.{ .reg = .fs },
|
||||
});
|
||||
try expectEqualHexStrings("\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs");
|
||||
@ -1514,19 +1514,19 @@ test "lower RM encoding" {
|
||||
|
||||
try enc.encode(.movsx, &.{
|
||||
.{ .reg = .eax },
|
||||
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp } }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp } }) },
|
||||
});
|
||||
try expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]");
|
||||
|
||||
try enc.encode(.movsx, &.{
|
||||
.{ .reg = .eax },
|
||||
.{ .mem = Instruction.Memory.sib(.byte, .{ .scale_index = .{ .index = .rax, .scale = 2 } }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.byte, .{ .scale_index = .{ .index = .rax, .scale = 2 } }) },
|
||||
});
|
||||
try expectEqualHexStrings("\x0F\xBE\x04\x45\x00\x00\x00\x00", enc.code(), "movsx eax, BYTE PTR [rax * 2]");
|
||||
|
||||
try enc.encode(.movsx, &.{
|
||||
.{ .reg = .ax },
|
||||
.{ .mem = Instruction.Memory.rip(.byte, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initRip(.byte, 0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x66\x0F\xBE\x05\x10\x00\x00\x00", enc.code(), "movsx ax, BYTE PTR [rip + 0x10]");
|
||||
|
||||
@ -1544,37 +1544,37 @@ test "lower RM encoding" {
|
||||
|
||||
try enc.encode(.lea, &.{
|
||||
.{ .reg = .rax },
|
||||
.{ .mem = Instruction.Memory.rip(.qword, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initRip(.qword, 0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, QWORD PTR [rip + 0x10]");
|
||||
|
||||
try enc.encode(.lea, &.{
|
||||
.{ .reg = .rax },
|
||||
.{ .mem = Instruction.Memory.rip(.dword, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initRip(.dword, 0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, DWORD PTR [rip + 0x10]");
|
||||
|
||||
try enc.encode(.lea, &.{
|
||||
.{ .reg = .eax },
|
||||
.{ .mem = Instruction.Memory.rip(.dword, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initRip(.dword, 0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, DWORD PTR [rip + 0x10]");
|
||||
|
||||
try enc.encode(.lea, &.{
|
||||
.{ .reg = .eax },
|
||||
.{ .mem = Instruction.Memory.rip(.word, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initRip(.word, 0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, WORD PTR [rip + 0x10]");
|
||||
|
||||
try enc.encode(.lea, &.{
|
||||
.{ .reg = .ax },
|
||||
.{ .mem = Instruction.Memory.rip(.byte, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initRip(.byte, 0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x66\x8D\x05\x10\x00\x00\x00", enc.code(), "lea ax, BYTE PTR [rip + 0x10]");
|
||||
|
||||
try enc.encode(.lea, &.{
|
||||
.{ .reg = .rsi },
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{
|
||||
.base = .{ .reg = .rbp },
|
||||
.scale_index = .{ .scale = 1, .index = .rcx },
|
||||
}) },
|
||||
@ -1583,31 +1583,31 @@ test "lower RM encoding" {
|
||||
|
||||
try enc.encode(.add, &.{
|
||||
.{ .reg = .r11 },
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
|
||||
});
|
||||
try expectEqualHexStrings("\x4C\x03\x1C\x25\x00\x00\x00\x10", enc.code(), "add r11, QWORD PTR ds:0x10000000");
|
||||
|
||||
try enc.encode(.add, &.{
|
||||
.{ .reg = .r12b },
|
||||
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
|
||||
});
|
||||
try expectEqualHexStrings("\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR ds:0x10000000");
|
||||
|
||||
try enc.encode(.add, &.{
|
||||
.{ .reg = .r12b },
|
||||
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .fs }, .disp = 0x10000000 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .fs }, .disp = 0x10000000 }) },
|
||||
});
|
||||
try expectEqualHexStrings("\x64\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR fs:0x10000000");
|
||||
|
||||
try enc.encode(.sub, &.{
|
||||
.{ .reg = .r11 },
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r13 }, .disp = 0x10000000 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r13 }, .disp = 0x10000000 }) },
|
||||
});
|
||||
try expectEqualHexStrings("\x4D\x2B\x9D\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r13 + 0x10000000]");
|
||||
|
||||
try enc.encode(.sub, &.{
|
||||
.{ .reg = .r11 },
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
|
||||
});
|
||||
try expectEqualHexStrings("\x4D\x2B\x9C\x24\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r12 + 0x10000000]");
|
||||
|
||||
@ -1630,7 +1630,7 @@ test "lower RMI encoding" {
|
||||
|
||||
try enc.encode(.imul, &.{
|
||||
.{ .reg = .r11 },
|
||||
.{ .mem = Instruction.Memory.rip(.qword, -16) },
|
||||
.{ .mem = Instruction.Memory.initRip(.qword, -16) },
|
||||
.{ .imm = Instruction.Immediate.s(-1024) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
@ -1641,7 +1641,7 @@ test "lower RMI encoding" {
|
||||
|
||||
try enc.encode(.imul, &.{
|
||||
.{ .reg = .bx },
|
||||
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
|
||||
.{ .imm = Instruction.Immediate.s(-1024) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
@ -1652,7 +1652,7 @@ test "lower RMI encoding" {
|
||||
|
||||
try enc.encode(.imul, &.{
|
||||
.{ .reg = .bx },
|
||||
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
|
||||
.{ .imm = Instruction.Immediate.u(1024) },
|
||||
});
|
||||
try expectEqualHexStrings(
|
||||
@ -1672,19 +1672,19 @@ test "lower MR encoding" {
|
||||
try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) },
|
||||
.{ .reg = .r11 },
|
||||
});
|
||||
try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .mem = Instruction.Memory.rip(.qword, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initRip(.qword, 0x10) },
|
||||
.{ .reg = .r12 },
|
||||
});
|
||||
try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rip + 0x10], r12");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{
|
||||
.base = .{ .reg = .r11 },
|
||||
.scale_index = .{ .scale = 2, .index = .r12 },
|
||||
.disp = 0x10,
|
||||
@ -1694,13 +1694,13 @@ test "lower MR encoding" {
|
||||
try expectEqualHexStrings("\x4F\x89\x6C\x63\x10", enc.code(), "mov QWORD PTR [r11 + 2 * r12 + 0x10], r13");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .mem = Instruction.Memory.rip(.word, -0x10) },
|
||||
.{ .mem = Instruction.Memory.initRip(.word, -0x10) },
|
||||
.{ .reg = .r12w },
|
||||
});
|
||||
try expectEqualHexStrings("\x66\x44\x89\x25\xF0\xFF\xFF\xFF", enc.code(), "mov WORD PTR [rip - 0x10], r12w");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.byte, .{
|
||||
.{ .mem = Instruction.Memory.initSib(.byte, .{
|
||||
.base = .{ .reg = .r11 },
|
||||
.scale_index = .{ .scale = 2, .index = .r12 },
|
||||
.disp = 0x10,
|
||||
@ -1710,25 +1710,25 @@ test "lower MR encoding" {
|
||||
try expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b");
|
||||
|
||||
try enc.encode(.add, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
|
||||
.{ .reg = .r12b },
|
||||
});
|
||||
try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b");
|
||||
|
||||
try enc.encode(.add, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
|
||||
.{ .reg = .r12d },
|
||||
});
|
||||
try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d");
|
||||
|
||||
try enc.encode(.add, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .gs }, .disp = 0x10000000 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .gs }, .disp = 0x10000000 }) },
|
||||
.{ .reg = .r12d },
|
||||
});
|
||||
try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d");
|
||||
|
||||
try enc.encode(.sub, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
|
||||
.{ .reg = .r12 },
|
||||
});
|
||||
try expectEqualHexStrings("\x4D\x29\xA3\x00\x00\x00\x10", enc.code(), "sub QWORD PTR [r11 + 0x10000000], r12");
|
||||
@ -1743,12 +1743,12 @@ test "lower M encoding" {
|
||||
try expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12");
|
||||
|
||||
try enc.encode(.call, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r12 } }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r12 } }) },
|
||||
});
|
||||
try expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]");
|
||||
|
||||
try enc.encode(.call, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{
|
||||
.base = .none,
|
||||
.scale_index = .{ .index = .r11, .scale = 2 },
|
||||
}) },
|
||||
@ -1756,7 +1756,7 @@ test "lower M encoding" {
|
||||
try expectEqualHexStrings("\x42\xFF\x14\x5D\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r11 * 2]");
|
||||
|
||||
try enc.encode(.call, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{
|
||||
.base = .none,
|
||||
.scale_index = .{ .index = .r12, .scale = 2 },
|
||||
}) },
|
||||
@ -1764,7 +1764,7 @@ test "lower M encoding" {
|
||||
try expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]");
|
||||
|
||||
try enc.encode(.call, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .gs } }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .gs } }) },
|
||||
});
|
||||
try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0");
|
||||
|
||||
@ -1774,22 +1774,22 @@ test "lower M encoding" {
|
||||
try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0");
|
||||
|
||||
try enc.encode(.push, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp } }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp } }) },
|
||||
});
|
||||
try expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
|
||||
|
||||
try enc.encode(.push, &.{
|
||||
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp } }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp } }) },
|
||||
});
|
||||
try expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
|
||||
|
||||
try enc.encode(.pop, &.{
|
||||
.{ .mem = Instruction.Memory.rip(.qword, 0) },
|
||||
.{ .mem = Instruction.Memory.initRip(.qword, 0) },
|
||||
});
|
||||
try expectEqualHexStrings("\x8F\x05\x00\x00\x00\x00", enc.code(), "pop QWORD PTR [rip]");
|
||||
|
||||
try enc.encode(.pop, &.{
|
||||
.{ .mem = Instruction.Memory.rip(.word, 0) },
|
||||
.{ .mem = Instruction.Memory.initRip(.word, 0) },
|
||||
});
|
||||
try expectEqualHexStrings("\x66\x8F\x05\x00\x00\x00\x00", enc.code(), "pop WORD PTR [rbp]");
|
||||
|
||||
@ -1870,48 +1870,48 @@ test "lower FD/TD encoding" {
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .reg = .rax },
|
||||
.{ .mem = Instruction.Memory.moffs(.cs, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initMoffs(.cs, 0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x2E\x48\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs rax, cs:0x10");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .reg = .eax },
|
||||
.{ .mem = Instruction.Memory.moffs(.fs, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initMoffs(.fs, 0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x64\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs eax, fs:0x10");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .reg = .ax },
|
||||
.{ .mem = Instruction.Memory.moffs(.gs, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initMoffs(.gs, 0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\x65\x66\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ax, gs:0x10");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .reg = .al },
|
||||
.{ .mem = Instruction.Memory.moffs(.ds, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initMoffs(.ds, 0x10) },
|
||||
});
|
||||
try expectEqualHexStrings("\xA0\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs al, ds:0x10");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .mem = Instruction.Memory.moffs(.cs, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initMoffs(.cs, 0x10) },
|
||||
.{ .reg = .rax },
|
||||
});
|
||||
try expectEqualHexStrings("\x2E\x48\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs cs:0x10, rax");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .mem = Instruction.Memory.moffs(.fs, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initMoffs(.fs, 0x10) },
|
||||
.{ .reg = .eax },
|
||||
});
|
||||
try expectEqualHexStrings("\x64\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs fs:0x10, eax");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .mem = Instruction.Memory.moffs(.gs, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initMoffs(.gs, 0x10) },
|
||||
.{ .reg = .ax },
|
||||
});
|
||||
try expectEqualHexStrings("\x65\x66\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs gs:0x10, ax");
|
||||
|
||||
try enc.encode(.mov, &.{
|
||||
.{ .mem = Instruction.Memory.moffs(.ds, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initMoffs(.ds, 0x10) },
|
||||
.{ .reg = .al },
|
||||
});
|
||||
try expectEqualHexStrings("\xA2\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ds:0x10, al");
|
||||
@ -1949,16 +1949,16 @@ test "invalid instruction" {
|
||||
.{ .reg = .al },
|
||||
});
|
||||
try invalidInstruction(.call, &.{
|
||||
.{ .mem = Instruction.Memory.rip(.dword, 0) },
|
||||
.{ .mem = Instruction.Memory.initRip(.dword, 0) },
|
||||
});
|
||||
try invalidInstruction(.call, &.{
|
||||
.{ .mem = Instruction.Memory.rip(.word, 0) },
|
||||
.{ .mem = Instruction.Memory.initRip(.word, 0) },
|
||||
});
|
||||
try invalidInstruction(.call, &.{
|
||||
.{ .mem = Instruction.Memory.rip(.byte, 0) },
|
||||
.{ .mem = Instruction.Memory.initRip(.byte, 0) },
|
||||
});
|
||||
try invalidInstruction(.mov, &.{
|
||||
.{ .mem = Instruction.Memory.rip(.word, 0x10) },
|
||||
.{ .mem = Instruction.Memory.initRip(.word, 0x10) },
|
||||
.{ .reg = .r12 },
|
||||
});
|
||||
try invalidInstruction(.lea, &.{
|
||||
@ -1967,7 +1967,7 @@ test "invalid instruction" {
|
||||
});
|
||||
try invalidInstruction(.lea, &.{
|
||||
.{ .reg = .al },
|
||||
.{ .mem = Instruction.Memory.rip(.byte, 0) },
|
||||
.{ .mem = Instruction.Memory.initRip(.byte, 0) },
|
||||
});
|
||||
try invalidInstruction(.pop, &.{
|
||||
.{ .reg = .r12b },
|
||||
@ -1992,7 +1992,7 @@ fn cannotEncode(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand
|
||||
|
||||
test "cannot encode" {
|
||||
try cannotEncode(.@"test", &.{
|
||||
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) },
|
||||
.{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .r12 } }) },
|
||||
.{ .reg = .ah },
|
||||
});
|
||||
try cannotEncode(.@"test", &.{
|
||||
@ -2369,7 +2369,7 @@ const Assembler = struct {
|
||||
if (res.rip) {
|
||||
if (res.base != null or res.scale_index != null or res.offset != null)
|
||||
return error.InvalidMemoryOperand;
|
||||
return Instruction.Memory.rip(ptr_size orelse .qword, res.disp orelse 0);
|
||||
return Instruction.Memory.initRip(ptr_size orelse .qword, res.disp orelse 0);
|
||||
}
|
||||
if (res.base) |base| {
|
||||
if (res.rip)
|
||||
@ -2377,9 +2377,9 @@ const Assembler = struct {
|
||||
if (res.offset) |offset| {
|
||||
if (res.scale_index != null or res.disp != null)
|
||||
return error.InvalidMemoryOperand;
|
||||
return Instruction.Memory.moffs(base, offset);
|
||||
return Instruction.Memory.initMoffs(base, offset);
|
||||
}
|
||||
return Instruction.Memory.sib(ptr_size orelse .qword, .{
|
||||
return Instruction.Memory.initSib(ptr_size orelse .qword, .{
|
||||
.base = .{ .reg = base },
|
||||
.scale_index = res.scale_index,
|
||||
.disp = res.disp orelse 0,
|
||||
|
||||
@ -836,16 +836,6 @@ pub const GenResult = union(enum) {
|
||||
/// Traditionally, this corresponds to emitting a relocation in a relocatable object file.
|
||||
lea_symbol: u32,
|
||||
};
|
||||
|
||||
fn fail(
|
||||
gpa: Allocator,
|
||||
src_loc: Zcu.LazySrcLoc,
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) Allocator.Error!GenResult {
|
||||
const msg = try ErrorMsg.create(gpa, src_loc, format, args);
|
||||
return .{ .fail = msg };
|
||||
}
|
||||
};
|
||||
|
||||
fn genNavRef(
|
||||
@ -935,7 +925,8 @@ fn genNavRef(
|
||||
const atom = p9.getAtom(atom_index);
|
||||
return .{ .mcv = .{ .memory = atom.getOffsetTableAddress(p9) } };
|
||||
} else {
|
||||
return GenResult.fail(gpa, src_loc, "TODO genNavRef for target {}", .{target});
|
||||
const msg = try ErrorMsg.create(gpa, src_loc, "TODO genNavRef for target {}", .{target});
|
||||
return .{ .fail = msg };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -33,9 +33,9 @@ pub const Block = struct {
|
||||
.abbrevs = .{ .abbrevs = .{} },
|
||||
};
|
||||
|
||||
const set_bid: u32 = 1;
|
||||
const block_name: u32 = 2;
|
||||
const set_record_name: u32 = 3;
|
||||
const set_bid_id: u32 = 1;
|
||||
const block_name_id: u32 = 2;
|
||||
const set_record_name_id: u32 = 3;
|
||||
|
||||
fn deinit(info: *Info, allocator: std.mem.Allocator) void {
|
||||
allocator.free(info.block_name);
|
||||
@ -61,7 +61,7 @@ pub const Record = struct {
|
||||
assert(record.id == Abbrev.Builtin.define_abbrev.toRecordId());
|
||||
var i: usize = 0;
|
||||
while (i < record.operands.len) switch (record.operands[i]) {
|
||||
Abbrev.Operand.literal => {
|
||||
Abbrev.Operand.literal_id => {
|
||||
try operands.append(.{ .literal = record.operands[i + 1] });
|
||||
i += 2;
|
||||
},
|
||||
@ -211,7 +211,7 @@ fn nextRecord(bc: *BitcodeReader) !?Record {
|
||||
.align_32_bits, .block_len => return error.UnsupportedArrayElement,
|
||||
.abbrev_op => switch (try bc.readFixed(u1, 1)) {
|
||||
1 => try operands.appendSlice(&.{
|
||||
Abbrev.Operand.literal,
|
||||
Abbrev.Operand.literal_id,
|
||||
try bc.readVbr(u64, 8),
|
||||
}),
|
||||
0 => {
|
||||
@ -334,9 +334,9 @@ fn parseBlockInfoBlock(bc: *BitcodeReader) !void {
|
||||
try record.toOwnedAbbrev(bc.allocator),
|
||||
);
|
||||
},
|
||||
Block.Info.set_bid => block_id = std.math.cast(u32, record.operands[0]) orelse
|
||||
Block.Info.set_bid_id => block_id = std.math.cast(u32, record.operands[0]) orelse
|
||||
return error.Overflow,
|
||||
Block.Info.block_name => if (bc.keep_names) {
|
||||
Block.Info.block_name_id => if (bc.keep_names) {
|
||||
const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse
|
||||
return error.UnspecifiedBlockId);
|
||||
if (!gop.found_existing) gop.value_ptr.* = Block.Info.default;
|
||||
@ -346,7 +346,7 @@ fn parseBlockInfoBlock(bc: *BitcodeReader) !void {
|
||||
byte.* = std.math.cast(u8, operand) orelse return error.InvalidName;
|
||||
gop.value_ptr.block_name = name;
|
||||
},
|
||||
Block.Info.set_record_name => if (bc.keep_names) {
|
||||
Block.Info.set_record_name_id => if (bc.keep_names) {
|
||||
const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse
|
||||
return error.UnspecifiedBlockId);
|
||||
if (!gop.found_existing) gop.value_ptr.* = Block.Info.default;
|
||||
@ -467,7 +467,7 @@ const Abbrev = struct {
|
||||
block_len,
|
||||
abbrev_op,
|
||||
|
||||
const literal = std.math.maxInt(u64);
|
||||
const literal_id = std.math.maxInt(u64);
|
||||
const Encoding = enum(u3) {
|
||||
fixed = 1,
|
||||
vbr = 2,
|
||||
|
||||
@ -3467,7 +3467,7 @@ fn updateSectionSizes(self: *Elf) !void {
|
||||
if (atom_list.items.len == 0) continue;
|
||||
|
||||
// Create jump/branch range extenders if needed.
|
||||
try thunks.createThunks(shdr, @intCast(shndx), self);
|
||||
try self.createThunks(shdr, @intCast(shndx));
|
||||
}
|
||||
}
|
||||
|
||||
@ -5576,6 +5576,88 @@ fn defaultEntrySymbolName(cpu_arch: std.Target.Cpu.Arch) []const u8 {
|
||||
};
|
||||
}
|
||||
|
||||
fn createThunks(elf_file: *Elf, shdr: *elf.Elf64_Shdr, shndx: u32) !void {
|
||||
const gpa = elf_file.base.comp.gpa;
|
||||
const cpu_arch = elf_file.getTarget().cpu.arch;
|
||||
// A branch will need an extender if its target is larger than
|
||||
// `2^(jump_bits - 1) - margin` where margin is some arbitrary number.
|
||||
const max_distance = switch (cpu_arch) {
|
||||
.aarch64 => 0x500_000,
|
||||
.x86_64, .riscv64 => unreachable,
|
||||
else => @panic("unhandled arch"),
|
||||
};
|
||||
const atoms = elf_file.sections.items(.atom_list)[shndx].items;
|
||||
assert(atoms.len > 0);
|
||||
|
||||
for (atoms) |ref| {
|
||||
elf_file.atom(ref).?.value = -1;
|
||||
}
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < atoms.len) {
|
||||
const start = i;
|
||||
const start_atom = elf_file.atom(atoms[start]).?;
|
||||
assert(start_atom.alive);
|
||||
start_atom.value = try advanceSection(shdr, start_atom.size, start_atom.alignment);
|
||||
i += 1;
|
||||
|
||||
while (i < atoms.len) : (i += 1) {
|
||||
const atom_ptr = elf_file.atom(atoms[i]).?;
|
||||
assert(atom_ptr.alive);
|
||||
if (@as(i64, @intCast(atom_ptr.alignment.forward(shdr.sh_size))) - start_atom.value >= max_distance)
|
||||
break;
|
||||
atom_ptr.value = try advanceSection(shdr, atom_ptr.size, atom_ptr.alignment);
|
||||
}
|
||||
|
||||
// Insert a thunk at the group end
|
||||
const thunk_index = try elf_file.addThunk();
|
||||
const thunk_ptr = elf_file.thunk(thunk_index);
|
||||
thunk_ptr.output_section_index = shndx;
|
||||
|
||||
// Scan relocs in the group and create trampolines for any unreachable callsite
|
||||
for (atoms[start..i]) |ref| {
|
||||
const atom_ptr = elf_file.atom(ref).?;
|
||||
const file_ptr = atom_ptr.file(elf_file).?;
|
||||
log.debug("atom({}) {s}", .{ ref, atom_ptr.name(elf_file) });
|
||||
for (atom_ptr.relocs(elf_file)) |rel| {
|
||||
const is_reachable = switch (cpu_arch) {
|
||||
.aarch64 => r: {
|
||||
const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type());
|
||||
if (r_type != .CALL26 and r_type != .JUMP26) break :r true;
|
||||
const target_ref = file_ptr.resolveSymbol(rel.r_sym(), elf_file);
|
||||
const target = elf_file.symbol(target_ref).?;
|
||||
if (target.flags.has_plt) break :r false;
|
||||
if (atom_ptr.output_section_index != target.output_section_index) break :r false;
|
||||
const target_atom = target.atom(elf_file).?;
|
||||
if (target_atom.value == -1) break :r false;
|
||||
const saddr = atom_ptr.address(elf_file) + @as(i64, @intCast(rel.r_offset));
|
||||
const taddr = target.address(.{}, elf_file);
|
||||
_ = math.cast(i28, taddr + rel.r_addend - saddr) orelse break :r false;
|
||||
break :r true;
|
||||
},
|
||||
.x86_64, .riscv64 => unreachable,
|
||||
else => @panic("unsupported arch"),
|
||||
};
|
||||
if (is_reachable) continue;
|
||||
const target = file_ptr.resolveSymbol(rel.r_sym(), elf_file);
|
||||
try thunk_ptr.symbols.put(gpa, target, {});
|
||||
}
|
||||
atom_ptr.addExtra(.{ .thunk = thunk_index }, elf_file);
|
||||
}
|
||||
|
||||
thunk_ptr.value = try advanceSection(shdr, thunk_ptr.size(elf_file), Atom.Alignment.fromNonzeroByteUnits(2));
|
||||
|
||||
log.debug("thunk({d}) : {}", .{ thunk_index, thunk_ptr.fmt(elf_file) });
|
||||
}
|
||||
}
|
||||
fn advanceSection(shdr: *elf.Elf64_Shdr, adv_size: u64, alignment: Atom.Alignment) !i64 {
|
||||
const offset = alignment.forward(shdr.sh_size);
|
||||
const padding = offset - shdr.sh_size;
|
||||
shdr.sh_size += padding + adv_size;
|
||||
shdr.sh_addralign = @max(shdr.sh_addralign, alignment.toByteUnits() orelse 1);
|
||||
return @intCast(offset);
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const build_options = @import("build_options");
|
||||
const builtin = @import("builtin");
|
||||
@ -5598,7 +5680,6 @@ const musl = @import("../musl.zig");
|
||||
const relocatable = @import("Elf/relocatable.zig");
|
||||
const relocation = @import("Elf/relocation.zig");
|
||||
const target_util = @import("../target.zig");
|
||||
const thunks = @import("Elf/thunks.zig");
|
||||
const trace = @import("../tracy.zig").trace;
|
||||
const synthetic_sections = @import("Elf/synthetic_sections.zig");
|
||||
|
||||
@ -5636,7 +5717,7 @@ const PltGotSection = synthetic_sections.PltGotSection;
|
||||
const SharedObject = @import("Elf/SharedObject.zig");
|
||||
const Symbol = @import("Elf/Symbol.zig");
|
||||
const StringTable = @import("StringTable.zig");
|
||||
const Thunk = thunks.Thunk;
|
||||
const Thunk = @import("Elf/Thunk.zig");
|
||||
const Value = @import("../Value.zig");
|
||||
const VerneedSection = synthetic_sections.VerneedSection;
|
||||
const ZigObject = @import("Elf/ZigObject.zig");
|
||||
|
||||
@ -2251,6 +2251,6 @@ const Fde = eh_frame.Fde;
|
||||
const File = @import("file.zig").File;
|
||||
const Object = @import("Object.zig");
|
||||
const Symbol = @import("Symbol.zig");
|
||||
const Thunk = @import("thunks.zig").Thunk;
|
||||
const Thunk = @import("Thunk.zig");
|
||||
const ZigObject = @import("ZigObject.zig");
|
||||
const dev = @import("../../dev.zig");
|
||||
|
||||
144
src/link/Elf/Thunk.zig
Normal file
144
src/link/Elf/Thunk.zig
Normal file
@ -0,0 +1,144 @@
|
||||
value: i64 = 0,
|
||||
output_section_index: u32 = 0,
|
||||
symbols: std.AutoArrayHashMapUnmanaged(Elf.Ref, void) = .{},
|
||||
output_symtab_ctx: Elf.SymtabCtx = .{},
|
||||
|
||||
pub fn deinit(thunk: *Thunk, allocator: Allocator) void {
|
||||
thunk.symbols.deinit(allocator);
|
||||
}
|
||||
|
||||
pub fn size(thunk: Thunk, elf_file: *Elf) usize {
|
||||
const cpu_arch = elf_file.getTarget().cpu.arch;
|
||||
return thunk.symbols.keys().len * trampolineSize(cpu_arch);
|
||||
}
|
||||
|
||||
pub fn address(thunk: Thunk, elf_file: *Elf) i64 {
|
||||
const shdr = elf_file.sections.items(.shdr)[thunk.output_section_index];
|
||||
return @as(i64, @intCast(shdr.sh_addr)) + thunk.value;
|
||||
}
|
||||
|
||||
pub fn targetAddress(thunk: Thunk, ref: Elf.Ref, elf_file: *Elf) i64 {
|
||||
const cpu_arch = elf_file.getTarget().cpu.arch;
|
||||
return thunk.address(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(ref).? * trampolineSize(cpu_arch)));
|
||||
}
|
||||
|
||||
pub fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void {
|
||||
switch (elf_file.getTarget().cpu.arch) {
|
||||
.aarch64 => try aarch64.write(thunk, elf_file, writer),
|
||||
.x86_64, .riscv64 => unreachable,
|
||||
else => @panic("unhandled arch"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calcSymtabSize(thunk: *Thunk, elf_file: *Elf) void {
|
||||
thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len));
|
||||
for (thunk.symbols.keys()) |ref| {
|
||||
const sym = elf_file.symbol(ref).?;
|
||||
thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.name(elf_file).len + "$thunk".len + 1));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeSymtab(thunk: Thunk, elf_file: *Elf) void {
|
||||
const cpu_arch = elf_file.getTarget().cpu.arch;
|
||||
for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| {
|
||||
const sym = elf_file.symbol(ref).?;
|
||||
const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
|
||||
elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
|
||||
elf_file.strtab.appendSliceAssumeCapacity("$thunk");
|
||||
elf_file.strtab.appendAssumeCapacity(0);
|
||||
elf_file.symtab.items[ilocal] = .{
|
||||
.st_name = st_name,
|
||||
.st_info = elf.STT_FUNC,
|
||||
.st_other = 0,
|
||||
.st_shndx = @intCast(thunk.output_section_index),
|
||||
.st_value = @intCast(thunk.targetAddress(ref, elf_file)),
|
||||
.st_size = trampolineSize(cpu_arch),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) usize {
|
||||
return switch (cpu_arch) {
|
||||
.aarch64 => aarch64.trampoline_size,
|
||||
.x86_64, .riscv64 => unreachable,
|
||||
else => @panic("unhandled arch"),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
thunk: Thunk,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = thunk;
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
_ = writer;
|
||||
@compileError("do not format Thunk directly");
|
||||
}
|
||||
|
||||
pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(format2) {
|
||||
return .{ .data = .{
|
||||
.thunk = thunk,
|
||||
.elf_file = elf_file,
|
||||
} };
|
||||
}
|
||||
|
||||
const FormatContext = struct {
|
||||
thunk: Thunk,
|
||||
elf_file: *Elf,
|
||||
};
|
||||
|
||||
fn format2(
|
||||
ctx: FormatContext,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = options;
|
||||
_ = unused_fmt_string;
|
||||
const thunk = ctx.thunk;
|
||||
const elf_file = ctx.elf_file;
|
||||
try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) });
|
||||
for (thunk.symbols.keys()) |ref| {
|
||||
const sym = elf_file.symbol(ref).?;
|
||||
try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value });
|
||||
}
|
||||
}
|
||||
|
||||
pub const Index = u32;
|
||||
|
||||
const aarch64 = struct {
|
||||
fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void {
|
||||
for (thunk.symbols.keys(), 0..) |ref, i| {
|
||||
const sym = elf_file.symbol(ref).?;
|
||||
const saddr = thunk.address(elf_file) + @as(i64, @intCast(i * trampoline_size));
|
||||
const taddr = sym.address(.{}, elf_file);
|
||||
const pages = try util.calcNumberOfPages(saddr, taddr);
|
||||
try writer.writeInt(u32, Instruction.adrp(.x16, pages).toU32(), .little);
|
||||
const off: u12 = @truncate(@as(u64, @bitCast(taddr)));
|
||||
try writer.writeInt(u32, Instruction.add(.x16, .x16, off, false).toU32(), .little);
|
||||
try writer.writeInt(u32, Instruction.br(.x16).toU32(), .little);
|
||||
}
|
||||
}
|
||||
|
||||
const trampoline_size = 3 * @sizeOf(u32);
|
||||
|
||||
const util = @import("../aarch64.zig");
|
||||
const Instruction = util.Instruction;
|
||||
};
|
||||
|
||||
const assert = std.debug.assert;
|
||||
const elf = std.elf;
|
||||
const log = std.log.scoped(.link);
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const std = @import("std");
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const Elf = @import("../Elf.zig");
|
||||
const Symbol = @import("Symbol.zig");
|
||||
|
||||
const Thunk = @This();
|
||||
@ -1,234 +0,0 @@
|
||||
pub fn createThunks(shdr: *elf.Elf64_Shdr, shndx: u32, elf_file: *Elf) !void {
|
||||
const gpa = elf_file.base.comp.gpa;
|
||||
const cpu_arch = elf_file.getTarget().cpu.arch;
|
||||
const max_distance = maxAllowedDistance(cpu_arch);
|
||||
const atoms = elf_file.sections.items(.atom_list)[shndx].items;
|
||||
assert(atoms.len > 0);
|
||||
|
||||
for (atoms) |ref| {
|
||||
elf_file.atom(ref).?.value = -1;
|
||||
}
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < atoms.len) {
|
||||
const start = i;
|
||||
const start_atom = elf_file.atom(atoms[start]).?;
|
||||
assert(start_atom.alive);
|
||||
start_atom.value = try advance(shdr, start_atom.size, start_atom.alignment);
|
||||
i += 1;
|
||||
|
||||
while (i < atoms.len) : (i += 1) {
|
||||
const atom = elf_file.atom(atoms[i]).?;
|
||||
assert(atom.alive);
|
||||
if (@as(i64, @intCast(atom.alignment.forward(shdr.sh_size))) - start_atom.value >= max_distance)
|
||||
break;
|
||||
atom.value = try advance(shdr, atom.size, atom.alignment);
|
||||
}
|
||||
|
||||
// Insert a thunk at the group end
|
||||
const thunk_index = try elf_file.addThunk();
|
||||
const thunk = elf_file.thunk(thunk_index);
|
||||
thunk.output_section_index = shndx;
|
||||
|
||||
// Scan relocs in the group and create trampolines for any unreachable callsite
|
||||
for (atoms[start..i]) |ref| {
|
||||
const atom = elf_file.atom(ref).?;
|
||||
const file = atom.file(elf_file).?;
|
||||
log.debug("atom({}) {s}", .{ ref, atom.name(elf_file) });
|
||||
for (atom.relocs(elf_file)) |rel| {
|
||||
const is_reachable = switch (cpu_arch) {
|
||||
.aarch64 => aarch64.isReachable(atom, rel, elf_file),
|
||||
.x86_64, .riscv64 => unreachable,
|
||||
else => @panic("unsupported arch"),
|
||||
};
|
||||
if (is_reachable) continue;
|
||||
const target = file.resolveSymbol(rel.r_sym(), elf_file);
|
||||
try thunk.symbols.put(gpa, target, {});
|
||||
}
|
||||
atom.addExtra(.{ .thunk = thunk_index }, elf_file);
|
||||
}
|
||||
|
||||
thunk.value = try advance(shdr, thunk.size(elf_file), Atom.Alignment.fromNonzeroByteUnits(2));
|
||||
|
||||
log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(elf_file) });
|
||||
}
|
||||
}
|
||||
|
||||
fn advance(shdr: *elf.Elf64_Shdr, size: u64, alignment: Atom.Alignment) !i64 {
|
||||
const offset = alignment.forward(shdr.sh_size);
|
||||
const padding = offset - shdr.sh_size;
|
||||
shdr.sh_size += padding + size;
|
||||
shdr.sh_addralign = @max(shdr.sh_addralign, alignment.toByteUnits() orelse 1);
|
||||
return @intCast(offset);
|
||||
}
|
||||
|
||||
/// A branch will need an extender if its target is larger than
|
||||
/// `2^(jump_bits - 1) - margin` where margin is some arbitrary number.
|
||||
fn maxAllowedDistance(cpu_arch: std.Target.Cpu.Arch) u32 {
|
||||
return switch (cpu_arch) {
|
||||
.aarch64 => 0x500_000,
|
||||
.x86_64, .riscv64 => unreachable,
|
||||
else => @panic("unhandled arch"),
|
||||
};
|
||||
}
|
||||
|
||||
pub const Thunk = struct {
|
||||
value: i64 = 0,
|
||||
output_section_index: u32 = 0,
|
||||
symbols: std.AutoArrayHashMapUnmanaged(Elf.Ref, void) = .{},
|
||||
output_symtab_ctx: Elf.SymtabCtx = .{},
|
||||
|
||||
pub fn deinit(thunk: *Thunk, allocator: Allocator) void {
|
||||
thunk.symbols.deinit(allocator);
|
||||
}
|
||||
|
||||
pub fn size(thunk: Thunk, elf_file: *Elf) usize {
|
||||
const cpu_arch = elf_file.getTarget().cpu.arch;
|
||||
return thunk.symbols.keys().len * trampolineSize(cpu_arch);
|
||||
}
|
||||
|
||||
pub fn address(thunk: Thunk, elf_file: *Elf) i64 {
|
||||
const shdr = elf_file.sections.items(.shdr)[thunk.output_section_index];
|
||||
return @as(i64, @intCast(shdr.sh_addr)) + thunk.value;
|
||||
}
|
||||
|
||||
pub fn targetAddress(thunk: Thunk, ref: Elf.Ref, elf_file: *Elf) i64 {
|
||||
const cpu_arch = elf_file.getTarget().cpu.arch;
|
||||
return thunk.address(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(ref).? * trampolineSize(cpu_arch)));
|
||||
}
|
||||
|
||||
pub fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void {
|
||||
switch (elf_file.getTarget().cpu.arch) {
|
||||
.aarch64 => try aarch64.write(thunk, elf_file, writer),
|
||||
.x86_64, .riscv64 => unreachable,
|
||||
else => @panic("unhandled arch"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calcSymtabSize(thunk: *Thunk, elf_file: *Elf) void {
|
||||
thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len));
|
||||
for (thunk.symbols.keys()) |ref| {
|
||||
const sym = elf_file.symbol(ref).?;
|
||||
thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.name(elf_file).len + "$thunk".len + 1));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeSymtab(thunk: Thunk, elf_file: *Elf) void {
|
||||
const cpu_arch = elf_file.getTarget().cpu.arch;
|
||||
for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| {
|
||||
const sym = elf_file.symbol(ref).?;
|
||||
const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
|
||||
elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
|
||||
elf_file.strtab.appendSliceAssumeCapacity("$thunk");
|
||||
elf_file.strtab.appendAssumeCapacity(0);
|
||||
elf_file.symtab.items[ilocal] = .{
|
||||
.st_name = st_name,
|
||||
.st_info = elf.STT_FUNC,
|
||||
.st_other = 0,
|
||||
.st_shndx = @intCast(thunk.output_section_index),
|
||||
.st_value = @intCast(thunk.targetAddress(ref, elf_file)),
|
||||
.st_size = trampolineSize(cpu_arch),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) usize {
|
||||
return switch (cpu_arch) {
|
||||
.aarch64 => aarch64.trampoline_size,
|
||||
.x86_64, .riscv64 => unreachable,
|
||||
else => @panic("unhandled arch"),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
thunk: Thunk,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = thunk;
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
_ = writer;
|
||||
@compileError("do not format Thunk directly");
|
||||
}
|
||||
|
||||
pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(format2) {
|
||||
return .{ .data = .{
|
||||
.thunk = thunk,
|
||||
.elf_file = elf_file,
|
||||
} };
|
||||
}
|
||||
|
||||
const FormatContext = struct {
|
||||
thunk: Thunk,
|
||||
elf_file: *Elf,
|
||||
};
|
||||
|
||||
fn format2(
|
||||
ctx: FormatContext,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = options;
|
||||
_ = unused_fmt_string;
|
||||
const thunk = ctx.thunk;
|
||||
const elf_file = ctx.elf_file;
|
||||
try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) });
|
||||
for (thunk.symbols.keys()) |ref| {
|
||||
const sym = elf_file.symbol(ref).?;
|
||||
try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value });
|
||||
}
|
||||
}
|
||||
|
||||
pub const Index = u32;
|
||||
};
|
||||
|
||||
const aarch64 = struct {
|
||||
fn isReachable(atom: *const Atom, rel: elf.Elf64_Rela, elf_file: *Elf) bool {
|
||||
const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type());
|
||||
if (r_type != .CALL26 and r_type != .JUMP26) return true;
|
||||
const file = atom.file(elf_file).?;
|
||||
const target_ref = file.resolveSymbol(rel.r_sym(), elf_file);
|
||||
const target = elf_file.symbol(target_ref).?;
|
||||
if (target.flags.has_plt) return false;
|
||||
if (atom.output_section_index != target.output_section_index) return false;
|
||||
const target_atom = target.atom(elf_file).?;
|
||||
if (target_atom.value == -1) return false;
|
||||
const saddr = atom.address(elf_file) + @as(i64, @intCast(rel.r_offset));
|
||||
const taddr = target.address(.{}, elf_file);
|
||||
_ = math.cast(i28, taddr + rel.r_addend - saddr) orelse return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void {
|
||||
for (thunk.symbols.keys(), 0..) |ref, i| {
|
||||
const sym = elf_file.symbol(ref).?;
|
||||
const saddr = thunk.address(elf_file) + @as(i64, @intCast(i * trampoline_size));
|
||||
const taddr = sym.address(.{}, elf_file);
|
||||
const pages = try util.calcNumberOfPages(saddr, taddr);
|
||||
try writer.writeInt(u32, Instruction.adrp(.x16, pages).toU32(), .little);
|
||||
const off: u12 = @truncate(@as(u64, @bitCast(taddr)));
|
||||
try writer.writeInt(u32, Instruction.add(.x16, .x16, off, false).toU32(), .little);
|
||||
try writer.writeInt(u32, Instruction.br(.x16).toU32(), .little);
|
||||
}
|
||||
}
|
||||
|
||||
const trampoline_size = 3 * @sizeOf(u32);
|
||||
|
||||
const util = @import("../aarch64.zig");
|
||||
const Instruction = util.Instruction;
|
||||
};
|
||||
|
||||
const assert = std.debug.assert;
|
||||
const elf = std.elf;
|
||||
const log = std.log.scoped(.link);
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const std = @import("std");
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const Elf = @import("../Elf.zig");
|
||||
const Symbol = @import("Symbol.zig");
|
||||
@ -64,10 +64,10 @@ stubs_helper: StubsHelperSection = .{},
|
||||
objc_stubs: ObjcStubsSection = .{},
|
||||
la_symbol_ptr: LaSymbolPtrSection = .{},
|
||||
tlv_ptr: TlvPtrSection = .{},
|
||||
rebase: Rebase = .{},
|
||||
bind: Bind = .{},
|
||||
weak_bind: WeakBind = .{},
|
||||
lazy_bind: LazyBind = .{},
|
||||
rebase_section: Rebase = .{},
|
||||
bind_section: Bind = .{},
|
||||
weak_bind_section: WeakBind = .{},
|
||||
lazy_bind_section: LazyBind = .{},
|
||||
export_trie: ExportTrie = .{},
|
||||
unwind_info: UnwindInfo = .{},
|
||||
data_in_code: DataInCode = .{},
|
||||
@ -324,10 +324,10 @@ pub fn deinit(self: *MachO) void {
|
||||
self.stubs.deinit(gpa);
|
||||
self.objc_stubs.deinit(gpa);
|
||||
self.tlv_ptr.deinit(gpa);
|
||||
self.rebase.deinit(gpa);
|
||||
self.bind.deinit(gpa);
|
||||
self.weak_bind.deinit(gpa);
|
||||
self.lazy_bind.deinit(gpa);
|
||||
self.rebase_section.deinit(gpa);
|
||||
self.bind_section.deinit(gpa);
|
||||
self.weak_bind_section.deinit(gpa);
|
||||
self.lazy_bind_section.deinit(gpa);
|
||||
self.export_trie.deinit(gpa);
|
||||
self.unwind_info.deinit(gpa);
|
||||
self.data_in_code.deinit(gpa);
|
||||
@ -2005,7 +2005,7 @@ fn calcSectionSizeWorker(self: *MachO, sect_id: u8) void {
|
||||
fn createThunksWorker(self: *MachO, sect_id: u8) void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
thunks.createThunks(sect_id, self) catch |err| {
|
||||
self.createThunks(sect_id) catch |err| {
|
||||
const header = self.sections.items(.header)[sect_id];
|
||||
self.reportUnexpectedError("failed to create thunks and calculate size of section '{s},{s}': {s}", .{
|
||||
header.segName(),
|
||||
@ -2562,7 +2562,7 @@ fn updateLazyBindSizeWorker(self: *MachO) void {
|
||||
defer tracy.end();
|
||||
const doWork = struct {
|
||||
fn doWork(macho_file: *MachO) !void {
|
||||
try macho_file.lazy_bind.updateSize(macho_file);
|
||||
try macho_file.lazy_bind_section.updateSize(macho_file);
|
||||
const sect_id = macho_file.stubs_helper_sect_index.?;
|
||||
const out = &macho_file.sections.items(.out)[sect_id];
|
||||
var stream = std.io.fixedBufferStream(out.items);
|
||||
@ -2585,9 +2585,9 @@ pub fn updateLinkeditSizeWorker(self: *MachO, tag: enum {
|
||||
data_in_code,
|
||||
}) void {
|
||||
const res = switch (tag) {
|
||||
.rebase => self.rebase.updateSize(self),
|
||||
.bind => self.bind.updateSize(self),
|
||||
.weak_bind => self.weak_bind.updateSize(self),
|
||||
.rebase => self.rebase_section.updateSize(self),
|
||||
.bind => self.bind_section.updateSize(self),
|
||||
.weak_bind => self.weak_bind_section.updateSize(self),
|
||||
.export_trie => self.export_trie.updateSize(self),
|
||||
.data_in_code => self.data_in_code.updateSize(self),
|
||||
};
|
||||
@ -2640,13 +2640,13 @@ fn writeDyldInfo(self: *MachO) !void {
|
||||
var stream = std.io.fixedBufferStream(buffer);
|
||||
const writer = stream.writer();
|
||||
|
||||
try self.rebase.write(writer);
|
||||
try self.rebase_section.write(writer);
|
||||
try stream.seekTo(cmd.bind_off - base_off);
|
||||
try self.bind.write(writer);
|
||||
try self.bind_section.write(writer);
|
||||
try stream.seekTo(cmd.weak_bind_off - base_off);
|
||||
try self.weak_bind.write(writer);
|
||||
try self.weak_bind_section.write(writer);
|
||||
try stream.seekTo(cmd.lazy_bind_off - base_off);
|
||||
try self.lazy_bind.write(writer);
|
||||
try self.lazy_bind_section.write(writer);
|
||||
try stream.seekTo(cmd.export_off - base_off);
|
||||
try self.export_trie.write(writer);
|
||||
try self.base.file.?.pwriteAll(buffer, cmd.rebase_off);
|
||||
@ -4602,7 +4602,6 @@ const load_commands = @import("MachO/load_commands.zig");
|
||||
const relocatable = @import("MachO/relocatable.zig");
|
||||
const tapi = @import("tapi.zig");
|
||||
const target_util = @import("../target.zig");
|
||||
const thunks = @import("MachO/thunks.zig");
|
||||
const trace = @import("../tracy.zig").trace;
|
||||
const synthetic = @import("MachO/synthetic.zig");
|
||||
|
||||
@ -4641,7 +4640,7 @@ const StringTable = @import("StringTable.zig");
|
||||
const StubsSection = synthetic.StubsSection;
|
||||
const StubsHelperSection = synthetic.StubsHelperSection;
|
||||
const Symbol = @import("MachO/Symbol.zig");
|
||||
const Thunk = thunks.Thunk;
|
||||
const Thunk = @import("MachO/Thunk.zig");
|
||||
const TlvPtrSection = synthetic.TlvPtrSection;
|
||||
const Value = @import("../Value.zig");
|
||||
const UnwindInfo = @import("MachO/UnwindInfo.zig");
|
||||
@ -5292,3 +5291,96 @@ pub const KernE = enum(u32) {
|
||||
NOT_FOUND = 56,
|
||||
_,
|
||||
};
|
||||
|
||||
fn createThunks(macho_file: *MachO, sect_id: u8) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const gpa = macho_file.base.comp.gpa;
|
||||
const slice = macho_file.sections.slice();
|
||||
const header = &slice.items(.header)[sect_id];
|
||||
const thnks = &slice.items(.thunks)[sect_id];
|
||||
const atoms = slice.items(.atoms)[sect_id].items;
|
||||
assert(atoms.len > 0);
|
||||
|
||||
for (atoms) |ref| {
|
||||
ref.getAtom(macho_file).?.value = @bitCast(@as(i64, -1));
|
||||
}
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < atoms.len) {
|
||||
const start = i;
|
||||
const start_atom = atoms[start].getAtom(macho_file).?;
|
||||
assert(start_atom.isAlive());
|
||||
start_atom.value = advanceSection(header, start_atom.size, start_atom.alignment);
|
||||
i += 1;
|
||||
|
||||
while (i < atoms.len and
|
||||
header.size - start_atom.value < max_allowed_distance) : (i += 1)
|
||||
{
|
||||
const atom = atoms[i].getAtom(macho_file).?;
|
||||
assert(atom.isAlive());
|
||||
atom.value = advanceSection(header, atom.size, atom.alignment);
|
||||
}
|
||||
|
||||
// Insert a thunk at the group end
|
||||
const thunk_index = try macho_file.addThunk();
|
||||
const thunk = macho_file.getThunk(thunk_index);
|
||||
thunk.out_n_sect = sect_id;
|
||||
try thnks.append(gpa, thunk_index);
|
||||
|
||||
// Scan relocs in the group and create trampolines for any unreachable callsite
|
||||
try scanThunkRelocs(thunk_index, gpa, atoms[start..i], macho_file);
|
||||
thunk.value = advanceSection(header, thunk.size(), .@"4");
|
||||
|
||||
log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(macho_file) });
|
||||
}
|
||||
}
|
||||
|
||||
fn advanceSection(sect: *macho.section_64, adv_size: u64, alignment: Atom.Alignment) u64 {
|
||||
const offset = alignment.forward(sect.size);
|
||||
const padding = offset - sect.size;
|
||||
sect.size += padding + adv_size;
|
||||
sect.@"align" = @max(sect.@"align", alignment.toLog2Units());
|
||||
return offset;
|
||||
}
|
||||
|
||||
fn scanThunkRelocs(thunk_index: Thunk.Index, gpa: Allocator, atoms: []const MachO.Ref, macho_file: *MachO) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const thunk = macho_file.getThunk(thunk_index);
|
||||
|
||||
for (atoms) |ref| {
|
||||
const atom = ref.getAtom(macho_file).?;
|
||||
log.debug("atom({d}) {s}", .{ atom.atom_index, atom.getName(macho_file) });
|
||||
for (atom.getRelocs(macho_file)) |rel| {
|
||||
if (rel.type != .branch) continue;
|
||||
if (isReachable(atom, rel, macho_file)) continue;
|
||||
try thunk.symbols.put(gpa, rel.getTargetSymbolRef(atom.*, macho_file), {});
|
||||
}
|
||||
atom.addExtra(.{ .thunk = thunk_index }, macho_file);
|
||||
}
|
||||
}
|
||||
|
||||
fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
|
||||
const target = rel.getTargetSymbol(atom.*, macho_file);
|
||||
if (target.getSectionFlags().stubs or target.getSectionFlags().objc_stubs) return false;
|
||||
if (atom.out_n_sect != target.getOutputSectionIndex(macho_file)) return false;
|
||||
const target_atom = target.getAtom(macho_file).?;
|
||||
if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false;
|
||||
const saddr = @as(i64, @intCast(atom.getAddress(macho_file))) + @as(i64, @intCast(rel.offset - atom.off));
|
||||
const taddr: i64 = @intCast(rel.getTargetAddress(atom.*, macho_file));
|
||||
_ = math.cast(i28, taddr + rel.addend - saddr) orelse return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Branch instruction has 26 bits immediate but is 4 byte aligned.
|
||||
const jump_bits = @bitSizeOf(i28);
|
||||
const max_distance = (1 << (jump_bits - 1));
|
||||
|
||||
/// A branch will need an extender if its target is larger than
|
||||
/// `2^(jump_bits - 1) - margin` where margin is some arbitrary number.
|
||||
/// mold uses 5MiB margin, while ld64 uses 4MiB margin. We will follow mold
|
||||
/// and assume margin to be 5MiB.
|
||||
const max_allowed_distance = max_distance - 0x500_000;
|
||||
|
||||
@ -1220,6 +1220,6 @@ const MachO = @import("../MachO.zig");
|
||||
const Object = @import("Object.zig");
|
||||
const Relocation = @import("Relocation.zig");
|
||||
const Symbol = @import("Symbol.zig");
|
||||
const Thunk = @import("thunks.zig").Thunk;
|
||||
const Thunk = @import("Thunk.zig");
|
||||
const UnwindInfo = @import("UnwindInfo.zig");
|
||||
const dev = @import("../../dev.zig");
|
||||
|
||||
125
src/link/MachO/Thunk.zig
Normal file
125
src/link/MachO/Thunk.zig
Normal file
@ -0,0 +1,125 @@
|
||||
value: u64 = 0,
|
||||
out_n_sect: u8 = 0,
|
||||
symbols: std.AutoArrayHashMapUnmanaged(MachO.Ref, void) = .{},
|
||||
output_symtab_ctx: MachO.SymtabCtx = .{},
|
||||
|
||||
pub fn deinit(thunk: *Thunk, allocator: Allocator) void {
|
||||
thunk.symbols.deinit(allocator);
|
||||
}
|
||||
|
||||
pub fn size(thunk: Thunk) usize {
|
||||
return thunk.symbols.keys().len * trampoline_size;
|
||||
}
|
||||
|
||||
pub fn getAddress(thunk: Thunk, macho_file: *MachO) u64 {
|
||||
const header = macho_file.sections.items(.header)[thunk.out_n_sect];
|
||||
return header.addr + thunk.value;
|
||||
}
|
||||
|
||||
pub fn getTargetAddress(thunk: Thunk, ref: MachO.Ref, macho_file: *MachO) u64 {
|
||||
return thunk.getAddress(macho_file) + thunk.symbols.getIndex(ref).? * trampoline_size;
|
||||
}
|
||||
|
||||
pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void {
|
||||
for (thunk.symbols.keys(), 0..) |ref, i| {
|
||||
const sym = ref.getSymbol(macho_file).?;
|
||||
const saddr = thunk.getAddress(macho_file) + i * trampoline_size;
|
||||
const taddr = sym.getAddress(.{}, macho_file);
|
||||
const pages = try aarch64.calcNumberOfPages(@intCast(saddr), @intCast(taddr));
|
||||
try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);
|
||||
const off: u12 = @truncate(taddr);
|
||||
try writer.writeInt(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32(), .little);
|
||||
try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calcSymtabSize(thunk: *Thunk, macho_file: *MachO) void {
|
||||
thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len));
|
||||
for (thunk.symbols.keys()) |ref| {
|
||||
const sym = ref.getSymbol(macho_file).?;
|
||||
thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + "__thunk".len + 1));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeSymtab(thunk: Thunk, macho_file: *MachO, ctx: anytype) void {
|
||||
var n_strx = thunk.output_symtab_ctx.stroff;
|
||||
for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| {
|
||||
const sym = ref.getSymbol(macho_file).?;
|
||||
const name = sym.getName(macho_file);
|
||||
const out_sym = &ctx.symtab.items[ilocal];
|
||||
out_sym.n_strx = n_strx;
|
||||
@memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
|
||||
n_strx += @intCast(name.len);
|
||||
@memcpy(ctx.strtab.items[n_strx..][0.."__thunk".len], "__thunk");
|
||||
n_strx += @intCast("__thunk".len);
|
||||
ctx.strtab.items[n_strx] = 0;
|
||||
n_strx += 1;
|
||||
out_sym.n_type = macho.N_SECT;
|
||||
out_sym.n_sect = @intCast(thunk.out_n_sect + 1);
|
||||
out_sym.n_value = @intCast(thunk.getTargetAddress(ref, macho_file));
|
||||
out_sym.n_desc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
thunk: Thunk,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = thunk;
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
_ = writer;
|
||||
@compileError("do not format Thunk directly");
|
||||
}
|
||||
|
||||
pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(format2) {
|
||||
return .{ .data = .{
|
||||
.thunk = thunk,
|
||||
.macho_file = macho_file,
|
||||
} };
|
||||
}
|
||||
|
||||
const FormatContext = struct {
|
||||
thunk: Thunk,
|
||||
macho_file: *MachO,
|
||||
};
|
||||
|
||||
fn format2(
|
||||
ctx: FormatContext,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = options;
|
||||
_ = unused_fmt_string;
|
||||
const thunk = ctx.thunk;
|
||||
const macho_file = ctx.macho_file;
|
||||
try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() });
|
||||
for (thunk.symbols.keys()) |ref| {
|
||||
const sym = ref.getSymbol(macho_file).?;
|
||||
try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value });
|
||||
}
|
||||
}
|
||||
|
||||
const trampoline_size = 3 * @sizeOf(u32);
|
||||
|
||||
pub const Index = u32;
|
||||
|
||||
const aarch64 = @import("../aarch64.zig");
|
||||
const assert = std.debug.assert;
|
||||
const log = std.log.scoped(.link);
|
||||
const macho = std.macho;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const std = @import("std");
|
||||
const trace = @import("../../tracy.zig").trace;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const MachO = @import("../MachO.zig");
|
||||
const Relocation = @import("Relocation.zig");
|
||||
const Symbol = @import("Symbol.zig");
|
||||
|
||||
const Thunk = @This();
|
||||
@ -204,7 +204,7 @@ pub const StubsHelperSection = struct {
|
||||
for (macho_file.stubs.symbols.items) |ref| {
|
||||
const sym = ref.getSymbol(macho_file).?;
|
||||
if (sym.flags.weak) continue;
|
||||
const offset = macho_file.lazy_bind.offsets.items[idx];
|
||||
const offset = macho_file.lazy_bind_section.offsets.items[idx];
|
||||
const source: i64 = @intCast(sect.addr + preamble_size + entry_size * idx);
|
||||
const target: i64 = @intCast(sect.addr);
|
||||
switch (cpu_arch) {
|
||||
|
||||
@ -1,218 +0,0 @@
|
||||
pub fn createThunks(sect_id: u8, macho_file: *MachO) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const gpa = macho_file.base.comp.gpa;
|
||||
const slice = macho_file.sections.slice();
|
||||
const header = &slice.items(.header)[sect_id];
|
||||
const thnks = &slice.items(.thunks)[sect_id];
|
||||
const atoms = slice.items(.atoms)[sect_id].items;
|
||||
assert(atoms.len > 0);
|
||||
|
||||
for (atoms) |ref| {
|
||||
ref.getAtom(macho_file).?.value = @bitCast(@as(i64, -1));
|
||||
}
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < atoms.len) {
|
||||
const start = i;
|
||||
const start_atom = atoms[start].getAtom(macho_file).?;
|
||||
assert(start_atom.isAlive());
|
||||
start_atom.value = advance(header, start_atom.size, start_atom.alignment);
|
||||
i += 1;
|
||||
|
||||
while (i < atoms.len and
|
||||
header.size - start_atom.value < max_allowed_distance) : (i += 1)
|
||||
{
|
||||
const atom = atoms[i].getAtom(macho_file).?;
|
||||
assert(atom.isAlive());
|
||||
atom.value = advance(header, atom.size, atom.alignment);
|
||||
}
|
||||
|
||||
// Insert a thunk at the group end
|
||||
const thunk_index = try macho_file.addThunk();
|
||||
const thunk = macho_file.getThunk(thunk_index);
|
||||
thunk.out_n_sect = sect_id;
|
||||
try thnks.append(gpa, thunk_index);
|
||||
|
||||
// Scan relocs in the group and create trampolines for any unreachable callsite
|
||||
try scanRelocs(thunk_index, gpa, atoms[start..i], macho_file);
|
||||
thunk.value = advance(header, thunk.size(), .@"4");
|
||||
|
||||
log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(macho_file) });
|
||||
}
|
||||
}
|
||||
|
||||
fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) u64 {
|
||||
const offset = alignment.forward(sect.size);
|
||||
const padding = offset - sect.size;
|
||||
sect.size += padding + size;
|
||||
sect.@"align" = @max(sect.@"align", alignment.toLog2Units());
|
||||
return offset;
|
||||
}
|
||||
|
||||
fn scanRelocs(thunk_index: Thunk.Index, gpa: Allocator, atoms: []const MachO.Ref, macho_file: *MachO) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const thunk = macho_file.getThunk(thunk_index);
|
||||
|
||||
for (atoms) |ref| {
|
||||
const atom = ref.getAtom(macho_file).?;
|
||||
log.debug("atom({d}) {s}", .{ atom.atom_index, atom.getName(macho_file) });
|
||||
for (atom.getRelocs(macho_file)) |rel| {
|
||||
if (rel.type != .branch) continue;
|
||||
if (isReachable(atom, rel, macho_file)) continue;
|
||||
try thunk.symbols.put(gpa, rel.getTargetSymbolRef(atom.*, macho_file), {});
|
||||
}
|
||||
atom.addExtra(.{ .thunk = thunk_index }, macho_file);
|
||||
}
|
||||
}
|
||||
|
||||
fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
|
||||
const target = rel.getTargetSymbol(atom.*, macho_file);
|
||||
if (target.getSectionFlags().stubs or target.getSectionFlags().objc_stubs) return false;
|
||||
if (atom.out_n_sect != target.getOutputSectionIndex(macho_file)) return false;
|
||||
const target_atom = target.getAtom(macho_file).?;
|
||||
if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false;
|
||||
const saddr = @as(i64, @intCast(atom.getAddress(macho_file))) + @as(i64, @intCast(rel.offset - atom.off));
|
||||
const taddr: i64 = @intCast(rel.getTargetAddress(atom.*, macho_file));
|
||||
_ = math.cast(i28, taddr + rel.addend - saddr) orelse return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
pub const Thunk = struct {
|
||||
value: u64 = 0,
|
||||
out_n_sect: u8 = 0,
|
||||
symbols: std.AutoArrayHashMapUnmanaged(MachO.Ref, void) = .{},
|
||||
output_symtab_ctx: MachO.SymtabCtx = .{},
|
||||
|
||||
pub fn deinit(thunk: *Thunk, allocator: Allocator) void {
|
||||
thunk.symbols.deinit(allocator);
|
||||
}
|
||||
|
||||
pub fn size(thunk: Thunk) usize {
|
||||
return thunk.symbols.keys().len * trampoline_size;
|
||||
}
|
||||
|
||||
pub fn getAddress(thunk: Thunk, macho_file: *MachO) u64 {
|
||||
const header = macho_file.sections.items(.header)[thunk.out_n_sect];
|
||||
return header.addr + thunk.value;
|
||||
}
|
||||
|
||||
pub fn getTargetAddress(thunk: Thunk, ref: MachO.Ref, macho_file: *MachO) u64 {
|
||||
return thunk.getAddress(macho_file) + thunk.symbols.getIndex(ref).? * trampoline_size;
|
||||
}
|
||||
|
||||
pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void {
|
||||
for (thunk.symbols.keys(), 0..) |ref, i| {
|
||||
const sym = ref.getSymbol(macho_file).?;
|
||||
const saddr = thunk.getAddress(macho_file) + i * trampoline_size;
|
||||
const taddr = sym.getAddress(.{}, macho_file);
|
||||
const pages = try aarch64.calcNumberOfPages(@intCast(saddr), @intCast(taddr));
|
||||
try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);
|
||||
const off: u12 = @truncate(taddr);
|
||||
try writer.writeInt(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32(), .little);
|
||||
try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calcSymtabSize(thunk: *Thunk, macho_file: *MachO) void {
|
||||
thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len));
|
||||
for (thunk.symbols.keys()) |ref| {
|
||||
const sym = ref.getSymbol(macho_file).?;
|
||||
thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + "__thunk".len + 1));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeSymtab(thunk: Thunk, macho_file: *MachO, ctx: anytype) void {
|
||||
var n_strx = thunk.output_symtab_ctx.stroff;
|
||||
for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| {
|
||||
const sym = ref.getSymbol(macho_file).?;
|
||||
const name = sym.getName(macho_file);
|
||||
const out_sym = &ctx.symtab.items[ilocal];
|
||||
out_sym.n_strx = n_strx;
|
||||
@memcpy(ctx.strtab.items[n_strx..][0..name.len], name);
|
||||
n_strx += @intCast(name.len);
|
||||
@memcpy(ctx.strtab.items[n_strx..][0.."__thunk".len], "__thunk");
|
||||
n_strx += @intCast("__thunk".len);
|
||||
ctx.strtab.items[n_strx] = 0;
|
||||
n_strx += 1;
|
||||
out_sym.n_type = macho.N_SECT;
|
||||
out_sym.n_sect = @intCast(thunk.out_n_sect + 1);
|
||||
out_sym.n_value = @intCast(thunk.getTargetAddress(ref, macho_file));
|
||||
out_sym.n_desc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
thunk: Thunk,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = thunk;
|
||||
_ = unused_fmt_string;
|
||||
_ = options;
|
||||
_ = writer;
|
||||
@compileError("do not format Thunk directly");
|
||||
}
|
||||
|
||||
pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(format2) {
|
||||
return .{ .data = .{
|
||||
.thunk = thunk,
|
||||
.macho_file = macho_file,
|
||||
} };
|
||||
}
|
||||
|
||||
const FormatContext = struct {
|
||||
thunk: Thunk,
|
||||
macho_file: *MachO,
|
||||
};
|
||||
|
||||
fn format2(
|
||||
ctx: FormatContext,
|
||||
comptime unused_fmt_string: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = options;
|
||||
_ = unused_fmt_string;
|
||||
const thunk = ctx.thunk;
|
||||
const macho_file = ctx.macho_file;
|
||||
try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() });
|
||||
for (thunk.symbols.keys()) |ref| {
|
||||
const sym = ref.getSymbol(macho_file).?;
|
||||
try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value });
|
||||
}
|
||||
}
|
||||
|
||||
const trampoline_size = 3 * @sizeOf(u32);
|
||||
|
||||
pub const Index = u32;
|
||||
};
|
||||
|
||||
/// Branch instruction has 26 bits immediate but is 4 byte aligned.
|
||||
const jump_bits = @bitSizeOf(i28);
|
||||
const max_distance = (1 << (jump_bits - 1));
|
||||
|
||||
/// A branch will need an extender if its target is larger than
|
||||
/// `2^(jump_bits - 1) - margin` where margin is some arbitrary number.
|
||||
/// mold uses 5MiB margin, while ld64 uses 4MiB margin. We will follow mold
|
||||
/// and assume margin to be 5MiB.
|
||||
const max_allowed_distance = max_distance - 0x500_000;
|
||||
|
||||
const aarch64 = @import("../aarch64.zig");
|
||||
const assert = std.debug.assert;
|
||||
const log = std.log.scoped(.link);
|
||||
const macho = std.macho;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const std = @import("std");
|
||||
const trace = @import("../../tracy.zig").trace;
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
const Atom = @import("Atom.zig");
|
||||
const MachO = @import("../MachO.zig");
|
||||
const Relocation = @import("Relocation.zig");
|
||||
const Symbol = @import("Symbol.zig");
|
||||
Loading…
x
Reference in New Issue
Block a user