package x86_64 import ( "errors" "fmt" "math" "reflect" "strconv" "strings" "sync/atomic" ) // RelativeOffset represents an RIP-relative offset. type RelativeOffset int32 // String implements the fmt.Stringer interface. func (self RelativeOffset) String() string { if self == 0 { return "(%rip)" } else { return fmt.Sprintf("%d(%%rip)", self) } } // RoundingControl represents a floating-point rounding option. type RoundingControl uint8 const ( // RN_SAE represents "Round Nearest", which is the default rounding option. RN_SAE RoundingControl = iota // RD_SAE represents "Round Down". RD_SAE // RU_SAE represents "Round Up". RU_SAE // RZ_SAE represents "Round towards Zero". RZ_SAE ) var _RC_NAMES = map[RoundingControl]string{ RN_SAE: "rn-sae", RD_SAE: "rd-sae", RU_SAE: "ru-sae", RZ_SAE: "rz-sae", } func (self RoundingControl) String() string { if v, ok := _RC_NAMES[self]; ok { return v } else { panic("invalid RoundingControl value") } } // ExceptionControl represents the "Suppress All Exceptions" flag. type ExceptionControl uint8 const ( // SAE represents the flag "Suppress All Exceptions" for floating point operations. SAE ExceptionControl = iota ) func (ExceptionControl) String() string { return "sae" } // AddressType indicates which kind of value that an Addressable object contains. type AddressType uint const ( // None indicates the Addressable does not contain any addressable value. None AddressType = iota // Memory indicates the Addressable contains a memory address. Memory // Offset indicates the Addressable contains an RIP-relative offset. Offset // Reference indicates the Addressable contains a label reference. Reference ) // Disposable is a type of object that can be Free'd manually. type Disposable interface { Free() } // Label represents a location within the program. type Label struct { refs int64 Name string Dest *Instruction } func (self *Label) offset(p uintptr, n int) RelativeOffset { if self.Dest == nil { panic("unresolved label: " + self.Name) } else { return RelativeOffset(self.Dest.pc - p - uintptr(n)) } } // Free decreases the reference count of a Label, if the // refcount drops to 0, the Label will be recycled. func (self *Label) Free() { if atomic.AddInt64(&self.refs, -1) == 0 { //freeLabel(self) } } // String implements the fmt.Stringer interface. func (self *Label) String() string { if self.Dest == nil { return fmt.Sprintf("%s(%%rip)", self.Name) } else { return fmt.Sprintf("%s(%%rip)@%#x", self.Name, self.Dest.pc) } } // Retain increases the reference count of a Label. func (self *Label) Retain() *Label { atomic.AddInt64(&self.refs, 1) return self } // Evaluate implements the interface expr.Term. func (self *Label) Evaluate() (int64, error) { if self.Dest != nil { return int64(self.Dest.pc), nil } else { return 0, errors.New("unresolved label: " + self.Name) } } // Addressable is a union to represent an addressable operand. type Addressable struct { Type AddressType Memory MemoryAddress Offset RelativeOffset Reference *Label } // String implements the fmt.Stringer interface. func (self *Addressable) String() string { switch self.Type { case None: return "(not addressable)" case Memory: return self.Memory.String() case Offset: return self.Offset.String() case Reference: return self.Reference.String() default: return "(invalid addressable)" } } // MemoryOperand represents a memory operand for an instruction. type MemoryOperand struct { refs int64 Size int Addr Addressable Mask RegisterMask Masked bool Broadcast uint8 } const ( _Sizes = 0b10000000100010111 // bit-mask for valid sizes (0, 1, 2, 4, 8, 16) ) func (self *MemoryOperand) isVMX(evex bool) bool { return self.Addr.Type == Memory && self.Addr.Memory.isVMX(evex) } func (self *MemoryOperand) isVMY(evex bool) bool { return self.Addr.Type == Memory && self.Addr.Memory.isVMY(evex) } func (self *MemoryOperand) isVMZ() bool { return self.Addr.Type == Memory && self.Addr.Memory.isVMZ() } func (self *MemoryOperand) isMem() bool { if (_Sizes & (1 << self.Broadcast)) == 0 { return false } else if self.Addr.Type == Memory { return self.Addr.Memory.isMem() } else if self.Addr.Type == Offset { return true } else if self.Addr.Type == Reference { return true } else { return false } } func (self *MemoryOperand) isSize(n int) bool { return self.Size == 0 || self.Size == n } func (self *MemoryOperand) isBroadcast(n int, b uint8) bool { return self.Size == n && self.Broadcast == b } func (self *MemoryOperand) formatMask() string { if !self.Masked { return "" } else { return self.Mask.String() } } func (self *MemoryOperand) formatBroadcast() string { if self.Broadcast == 0 { return "" } else { return fmt.Sprintf("{1to%d}", self.Broadcast) } } func (self *MemoryOperand) ensureAddrValid() { switch self.Addr.Type { case None: break case Memory: self.Addr.Memory.EnsureValid() case Offset: break case Reference: break default: panic("invalid address type") } } func (self *MemoryOperand) ensureSizeValid() { if (_Sizes & (1 << self.Size)) == 0 { panic("invalid memory operand size") } } func (self *MemoryOperand) ensureBroadcastValid() { if (_Sizes & (1 << self.Broadcast)) == 0 { panic("invalid memory operand broadcast") } } // Free decreases the reference count of a MemoryOperand, if the // refcount drops to 0, the Label will be recycled. func (self *MemoryOperand) Free() { if atomic.AddInt64(&self.refs, -1) == 0 { //freeMemoryOperand(self) } } // String implements the fmt.Stringer interface. func (self *MemoryOperand) String() string { return self.Addr.String() + self.formatMask() + self.formatBroadcast() } // Retain increases the reference count of a MemoryOperand. func (self *MemoryOperand) Retain() *MemoryOperand { atomic.AddInt64(&self.refs, 1) return self } // EnsureValid checks if the memory operand is valid, if not, it panics. func (self *MemoryOperand) EnsureValid() { self.ensureAddrValid() self.ensureSizeValid() self.ensureBroadcastValid() } // MemoryAddress represents a memory address. type MemoryAddress struct { Base Register Index Register Scale uint8 Displacement int32 } const ( _Scales = 0b100010111 // bit-mask for valid scales (0, 1, 2, 4, 8) ) func (self *MemoryAddress) isVMX(evex bool) bool { return self.isMemBase() && (self.Index == nil || isXMM(self.Index) || (evex && isEVEXXMM(self.Index))) } func (self *MemoryAddress) isVMY(evex bool) bool { return self.isMemBase() && (self.Index == nil || isYMM(self.Index) || (evex && isEVEXYMM(self.Index))) } func (self *MemoryAddress) isVMZ() bool { return self.isMemBase() && (self.Index == nil || isZMM(self.Index)) } func (self *MemoryAddress) isMem() bool { return self.isMemBase() && (self.Index == nil || isReg64(self.Index)) } func (self *MemoryAddress) isMemBase() bool { return (self.Base == nil || isReg64(self.Base)) && // `Base` must be 64-bit if present (self.Scale == 0) == (self.Index == nil) && // `Scale` and `Index` depends on each other (_Scales&(1<= 2 { sb.WriteByte(',') sb.WriteString(strconv.Itoa(int(self.Scale))) } } /* close the bracket */ sb.WriteByte(')') return sb.String() } // EnsureValid checks if the memory address is valid, if not, it panics. func (self *MemoryAddress) EnsureValid() { if !self.isMemBase() || (self.Index != nil && !isIndexable(self.Index)) { panic("not a valid memory address") } } // Ref constructs a memory reference to a label. func Ref(ref *Label) (v *MemoryOperand) { v = CreateMemoryOperand() v.Addr.Type = Reference v.Addr.Reference = ref return } // Abs construct a simple memory address that represents absolute addressing. func Abs(disp int32) *MemoryOperand { return Sib(nil, nil, 0, disp) } // Ptr constructs a simple memory operand with base and displacement. func Ptr(base Register, disp int32) *MemoryOperand { return Sib(base, nil, 0, disp) } // Sib constructs a simple memory operand that represents a complete memory address. func Sib(base Register, index Register, scale uint8, disp int32) (v *MemoryOperand) { v = CreateMemoryOperand() v.Addr.Type = Memory v.Addr.Memory.Base = base v.Addr.Memory.Index = index v.Addr.Memory.Scale = scale v.Addr.Memory.Displacement = disp v.EnsureValid() return } /** Operand Matching Helpers **/ const _IntMask = (1 << reflect.Int) | (1 << reflect.Int8) | (1 << reflect.Int16) | (1 << reflect.Int32) | (1 << reflect.Int64) | (1 << reflect.Uint) | (1 << reflect.Uint8) | (1 << reflect.Uint16) | (1 << reflect.Uint32) | (1 << reflect.Uint64) | (1 << reflect.Uintptr) func isInt(k reflect.Kind) bool { return (_IntMask & (1 << k)) != 0 } func asInt64(v interface{}) (int64, bool) { if isSpecial(v) { return 0, false } else if x := efaceOf(v); isInt(x.kind()) { return x.toInt64(), true } else { return 0, false } } func inRange(v interface{}, low int64, high int64) bool { x, ok := asInt64(v) return ok && x >= low && x <= high } func isSpecial(v interface{}) bool { switch v.(type) { case Register8: return true case Register16: return true case Register32: return true case Register64: return true case KRegister: return true case MMRegister: return true case XMMRegister: return true case YMMRegister: return true case ZMMRegister: return true case RelativeOffset: return true case RoundingControl: return true case ExceptionControl: return true default: return false } } func isIndexable(v interface{}) bool { return isZMM(v) || isReg64(v) || isEVEXXMM(v) || isEVEXYMM(v) } func isImm4(v interface{}) bool { return inRange(v, 0, 15) } func isImm8(v interface{}) bool { return inRange(v, math.MinInt8, math.MaxUint8) } func isImm16(v interface{}) bool { return inRange(v, math.MinInt16, math.MaxUint16) } func isImm32(v interface{}) bool { return inRange(v, math.MinInt32, math.MaxUint32) } func isImm64(v interface{}) bool { _, r := asInt64(v); return r } func isConst1(v interface{}) bool { x, r := asInt64(v); return r && x == 1 } func isConst3(v interface{}) bool { x, r := asInt64(v); return r && x == 3 } func isRel8(v interface{}) bool { x, r := v.(RelativeOffset) return r && x >= math.MinInt8 && x <= math.MaxInt8 } func isRel32(v interface{}) bool { _, r := v.(RelativeOffset); return r } func isLabel(v interface{}) bool { _, r := v.(*Label); return r } func isReg8(v interface{}) bool { _, r := v.(Register8); return r } func isReg8REX(v interface{}) bool { x, r := v.(Register8) return r && (x&0x80) == 0 && x >= SPL } func isReg16(v interface{}) bool { _, r := v.(Register16); return r } func isReg32(v interface{}) bool { _, r := v.(Register32); return r } func isReg64(v interface{}) bool { _, r := v.(Register64); return r } func isMM(v interface{}) bool { _, r := v.(MMRegister); return r } func isXMM(v interface{}) bool { x, r := v.(XMMRegister); return r && x <= XMM15 } func isEVEXXMM(v interface{}) bool { _, r := v.(XMMRegister); return r } func isXMMk(v interface{}) bool { x, r := v.(MaskedRegister) return isXMM(v) || (r && isXMM(x.Reg) && !x.Mask.Z) } func isXMMkz(v interface{}) bool { x, r := v.(MaskedRegister) return isXMM(v) || (r && isXMM(x.Reg)) } func isYMM(v interface{}) bool { x, r := v.(YMMRegister); return r && x <= YMM15 } func isEVEXYMM(v interface{}) bool { _, r := v.(YMMRegister); return r } func isYMMk(v interface{}) bool { x, r := v.(MaskedRegister) return isYMM(v) || (r && isYMM(x.Reg) && !x.Mask.Z) } func isYMMkz(v interface{}) bool { x, r := v.(MaskedRegister) return isYMM(v) || (r && isYMM(x.Reg)) } func isZMM(v interface{}) bool { _, r := v.(ZMMRegister); return r } func isZMMk(v interface{}) bool { x, r := v.(MaskedRegister) return isZMM(v) || (r && isZMM(x.Reg) && !x.Mask.Z) } func isZMMkz(v interface{}) bool { x, r := v.(MaskedRegister) return isZMM(v) || (r && isZMM(x.Reg)) } func isK(v interface{}) bool { _, r := v.(KRegister); return r } func isKk(v interface{}) bool { x, r := v.(MaskedRegister) return isK(v) || (r && isK(x.Reg) && !x.Mask.Z) } func isM(v interface{}) bool { x, r := v.(*MemoryOperand) return r && x.isMem() && x.Broadcast == 0 && !x.Masked } func isMk(v interface{}) bool { x, r := v.(*MemoryOperand) return r && x.isMem() && x.Broadcast == 0 && !(x.Masked && x.Mask.Z) } func isMkz(v interface{}) bool { x, r := v.(*MemoryOperand) return r && x.isMem() && x.Broadcast == 0 } func isM8(v interface{}) bool { x, r := v.(*MemoryOperand) return r && isM(v) && x.isSize(1) } func isM16(v interface{}) bool { x, r := v.(*MemoryOperand) return r && isM(v) && x.isSize(2) } func isM16kz(v interface{}) bool { x, r := v.(*MemoryOperand) return r && isMkz(v) && x.isSize(2) } func isM32(v interface{}) bool { x, r := v.(*MemoryOperand) return r && isM(v) && x.isSize(4) } func isM32k(v interface{}) bool { x, r := v.(*MemoryOperand) return r && isMk(v) && x.isSize(4) } func isM32kz(v interface{}) bool { x, r := v.(*MemoryOperand) return r && isMkz(v) && x.isSize(4) } func isM64(v interface{}) bool { x, r := v.(*MemoryOperand) return r && isM(v) && x.isSize(8) } func isM64k(v interface{}) bool { x, r := v.(*MemoryOperand) return r && isMk(v) && x.isSize(8) } func isM64kz(v interface{}) bool { x, r := v.(*MemoryOperand) return r && isMkz(v) && x.isSize(8) } func isM128(v interface{}) bool { x, r := v.(*MemoryOperand) return r && isM(v) && x.isSize(16) } func isM128kz(v interface{}) bool { x, r := v.(*MemoryOperand) return r && isMkz(v) && x.isSize(16) } func isM256(v interface{}) bool { x, r := v.(*MemoryOperand) return r && isM(v) && x.isSize(32) } func isM256kz(v interface{}) bool { x, r := v.(*MemoryOperand) return r && isMkz(v) && x.isSize(32) } func isM512(v interface{}) bool { x, r := v.(*MemoryOperand) return r && isM(v) && x.isSize(64) } func isM512kz(v interface{}) bool { x, r := v.(*MemoryOperand) return r && isMkz(v) && x.isSize(64) } func isM64M32bcst(v interface{}) bool { x, r := v.(*MemoryOperand) return isM64(v) || (r && x.isBroadcast(4, 2)) } func isM128M32bcst(v interface{}) bool { x, r := v.(*MemoryOperand) return isM128(v) || (r && x.isBroadcast(4, 4)) } func isM256M32bcst(v interface{}) bool { x, r := v.(*MemoryOperand) return isM256(v) || (r && x.isBroadcast(4, 8)) } func isM512M32bcst(v interface{}) bool { x, r := v.(*MemoryOperand) return isM512(v) || (r && x.isBroadcast(4, 16)) } func isM128M64bcst(v interface{}) bool { x, r := v.(*MemoryOperand) return isM128(v) || (r && x.isBroadcast(8, 2)) } func isM256M64bcst(v interface{}) bool { x, r := v.(*MemoryOperand) return isM256(v) || (r && x.isBroadcast(8, 4)) } func isM512M64bcst(v interface{}) bool { x, r := v.(*MemoryOperand) return isM512(v) || (r && x.isBroadcast(8, 8)) } func isVMX(v interface{}) bool { x, r := v.(*MemoryOperand) return r && x.isVMX(false) && !x.Masked } func isEVEXVMX(v interface{}) bool { x, r := v.(*MemoryOperand) return r && x.isVMX(true) && !x.Masked } func isVMXk(v interface{}) bool { x, r := v.(*MemoryOperand); return r && x.isVMX(true) } func isVMY(v interface{}) bool { x, r := v.(*MemoryOperand) return r && x.isVMY(false) && !x.Masked } func isEVEXVMY(v interface{}) bool { x, r := v.(*MemoryOperand) return r && x.isVMY(true) && !x.Masked } func isVMYk(v interface{}) bool { x, r := v.(*MemoryOperand); return r && x.isVMY(true) } func isVMZ(v interface{}) bool { x, r := v.(*MemoryOperand) return r && x.isVMZ() && !x.Masked } func isVMZk(v interface{}) bool { x, r := v.(*MemoryOperand); return r && x.isVMZ() } func isSAE(v interface{}) bool { _, r := v.(ExceptionControl); return r } func isER(v interface{}) bool { _, r := v.(RoundingControl); return r } func isImmExt(v interface{}, ext int, min int64, max int64) bool { if x, ok := asInt64(v); !ok { return false } else if m := int64(1) << (8 * ext); x < m && x >= m+min { return true } else { return x <= max && x >= min } } func isImm8Ext(v interface{}, ext int) bool { return isImmExt(v, ext, math.MinInt8, math.MaxInt8) } func isImm32Ext(v interface{}, ext int) bool { return isImmExt(v, ext, math.MinInt32, math.MaxInt32) }