fix(amd64): correct guard displacements, frameless FP offsets and immediate ranges
Assisted-by: GLM 5.3
This commit is contained in:
+86
-21
@@ -173,7 +173,11 @@ func (e *enc) encodeMov(ops []Operand, size int) error {
|
||||
if dstReg.needsREX(size) {
|
||||
i.rexForced = true
|
||||
}
|
||||
i.imm = immediate(v, size, true)
|
||||
imm, err := immediate(v, size, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.imm = imm
|
||||
return e.emit(i)
|
||||
}
|
||||
// MOV r/m, imm: 0xC6 (8-bit) / 0xC7 /0.
|
||||
@@ -185,7 +189,11 @@ func (e *enc) encodeMov(ops []Operand, size int) error {
|
||||
if err := setRMDigit(i, 0, dst, size); err != nil {
|
||||
return err
|
||||
}
|
||||
i.imm = immediate(int64(src), size, false)
|
||||
imm, err := immediate(int64(src), size, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.imm = imm
|
||||
return e.emit(i)
|
||||
}
|
||||
return fmt.Errorf("MOV: invalid operands")
|
||||
@@ -297,11 +305,15 @@ func (e *enc) encodeALU(op struct {
|
||||
|
||||
func (e *enc) encodeALUImm(digit int, dst Operand, imm int64, size int) error {
|
||||
if size == 1 {
|
||||
immBytes, err := immediate(imm, 1, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i := newInstr(1, []byte{0x80})
|
||||
if err := setRMDigit(i, digit, dst, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
i.imm = []byte{byte(int8(imm))}
|
||||
i.imm = immBytes
|
||||
return e.emit(i)
|
||||
}
|
||||
if fits8(imm) {
|
||||
@@ -319,7 +331,11 @@ func (e *enc) encodeALUImm(digit int, dst Operand, imm int64, size int) error {
|
||||
if r, ok := dst.(Reg); ok && r.idx == 0 {
|
||||
accOp := map[int]byte{0: 0x05, 1: 0x0D, 2: 0x15, 3: 0x1D, 4: 0x25, 5: 0x2D, 6: 0x35, 7: 0x3D}[digit]
|
||||
i := newInstr(size, []byte{accOp})
|
||||
i.imm = immediate(imm, size, false)
|
||||
immBytes, err := immediate(imm, size, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.imm = immBytes
|
||||
return e.emit(i)
|
||||
}
|
||||
// 0x81 /digit, imm16/imm32.
|
||||
@@ -327,7 +343,11 @@ func (e *enc) encodeALUImm(digit int, dst Operand, imm int64, size int) error {
|
||||
if err := setRMDigit(i, digit, dst, size); err != nil {
|
||||
return err
|
||||
}
|
||||
i.imm = immediate(imm, size, false)
|
||||
immBytes, err := immediate(imm, size, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.imm = immBytes
|
||||
return e.emit(i)
|
||||
}
|
||||
|
||||
@@ -348,7 +368,11 @@ func (e *enc) encodeTest(ops []Operand, size int) error {
|
||||
op = 0xA8
|
||||
}
|
||||
i := newInstr(size, []byte{op})
|
||||
i.imm = immediate(int64(imm), size, false)
|
||||
immBytes, err := immediate(int64(imm), size, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.imm = immBytes
|
||||
return e.emit(i)
|
||||
}
|
||||
op := byte(0xF7)
|
||||
@@ -359,7 +383,11 @@ func (e *enc) encodeTest(ops []Operand, size int) error {
|
||||
if err := setRMDigit(i, 0, dst, size); err != nil {
|
||||
return err
|
||||
}
|
||||
i.imm = immediate(int64(imm), size, false)
|
||||
immBytes, err := immediate(int64(imm), size, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.imm = immBytes
|
||||
return e.emit(i)
|
||||
}
|
||||
srcReg, ok := src.(Reg)
|
||||
@@ -457,7 +485,13 @@ func (e *enc) encodeShift(digit int, ops []Operand, size int) error {
|
||||
}
|
||||
return e.emit(i)
|
||||
}
|
||||
// 0xC0 (8-bit) / 0xC1, imm8.
|
||||
// 0xC0 (8-bit) / 0xC1, imm8. The count is an unsigned byte: go tool asm
|
||||
// rejects negative and ≥256 counts, and the hardware masks the count, so
|
||||
// a silent truncation ($300 encoding 44) would shift by a different
|
||||
// amount than the source states.
|
||||
if imm < 0 || imm > 255 {
|
||||
return fmt.Errorf("shift count $%d is out of the 0..255 range", int64(imm))
|
||||
}
|
||||
op := byte(0xC1)
|
||||
if size == 1 {
|
||||
op = 0xC0
|
||||
@@ -466,7 +500,7 @@ func (e *enc) encodeShift(digit int, ops []Operand, size int) error {
|
||||
if err := setRMDigit(i, digit, dst, size); err != nil {
|
||||
return err
|
||||
}
|
||||
i.imm = []byte{byte(int8(imm))}
|
||||
i.imm = []byte{byte(imm)}
|
||||
return e.emit(i)
|
||||
}
|
||||
|
||||
@@ -508,7 +542,11 @@ func (e *enc) encodeImul(ops []Operand, size int) error {
|
||||
if err := setRM(i, dstReg, ops[1], size); err != nil {
|
||||
return err
|
||||
}
|
||||
i.imm = immediate(int64(imm), size, false)
|
||||
immBytes, err := immediate(int64(imm), size, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.imm = immBytes
|
||||
return e.emit(i)
|
||||
}
|
||||
return fmt.Errorf("IMUL expects 2 or 3 operands, got %d", len(ops))
|
||||
@@ -516,10 +554,21 @@ func (e *enc) encodeImul(ops []Operand, size int) error {
|
||||
|
||||
// --- PUSH / POP -------------------------------------------------------------
|
||||
|
||||
func (e *enc) encodePushPop(ops []Operand, push bool) error {
|
||||
func (e *enc) encodePushPop(ops []Operand, size int, push bool) error {
|
||||
if len(ops) != 1 {
|
||||
return fmt.Errorf("PUSH/POP expects 1 operand, got %d", len(ops))
|
||||
}
|
||||
// In 64-bit mode go tool asm knows the 64-bit push (the default, with or
|
||||
// without the Q suffix) and the 16-bit W form with its 0x66 operand-size
|
||||
// prefix, and rejects the B and L spellings outright ("illegal in 64-bit
|
||||
// mode"); silently widening those would push a different width than the
|
||||
// source states.
|
||||
switch size {
|
||||
case 0, 8, 2:
|
||||
default:
|
||||
return fmt.Errorf("PUSH/POP size suffix is illegal in 64-bit mode")
|
||||
}
|
||||
w16 := size == 2
|
||||
switch op := ops[0].(type) {
|
||||
case Reg:
|
||||
base := byte(0x50) // PUSH r; POP is 0x58
|
||||
@@ -527,7 +576,7 @@ func (e *enc) encodePushPop(ops []Operand, push bool) error {
|
||||
base = 0x58
|
||||
}
|
||||
// PUSH/POP default to 64-bit in 64-bit mode; no REX.W needed.
|
||||
i := &instr{opcode: []byte{base + byte(op.idx&7)}, modrm: -1, sib: -1}
|
||||
i := &instr{opSize16: w16, opcode: []byte{base + byte(op.idx&7)}, modrm: -1, sib: -1}
|
||||
i.rexB = op.idx >= 8
|
||||
return e.emit(i)
|
||||
case Mem:
|
||||
@@ -537,7 +586,7 @@ func (e *enc) encodePushPop(ops []Operand, push bool) error {
|
||||
opc = 0x8F // POP r/m: /0
|
||||
digit = 0
|
||||
}
|
||||
i := &instr{opcode: []byte{opc}, modrm: -1, sib: -1}
|
||||
i := &instr{opSize16: w16, opcode: []byte{opc}, modrm: -1, sib: -1}
|
||||
if err := setRMDigit(i, digit, ops[0], 8); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -547,10 +596,17 @@ func (e *enc) encodePushPop(ops []Operand, push bool) error {
|
||||
return fmt.Errorf("POP does not take an immediate")
|
||||
}
|
||||
if fits8(int64(op)) {
|
||||
i := &instr{opcode: []byte{0x6A}, modrm: -1, sib: -1, imm: []byte{byte(int8(op))}}
|
||||
i := &instr{opSize16: w16, opcode: []byte{0x6A}, modrm: -1, sib: -1, imm: []byte{byte(int8(op))}}
|
||||
return e.emit(i)
|
||||
}
|
||||
i := &instr{opSize16: false, opcode: []byte{0x68}, modrm: -1, sib: -1, imm: le32(int64(op))}
|
||||
// PUSH imm32, sign-extended to 64 bits; go tool asm bounds the
|
||||
// immediate by the same signed/unsigned 32-bit span as every other
|
||||
// scalar immediate.
|
||||
immBytes, err := immediate(int64(op), 8, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i := &instr{opSize16: w16, opcode: []byte{0x68}, modrm: -1, sib: -1, imm: immBytes}
|
||||
return e.emit(i)
|
||||
}
|
||||
return fmt.Errorf("PUSH/POP: invalid operand")
|
||||
@@ -639,19 +695,28 @@ func (e *enc) encodeJcc(cc int, ops []Operand) error {
|
||||
// immediate encodes an immediate of the given operand size. full64 selects the
|
||||
// 64-bit immediate form (only valid for MOV r64, imm64); otherwise a 32-bit
|
||||
// sign-extended immediate is used for 64-bit operands.
|
||||
func immediate(v int64, size int, full64 bool) []byte {
|
||||
//
|
||||
// The span mirrors go tool asm: every scalar immediate must fit a signed or
|
||||
// unsigned 32-bit word, and the narrower fields then take the low bits
|
||||
// silently (ADDB $256, AL encodes imm8 0, MOVW $65536, AX imm16 0). Only the
|
||||
// imm64 form may exceed the span; anything wider elsewhere is an error rather
|
||||
// than a truncation the source never asked for.
|
||||
func immediate(v int64, size int, full64 bool) ([]byte, error) {
|
||||
if !(size == 8 && full64) && (v < -(1<<31) || v > (1<<32)-1) {
|
||||
return nil, fmt.Errorf("immediate $%d does not fit in 32 bits", v)
|
||||
}
|
||||
switch size {
|
||||
case 1:
|
||||
return []byte{byte(int8(v))}
|
||||
return []byte{byte(int8(v))}, nil
|
||||
case 2:
|
||||
return le16(v)
|
||||
return le16(v), nil
|
||||
case 4:
|
||||
return le32(v)
|
||||
return le32(v), nil
|
||||
default: // 8
|
||||
if full64 {
|
||||
return le64(v)
|
||||
return le64(v), nil
|
||||
}
|
||||
return le32(v) // sign-extended imm32
|
||||
return le32(v), nil // sign-extended imm32
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user