From 47d561b229366f83bcc1d77b65303f3a4489b212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Wed, 7 Oct 2026 19:36:23 +0200 Subject: [PATCH] refactor(arch): extract the displacement tail and SIB builders Assisted-by: GLM 5.3 Flash --- arch/amd64_ext.go | 79 +++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 43 deletions(-) diff --git a/arch/amd64_ext.go b/arch/amd64_ext.go index 9cf1c7c..d64547c 100644 --- a/arch/amd64_ext.go +++ b/arch/amd64_ext.go @@ -183,34 +183,48 @@ func (in ExtInstr) amd64Memory(op ExtOperand, pos int) (base int, disp int64, er return op.Reg, op.Imm, nil } +// amd64DispTail lays the displacement bytes and their ModR/M mod field: no +// bytes at displacement zero, a disp8 when the value fits a signed byte and +// a disp32 otherwise. The force flag makes a zero displacement encode +// anyway, the mod-01 shape the RBP and R13 bases demand, whose bare r/m 101 +// would otherwise address RIP-relative. +func amd64DispTail(disp int64, force bool) (mod byte, tail []byte) { + if !force && disp == 0 { + return 0, nil + } + if disp >= -128 && disp <= 127 { + return 1, []byte{byte(disp)} + } + return 2, []byte{byte(disp), byte(disp >> 8), byte(disp >> 16), byte(disp >> 24)} +} + +// amd64Sib builds the SIB byte the scaled forms carry: the scale field over +// the index and the base, whose number rides the r/m field. +func amd64Sib(base, index, scale int) byte { + sib := byte(base & 7) + sib |= byte(index&7) << 3 + switch scale { + case 2: + sib |= 1 << 6 + case 4: + sib |= 2 << 6 + case 8: + sib |= 3 << 6 + } + return sib +} + // amd64EncodeMemory returns the register-form template with a base-relative // memory operand filled in: dest and vvvv keep their register meanings, the // ModR/M r/m field carries the base, and the high base bit rides EVEX.B as // amd64Encode lays it. The ModR/M mod bits and the trailing SIB and // displacement bytes follow the canonical choices the GNU assembler makes -// for the plain, unscaled SDM displacements: no displacement bytes at -// displacement zero, a disp8 when the value fits a signed byte and a disp32 -// otherwise, the SIB byte 0x24 when the base is RSP or R12, whose r/m -// encoding 100 demands it, and a forced displacement on RBP and R13, whose -// mod-00 r/m encoding 101 means RIP-relative. The operand must have passed -// amd64Memory first. +// for the plain, unscaled SDM displacements, and the operand must have +// passed amd64Memory first. func amd64EncodeMemory(b []byte, dest, vvvv, base int, disp int64) []byte { out := amd64Encode(b, dest, vvvv, base) rm := base & 7 - var tail []byte - mod := byte(0) - switch { - case rm == 5 || disp != 0: - // RBP and R13 cannot drop the displacement: mod 00 with r/m 101 - // addresses RIP-relative, not through the base. - if disp >= -128 && disp <= 127 { - mod = 1 - tail = []byte{byte(disp)} - } else { - mod = 2 - tail = []byte{byte(disp), byte(disp >> 8), byte(disp >> 16), byte(disp >> 24)} - } - } + mod, tail := amd64DispTail(disp, rm == 5) if rm == 4 { // RSP and R12 need the SIB byte: no index, base 100. tail = append([]byte{0x24}, tail...) @@ -247,29 +261,8 @@ func amd64EncodeScaledMemory(b []byte, dest, vvvv, base, index, scale int, disp out[1] &^= 0x40 } rm := base & 7 - var tail []byte - mod := byte(0) - switch { - case rm == 5 || disp != 0: - if disp >= -128 && disp <= 127 { - mod = 1 - tail = []byte{byte(disp)} - } else { - mod = 2 - tail = []byte{byte(disp), byte(disp >> 8), byte(disp >> 16), byte(disp >> 24)} - } - } - sib := byte(rm) - sib |= byte(index&7) << 3 - switch scale { - case 2: - sib |= 1 << 6 - case 4: - sib |= 2 << 6 - case 8: - sib |= 3 << 6 - } - tail = append([]byte{sib}, tail...) + mod, tail := amd64DispTail(disp, rm == 5) + tail = append([]byte{amd64Sib(rm, index, scale)}, tail...) out[5] = out[5]&0x38 | mod<<6 | 4 return append(out, tail...) }