| author | |
| committer | |
| log | c62487da76b08a0dfb69fbf76501250ca065c140 |
| tree | 1ef46d45f3ff9edaade3805d9ab5a9910cb8571b |
| parent | ba8d3f69ca65738f27deea43e795f5e787a061f2 |
| signature |
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,7 +613,7 @@ set(ZIG_STAGE2_SOURCES |
| 613 | src/link/Elf/relocatable.zig | 613 | src/link/Elf/relocatable.zig |
| 614 | src/link/Elf/relocation.zig | 614 | src/link/Elf/relocation.zig |
| 615 | src/link/Elf/synthetic_sections.zig | 615 | src/link/Elf/synthetic_sections.zig |
| 616 | src/link/Elf/thunks.zig | 616 | src/link/Elf/Thunk.zig |
| 617 | src/link/MachO.zig | 617 | src/link/MachO.zig |
| 618 | src/link/MachO/Archive.zig | 618 | src/link/MachO/Archive.zig |
| 619 | src/link/MachO/Atom.zig | 619 | src/link/MachO/Atom.zig |
| ... | @@ -638,7 +638,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -638,7 +638,7 @@ set(ZIG_STAGE2_SOURCES |
| 638 | src/link/MachO/load_commands.zig | 638 | src/link/MachO/load_commands.zig |
| 639 | src/link/MachO/relocatable.zig | 639 | src/link/MachO/relocatable.zig |
| 640 | src/link/MachO/synthetic.zig | 640 | src/link/MachO/synthetic.zig |
| 641 | src/link/MachO/thunks.zig | 641 | src/link/MachO/Thunk.zig |
| 642 | src/link/MachO/uuid.zig | 642 | src/link/MachO/uuid.zig |
| 643 | src/link/NvPtx.zig | 643 | src/link/NvPtx.zig |
| 644 | src/link/Plan9.zig | 644 | src/link/Plan9.zig |
src/arch/aarch64/bits.zig+4-4| ... | @@ -1069,7 +1069,7 @@ pub const Instruction = union(enum) { | ... | @@ -1069,7 +1069,7 @@ pub const Instruction = union(enum) { |
| 1069 | }; | 1069 | }; |
| 1070 | } | 1070 | } |
| 1071 | 1071 | ||
| 1072 | fn bitfield( | 1072 | fn initBitfield( |
| 1073 | opc: u2, | 1073 | opc: u2, |
| 1074 | n: u1, | 1074 | n: u1, |
| 1075 | rd: Register, | 1075 | rd: Register, |
| ... | @@ -1579,7 +1579,7 @@ pub const Instruction = union(enum) { | ... | @@ -1579,7 +1579,7 @@ pub const Instruction = union(enum) { |
| 1579 | 64 => 0b1, | 1579 | 64 => 0b1, |
| 1580 | else => unreachable, // unexpected register size | 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 | pub fn bfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction { | 1585 | pub fn bfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction { |
| ... | @@ -1588,7 +1588,7 @@ pub const Instruction = union(enum) { | ... | @@ -1588,7 +1588,7 @@ pub const Instruction = union(enum) { |
| 1588 | 64 => 0b1, | 1588 | 64 => 0b1, |
| 1589 | else => unreachable, // unexpected register size | 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 | pub fn ubfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction { | 1594 | pub fn ubfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction { |
| ... | @@ -1597,7 +1597,7 @@ pub const Instruction = union(enum) { | ... | @@ -1597,7 +1597,7 @@ pub const Instruction = union(enum) { |
| 1597 | 64 => 0b1, | 1597 | 64 => 0b1, |
| 1598 | else => unreachable, // unexpected register size | 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 | pub fn asrImmediate(rd: Register, rn: Register, shift: u6) Instruction { | 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,7 +662,7 @@ pub const Instruction = union(enum) { |
| 662 | }; | 662 | }; |
| 663 | } | 663 | } |
| 664 | 664 | ||
| 665 | fn multiply( | 665 | fn initMultiply( |
| 666 | cond: Condition, | 666 | cond: Condition, |
| 667 | set_cond: u1, | 667 | set_cond: u1, |
| 668 | rd: Register, | 668 | rd: Register, |
| ... | @@ -864,7 +864,7 @@ pub const Instruction = union(enum) { | ... | @@ -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 | return Instruction{ | 868 | return Instruction{ |
| 869 | .branch = .{ | 869 | .branch = .{ |
| 870 | .cond = @intFromEnum(cond), | 870 | .cond = @intFromEnum(cond), |
| ... | @@ -900,7 +900,7 @@ pub const Instruction = union(enum) { | ... | @@ -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 | return Instruction{ | 904 | return Instruction{ |
| 905 | .breakpoint = .{ | 905 | .breakpoint = .{ |
| 906 | .imm12 = @as(u12, @truncate(imm >> 4)), | 906 | .imm12 = @as(u12, @truncate(imm >> 4)), |
| ... | @@ -1087,19 +1087,19 @@ pub const Instruction = union(enum) { | ... | @@ -1087,19 +1087,19 @@ pub const Instruction = union(enum) { |
| 1087 | // Multiply | 1087 | // Multiply |
| 1088 | 1088 | ||
| 1089 | pub fn mul(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { | 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 | pub fn muls(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { | 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 | pub fn mla(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction { | 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 | pub fn mlas(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction { | 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 | // Multiply long | 1105 | // Multiply long |
| ... | @@ -1261,11 +1261,11 @@ pub const Instruction = union(enum) { | ... | @@ -1261,11 +1261,11 @@ pub const Instruction = union(enum) { |
| 1261 | // Branch | 1261 | // Branch |
| 1262 | 1262 | ||
| 1263 | pub fn b(cond: Condition, offset: i26) Instruction { | 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 | pub fn bl(cond: Condition, offset: i26) Instruction { | 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 | // Branch and exchange | 1271 | // Branch and exchange |
| ... | @@ -1289,7 +1289,7 @@ pub const Instruction = union(enum) { | ... | @@ -1289,7 +1289,7 @@ pub const Instruction = union(enum) { |
| 1289 | // Breakpoint | 1289 | // Breakpoint |
| 1290 | 1290 | ||
| 1291 | pub fn bkpt(imm: u16) Instruction { | 1291 | pub fn bkpt(imm: u16) Instruction { |
| 1292 | return breakpoint(imm); | 1292 | return initBreakpoint(imm); |
| 1293 | } | 1293 | } |
| 1294 | 1294 | ||
| 1295 | // Aliases | 1295 | // Aliases |
src/arch/x86_64/CodeGen.zig+1-1| ... | @@ -15563,7 +15563,7 @@ fn genLazySymbolRef( | ... | @@ -15563,7 +15563,7 @@ fn genLazySymbolRef( |
| 15563 | .mov => try self.asmRegisterMemory( | 15563 | .mov => try self.asmRegisterMemory( |
| 15564 | .{ ._, tag }, | 15564 | .{ ._, tag }, |
| 15565 | reg.to64(), | 15565 | reg.to64(), |
| 15566 | Memory.sib(.qword, .{ .base = .{ .reg = reg.to64() } }), | 15566 | Memory.initSib(.qword, .{ .base = .{ .reg = reg.to64() } }), |
| 15567 | ), | 15567 | ), |
| 15568 | else => unreachable, | 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,7 +95,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { |
| 95 | 95 | ||
| 96 | if (modrm.rip()) { | 96 | if (modrm.rip()) { |
| 97 | return inst(act_enc, .{ | 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 | .op2 = op2, | 99 | .op2 = op2, |
| 100 | }); | 100 | }); |
| 101 | } | 101 | } |
| ... | @@ -106,7 +106,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { | ... | @@ -106,7 +106,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { |
| 106 | else | 106 | else |
| 107 | parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, 64); | 107 | parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, 64); |
| 108 | return inst(act_enc, .{ | 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 | .base = if (base) |base_reg| .{ .reg = base_reg } else .none, | 110 | .base = if (base) |base_reg| .{ .reg = base_reg } else .none, |
| 111 | .scale_index = scale_index, | 111 | .scale_index = scale_index, |
| 112 | .disp = disp, | 112 | .disp = disp, |
| ... | @@ -119,14 +119,14 @@ pub fn next(dis: *Disassembler) Error!?Instruction { | ... | @@ -119,14 +119,14 @@ pub fn next(dis: *Disassembler) Error!?Instruction { |
| 119 | const offset = try dis.parseOffset(); | 119 | const offset = try dis.parseOffset(); |
| 120 | return inst(enc, .{ | 120 | return inst(enc, .{ |
| 121 | .op1 = .{ .reg = Register.rax.toBitSize(enc.data.ops[0].regBitSize()) }, | 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 | .td => { | 125 | .td => { |
| 126 | const seg = segmentRegister(prefixes.legacy); | 126 | const seg = segmentRegister(prefixes.legacy); |
| 127 | const offset = try dis.parseOffset(); | 127 | const offset = try dis.parseOffset(); |
| 128 | return inst(enc, .{ | 128 | return inst(enc, .{ |
| 129 | .op1 = .{ .mem = Memory.moffs(seg, offset) }, | 129 | .op1 = .{ .mem = Memory.initMoffs(seg, offset) }, |
| 130 | .op2 = .{ .reg = Register.rax.toBitSize(enc.data.ops[1].regBitSize()) }, | 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,7 +153,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { |
| 153 | 153 | ||
| 154 | if (modrm.rip()) { | 154 | if (modrm.rip()) { |
| 155 | return inst(enc, .{ | 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 | .op2 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.r, prefixes.rex, src_bit_size) }, | 157 | .op2 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.r, prefixes.rex, src_bit_size) }, |
| 158 | .op3 = op3, | 158 | .op3 = op3, |
| 159 | }); | 159 | }); |
| ... | @@ -165,7 +165,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { | ... | @@ -165,7 +165,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { |
| 165 | else | 165 | else |
| 166 | parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, 64); | 166 | parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, 64); |
| 167 | return inst(enc, .{ | 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 | .base = if (base) |base_reg| .{ .reg = base_reg } else .none, | 169 | .base = if (base) |base_reg| .{ .reg = base_reg } else .none, |
| 170 | .scale_index = scale_index, | 170 | .scale_index = scale_index, |
| 171 | .disp = disp, | 171 | .disp = disp, |
| ... | @@ -203,7 +203,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { | ... | @@ -203,7 +203,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { |
| 203 | if (modrm.rip()) { | 203 | if (modrm.rip()) { |
| 204 | return inst(enc, .{ | 204 | return inst(enc, .{ |
| 205 | .op1 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.r, prefixes.rex, dst_bit_size) }, | 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 | .op3 = op3, | 207 | .op3 = op3, |
| 208 | }); | 208 | }); |
| 209 | } | 209 | } |
| ... | @@ -215,7 +215,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { | ... | @@ -215,7 +215,7 @@ pub fn next(dis: *Disassembler) Error!?Instruction { |
| 215 | parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, 64); | 215 | parseGpRegister(modrm.op2, prefixes.rex.b, prefixes.rex, 64); |
| 216 | return inst(enc, .{ | 216 | return inst(enc, .{ |
| 217 | .op1 = .{ .reg = parseGpRegister(modrm.op1, prefixes.rex.r, prefixes.rex, dst_bit_size) }, | 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 | .base = if (base) |base_reg| .{ .reg = base_reg } else .none, | 219 | .base = if (base) |base_reg| .{ .reg = base_reg } else .none, |
| 220 | .scale_index = scale_index, | 220 | .scale_index = scale_index, |
| 221 | .disp = disp, | 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,13 +200,13 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { |
| 200 | }); | 200 | }); |
| 201 | try lower.emit(.none, .lea, &.{ | 201 | try lower.emit(.none, .lea, &.{ |
| 202 | .{ .reg = inst.data.ri.r1 }, | 202 | .{ .reg = inst.data.ri.r1 }, |
| 203 | .{ .mem = Memory.sib(.qword, .{ | 203 | .{ .mem = Memory.initSib(.qword, .{ |
| 204 | .base = .{ .reg = inst.data.ri.r1 }, | 204 | .base = .{ .reg = inst.data.ri.r1 }, |
| 205 | .disp = -page_size, | 205 | .disp = -page_size, |
| 206 | }) }, | 206 | }) }, |
| 207 | }); | 207 | }); |
| 208 | try lower.emit(.none, .@"test", &.{ | 208 | try lower.emit(.none, .@"test", &.{ |
| 209 | .{ .mem = Memory.sib(.dword, .{ | 209 | .{ .mem = Memory.initSib(.dword, .{ |
| 210 | .base = .{ .reg = inst.data.ri.r1 }, | 210 | .base = .{ .reg = inst.data.ri.r1 }, |
| 211 | }) }, | 211 | }) }, |
| 212 | .{ .reg = inst.data.ri.r1.to32() }, | 212 | .{ .reg = inst.data.ri.r1.to32() }, |
| ... | @@ -220,7 +220,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { | ... | @@ -220,7 +220,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { |
| 220 | var offset = page_size; | 220 | var offset = page_size; |
| 221 | while (offset < @as(i32, @bitCast(inst.data.ri.i))) : (offset += page_size) { | 221 | while (offset < @as(i32, @bitCast(inst.data.ri.i))) : (offset += page_size) { |
| 222 | try lower.emit(.none, .@"test", &.{ | 222 | try lower.emit(.none, .@"test", &.{ |
| 223 | .{ .mem = Memory.sib(.dword, .{ | 223 | .{ .mem = Memory.initSib(.dword, .{ |
| 224 | .base = .{ .reg = inst.data.ri.r1 }, | 224 | .base = .{ .reg = inst.data.ri.r1 }, |
| 225 | .disp = -offset, | 225 | .disp = -offset, |
| 226 | }) }, | 226 | }) }, |
| ... | @@ -246,7 +246,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { | ... | @@ -246,7 +246,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { |
| 246 | }, | 246 | }, |
| 247 | .pseudo_probe_adjust_loop_rr => { | 247 | .pseudo_probe_adjust_loop_rr => { |
| 248 | try lower.emit(.none, .@"test", &.{ | 248 | try lower.emit(.none, .@"test", &.{ |
| 249 | .{ .mem = Memory.sib(.dword, .{ | 249 | .{ .mem = Memory.initSib(.dword, .{ |
| 250 | .base = .{ .reg = inst.data.rr.r1 }, | 250 | .base = .{ .reg = inst.data.rr.r1 }, |
| 251 | .scale_index = .{ .scale = 1, .index = inst.data.rr.r2 }, | 251 | .scale_index = .{ .scale = 1, .index = inst.data.rr.r2 }, |
| 252 | .disp = -page_size, | 252 | .disp = -page_size, |
| ... | @@ -417,7 +417,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) | ... | @@ -417,7 +417,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 417 | lower.result_insts[lower.result_insts_len] = | 417 | lower.result_insts[lower.result_insts_len] = |
| 418 | try Instruction.new(.none, .lea, &[_]Operand{ | 418 | try Instruction.new(.none, .lea, &[_]Operand{ |
| 419 | .{ .reg = .rdi }, | 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 | lower.result_insts_len += 1; | 422 | lower.result_insts_len += 1; |
| 423 | _ = lower.reloc(.{ | 423 | _ = lower.reloc(.{ |
| ... | @@ -430,7 +430,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) | ... | @@ -430,7 +430,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 430 | lower.result_insts_len += 1; | 430 | lower.result_insts_len += 1; |
| 431 | _ = lower.reloc(.{ .linker_dtpoff = sym_index }, 0); | 431 | _ = lower.reloc(.{ .linker_dtpoff = sym_index }, 0); |
| 432 | emit_mnemonic = .lea; | 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 | .base = .{ .reg = .rax }, | 434 | .base = .{ .reg = .rax }, |
| 435 | .disp = std.math.minInt(i32), | 435 | .disp = std.math.minInt(i32), |
| 436 | }) }; | 436 | }) }; |
| ... | @@ -439,12 +439,12 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) | ... | @@ -439,12 +439,12 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 439 | lower.result_insts[lower.result_insts_len] = | 439 | lower.result_insts[lower.result_insts_len] = |
| 440 | try Instruction.new(.none, .mov, &[_]Operand{ | 440 | try Instruction.new(.none, .mov, &[_]Operand{ |
| 441 | .{ .reg = .rax }, | 441 | .{ .reg = .rax }, |
| 442 | .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .fs } }) }, | 442 | .{ .mem = Memory.initSib(.qword, .{ .base = .{ .reg = .fs } }) }, |
| 443 | }); | 443 | }); |
| 444 | lower.result_insts_len += 1; | 444 | lower.result_insts_len += 1; |
| 445 | _ = lower.reloc(.{ .linker_reloc = sym_index }, 0); | 445 | _ = lower.reloc(.{ .linker_reloc = sym_index }, 0); |
| 446 | emit_mnemonic = .lea; | 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 | .base = .{ .reg = .rax }, | 448 | .base = .{ .reg = .rax }, |
| 449 | .disp = std.math.minInt(i32), | 449 | .disp = std.math.minInt(i32), |
| 450 | }) }; | 450 | }) }; |
| ... | @@ -455,7 +455,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) | ... | @@ -455,7 +455,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 455 | if (lower.pic) switch (mnemonic) { | 455 | if (lower.pic) switch (mnemonic) { |
| 456 | .lea => { | 456 | .lea => { |
| 457 | if (elf_sym.flags.is_extern_ptr) emit_mnemonic = .mov; | 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 | .mov => { | 460 | .mov => { |
| 461 | if (elf_sym.flags.is_extern_ptr) { | 461 | if (elf_sym.flags.is_extern_ptr) { |
| ... | @@ -463,25 +463,25 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) | ... | @@ -463,25 +463,25 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 463 | lower.result_insts[lower.result_insts_len] = | 463 | lower.result_insts[lower.result_insts_len] = |
| 464 | try Instruction.new(.none, .mov, &[_]Operand{ | 464 | try Instruction.new(.none, .mov, &[_]Operand{ |
| 465 | .{ .reg = reg.to64() }, | 465 | .{ .reg = reg.to64() }, |
| 466 | .{ .mem = Memory.rip(.qword, 0) }, | 466 | .{ .mem = Memory.initRip(.qword, 0) }, |
| 467 | }); | 467 | }); |
| 468 | lower.result_insts_len += 1; | 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 | .reg = reg.to64(), | 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 | else => unreachable, | 475 | else => unreachable, |
| 476 | } else switch (mnemonic) { | 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 | .base = .{ .reg = .ds }, | 478 | .base = .{ .reg = .ds }, |
| 479 | }) }, | 479 | }) }, |
| 480 | .lea => { | 480 | .lea => { |
| 481 | emit_mnemonic = .mov; | 481 | emit_mnemonic = .mov; |
| 482 | break :op .{ .imm = Immediate.s(0) }; | 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 | .base = .{ .reg = .ds }, | 485 | .base = .{ .reg = .ds }, |
| 486 | }) }, | 486 | }) }, |
| 487 | else => unreachable, | 487 | else => unreachable, |
| ... | @@ -495,12 +495,12 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) | ... | @@ -495,12 +495,12 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 495 | lower.result_insts[lower.result_insts_len] = | 495 | lower.result_insts[lower.result_insts_len] = |
| 496 | try Instruction.new(.none, .mov, &[_]Operand{ | 496 | try Instruction.new(.none, .mov, &[_]Operand{ |
| 497 | .{ .reg = .rdi }, | 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 | lower.result_insts_len += 1; | 500 | lower.result_insts_len += 1; |
| 501 | lower.result_insts[lower.result_insts_len] = | 501 | lower.result_insts[lower.result_insts_len] = |
| 502 | try Instruction.new(.none, .call, &[_]Operand{ | 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 | lower.result_insts_len += 1; | 505 | lower.result_insts_len += 1; |
| 506 | emit_mnemonic = .mov; | 506 | emit_mnemonic = .mov; |
| ... | @@ -511,7 +511,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) | ... | @@ -511,7 +511,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 511 | break :op switch (mnemonic) { | 511 | break :op switch (mnemonic) { |
| 512 | .lea => { | 512 | .lea => { |
| 513 | if (macho_sym.flags.is_extern_ptr) emit_mnemonic = .mov; | 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 | .mov => { | 516 | .mov => { |
| 517 | if (macho_sym.flags.is_extern_ptr) { | 517 | if (macho_sym.flags.is_extern_ptr) { |
| ... | @@ -519,14 +519,14 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) | ... | @@ -519,14 +519,14 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) |
| 519 | lower.result_insts[lower.result_insts_len] = | 519 | lower.result_insts[lower.result_insts_len] = |
| 520 | try Instruction.new(.none, .mov, &[_]Operand{ | 520 | try Instruction.new(.none, .mov, &[_]Operand{ |
| 521 | .{ .reg = reg.to64() }, | 521 | .{ .reg = reg.to64() }, |
| 522 | .{ .mem = Memory.rip(.qword, 0) }, | 522 | .{ .mem = Memory.initRip(.qword, 0) }, |
| 523 | }); | 523 | }); |
| 524 | lower.result_insts_len += 1; | 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 | .reg = reg.to64(), | 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 | else => unreachable, | 531 | else => unreachable, |
| 532 | }; | 532 | }; |
| ... | @@ -701,7 +701,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { | ... | @@ -701,7 +701,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { |
| 701 | }, extra.off); | 701 | }, extra.off); |
| 702 | break :ops &.{ | 702 | break :ops &.{ |
| 703 | .{ .reg = reg }, | 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 | else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }), | 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,9 +1234,9 @@ pub const Memory = struct { |
| 1234 | .rm => { | 1234 | .rm => { |
| 1235 | if (mem.info.base == .reg and @as(Register, @enumFromInt(mem.base)) == .rip) { | 1235 | if (mem.info.base == .reg and @as(Register, @enumFromInt(mem.base)) == .rip) { |
| 1236 | assert(mem.info.index == .none and mem.info.scale == .@"1"); | 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 | .disp = @bitCast(mem.off), | 1240 | .disp = @bitCast(mem.off), |
| 1241 | .base = switch (mem.info.base) { | 1241 | .base = switch (mem.info.base) { |
| 1242 | .none => .none, | 1242 | .none => .none, |
| ... | @@ -1258,7 +1258,7 @@ pub const Memory = struct { | ... | @@ -1258,7 +1258,7 @@ pub const Memory = struct { |
| 1258 | }, | 1258 | }, |
| 1259 | .off => { | 1259 | .off => { |
| 1260 | assert(mem.info.base == .reg); | 1260 | assert(mem.info.base == .reg); |
| 1261 | return encoder.Instruction.Memory.moffs( | 1261 | return encoder.Instruction.Memory.initMoffs( |
| 1262 | @enumFromInt(mem.base), | 1262 | @enumFromInt(mem.base), |
| 1263 | @as(u64, mem.extra) << 32 | mem.off, | 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,12 +110,12 @@ pub const Instruction = struct { |
| 110 | offset: u64, | 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 | assert(reg.class() == .segment); | 114 | assert(reg.class() == .segment); |
| 115 | return .{ .moffs = .{ .seg = reg, .offset = offset } }; | 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 | disp: i32 = 0, | 119 | disp: i32 = 0, |
| 120 | base: Base = .none, | 120 | base: Base = .none, |
| 121 | scale_index: ?ScaleIndex = null, | 121 | scale_index: ?ScaleIndex = null, |
| ... | @@ -129,7 +129,7 @@ pub const Instruction = struct { | ... | @@ -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 | return .{ .rip = .{ .ptr_size = ptr_size, .disp = displacement } }; | 133 | return .{ .rip = .{ .ptr_size = ptr_size, .disp = displacement } }; |
| 134 | } | 134 | } |
| 135 | 135 | ||
| ... | @@ -1266,7 +1266,7 @@ test "lower MI encoding" { | ... | @@ -1266,7 +1266,7 @@ test "lower MI encoding" { |
| 1266 | try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); | 1266 | try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); |
| 1267 | 1267 | ||
| 1268 | try enc.encode(.mov, &.{ | 1268 | try enc.encode(.mov, &.{ |
| 1269 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) }, | 1269 | .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .r12 } }) }, |
| 1270 | .{ .imm = Instruction.Immediate.u(0x10) }, | 1270 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1271 | }); | 1271 | }); |
| 1272 | try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10"); | 1272 | try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10"); |
| ... | @@ -1290,13 +1290,13 @@ test "lower MI encoding" { | ... | @@ -1290,13 +1290,13 @@ test "lower MI encoding" { |
| 1290 | try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10"); | 1290 | try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10"); |
| 1291 | 1291 | ||
| 1292 | try enc.encode(.mov, &.{ | 1292 | try enc.encode(.mov, &.{ |
| 1293 | .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .{ .reg = .r11 } }) }, | 1293 | .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r11 } }) }, |
| 1294 | .{ .imm = Instruction.Immediate.u(0x10) }, | 1294 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1295 | }); | 1295 | }); |
| 1296 | try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10"); | 1296 | try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10"); |
| 1297 | 1297 | ||
| 1298 | try enc.encode(.mov, &.{ | 1298 | try enc.encode(.mov, &.{ |
| 1299 | .{ .mem = Instruction.Memory.rip(.qword, 0x10) }, | 1299 | .{ .mem = Instruction.Memory.initRip(.qword, 0x10) }, |
| 1300 | .{ .imm = Instruction.Immediate.u(0x10) }, | 1300 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1301 | }); | 1301 | }); |
| 1302 | try expectEqualHexStrings( | 1302 | try expectEqualHexStrings( |
| ... | @@ -1306,25 +1306,25 @@ test "lower MI encoding" { | ... | @@ -1306,25 +1306,25 @@ test "lower MI encoding" { |
| 1306 | ); | 1306 | ); |
| 1307 | 1307 | ||
| 1308 | try enc.encode(.mov, &.{ | 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 | .{ .imm = Instruction.Immediate.u(0x10) }, | 1310 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1311 | }); | 1311 | }); |
| 1312 | try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10"); | 1312 | try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10"); |
| 1313 | 1313 | ||
| 1314 | try enc.encode(.mov, &.{ | 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 | .{ .imm = Instruction.Immediate.s(-16) }, | 1316 | .{ .imm = Instruction.Immediate.s(-16) }, |
| 1317 | }); | 1317 | }); |
| 1318 | try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16"); | 1318 | try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16"); |
| 1319 | 1319 | ||
| 1320 | try enc.encode(.mov, &.{ | 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 | .{ .imm = Instruction.Immediate.u(0x10) }, | 1322 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1323 | }); | 1323 | }); |
| 1324 | try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10"); | 1324 | try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10"); |
| 1325 | 1325 | ||
| 1326 | try enc.encode(.mov, &.{ | 1326 | try enc.encode(.mov, &.{ |
| 1327 | .{ .mem = Instruction.Memory.sib(.qword, .{ | 1327 | .{ .mem = Instruction.Memory.initSib(.qword, .{ |
| 1328 | .base = .{ .reg = .ds }, | 1328 | .base = .{ .reg = .ds }, |
| 1329 | .disp = 0x10000000, | 1329 | .disp = 0x10000000, |
| 1330 | .scale_index = .{ .scale = 2, .index = .rcx }, | 1330 | .scale_index = .{ .scale = 2, .index = .rcx }, |
| ... | @@ -1338,13 +1338,13 @@ test "lower MI encoding" { | ... | @@ -1338,13 +1338,13 @@ test "lower MI encoding" { |
| 1338 | ); | 1338 | ); |
| 1339 | 1339 | ||
| 1340 | try enc.encode(.adc, &.{ | 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 | .{ .imm = Instruction.Immediate.u(0x10) }, | 1342 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1343 | }); | 1343 | }); |
| 1344 | try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10"); | 1344 | try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10"); |
| 1345 | 1345 | ||
| 1346 | try enc.encode(.adc, &.{ | 1346 | try enc.encode(.adc, &.{ |
| 1347 | .{ .mem = Instruction.Memory.rip(.qword, 0) }, | 1347 | .{ .mem = Instruction.Memory.initRip(.qword, 0) }, |
| 1348 | .{ .imm = Instruction.Immediate.u(0x10) }, | 1348 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1349 | }); | 1349 | }); |
| 1350 | try expectEqualHexStrings("\x48\x83\x15\x00\x00\x00\x00\x10", enc.code(), "adc QWORD PTR [rip], 0x10"); | 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,7 +1356,7 @@ test "lower MI encoding" { |
| 1356 | try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10"); | 1356 | try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10"); |
| 1357 | 1357 | ||
| 1358 | try enc.encode(.add, &.{ | 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 | .{ .imm = Instruction.Immediate.u(0x10) }, | 1360 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1361 | }); | 1361 | }); |
| 1362 | try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10"); | 1362 | try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10"); |
| ... | @@ -1368,13 +1368,13 @@ test "lower MI encoding" { | ... | @@ -1368,13 +1368,13 @@ test "lower MI encoding" { |
| 1368 | try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); | 1368 | try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); |
| 1369 | 1369 | ||
| 1370 | try enc.encode(.add, &.{ | 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 | .{ .imm = Instruction.Immediate.s(-0x10) }, | 1372 | .{ .imm = Instruction.Immediate.s(-0x10) }, |
| 1373 | }); | 1373 | }); |
| 1374 | try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10"); | 1374 | try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10"); |
| 1375 | 1375 | ||
| 1376 | try enc.encode(.@"and", &.{ | 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 | .{ .imm = Instruction.Immediate.u(0x10) }, | 1378 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1379 | }); | 1379 | }); |
| 1380 | try expectEqualHexStrings( | 1380 | try expectEqualHexStrings( |
| ... | @@ -1384,7 +1384,7 @@ test "lower MI encoding" { | ... | @@ -1384,7 +1384,7 @@ test "lower MI encoding" { |
| 1384 | ); | 1384 | ); |
| 1385 | 1385 | ||
| 1386 | try enc.encode(.@"and", &.{ | 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 | .{ .imm = Instruction.Immediate.u(0x10) }, | 1388 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1389 | }); | 1389 | }); |
| 1390 | try expectEqualHexStrings( | 1390 | try expectEqualHexStrings( |
| ... | @@ -1394,7 +1394,7 @@ test "lower MI encoding" { | ... | @@ -1394,7 +1394,7 @@ test "lower MI encoding" { |
| 1394 | ); | 1394 | ); |
| 1395 | 1395 | ||
| 1396 | try enc.encode(.@"and", &.{ | 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 | .{ .imm = Instruction.Immediate.u(0x10) }, | 1398 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1399 | }); | 1399 | }); |
| 1400 | try expectEqualHexStrings( | 1400 | try expectEqualHexStrings( |
| ... | @@ -1404,7 +1404,7 @@ test "lower MI encoding" { | ... | @@ -1404,7 +1404,7 @@ test "lower MI encoding" { |
| 1404 | ); | 1404 | ); |
| 1405 | 1405 | ||
| 1406 | try enc.encode(.sub, &.{ | 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 | .{ .imm = Instruction.Immediate.u(0x10) }, | 1408 | .{ .imm = Instruction.Immediate.u(0x10) }, |
| 1409 | }); | 1409 | }); |
| 1410 | try expectEqualHexStrings( | 1410 | try expectEqualHexStrings( |
| ... | @@ -1419,25 +1419,25 @@ test "lower RM encoding" { | ... | @@ -1419,25 +1419,25 @@ test "lower RM encoding" { |
| 1419 | 1419 | ||
| 1420 | try enc.encode(.mov, &.{ | 1420 | try enc.encode(.mov, &.{ |
| 1421 | .{ .reg = .rax }, | 1421 | .{ .reg = .rax }, |
| 1422 | .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .{ .reg = .r11 } }) }, | 1422 | .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r11 } }) }, |
| 1423 | }); | 1423 | }); |
| 1424 | try expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]"); | 1424 | try expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]"); |
| 1425 | 1425 | ||
| 1426 | try enc.encode(.mov, &.{ | 1426 | try enc.encode(.mov, &.{ |
| 1427 | .{ .reg = .rbx }, | 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 | try expectEqualHexStrings("\x48\x8B\x1C\x25\x10\x00\x00\x00", enc.code(), "mov rbx, QWORD PTR ds:0x10"); | 1430 | try expectEqualHexStrings("\x48\x8B\x1C\x25\x10\x00\x00\x00", enc.code(), "mov rbx, QWORD PTR ds:0x10"); |
| 1431 | 1431 | ||
| 1432 | try enc.encode(.mov, &.{ | 1432 | try enc.encode(.mov, &.{ |
| 1433 | .{ .reg = .rax }, | 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 | try expectEqualHexStrings("\x48\x8B\x45\xFC", enc.code(), "mov rax, QWORD PTR [rbp - 4]"); | 1436 | try expectEqualHexStrings("\x48\x8B\x45\xFC", enc.code(), "mov rax, QWORD PTR [rbp - 4]"); |
| 1437 | 1437 | ||
| 1438 | try enc.encode(.mov, &.{ | 1438 | try enc.encode(.mov, &.{ |
| 1439 | .{ .reg = .rax }, | 1439 | .{ .reg = .rax }, |
| 1440 | .{ .mem = Instruction.Memory.sib(.qword, .{ | 1440 | .{ .mem = Instruction.Memory.initSib(.qword, .{ |
| 1441 | .base = .{ .reg = .rbp }, | 1441 | .base = .{ .reg = .rbp }, |
| 1442 | .scale_index = .{ .scale = 1, .index = .rcx }, | 1442 | .scale_index = .{ .scale = 1, .index = .rcx }, |
| 1443 | .disp = -8, | 1443 | .disp = -8, |
| ... | @@ -1447,7 +1447,7 @@ test "lower RM encoding" { | ... | @@ -1447,7 +1447,7 @@ test "lower RM encoding" { |
| 1447 | 1447 | ||
| 1448 | try enc.encode(.mov, &.{ | 1448 | try enc.encode(.mov, &.{ |
| 1449 | .{ .reg = .eax }, | 1449 | .{ .reg = .eax }, |
| 1450 | .{ .mem = Instruction.Memory.sib(.dword, .{ | 1450 | .{ .mem = Instruction.Memory.initSib(.dword, .{ |
| 1451 | .base = .{ .reg = .rbp }, | 1451 | .base = .{ .reg = .rbp }, |
| 1452 | .scale_index = .{ .scale = 4, .index = .rdx }, | 1452 | .scale_index = .{ .scale = 4, .index = .rdx }, |
| 1453 | .disp = -4, | 1453 | .disp = -4, |
| ... | @@ -1457,7 +1457,7 @@ test "lower RM encoding" { | ... | @@ -1457,7 +1457,7 @@ test "lower RM encoding" { |
| 1457 | 1457 | ||
| 1458 | try enc.encode(.mov, &.{ | 1458 | try enc.encode(.mov, &.{ |
| 1459 | .{ .reg = .rax }, | 1459 | .{ .reg = .rax }, |
| 1460 | .{ .mem = Instruction.Memory.sib(.qword, .{ | 1460 | .{ .mem = Instruction.Memory.initSib(.qword, .{ |
| 1461 | .base = .{ .reg = .rbp }, | 1461 | .base = .{ .reg = .rbp }, |
| 1462 | .scale_index = .{ .scale = 8, .index = .rcx }, | 1462 | .scale_index = .{ .scale = 8, .index = .rcx }, |
| 1463 | .disp = -8, | 1463 | .disp = -8, |
| ... | @@ -1467,7 +1467,7 @@ test "lower RM encoding" { | ... | @@ -1467,7 +1467,7 @@ test "lower RM encoding" { |
| 1467 | 1467 | ||
| 1468 | try enc.encode(.mov, &.{ | 1468 | try enc.encode(.mov, &.{ |
| 1469 | .{ .reg = .r8b }, | 1469 | .{ .reg = .r8b }, |
| 1470 | .{ .mem = Instruction.Memory.sib(.byte, .{ | 1470 | .{ .mem = Instruction.Memory.initSib(.byte, .{ |
| 1471 | .base = .{ .reg = .rsi }, | 1471 | .base = .{ .reg = .rsi }, |
| 1472 | .scale_index = .{ .scale = 1, .index = .rcx }, | 1472 | .scale_index = .{ .scale = 1, .index = .rcx }, |
| 1473 | .disp = -24, | 1473 | .disp = -24, |
| ... | @@ -1483,7 +1483,7 @@ test "lower RM encoding" { | ... | @@ -1483,7 +1483,7 @@ test "lower RM encoding" { |
| 1483 | try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs"); | 1483 | try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs"); |
| 1484 | 1484 | ||
| 1485 | try enc.encode(.mov, &.{ | 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 | .{ .reg = .fs }, | 1487 | .{ .reg = .fs }, |
| 1488 | }); | 1488 | }); |
| 1489 | try expectEqualHexStrings("\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs"); | 1489 | try expectEqualHexStrings("\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs"); |
| ... | @@ -1514,19 +1514,19 @@ test "lower RM encoding" { | ... | @@ -1514,19 +1514,19 @@ test "lower RM encoding" { |
| 1514 | 1514 | ||
| 1515 | try enc.encode(.movsx, &.{ | 1515 | try enc.encode(.movsx, &.{ |
| 1516 | .{ .reg = .eax }, | 1516 | .{ .reg = .eax }, |
| 1517 | .{ .mem = Instruction.Memory.sib(.word, .{ .base = .{ .reg = .rbp } }) }, | 1517 | .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp } }) }, |
| 1518 | }); | 1518 | }); |
| 1519 | try expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]"); | 1519 | try expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]"); |
| 1520 | 1520 | ||
| 1521 | try enc.encode(.movsx, &.{ | 1521 | try enc.encode(.movsx, &.{ |
| 1522 | .{ .reg = .eax }, | 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 | try expectEqualHexStrings("\x0F\xBE\x04\x45\x00\x00\x00\x00", enc.code(), "movsx eax, BYTE PTR [rax * 2]"); | 1525 | try expectEqualHexStrings("\x0F\xBE\x04\x45\x00\x00\x00\x00", enc.code(), "movsx eax, BYTE PTR [rax * 2]"); |
| 1526 | 1526 | ||
| 1527 | try enc.encode(.movsx, &.{ | 1527 | try enc.encode(.movsx, &.{ |
| 1528 | .{ .reg = .ax }, | 1528 | .{ .reg = .ax }, |
| 1529 | .{ .mem = Instruction.Memory.rip(.byte, 0x10) }, | 1529 | .{ .mem = Instruction.Memory.initRip(.byte, 0x10) }, |
| 1530 | }); | 1530 | }); |
| 1531 | try expectEqualHexStrings("\x66\x0F\xBE\x05\x10\x00\x00\x00", enc.code(), "movsx ax, BYTE PTR [rip + 0x10]"); | 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,37 +1544,37 @@ test "lower RM encoding" { |
| 1544 | 1544 | ||
| 1545 | try enc.encode(.lea, &.{ | 1545 | try enc.encode(.lea, &.{ |
| 1546 | .{ .reg = .rax }, | 1546 | .{ .reg = .rax }, |
| 1547 | .{ .mem = Instruction.Memory.rip(.qword, 0x10) }, | 1547 | .{ .mem = Instruction.Memory.initRip(.qword, 0x10) }, |
| 1548 | }); | 1548 | }); |
| 1549 | try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, QWORD PTR [rip + 0x10]"); | 1549 | try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, QWORD PTR [rip + 0x10]"); |
| 1550 | 1550 | ||
| 1551 | try enc.encode(.lea, &.{ | 1551 | try enc.encode(.lea, &.{ |
| 1552 | .{ .reg = .rax }, | 1552 | .{ .reg = .rax }, |
| 1553 | .{ .mem = Instruction.Memory.rip(.dword, 0x10) }, | 1553 | .{ .mem = Instruction.Memory.initRip(.dword, 0x10) }, |
| 1554 | }); | 1554 | }); |
| 1555 | try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, DWORD PTR [rip + 0x10]"); | 1555 | try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, DWORD PTR [rip + 0x10]"); |
| 1556 | 1556 | ||
| 1557 | try enc.encode(.lea, &.{ | 1557 | try enc.encode(.lea, &.{ |
| 1558 | .{ .reg = .eax }, | 1558 | .{ .reg = .eax }, |
| 1559 | .{ .mem = Instruction.Memory.rip(.dword, 0x10) }, | 1559 | .{ .mem = Instruction.Memory.initRip(.dword, 0x10) }, |
| 1560 | }); | 1560 | }); |
| 1561 | try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, DWORD PTR [rip + 0x10]"); | 1561 | try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, DWORD PTR [rip + 0x10]"); |
| 1562 | 1562 | ||
| 1563 | try enc.encode(.lea, &.{ | 1563 | try enc.encode(.lea, &.{ |
| 1564 | .{ .reg = .eax }, | 1564 | .{ .reg = .eax }, |
| 1565 | .{ .mem = Instruction.Memory.rip(.word, 0x10) }, | 1565 | .{ .mem = Instruction.Memory.initRip(.word, 0x10) }, |
| 1566 | }); | 1566 | }); |
| 1567 | try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, WORD PTR [rip + 0x10]"); | 1567 | try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, WORD PTR [rip + 0x10]"); |
| 1568 | 1568 | ||
| 1569 | try enc.encode(.lea, &.{ | 1569 | try enc.encode(.lea, &.{ |
| 1570 | .{ .reg = .ax }, | 1570 | .{ .reg = .ax }, |
| 1571 | .{ .mem = Instruction.Memory.rip(.byte, 0x10) }, | 1571 | .{ .mem = Instruction.Memory.initRip(.byte, 0x10) }, |
| 1572 | }); | 1572 | }); |
| 1573 | try expectEqualHexStrings("\x66\x8D\x05\x10\x00\x00\x00", enc.code(), "lea ax, BYTE PTR [rip + 0x10]"); | 1573 | try expectEqualHexStrings("\x66\x8D\x05\x10\x00\x00\x00", enc.code(), "lea ax, BYTE PTR [rip + 0x10]"); |
| 1574 | 1574 | ||
| 1575 | try enc.encode(.lea, &.{ | 1575 | try enc.encode(.lea, &.{ |
| 1576 | .{ .reg = .rsi }, | 1576 | .{ .reg = .rsi }, |
| 1577 | .{ .mem = Instruction.Memory.sib(.qword, .{ | 1577 | .{ .mem = Instruction.Memory.initSib(.qword, .{ |
| 1578 | .base = .{ .reg = .rbp }, | 1578 | .base = .{ .reg = .rbp }, |
| 1579 | .scale_index = .{ .scale = 1, .index = .rcx }, | 1579 | .scale_index = .{ .scale = 1, .index = .rcx }, |
| 1580 | }) }, | 1580 | }) }, |
| ... | @@ -1583,31 +1583,31 @@ test "lower RM encoding" { | ... | @@ -1583,31 +1583,31 @@ test "lower RM encoding" { |
| 1583 | 1583 | ||
| 1584 | try enc.encode(.add, &.{ | 1584 | try enc.encode(.add, &.{ |
| 1585 | .{ .reg = .r11 }, | 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 | try expectEqualHexStrings("\x4C\x03\x1C\x25\x00\x00\x00\x10", enc.code(), "add r11, QWORD PTR ds:0x10000000"); | 1588 | try expectEqualHexStrings("\x4C\x03\x1C\x25\x00\x00\x00\x10", enc.code(), "add r11, QWORD PTR ds:0x10000000"); |
| 1589 | 1589 | ||
| 1590 | try enc.encode(.add, &.{ | 1590 | try enc.encode(.add, &.{ |
| 1591 | .{ .reg = .r12b }, | 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 | try expectEqualHexStrings("\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR ds:0x10000000"); | 1594 | try expectEqualHexStrings("\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR ds:0x10000000"); |
| 1595 | 1595 | ||
| 1596 | try enc.encode(.add, &.{ | 1596 | try enc.encode(.add, &.{ |
| 1597 | .{ .reg = .r12b }, | 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 | try expectEqualHexStrings("\x64\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR fs:0x10000000"); | 1600 | try expectEqualHexStrings("\x64\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR fs:0x10000000"); |
| 1601 | 1601 | ||
| 1602 | try enc.encode(.sub, &.{ | 1602 | try enc.encode(.sub, &.{ |
| 1603 | .{ .reg = .r11 }, | 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 | try expectEqualHexStrings("\x4D\x2B\x9D\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r13 + 0x10000000]"); | 1606 | try expectEqualHexStrings("\x4D\x2B\x9D\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r13 + 0x10000000]"); |
| 1607 | 1607 | ||
| 1608 | try enc.encode(.sub, &.{ | 1608 | try enc.encode(.sub, &.{ |
| 1609 | .{ .reg = .r11 }, | 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 | try expectEqualHexStrings("\x4D\x2B\x9C\x24\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r12 + 0x10000000]"); | 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,7 +1630,7 @@ test "lower RMI encoding" { |
| 1630 | 1630 | ||
| 1631 | try enc.encode(.imul, &.{ | 1631 | try enc.encode(.imul, &.{ |
| 1632 | .{ .reg = .r11 }, | 1632 | .{ .reg = .r11 }, |
| 1633 | .{ .mem = Instruction.Memory.rip(.qword, -16) }, | 1633 | .{ .mem = Instruction.Memory.initRip(.qword, -16) }, |
| 1634 | .{ .imm = Instruction.Immediate.s(-1024) }, | 1634 | .{ .imm = Instruction.Immediate.s(-1024) }, |
| 1635 | }); | 1635 | }); |
| 1636 | try expectEqualHexStrings( | 1636 | try expectEqualHexStrings( |
| ... | @@ -1641,7 +1641,7 @@ test "lower RMI encoding" { | ... | @@ -1641,7 +1641,7 @@ test "lower RMI encoding" { |
| 1641 | 1641 | ||
| 1642 | try enc.encode(.imul, &.{ | 1642 | try enc.encode(.imul, &.{ |
| 1643 | .{ .reg = .bx }, | 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 | .{ .imm = Instruction.Immediate.s(-1024) }, | 1645 | .{ .imm = Instruction.Immediate.s(-1024) }, |
| 1646 | }); | 1646 | }); |
| 1647 | try expectEqualHexStrings( | 1647 | try expectEqualHexStrings( |
| ... | @@ -1652,7 +1652,7 @@ test "lower RMI encoding" { | ... | @@ -1652,7 +1652,7 @@ test "lower RMI encoding" { |
| 1652 | 1652 | ||
| 1653 | try enc.encode(.imul, &.{ | 1653 | try enc.encode(.imul, &.{ |
| 1654 | .{ .reg = .bx }, | 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 | .{ .imm = Instruction.Immediate.u(1024) }, | 1656 | .{ .imm = Instruction.Immediate.u(1024) }, |
| 1657 | }); | 1657 | }); |
| 1658 | try expectEqualHexStrings( | 1658 | try expectEqualHexStrings( |
| ... | @@ -1672,19 +1672,19 @@ test "lower MR encoding" { | ... | @@ -1672,19 +1672,19 @@ test "lower MR encoding" { |
| 1672 | try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx"); | 1672 | try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx"); |
| 1673 | 1673 | ||
| 1674 | try enc.encode(.mov, &.{ | 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 | .{ .reg = .r11 }, | 1676 | .{ .reg = .r11 }, |
| 1677 | }); | 1677 | }); |
| 1678 | try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11"); | 1678 | try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11"); |
| 1679 | 1679 | ||
| 1680 | try enc.encode(.mov, &.{ | 1680 | try enc.encode(.mov, &.{ |
| 1681 | .{ .mem = Instruction.Memory.rip(.qword, 0x10) }, | 1681 | .{ .mem = Instruction.Memory.initRip(.qword, 0x10) }, |
| 1682 | .{ .reg = .r12 }, | 1682 | .{ .reg = .r12 }, |
| 1683 | }); | 1683 | }); |
| 1684 | try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rip + 0x10], r12"); | 1684 | try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rip + 0x10], r12"); |
| 1685 | 1685 | ||
| 1686 | try enc.encode(.mov, &.{ | 1686 | try enc.encode(.mov, &.{ |
| 1687 | .{ .mem = Instruction.Memory.sib(.qword, .{ | 1687 | .{ .mem = Instruction.Memory.initSib(.qword, .{ |
| 1688 | .base = .{ .reg = .r11 }, | 1688 | .base = .{ .reg = .r11 }, |
| 1689 | .scale_index = .{ .scale = 2, .index = .r12 }, | 1689 | .scale_index = .{ .scale = 2, .index = .r12 }, |
| 1690 | .disp = 0x10, | 1690 | .disp = 0x10, |
| ... | @@ -1694,13 +1694,13 @@ test "lower MR encoding" { | ... | @@ -1694,13 +1694,13 @@ test "lower MR encoding" { |
| 1694 | try expectEqualHexStrings("\x4F\x89\x6C\x63\x10", enc.code(), "mov QWORD PTR [r11 + 2 * r12 + 0x10], r13"); | 1694 | try expectEqualHexStrings("\x4F\x89\x6C\x63\x10", enc.code(), "mov QWORD PTR [r11 + 2 * r12 + 0x10], r13"); |
| 1695 | 1695 | ||
| 1696 | try enc.encode(.mov, &.{ | 1696 | try enc.encode(.mov, &.{ |
| 1697 | .{ .mem = Instruction.Memory.rip(.word, -0x10) }, | 1697 | .{ .mem = Instruction.Memory.initRip(.word, -0x10) }, |
| 1698 | .{ .reg = .r12w }, | 1698 | .{ .reg = .r12w }, |
| 1699 | }); | 1699 | }); |
| 1700 | try expectEqualHexStrings("\x66\x44\x89\x25\xF0\xFF\xFF\xFF", enc.code(), "mov WORD PTR [rip - 0x10], r12w"); | 1700 | try expectEqualHexStrings("\x66\x44\x89\x25\xF0\xFF\xFF\xFF", enc.code(), "mov WORD PTR [rip - 0x10], r12w"); |
| 1701 | 1701 | ||
| 1702 | try enc.encode(.mov, &.{ | 1702 | try enc.encode(.mov, &.{ |
| 1703 | .{ .mem = Instruction.Memory.sib(.byte, .{ | 1703 | .{ .mem = Instruction.Memory.initSib(.byte, .{ |
| 1704 | .base = .{ .reg = .r11 }, | 1704 | .base = .{ .reg = .r11 }, |
| 1705 | .scale_index = .{ .scale = 2, .index = .r12 }, | 1705 | .scale_index = .{ .scale = 2, .index = .r12 }, |
| 1706 | .disp = 0x10, | 1706 | .disp = 0x10, |
| ... | @@ -1710,25 +1710,25 @@ test "lower MR encoding" { | ... | @@ -1710,25 +1710,25 @@ test "lower MR encoding" { |
| 1710 | try expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b"); | 1710 | try expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b"); |
| 1711 | 1711 | ||
| 1712 | try enc.encode(.add, &.{ | 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 | .{ .reg = .r12b }, | 1714 | .{ .reg = .r12b }, |
| 1715 | }); | 1715 | }); |
| 1716 | try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b"); | 1716 | try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b"); |
| 1717 | 1717 | ||
| 1718 | try enc.encode(.add, &.{ | 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 | .{ .reg = .r12d }, | 1720 | .{ .reg = .r12d }, |
| 1721 | }); | 1721 | }); |
| 1722 | try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d"); | 1722 | try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d"); |
| 1723 | 1723 | ||
| 1724 | try enc.encode(.add, &.{ | 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 | .{ .reg = .r12d }, | 1726 | .{ .reg = .r12d }, |
| 1727 | }); | 1727 | }); |
| 1728 | try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d"); | 1728 | try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d"); |
| 1729 | 1729 | ||
| 1730 | try enc.encode(.sub, &.{ | 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 | .{ .reg = .r12 }, | 1732 | .{ .reg = .r12 }, |
| 1733 | }); | 1733 | }); |
| 1734 | try expectEqualHexStrings("\x4D\x29\xA3\x00\x00\x00\x10", enc.code(), "sub QWORD PTR [r11 + 0x10000000], r12"); | 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,12 +1743,12 @@ test "lower M encoding" { |
| 1743 | try expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12"); | 1743 | try expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12"); |
| 1744 | 1744 | ||
| 1745 | try enc.encode(.call, &.{ | 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 | try expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]"); | 1748 | try expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]"); |
| 1749 | 1749 | ||
| 1750 | try enc.encode(.call, &.{ | 1750 | try enc.encode(.call, &.{ |
| 1751 | .{ .mem = Instruction.Memory.sib(.qword, .{ | 1751 | .{ .mem = Instruction.Memory.initSib(.qword, .{ |
| 1752 | .base = .none, | 1752 | .base = .none, |
| 1753 | .scale_index = .{ .index = .r11, .scale = 2 }, | 1753 | .scale_index = .{ .index = .r11, .scale = 2 }, |
| 1754 | }) }, | 1754 | }) }, |
| ... | @@ -1756,7 +1756,7 @@ test "lower M encoding" { | ... | @@ -1756,7 +1756,7 @@ test "lower M encoding" { |
| 1756 | try expectEqualHexStrings("\x42\xFF\x14\x5D\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r11 * 2]"); | 1756 | try expectEqualHexStrings("\x42\xFF\x14\x5D\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r11 * 2]"); |
| 1757 | 1757 | ||
| 1758 | try enc.encode(.call, &.{ | 1758 | try enc.encode(.call, &.{ |
| 1759 | .{ .mem = Instruction.Memory.sib(.qword, .{ | 1759 | .{ .mem = Instruction.Memory.initSib(.qword, .{ |
| 1760 | .base = .none, | 1760 | .base = .none, |
| 1761 | .scale_index = .{ .index = .r12, .scale = 2 }, | 1761 | .scale_index = .{ .index = .r12, .scale = 2 }, |
| 1762 | }) }, | 1762 | }) }, |
| ... | @@ -1764,7 +1764,7 @@ test "lower M encoding" { | ... | @@ -1764,7 +1764,7 @@ test "lower M encoding" { |
| 1764 | try expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]"); | 1764 | try expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]"); |
| 1765 | 1765 | ||
| 1766 | try enc.encode(.call, &.{ | 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 | try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0"); | 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,22 +1774,22 @@ test "lower M encoding" { |
| 1774 | try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0"); | 1774 | try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0"); |
| 1775 | 1775 | ||
| 1776 | try enc.encode(.push, &.{ | 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 | try expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]"); | 1779 | try expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]"); |
| 1780 | 1780 | ||
| 1781 | try enc.encode(.push, &.{ | 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 | try expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]"); | 1784 | try expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]"); |
| 1785 | 1785 | ||
| 1786 | try enc.encode(.pop, &.{ | 1786 | try enc.encode(.pop, &.{ |
| 1787 | .{ .mem = Instruction.Memory.rip(.qword, 0) }, | 1787 | .{ .mem = Instruction.Memory.initRip(.qword, 0) }, |
| 1788 | }); | 1788 | }); |
| 1789 | try expectEqualHexStrings("\x8F\x05\x00\x00\x00\x00", enc.code(), "pop QWORD PTR [rip]"); | 1789 | try expectEqualHexStrings("\x8F\x05\x00\x00\x00\x00", enc.code(), "pop QWORD PTR [rip]"); |
| 1790 | 1790 | ||
| 1791 | try enc.encode(.pop, &.{ | 1791 | try enc.encode(.pop, &.{ |
| 1792 | .{ .mem = Instruction.Memory.rip(.word, 0) }, | 1792 | .{ .mem = Instruction.Memory.initRip(.word, 0) }, |
| 1793 | }); | 1793 | }); |
| 1794 | try expectEqualHexStrings("\x66\x8F\x05\x00\x00\x00\x00", enc.code(), "pop WORD PTR [rbp]"); | 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,48 +1870,48 @@ test "lower FD/TD encoding" { |
| 1870 | 1870 | ||
| 1871 | try enc.encode(.mov, &.{ | 1871 | try enc.encode(.mov, &.{ |
| 1872 | .{ .reg = .rax }, | 1872 | .{ .reg = .rax }, |
| 1873 | .{ .mem = Instruction.Memory.moffs(.cs, 0x10) }, | 1873 | .{ .mem = Instruction.Memory.initMoffs(.cs, 0x10) }, |
| 1874 | }); | 1874 | }); |
| 1875 | try expectEqualHexStrings("\x2E\x48\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs rax, cs:0x10"); | 1875 | try expectEqualHexStrings("\x2E\x48\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs rax, cs:0x10"); |
| 1876 | 1876 | ||
| 1877 | try enc.encode(.mov, &.{ | 1877 | try enc.encode(.mov, &.{ |
| 1878 | .{ .reg = .eax }, | 1878 | .{ .reg = .eax }, |
| 1879 | .{ .mem = Instruction.Memory.moffs(.fs, 0x10) }, | 1879 | .{ .mem = Instruction.Memory.initMoffs(.fs, 0x10) }, |
| 1880 | }); | 1880 | }); |
| 1881 | try expectEqualHexStrings("\x64\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs eax, fs:0x10"); | 1881 | try expectEqualHexStrings("\x64\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs eax, fs:0x10"); |
| 1882 | 1882 | ||
| 1883 | try enc.encode(.mov, &.{ | 1883 | try enc.encode(.mov, &.{ |
| 1884 | .{ .reg = .ax }, | 1884 | .{ .reg = .ax }, |
| 1885 | .{ .mem = Instruction.Memory.moffs(.gs, 0x10) }, | 1885 | .{ .mem = Instruction.Memory.initMoffs(.gs, 0x10) }, |
| 1886 | }); | 1886 | }); |
| 1887 | try expectEqualHexStrings("\x65\x66\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ax, gs:0x10"); | 1887 | try expectEqualHexStrings("\x65\x66\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ax, gs:0x10"); |
| 1888 | 1888 | ||
| 1889 | try enc.encode(.mov, &.{ | 1889 | try enc.encode(.mov, &.{ |
| 1890 | .{ .reg = .al }, | 1890 | .{ .reg = .al }, |
| 1891 | .{ .mem = Instruction.Memory.moffs(.ds, 0x10) }, | 1891 | .{ .mem = Instruction.Memory.initMoffs(.ds, 0x10) }, |
| 1892 | }); | 1892 | }); |
| 1893 | try expectEqualHexStrings("\xA0\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs al, ds:0x10"); | 1893 | try expectEqualHexStrings("\xA0\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs al, ds:0x10"); |
| 1894 | 1894 | ||
| 1895 | try enc.encode(.mov, &.{ | 1895 | try enc.encode(.mov, &.{ |
| 1896 | .{ .mem = Instruction.Memory.moffs(.cs, 0x10) }, | 1896 | .{ .mem = Instruction.Memory.initMoffs(.cs, 0x10) }, |
| 1897 | .{ .reg = .rax }, | 1897 | .{ .reg = .rax }, |
| 1898 | }); | 1898 | }); |
| 1899 | try expectEqualHexStrings("\x2E\x48\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs cs:0x10, rax"); | 1899 | try expectEqualHexStrings("\x2E\x48\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs cs:0x10, rax"); |
| 1900 | 1900 | ||
| 1901 | try enc.encode(.mov, &.{ | 1901 | try enc.encode(.mov, &.{ |
| 1902 | .{ .mem = Instruction.Memory.moffs(.fs, 0x10) }, | 1902 | .{ .mem = Instruction.Memory.initMoffs(.fs, 0x10) }, |
| 1903 | .{ .reg = .eax }, | 1903 | .{ .reg = .eax }, |
| 1904 | }); | 1904 | }); |
| 1905 | try expectEqualHexStrings("\x64\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs fs:0x10, eax"); | 1905 | try expectEqualHexStrings("\x64\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs fs:0x10, eax"); |
| 1906 | 1906 | ||
| 1907 | try enc.encode(.mov, &.{ | 1907 | try enc.encode(.mov, &.{ |
| 1908 | .{ .mem = Instruction.Memory.moffs(.gs, 0x10) }, | 1908 | .{ .mem = Instruction.Memory.initMoffs(.gs, 0x10) }, |
| 1909 | .{ .reg = .ax }, | 1909 | .{ .reg = .ax }, |
| 1910 | }); | 1910 | }); |
| 1911 | try expectEqualHexStrings("\x65\x66\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs gs:0x10, ax"); | 1911 | try expectEqualHexStrings("\x65\x66\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs gs:0x10, ax"); |
| 1912 | 1912 | ||
| 1913 | try enc.encode(.mov, &.{ | 1913 | try enc.encode(.mov, &.{ |
| 1914 | .{ .mem = Instruction.Memory.moffs(.ds, 0x10) }, | 1914 | .{ .mem = Instruction.Memory.initMoffs(.ds, 0x10) }, |
| 1915 | .{ .reg = .al }, | 1915 | .{ .reg = .al }, |
| 1916 | }); | 1916 | }); |
| 1917 | try expectEqualHexStrings("\xA2\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ds:0x10, al"); | 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,16 +1949,16 @@ test "invalid instruction" { |
| 1949 | .{ .reg = .al }, | 1949 | .{ .reg = .al }, |
| 1950 | }); | 1950 | }); |
| 1951 | try invalidInstruction(.call, &.{ | 1951 | try invalidInstruction(.call, &.{ |
| 1952 | .{ .mem = Instruction.Memory.rip(.dword, 0) }, | 1952 | .{ .mem = Instruction.Memory.initRip(.dword, 0) }, |
| 1953 | }); | 1953 | }); |
| 1954 | try invalidInstruction(.call, &.{ | 1954 | try invalidInstruction(.call, &.{ |
| 1955 | .{ .mem = Instruction.Memory.rip(.word, 0) }, | 1955 | .{ .mem = Instruction.Memory.initRip(.word, 0) }, |
| 1956 | }); | 1956 | }); |
| 1957 | try invalidInstruction(.call, &.{ | 1957 | try invalidInstruction(.call, &.{ |
| 1958 | .{ .mem = Instruction.Memory.rip(.byte, 0) }, | 1958 | .{ .mem = Instruction.Memory.initRip(.byte, 0) }, |
| 1959 | }); | 1959 | }); |
| 1960 | try invalidInstruction(.mov, &.{ | 1960 | try invalidInstruction(.mov, &.{ |
| 1961 | .{ .mem = Instruction.Memory.rip(.word, 0x10) }, | 1961 | .{ .mem = Instruction.Memory.initRip(.word, 0x10) }, |
| 1962 | .{ .reg = .r12 }, | 1962 | .{ .reg = .r12 }, |
| 1963 | }); | 1963 | }); |
| 1964 | try invalidInstruction(.lea, &.{ | 1964 | try invalidInstruction(.lea, &.{ |
| ... | @@ -1967,7 +1967,7 @@ test "invalid instruction" { | ... | @@ -1967,7 +1967,7 @@ test "invalid instruction" { |
| 1967 | }); | 1967 | }); |
| 1968 | try invalidInstruction(.lea, &.{ | 1968 | try invalidInstruction(.lea, &.{ |
| 1969 | .{ .reg = .al }, | 1969 | .{ .reg = .al }, |
| 1970 | .{ .mem = Instruction.Memory.rip(.byte, 0) }, | 1970 | .{ .mem = Instruction.Memory.initRip(.byte, 0) }, |
| 1971 | }); | 1971 | }); |
| 1972 | try invalidInstruction(.pop, &.{ | 1972 | try invalidInstruction(.pop, &.{ |
| 1973 | .{ .reg = .r12b }, | 1973 | .{ .reg = .r12b }, |
| ... | @@ -1992,7 +1992,7 @@ fn cannotEncode(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand | ... | @@ -1992,7 +1992,7 @@ fn cannotEncode(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand |
| 1992 | 1992 | ||
| 1993 | test "cannot encode" { | 1993 | test "cannot encode" { |
| 1994 | try cannotEncode(.@"test", &.{ | 1994 | try cannotEncode(.@"test", &.{ |
| 1995 | .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .{ .reg = .r12 } }) }, | 1995 | .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .r12 } }) }, |
| 1996 | .{ .reg = .ah }, | 1996 | .{ .reg = .ah }, |
| 1997 | }); | 1997 | }); |
| 1998 | try cannotEncode(.@"test", &.{ | 1998 | try cannotEncode(.@"test", &.{ |
| ... | @@ -2369,7 +2369,7 @@ const Assembler = struct { | ... | @@ -2369,7 +2369,7 @@ const Assembler = struct { |
| 2369 | if (res.rip) { | 2369 | if (res.rip) { |
| 2370 | if (res.base != null or res.scale_index != null or res.offset != null) | 2370 | if (res.base != null or res.scale_index != null or res.offset != null) |
| 2371 | return error.InvalidMemoryOperand; | 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 | if (res.base) |base| { | 2374 | if (res.base) |base| { |
| 2375 | if (res.rip) | 2375 | if (res.rip) |
| ... | @@ -2377,9 +2377,9 @@ const Assembler = struct { | ... | @@ -2377,9 +2377,9 @@ const Assembler = struct { |
| 2377 | if (res.offset) |offset| { | 2377 | if (res.offset) |offset| { |
| 2378 | if (res.scale_index != null or res.disp != null) | 2378 | if (res.scale_index != null or res.disp != null) |
| 2379 | return error.InvalidMemoryOperand; | 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 | .base = .{ .reg = base }, | 2383 | .base = .{ .reg = base }, |
| 2384 | .scale_index = res.scale_index, | 2384 | .scale_index = res.scale_index, |
| 2385 | .disp = res.disp orelse 0, | 2385 | .disp = res.disp orelse 0, |
src/codegen.zig+2-11| ... | @@ -836,16 +836,6 @@ pub const GenResult = union(enum) { | ... | @@ -836,16 +836,6 @@ pub const GenResult = union(enum) { |
| 836 | /// Traditionally, this corresponds to emitting a relocation in a relocatable object file. | 836 | /// Traditionally, this corresponds to emitting a relocation in a relocatable object file. |
| 837 | lea_symbol: u32, | 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 | fn genNavRef( | 841 | fn genNavRef( |
| ... | @@ -935,7 +925,8 @@ fn genNavRef( | ... | @@ -935,7 +925,8 @@ fn genNavRef( |
| 935 | const atom = p9.getAtom(atom_index); | 925 | const atom = p9.getAtom(atom_index); |
| 936 | return .{ .mcv = .{ .memory = atom.getOffsetTableAddress(p9) } }; | 926 | return .{ .mcv = .{ .memory = atom.getOffsetTableAddress(p9) } }; |
| 937 | } else { | 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,9 +33,9 @@ pub const Block = struct { |
| 33 | .abbrevs = .{ .abbrevs = .{} }, | 33 | .abbrevs = .{ .abbrevs = .{} }, |
| 34 | }; | 34 | }; |
| 35 | 35 | ||
| 36 | const set_bid: u32 = 1; | 36 | const set_bid_id: u32 = 1; |
| 37 | const block_name: u32 = 2; | 37 | const block_name_id: u32 = 2; |
| 38 | const set_record_name: u32 = 3; | 38 | const set_record_name_id: u32 = 3; |
| 39 | 39 | ||
| 40 | fn deinit(info: *Info, allocator: std.mem.Allocator) void { | 40 | fn deinit(info: *Info, allocator: std.mem.Allocator) void { |
| 41 | allocator.free(info.block_name); | 41 | allocator.free(info.block_name); |
| ... | @@ -61,7 +61,7 @@ pub const Record = struct { | ... | @@ -61,7 +61,7 @@ pub const Record = struct { |
| 61 | assert(record.id == Abbrev.Builtin.define_abbrev.toRecordId()); | 61 | assert(record.id == Abbrev.Builtin.define_abbrev.toRecordId()); |
| 62 | var i: usize = 0; | 62 | var i: usize = 0; |
| 63 | while (i < record.operands.len) switch (record.operands[i]) { | 63 | while (i < record.operands.len) switch (record.operands[i]) { |
| 64 | Abbrev.Operand.literal => { | 64 | Abbrev.Operand.literal_id => { |
| 65 | try operands.append(.{ .literal = record.operands[i + 1] }); | 65 | try operands.append(.{ .literal = record.operands[i + 1] }); |
| 66 | i += 2; | 66 | i += 2; |
| 67 | }, | 67 | }, |
| ... | @@ -211,7 +211,7 @@ fn nextRecord(bc: *BitcodeReader) !?Record { | ... | @@ -211,7 +211,7 @@ fn nextRecord(bc: *BitcodeReader) !?Record { |
| 211 | .align_32_bits, .block_len => return error.UnsupportedArrayElement, | 211 | .align_32_bits, .block_len => return error.UnsupportedArrayElement, |
| 212 | .abbrev_op => switch (try bc.readFixed(u1, 1)) { | 212 | .abbrev_op => switch (try bc.readFixed(u1, 1)) { |
| 213 | 1 => try operands.appendSlice(&.{ | 213 | 1 => try operands.appendSlice(&.{ |
| 214 | Abbrev.Operand.literal, | 214 | Abbrev.Operand.literal_id, |
| 215 | try bc.readVbr(u64, 8), | 215 | try bc.readVbr(u64, 8), |
| 216 | }), | 216 | }), |
| 217 | 0 => { | 217 | 0 => { |
| ... | @@ -334,9 +334,9 @@ fn parseBlockInfoBlock(bc: *BitcodeReader) !void { | ... | @@ -334,9 +334,9 @@ fn parseBlockInfoBlock(bc: *BitcodeReader) !void { |
| 334 | try record.toOwnedAbbrev(bc.allocator), | 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 | return error.Overflow, | 338 | return error.Overflow, |
| 339 | Block.Info.block_name => if (bc.keep_names) { | 339 | Block.Info.block_name_id => if (bc.keep_names) { |
| 340 | const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse | 340 | const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse |
| 341 | return error.UnspecifiedBlockId); | 341 | return error.UnspecifiedBlockId); |
| 342 | if (!gop.found_existing) gop.value_ptr.* = Block.Info.default; | 342 | if (!gop.found_existing) gop.value_ptr.* = Block.Info.default; |
| ... | @@ -346,7 +346,7 @@ fn parseBlockInfoBlock(bc: *BitcodeReader) !void { | ... | @@ -346,7 +346,7 @@ fn parseBlockInfoBlock(bc: *BitcodeReader) !void { |
| 346 | byte.* = std.math.cast(u8, operand) orelse return error.InvalidName; | 346 | byte.* = std.math.cast(u8, operand) orelse return error.InvalidName; |
| 347 | gop.value_ptr.block_name = name; | 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 | const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse | 350 | const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse |
| 351 | return error.UnspecifiedBlockId); | 351 | return error.UnspecifiedBlockId); |
| 352 | if (!gop.found_existing) gop.value_ptr.* = Block.Info.default; | 352 | if (!gop.found_existing) gop.value_ptr.* = Block.Info.default; |
| ... | @@ -467,7 +467,7 @@ const Abbrev = struct { | ... | @@ -467,7 +467,7 @@ const Abbrev = struct { |
| 467 | block_len, | 467 | block_len, |
| 468 | abbrev_op, | 468 | abbrev_op, |
| 469 | 469 | ||
| 470 | const literal = std.math.maxInt(u64); | 470 | const literal_id = std.math.maxInt(u64); |
| 471 | const Encoding = enum(u3) { | 471 | const Encoding = enum(u3) { |
| 472 | fixed = 1, | 472 | fixed = 1, |
| 473 | vbr = 2, | 473 | vbr = 2, |
src/link/Elf.zig+84-3| ... | @@ -3467,7 +3467,7 @@ fn updateSectionSizes(self: *Elf) !void { | ... | @@ -3467,7 +3467,7 @@ fn updateSectionSizes(self: *Elf) !void { |
| 3467 | if (atom_list.items.len == 0) continue; | 3467 | if (atom_list.items.len == 0) continue; |
| 3468 | 3468 | ||
| 3469 | // Create jump/branch range extenders if needed. | 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,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 | const std = @import("std"); | 5661 | const std = @import("std"); |
| 5580 | const build_options = @import("build_options"); | 5662 | const build_options = @import("build_options"); |
| 5581 | const builtin = @import("builtin"); | 5663 | const builtin = @import("builtin"); |
| ... | @@ -5598,7 +5680,6 @@ const musl = @import("../musl.zig"); | ... | @@ -5598,7 +5680,6 @@ const musl = @import("../musl.zig"); |
| 5598 | const relocatable = @import("Elf/relocatable.zig"); | 5680 | const relocatable = @import("Elf/relocatable.zig"); |
| 5599 | const relocation = @import("Elf/relocation.zig"); | 5681 | const relocation = @import("Elf/relocation.zig"); |
| 5600 | const target_util = @import("../target.zig"); | 5682 | const target_util = @import("../target.zig"); |
| 5601 | const thunks = @import("Elf/thunks.zig"); | ||
| 5602 | const trace = @import("../tracy.zig").trace; | 5683 | const trace = @import("../tracy.zig").trace; |
| 5603 | const synthetic_sections = @import("Elf/synthetic_sections.zig"); | 5684 | const synthetic_sections = @import("Elf/synthetic_sections.zig"); |
| 5604 | 5685 | ||
| ... | @@ -5636,7 +5717,7 @@ const PltGotSection = synthetic_sections.PltGotSection; | ... | @@ -5636,7 +5717,7 @@ const PltGotSection = synthetic_sections.PltGotSection; |
| 5636 | const SharedObject = @import("Elf/SharedObject.zig"); | 5717 | const SharedObject = @import("Elf/SharedObject.zig"); |
| 5637 | const Symbol = @import("Elf/Symbol.zig"); | 5718 | const Symbol = @import("Elf/Symbol.zig"); |
| 5638 | const StringTable = @import("StringTable.zig"); | 5719 | const StringTable = @import("StringTable.zig"); |
| 5639 | const Thunk = thunks.Thunk; | 5720 | const Thunk = @import("Elf/Thunk.zig"); |
| 5640 | const Value = @import("../Value.zig"); | 5721 | const Value = @import("../Value.zig"); |
| 5641 | const VerneedSection = synthetic_sections.VerneedSection; | 5722 | const VerneedSection = synthetic_sections.VerneedSection; |
| 5642 | const ZigObject = @import("Elf/ZigObject.zig"); | 5723 | const ZigObject = @import("Elf/ZigObject.zig"); |
src/link/Elf/Atom.zig+1-1| ... | @@ -2251,6 +2251,6 @@ const Fde = eh_frame.Fde; | ... | @@ -2251,6 +2251,6 @@ const Fde = eh_frame.Fde; |
| 2251 | const File = @import("file.zig").File; | 2251 | const File = @import("file.zig").File; |
| 2252 | const Object = @import("Object.zig"); | 2252 | const Object = @import("Object.zig"); |
| 2253 | const Symbol = @import("Symbol.zig"); | 2253 | const Symbol = @import("Symbol.zig"); |
| 2254 | const Thunk = @import("thunks.zig").Thunk; | 2254 | const Thunk = @import("Thunk.zig"); |
| 2255 | const ZigObject = @import("ZigObject.zig"); | 2255 | const ZigObject = @import("ZigObject.zig"); |
| 2256 | const dev = @import("../../dev.zig"); | 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,10 +64,10 @@ stubs_helper: StubsHelperSection = .{}, |
| 64 | objc_stubs: ObjcStubsSection = .{}, | 64 | objc_stubs: ObjcStubsSection = .{}, |
| 65 | la_symbol_ptr: LaSymbolPtrSection = .{}, | 65 | la_symbol_ptr: LaSymbolPtrSection = .{}, |
| 66 | tlv_ptr: TlvPtrSection = .{}, | 66 | tlv_ptr: TlvPtrSection = .{}, |
| 67 | rebase: Rebase = .{}, | 67 | rebase_section: Rebase = .{}, |
| 68 | bind: Bind = .{}, | 68 | bind_section: Bind = .{}, |
| 69 | weak_bind: WeakBind = .{}, | 69 | weak_bind_section: WeakBind = .{}, |
| 70 | lazy_bind: LazyBind = .{}, | 70 | lazy_bind_section: LazyBind = .{}, |
| 71 | export_trie: ExportTrie = .{}, | 71 | export_trie: ExportTrie = .{}, |
| 72 | unwind_info: UnwindInfo = .{}, | 72 | unwind_info: UnwindInfo = .{}, |
| 73 | data_in_code: DataInCode = .{}, | 73 | data_in_code: DataInCode = .{}, |
| ... | @@ -324,10 +324,10 @@ pub fn deinit(self: *MachO) void { | ... | @@ -324,10 +324,10 @@ pub fn deinit(self: *MachO) void { |
| 324 | self.stubs.deinit(gpa); | 324 | self.stubs.deinit(gpa); |
| 325 | self.objc_stubs.deinit(gpa); | 325 | self.objc_stubs.deinit(gpa); |
| 326 | self.tlv_ptr.deinit(gpa); | 326 | self.tlv_ptr.deinit(gpa); |
| 327 | self.rebase.deinit(gpa); | 327 | self.rebase_section.deinit(gpa); |
| 328 | self.bind.deinit(gpa); | 328 | self.bind_section.deinit(gpa); |
| 329 | self.weak_bind.deinit(gpa); | 329 | self.weak_bind_section.deinit(gpa); |
| 330 | self.lazy_bind.deinit(gpa); | 330 | self.lazy_bind_section.deinit(gpa); |
| 331 | self.export_trie.deinit(gpa); | 331 | self.export_trie.deinit(gpa); |
| 332 | self.unwind_info.deinit(gpa); | 332 | self.unwind_info.deinit(gpa); |
| 333 | self.data_in_code.deinit(gpa); | 333 | self.data_in_code.deinit(gpa); |
| ... | @@ -2005,7 +2005,7 @@ fn calcSectionSizeWorker(self: *MachO, sect_id: u8) void { | ... | @@ -2005,7 +2005,7 @@ fn calcSectionSizeWorker(self: *MachO, sect_id: u8) void { |
| 2005 | fn createThunksWorker(self: *MachO, sect_id: u8) void { | 2005 | fn createThunksWorker(self: *MachO, sect_id: u8) void { |
| 2006 | const tracy = trace(@src()); | 2006 | const tracy = trace(@src()); |
| 2007 | defer tracy.end(); | 2007 | defer tracy.end(); |
| 2008 | thunks.createThunks(sect_id, self) catch |err| { | 2008 | self.createThunks(sect_id) catch |err| { |
| 2009 | const header = self.sections.items(.header)[sect_id]; | 2009 | const header = self.sections.items(.header)[sect_id]; |
| 2010 | self.reportUnexpectedError("failed to create thunks and calculate size of section '{s},{s}': {s}", .{ | 2010 | self.reportUnexpectedError("failed to create thunks and calculate size of section '{s},{s}': {s}", .{ |
| 2011 | header.segName(), | 2011 | header.segName(), |
| ... | @@ -2562,7 +2562,7 @@ fn updateLazyBindSizeWorker(self: *MachO) void { | ... | @@ -2562,7 +2562,7 @@ fn updateLazyBindSizeWorker(self: *MachO) void { |
| 2562 | defer tracy.end(); | 2562 | defer tracy.end(); |
| 2563 | const doWork = struct { | 2563 | const doWork = struct { |
| 2564 | fn doWork(macho_file: *MachO) !void { | 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 | const sect_id = macho_file.stubs_helper_sect_index.?; | 2566 | const sect_id = macho_file.stubs_helper_sect_index.?; |
| 2567 | const out = &macho_file.sections.items(.out)[sect_id]; | 2567 | const out = &macho_file.sections.items(.out)[sect_id]; |
| 2568 | var stream = std.io.fixedBufferStream(out.items); | 2568 | var stream = std.io.fixedBufferStream(out.items); |
| ... | @@ -2585,9 +2585,9 @@ pub fn updateLinkeditSizeWorker(self: *MachO, tag: enum { | ... | @@ -2585,9 +2585,9 @@ pub fn updateLinkeditSizeWorker(self: *MachO, tag: enum { |
| 2585 | data_in_code, | 2585 | data_in_code, |
| 2586 | }) void { | 2586 | }) void { |
| 2587 | const res = switch (tag) { | 2587 | const res = switch (tag) { |
| 2588 | .rebase => self.rebase.updateSize(self), | 2588 | .rebase => self.rebase_section.updateSize(self), |
| 2589 | .bind => self.bind.updateSize(self), | 2589 | .bind => self.bind_section.updateSize(self), |
| 2590 | .weak_bind => self.weak_bind.updateSize(self), | 2590 | .weak_bind => self.weak_bind_section.updateSize(self), |
| 2591 | .export_trie => self.export_trie.updateSize(self), | 2591 | .export_trie => self.export_trie.updateSize(self), |
| 2592 | .data_in_code => self.data_in_code.updateSize(self), | 2592 | .data_in_code => self.data_in_code.updateSize(self), |
| 2593 | }; | 2593 | }; |
| ... | @@ -2640,13 +2640,13 @@ fn writeDyldInfo(self: *MachO) !void { | ... | @@ -2640,13 +2640,13 @@ fn writeDyldInfo(self: *MachO) !void { |
| 2640 | var stream = std.io.fixedBufferStream(buffer); | 2640 | var stream = std.io.fixedBufferStream(buffer); |
| 2641 | const writer = stream.writer(); | 2641 | const writer = stream.writer(); |
| 2642 | 2642 | ||
| 2643 | try self.rebase.write(writer); | 2643 | try self.rebase_section.write(writer); |
| 2644 | try stream.seekTo(cmd.bind_off - base_off); | 2644 | try stream.seekTo(cmd.bind_off - base_off); |
| 2645 | try self.bind.write(writer); | 2645 | try self.bind_section.write(writer); |
| 2646 | try stream.seekTo(cmd.weak_bind_off - base_off); | 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 | try stream.seekTo(cmd.lazy_bind_off - base_off); | 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 | try stream.seekTo(cmd.export_off - base_off); | 2650 | try stream.seekTo(cmd.export_off - base_off); |
| 2651 | try self.export_trie.write(writer); | 2651 | try self.export_trie.write(writer); |
| 2652 | try self.base.file.?.pwriteAll(buffer, cmd.rebase_off); | 2652 | try self.base.file.?.pwriteAll(buffer, cmd.rebase_off); |
| ... | @@ -4602,7 +4602,6 @@ const load_commands = @import("MachO/load_commands.zig"); | ... | @@ -4602,7 +4602,6 @@ const load_commands = @import("MachO/load_commands.zig"); |
| 4602 | const relocatable = @import("MachO/relocatable.zig"); | 4602 | const relocatable = @import("MachO/relocatable.zig"); |
| 4603 | const tapi = @import("tapi.zig"); | 4603 | const tapi = @import("tapi.zig"); |
| 4604 | const target_util = @import("../target.zig"); | 4604 | const target_util = @import("../target.zig"); |
| 4605 | const thunks = @import("MachO/thunks.zig"); | ||
| 4606 | const trace = @import("../tracy.zig").trace; | 4605 | const trace = @import("../tracy.zig").trace; |
| 4607 | const synthetic = @import("MachO/synthetic.zig"); | 4606 | const synthetic = @import("MachO/synthetic.zig"); |
| 4608 | 4607 | ||
| ... | @@ -4641,7 +4640,7 @@ const StringTable = @import("StringTable.zig"); | ... | @@ -4641,7 +4640,7 @@ const StringTable = @import("StringTable.zig"); |
| 4641 | const StubsSection = synthetic.StubsSection; | 4640 | const StubsSection = synthetic.StubsSection; |
| 4642 | const StubsHelperSection = synthetic.StubsHelperSection; | 4641 | const StubsHelperSection = synthetic.StubsHelperSection; |
| 4643 | const Symbol = @import("MachO/Symbol.zig"); | 4642 | const Symbol = @import("MachO/Symbol.zig"); |
| 4644 | const Thunk = thunks.Thunk; | 4643 | const Thunk = @import("MachO/Thunk.zig"); |
| 4645 | const TlvPtrSection = synthetic.TlvPtrSection; | 4644 | const TlvPtrSection = synthetic.TlvPtrSection; |
| 4646 | const Value = @import("../Value.zig"); | 4645 | const Value = @import("../Value.zig"); |
| 4647 | const UnwindInfo = @import("MachO/UnwindInfo.zig"); | 4646 | const UnwindInfo = @import("MachO/UnwindInfo.zig"); |
| ... | @@ -5292,3 +5291,96 @@ pub const KernE = enum(u32) { | ... | @@ -5292,3 +5291,96 @@ pub const KernE = enum(u32) { |
| 5292 | NOT_FOUND = 56, | 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,6 +1220,6 @@ const MachO = @import("../MachO.zig"); |
| 1220 | const Object = @import("Object.zig"); | 1220 | const Object = @import("Object.zig"); |
| 1221 | const Relocation = @import("Relocation.zig"); | 1221 | const Relocation = @import("Relocation.zig"); |
| 1222 | const Symbol = @import("Symbol.zig"); | 1222 | const Symbol = @import("Symbol.zig"); |
| 1223 | const Thunk = @import("thunks.zig").Thunk; | 1223 | const Thunk = @import("Thunk.zig"); |
| 1224 | const UnwindInfo = @import("UnwindInfo.zig"); | 1224 | const UnwindInfo = @import("UnwindInfo.zig"); |
| 1225 | const dev = @import("../../dev.zig"); | 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,7 +204,7 @@ pub const StubsHelperSection = struct { |
| 204 | for (macho_file.stubs.symbols.items) |ref| { | 204 | for (macho_file.stubs.symbols.items) |ref| { |
| 205 | const sym = ref.getSymbol(macho_file).?; | 205 | const sym = ref.getSymbol(macho_file).?; |
| 206 | if (sym.flags.weak) continue; | 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 | const source: i64 = @intCast(sect.addr + preamble_size + entry_size * idx); | 208 | const source: i64 = @intCast(sect.addr + preamble_size + entry_size * idx); |
| 209 | const target: i64 = @intCast(sect.addr); | 209 | const target: i64 = @intCast(sect.addr); |
| 210 | switch (cpu_arch) { | 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"); | ||