| author | |
| committer | |
| log | c62487da76b08a0dfb69fbf76501250ca065c140 |
| tree | 1ef46d45f3ff9edaade3805d9ab5a9910cb8571b |
| parent | ba8d3f69ca65738f27deea43e795f5e787a061f2 |
| signature | Commit is signed but in an unrecognized format. |
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.19 files changed, 604 insertions(+), 623 deletions(-)
CMakeLists.txt+2-2| ... | ... | @@ -613,7 +613,7 @@ set(ZIG_STAGE2_SOURCES |
| 613 | 613 | src/link/Elf/relocatable.zig |
| 614 | 614 | src/link/Elf/relocation.zig |
| 615 | 615 | src/link/Elf/synthetic_sections.zig |
| 616 | src/link/Elf/thunks.zig | |
| 616 | src/link/Elf/Thunk.zig | |
| 617 | 617 | src/link/MachO.zig |
| 618 | 618 | src/link/MachO/Archive.zig |
| 619 | 619 | src/link/MachO/Atom.zig |
| ... | ... | @@ -638,7 +638,7 @@ set(ZIG_STAGE2_SOURCES |
| 638 | 638 | src/link/MachO/load_commands.zig |
| 639 | 639 | src/link/MachO/relocatable.zig |
| 640 | 640 | src/link/MachO/synthetic.zig |
| 641 | src/link/MachO/thunks.zig | |
| 641 | src/link/MachO/Thunk.zig | |
| 642 | 642 | src/link/MachO/uuid.zig |
| 643 | 643 | src/link/NvPtx.zig |
| 644 | 644 | src/link/Plan9.zig |
src/arch/aarch64/bits.zig+4-4| ... | ... | @@ -1069,7 +1069,7 @@ pub const Instruction = union(enum) { |
| 1069 | 1069 | }; |
| 1070 | 1070 | } |
| 1071 | 1071 | |
| 1072 | fn bitfield( | |
| 1072 | fn initBitfield( | |
| 1073 | 1073 | opc: u2, |
| 1074 | 1074 | n: u1, |
| 1075 | 1075 | rd: Register, |
| ... | ... | @@ -1579,7 +1579,7 @@ pub const Instruction = union(enum) { |
| 1579 | 1579 | 64 => 0b1, |
| 1580 | 1580 | else => unreachable, // unexpected register size |
| 1581 | 1581 | }; |
| 1582 | return bitfield(0b00, n, rd, rn, immr, imms); | |
| 1582 | return initBitfield(0b00, n, rd, rn, immr, imms); | |
| 1583 | 1583 | } |
| 1584 | 1584 | |
| 1585 | 1585 | pub fn bfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction { |
| ... | ... | @@ -1588,7 +1588,7 @@ pub const Instruction = union(enum) { |
| 1588 | 1588 | 64 => 0b1, |
| 1589 | 1589 | else => unreachable, // unexpected register size |
| 1590 | 1590 | }; |
| 1591 | return bitfield(0b01, n, rd, rn, immr, imms); | |
| 1591 | return initBitfield(0b01, n, rd, rn, immr, imms); | |
| 1592 | 1592 | } |
| 1593 | 1593 | |
| 1594 | 1594 | pub fn ubfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction { |
| ... | ... | @@ -1597,7 +1597,7 @@ pub const Instruction = union(enum) { |
| 1597 | 1597 | 64 => 0b1, |
| 1598 | 1598 | else => unreachable, // unexpected register size |
| 1599 | 1599 | }; |
| 1600 | return bitfield(0b10, n, rd, rn, immr, imms); | |
| 1600 | return initBitfield(0b10, n, rd, rn, immr, imms); | |
| 1601 | 1601 | } |
| 1602 | 1602 | |
| 1603 | 1603 | pub fn asrImmediate(rd: Register, rn: Register, shift: u6) Instruction { |
src/arch/arm/bits.zig+10-10| ... | ... | @@ -662,7 +662,7 @@ pub const Instruction = union(enum) { |
| 662 | 662 | }; |
| 663 | 663 | } |
| 664 | 664 | |
| 665 | fn multiply( | |
| 665 | fn initMultiply( | |
| 666 | 666 | cond: Condition, |
| 667 | 667 | set_cond: u1, |
| 668 | 668 | rd: Register, |
| ... | ... | @@ -864,7 +864,7 @@ pub const Instruction = union(enum) { |
| 864 | 864 | }; |
| 865 | 865 | } |
| 866 | 866 | |
| 867 | fn branch(cond: Condition, offset: i26, link: u1) Instruction { | |
| 867 | fn initBranch(cond: Condition, offset: i26, link: u1) Instruction { | |
| 868 | 868 | return Instruction{ |
| 869 | 869 | .branch = .{ |
| 870 | 870 | .cond = @intFromEnum(cond), |
| ... | ... | @@ -900,7 +900,7 @@ pub const Instruction = union(enum) { |
| 900 | 900 | }; |
| 901 | 901 | } |
| 902 | 902 | |
| 903 | fn breakpoint(imm: u16) Instruction { | |
| 903 | fn initBreakpoint(imm: u16) Instruction { | |
| 904 | 904 | return Instruction{ |
| 905 | 905 | .breakpoint = .{ |
| 906 | 906 | .imm12 = @as(u12, @truncate(imm >> 4)), |
| ... | ... | @@ -1087,19 +1087,19 @@ pub const Instruction = union(enum) { |
| 1087 | 1087 | // Multiply |
| 1088 | 1088 | |
| 1089 | 1089 | pub fn mul(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { |
| 1090 | return multiply(cond, 0, rd, rn, rm, null); | |
| 1090 | return initMultiply(cond, 0, rd, rn, rm, null); | |
| 1091 | 1091 | } |
| 1092 | 1092 | |
| 1093 | 1093 | pub fn muls(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { |
| 1094 | return multiply(cond, 1, rd, rn, rm, null); | |
| 1094 | return initMultiply(cond, 1, rd, rn, rm, null); | |
| 1095 | 1095 | } |
| 1096 | 1096 | |
| 1097 | 1097 | pub fn mla(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction { |
| 1098 | return multiply(cond, 0, rd, rn, rm, ra); | |
| 1098 | return initMultiply(cond, 0, rd, rn, rm, ra); | |
| 1099 | 1099 | } |
| 1100 | 1100 | |
| 1101 | 1101 | pub fn mlas(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction { |
| 1102 | return multiply(cond, 1, rd, rn, rm, ra); | |
| 1102 | return initMultiply(cond, 1, rd, rn, rm, ra); | |
| 1103 | 1103 | } |
| 1104 | 1104 | |
| 1105 | 1105 | // Multiply long |
| ... | ... | @@ -1261,11 +1261,11 @@ pub const Instruction = union(enum) { |
| 1261 | 1261 | // Branch |
| 1262 | 1262 | |
| 1263 | 1263 | pub fn b(cond: Condition, offset: i26) Instruction { |
| 1264 | return branch(cond, offset, 0); | |
| 1264 | return initBranch(cond, offset, 0); | |
| 1265 | 1265 | } |
| 1266 | 1266 | |
| 1267 | 1267 | pub fn bl(cond: Condition, offset: i26) Instruction { |
| 1268 | return branch(cond, offset, 1); | |
| 1268 | return initBranch(cond, offset, 1); | |
| 1269 | 1269 | } |
| 1270 | 1270 | |
| 1271 | 1271 | // Branch and exchange |
| ... | ... | @@ -1289,7 +1289,7 @@ pub const Instruction = union(enum) { |
| 1289 | 1289 | // Breakpoint |
| 1290 | 1290 | |
| 1291 | 1291 | pub fn bkpt(imm: u16) Instruction { |
| 1292 | return breakpoint(imm); | |
| 1292 | return initBreakpoint(imm); | |
| 1293 | 1293 | } |
| 1294 | 1294 | |
| 1295 | 1295 | // Aliases |
src/arch/x86_64/CodeGen.zig+1-1| ... | ... | @@ -15563,7 +15563,7 @@ fn genLazySymbolRef( |
| 15563 | 15563 | .mov => try self.asmRegisterMemory( |
| 15564 | 15564 | .{ ._, tag }, |
| 15565 | 15565 | reg.to64(), |
| 15566 | Memory.sib(.qword, .{ .base = .{ .reg = reg.to64() } }), | |
| 15566 | Memory.initSib(.qword, .{ .base = .{ .reg = reg.to64() } }), | |
| 15567 | 15567 | ), |
| 15568 | 15568 | else => unreachable, |
| 15569 | 15569 | } |
src/arch/x86_64/Disassembler.zig+8-8| ... | ... | @@ -95,7 +95,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { |
| 95 | 95 | |
| 96 | 96 | if (modrm.rip()) { |
| 97 | 97 | return inst(act_enc, .{ |
| 98 | .op1 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(act_enc.data.ops[0].memBitSize()), disp) }, | |
| 98 | .op1 = .{ .mem = Memory.initRip(Memory.PtrSize.fromBitSize(act_enc.data.ops[0].memBitSize()), disp) }, | |
| 99 | 99 | .op2 = op2, |
| 100 | 100 | }); |
| 101 | 101 | } |
| ... | ... | @@ -106,7 +106,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { |
| 106 | 106 | else |
| 107 | 107 | parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, 64); |
| 108 | 108 | return inst(act_enc, .{ |
| 109 | .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(act_enc.data.ops[0].memBitSize()), .{ | |
| 109 | .op1 = .{ .mem = Memory.initSib(Memory.PtrSize.fromBitSize(act_enc.data.ops[0].memBitSize()), .{ | |
| 110 | 110 | .base = if (base) |base_reg| .{ .reg = base_reg } else .none, |
| 111 | 111 | .scale_index = scale_index, |
| 112 | 112 | .disp = disp, |
| ... | ... | @@ -119,14 +119,14 @@ pub fn next(dis: *Disassembler) Error!?Instruction { |
| 119 | 119 | const offset = try dis.parseOffset(); |
| 120 | 120 | return inst(enc, .{ |
| 121 | 121 | .op1 = .{ .reg = Register.rax.toBitSize(enc.data.ops[0].regBitSize()) }, |
| 122 | .op2 = .{ .mem = Memory.moffs(seg, offset) }, | |
| 122 | .op2 = .{ .mem = Memory.initMoffs(seg, offset) }, | |
| 123 | 123 | }); |
| 124 | 124 | }, |
| 125 | 125 | .td => { |
| 126 | 126 | const seg = segmentRegister(prefixes.legacy); |
| 127 | 127 | const offset = try dis.parseOffset(); |
| 128 | 128 | return inst(enc, .{ |
| 129 | .op1 = .{ .mem = Memory.moffs(seg, offset) }, | |
| 129 | .op1 = .{ .mem = Memory.initMoffs(seg, offset) }, | |
| 130 | 130 | .op2 = .{ .reg = Register.rax.toBitSize(enc.data.ops[1].regBitSize()) }, |
| 131 | 131 | }); |
| 132 | 132 | }, |
| ... | ... | @@ -153,7 +153,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { |
| 153 | 153 | |
| 154 | 154 | if (modrm.rip()) { |
| 155 | 155 | return inst(enc, .{ |
| 156 | .op1 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(dst_bit_size), disp) }, | |
| 156 | .op1 = .{ .mem = Memory.initRip(Memory.PtrSize.fromBitSize(dst_bit_size), disp) }, | |
| 157 | 157 | .op2 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.r, prefixes.rex, src_bit_size) }, |
| 158 | 158 | .op3 = op3, |
| 159 | 159 | }); |
| ... | ... | @@ -165,7 +165,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { |
| 165 | 165 | else |
| 166 | 166 | parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, 64); |
| 167 | 167 | return inst(enc, .{ |
| 168 | .op1 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(dst_bit_size), .{ | |
| 168 | .op1 = .{ .mem = Memory.initSib(Memory.PtrSize.fromBitSize(dst_bit_size), .{ | |
| 169 | 169 | .base = if (base) |base_reg| .{ .reg = base_reg } else .none, |
| 170 | 170 | .scale_index = scale_index, |
| 171 | 171 | .disp = disp, |
| ... | ... | @@ -203,7 +203,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { |
| 203 | 203 | if (modrm.rip()) { |
| 204 | 204 | return inst(enc, .{ |
| 205 | 205 | .op1 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.r, prefixes.rex, dst_bit_size) }, |
| 206 | .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(src_bit_size), disp) }, | |
| 206 | .op2 = .{ .mem = Memory.initRip(Memory.PtrSize.fromBitSize(src_bit_size), disp) }, | |
| 207 | 207 | .op3 = op3, |
| 208 | 208 | }); |
| 209 | 209 | } |
| ... | ... | @@ -215,7 +215,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { |
| 215 | 215 | parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, 64); |
| 216 | 216 | return inst(enc, .{ |
| 217 | 217 | .op1 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.r, prefixes.rex, dst_bit_size) }, |
| 218 | .op2 = .{ .mem = Memory.sib(Memory.PtrSize.fromBitSize(src_bit_size), .{ | |
| 218 | .op2 = .{ .mem = Memory.initSib(Memory.PtrSize.fromBitSize(src_bit_size), .{ | |
| 219 | 219 | .base = if (base) |base_reg| .{ .reg = base_reg } else .none, |
| 220 | 220 | .scale_index = scale_index, |
| 221 | 221 | .disp = disp, |
src/arch/x86_64/Lower.zig+21-21| ... | ... | @@ -200,13 +200,13 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { |
| 200 | 200 | }); |
| 201 | 201 | try lower.emit(.none, .lea, &.{ |
| 202 | 202 | .{ .reg = inst.data.ri.r1 }, |
| 203 | .{ .mem = Memory.sib(.qword, .{ | |
| 203 | .{ .mem = Memory.initSib(.qword, .{ | |
| 204 | 204 | .base = .{ .reg = inst.data.ri.r1 }, |
| 205 | 205 | .disp = -page_size, |
| 206 | 206 | }) }, |
| 207 | 207 | }); |
| 208 | 208 | try lower.emit(.none, .@"test", &.{ |
| 209 | .{ .mem = Memory.sib(.dword, .{ | |
| 209 | .{ .mem = Memory.initSib(.dword, .{ | |
| 210 | 210 | .base = .{ .reg = inst.data.ri.r1 }, |
| 211 | 211 | }) }, |
| 212 | 212 | .{ .reg = inst.data.ri.r1.to32() }, |
| ... | ... | @@ -220,7 +220,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { |
| 220 | 220 | var offset = page_size; |
| 221 | 221 | while (offset < @as(i32, @bitCast(inst.data.ri.i))) : (offset += page_size) { |
| 222 | 222 | try lower.emit(.none, .@"test", &.{ |
| 223 | .{ .mem = Memory.sib(.dword, .{ | |
| 223 | .{ .mem = Memory.initSib(.dword, .{ | |
| 224 | 224 | .base = .{ .reg = inst.data.ri.r1 }, |
| 225 | 225 | .disp = -offset, |
| 226 | 226 | }) }, |
| ... | ... | @@ -246,7 +246,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { |
| 246 | 246 | }, |
| 247 | 247 | .pseudo_probe_adjust_loop_rr => { |
| 248 | 248 | try lower.emit(.none, .@"test", &.{ |
| 249 | .{ .mem = Memory.sib(.dword, .{ | |
| 249 | .{ .mem = Memory.initSib(.dword, .{ | |
| 250 | 250 | .base = .{ .reg = inst.data.rr.r1 }, |
| 251 | 251 | .scale_index = .{ .scale = 1, .index = inst.data.rr.r2 }, |
| 252 | 252 | .disp = -page_size, |
| ... | ... | @@ -417,7 +417,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 417 | 417 | lower.result_insts[lower.result_insts_len] = |
| 418 | 418 | try Instruction.new(.none, .lea, &[_]Operand{ |
| 419 | 419 | .{ .reg = .rdi }, |
| 420 | .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }, | |
| 420 | .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) }, | |
| 421 | 421 | }); |
| 422 | 422 | lower.result_insts_len += 1; |
| 423 | 423 | _ = lower.reloc(.{ |
| ... | ... | @@ -430,7 +430,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 430 | 430 | lower.result_insts_len += 1; |
| 431 | 431 | _ = lower.reloc(.{ .linker_dtpoff = sym_index }, 0); |
| 432 | 432 | emit_mnemonic = .lea; |
| 433 | break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ | |
| 433 | break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ | |
| 434 | 434 | .base = .{ .reg = .rax }, |
| 435 | 435 | .disp = std.math.minInt(i32), |
| 436 | 436 | }) }; |
| ... | ... | @@ -439,12 +439,12 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 439 | 439 | lower.result_insts[lower.result_insts_len] = |
| 440 | 440 | try Instruction.new(.none, .mov, &[_]Operand{ |
| 441 | 441 | .{ .reg = .rax }, |
| 442 | .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .fs } }) }, | |
| 442 | .{ .mem = Memory.initSib(.qword, .{ .base = .{ .reg = .fs } }) }, | |
| 443 | 443 | }); |
| 444 | 444 | lower.result_insts_len += 1; |
| 445 | 445 | _ = lower.reloc(.{ .linker_reloc = sym_index }, 0); |
| 446 | 446 | emit_mnemonic = .lea; |
| 447 | break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ | |
| 447 | break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ | |
| 448 | 448 | .base = .{ .reg = .rax }, |
| 449 | 449 | .disp = std.math.minInt(i32), |
| 450 | 450 | }) }; |
| ... | ... | @@ -455,7 +455,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 455 | 455 | if (lower.pic) switch (mnemonic) { |
| 456 | 456 | .lea => { |
| 457 | 457 | if (elf_sym.flags.is_extern_ptr) emit_mnemonic = .mov; |
| 458 | break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }; | |
| 458 | break :op .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) }; | |
| 459 | 459 | }, |
| 460 | 460 | .mov => { |
| 461 | 461 | if (elf_sym.flags.is_extern_ptr) { |
| ... | ... | @@ -463,25 +463,25 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 463 | 463 | lower.result_insts[lower.result_insts_len] = |
| 464 | 464 | try Instruction.new(.none, .mov, &[_]Operand{ |
| 465 | 465 | .{ .reg = reg.to64() }, |
| 466 | .{ .mem = Memory.rip(.qword, 0) }, | |
| 466 | .{ .mem = Memory.initRip(.qword, 0) }, | |
| 467 | 467 | }); |
| 468 | 468 | lower.result_insts_len += 1; |
| 469 | break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ .base = .{ | |
| 469 | break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ .base = .{ | |
| 470 | 470 | .reg = reg.to64(), |
| 471 | 471 | } }) }; |
| 472 | 472 | } |
| 473 | break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }; | |
| 473 | break :op .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) }; | |
| 474 | 474 | }, |
| 475 | 475 | else => unreachable, |
| 476 | 476 | } else switch (mnemonic) { |
| 477 | .call => break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ | |
| 477 | .call => break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ | |
| 478 | 478 | .base = .{ .reg = .ds }, |
| 479 | 479 | }) }, |
| 480 | 480 | .lea => { |
| 481 | 481 | emit_mnemonic = .mov; |
| 482 | 482 | break :op .{ .imm = Immediate.s(0) }; |
| 483 | 483 | }, |
| 484 | .mov => break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ | |
| 484 | .mov => break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ | |
| 485 | 485 | .base = .{ .reg = .ds }, |
| 486 | 486 | }) }, |
| 487 | 487 | else => unreachable, |
| ... | ... | @@ -495,12 +495,12 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 495 | 495 | lower.result_insts[lower.result_insts_len] = |
| 496 | 496 | try Instruction.new(.none, .mov, &[_]Operand{ |
| 497 | 497 | .{ .reg = .rdi }, |
| 498 | .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }, | |
| 498 | .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) }, | |
| 499 | 499 | }); |
| 500 | 500 | lower.result_insts_len += 1; |
| 501 | 501 | lower.result_insts[lower.result_insts_len] = |
| 502 | 502 | try Instruction.new(.none, .call, &[_]Operand{ |
| 503 | .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .rdi } }) }, | |
| 503 | .{ .mem = Memory.initSib(.qword, .{ .base = .{ .reg = .rdi } }) }, | |
| 504 | 504 | }); |
| 505 | 505 | lower.result_insts_len += 1; |
| 506 | 506 | emit_mnemonic = .mov; |
| ... | ... | @@ -511,7 +511,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 511 | 511 | break :op switch (mnemonic) { |
| 512 | 512 | .lea => { |
| 513 | 513 | if (macho_sym.flags.is_extern_ptr) emit_mnemonic = .mov; |
| 514 | break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }; | |
| 514 | break :op .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) }; | |
| 515 | 515 | }, |
| 516 | 516 | .mov => { |
| 517 | 517 | if (macho_sym.flags.is_extern_ptr) { |
| ... | ... | @@ -519,14 +519,14 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 519 | 519 | lower.result_insts[lower.result_insts_len] = |
| 520 | 520 | try Instruction.new(.none, .mov, &[_]Operand{ |
| 521 | 521 | .{ .reg = reg.to64() }, |
| 522 | .{ .mem = Memory.rip(.qword, 0) }, | |
| 522 | .{ .mem = Memory.initRip(.qword, 0) }, | |
| 523 | 523 | }); |
| 524 | 524 | lower.result_insts_len += 1; |
| 525 | break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ .base = .{ | |
| 525 | break :op .{ .mem = Memory.initSib(mem_op.sib.ptr_size, .{ .base = .{ | |
| 526 | 526 | .reg = reg.to64(), |
| 527 | 527 | } }) }; |
| 528 | 528 | } |
| 529 | break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }; | |
| 529 | break :op .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) }; | |
| 530 | 530 | }, |
| 531 | 531 | else => unreachable, |
| 532 | 532 | }; |
| ... | ... | @@ -701,7 +701,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { |
| 701 | 701 | }, extra.off); |
| 702 | 702 | break :ops &.{ |
| 703 | 703 | .{ .reg = reg }, |
| 704 | .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) }, | |
| 704 | .{ .mem = Memory.initRip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) }, | |
| 705 | 705 | }; |
| 706 | 706 | }, |
| 707 | 707 | else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }), |
src/arch/x86_64/Mir.zig+3-3| ... | ... | @@ -1234,9 +1234,9 @@ pub const Memory = struct { |
| 1234 | 1234 | .rm => { |
| 1235 | 1235 | if (mem.info.base == .reg and @as(Register, @enumFromInt(mem.base)) == .rip) { |
| 1236 | 1236 | assert(mem.info.index == .none and mem.info.scale == .@"1"); |
| 1237 | return encoder.Instruction.Memory.rip(mem.info.size, @bitCast(mem.off)); | |
| 1237 | return encoder.Instruction.Memory.initRip(mem.info.size, @bitCast(mem.off)); | |
| 1238 | 1238 | } |
| 1239 | return encoder.Instruction.Memory.sib(mem.info.size, .{ | |
| 1239 | return encoder.Instruction.Memory.initSib(mem.info.size, .{ | |
| 1240 | 1240 | .disp = @bitCast(mem.off), |
| 1241 | 1241 | .base = switch (mem.info.base) { |
| 1242 | 1242 | .none => .none, |
| ... | ... | @@ -1258,7 +1258,7 @@ pub const Memory = struct { |
| 1258 | 1258 | }, |
| 1259 | 1259 | .off => { |
| 1260 | 1260 | assert(mem.info.base == .reg); |
| 1261 | return encoder.Instruction.Memory.moffs( | |
| 1261 | return encoder.Instruction.Memory.initMoffs( | |
| 1262 | 1262 | @enumFromInt(mem.base), |
| 1263 | 1263 | @as(u64, mem.extra) << 32 | mem.off, |
| 1264 | 1264 | ); |
src/arch/x86_64/encoder.zig+77-77| ... | ... | @@ -110,12 +110,12 @@ pub const Instruction = struct { |
| 110 | 110 | offset: u64, |
| 111 | 111 | }; |
| 112 | 112 | |
| 113 | pub fn moffs(reg: Register, offset: u64) Memory { | |
| 113 | pub fn initMoffs(reg: Register, offset: u64) Memory { | |
| 114 | 114 | assert(reg.class() == .segment); |
| 115 | 115 | return .{ .moffs = .{ .seg = reg, .offset = offset } }; |
| 116 | 116 | } |
| 117 | 117 | |
| 118 | pub fn sib(ptr_size: PtrSize, args: struct { | |
| 118 | pub fn initSib(ptr_size: PtrSize, args: struct { | |
| 119 | 119 | disp: i32 = 0, |
| 120 | 120 | base: Base = .none, |
| 121 | 121 | scale_index: ?ScaleIndex = null, |
| ... | ... | @@ -129,7 +129,7 @@ pub const Instruction = struct { |
| 129 | 129 | } }; |
| 130 | 130 | } |
| 131 | 131 | |
| 132 | pub fn rip(ptr_size: PtrSize, displacement: i32) Memory { | |
| 132 | pub fn initRip(ptr_size: PtrSize, displacement: i32) Memory { | |
| 133 | 133 | return .{ .rip = .{ .ptr_size = ptr_size, .disp = displacement } }; |
| 134 | 134 | } |
| 135 | 135 | |
| ... | ... | @@ -1266,7 +1266,7 @@ test "lower MI encoding" { |
| 1266 | 1266 | try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); |
| 1267 | 1267 | |
| 1268 | 1268 | try enc.encode(.mov, &.{ |
| 1269 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) }, | |
| 1269 | .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .r12 } }) }, | |
| 1270 | 1270 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1271 | 1271 | }); |
| 1272 | 1272 | try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10"); |
| ... | ... | @@ -1290,13 +1290,13 @@ test "lower MI encoding" { |
| 1290 | 1290 | try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10"); |
| 1291 | 1291 | |
| 1292 | 1292 | try enc.encode(.mov, &.{ |
| 1293 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 } }) }, | |
| 1293 | .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r11 } }) }, | |
| 1294 | 1294 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1295 | 1295 | }); |
| 1296 | 1296 | try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10"); |
| 1297 | 1297 | |
| 1298 | 1298 | try enc.encode(.mov, &.{ |
| 1299 | .{ .mem = Instruction.Memory.rip(.qword, 0x10) }, | |
| 1299 | .{ .mem = Instruction.Memory.initRip(.qword, 0x10) }, | |
| 1300 | 1300 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1301 | 1301 | }); |
| 1302 | 1302 | try expectEqualHexStrings( |
| ... | ... | @@ -1306,25 +1306,25 @@ test "lower MI encoding" { |
| 1306 | 1306 | ); |
| 1307 | 1307 | |
| 1308 | 1308 | try enc.encode(.mov, &.{ |
| 1309 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -8 }) }, | |
| 1309 | .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -8 }) }, | |
| 1310 | 1310 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1311 | 1311 | }); |
| 1312 | 1312 | try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10"); |
| 1313 | 1313 | |
| 1314 | 1314 | try enc.encode(.mov, &.{ |
| 1315 | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -2 }) }, | |
| 1315 | .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -2 }) }, | |
| 1316 | 1316 | .{ .imm = Instruction.Immediate.s(-16) }, |
| 1317 | 1317 | }); |
| 1318 | 1318 | try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16"); |
| 1319 | 1319 | |
| 1320 | 1320 | try enc.encode(.mov, &.{ |
| 1321 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -1 }) }, | |
| 1321 | .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -1 }) }, | |
| 1322 | 1322 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1323 | 1323 | }); |
| 1324 | 1324 | try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10"); |
| 1325 | 1325 | |
| 1326 | 1326 | try enc.encode(.mov, &.{ |
| 1327 | .{ .mem = Instruction.Memory.sib(.qword, .{ | |
| 1327 | .{ .mem = Instruction.Memory.initSib(.qword, .{ | |
| 1328 | 1328 | .base = .{ .reg = .ds }, |
| 1329 | 1329 | .disp = 0x10000000, |
| 1330 | 1330 | .scale_index = .{ .scale = 2, .index = .rcx }, |
| ... | ... | @@ -1338,13 +1338,13 @@ test "lower MI encoding" { |
| 1338 | 1338 | ); |
| 1339 | 1339 | |
| 1340 | 1340 | try enc.encode(.adc, &.{ |
| 1341 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) }, | |
| 1341 | .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) }, | |
| 1342 | 1342 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1343 | 1343 | }); |
| 1344 | 1344 | try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10"); |
| 1345 | 1345 | |
| 1346 | 1346 | try enc.encode(.adc, &.{ |
| 1347 | .{ .mem = Instruction.Memory.rip(.qword, 0) }, | |
| 1347 | .{ .mem = Instruction.Memory.initRip(.qword, 0) }, | |
| 1348 | 1348 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1349 | 1349 | }); |
| 1350 | 1350 | 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" { |
| 1356 | 1356 | try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10"); |
| 1357 | 1357 | |
| 1358 | 1358 | try enc.encode(.add, &.{ |
| 1359 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .rdx }, .disp = -8 }) }, | |
| 1359 | .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .rdx }, .disp = -8 }) }, | |
| 1360 | 1360 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1361 | 1361 | }); |
| 1362 | 1362 | try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10"); |
| ... | ... | @@ -1368,13 +1368,13 @@ test "lower MI encoding" { |
| 1368 | 1368 | try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); |
| 1369 | 1369 | |
| 1370 | 1370 | try enc.encode(.add, &.{ |
| 1371 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) }, | |
| 1371 | .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) }, | |
| 1372 | 1372 | .{ .imm = Instruction.Immediate.s(-0x10) }, |
| 1373 | 1373 | }); |
| 1374 | 1374 | try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10"); |
| 1375 | 1375 | |
| 1376 | 1376 | try enc.encode(.@"and", &.{ |
| 1377 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, | |
| 1377 | .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, | |
| 1378 | 1378 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1379 | 1379 | }); |
| 1380 | 1380 | try expectEqualHexStrings( |
| ... | ... | @@ -1384,7 +1384,7 @@ test "lower MI encoding" { |
| 1384 | 1384 | ); |
| 1385 | 1385 | |
| 1386 | 1386 | try enc.encode(.@"and", &.{ |
| 1387 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) }, | |
| 1387 | .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) }, | |
| 1388 | 1388 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1389 | 1389 | }); |
| 1390 | 1390 | try expectEqualHexStrings( |
| ... | ... | @@ -1394,7 +1394,7 @@ test "lower MI encoding" { |
| 1394 | 1394 | ); |
| 1395 | 1395 | |
| 1396 | 1396 | try enc.encode(.@"and", &.{ |
| 1397 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) }, | |
| 1397 | .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) }, | |
| 1398 | 1398 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1399 | 1399 | }); |
| 1400 | 1400 | try expectEqualHexStrings( |
| ... | ... | @@ -1404,7 +1404,7 @@ test "lower MI encoding" { |
| 1404 | 1404 | ); |
| 1405 | 1405 | |
| 1406 | 1406 | try enc.encode(.sub, &.{ |
| 1407 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) }, | |
| 1407 | .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) }, | |
| 1408 | 1408 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1409 | 1409 | }); |
| 1410 | 1410 | try expectEqualHexStrings( |
| ... | ... | @@ -1419,25 +1419,25 @@ test "lower RM encoding" { |
| 1419 | 1419 | |
| 1420 | 1420 | try enc.encode(.mov, &.{ |
| 1421 | 1421 | .{ .reg = .rax }, |
| 1422 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r11 } }) }, | |
| 1422 | .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r11 } }) }, | |
| 1423 | 1423 | }); |
| 1424 | 1424 | try expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]"); |
| 1425 | 1425 | |
| 1426 | 1426 | try enc.encode(.mov, &.{ |
| 1427 | 1427 | .{ .reg = .rbx }, |
| 1428 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10 }) }, | |
| 1428 | .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10 }) }, | |
| 1429 | 1429 | }); |
| 1430 | 1430 | try expectEqualHexStrings("\x48\x8B\x1C\x25\x10\x00\x00\x00", enc.code(), "mov rbx, QWORD PTR ds:0x10"); |
| 1431 | 1431 | |
| 1432 | 1432 | try enc.encode(.mov, &.{ |
| 1433 | 1433 | .{ .reg = .rax }, |
| 1434 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) }, | |
| 1434 | .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) }, | |
| 1435 | 1435 | }); |
| 1436 | 1436 | try expectEqualHexStrings("\x48\x8B\x45\xFC", enc.code(), "mov rax, QWORD PTR [rbp - 4]"); |
| 1437 | 1437 | |
| 1438 | 1438 | try enc.encode(.mov, &.{ |
| 1439 | 1439 | .{ .reg = .rax }, |
| 1440 | .{ .mem = Instruction.Memory.sib(.qword, .{ | |
| 1440 | .{ .mem = Instruction.Memory.initSib(.qword, .{ | |
| 1441 | 1441 | .base = .{ .reg = .rbp }, |
| 1442 | 1442 | .scale_index = .{ .scale = 1, .index = .rcx }, |
| 1443 | 1443 | .disp = -8, |
| ... | ... | @@ -1447,7 +1447,7 @@ test "lower RM encoding" { |
| 1447 | 1447 | |
| 1448 | 1448 | try enc.encode(.mov, &.{ |
| 1449 | 1449 | .{ .reg = .eax }, |
| 1450 | .{ .mem = Instruction.Memory.sib(.dword, .{ | |
| 1450 | .{ .mem = Instruction.Memory.initSib(.dword, .{ | |
| 1451 | 1451 | .base = .{ .reg = .rbp }, |
| 1452 | 1452 | .scale_index = .{ .scale = 4, .index = .rdx }, |
| 1453 | 1453 | .disp = -4, |
| ... | ... | @@ -1457,7 +1457,7 @@ test "lower RM encoding" { |
| 1457 | 1457 | |
| 1458 | 1458 | try enc.encode(.mov, &.{ |
| 1459 | 1459 | .{ .reg = .rax }, |
| 1460 | .{ .mem = Instruction.Memory.sib(.qword, .{ | |
| 1460 | .{ .mem = Instruction.Memory.initSib(.qword, .{ | |
| 1461 | 1461 | .base = .{ .reg = .rbp }, |
| 1462 | 1462 | .scale_index = .{ .scale = 8, .index = .rcx }, |
| 1463 | 1463 | .disp = -8, |
| ... | ... | @@ -1467,7 +1467,7 @@ test "lower RM encoding" { |
| 1467 | 1467 | |
| 1468 | 1468 | try enc.encode(.mov, &.{ |
| 1469 | 1469 | .{ .reg = .r8b }, |
| 1470 | .{ .mem = Instruction.Memory.sib(.byte, .{ | |
| 1470 | .{ .mem = Instruction.Memory.initSib(.byte, .{ | |
| 1471 | 1471 | .base = .{ .reg = .rsi }, |
| 1472 | 1472 | .scale_index = .{ .scale = 1, .index = .rcx }, |
| 1473 | 1473 | .disp = -24, |
| ... | ... | @@ -1483,7 +1483,7 @@ test "lower RM encoding" { |
| 1483 | 1483 | try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs"); |
| 1484 | 1484 | |
| 1485 | 1485 | try enc.encode(.mov, &.{ |
| 1486 | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) }, | |
| 1486 | .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) }, | |
| 1487 | 1487 | .{ .reg = .fs }, |
| 1488 | 1488 | }); |
| 1489 | 1489 | try expectEqualHexStrings("\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs"); |
| ... | ... | @@ -1514,19 +1514,19 @@ test "lower RM encoding" { |
| 1514 | 1514 | |
| 1515 | 1515 | try enc.encode(.movsx, &.{ |
| 1516 | 1516 | .{ .reg = .eax }, |
| 1517 | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp } }) }, | |
| 1517 | .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp } }) }, | |
| 1518 | 1518 | }); |
| 1519 | 1519 | try expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]"); |
| 1520 | 1520 | |
| 1521 | 1521 | try enc.encode(.movsx, &.{ |
| 1522 | 1522 | .{ .reg = .eax }, |
| 1523 | .{ .mem = Instruction.Memory.sib(.byte, .{ .scale_index = .{ .index = .rax, .scale = 2 } }) }, | |
| 1523 | .{ .mem = Instruction.Memory.initSib(.byte, .{ .scale_index = .{ .index = .rax, .scale = 2 } }) }, | |
| 1524 | 1524 | }); |
| 1525 | 1525 | try expectEqualHexStrings("\x0F\xBE\x04\x45\x00\x00\x00\x00", enc.code(), "movsx eax, BYTE PTR [rax * 2]"); |
| 1526 | 1526 | |
| 1527 | 1527 | try enc.encode(.movsx, &.{ |
| 1528 | 1528 | .{ .reg = .ax }, |
| 1529 | .{ .mem = Instruction.Memory.rip(.byte, 0x10) }, | |
| 1529 | .{ .mem = Instruction.Memory.initRip(.byte, 0x10) }, | |
| 1530 | 1530 | }); |
| 1531 | 1531 | try expectEqualHexStrings("\x66\x0F\xBE\x05\x10\x00\x00\x00", enc.code(), "movsx ax, BYTE PTR [rip + 0x10]"); |
| 1532 | 1532 | |
| ... | ... | @@ -1544,37 +1544,37 @@ test "lower RM encoding" { |
| 1544 | 1544 | |
| 1545 | 1545 | try enc.encode(.lea, &.{ |
| 1546 | 1546 | .{ .reg = .rax }, |
| 1547 | .{ .mem = Instruction.Memory.rip(.qword, 0x10) }, | |
| 1547 | .{ .mem = Instruction.Memory.initRip(.qword, 0x10) }, | |
| 1548 | 1548 | }); |
| 1549 | 1549 | try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, QWORD PTR [rip + 0x10]"); |
| 1550 | 1550 | |
| 1551 | 1551 | try enc.encode(.lea, &.{ |
| 1552 | 1552 | .{ .reg = .rax }, |
| 1553 | .{ .mem = Instruction.Memory.rip(.dword, 0x10) }, | |
| 1553 | .{ .mem = Instruction.Memory.initRip(.dword, 0x10) }, | |
| 1554 | 1554 | }); |
| 1555 | 1555 | try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, DWORD PTR [rip + 0x10]"); |
| 1556 | 1556 | |
| 1557 | 1557 | try enc.encode(.lea, &.{ |
| 1558 | 1558 | .{ .reg = .eax }, |
| 1559 | .{ .mem = Instruction.Memory.rip(.dword, 0x10) }, | |
| 1559 | .{ .mem = Instruction.Memory.initRip(.dword, 0x10) }, | |
| 1560 | 1560 | }); |
| 1561 | 1561 | try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, DWORD PTR [rip + 0x10]"); |
| 1562 | 1562 | |
| 1563 | 1563 | try enc.encode(.lea, &.{ |
| 1564 | 1564 | .{ .reg = .eax }, |
| 1565 | .{ .mem = Instruction.Memory.rip(.word, 0x10) }, | |
| 1565 | .{ .mem = Instruction.Memory.initRip(.word, 0x10) }, | |
| 1566 | 1566 | }); |
| 1567 | 1567 | try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, WORD PTR [rip + 0x10]"); |
| 1568 | 1568 | |
| 1569 | 1569 | try enc.encode(.lea, &.{ |
| 1570 | 1570 | .{ .reg = .ax }, |
| 1571 | .{ .mem = Instruction.Memory.rip(.byte, 0x10) }, | |
| 1571 | .{ .mem = Instruction.Memory.initRip(.byte, 0x10) }, | |
| 1572 | 1572 | }); |
| 1573 | 1573 | try expectEqualHexStrings("\x66\x8D\x05\x10\x00\x00\x00", enc.code(), "lea ax, BYTE PTR [rip + 0x10]"); |
| 1574 | 1574 | |
| 1575 | 1575 | try enc.encode(.lea, &.{ |
| 1576 | 1576 | .{ .reg = .rsi }, |
| 1577 | .{ .mem = Instruction.Memory.sib(.qword, .{ | |
| 1577 | .{ .mem = Instruction.Memory.initSib(.qword, .{ | |
| 1578 | 1578 | .base = .{ .reg = .rbp }, |
| 1579 | 1579 | .scale_index = .{ .scale = 1, .index = .rcx }, |
| 1580 | 1580 | }) }, |
| ... | ... | @@ -1583,31 +1583,31 @@ test "lower RM encoding" { |
| 1583 | 1583 | |
| 1584 | 1584 | try enc.encode(.add, &.{ |
| 1585 | 1585 | .{ .reg = .r11 }, |
| 1586 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, | |
| 1586 | .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, | |
| 1587 | 1587 | }); |
| 1588 | 1588 | try expectEqualHexStrings("\x4C\x03\x1C\x25\x00\x00\x00\x10", enc.code(), "add r11, QWORD PTR ds:0x10000000"); |
| 1589 | 1589 | |
| 1590 | 1590 | try enc.encode(.add, &.{ |
| 1591 | 1591 | .{ .reg = .r12b }, |
| 1592 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, | |
| 1592 | .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, | |
| 1593 | 1593 | }); |
| 1594 | 1594 | try expectEqualHexStrings("\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR ds:0x10000000"); |
| 1595 | 1595 | |
| 1596 | 1596 | try enc.encode(.add, &.{ |
| 1597 | 1597 | .{ .reg = .r12b }, |
| 1598 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .fs }, .disp = 0x10000000 }) }, | |
| 1598 | .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .fs }, .disp = 0x10000000 }) }, | |
| 1599 | 1599 | }); |
| 1600 | 1600 | try expectEqualHexStrings("\x64\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR fs:0x10000000"); |
| 1601 | 1601 | |
| 1602 | 1602 | try enc.encode(.sub, &.{ |
| 1603 | 1603 | .{ .reg = .r11 }, |
| 1604 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r13 }, .disp = 0x10000000 }) }, | |
| 1604 | .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r13 }, .disp = 0x10000000 }) }, | |
| 1605 | 1605 | }); |
| 1606 | 1606 | try expectEqualHexStrings("\x4D\x2B\x9D\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r13 + 0x10000000]"); |
| 1607 | 1607 | |
| 1608 | 1608 | try enc.encode(.sub, &.{ |
| 1609 | 1609 | .{ .reg = .r11 }, |
| 1610 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) }, | |
| 1610 | .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) }, | |
| 1611 | 1611 | }); |
| 1612 | 1612 | try expectEqualHexStrings("\x4D\x2B\x9C\x24\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r12 + 0x10000000]"); |
| 1613 | 1613 | |
| ... | ... | @@ -1630,7 +1630,7 @@ test "lower RMI encoding" { |
| 1630 | 1630 | |
| 1631 | 1631 | try enc.encode(.imul, &.{ |
| 1632 | 1632 | .{ .reg = .r11 }, |
| 1633 | .{ .mem = Instruction.Memory.rip(.qword, -16) }, | |
| 1633 | .{ .mem = Instruction.Memory.initRip(.qword, -16) }, | |
| 1634 | 1634 | .{ .imm = Instruction.Immediate.s(-1024) }, |
| 1635 | 1635 | }); |
| 1636 | 1636 | try expectEqualHexStrings( |
| ... | ... | @@ -1641,7 +1641,7 @@ test "lower RMI encoding" { |
| 1641 | 1641 | |
| 1642 | 1642 | try enc.encode(.imul, &.{ |
| 1643 | 1643 | .{ .reg = .bx }, |
| 1644 | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) }, | |
| 1644 | .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) }, | |
| 1645 | 1645 | .{ .imm = Instruction.Immediate.s(-1024) }, |
| 1646 | 1646 | }); |
| 1647 | 1647 | try expectEqualHexStrings( |
| ... | ... | @@ -1652,7 +1652,7 @@ test "lower RMI encoding" { |
| 1652 | 1652 | |
| 1653 | 1653 | try enc.encode(.imul, &.{ |
| 1654 | 1654 | .{ .reg = .bx }, |
| 1655 | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) }, | |
| 1655 | .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) }, | |
| 1656 | 1656 | .{ .imm = Instruction.Immediate.u(1024) }, |
| 1657 | 1657 | }); |
| 1658 | 1658 | try expectEqualHexStrings( |
| ... | ... | @@ -1672,19 +1672,19 @@ test "lower MR encoding" { |
| 1672 | 1672 | try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx"); |
| 1673 | 1673 | |
| 1674 | 1674 | try enc.encode(.mov, &.{ |
| 1675 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) }, | |
| 1675 | .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) }, | |
| 1676 | 1676 | .{ .reg = .r11 }, |
| 1677 | 1677 | }); |
| 1678 | 1678 | try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11"); |
| 1679 | 1679 | |
| 1680 | 1680 | try enc.encode(.mov, &.{ |
| 1681 | .{ .mem = Instruction.Memory.rip(.qword, 0x10) }, | |
| 1681 | .{ .mem = Instruction.Memory.initRip(.qword, 0x10) }, | |
| 1682 | 1682 | .{ .reg = .r12 }, |
| 1683 | 1683 | }); |
| 1684 | 1684 | try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rip + 0x10], r12"); |
| 1685 | 1685 | |
| 1686 | 1686 | try enc.encode(.mov, &.{ |
| 1687 | .{ .mem = Instruction.Memory.sib(.qword, .{ | |
| 1687 | .{ .mem = Instruction.Memory.initSib(.qword, .{ | |
| 1688 | 1688 | .base = .{ .reg = .r11 }, |
| 1689 | 1689 | .scale_index = .{ .scale = 2, .index = .r12 }, |
| 1690 | 1690 | .disp = 0x10, |
| ... | ... | @@ -1694,13 +1694,13 @@ test "lower MR encoding" { |
| 1694 | 1694 | try expectEqualHexStrings("\x4F\x89\x6C\x63\x10", enc.code(), "mov QWORD PTR [r11 + 2 * r12 + 0x10], r13"); |
| 1695 | 1695 | |
| 1696 | 1696 | try enc.encode(.mov, &.{ |
| 1697 | .{ .mem = Instruction.Memory.rip(.word, -0x10) }, | |
| 1697 | .{ .mem = Instruction.Memory.initRip(.word, -0x10) }, | |
| 1698 | 1698 | .{ .reg = .r12w }, |
| 1699 | 1699 | }); |
| 1700 | 1700 | try expectEqualHexStrings("\x66\x44\x89\x25\xF0\xFF\xFF\xFF", enc.code(), "mov WORD PTR [rip - 0x10], r12w"); |
| 1701 | 1701 | |
| 1702 | 1702 | try enc.encode(.mov, &.{ |
| 1703 | .{ .mem = Instruction.Memory.sib(.byte, .{ | |
| 1703 | .{ .mem = Instruction.Memory.initSib(.byte, .{ | |
| 1704 | 1704 | .base = .{ .reg = .r11 }, |
| 1705 | 1705 | .scale_index = .{ .scale = 2, .index = .r12 }, |
| 1706 | 1706 | .disp = 0x10, |
| ... | ... | @@ -1710,25 +1710,25 @@ test "lower MR encoding" { |
| 1710 | 1710 | try expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b"); |
| 1711 | 1711 | |
| 1712 | 1712 | try enc.encode(.add, &.{ |
| 1713 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, | |
| 1713 | .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, | |
| 1714 | 1714 | .{ .reg = .r12b }, |
| 1715 | 1715 | }); |
| 1716 | 1716 | try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b"); |
| 1717 | 1717 | |
| 1718 | 1718 | try enc.encode(.add, &.{ |
| 1719 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, | |
| 1719 | .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) }, | |
| 1720 | 1720 | .{ .reg = .r12d }, |
| 1721 | 1721 | }); |
| 1722 | 1722 | try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d"); |
| 1723 | 1723 | |
| 1724 | 1724 | try enc.encode(.add, &.{ |
| 1725 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .gs }, .disp = 0x10000000 }) }, | |
| 1725 | .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .gs }, .disp = 0x10000000 }) }, | |
| 1726 | 1726 | .{ .reg = .r12d }, |
| 1727 | 1727 | }); |
| 1728 | 1728 | try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d"); |
| 1729 | 1729 | |
| 1730 | 1730 | try enc.encode(.sub, &.{ |
| 1731 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) }, | |
| 1731 | .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) }, | |
| 1732 | 1732 | .{ .reg = .r12 }, |
| 1733 | 1733 | }); |
| 1734 | 1734 | 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" { |
| 1743 | 1743 | try expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12"); |
| 1744 | 1744 | |
| 1745 | 1745 | try enc.encode(.call, &.{ |
| 1746 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r12 } }) }, | |
| 1746 | .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r12 } }) }, | |
| 1747 | 1747 | }); |
| 1748 | 1748 | try expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]"); |
| 1749 | 1749 | |
| 1750 | 1750 | try enc.encode(.call, &.{ |
| 1751 | .{ .mem = Instruction.Memory.sib(.qword, .{ | |
| 1751 | .{ .mem = Instruction.Memory.initSib(.qword, .{ | |
| 1752 | 1752 | .base = .none, |
| 1753 | 1753 | .scale_index = .{ .index = .r11, .scale = 2 }, |
| 1754 | 1754 | }) }, |
| ... | ... | @@ -1756,7 +1756,7 @@ test "lower M encoding" { |
| 1756 | 1756 | try expectEqualHexStrings("\x42\xFF\x14\x5D\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r11 * 2]"); |
| 1757 | 1757 | |
| 1758 | 1758 | try enc.encode(.call, &.{ |
| 1759 | .{ .mem = Instruction.Memory.sib(.qword, .{ | |
| 1759 | .{ .mem = Instruction.Memory.initSib(.qword, .{ | |
| 1760 | 1760 | .base = .none, |
| 1761 | 1761 | .scale_index = .{ .index = .r12, .scale = 2 }, |
| 1762 | 1762 | }) }, |
| ... | ... | @@ -1764,7 +1764,7 @@ test "lower M encoding" { |
| 1764 | 1764 | try expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]"); |
| 1765 | 1765 | |
| 1766 | 1766 | try enc.encode(.call, &.{ |
| 1767 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .gs } }) }, | |
| 1767 | .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .gs } }) }, | |
| 1768 | 1768 | }); |
| 1769 | 1769 | try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0"); |
| 1770 | 1770 | |
| ... | ... | @@ -1774,22 +1774,22 @@ test "lower M encoding" { |
| 1774 | 1774 | try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0"); |
| 1775 | 1775 | |
| 1776 | 1776 | try enc.encode(.push, &.{ |
| 1777 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .rbp } }) }, | |
| 1777 | .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp } }) }, | |
| 1778 | 1778 | }); |
| 1779 | 1779 | try expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]"); |
| 1780 | 1780 | |
| 1781 | 1781 | try enc.encode(.push, &.{ |
| 1782 | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp } }) }, | |
| 1782 | .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp } }) }, | |
| 1783 | 1783 | }); |
| 1784 | 1784 | try expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]"); |
| 1785 | 1785 | |
| 1786 | 1786 | try enc.encode(.pop, &.{ |
| 1787 | .{ .mem = Instruction.Memory.rip(.qword, 0) }, | |
| 1787 | .{ .mem = Instruction.Memory.initRip(.qword, 0) }, | |
| 1788 | 1788 | }); |
| 1789 | 1789 | try expectEqualHexStrings("\x8F\x05\x00\x00\x00\x00", enc.code(), "pop QWORD PTR [rip]"); |
| 1790 | 1790 | |
| 1791 | 1791 | try enc.encode(.pop, &.{ |
| 1792 | .{ .mem = Instruction.Memory.rip(.word, 0) }, | |
| 1792 | .{ .mem = Instruction.Memory.initRip(.word, 0) }, | |
| 1793 | 1793 | }); |
| 1794 | 1794 | try expectEqualHexStrings("\x66\x8F\x05\x00\x00\x00\x00", enc.code(), "pop WORD PTR [rbp]"); |
| 1795 | 1795 | |
| ... | ... | @@ -1870,48 +1870,48 @@ test "lower FD/TD encoding" { |
| 1870 | 1870 | |
| 1871 | 1871 | try enc.encode(.mov, &.{ |
| 1872 | 1872 | .{ .reg = .rax }, |
| 1873 | .{ .mem = Instruction.Memory.moffs(.cs, 0x10) }, | |
| 1873 | .{ .mem = Instruction.Memory.initMoffs(.cs, 0x10) }, | |
| 1874 | 1874 | }); |
| 1875 | 1875 | try expectEqualHexStrings("\x2E\x48\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs rax, cs:0x10"); |
| 1876 | 1876 | |
| 1877 | 1877 | try enc.encode(.mov, &.{ |
| 1878 | 1878 | .{ .reg = .eax }, |
| 1879 | .{ .mem = Instruction.Memory.moffs(.fs, 0x10) }, | |
| 1879 | .{ .mem = Instruction.Memory.initMoffs(.fs, 0x10) }, | |
| 1880 | 1880 | }); |
| 1881 | 1881 | try expectEqualHexStrings("\x64\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs eax, fs:0x10"); |
| 1882 | 1882 | |
| 1883 | 1883 | try enc.encode(.mov, &.{ |
| 1884 | 1884 | .{ .reg = .ax }, |
| 1885 | .{ .mem = Instruction.Memory.moffs(.gs, 0x10) }, | |
| 1885 | .{ .mem = Instruction.Memory.initMoffs(.gs, 0x10) }, | |
| 1886 | 1886 | }); |
| 1887 | 1887 | try expectEqualHexStrings("\x65\x66\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ax, gs:0x10"); |
| 1888 | 1888 | |
| 1889 | 1889 | try enc.encode(.mov, &.{ |
| 1890 | 1890 | .{ .reg = .al }, |
| 1891 | .{ .mem = Instruction.Memory.moffs(.ds, 0x10) }, | |
| 1891 | .{ .mem = Instruction.Memory.initMoffs(.ds, 0x10) }, | |
| 1892 | 1892 | }); |
| 1893 | 1893 | try expectEqualHexStrings("\xA0\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs al, ds:0x10"); |
| 1894 | 1894 | |
| 1895 | 1895 | try enc.encode(.mov, &.{ |
| 1896 | .{ .mem = Instruction.Memory.moffs(.cs, 0x10) }, | |
| 1896 | .{ .mem = Instruction.Memory.initMoffs(.cs, 0x10) }, | |
| 1897 | 1897 | .{ .reg = .rax }, |
| 1898 | 1898 | }); |
| 1899 | 1899 | try expectEqualHexStrings("\x2E\x48\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs cs:0x10, rax"); |
| 1900 | 1900 | |
| 1901 | 1901 | try enc.encode(.mov, &.{ |
| 1902 | .{ .mem = Instruction.Memory.moffs(.fs, 0x10) }, | |
| 1902 | .{ .mem = Instruction.Memory.initMoffs(.fs, 0x10) }, | |
| 1903 | 1903 | .{ .reg = .eax }, |
| 1904 | 1904 | }); |
| 1905 | 1905 | try expectEqualHexStrings("\x64\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs fs:0x10, eax"); |
| 1906 | 1906 | |
| 1907 | 1907 | try enc.encode(.mov, &.{ |
| 1908 | .{ .mem = Instruction.Memory.moffs(.gs, 0x10) }, | |
| 1908 | .{ .mem = Instruction.Memory.initMoffs(.gs, 0x10) }, | |
| 1909 | 1909 | .{ .reg = .ax }, |
| 1910 | 1910 | }); |
| 1911 | 1911 | try expectEqualHexStrings("\x65\x66\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs gs:0x10, ax"); |
| 1912 | 1912 | |
| 1913 | 1913 | try enc.encode(.mov, &.{ |
| 1914 | .{ .mem = Instruction.Memory.moffs(.ds, 0x10) }, | |
| 1914 | .{ .mem = Instruction.Memory.initMoffs(.ds, 0x10) }, | |
| 1915 | 1915 | .{ .reg = .al }, |
| 1916 | 1916 | }); |
| 1917 | 1917 | try expectEqualHexStrings("\xA2\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ds:0x10, al"); |
| ... | ... | @@ -1949,16 +1949,16 @@ test "invalid instruction" { |
| 1949 | 1949 | .{ .reg = .al }, |
| 1950 | 1950 | }); |
| 1951 | 1951 | try invalidInstruction(.call, &.{ |
| 1952 | .{ .mem = Instruction.Memory.rip(.dword, 0) }, | |
| 1952 | .{ .mem = Instruction.Memory.initRip(.dword, 0) }, | |
| 1953 | 1953 | }); |
| 1954 | 1954 | try invalidInstruction(.call, &.{ |
| 1955 | .{ .mem = Instruction.Memory.rip(.word, 0) }, | |
| 1955 | .{ .mem = Instruction.Memory.initRip(.word, 0) }, | |
| 1956 | 1956 | }); |
| 1957 | 1957 | try invalidInstruction(.call, &.{ |
| 1958 | .{ .mem = Instruction.Memory.rip(.byte, 0) }, | |
| 1958 | .{ .mem = Instruction.Memory.initRip(.byte, 0) }, | |
| 1959 | 1959 | }); |
| 1960 | 1960 | try invalidInstruction(.mov, &.{ |
| 1961 | .{ .mem = Instruction.Memory.rip(.word, 0x10) }, | |
| 1961 | .{ .mem = Instruction.Memory.initRip(.word, 0x10) }, | |
| 1962 | 1962 | .{ .reg = .r12 }, |
| 1963 | 1963 | }); |
| 1964 | 1964 | try invalidInstruction(.lea, &.{ |
| ... | ... | @@ -1967,7 +1967,7 @@ test "invalid instruction" { |
| 1967 | 1967 | }); |
| 1968 | 1968 | try invalidInstruction(.lea, &.{ |
| 1969 | 1969 | .{ .reg = .al }, |
| 1970 | .{ .mem = Instruction.Memory.rip(.byte, 0) }, | |
| 1970 | .{ .mem = Instruction.Memory.initRip(.byte, 0) }, | |
| 1971 | 1971 | }); |
| 1972 | 1972 | try invalidInstruction(.pop, &.{ |
| 1973 | 1973 | .{ .reg = .r12b }, |
| ... | ... | @@ -1992,7 +1992,7 @@ fn cannotEncode(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand |
| 1992 | 1992 | |
| 1993 | 1993 | test "cannot encode" { |
| 1994 | 1994 | try cannotEncode(.@"test", &.{ |
| 1995 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) }, | |
| 1995 | .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .r12 } }) }, | |
| 1996 | 1996 | .{ .reg = .ah }, |
| 1997 | 1997 | }); |
| 1998 | 1998 | try cannotEncode(.@"test", &.{ |
| ... | ... | @@ -2369,7 +2369,7 @@ const Assembler = struct { |
| 2369 | 2369 | if (res.rip) { |
| 2370 | 2370 | if (res.base != null or res.scale_index != null or res.offset != null) |
| 2371 | 2371 | return error.InvalidMemoryOperand; |
| 2372 | return Instruction.Memory.rip(ptr_size orelse .qword, res.disp orelse 0); | |
| 2372 | return Instruction.Memory.initRip(ptr_size orelse .qword, res.disp orelse 0); | |
| 2373 | 2373 | } |
| 2374 | 2374 | if (res.base) |base| { |
| 2375 | 2375 | if (res.rip) |
| ... | ... | @@ -2377,9 +2377,9 @@ const Assembler = struct { |
| 2377 | 2377 | if (res.offset) |offset| { |
| 2378 | 2378 | if (res.scale_index != null or res.disp != null) |
| 2379 | 2379 | return error.InvalidMemoryOperand; |
| 2380 | return Instruction.Memory.moffs(base, offset); | |
| 2380 | return Instruction.Memory.initMoffs(base, offset); | |
| 2381 | 2381 | } |
| 2382 | return Instruction.Memory.sib(ptr_size orelse .qword, .{ | |
| 2382 | return Instruction.Memory.initSib(ptr_size orelse .qword, .{ | |
| 2383 | 2383 | .base = .{ .reg = base }, |
| 2384 | 2384 | .scale_index = res.scale_index, |
| 2385 | 2385 | .disp = res.disp orelse 0, |
src/codegen.zig+2-11| ... | ... | @@ -836,16 +836,6 @@ pub const GenResult = union(enum) { |
| 836 | 836 | /// Traditionally, this corresponds to emitting a relocation in a relocatable object file. |
| 837 | 837 | lea_symbol: u32, |
| 838 | 838 | }; |
| 839 | ||
| 840 | fn fail( | |
| 841 | gpa: Allocator, | |
| 842 | src_loc: Zcu.LazySrcLoc, | |
| 843 | comptime format: []const u8, | |
| 844 | args: anytype, | |
| 845 | ) Allocator.Error!GenResult { | |
| 846 | const msg = try ErrorMsg.create(gpa, src_loc, format, args); | |
| 847 | return .{ .fail = msg }; | |
| 848 | } | |
| 849 | 839 | }; |
| 850 | 840 | |
| 851 | 841 | fn genNavRef( |
| ... | ... | @@ -935,7 +925,8 @@ fn genNavRef( |
| 935 | 925 | const atom = p9.getAtom(atom_index); |
| 936 | 926 | return .{ .mcv = .{ .memory = atom.getOffsetTableAddress(p9) } }; |
| 937 | 927 | } else { |
| 938 | return GenResult.fail(gpa, src_loc, "TODO genNavRef for target {}", .{target}); | |
| 928 | const msg = try ErrorMsg.create(gpa, src_loc, "TODO genNavRef for target {}", .{target}); | |
| 929 | return .{ .fail = msg }; | |
| 939 | 930 | } |
| 940 | 931 | } |
| 941 | 932 |
src/codegen/llvm/BitcodeReader.zig+9-9| ... | ... | @@ -33,9 +33,9 @@ pub const Block = struct { |
| 33 | 33 | .abbrevs = .{ .abbrevs = .{} }, |
| 34 | 34 | }; |
| 35 | 35 | |
| 36 | const set_bid: u32 = 1; | |
| 37 | const block_name: u32 = 2; | |
| 38 | const set_record_name: u32 = 3; | |
| 36 | const set_bid_id: u32 = 1; | |
| 37 | const block_name_id: u32 = 2; | |
| 38 | const set_record_name_id: u32 = 3; | |
| 39 | 39 | |
| 40 | 40 | fn deinit(info: *Info, allocator: std.mem.Allocator) void { |
| 41 | 41 | allocator.free(info.block_name); |
| ... | ... | @@ -61,7 +61,7 @@ pub const Record = struct { |
| 61 | 61 | assert(record.id == Abbrev.Builtin.define_abbrev.toRecordId()); |
| 62 | 62 | var i: usize = 0; |
| 63 | 63 | while (i < record.operands.len) switch (record.operands[i]) { |
| 64 | Abbrev.Operand.literal => { | |
| 64 | Abbrev.Operand.literal_id => { | |
| 65 | 65 | try operands.append(.{ .literal = record.operands[i + 1] }); |
| 66 | 66 | i += 2; |
| 67 | 67 | }, |
| ... | ... | @@ -211,7 +211,7 @@ fn nextRecord(bc: *BitcodeReader) !?Record { |
| 211 | 211 | .align_32_bits, .block_len => return error.UnsupportedArrayElement, |
| 212 | 212 | .abbrev_op => switch (try bc.readFixed(u1, 1)) { |
| 213 | 213 | 1 => try operands.appendSlice(&.{ |
| 214 | Abbrev.Operand.literal, | |
| 214 | Abbrev.Operand.literal_id, | |
| 215 | 215 | try bc.readVbr(u64, 8), |
| 216 | 216 | }), |
| 217 | 217 | 0 => { |
| ... | ... | @@ -334,9 +334,9 @@ fn parseBlockInfoBlock(bc: *BitcodeReader) !void { |
| 334 | 334 | try record.toOwnedAbbrev(bc.allocator), |
| 335 | 335 | ); |
| 336 | 336 | }, |
| 337 | Block.Info.set_bid => block_id = std.math.cast(u32, record.operands[0]) orelse | |
| 337 | Block.Info.set_bid_id => block_id = std.math.cast(u32, record.operands[0]) orelse | |
| 338 | 338 | return error.Overflow, |
| 339 | Block.Info.block_name => if (bc.keep_names) { | |
| 339 | Block.Info.block_name_id => if (bc.keep_names) { | |
| 340 | 340 | const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse |
| 341 | 341 | return error.UnspecifiedBlockId); |
| 342 | 342 | if (!gop.found_existing) gop.value_ptr.* = Block.Info.default; |
| ... | ... | @@ -346,7 +346,7 @@ fn parseBlockInfoBlock(bc: *BitcodeReader) !void { |
| 346 | 346 | byte.* = std.math.cast(u8, operand) orelse return error.InvalidName; |
| 347 | 347 | gop.value_ptr.block_name = name; |
| 348 | 348 | }, |
| 349 | Block.Info.set_record_name => if (bc.keep_names) { | |
| 349 | Block.Info.set_record_name_id => if (bc.keep_names) { | |
| 350 | 350 | const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse |
| 351 | 351 | return error.UnspecifiedBlockId); |
| 352 | 352 | if (!gop.found_existing) gop.value_ptr.* = Block.Info.default; |
| ... | ... | @@ -467,7 +467,7 @@ const Abbrev = struct { |
| 467 | 467 | block_len, |
| 468 | 468 | abbrev_op, |
| 469 | 469 | |
| 470 | const literal = std.math.maxInt(u64); | |
| 470 | const literal_id = std.math.maxInt(u64); | |
| 471 | 471 | const Encoding = enum(u3) { |
| 472 | 472 | fixed = 1, |
| 473 | 473 | vbr = 2, |
src/link/Elf.zig+84-3| ... | ... | @@ -3467,7 +3467,7 @@ fn updateSectionSizes(self: *Elf) !void { |
| 3467 | 3467 | if (atom_list.items.len == 0) continue; |
| 3468 | 3468 | |
| 3469 | 3469 | // Create jump/branch range extenders if needed. |
| 3470 | try thunks.createThunks(shdr, @intCast(shndx), self); | |
| 3470 | try self.createThunks(shdr, @intCast(shndx)); | |
| 3471 | 3471 | } |
| 3472 | 3472 | } |
| 3473 | 3473 | |
| ... | ... | @@ -5576,6 +5576,88 @@ fn defaultEntrySymbolName(cpu_arch: std.Target.Cpu.Arch) []const u8 { |
| 5576 | 5576 | }; |
| 5577 | 5577 | } |
| 5578 | 5578 | |
| 5579 | fn createThunks(elf_file: *Elf, shdr: *elf.Elf64_Shdr, shndx: u32) !void { | |
| 5580 | const gpa = elf_file.base.comp.gpa; | |
| 5581 | const cpu_arch = elf_file.getTarget().cpu.arch; | |
| 5582 | // A branch will need an extender if its target is larger than | |
| 5583 | // `2^(jump_bits - 1) - margin` where margin is some arbitrary number. | |
| 5584 | const max_distance = switch (cpu_arch) { | |
| 5585 | .aarch64 => 0x500_000, | |
| 5586 | .x86_64, .riscv64 => unreachable, | |
| 5587 | else => @panic("unhandled arch"), | |
| 5588 | }; | |
| 5589 | const atoms = elf_file.sections.items(.atom_list)[shndx].items; | |
| 5590 | assert(atoms.len > 0); | |
| 5591 | ||
| 5592 | for (atoms) |ref| { | |
| 5593 | elf_file.atom(ref).?.value = -1; | |
| 5594 | } | |
| 5595 | ||
| 5596 | var i: usize = 0; | |
| 5597 | while (i < atoms.len) { | |
| 5598 | const start = i; | |
| 5599 | const start_atom = elf_file.atom(atoms[start]).?; | |
| 5600 | assert(start_atom.alive); | |
| 5601 | start_atom.value = try advanceSection(shdr, start_atom.size, start_atom.alignment); | |
| 5602 | i += 1; | |
| 5603 | ||
| 5604 | while (i < atoms.len) : (i += 1) { | |
| 5605 | const atom_ptr = elf_file.atom(atoms[i]).?; | |
| 5606 | assert(atom_ptr.alive); | |
| 5607 | if (@as(i64, @intCast(atom_ptr.alignment.forward(shdr.sh_size))) - start_atom.value >= max_distance) | |
| 5608 | break; | |
| 5609 | atom_ptr.value = try advanceSection(shdr, atom_ptr.size, atom_ptr.alignment); | |
| 5610 | } | |
| 5611 | ||
| 5612 | // Insert a thunk at the group end | |
| 5613 | const thunk_index = try elf_file.addThunk(); | |
| 5614 | const thunk_ptr = elf_file.thunk(thunk_index); | |
| 5615 | thunk_ptr.output_section_index = shndx; | |
| 5616 | ||
| 5617 | // Scan relocs in the group and create trampolines for any unreachable callsite | |
| 5618 | for (atoms[start..i]) |ref| { | |
| 5619 | const atom_ptr = elf_file.atom(ref).?; | |
| 5620 | const file_ptr = atom_ptr.file(elf_file).?; | |
| 5621 | log.debug("atom({}) {s}", .{ ref, atom_ptr.name(elf_file) }); | |
| 5622 | for (atom_ptr.relocs(elf_file)) |rel| { | |
| 5623 | const is_reachable = switch (cpu_arch) { | |
| 5624 | .aarch64 => r: { | |
| 5625 | const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type()); | |
| 5626 | if (r_type != .CALL26 and r_type != .JUMP26) break :r true; | |
| 5627 | const target_ref = file_ptr.resolveSymbol(rel.r_sym(), elf_file); | |
| 5628 | const target = elf_file.symbol(target_ref).?; | |
| 5629 | if (target.flags.has_plt) break :r false; | |
| 5630 | if (atom_ptr.output_section_index != target.output_section_index) break :r false; | |
| 5631 | const target_atom = target.atom(elf_file).?; | |
| 5632 | if (target_atom.value == -1) break :r false; | |
| 5633 | const saddr = atom_ptr.address(elf_file) + @as(i64, @intCast(rel.r_offset)); | |
| 5634 | const taddr = target.address(.{}, elf_file); | |
| 5635 | _ = math.cast(i28, taddr + rel.r_addend - saddr) orelse break :r false; | |
| 5636 | break :r true; | |
| 5637 | }, | |
| 5638 | .x86_64, .riscv64 => unreachable, | |
| 5639 | else => @panic("unsupported arch"), | |
| 5640 | }; | |
| 5641 | if (is_reachable) continue; | |
| 5642 | const target = file_ptr.resolveSymbol(rel.r_sym(), elf_file); | |
| 5643 | try thunk_ptr.symbols.put(gpa, target, {}); | |
| 5644 | } | |
| 5645 | atom_ptr.addExtra(.{ .thunk = thunk_index }, elf_file); | |
| 5646 | } | |
| 5647 | ||
| 5648 | thunk_ptr.value = try advanceSection(shdr, thunk_ptr.size(elf_file), Atom.Alignment.fromNonzeroByteUnits(2)); | |
| 5649 | ||
| 5650 | log.debug("thunk({d}) : {}", .{ thunk_index, thunk_ptr.fmt(elf_file) }); | |
| 5651 | } | |
| 5652 | } | |
| 5653 | fn advanceSection(shdr: *elf.Elf64_Shdr, adv_size: u64, alignment: Atom.Alignment) !i64 { | |
| 5654 | const offset = alignment.forward(shdr.sh_size); | |
| 5655 | const padding = offset - shdr.sh_size; | |
| 5656 | shdr.sh_size += padding + adv_size; | |
| 5657 | shdr.sh_addralign = @max(shdr.sh_addralign, alignment.toByteUnits() orelse 1); | |
| 5658 | return @intCast(offset); | |
| 5659 | } | |
| 5660 | ||
| 5579 | 5661 | const std = @import("std"); |
| 5580 | 5662 | const build_options = @import("build_options"); |
| 5581 | 5663 | const builtin = @import("builtin"); |
| ... | ... | @@ -5598,7 +5680,6 @@ const musl = @import("../musl.zig"); |
| 5598 | 5680 | const relocatable = @import("Elf/relocatable.zig"); |
| 5599 | 5681 | const relocation = @import("Elf/relocation.zig"); |
| 5600 | 5682 | const target_util = @import("../target.zig"); |
| 5601 | const thunks = @import("Elf/thunks.zig"); | |
| 5602 | 5683 | const trace = @import("../tracy.zig").trace; |
| 5603 | 5684 | const synthetic_sections = @import("Elf/synthetic_sections.zig"); |
| 5604 | 5685 | |
| ... | ... | @@ -5636,7 +5717,7 @@ const PltGotSection = synthetic_sections.PltGotSection; |
| 5636 | 5717 | const SharedObject = @import("Elf/SharedObject.zig"); |
| 5637 | 5718 | const Symbol = @import("Elf/Symbol.zig"); |
| 5638 | 5719 | const StringTable = @import("StringTable.zig"); |
| 5639 | const Thunk = thunks.Thunk; | |
| 5720 | const Thunk = @import("Elf/Thunk.zig"); | |
| 5640 | 5721 | const Value = @import("../Value.zig"); |
| 5641 | 5722 | const VerneedSection = synthetic_sections.VerneedSection; |
| 5642 | 5723 | const ZigObject = @import("Elf/ZigObject.zig"); |
src/link/Elf/Atom.zig+1-1| ... | ... | @@ -2251,6 +2251,6 @@ const Fde = eh_frame.Fde; |
| 2251 | 2251 | const File = @import("file.zig").File; |
| 2252 | 2252 | const Object = @import("Object.zig"); |
| 2253 | 2253 | const Symbol = @import("Symbol.zig"); |
| 2254 | const Thunk = @import("thunks.zig").Thunk; | |
| 2254 | const Thunk = @import("Thunk.zig"); | |
| 2255 | 2255 | const ZigObject = @import("ZigObject.zig"); |
| 2256 | 2256 | const dev = @import("../../dev.zig"); |
src/link/Elf/Thunk.zig created+144| ... | ... | @@ -0,0 +1,144 @@ |
| 1 | value: i64 = 0, | |
| 2 | output_section_index: u32 = 0, | |
| 3 | symbols: std.AutoArrayHashMapUnmanaged(Elf.Ref, void) = .{}, | |
| 4 | output_symtab_ctx: Elf.SymtabCtx = .{}, | |
| 5 | ||
| 6 | pub fn deinit(thunk: *Thunk, allocator: Allocator) void { | |
| 7 | thunk.symbols.deinit(allocator); | |
| 8 | } | |
| 9 | ||
| 10 | pub fn size(thunk: Thunk, elf_file: *Elf) usize { | |
| 11 | const cpu_arch = elf_file.getTarget().cpu.arch; | |
| 12 | return thunk.symbols.keys().len * trampolineSize(cpu_arch); | |
| 13 | } | |
| 14 | ||
| 15 | pub fn address(thunk: Thunk, elf_file: *Elf) i64 { | |
| 16 | const shdr = elf_file.sections.items(.shdr)[thunk.output_section_index]; | |
| 17 | return @as(i64, @intCast(shdr.sh_addr)) + thunk.value; | |
| 18 | } | |
| 19 | ||
| 20 | pub fn targetAddress(thunk: Thunk, ref: Elf.Ref, elf_file: *Elf) i64 { | |
| 21 | const cpu_arch = elf_file.getTarget().cpu.arch; | |
| 22 | return thunk.address(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(ref).? * trampolineSize(cpu_arch))); | |
| 23 | } | |
| 24 | ||
| 25 | pub fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void { | |
| 26 | switch (elf_file.getTarget().cpu.arch) { | |
| 27 | .aarch64 => try aarch64.write(thunk, elf_file, writer), | |
| 28 | .x86_64, .riscv64 => unreachable, | |
| 29 | else => @panic("unhandled arch"), | |
| 30 | } | |
| 31 | } | |
| 32 | ||
| 33 | pub fn calcSymtabSize(thunk: *Thunk, elf_file: *Elf) void { | |
| 34 | thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len)); | |
| 35 | for (thunk.symbols.keys()) |ref| { | |
| 36 | const sym = elf_file.symbol(ref).?; | |
| 37 | thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.name(elf_file).len + "$thunk".len + 1)); | |
| 38 | } | |
| 39 | } | |
| 40 | ||
| 41 | pub fn writeSymtab(thunk: Thunk, elf_file: *Elf) void { | |
| 42 | const cpu_arch = elf_file.getTarget().cpu.arch; | |
| 43 | for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| { | |
| 44 | const sym = elf_file.symbol(ref).?; | |
| 45 | const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); | |
| 46 | elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file)); | |
| 47 | elf_file.strtab.appendSliceAssumeCapacity("$thunk"); | |
| 48 | elf_file.strtab.appendAssumeCapacity(0); | |
| 49 | elf_file.symtab.items[ilocal] = .{ | |
| 50 | .st_name = st_name, | |
| 51 | .st_info = elf.STT_FUNC, | |
| 52 | .st_other = 0, | |
| 53 | .st_shndx = @intCast(thunk.output_section_index), | |
| 54 | .st_value = @intCast(thunk.targetAddress(ref, elf_file)), | |
| 55 | .st_size = trampolineSize(cpu_arch), | |
| 56 | }; | |
| 57 | } | |
| 58 | } | |
| 59 | ||
| 60 | fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) usize { | |
| 61 | return switch (cpu_arch) { | |
| 62 | .aarch64 => aarch64.trampoline_size, | |
| 63 | .x86_64, .riscv64 => unreachable, | |
| 64 | else => @panic("unhandled arch"), | |
| 65 | }; | |
| 66 | } | |
| 67 | ||
| 68 | pub fn format( | |
| 69 | thunk: Thunk, | |
| 70 | comptime unused_fmt_string: []const u8, | |
| 71 | options: std.fmt.FormatOptions, | |
| 72 | writer: anytype, | |
| 73 | ) !void { | |
| 74 | _ = thunk; | |
| 75 | _ = unused_fmt_string; | |
| 76 | _ = options; | |
| 77 | _ = writer; | |
| 78 | @compileError("do not format Thunk directly"); | |
| 79 | } | |
| 80 | ||
| 81 | pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(format2) { | |
| 82 | return .{ .data = .{ | |
| 83 | .thunk = thunk, | |
| 84 | .elf_file = elf_file, | |
| 85 | } }; | |
| 86 | } | |
| 87 | ||
| 88 | const FormatContext = struct { | |
| 89 | thunk: Thunk, | |
| 90 | elf_file: *Elf, | |
| 91 | }; | |
| 92 | ||
| 93 | fn format2( | |
| 94 | ctx: FormatContext, | |
| 95 | comptime unused_fmt_string: []const u8, | |
| 96 | options: std.fmt.FormatOptions, | |
| 97 | writer: anytype, | |
| 98 | ) !void { | |
| 99 | _ = options; | |
| 100 | _ = unused_fmt_string; | |
| 101 | const thunk = ctx.thunk; | |
| 102 | const elf_file = ctx.elf_file; | |
| 103 | try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) }); | |
| 104 | for (thunk.symbols.keys()) |ref| { | |
| 105 | const sym = elf_file.symbol(ref).?; | |
| 106 | try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value }); | |
| 107 | } | |
| 108 | } | |
| 109 | ||
| 110 | pub const Index = u32; | |
| 111 | ||
| 112 | const aarch64 = struct { | |
| 113 | fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void { | |
| 114 | for (thunk.symbols.keys(), 0..) |ref, i| { | |
| 115 | const sym = elf_file.symbol(ref).?; | |
| 116 | const saddr = thunk.address(elf_file) + @as(i64, @intCast(i * trampoline_size)); | |
| 117 | const taddr = sym.address(.{}, elf_file); | |
| 118 | const pages = try util.calcNumberOfPages(saddr, taddr); | |
| 119 | try writer.writeInt(u32, Instruction.adrp(.x16, pages).toU32(), .little); | |
| 120 | const off: u12 = @truncate(@as(u64, @bitCast(taddr))); | |
| 121 | try writer.writeInt(u32, Instruction.add(.x16, .x16, off, false).toU32(), .little); | |
| 122 | try writer.writeInt(u32, Instruction.br(.x16).toU32(), .little); | |
| 123 | } | |
| 124 | } | |
| 125 | ||
| 126 | const trampoline_size = 3 * @sizeOf(u32); | |
| 127 | ||
| 128 | const util = @import("../aarch64.zig"); | |
| 129 | const Instruction = util.Instruction; | |
| 130 | }; | |
| 131 | ||
| 132 | const assert = std.debug.assert; | |
| 133 | const elf = std.elf; | |
| 134 | const log = std.log.scoped(.link); | |
| 135 | const math = std.math; | |
| 136 | const mem = std.mem; | |
| 137 | const std = @import("std"); | |
| 138 | ||
| 139 | const Allocator = mem.Allocator; | |
| 140 | const Atom = @import("Atom.zig"); | |
| 141 | const Elf = @import("../Elf.zig"); | |
| 142 | const Symbol = @import("Symbol.zig"); | |
| 143 | ||
| 144 | const Thunk = @This(); |
src/link/Elf/thunks.zig deleted-234| ... | ... | @@ -1,234 +0,0 @@ |
| 1 | pub fn createThunks(shdr: *elf.Elf64_Shdr, shndx: u32, elf_file: *Elf) !void { | |
| 2 | const gpa = elf_file.base.comp.gpa; | |
| 3 | const cpu_arch = elf_file.getTarget().cpu.arch; | |
| 4 | const max_distance = maxAllowedDistance(cpu_arch); | |
| 5 | const atoms = elf_file.sections.items(.atom_list)[shndx].items; | |
| 6 | assert(atoms.len > 0); | |
| 7 | ||
| 8 | for (atoms) |ref| { | |
| 9 | elf_file.atom(ref).?.value = -1; | |
| 10 | } | |
| 11 | ||
| 12 | var i: usize = 0; | |
| 13 | while (i < atoms.len) { | |
| 14 | const start = i; | |
| 15 | const start_atom = elf_file.atom(atoms[start]).?; | |
| 16 | assert(start_atom.alive); | |
| 17 | start_atom.value = try advance(shdr, start_atom.size, start_atom.alignment); | |
| 18 | i += 1; | |
| 19 | ||
| 20 | while (i < atoms.len) : (i += 1) { | |
| 21 | const atom = elf_file.atom(atoms[i]).?; | |
| 22 | assert(atom.alive); | |
| 23 | if (@as(i64, @intCast(atom.alignment.forward(shdr.sh_size))) - start_atom.value >= max_distance) | |
| 24 | break; | |
| 25 | atom.value = try advance(shdr, atom.size, atom.alignment); | |
| 26 | } | |
| 27 | ||
| 28 | // Insert a thunk at the group end | |
| 29 | const thunk_index = try elf_file.addThunk(); | |
| 30 | const thunk = elf_file.thunk(thunk_index); | |
| 31 | thunk.output_section_index = shndx; | |
| 32 | ||
| 33 | // Scan relocs in the group and create trampolines for any unreachable callsite | |
| 34 | for (atoms[start..i]) |ref| { | |
| 35 | const atom = elf_file.atom(ref).?; | |
| 36 | const file = atom.file(elf_file).?; | |
| 37 | log.debug("atom({}) {s}", .{ ref, atom.name(elf_file) }); | |
| 38 | for (atom.relocs(elf_file)) |rel| { | |
| 39 | const is_reachable = switch (cpu_arch) { | |
| 40 | .aarch64 => aarch64.isReachable(atom, rel, elf_file), | |
| 41 | .x86_64, .riscv64 => unreachable, | |
| 42 | else => @panic("unsupported arch"), | |
| 43 | }; | |
| 44 | if (is_reachable) continue; | |
| 45 | const target = file.resolveSymbol(rel.r_sym(), elf_file); | |
| 46 | try thunk.symbols.put(gpa, target, {}); | |
| 47 | } | |
| 48 | atom.addExtra(.{ .thunk = thunk_index }, elf_file); | |
| 49 | } | |
| 50 | ||
| 51 | thunk.value = try advance(shdr, thunk.size(elf_file), Atom.Alignment.fromNonzeroByteUnits(2)); | |
| 52 | ||
| 53 | log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(elf_file) }); | |
| 54 | } | |
| 55 | } | |
| 56 | ||
| 57 | fn advance(shdr: *elf.Elf64_Shdr, size: u64, alignment: Atom.Alignment) !i64 { | |
| 58 | const offset = alignment.forward(shdr.sh_size); | |
| 59 | const padding = offset - shdr.sh_size; | |
| 60 | shdr.sh_size += padding + size; | |
| 61 | shdr.sh_addralign = @max(shdr.sh_addralign, alignment.toByteUnits() orelse 1); | |
| 62 | return @intCast(offset); | |
| 63 | } | |
| 64 | ||
| 65 | /// A branch will need an extender if its target is larger than | |
| 66 | /// `2^(jump_bits - 1) - margin` where margin is some arbitrary number. | |
| 67 | fn maxAllowedDistance(cpu_arch: std.Target.Cpu.Arch) u32 { | |
| 68 | return switch (cpu_arch) { | |
| 69 | .aarch64 => 0x500_000, | |
| 70 | .x86_64, .riscv64 => unreachable, | |
| 71 | else => @panic("unhandled arch"), | |
| 72 | }; | |
| 73 | } | |
| 74 | ||
| 75 | pub const Thunk = struct { | |
| 76 | value: i64 = 0, | |
| 77 | output_section_index: u32 = 0, | |
| 78 | symbols: std.AutoArrayHashMapUnmanaged(Elf.Ref, void) = .{}, | |
| 79 | output_symtab_ctx: Elf.SymtabCtx = .{}, | |
| 80 | ||
| 81 | pub fn deinit(thunk: *Thunk, allocator: Allocator) void { | |
| 82 | thunk.symbols.deinit(allocator); | |
| 83 | } | |
| 84 | ||
| 85 | pub fn size(thunk: Thunk, elf_file: *Elf) usize { | |
| 86 | const cpu_arch = elf_file.getTarget().cpu.arch; | |
| 87 | return thunk.symbols.keys().len * trampolineSize(cpu_arch); | |
| 88 | } | |
| 89 | ||
| 90 | pub fn address(thunk: Thunk, elf_file: *Elf) i64 { | |
| 91 | const shdr = elf_file.sections.items(.shdr)[thunk.output_section_index]; | |
| 92 | return @as(i64, @intCast(shdr.sh_addr)) + thunk.value; | |
| 93 | } | |
| 94 | ||
| 95 | pub fn targetAddress(thunk: Thunk, ref: Elf.Ref, elf_file: *Elf) i64 { | |
| 96 | const cpu_arch = elf_file.getTarget().cpu.arch; | |
| 97 | return thunk.address(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(ref).? * trampolineSize(cpu_arch))); | |
| 98 | } | |
| 99 | ||
| 100 | pub fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void { | |
| 101 | switch (elf_file.getTarget().cpu.arch) { | |
| 102 | .aarch64 => try aarch64.write(thunk, elf_file, writer), | |
| 103 | .x86_64, .riscv64 => unreachable, | |
| 104 | else => @panic("unhandled arch"), | |
| 105 | } | |
| 106 | } | |
| 107 | ||
| 108 | pub fn calcSymtabSize(thunk: *Thunk, elf_file: *Elf) void { | |
| 109 | thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len)); | |
| 110 | for (thunk.symbols.keys()) |ref| { | |
| 111 | const sym = elf_file.symbol(ref).?; | |
| 112 | thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.name(elf_file).len + "$thunk".len + 1)); | |
| 113 | } | |
| 114 | } | |
| 115 | ||
| 116 | pub fn writeSymtab(thunk: Thunk, elf_file: *Elf) void { | |
| 117 | const cpu_arch = elf_file.getTarget().cpu.arch; | |
| 118 | for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| { | |
| 119 | const sym = elf_file.symbol(ref).?; | |
| 120 | const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); | |
| 121 | elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file)); | |
| 122 | elf_file.strtab.appendSliceAssumeCapacity("$thunk"); | |
| 123 | elf_file.strtab.appendAssumeCapacity(0); | |
| 124 | elf_file.symtab.items[ilocal] = .{ | |
| 125 | .st_name = st_name, | |
| 126 | .st_info = elf.STT_FUNC, | |
| 127 | .st_other = 0, | |
| 128 | .st_shndx = @intCast(thunk.output_section_index), | |
| 129 | .st_value = @intCast(thunk.targetAddress(ref, elf_file)), | |
| 130 | .st_size = trampolineSize(cpu_arch), | |
| 131 | }; | |
| 132 | } | |
| 133 | } | |
| 134 | ||
| 135 | fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) usize { | |
| 136 | return switch (cpu_arch) { | |
| 137 | .aarch64 => aarch64.trampoline_size, | |
| 138 | .x86_64, .riscv64 => unreachable, | |
| 139 | else => @panic("unhandled arch"), | |
| 140 | }; | |
| 141 | } | |
| 142 | ||
| 143 | pub fn format( | |
| 144 | thunk: Thunk, | |
| 145 | comptime unused_fmt_string: []const u8, | |
| 146 | options: std.fmt.FormatOptions, | |
| 147 | writer: anytype, | |
| 148 | ) !void { | |
| 149 | _ = thunk; | |
| 150 | _ = unused_fmt_string; | |
| 151 | _ = options; | |
| 152 | _ = writer; | |
| 153 | @compileError("do not format Thunk directly"); | |
| 154 | } | |
| 155 | ||
| 156 | pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(format2) { | |
| 157 | return .{ .data = .{ | |
| 158 | .thunk = thunk, | |
| 159 | .elf_file = elf_file, | |
| 160 | } }; | |
| 161 | } | |
| 162 | ||
| 163 | const FormatContext = struct { | |
| 164 | thunk: Thunk, | |
| 165 | elf_file: *Elf, | |
| 166 | }; | |
| 167 | ||
| 168 | fn format2( | |
| 169 | ctx: FormatContext, | |
| 170 | comptime unused_fmt_string: []const u8, | |
| 171 | options: std.fmt.FormatOptions, | |
| 172 | writer: anytype, | |
| 173 | ) !void { | |
| 174 | _ = options; | |
| 175 | _ = unused_fmt_string; | |
| 176 | const thunk = ctx.thunk; | |
| 177 | const elf_file = ctx.elf_file; | |
| 178 | try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) }); | |
| 179 | for (thunk.symbols.keys()) |ref| { | |
| 180 | const sym = elf_file.symbol(ref).?; | |
| 181 | try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value }); | |
| 182 | } | |
| 183 | } | |
| 184 | ||
| 185 | pub const Index = u32; | |
| 186 | }; | |
| 187 | ||
| 188 | const aarch64 = struct { | |
| 189 | fn isReachable(atom: *const Atom, rel: elf.Elf64_Rela, elf_file: *Elf) bool { | |
| 190 | const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type()); | |
| 191 | if (r_type != .CALL26 and r_type != .JUMP26) return true; | |
| 192 | const file = atom.file(elf_file).?; | |
| 193 | const target_ref = file.resolveSymbol(rel.r_sym(), elf_file); | |
| 194 | const target = elf_file.symbol(target_ref).?; | |
| 195 | if (target.flags.has_plt) return false; | |
| 196 | if (atom.output_section_index != target.output_section_index) return false; | |
| 197 | const target_atom = target.atom(elf_file).?; | |
| 198 | if (target_atom.value == -1) return false; | |
| 199 | const saddr = atom.address(elf_file) + @as(i64, @intCast(rel.r_offset)); | |
| 200 | const taddr = target.address(.{}, elf_file); | |
| 201 | _ = math.cast(i28, taddr + rel.r_addend - saddr) orelse return false; | |
| 202 | return true; | |
| 203 | } | |
| 204 | ||
| 205 | fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void { | |
| 206 | for (thunk.symbols.keys(), 0..) |ref, i| { | |
| 207 | const sym = elf_file.symbol(ref).?; | |
| 208 | const saddr = thunk.address(elf_file) + @as(i64, @intCast(i * trampoline_size)); | |
| 209 | const taddr = sym.address(.{}, elf_file); | |
| 210 | const pages = try util.calcNumberOfPages(saddr, taddr); | |
| 211 | try writer.writeInt(u32, Instruction.adrp(.x16, pages).toU32(), .little); | |
| 212 | const off: u12 = @truncate(@as(u64, @bitCast(taddr))); | |
| 213 | try writer.writeInt(u32, Instruction.add(.x16, .x16, off, false).toU32(), .little); | |
| 214 | try writer.writeInt(u32, Instruction.br(.x16).toU32(), .little); | |
| 215 | } | |
| 216 | } | |
| 217 | ||
| 218 | const trampoline_size = 3 * @sizeOf(u32); | |
| 219 | ||
| 220 | const util = @import("../aarch64.zig"); | |
| 221 | const Instruction = util.Instruction; | |
| 222 | }; | |
| 223 | ||
| 224 | const assert = std.debug.assert; | |
| 225 | const elf = std.elf; | |
| 226 | const log = std.log.scoped(.link); | |
| 227 | const math = std.math; | |
| 228 | const mem = std.mem; | |
| 229 | const std = @import("std"); | |
| 230 | ||
| 231 | const Allocator = mem.Allocator; | |
| 232 | const Atom = @import("Atom.zig"); | |
| 233 | const Elf = @import("../Elf.zig"); | |
| 234 | const Symbol = @import("Symbol.zig"); |
src/link/MachO.zig+111-19| ... | ... | @@ -64,10 +64,10 @@ stubs_helper: StubsHelperSection = .{}, |
| 64 | 64 | objc_stubs: ObjcStubsSection = .{}, |
| 65 | 65 | la_symbol_ptr: LaSymbolPtrSection = .{}, |
| 66 | 66 | tlv_ptr: TlvPtrSection = .{}, |
| 67 | rebase: Rebase = .{}, | |
| 68 | bind: Bind = .{}, | |
| 69 | weak_bind: WeakBind = .{}, | |
| 70 | lazy_bind: LazyBind = .{}, | |
| 67 | rebase_section: Rebase = .{}, | |
| 68 | bind_section: Bind = .{}, | |
| 69 | weak_bind_section: WeakBind = .{}, | |
| 70 | lazy_bind_section: LazyBind = .{}, | |
| 71 | 71 | export_trie: ExportTrie = .{}, |
| 72 | 72 | unwind_info: UnwindInfo = .{}, |
| 73 | 73 | data_in_code: DataInCode = .{}, |
| ... | ... | @@ -324,10 +324,10 @@ pub fn deinit(self: *MachO) void { |
| 324 | 324 | self.stubs.deinit(gpa); |
| 325 | 325 | self.objc_stubs.deinit(gpa); |
| 326 | 326 | self.tlv_ptr.deinit(gpa); |
| 327 | self.rebase.deinit(gpa); | |
| 328 | self.bind.deinit(gpa); | |
| 329 | self.weak_bind.deinit(gpa); | |
| 330 | self.lazy_bind.deinit(gpa); | |
| 327 | self.rebase_section.deinit(gpa); | |
| 328 | self.bind_section.deinit(gpa); | |
| 329 | self.weak_bind_section.deinit(gpa); | |
| 330 | self.lazy_bind_section.deinit(gpa); | |
| 331 | 331 | self.export_trie.deinit(gpa); |
| 332 | 332 | self.unwind_info.deinit(gpa); |
| 333 | 333 | self.data_in_code.deinit(gpa); |
| ... | ... | @@ -2005,7 +2005,7 @@ fn calcSectionSizeWorker(self: *MachO, sect_id: u8) void { |
| 2005 | 2005 | fn createThunksWorker(self: *MachO, sect_id: u8) void { |
| 2006 | 2006 | const tracy = trace(@src()); |
| 2007 | 2007 | defer tracy.end(); |
| 2008 | thunks.createThunks(sect_id, self) catch |err| { | |
| 2008 | self.createThunks(sect_id) catch |err| { | |
| 2009 | 2009 | const header = self.sections.items(.header)[sect_id]; |
| 2010 | 2010 | self.reportUnexpectedError("failed to create thunks and calculate size of section '{s},{s}': {s}", .{ |
| 2011 | 2011 | header.segName(), |
| ... | ... | @@ -2562,7 +2562,7 @@ fn updateLazyBindSizeWorker(self: *MachO) void { |
| 2562 | 2562 | defer tracy.end(); |
| 2563 | 2563 | const doWork = struct { |
| 2564 | 2564 | fn doWork(macho_file: *MachO) !void { |
| 2565 | try macho_file.lazy_bind.updateSize(macho_file); | |
| 2565 | try macho_file.lazy_bind_section.updateSize(macho_file); | |
| 2566 | 2566 | const sect_id = macho_file.stubs_helper_sect_index.?; |
| 2567 | 2567 | const out = &macho_file.sections.items(.out)[sect_id]; |
| 2568 | 2568 | var stream = std.io.fixedBufferStream(out.items); |
| ... | ... | @@ -2585,9 +2585,9 @@ pub fn updateLinkeditSizeWorker(self: *MachO, tag: enum { |
| 2585 | 2585 | data_in_code, |
| 2586 | 2586 | }) void { |
| 2587 | 2587 | const res = switch (tag) { |
| 2588 | .rebase => self.rebase.updateSize(self), | |
| 2589 | .bind => self.bind.updateSize(self), | |
| 2590 | .weak_bind => self.weak_bind.updateSize(self), | |
| 2588 | .rebase => self.rebase_section.updateSize(self), | |
| 2589 | .bind => self.bind_section.updateSize(self), | |
| 2590 | .weak_bind => self.weak_bind_section.updateSize(self), | |
| 2591 | 2591 | .export_trie => self.export_trie.updateSize(self), |
| 2592 | 2592 | .data_in_code => self.data_in_code.updateSize(self), |
| 2593 | 2593 | }; |
| ... | ... | @@ -2640,13 +2640,13 @@ fn writeDyldInfo(self: *MachO) !void { |
| 2640 | 2640 | var stream = std.io.fixedBufferStream(buffer); |
| 2641 | 2641 | const writer = stream.writer(); |
| 2642 | 2642 | |
| 2643 | try self.rebase.write(writer); | |
| 2643 | try self.rebase_section.write(writer); | |
| 2644 | 2644 | try stream.seekTo(cmd.bind_off - base_off); |
| 2645 | try self.bind.write(writer); | |
| 2645 | try self.bind_section.write(writer); | |
| 2646 | 2646 | try stream.seekTo(cmd.weak_bind_off - base_off); |
| 2647 | try self.weak_bind.write(writer); | |
| 2647 | try self.weak_bind_section.write(writer); | |
| 2648 | 2648 | try stream.seekTo(cmd.lazy_bind_off - base_off); |
| 2649 | try self.lazy_bind.write(writer); | |
| 2649 | try self.lazy_bind_section.write(writer); | |
| 2650 | 2650 | try stream.seekTo(cmd.export_off - base_off); |
| 2651 | 2651 | try self.export_trie.write(writer); |
| 2652 | 2652 | try self.base.file.?.pwriteAll(buffer, cmd.rebase_off); |
| ... | ... | @@ -4602,7 +4602,6 @@ const load_commands = @import("MachO/load_commands.zig"); |
| 4602 | 4602 | const relocatable = @import("MachO/relocatable.zig"); |
| 4603 | 4603 | const tapi = @import("tapi.zig"); |
| 4604 | 4604 | const target_util = @import("../target.zig"); |
| 4605 | const thunks = @import("MachO/thunks.zig"); | |
| 4606 | 4605 | const trace = @import("../tracy.zig").trace; |
| 4607 | 4606 | const synthetic = @import("MachO/synthetic.zig"); |
| 4608 | 4607 | |
| ... | ... | @@ -4641,7 +4640,7 @@ const StringTable = @import("StringTable.zig"); |
| 4641 | 4640 | const StubsSection = synthetic.StubsSection; |
| 4642 | 4641 | const StubsHelperSection = synthetic.StubsHelperSection; |
| 4643 | 4642 | const Symbol = @import("MachO/Symbol.zig"); |
| 4644 | const Thunk = thunks.Thunk; | |
| 4643 | const Thunk = @import("MachO/Thunk.zig"); | |
| 4645 | 4644 | const TlvPtrSection = synthetic.TlvPtrSection; |
| 4646 | 4645 | const Value = @import("../Value.zig"); |
| 4647 | 4646 | const UnwindInfo = @import("MachO/UnwindInfo.zig"); |
| ... | ... | @@ -5292,3 +5291,96 @@ pub const KernE = enum(u32) { |
| 5292 | 5291 | NOT_FOUND = 56, |
| 5293 | 5292 | _, |
| 5294 | 5293 | }; |
| 5294 | ||
| 5295 | fn createThunks(macho_file: *MachO, sect_id: u8) !void { | |
| 5296 | const tracy = trace(@src()); | |
| 5297 | defer tracy.end(); | |
| 5298 | ||
| 5299 | const gpa = macho_file.base.comp.gpa; | |
| 5300 | const slice = macho_file.sections.slice(); | |
| 5301 | const header = &slice.items(.header)[sect_id]; | |
| 5302 | const thnks = &slice.items(.thunks)[sect_id]; | |
| 5303 | const atoms = slice.items(.atoms)[sect_id].items; | |
| 5304 | assert(atoms.len > 0); | |
| 5305 | ||
| 5306 | for (atoms) |ref| { | |
| 5307 | ref.getAtom(macho_file).?.value = @bitCast(@as(i64, -1)); | |
| 5308 | } | |
| 5309 | ||
| 5310 | var i: usize = 0; | |
| 5311 | while (i < atoms.len) { | |
| 5312 | const start = i; | |
| 5313 | const start_atom = atoms[start].getAtom(macho_file).?; | |
| 5314 | assert(start_atom.isAlive()); | |
| 5315 | start_atom.value = advanceSection(header, start_atom.size, start_atom.alignment); | |
| 5316 | i += 1; | |
| 5317 | ||
| 5318 | while (i < atoms.len and | |
| 5319 | header.size - start_atom.value < max_allowed_distance) : (i += 1) | |
| 5320 | { | |
| 5321 | const atom = atoms[i].getAtom(macho_file).?; | |
| 5322 | assert(atom.isAlive()); | |
| 5323 | atom.value = advanceSection(header, atom.size, atom.alignment); | |
| 5324 | } | |
| 5325 | ||
| 5326 | // Insert a thunk at the group end | |
| 5327 | const thunk_index = try macho_file.addThunk(); | |
| 5328 | const thunk = macho_file.getThunk(thunk_index); | |
| 5329 | thunk.out_n_sect = sect_id; | |
| 5330 | try thnks.append(gpa, thunk_index); | |
| 5331 | ||
| 5332 | // Scan relocs in the group and create trampolines for any unreachable callsite | |
| 5333 | try scanThunkRelocs(thunk_index, gpa, atoms[start..i], macho_file); | |
| 5334 | thunk.value = advanceSection(header, thunk.size(), .@"4"); | |
| 5335 | ||
| 5336 | log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(macho_file) }); | |
| 5337 | } | |
| 5338 | } | |
| 5339 | ||
| 5340 | fn advanceSection(sect: *macho.section_64, adv_size: u64, alignment: Atom.Alignment) u64 { | |
| 5341 | const offset = alignment.forward(sect.size); | |
| 5342 | const padding = offset - sect.size; | |
| 5343 | sect.size += padding + adv_size; | |
| 5344 | sect.@"align" = @max(sect.@"align", alignment.toLog2Units()); | |
| 5345 | return offset; | |
| 5346 | } | |
| 5347 | ||
| 5348 | fn scanThunkRelocs(thunk_index: Thunk.Index, gpa: Allocator, atoms: []const MachO.Ref, macho_file: *MachO) !void { | |
| 5349 | const tracy = trace(@src()); | |
| 5350 | defer tracy.end(); | |
| 5351 | ||
| 5352 | const thunk = macho_file.getThunk(thunk_index); | |
| 5353 | ||
| 5354 | for (atoms) |ref| { | |
| 5355 | const atom = ref.getAtom(macho_file).?; | |
| 5356 | log.debug("atom({d}) {s}", .{ atom.atom_index, atom.getName(macho_file) }); | |
| 5357 | for (atom.getRelocs(macho_file)) |rel| { | |
| 5358 | if (rel.type != .branch) continue; | |
| 5359 | if (isReachable(atom, rel, macho_file)) continue; | |
| 5360 | try thunk.symbols.put(gpa, rel.getTargetSymbolRef(atom.*, macho_file), {}); | |
| 5361 | } | |
| 5362 | atom.addExtra(.{ .thunk = thunk_index }, macho_file); | |
| 5363 | } | |
| 5364 | } | |
| 5365 | ||
| 5366 | fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool { | |
| 5367 | const target = rel.getTargetSymbol(atom.*, macho_file); | |
| 5368 | if (target.getSectionFlags().stubs or target.getSectionFlags().objc_stubs) return false; | |
| 5369 | if (atom.out_n_sect != target.getOutputSectionIndex(macho_file)) return false; | |
| 5370 | const target_atom = target.getAtom(macho_file).?; | |
| 5371 | if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false; | |
| 5372 | const saddr = @as(i64, @intCast(atom.getAddress(macho_file))) + @as(i64, @intCast(rel.offset - atom.off)); | |
| 5373 | const taddr: i64 = @intCast(rel.getTargetAddress(atom.*, macho_file)); | |
| 5374 | _ = math.cast(i28, taddr + rel.addend - saddr) orelse return false; | |
| 5375 | return true; | |
| 5376 | } | |
| 5377 | ||
| 5378 | /// Branch instruction has 26 bits immediate but is 4 byte aligned. | |
| 5379 | const jump_bits = @bitSizeOf(i28); | |
| 5380 | const max_distance = (1 << (jump_bits - 1)); | |
| 5381 | ||
| 5382 | /// A branch will need an extender if its target is larger than | |
| 5383 | /// `2^(jump_bits - 1) - margin` where margin is some arbitrary number. | |
| 5384 | /// mold uses 5MiB margin, while ld64 uses 4MiB margin. We will follow mold | |
| 5385 | /// and assume margin to be 5MiB. | |
| 5386 | const max_allowed_distance = max_distance - 0x500_000; |
src/link/MachO/Atom.zig+1-1| ... | ... | @@ -1220,6 +1220,6 @@ const MachO = @import("../MachO.zig"); |
| 1220 | 1220 | const Object = @import("Object.zig"); |
| 1221 | 1221 | const Relocation = @import("Relocation.zig"); |
| 1222 | 1222 | const Symbol = @import("Symbol.zig"); |
| 1223 | const Thunk = @import("thunks.zig").Thunk; | |
| 1223 | const Thunk = @import("Thunk.zig"); | |
| 1224 | 1224 | const UnwindInfo = @import("UnwindInfo.zig"); |
| 1225 | 1225 | const dev = @import("../../dev.zig"); |
src/link/MachO/Thunk.zig created+125| ... | ... | @@ -0,0 +1,125 @@ |
| 1 | value: u64 = 0, | |
| 2 | out_n_sect: u8 = 0, | |
| 3 | symbols: std.AutoArrayHashMapUnmanaged(MachO.Ref, void) = .{}, | |
| 4 | output_symtab_ctx: MachO.SymtabCtx = .{}, | |
| 5 | ||
| 6 | pub fn deinit(thunk: *Thunk, allocator: Allocator) void { | |
| 7 | thunk.symbols.deinit(allocator); | |
| 8 | } | |
| 9 | ||
| 10 | pub fn size(thunk: Thunk) usize { | |
| 11 | return thunk.symbols.keys().len * trampoline_size; | |
| 12 | } | |
| 13 | ||
| 14 | pub fn getAddress(thunk: Thunk, macho_file: *MachO) u64 { | |
| 15 | const header = macho_file.sections.items(.header)[thunk.out_n_sect]; | |
| 16 | return header.addr + thunk.value; | |
| 17 | } | |
| 18 | ||
| 19 | pub fn getTargetAddress(thunk: Thunk, ref: MachO.Ref, macho_file: *MachO) u64 { | |
| 20 | return thunk.getAddress(macho_file) + thunk.symbols.getIndex(ref).? * trampoline_size; | |
| 21 | } | |
| 22 | ||
| 23 | pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void { | |
| 24 | for (thunk.symbols.keys(), 0..) |ref, i| { | |
| 25 | const sym = ref.getSymbol(macho_file).?; | |
| 26 | const saddr = thunk.getAddress(macho_file) + i * trampoline_size; | |
| 27 | const taddr = sym.getAddress(.{}, macho_file); | |
| 28 | const pages = try aarch64.calcNumberOfPages(@intCast(saddr), @intCast(taddr)); | |
| 29 | try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little); | |
| 30 | const off: u12 = @truncate(taddr); | |
| 31 | try writer.writeInt(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32(), .little); | |
| 32 | try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little); | |
| 33 | } | |
| 34 | } | |
| 35 | ||
| 36 | pub fn calcSymtabSize(thunk: *Thunk, macho_file: *MachO) void { | |
| 37 | thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len)); | |
| 38 | for (thunk.symbols.keys()) |ref| { | |
| 39 | const sym = ref.getSymbol(macho_file).?; | |
| 40 | thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + "__thunk".len + 1)); | |
| 41 | } | |
| 42 | } | |
| 43 | ||
| 44 | pub fn writeSymtab(thunk: Thunk, macho_file: *MachO, ctx: anytype) void { | |
| 45 | var n_strx = thunk.output_symtab_ctx.stroff; | |
| 46 | for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| { | |
| 47 | const sym = ref.getSymbol(macho_file).?; | |
| 48 | const name = sym.getName(macho_file); | |
| 49 | const out_sym = &ctx.symtab.items[ilocal]; | |
| 50 | out_sym.n_strx = n_strx; | |
| 51 | @memcpy(ctx.strtab.items[n_strx..][0..name.len], name); | |
| 52 | n_strx += @intCast(name.len); | |
| 53 | @memcpy(ctx.strtab.items[n_strx..][0.."__thunk".len], "__thunk"); | |
| 54 | n_strx += @intCast("__thunk".len); | |
| 55 | ctx.strtab.items[n_strx] = 0; | |
| 56 | n_strx += 1; | |
| 57 | out_sym.n_type = macho.N_SECT; | |
| 58 | out_sym.n_sect = @intCast(thunk.out_n_sect + 1); | |
| 59 | out_sym.n_value = @intCast(thunk.getTargetAddress(ref, macho_file)); | |
| 60 | out_sym.n_desc = 0; | |
| 61 | } | |
| 62 | } | |
| 63 | ||
| 64 | pub fn format( | |
| 65 | thunk: Thunk, | |
| 66 | comptime unused_fmt_string: []const u8, | |
| 67 | options: std.fmt.FormatOptions, | |
| 68 | writer: anytype, | |
| 69 | ) !void { | |
| 70 | _ = thunk; | |
| 71 | _ = unused_fmt_string; | |
| 72 | _ = options; | |
| 73 | _ = writer; | |
| 74 | @compileError("do not format Thunk directly"); | |
| 75 | } | |
| 76 | ||
| 77 | pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(format2) { | |
| 78 | return .{ .data = .{ | |
| 79 | .thunk = thunk, | |
| 80 | .macho_file = macho_file, | |
| 81 | } }; | |
| 82 | } | |
| 83 | ||
| 84 | const FormatContext = struct { | |
| 85 | thunk: Thunk, | |
| 86 | macho_file: *MachO, | |
| 87 | }; | |
| 88 | ||
| 89 | fn format2( | |
| 90 | ctx: FormatContext, | |
| 91 | comptime unused_fmt_string: []const u8, | |
| 92 | options: std.fmt.FormatOptions, | |
| 93 | writer: anytype, | |
| 94 | ) !void { | |
| 95 | _ = options; | |
| 96 | _ = unused_fmt_string; | |
| 97 | const thunk = ctx.thunk; | |
| 98 | const macho_file = ctx.macho_file; | |
| 99 | try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() }); | |
| 100 | for (thunk.symbols.keys()) |ref| { | |
| 101 | const sym = ref.getSymbol(macho_file).?; | |
| 102 | try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value }); | |
| 103 | } | |
| 104 | } | |
| 105 | ||
| 106 | const trampoline_size = 3 * @sizeOf(u32); | |
| 107 | ||
| 108 | pub const Index = u32; | |
| 109 | ||
| 110 | const aarch64 = @import("../aarch64.zig"); | |
| 111 | const assert = std.debug.assert; | |
| 112 | const log = std.log.scoped(.link); | |
| 113 | const macho = std.macho; | |
| 114 | const math = std.math; | |
| 115 | const mem = std.mem; | |
| 116 | const std = @import("std"); | |
| 117 | const trace = @import("../../tracy.zig").trace; | |
| 118 | ||
| 119 | const Allocator = mem.Allocator; | |
| 120 | const Atom = @import("Atom.zig"); | |
| 121 | const MachO = @import("../MachO.zig"); | |
| 122 | const Relocation = @import("Relocation.zig"); | |
| 123 | const Symbol = @import("Symbol.zig"); | |
| 124 | ||
| 125 | const Thunk = @This(); |
src/link/MachO/synthetic.zig+1-1| ... | ... | @@ -204,7 +204,7 @@ pub const StubsHelperSection = struct { |
| 204 | 204 | for (macho_file.stubs.symbols.items) |ref| { |
| 205 | 205 | const sym = ref.getSymbol(macho_file).?; |
| 206 | 206 | if (sym.flags.weak) continue; |
| 207 | const offset = macho_file.lazy_bind.offsets.items[idx]; | |
| 207 | const offset = macho_file.lazy_bind_section.offsets.items[idx]; | |
| 208 | 208 | const source: i64 = @intCast(sect.addr + preamble_size + entry_size * idx); |
| 209 | 209 | const target: i64 = @intCast(sect.addr); |
| 210 | 210 | switch (cpu_arch) { |
src/link/MachO/thunks.zig deleted-218| ... | ... | @@ -1,218 +0,0 @@ |
| 1 | pub fn createThunks(sect_id: u8, macho_file: *MachO) !void { | |
| 2 | const tracy = trace(@src()); | |
| 3 | defer tracy.end(); | |
| 4 | ||
| 5 | const gpa = macho_file.base.comp.gpa; | |
| 6 | const slice = macho_file.sections.slice(); | |
| 7 | const header = &slice.items(.header)[sect_id]; | |
| 8 | const thnks = &slice.items(.thunks)[sect_id]; | |
| 9 | const atoms = slice.items(.atoms)[sect_id].items; | |
| 10 | assert(atoms.len > 0); | |
| 11 | ||
| 12 | for (atoms) |ref| { | |
| 13 | ref.getAtom(macho_file).?.value = @bitCast(@as(i64, -1)); | |
| 14 | } | |
| 15 | ||
| 16 | var i: usize = 0; | |
| 17 | while (i < atoms.len) { | |
| 18 | const start = i; | |
| 19 | const start_atom = atoms[start].getAtom(macho_file).?; | |
| 20 | assert(start_atom.isAlive()); | |
| 21 | start_atom.value = advance(header, start_atom.size, start_atom.alignment); | |
| 22 | i += 1; | |
| 23 | ||
| 24 | while (i < atoms.len and | |
| 25 | header.size - start_atom.value < max_allowed_distance) : (i += 1) | |
| 26 | { | |
| 27 | const atom = atoms[i].getAtom(macho_file).?; | |
| 28 | assert(atom.isAlive()); | |
| 29 | atom.value = advance(header, atom.size, atom.alignment); | |
| 30 | } | |
| 31 | ||
| 32 | // Insert a thunk at the group end | |
| 33 | const thunk_index = try macho_file.addThunk(); | |
| 34 | const thunk = macho_file.getThunk(thunk_index); | |
| 35 | thunk.out_n_sect = sect_id; | |
| 36 | try thnks.append(gpa, thunk_index); | |
| 37 | ||
| 38 | // Scan relocs in the group and create trampolines for any unreachable callsite | |
| 39 | try scanRelocs(thunk_index, gpa, atoms[start..i], macho_file); | |
| 40 | thunk.value = advance(header, thunk.size(), .@"4"); | |
| 41 | ||
| 42 | log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(macho_file) }); | |
| 43 | } | |
| 44 | } | |
| 45 | ||
| 46 | fn advance(sect: *macho.section_64, size: u64, alignment: Atom.Alignment) u64 { | |
| 47 | const offset = alignment.forward(sect.size); | |
| 48 | const padding = offset - sect.size; | |
| 49 | sect.size += padding + size; | |
| 50 | sect.@"align" = @max(sect.@"align", alignment.toLog2Units()); | |
| 51 | return offset; | |
| 52 | } | |
| 53 | ||
| 54 | fn scanRelocs(thunk_index: Thunk.Index, gpa: Allocator, atoms: []const MachO.Ref, macho_file: *MachO) !void { | |
| 55 | const tracy = trace(@src()); | |
| 56 | defer tracy.end(); | |
| 57 | ||
| 58 | const thunk = macho_file.getThunk(thunk_index); | |
| 59 | ||
| 60 | for (atoms) |ref| { | |
| 61 | const atom = ref.getAtom(macho_file).?; | |
| 62 | log.debug("atom({d}) {s}", .{ atom.atom_index, atom.getName(macho_file) }); | |
| 63 | for (atom.getRelocs(macho_file)) |rel| { | |
| 64 | if (rel.type != .branch) continue; | |
| 65 | if (isReachable(atom, rel, macho_file)) continue; | |
| 66 | try thunk.symbols.put(gpa, rel.getTargetSymbolRef(atom.*, macho_file), {}); | |
| 67 | } | |
| 68 | atom.addExtra(.{ .thunk = thunk_index }, macho_file); | |
| 69 | } | |
| 70 | } | |
| 71 | ||
| 72 | fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool { | |
| 73 | const target = rel.getTargetSymbol(atom.*, macho_file); | |
| 74 | if (target.getSectionFlags().stubs or target.getSectionFlags().objc_stubs) return false; | |
| 75 | if (atom.out_n_sect != target.getOutputSectionIndex(macho_file)) return false; | |
| 76 | const target_atom = target.getAtom(macho_file).?; | |
| 77 | if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false; | |
| 78 | const saddr = @as(i64, @intCast(atom.getAddress(macho_file))) + @as(i64, @intCast(rel.offset - atom.off)); | |
| 79 | const taddr: i64 = @intCast(rel.getTargetAddress(atom.*, macho_file)); | |
| 80 | _ = math.cast(i28, taddr + rel.addend - saddr) orelse return false; | |
| 81 | return true; | |
| 82 | } | |
| 83 | ||
| 84 | pub const Thunk = struct { | |
| 85 | value: u64 = 0, | |
| 86 | out_n_sect: u8 = 0, | |
| 87 | symbols: std.AutoArrayHashMapUnmanaged(MachO.Ref, void) = .{}, | |
| 88 | output_symtab_ctx: MachO.SymtabCtx = .{}, | |
| 89 | ||
| 90 | pub fn deinit(thunk: *Thunk, allocator: Allocator) void { | |
| 91 | thunk.symbols.deinit(allocator); | |
| 92 | } | |
| 93 | ||
| 94 | pub fn size(thunk: Thunk) usize { | |
| 95 | return thunk.symbols.keys().len * trampoline_size; | |
| 96 | } | |
| 97 | ||
| 98 | pub fn getAddress(thunk: Thunk, macho_file: *MachO) u64 { | |
| 99 | const header = macho_file.sections.items(.header)[thunk.out_n_sect]; | |
| 100 | return header.addr + thunk.value; | |
| 101 | } | |
| 102 | ||
| 103 | pub fn getTargetAddress(thunk: Thunk, ref: MachO.Ref, macho_file: *MachO) u64 { | |
| 104 | return thunk.getAddress(macho_file) + thunk.symbols.getIndex(ref).? * trampoline_size; | |
| 105 | } | |
| 106 | ||
| 107 | pub fn write(thunk: Thunk, macho_file: *MachO, writer: anytype) !void { | |
| 108 | for (thunk.symbols.keys(), 0..) |ref, i| { | |
| 109 | const sym = ref.getSymbol(macho_file).?; | |
| 110 | const saddr = thunk.getAddress(macho_file) + i * trampoline_size; | |
| 111 | const taddr = sym.getAddress(.{}, macho_file); | |
| 112 | const pages = try aarch64.calcNumberOfPages(@intCast(saddr), @intCast(taddr)); | |
| 113 | try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little); | |
| 114 | const off: u12 = @truncate(taddr); | |
| 115 | try writer.writeInt(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32(), .little); | |
| 116 | try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little); | |
| 117 | } | |
| 118 | } | |
| 119 | ||
| 120 | pub fn calcSymtabSize(thunk: *Thunk, macho_file: *MachO) void { | |
| 121 | thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len)); | |
| 122 | for (thunk.symbols.keys()) |ref| { | |
| 123 | const sym = ref.getSymbol(macho_file).?; | |
| 124 | thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + "__thunk".len + 1)); | |
| 125 | } | |
| 126 | } | |
| 127 | ||
| 128 | pub fn writeSymtab(thunk: Thunk, macho_file: *MachO, ctx: anytype) void { | |
| 129 | var n_strx = thunk.output_symtab_ctx.stroff; | |
| 130 | for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| { | |
| 131 | const sym = ref.getSymbol(macho_file).?; | |
| 132 | const name = sym.getName(macho_file); | |
| 133 | const out_sym = &ctx.symtab.items[ilocal]; | |
| 134 | out_sym.n_strx = n_strx; | |
| 135 | @memcpy(ctx.strtab.items[n_strx..][0..name.len], name); | |
| 136 | n_strx += @intCast(name.len); | |
| 137 | @memcpy(ctx.strtab.items[n_strx..][0.."__thunk".len], "__thunk"); | |
| 138 | n_strx += @intCast("__thunk".len); | |
| 139 | ctx.strtab.items[n_strx] = 0; | |
| 140 | n_strx += 1; | |
| 141 | out_sym.n_type = macho.N_SECT; | |
| 142 | out_sym.n_sect = @intCast(thunk.out_n_sect + 1); | |
| 143 | out_sym.n_value = @intCast(thunk.getTargetAddress(ref, macho_file)); | |
| 144 | out_sym.n_desc = 0; | |
| 145 | } | |
| 146 | } | |
| 147 | ||
| 148 | pub fn format( | |
| 149 | thunk: Thunk, | |
| 150 | comptime unused_fmt_string: []const u8, | |
| 151 | options: std.fmt.FormatOptions, | |
| 152 | writer: anytype, | |
| 153 | ) !void { | |
| 154 | _ = thunk; | |
| 155 | _ = unused_fmt_string; | |
| 156 | _ = options; | |
| 157 | _ = writer; | |
| 158 | @compileError("do not format Thunk directly"); | |
| 159 | } | |
| 160 | ||
| 161 | pub fn fmt(thunk: Thunk, macho_file: *MachO) std.fmt.Formatter(format2) { | |
| 162 | return .{ .data = .{ | |
| 163 | .thunk = thunk, | |
| 164 | .macho_file = macho_file, | |
| 165 | } }; | |
| 166 | } | |
| 167 | ||
| 168 | const FormatContext = struct { | |
| 169 | thunk: Thunk, | |
| 170 | macho_file: *MachO, | |
| 171 | }; | |
| 172 | ||
| 173 | fn format2( | |
| 174 | ctx: FormatContext, | |
| 175 | comptime unused_fmt_string: []const u8, | |
| 176 | options: std.fmt.FormatOptions, | |
| 177 | writer: anytype, | |
| 178 | ) !void { | |
| 179 | _ = options; | |
| 180 | _ = unused_fmt_string; | |
| 181 | const thunk = ctx.thunk; | |
| 182 | const macho_file = ctx.macho_file; | |
| 183 | try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size() }); | |
| 184 | for (thunk.symbols.keys()) |ref| { | |
| 185 | const sym = ref.getSymbol(macho_file).?; | |
| 186 | try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.getName(macho_file), sym.value }); | |
| 187 | } | |
| 188 | } | |
| 189 | ||
| 190 | const trampoline_size = 3 * @sizeOf(u32); | |
| 191 | ||
| 192 | pub const Index = u32; | |
| 193 | }; | |
| 194 | ||
| 195 | /// Branch instruction has 26 bits immediate but is 4 byte aligned. | |
| 196 | const jump_bits = @bitSizeOf(i28); | |
| 197 | const max_distance = (1 << (jump_bits - 1)); | |
| 198 | ||
| 199 | /// A branch will need an extender if its target is larger than | |
| 200 | /// `2^(jump_bits - 1) - margin` where margin is some arbitrary number. | |
| 201 | /// mold uses 5MiB margin, while ld64 uses 4MiB margin. We will follow mold | |
| 202 | /// and assume margin to be 5MiB. | |
| 203 | const max_allowed_distance = max_distance - 0x500_000; | |
| 204 | ||
| 205 | const aarch64 = @import("../aarch64.zig"); | |
| 206 | const assert = std.debug.assert; | |
| 207 | const log = std.log.scoped(.link); | |
| 208 | const macho = std.macho; | |
| 209 | const math = std.math; | |
| 210 | const mem = std.mem; | |
| 211 | const std = @import("std"); | |
| 212 | const trace = @import("../../tracy.zig").trace; | |
| 213 | ||
| 214 | const Allocator = mem.Allocator; | |
| 215 | const Atom = @import("Atom.zig"); | |
| 216 | const MachO = @import("../MachO.zig"); | |
| 217 | const Relocation = @import("Relocation.zig"); | |
| 218 | const Symbol = @import("Symbol.zig"); |