our
$VERSION
=
'0.005'
;
use
if
!
eval
{
pack
(
'Q<'
,1) },
'CPU::x86_64::InstructionWriter::_int32'
,
qw/pack/
;
my
@byte_registers
=
qw( AH AL BH BL CH CL DH DL SPL BPL SIL DIL R8B R9B R10B R11B R12B R13B R14B R15B )
;
my
%byte_register_alias
= (
map
{;
"R${_}L"
=>
"R${_}B"
} 8..15 );
my
@word_registers
=
qw( AX BX CX DX SI DI SP BP R8W R9W R10W R11W R12W R13W R14W R15W )
;
my
@long_registers
=
qw( EAX EBX ECX EDX ESI EDI ESP EBP R8D R9D R10D R11D R12D R13D R14D R15D )
;
my
@quad_registers
=
qw( RAX RBX RCX RDX RSI RDI RSP RBP R8 R9 R10 R11 R12 R13 R14 R15 RIP RFLAGS )
;
my
@sse_registers
= ( (
map
"XMM$_"
, 0..15), (
map
"YMM$_"
, 0..15), (
map
"ZMM$_"
, 0..31) );
my
@registers
= (
@byte_registers
,
@word_registers
,
@long_registers
,
@quad_registers
,
@sse_registers
);
{
no
strict
'refs'
;
eval
'sub '
.
$_
.
' { \''
.
$_
.
'\' } 1'
|| croak $@
for
@registers
;
*{__PACKAGE__.
"::$_"
}= *{__PACKAGE__.
"::$byte_register_alias{$_}"
}
for
keys
%byte_register_alias
;
}
my
%regnum8
= (
AL
=> 0x100,
CL
=> 0x101,
DL
=> 0x102,
BL
=> 0x103,
AH
=> 0x114,
CH
=> 0x115,
DH
=> 0x116,
BH
=> 0x117,
SPL
=> 0x124,
BPL
=> 0x125,
SIL
=> 0x126,
DIL
=> 0x127,
(
map
+(
"R${_}B"
=> (0x100|
$_
),
"R${_}L"
=> (0x100|
$_
) ), 0..3),
(
map
+(
"R${_}B"
=> (0x120|
$_
),
"R${_}L"
=> (0x120|
$_
) ), 4..15),
);
my
%regnum16
= (
AX
=> 0x200,
CX
=> 0x201,
DX
=> 0x202,
BX
=> 0x203,
SP
=> 0x204,
BP
=> 0x205,
SI
=> 0x206,
DI
=> 0x207,
(
map
+(
"R${_}W"
=> (0x200|
$_
) ), 0..15)
);
my
%regnum32
= (
EAX
=> 0x400,
ECX
=> 0x401,
EDX
=> 0x402,
EBX
=> 0x403,
ESP
=> 0x404,
EBP
=> 0x405,
ESI
=> 0x406,
EDI
=> 0x407,
(
map
+(
"R${_}D"
=> (0x400|
$_
) ), 0..15)
);
my
%regnum64
= (
RAX
=> 0x800,
RCX
=> 0x801,
RDX
=> 0x802,
RBX
=> 0x803,
RSP
=> 0x804,
RBP
=> 0x805,
RSI
=> 0x806,
RDI
=> 0x807,
RIP
=> 0x845,
(
map
+(
"R$_"
=> (0x800|
$_
) ), 0..15)
);
my
%regnum128
= (
(
map
+(
"XMM$_"
=> (0x1000|
$_
)), 0..15)
);
for
my
$regs
(\
%regnum8
, \
%regnum16
, \
%regnum32
, \
%regnum64
, \
%regnum128
) {
$regs
->{
lc
$_
}=
$regs
->{
$_
}
for
keys
%$regs
;
}
my
%regnum
= (
%regnum8
,
%regnum16
,
%regnum32
,
%regnum64
,
%regnum128
);
sub
unknown { CPU::x86_64::InstructionWriter::Unknown->new(
name
=>
$_
[0]); }
sub
unknown8 { CPU::x86_64::InstructionWriter::Unknown->new(
bits
=> 8,
name
=>
$_
[0]); }
sub
unknown16 { CPU::x86_64::InstructionWriter::Unknown->new(
bits
=> 16,
name
=>
$_
[0]); }
sub
unknown32 { CPU::x86_64::InstructionWriter::Unknown->new(
bits
=> 32,
name
=>
$_
[0]); }
sub
unknown64 { CPU::x86_64::InstructionWriter::Unknown->new(
bits
=> 64,
name
=>
$_
[0]); }
sub
unknown7 { CPU::x86_64::InstructionWriter::Unknown->new(
bits
=> 7,
name
=>
$_
[0]); }
sub
unknown15 { CPU::x86_64::InstructionWriter::Unknown->new(
bits
=> 15,
name
=>
$_
[0]); }
sub
unknown31 { CPU::x86_64::InstructionWriter::Unknown->new(
bits
=> 31,
name
=>
$_
[0]); }
sub
unknown63 { CPU::x86_64::InstructionWriter::Unknown->new(
bits
=> 63,
name
=>
$_
[0]); }
our
%EXPORT_TAGS
= (
registers
=> \
@registers
,
unknown
=> [
qw( unknown unknown8 unknown16 unknown32 unknown64 unknown7 unknown15 unknown31 unknown63 )
],
);
our
@EXPORT_OK
= (
map
{ @{
$_
} }
values
%EXPORT_TAGS
);
sub
new {
my
$class
=
shift
;
my
%args
=
@_
== 1 &&
ref
$_
[0] eq
'HASH'
? %{
$_
[0]}
: !(
@_
& 1)?
@_
: croak
"Expected hashref or even-length list of k,v pairs"
;
bless
{
start_address
=> (
$args
{start_address} // unknown64(
'start_address'
)),
debug
=>
$args
{debug},
_buf
=>
''
,
_unresolved
=> [],
labels
=> {},
scope
=>
''
,
_anon_label
=> 0,
},
$class
;
}
sub
start_address {
$_
[0]{start_address}=
$_
[1]
if
@_
> 1;
$_
[0]{start_address} }
sub
debug {
$_
[0]{debug}=
$_
[1]
if
@_
> 1;
$_
[0]{debug} }
sub
_buf { croak
"read-only"
if
@_
> 1;
$_
[0]{_buf} }
sub
_unresolved { croak
"read-only"
if
@_
> 1;
$_
[0]{_unresolved} }
sub
_anon_label {
$_
[0]{scope} .
'.anon'
. ++
$_
[0]{_anon_label} }
sub
labels { croak
"read-only"
if
@_
> 1;
$_
[0]{labels} }
sub
scope {
return
$_
[0]{scope}
unless
@_
> 1;
$_
[0]{scope}=
$_
[1] //
''
;
$_
[0]
}
sub
get_label {
my
(
$self
,
$name
)=
@_
;
my
$labels
=
$self
->labels;
$name
=
$self
->{scope} .
$name
if
defined
$name
&&
ord
$name
==
ord
'.'
;
unless
(
defined
$name
&&
defined
$labels
->{
$name
}) {
my
$label
=
bless
{
relative_to
=>
$self
->start_address }, __PACKAGE__.
'::Label'
;
$name
//=
$self
->_anon_label;
$label
->{name}=
$name
;
$labels
->{
$name
}=
$label
;
}
$labels
->{
$name
};
}
sub
label {
@_
== 2 or croak
"Invalid arguments to 'mark'"
;
$_
[1]=
$_
[0]->get_label
unless
defined
$_
[1];
my
(
$self
,
$label
)=
@_
;
$label
=
$self
->get_label(
$label
)
unless
ref
$label
;
defined
$label
->offset and croak
"Can't mark label '"
.
$label
->name.
"' twice"
;
$label
->{offset}=
length
(
$self
->{_buf});
$label
->{len}= 0;
push
@{
$self
->_unresolved },
$label
;
return
$self
;
}
sub
bytes {
my
$self
=
shift
;
$self
->_resolve;
return
$self
->_buf;
}
sub
append {
my
(
$self
,
$peer
,
$scope
)=
@_
;
$scope
//=
''
;
my
$ofs
=
length
$self
->{_buf};
$self
->{_buf} .=
$peer
->{_buf};
my
%label_map
;
for
my
$name
(
keys
%{
$peer
->{labels} }) {
my
$peer_label
=
$peer
->{labels}{
$name
};
my
$local_name
=
ord
$name
==
ord
'.'
?
$scope
.
$name
:
$name
;
my
$self_label
=
$self
->get_label(
$local_name
);
$label_map
{
$peer_label
}=
$self_label
;
croak
"Conflicting label '$local_name' is anchored in both writers"
if
defined
$self_label
->offset &&
defined
$peer_label
->offset
&&
$self_label
->relative_to !=
$peer_label
->relative_to;
$self_label
->{offset}=
$peer_label
->{offset};
$self_label
->{offset} +=
$ofs
if
defined
$self_label
->offset &&
$peer_label
->relative_to ==
$peer
->start_address;
}
push
@{
$self
->_unresolved },
map
+(
exists
$label_map
{
$_
}?
$label_map
{
$_
}
:
$_
->can(
'clone_into_writer'
)?
$_
->clone_into_writer(
$self
,
$ofs
, \
%label_map
)
: croak
"Don't know how to copy $_"
), @{
$peer
->{_unresolved} };
$self
;
}
sub
align {
my
(
$self
,
$bytes
,
$fill
)=
@_
;
(
$bytes
& (
$bytes
-1))
and croak
"Bytes must be a power of 2"
;
$self
->_align(~(
$bytes
-1),
$fill
);
}
sub
_align {
my
(
$self
,
$mask
,
$fill
)=
@_
;
$fill
//=
"\x90"
;
length
(
$fill
) == 1 or croak
"Fill byte must be 1 byte long"
;
$self
->_mark_unresolved(
0,
encoder
=>
sub
{
$fill
x (((
$_
[1]{offset} + ~
$mask
) &
$mask
) -
$_
[1]{offset})
}
);
}
sub
align2 {
splice
@_
, 1, 0, ~1;
&_align
; }
sub
align4 {
splice
@_
, 1, 0, ~3;
&_align
; }
sub
align8 {
splice
@_
, 1, 0, ~7;
&_align
; }
sub
data {
if
(
ref
$_
[1] eq
'HASH'
) {
my
(
$self
,
$set
)=
@_
;
my
$buf
=
''
;
my
$pos
=
length
$self
->{_buf};
for
my
$str
(
sort
{
length
$b
<=>
length
$a
}
keys
%$set
) {
my
$label
= (
$set
->{
$str
} //=
$self
->get_label);
defined
$label
->{offset} and croak
"Label for '$str' is already anchored"
;
my
$ofs
=
index
$buf
,
$str
;
if
(
$ofs
< 0) {
$ofs
=
length
$buf
;
$buf
.=
$str
;
}
$label
->{offset}=
$pos
+
$ofs
;
$label
->{len}=
length
$str
;
push
@{
$self
->_unresolved },
$label
;
}
$self
->{_buf} .=
$buf
;
}
else
{
$_
[0]{_buf} .=
$_
[1];
}
$_
[0]
}
sub
data_i8 {
$_
[0]{_buf} .=
chr
(
$_
[1]);
$_
[0] }
sub
data_i16 {
$_
[0]{_buf} .=
pack
(
'v'
,
$_
[1]);
$_
[0] }
sub
data_i32 {
$_
[0]{_buf} .=
pack
(
'V'
,
$_
[1]);
$_
[0] }
sub
data_i64 {
$_
[0]{_buf} .=
pack
(
'Q<'
,
$_
[1]);
$_
[0] }
sub
data_f32 {
$_
[0]{_buf} .=
pack
(
'f'
,
$_
[1]);
$_
[0] }
sub
data_f64 {
$_
[0]{_buf} .=
pack
(
'd'
,
$_
[1]);
$_
[0] }
sub
data_str {
my
(
$self
,
$str
,
%options
)=
@_
;
my
$encoding
=
$options
{encoding} //
'UTF-8'
;
my
$nul_terminate
=
$options
{nul_terminate} // 1;
if
(
ref
$str
eq
'HASH'
) {
my
%byte_strs
;
for
my
$sk
(
keys
%$str
) {
my
$bk
=
$sk
;
$bk
=~ s/(?<!\0)\z/\0/
if
$nul_terminate
;
$bk
= Encode::encode(
$encoding
,
$bk
, Encode::FB_CROAK);
$byte_strs
{
$bk
}= (
$str
->{
$sk
} //=
$self
->get_label);
}
return
$self
->data(\
%byte_strs
);
}
else
{
$str
=~ s/(?<!\0)\z/\0/
if
$nul_terminate
;
$self
->{_buf} .= Encode::encode(
$encoding
,
$str
, Encode::FB_CROAK);
}
$self
}
sub
_autodetect_signature_1 {
$_
[0]->_autodetect_signature(
$_
[1],
$_
[3],
$_
[2]) }
sub
_autodetect_signature_2 {
$_
[0]->_autodetect_signature(
$_
[1],
$_
[4],
$_
[2],
$_
[3]) }
sub
_autodetect_signature_3 {
$_
[0]->_autodetect_signature(
$_
[1],
$_
[5],
$_
[2],
$_
[3],
$_
[4]) }
sub
_autodetect_signature {
my
(
$self
,
$opname
,
$bits
,
@ops
)=
@_
;
my
@signature
;
for
(
@ops
) {
my
$reg
;
push
@signature
,
looks_like_number(
$_
)?
'imm'
:
ref
$_
eq
'ARRAY'
?
'mem'
:
ref
$_
&&
ref
(
$_
)->can(
'value'
)?
'imm'
:
defined
(
$reg
=
$regnum
{
$_
})? (
$reg
& 0x1000?
'xreg'
:
'reg'
)
: croak
"Can't identify type of destination operand $_"
;
$bits
//= (
$reg
& 0xFF00) >> 5
if
defined
$reg
;
}
croak
"Can't determine bit-width of "
.
uc
(
$opname
).
" instruction. "
.
"Use ->$opname(..., \$bits) to clarify, when there is no register"
unless
defined
$bits
;
my
$method
=
join
'_'
,
$opname
.
$bits
,
@signature
;
(
$self
->can(
$method
)
//
$self
->can(
join
'_'
,
$opname
,
@signature
)
// croak
"No "
.
uc
(
$opname
).
" variant $method available"
)
->(
$self
,
@ops
);
}
sub
cpuid {
$_
[0]{_buf} .=
"\x0F\xA2"
;
}
sub
nop {
$_
[0]{_buf} .= (
defined
$_
[1]?
"\x90"
x
$_
[1] :
"\x90"
);
$_
[0];
}
sub
pause {
$_
[0]{_buf} .= (
defined
$_
[1]?
"\xF3\x90"
x
$_
[1] :
"\xF3\x90"
);
$_
[0]
}
sub
call_label {
@_
== 2 or croak
"Wrong arguments"
;
$_
[1]=
$_
[0]->get_label
unless
defined
$_
[1];
my
(
$self
,
$label
)=
@_
;
$label
=
$self
->get_label(
$label
)
unless
ref
$label
;
$self
->_mark_unresolved(
5,
encoder
=>
sub
{
my
(
$self
,
$params
)=
@_
;
defined
$params
->{target}{offset} or croak
"Label $params->{target} is not anchored"
;
my
$ofs
=
$params
->{target}{offset} - (
$params
->{offset}+
$params
->{len});
(
$ofs
>> 31) == (
$ofs
>> 31 >> 1) or croak
"Offset must be within 31 bits"
;
return
pack
(
'CV'
, 0xE8,
$ofs
);
},
target
=>
$label
);
$self
;
}
sub
call_rel {
my
(
$self
,
$immed
)=
@_
;
$self
->{_buf} .=
pack
(
'CV'
, 0xE8,
ref
$immed
? 0 :
$immed
);
$self
->_mark_unresolved(-4,
encoder
=>
'_repack'
,
bits
=> 32,
value
=>
$immed
)
if
ref
$immed
;
$self
;
}
sub
call_abs_reg {
$_
[0]->_append_op64_regnum_reg(0, 0xFF, 2,
$_
[1]) }
sub
call_abs_mem {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xFF, 2,
$_
[1]) }
sub
ret {
my
(
$self
,
$pop_bytes
)=
@_
;
if
(
$pop_bytes
) {
$self
->{_buf} .=
pack
(
'Cv'
, 0xC2,
ref
$pop_bytes
? 0 :
$pop_bytes
);
$self
->_mark_unresolved(-2,
encoder
=>
'_repack'
,
bits
=> 16,
value
=>
$pop_bytes
)
if
ref
$pop_bytes
;
}
else
{
$self
->{_buf} .=
"\xC3"
;
}
$self
;
}
sub
_encode_jmp {
my
(
$self
,
$params
)=
@_
;
defined
$params
->target->offset or croak
"Label "
.
$params
->target.
" is not anchored"
;
my
$ofs
=
$params
->target->offset - (
$params
->offset +
$params
->len);
my
$short
= ((
$ofs
>>7) == (
$ofs
>>8));
return
$short
?
$params
->{short_op} .
pack
(
'c'
,
$ofs
)
:
defined
$params
->{long_op}?
$params
->{long_op} .
pack
(
'V'
,
$ofs
)
: croak
"Jump to label $params->{target}{name} is too far, can only short-jump"
.Data::Dumper::Dumper(
$params
);
}
sub
_append_jmp {
@_
== 4 or croak
"Missing label on jmp instruction"
;
my
(
$self
,
$short_op
,
$long_op
,
$label
)=
@_
;
$_
[3]=
$self
->get_label
unless
defined
$_
[3];
$self
->_mark_unresolved(
2,
encoder
=> \
&_encode_jmp
,
short_op
=>
$short_op
,
long_op
=>
$long_op
,
target
=>
ref
$label
?
$label
:
$self
->get_label(
$label
),
);
}
sub
jmp {
@_
== 2 or croak
"Wrong arguments"
;
splice
@_
, 1, 0,
"\xEB"
,
"\xE9"
;
&_append_jmp
;
}
sub
jmp_abs_reg {
$_
[0]->_append_op64_regnum_reg(0, 0xFF, 4,
$_
[1]) }
sub
jmp_abs_mem {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xFF, 4,
$_
[1]) }
sub
_jmp_cond_ops {
my
$cond
=
shift
;
pack
(
'C'
, 0x70 |
$cond
),
pack
(
'CC'
, 0x0F, 0x80 |
$cond
);
}
sub
jmp_if_eq {
splice
@_
, 1, 0, _jmp_cond_ops(4);
&_append_jmp
}
*jz
=
*jmp_if_eq
;
*je
=
*jmp_if_eq
;
sub
jmp_if_ne {
splice
@_
, 1, 0, _jmp_cond_ops(5);
&_append_jmp
}
*jne
=
*jmp_if_ne
;
*jnz
=
*jmp_if_ne
;
sub
jmp_if_unsigned_lt {
splice
@_
, 1, 0, _jmp_cond_ops(2);
&_append_jmp
}
*jb
=
*jmp_if_unsigned_lt
;
*jc
=
*jmp_if_unsigned_lt
;
sub
jmp_if_unsigned_gt {
splice
@_
, 1, 0, _jmp_cond_ops(7);
&_append_jmp
}
*ja
=
*jmp_if_unsigned_gt
;
sub
jmp_if_unsigned_le {
splice
@_
, 1, 0, _jmp_cond_ops(6);
&_append_jmp
}
*jbe
=
*jmp_if_unsigned_le
;
sub
jmp_if_unsigned_ge {
splice
@_
, 1, 0, _jmp_cond_ops(3);
&_append_jmp
}
*jae
=
*jmp_if_unsigned_ge
;
*jnc
=
*jmp_if_unsigned_ge
;
sub
jmp_if_signed_lt {
splice
@_
, 1, 0, _jmp_cond_ops(12);
&_append_jmp
}
*jl
=
*jmp_if_signed_lt
;
sub
jmp_if_signed_gt {
splice
@_
, 1, 0, _jmp_cond_ops(15);
&_append_jmp
}
*jg
=
*jmp_if_signed_gt
;
sub
jmp_if_signed_le {
splice
@_
, 1, 0, _jmp_cond_ops(14);
&_append_jmp
}
*jle
=
*jmp_if_signed_le
;
sub
jmp_if_signed_ge {
splice
@_
, 1, 0, _jmp_cond_ops(13);
&_append_jmp
}
*jge
=
*jmp_if_signed_ge
;
sub
jmp_if_sign {
splice
@_
, 1, 0, _jmp_cond_ops(8);
&_append_jmp
}
*js
=
*jmp_if_sign
;
sub
jmp_unless_sign {
splice
@_
, 1, 0, _jmp_cond_ops(9);
&_append_jmp
}
*jns
=
*jmp_unless_sign
;
sub
jmp_if_overflow {
splice
@_
, 1, 0, _jmp_cond_ops(0);
&_append_jmp
}
*jo
=
*jmp_if_overflow
;
sub
jmp_unless_overflow {
splice
@_
, 1, 0, _jmp_cond_ops(1);
&_append_jmp
}
*jno
=
*jmp_unless_overflow
;
sub
jmp_if_parity_even {
splice
@_
, 1, 0, _jmp_cond_ops(10);
&_append_jmp
}
*jpe
=
*jmp_if_parity_even
;
*jp
=
*jmp_if_parity_even
;
sub
jmp_if_parity_odd {
splice
@_
, 1, 0, _jmp_cond_ops(11);
&_append_jmp
}
*jpo
=
*jmp_if_parity_odd
;
*jnp
=
*jmp_if_parity_odd
;
sub
jmp_cx_zero {
splice
@_
, 1, 0,
"\xE3"
,
undef
;
&_append_jmp
}
*jrcxz
=
*jmp_cx_zero
;
sub
loop {
splice
@_
, 1, 0,
"\xE2"
,
undef
;
&_append_jmp
}
sub
loopz {
splice
@_
, 1, 0,
"\xE1"
,
undef
;
&_append_jmp
}
*loope
=
*loopz
;
sub
loopnz {
splice
@_
, 1, 0,
"\xE0"
,
undef
;
&_append_jmp
}
*loopne
=
*loopnz
;
sub
mov {
splice
(
@_
,1,0,
'mov'
);
&_autodetect_signature_2
}
sub
mov64_reg_reg {
shift
->_append_op64_reg_reg(0x89,
$_
[1],
$_
[0]) }
sub
mov32_reg_reg {
shift
->_append_op32_reg_reg(0x89,
$_
[1],
$_
[0]) }
sub
mov16_reg_reg {
shift
->_append_op16_reg_reg(0x89,
$_
[1],
$_
[0]) }
sub
mov8_reg_reg {
shift
->_append_op8_reg_reg (0x89,
$_
[1],
$_
[0]) }
sub
mov64_mem_reg {
$_
[0]->_append_mov_reg_mem(
$_
[2],
$_
[1], 64, 0x89, 0xA3); }
sub
mov64_reg_mem {
$_
[0]->_append_mov_reg_mem(
$_
[1],
$_
[2], 64, 0x8B, 0xA1); }
sub
mov32_mem_reg {
$_
[0]->_append_mov_reg_mem(
$_
[2],
$_
[1], 32, 0x89, 0xA3); }
sub
mov32_reg_mem {
$_
[0]->_append_mov_reg_mem(
$_
[1],
$_
[2], 32, 0x8B, 0xA1); }
sub
mov16_mem_reg {
$_
[0]->_append_mov_reg_mem(
$_
[2],
$_
[1], 16, 0x89, 0xA3); }
sub
mov16_reg_mem {
$_
[0]->_append_mov_reg_mem(
$_
[1],
$_
[2], 16, 0x8B, 0xA1); }
sub
mov8_mem_reg {
$_
[0]->_append_mov_reg_mem(
$_
[2],
$_
[1], 8, 0x88, 0xA2); }
sub
mov8_reg_mem {
$_
[0]->_append_mov_reg_mem(
$_
[1],
$_
[2], 8, 0x8A, 0xA0); }
sub
_append_mov_reg_mem {
my
(
$self
,
$reg
,
$mem
,
$bits
,
$opcode
,
$ax_opcode
)=
@_
;
if
(!
defined
$mem
->[0] &&
$mem
->[1] && !
defined
$mem
->[2] && (
$mem
->[1] > 0x7FFFFFFF ||
ref
$mem
->[1])) {
my
$disp
=
$mem
->[1];
if
(
lc
(
$reg
) eq (
$bits
== 64?
'rax'
:
$bits
== 32?
'eax'
:
$bits
== 16?
'ax'
:
'al'
)) {
my
$opstr
=
chr
(
$ax_opcode
);
$opstr
=
"\x48"
.
$opstr
if
$bits
== 64;
$opstr
=
"\x66"
.
$opstr
if
$bits
== 16;
my
$val
= looks_like_number(
$disp
)?
$disp
:
$disp
->value;
if
(!
defined
$val
) {
$self
->_mark_unresolved(
10,
encoder
=>
sub
{
my
$v
=
$disp
->value;
defined
$v
or croak
"Placeholder $disp has not been assigned"
;
return
$v
> 0x7FFFFFFF?
$opstr
.
pack
(
'Q<'
,
$v
)
: (
$bits
== 16?
"\x66"
:
''
)
.
$_
[0]->_encode_op_reg_mem(
$bits
== 64? 8 : 0,
$opcode
, 0,
undef
,
$v
);
}
);
}
else
{
$self
->{_buf} .=
$opstr
.
pack
(
'Q<'
,
$val
);
}
return
$self
;
}
}
return
$self
->_append_op64_reg_mem(8,
$opcode
,
$reg
,
$mem
)
if
$bits
== 64;
return
$self
->_append_op32_reg_mem(0,
$opcode
,
$reg
,
$mem
)
if
$bits
== 32;
return
$self
->_append_op16_reg_mem(0,
$opcode
,
$reg
,
$mem
)
if
$bits
== 16;
return
$self
->_append_op8_reg_mem (0,
$opcode
,
$reg
,
$mem
)
if
$bits
== 8;
}
sub
mov64_reg_imm {
my
(
$self
,
$reg
,
$immed
)=
@_
;
$reg
=
$regnum64
{
$reg
} // croak(
"$reg is not a 64-bit register"
);
$self
->_append_possible_unknown(
'_encode_mov64_imm'
, [
$reg
,
$immed
], 1, 10);
}
sub
_encode_mov64_imm {
my
(
$self
,
$reg
,
$immed
)=
@_
;
if
((
$immed
>> 31 >> 1) == 0) {
return
(
$reg
&8)?
pack
(
'C C L<'
, 0x41, 0xB8 | (
$reg
&7),
$immed
)
:
pack
(
'C L<'
, 0xB8 | (
$reg
&7),
$immed
);
}
elsif
((
$immed
>> 31) == -1) {
return
pack
(
'C C C l<'
, 0x48 | ((
$reg
& 8) >> 3), 0xC7, 0xC0 | (
$reg
& 7),
$immed
);
}
else
{
return
pack
(
'C C Q<'
, 0x48 | ((
$reg
& 8) >> 3), 0xB8 | (
$reg
& 7),
$immed
);
}
}
sub
mov32_reg_imm {
my
(
$self
,
$reg
,
$immed
)=
@_
;
$reg
=
$regnum32
{
$reg
} // croak(
"$reg is not a 32-bit register"
);
$self
->{_buf} .=
"\x41"
if
$reg
& 8;
$self
->{_buf} .=
pack
(
'C'
, 0xB8 | (
$reg
& 7));
$self
->_append_possible_unknown(
sub
{
pack
(
'V'
,
$_
[1]) }, [
$immed
], 0, 4);
}
sub
mov16_reg_imm {
my
(
$self
,
$reg
,
$immed
)=
@_
;
$reg
=
$regnum16
{
$reg
} // croak(
"$reg is not a 16-bit register"
);
$self
->{_buf} .=
"\x66"
;
$self
->{_buf} .=
"\x41"
if
$reg
& 8;
$self
->{_buf} .=
pack
(
'C'
, 0xB8 | (
$reg
& 7));
$self
->_append_possible_unknown(
sub
{
pack
(
'v'
,
$_
[1]) }, [
$immed
], 0, 2);
}
sub
mov8_reg_imm {
my
(
$self
,
$reg
,
$immed
)=
@_
;
$reg
=
$regnum8
{
$reg
} // croak(
"$_[1] is not a 8-bit register"
);
if
(
$reg
& 0x20) {
$self
->{_buf} .=
pack
(
'C'
, 0x40|((
$reg
&8)>>3));
}
$self
->{_buf} .=
pack
(
'C'
, 0xB0 | (
$reg
& 7));
$self
->_append_possible_unknown(
sub
{
pack
(
'C'
,
$_
[1]&0xFF) }, [
$immed
], 0, 1);
}
sub
mov64_mem_imm {
$_
[0]->_append_op64_const_to_mem(0xC7, 0,
$_
[2],
$_
[1]) }
sub
mov32_mem_imm {
$_
[0]->_append_op32_const_to_mem(0xC7, 0,
$_
[2],
$_
[1]) }
sub
mov16_mem_imm {
$_
[0]->_append_op16_const_to_mem(0xC7, 0,
$_
[2],
$_
[1]) }
sub
mov8_mem_imm {
$_
[0]->_append_op8_const_to_mem (0xC6, 0,
$_
[2],
$_
[1]) }
sub
lea {
splice
(
@_
,1,0,
'lea'
);
&_autodetect_signature_2
}
sub
lea16_reg_reg {
$_
[0]->_append_op16_reg_reg( 0x8D,
$_
[1],
$_
[2]) }
sub
lea16_reg_mem {
$_
[0]->_append_op16_reg_mem(0, 0x8D,
$_
[1],
$_
[2]) }
sub
lea32_reg_reg {
$_
[0]->_append_op32_reg_reg( 0x8D,
$_
[1],
$_
[2]) }
sub
lea32_reg_mem {
$_
[0]->_append_op32_reg_mem(0, 0x8D,
$_
[1],
$_
[2]) }
sub
lea64_reg_reg {
$_
[0]->_append_op64_reg_reg( 0x8D,
$_
[1],
$_
[2]) }
sub
lea64_reg_mem {
$_
[0]->_append_op64_reg_mem(8, 0x8D,
$_
[1],
$_
[2]) }
sub
add {
splice
(
@_
,1,0,
'add'
);
&_autodetect_signature_2
}
sub
add64_reg_reg {
$_
[0]->_append_op64_reg_reg(0x01,
$_
[2],
$_
[1]) }
sub
add32_reg_reg {
$_
[0]->_append_op32_reg_reg(0x01,
$_
[2],
$_
[1]) }
sub
add16_reg_reg {
$_
[0]->_append_op16_reg_reg(0x01,
$_
[2],
$_
[1]) }
sub
add8_reg_reg {
$_
[0]->_append_op8_reg_reg (0x00,
$_
[2],
$_
[1]) }
sub
add64_reg_mem {
$_
[0]->_append_op64_reg_mem(8, 0x03,
$_
[1],
$_
[2]); }
sub
add32_reg_mem {
$_
[0]->_append_op32_reg_mem(0, 0x03,
$_
[1],
$_
[2]); }
sub
add16_reg_mem {
$_
[0]->_append_op16_reg_mem(0, 0x03,
$_
[1],
$_
[2]); }
sub
add8_reg_mem {
$_
[0]->_append_op8_reg_mem (0, 0x02,
$_
[1],
$_
[2]); }
sub
add64_mem_reg {
$_
[0]->_append_op64_reg_mem(8, 0x01,
$_
[2],
$_
[1]); }
sub
add32_mem_reg {
$_
[0]->_append_op32_reg_mem(0, 0x01,
$_
[2],
$_
[1]); }
sub
add16_mem_reg {
$_
[0]->_append_op16_reg_mem(0, 0x01,
$_
[2],
$_
[1]); }
sub
add8_mem_reg {
$_
[0]->_append_op8_reg_mem (0, 0x00,
$_
[2],
$_
[1]); }
sub
add64_reg_imm {
shift
->_append_mathop64_const(0x05, 0x83, 0x81, 0,
@_
) }
sub
add32_reg_imm {
shift
->_append_mathop32_const(0x05, 0x83, 0x81, 0,
@_
) }
sub
add16_reg_imm {
shift
->_append_mathop16_const(0x05, 0x83, 0x81, 0,
@_
) }
sub
add8_reg_imm {
shift
->_append_mathop8_const (0x04, 0x80, 0,
@_
) }
sub
add64_mem_imm {
$_
[0]->_append_mathop64_const_to_mem(0x83, 0x81, 0,
$_
[2],
$_
[1]) }
sub
add32_mem_imm {
$_
[0]->_append_mathop32_const_to_mem(0x83, 0x81, 0,
$_
[2],
$_
[1]) }
sub
add16_mem_imm {
$_
[0]->_append_mathop16_const_to_mem(0x83, 0x81, 0,
$_
[2],
$_
[1]) }
sub
add8_mem_imm {
$_
[0]->_append_mathop8_const_to_mem (0x80, 0,
$_
[2],
$_
[1]) }
sub
addcarry {
splice
(
@_
,1,0,
'addcarry'
);
&_autodetect_signature_2
}
*adc
=
*addcarry
;
sub
addcarry64_reg_reg {
$_
[0]->_append_op64_reg_reg(0x11,
$_
[2],
$_
[1]) }
sub
addcarry32_reg_reg {
$_
[0]->_append_op32_reg_reg(0x11,
$_
[2],
$_
[1]) }
sub
addcarry16_reg_reg {
$_
[0]->_append_op16_reg_reg(0x11,
$_
[2],
$_
[1]) }
sub
addcarry8_reg_reg {
$_
[0]->_append_op8_reg_reg (0x10,
$_
[2],
$_
[1]) }
sub
addcarry64_reg_mem {
$_
[0]->_append_op64_reg_mem(8, 0x13,
$_
[1],
$_
[2]); }
sub
addcarry32_reg_mem {
$_
[0]->_append_op32_reg_mem(0, 0x13,
$_
[1],
$_
[2]); }
sub
addcarry16_reg_mem {
$_
[0]->_append_op16_reg_mem(0, 0x13,
$_
[1],
$_
[2]); }
sub
addcarry8_reg_mem {
$_
[0]->_append_op8_reg_mem (0, 0x12,
$_
[1],
$_
[2]); }
sub
addcarry64_mem_reg {
$_
[0]->_append_op64_reg_mem(8, 0x11,
$_
[2],
$_
[1]); }
sub
addcarry32_mem_reg {
$_
[0]->_append_op32_reg_mem(0, 0x11,
$_
[2],
$_
[1]); }
sub
addcarry16_mem_reg {
$_
[0]->_append_op16_reg_mem(0, 0x11,
$_
[2],
$_
[1]); }
sub
addcarry8_mem_reg {
$_
[0]->_append_op8_reg_mem (0, 0x10,
$_
[2],
$_
[1]); }
sub
addcarry64_reg_imm {
shift
->_append_mathop64_const(0x15, 0x83, 0x81, 2,
@_
) }
sub
addcarry32_reg_imm {
shift
->_append_mathop32_const(0x15, 0x83, 0x81, 2,
@_
) }
sub
addcarry16_reg_imm {
shift
->_append_mathop16_const(0x15, 0x83, 0x81, 2,
@_
) }
sub
addcarry8_reg_imm {
shift
->_append_mathop8_const (0x14, 0x80, 2,
@_
) }
sub
addcarry64_mem_imm {
$_
[0]->_append_mathop64_const_to_mem(0x83, 0x81, 2,
$_
[2],
$_
[1]) }
sub
addcarry32_mem_imm {
$_
[0]->_append_mathop32_const_to_mem(0x83, 0x81, 2,
$_
[2],
$_
[1]) }
sub
addcarry16_mem_imm {
$_
[0]->_append_mathop16_const_to_mem(0x83, 0x81, 2,
$_
[2],
$_
[1]) }
sub
addcarry8_mem_imm {
$_
[0]->_append_mathop8_const_to_mem (0x80, 2,
$_
[2],
$_
[1]) }
sub
sub
{
splice
(
@_
,1,0,
'sub'
);
&_autodetect_signature_2
}
sub
sub64_reg_reg {
$_
[0]->_append_op64_reg_reg(0x29,
$_
[2],
$_
[1]) }
sub
sub32_reg_reg {
$_
[0]->_append_op32_reg_reg(0x29,
$_
[2],
$_
[1]) }
sub
sub16_reg_reg {
$_
[0]->_append_op16_reg_reg(0x29,
$_
[2],
$_
[1]) }
sub
sub8_reg_reg {
$_
[0]->_append_op8_reg_reg (0x28,
$_
[2],
$_
[1]) }
sub
sub64_reg_mem {
$_
[0]->_append_op64_reg_mem(8, 0x2B,
$_
[1],
$_
[2]); }
sub
sub32_reg_mem {
$_
[0]->_append_op32_reg_mem(0, 0x2B,
$_
[1],
$_
[2]); }
sub
sub16_reg_mem {
$_
[0]->_append_op16_reg_mem(0, 0x2B,
$_
[1],
$_
[2]); }
sub
sub8_reg_mem {
$_
[0]->_append_op8_reg_mem (0, 0x2A,
$_
[1],
$_
[2]); }
sub
sub64_mem_reg {
$_
[0]->_append_op64_reg_mem(8, 0x29,
$_
[2],
$_
[1]); }
sub
sub32_mem_reg {
$_
[0]->_append_op32_reg_mem(0, 0x29,
$_
[2],
$_
[1]); }
sub
sub16_mem_reg {
$_
[0]->_append_op16_reg_mem(0, 0x29,
$_
[2],
$_
[1]); }
sub
sub8_mem_reg {
$_
[0]->_append_op8_reg_mem (0, 0x28,
$_
[2],
$_
[1]); }
sub
sub64_reg_imm {
shift
->_append_mathop64_const(0x2D, 0x83, 0x81, 5,
@_
) }
sub
sub32_reg_imm {
shift
->_append_mathop32_const(0x2D, 0x83, 0x81, 5,
@_
) }
sub
sub16_reg_imm {
shift
->_append_mathop16_const(0x2D, 0x83, 0x81, 5,
@_
) }
sub
sub8_reg_imm {
shift
->_append_mathop8_const (0x2C, 0x80, 5,
@_
) }
sub
sub64_mem_imm {
$_
[0]->_append_mathop64_const_to_mem(0x83, 0x81, 5,
$_
[2],
$_
[1]) }
sub
sub32_mem_imm {
$_
[0]->_append_mathop32_const_to_mem(0x83, 0x81, 5,
$_
[2],
$_
[1]) }
sub
sub16_mem_imm {
$_
[0]->_append_mathop16_const_to_mem(0x83, 0x81, 5,
$_
[2],
$_
[1]) }
sub
sub8_mem_imm {
$_
[0]->_append_mathop8_const_to_mem (0x80, 5,
$_
[2],
$_
[1]) }
sub
and {
splice
(
@_
,1,0,
'and'
);
&_autodetect_signature_2
}
sub
and64_reg_reg {
$_
[0]->_append_op64_reg_reg(0x21,
$_
[2],
$_
[1]) }
sub
and32_reg_reg {
$_
[0]->_append_op32_reg_reg(0x21,
$_
[2],
$_
[1]) }
sub
and16_reg_reg {
$_
[0]->_append_op16_reg_reg(0x21,
$_
[2],
$_
[1]) }
sub
and8_reg_reg {
$_
[0]->_append_op8_reg_reg (0x20,
$_
[2],
$_
[1]) }
sub
and64_reg_mem {
$_
[0]->_append_op64_reg_mem(8, 0x23,
$_
[1],
$_
[2]); }
sub
and32_reg_mem {
$_
[0]->_append_op32_reg_mem(0, 0x23,
$_
[1],
$_
[2]); }
sub
and16_reg_mem {
$_
[0]->_append_op16_reg_mem(0, 0x23,
$_
[1],
$_
[2]); }
sub
and8_reg_mem {
$_
[0]->_append_op8_reg_mem (0, 0x22,
$_
[1],
$_
[2]); }
sub
and64_mem_reg {
$_
[0]->_append_op64_reg_mem(8, 0x21,
$_
[2],
$_
[1]); }
sub
and32_mem_reg {
$_
[0]->_append_op32_reg_mem(0, 0x21,
$_
[2],
$_
[1]); }
sub
and16_mem_reg {
$_
[0]->_append_op16_reg_mem(0, 0x21,
$_
[2],
$_
[1]); }
sub
and8_mem_reg {
$_
[0]->_append_op8_reg_mem (0, 0x20,
$_
[2],
$_
[1]); }
sub
and64_reg_imm {
shift
->_append_mathop64_const(0x25, 0x83, 0x81, 4,
@_
) }
sub
and32_reg_imm {
shift
->_append_mathop32_const(0x25, 0x83, 0x81, 4,
@_
) }
sub
and16_reg_imm {
shift
->_append_mathop16_const(0x25, 0x83, 0x81, 4,
@_
) }
sub
and8_reg_imm {
shift
->_append_mathop8_const (0x24, 0x80, 4,
@_
) }
sub
and64_mem_imm {
$_
[0]->_append_mathop64_const_to_mem(0x83, 0x81, 4,
$_
[2],
$_
[1]) }
sub
and32_mem_imm {
$_
[0]->_append_mathop32_const_to_mem(0x83, 0x81, 4,
$_
[2],
$_
[1]) }
sub
and16_mem_imm {
$_
[0]->_append_mathop16_const_to_mem(0x83, 0x81, 4,
$_
[2],
$_
[1]) }
sub
and8_mem_imm {
$_
[0]->_append_mathop8_const_to_mem (0x80, 4,
$_
[2],
$_
[1]) }
sub
or {
splice
(
@_
,1,0,
'or'
);
&_autodetect_signature_2
}
sub
or64_reg_reg {
$_
[0]->_append_op64_reg_reg(0x09,
$_
[2],
$_
[1]) }
sub
or32_reg_reg {
$_
[0]->_append_op32_reg_reg(0x09,
$_
[2],
$_
[1]) }
sub
or16_reg_reg {
$_
[0]->_append_op16_reg_reg(0x09,
$_
[2],
$_
[1]) }
sub
or8_reg_reg {
$_
[0]->_append_op8_reg_reg (0x08,
$_
[2],
$_
[1]) }
sub
or64_reg_mem {
$_
[0]->_append_op64_reg_mem(8, 0x0B,
$_
[1],
$_
[2]); }
sub
or32_reg_mem {
$_
[0]->_append_op32_reg_mem(0, 0x0B,
$_
[1],
$_
[2]); }
sub
or16_reg_mem {
$_
[0]->_append_op16_reg_mem(0, 0x0B,
$_
[1],
$_
[2]); }
sub
or8_reg_mem {
$_
[0]->_append_op8_reg_mem (0, 0x0A,
$_
[1],
$_
[2]); }
sub
or64_mem_reg {
$_
[0]->_append_op64_reg_mem(8, 0x09,
$_
[2],
$_
[1]); }
sub
or32_mem_reg {
$_
[0]->_append_op32_reg_mem(0, 0x09,
$_
[2],
$_
[1]); }
sub
or16_mem_reg {
$_
[0]->_append_op16_reg_mem(0, 0x09,
$_
[2],
$_
[1]); }
sub
or8_mem_reg {
$_
[0]->_append_op8_reg_mem (0, 0x08,
$_
[2],
$_
[1]); }
sub
or64_reg_imm {
shift
->_append_mathop64_const(0x0D, 0x83, 0x81, 1,
@_
) }
sub
or32_reg_imm {
shift
->_append_mathop32_const(0x0D, 0x83, 0x81, 1,
@_
) }
sub
or16_reg_imm {
shift
->_append_mathop16_const(0x0D, 0x83, 0x81, 1,
@_
) }
sub
or8_reg_imm {
shift
->_append_mathop8_const (0x0C, 0x80, 1,
@_
) }
sub
or64_mem_imm {
$_
[0]->_append_mathop64_const_to_mem(0x83, 0x81, 1,
$_
[2],
$_
[1]) }
sub
or32_mem_imm {
$_
[0]->_append_mathop32_const_to_mem(0x83, 0x81, 1,
$_
[2],
$_
[1]) }
sub
or16_mem_imm {
$_
[0]->_append_mathop16_const_to_mem(0x83, 0x81, 1,
$_
[2],
$_
[1]) }
sub
or8_mem_imm {
$_
[0]->_append_mathop8_const_to_mem (0x80, 1,
$_
[2],
$_
[1]) }
sub
xor {
splice
(
@_
,1,0,
'xor'
);
&_autodetect_signature_2
}
sub
xor64_reg_reg {
$_
[0]->_append_op64_reg_reg(0x31,
$_
[2],
$_
[1]) }
sub
xor32_reg_reg {
$_
[0]->_append_op32_reg_reg(0x31,
$_
[2],
$_
[1]) }
sub
xor16_reg_reg {
$_
[0]->_append_op16_reg_reg(0x31,
$_
[2],
$_
[1]) }
sub
xor8_reg_reg {
$_
[0]->_append_op8_reg_reg (0x30,
$_
[2],
$_
[1]) }
sub
xor64_reg_mem {
$_
[0]->_append_op64_reg_mem(8, 0x33,
$_
[1],
$_
[2]); }
sub
xor32_reg_mem {
$_
[0]->_append_op32_reg_mem(0, 0x33,
$_
[1],
$_
[2]); }
sub
xor16_reg_mem {
$_
[0]->_append_op16_reg_mem(0, 0x33,
$_
[1],
$_
[2]); }
sub
xor8_reg_mem {
$_
[0]->_append_op8_reg_mem (0, 0x32,
$_
[1],
$_
[2]); }
sub
xor64_mem_reg {
$_
[0]->_append_op64_reg_mem(8, 0x31,
$_
[2],
$_
[1]); }
sub
xor32_mem_reg {
$_
[0]->_append_op32_reg_mem(0, 0x31,
$_
[2],
$_
[1]); }
sub
xor16_mem_reg {
$_
[0]->_append_op16_reg_mem(0, 0x31,
$_
[2],
$_
[1]); }
sub
xor8_mem_reg {
$_
[0]->_append_op8_reg_mem (0, 0x30,
$_
[2],
$_
[1]); }
sub
xor64_reg_imm {
shift
->_append_mathop64_const(0x35, 0x83, 0x81, 6,
@_
) }
sub
xor32_reg_imm {
shift
->_append_mathop32_const(0x35, 0x83, 0x81, 6,
@_
) }
sub
xor16_reg_imm {
shift
->_append_mathop16_const(0x35, 0x83, 0x81, 6,
@_
) }
sub
xor8_reg_imm {
shift
->_append_mathop8_const (0x34, 0x80, 6,
@_
) }
sub
xor64_mem_imm {
$_
[0]->_append_mathop64_const_to_mem(0x83, 0x81, 6,
$_
[2],
$_
[1]) }
sub
xor32_mem_imm {
$_
[0]->_append_mathop32_const_to_mem(0x83, 0x81, 6,
$_
[2],
$_
[1]) }
sub
xor16_mem_imm {
$_
[0]->_append_mathop16_const_to_mem(0x83, 0x81, 6,
$_
[2],
$_
[1]) }
sub
xor8_mem_imm {
$_
[0]->_append_mathop8_const_to_mem (0x80, 6,
$_
[2],
$_
[1]) }
sub
_append_shiftop_reg_imm {
my
(
$self
,
$bits
,
$opcode_sh1
,
$opcode_imm
,
$opreg
,
$reg
,
$immed
)=
@_
;
my
$op
=
$immed
eq 1?
$opcode_sh1
:
$opcode_imm
;
$bits
== 64?
$self
->_append_op64_regnum_reg(8,
$op
,
$opreg
,
$reg
)
:
$bits
== 32?
$self
->_append_op32_regnum_reg(
$op
,
$opreg
,
$reg
)
:
$bits
== 16?
$self
->_append_op16_regnum_reg(
$op
,
$opreg
,
$reg
)
:
$self
->_append_op8_regnum_reg(
$op
,
$opreg
,
$reg
);
unless
(
$immed
eq 1) {
$self
->{_buf} .=
pack
(
'C'
,
ref
$immed
? 0 :
$immed
);
$self
->_mark_unresolved(-1,
encoder
=>
'_repack'
,
bits
=> 8,
value
=>
$immed
)
if
ref
$immed
;
}
$self
;
}
sub
_append_shiftop_mem_imm {
my
(
$self
,
$bits
,
$opcode_sh1
,
$opcode_imm
,
$opreg
,
$mem
,
$immed
)=
@_
;
my
$op
=
$immed
eq 1?
$opcode_sh1
:
$opcode_imm
;
$self
->_append_op_reg_mem(
$bits
== 16?
"\x66"
:
undef
,
$bits
== 64? 8 : 0,
$op
,
$opreg
,
$mem
);
unless
(
$immed
eq 1) {
$self
->{_buf} .=
pack
(
'C'
,
ref
$immed
? 0 :
$immed
);
$self
->_mark_unresolved(-1,
encoder
=>
'_repack'
,
bits
=> 8,
value
=>
$immed
)
if
ref
$immed
;
}
$self
;
}
sub
shl {
splice
(
@_
,1,0,
'shl'
);
&_autodetect_signature_2
}
sub
shl64_reg_imm {
$_
[0]->_append_shiftop_reg_imm(64, 0xD1, 0xC1, 4,
$_
[1],
$_
[2]) }
sub
shl32_reg_imm {
$_
[0]->_append_shiftop_reg_imm(32, 0xD1, 0xC1, 4,
$_
[1],
$_
[2]) }
sub
shl16_reg_imm {
$_
[0]->_append_shiftop_reg_imm(16, 0xD1, 0xC1, 4,
$_
[1],
$_
[2]) }
sub
shl8_reg_imm {
$_
[0]->_append_shiftop_reg_imm( 8, 0xD0, 0xC0, 4,
$_
[1],
$_
[2]) }
sub
shl64_mem_imm {
$_
[0]->_append_shiftop_mem_imm(64, 0xD1, 0xC1, 4,
$_
[1],
$_
[2]) }
sub
shl32_mem_imm {
$_
[0]->_append_shiftop_mem_imm(32, 0xD1, 0xC1, 4,
$_
[1],
$_
[2]) }
sub
shl16_mem_imm {
$_
[0]->_append_shiftop_mem_imm(16, 0xD1, 0xC1, 4,
$_
[1],
$_
[2]) }
sub
shl8_mem_imm {
$_
[0]->_append_shiftop_mem_imm( 8, 0xD0, 0xC0, 4,
$_
[1],
$_
[2]) }
sub
shl64_reg_cl {
$_
[0]->_append_op64_regnum_reg(8, 0xD3, 4,
$_
[1]) }
sub
shl32_reg_cl {
$_
[0]->_append_op32_regnum_reg(0xD3, 4,
$_
[1]) }
sub
shl16_reg_cl {
$_
[0]->_append_op16_regnum_reg(0xD3, 4,
$_
[1]) }
sub
shl8_reg_cl {
$_
[0]->_append_op8_regnum_reg (0xD2, 4,
$_
[1]) }
sub
shl64_mem_cl {
$_
[0]->_append_op_reg_mem(
undef
, 8, 0xD3, 4,
$_
[1]) }
sub
shl32_mem_cl {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xD3, 4,
$_
[1]) }
sub
shl16_mem_cl {
$_
[0]->_append_op_reg_mem(
"\x66"
,0, 0xD3, 4,
$_
[1]) }
sub
shl8_mem_cl {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xD2, 4,
$_
[1]) }
sub
shr {
splice
(
@_
,1,0,
'shr'
);
&_autodetect_signature_2
}
sub
shr64_reg_imm {
$_
[0]->_append_shiftop_reg_imm(64, 0xD1, 0xC1, 5,
$_
[1],
$_
[2]) }
sub
shr32_reg_imm {
$_
[0]->_append_shiftop_reg_imm(32, 0xD1, 0xC1, 5,
$_
[1],
$_
[2]) }
sub
shr16_reg_imm {
$_
[0]->_append_shiftop_reg_imm(16, 0xD1, 0xC1, 5,
$_
[1],
$_
[2]) }
sub
shr8_reg_imm {
$_
[0]->_append_shiftop_reg_imm( 8, 0xD0, 0xC0, 5,
$_
[1],
$_
[2]) }
sub
shr64_reg_cl {
$_
[0]->_append_op64_regnum_reg(8, 0xD3, 5,
$_
[1]) }
sub
shr32_reg_cl {
$_
[0]->_append_op32_regnum_reg(0xD3, 5,
$_
[1]) }
sub
shr16_reg_cl {
$_
[0]->_append_op16_regnum_reg(0xD3, 5,
$_
[1]) }
sub
shr8_reg_cl {
$_
[0]->_append_op8_regnum_reg (0xD2, 5,
$_
[1]) }
sub
shr64_mem_imm {
$_
[0]->_append_shiftop_mem_imm(64, 0xD1, 0xC1, 5,
$_
[1],
$_
[2]) }
sub
shr32_mem_imm {
$_
[0]->_append_shiftop_mem_imm(32, 0xD1, 0xC1, 5,
$_
[1],
$_
[2]) }
sub
shr16_mem_imm {
$_
[0]->_append_shiftop_mem_imm(16, 0xD1, 0xC1, 5,
$_
[1],
$_
[2]) }
sub
shr8_mem_imm {
$_
[0]->_append_shiftop_mem_imm( 8, 0xD0, 0xC0, 5,
$_
[1],
$_
[2]) }
sub
shr64_mem_cl {
$_
[0]->_append_op_reg_mem(
undef
, 8, 0xD3, 5,
$_
[1]) }
sub
shr32_mem_cl {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xD3, 5,
$_
[1]) }
sub
shr16_mem_cl {
$_
[0]->_append_op_reg_mem(
"\x66"
,0, 0xD3, 5,
$_
[1]) }
sub
shr8_mem_cl {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xD2, 5,
$_
[1]) }
sub
sar {
splice
(
@_
,1,0,
'sar'
);
&_autodetect_signature_2
}
sub
sar64_reg_imm {
$_
[0]->_append_shiftop_reg_imm(64, 0xD1, 0xC1, 7,
$_
[1],
$_
[2]) }
sub
sar32_reg_imm {
$_
[0]->_append_shiftop_reg_imm(32, 0xD1, 0xC1, 7,
$_
[1],
$_
[2]) }
sub
sar16_reg_imm {
$_
[0]->_append_shiftop_reg_imm(16, 0xD1, 0xC1, 7,
$_
[1],
$_
[2]) }
sub
sar8_reg_imm {
$_
[0]->_append_shiftop_reg_imm( 8, 0xD0, 0xC0, 7,
$_
[1],
$_
[2]) }
sub
sar64_reg_cl {
$_
[0]->_append_op64_regnum_reg(8, 0xD3, 7,
$_
[1]) }
sub
sar32_reg_cl {
$_
[0]->_append_op32_regnum_reg(0xD3, 7,
$_
[1]) }
sub
sar16_reg_cl {
$_
[0]->_append_op16_regnum_reg(0xD3, 7,
$_
[1]) }
sub
sar8_reg_cl {
$_
[0]->_append_op8_regnum_reg (0xD2, 7,
$_
[1]) }
sub
sar64_mem_imm {
$_
[0]->_append_shiftop_mem_imm(64, 0xD1, 0xC1, 7,
$_
[1],
$_
[2]) }
sub
sar32_mem_imm {
$_
[0]->_append_shiftop_mem_imm(32, 0xD1, 0xC1, 7,
$_
[1],
$_
[2]) }
sub
sar16_mem_imm {
$_
[0]->_append_shiftop_mem_imm(16, 0xD1, 0xC1, 7,
$_
[1],
$_
[2]) }
sub
sar8_mem_imm {
$_
[0]->_append_shiftop_mem_imm( 8, 0xD0, 0xC0, 7,
$_
[1],
$_
[2]) }
sub
sar64_mem_cl {
$_
[0]->_append_op_reg_mem(
undef
, 8, 0xD3, 7,
$_
[1]) }
sub
sar32_mem_cl {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xD3, 7,
$_
[1]) }
sub
sar16_mem_cl {
$_
[0]->_append_op_reg_mem(
"\x66"
,0, 0xD3, 7,
$_
[1]) }
sub
sar8_mem_cl {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xD2, 7,
$_
[1]) }
sub
cmp {
splice
(
@_
,1,0,
'cmp'
);
&_autodetect_signature_2
}
sub
cmp64_reg_reg {
$_
[0]->_append_op64_reg_reg(0x39,
$_
[2],
$_
[1]) }
sub
cmp32_reg_reg {
$_
[0]->_append_op32_reg_reg(0x39,
$_
[2],
$_
[1]) }
sub
cmp16_reg_reg {
$_
[0]->_append_op16_reg_reg(0x39,
$_
[2],
$_
[1]) }
sub
cmp8_reg_reg {
$_
[0]->_append_op8_reg_reg (0x38,
$_
[2],
$_
[1]) }
sub
cmp64_reg_mem {
$_
[0]->_append_op64_reg_mem(8, 0x3B,
$_
[1],
$_
[2]); }
sub
cmp32_reg_mem {
$_
[0]->_append_op32_reg_mem(0, 0x3B,
$_
[1],
$_
[2]); }
sub
cmp16_reg_mem {
$_
[0]->_append_op16_reg_mem(0, 0x3B,
$_
[1],
$_
[2]); }
sub
cmp8_reg_mem {
$_
[0]->_append_op8_reg_mem (0, 0x3A,
$_
[1],
$_
[2]); }
sub
cmp64_mem_reg {
$_
[0]->_append_op64_reg_mem(8, 0x39,
$_
[2],
$_
[1]); }
sub
cmp32_mem_reg {
$_
[0]->_append_op32_reg_mem(0, 0x39,
$_
[2],
$_
[1]); }
sub
cmp16_mem_reg {
$_
[0]->_append_op16_reg_mem(0, 0x39,
$_
[2],
$_
[1]); }
sub
cmp8_mem_reg {
$_
[0]->_append_op8_reg_mem (0, 0x38,
$_
[2],
$_
[1]); }
sub
cmp64_reg_imm {
shift
->_append_mathop64_const(0x3D, 0x83, 0x81, 7,
@_
) }
sub
cmp32_reg_imm {
shift
->_append_mathop32_const(0x3D, 0x83, 0x81, 7,
@_
) }
sub
cmp16_reg_imm {
shift
->_append_mathop16_const(0x3D, 0x83, 0x81, 7,
@_
) }
sub
cmp8_reg_imm {
shift
->_append_mathop8_const (0x3C, 0x80, 7,
@_
) }
sub
cmp64_mem_imm {
$_
[0]->_append_mathop64_const_to_mem(0x83, 0x81, 7,
$_
[2],
$_
[1]) }
sub
cmp32_mem_imm {
$_
[0]->_append_mathop32_const_to_mem(0x83, 0x81, 7,
$_
[2],
$_
[1]) }
sub
cmp16_mem_imm {
$_
[0]->_append_mathop16_const_to_mem(0x83, 0x81, 7,
$_
[2],
$_
[1]) }
sub
cmp8_mem_imm {
$_
[0]->_append_mathop8_const_to_mem (0x80, 7,
$_
[2],
$_
[1]) }
sub
test {
splice
(
@_
,1,0,
'test'
);
&_autodetect_signature_2
}
sub
test64_reg_reg {
$_
[0]->_append_op64_reg_reg(0x85,
$_
[2],
$_
[1]) }
sub
test32_reg_reg {
$_
[0]->_append_op32_reg_reg(0x85,
$_
[2],
$_
[1]) }
sub
test16_reg_reg {
$_
[0]->_append_op16_reg_reg(0x85,
$_
[2],
$_
[1]) }
sub
test8_reg_reg {
$_
[0]->_append_op8_reg_reg (0x84,
$_
[2],
$_
[1]) }
sub
test64_reg_mem {
$_
[0]->_append_op64_reg_mem(8, 0x85,
$_
[1],
$_
[2]); }
sub
test32_reg_mem {
$_
[0]->_append_op32_reg_mem(0, 0x85,
$_
[1],
$_
[2]); }
sub
test16_reg_mem {
$_
[0]->_append_op16_reg_mem(0, 0x85,
$_
[1],
$_
[2]); }
sub
test8_reg_mem {
$_
[0]->_append_op8_reg_mem (0, 0x84,
$_
[1],
$_
[2]); }
sub
test64_reg_imm {
$_
[0]->_append_mathop64_const(0xA9,
undef
, 0xF7, 0,
$_
[1],
$_
[2]) }
sub
test32_reg_imm {
$_
[0]->_append_mathop32_const(0xA9,
undef
, 0xF7, 0,
$_
[1],
$_
[2]) }
sub
test16_reg_imm {
$_
[0]->_append_mathop16_const(0xA9,
undef
, 0xF7, 0,
$_
[1],
$_
[2]) }
sub
test8_reg_imm {
$_
[0]->_append_mathop8_const (0xA8, 0xF6, 0,
$_
[1],
$_
[2]) }
sub
test64_mem_imm {
$_
[0]->_append_mathop64_const_to_mem(
undef
, 0xF7, 0,
$_
[2],
$_
[1]) }
sub
test32_mem_imm {
$_
[0]->_append_mathop32_const_to_mem(
undef
, 0xF7, 0,
$_
[2],
$_
[1]) }
sub
test16_mem_imm {
$_
[0]->_append_mathop16_const_to_mem(
undef
, 0xF7, 0,
$_
[2],
$_
[1]) }
sub
test8_mem_imm {
$_
[0]->_append_mathop8_const_to_mem (0xF6, 0,
$_
[2],
$_
[1]) }
sub
dec {
splice
(
@_
,1,0,
'dec'
);
&_autodetect_signature_1
; }
sub
dec64_reg {
$_
[0]->_append_op64_regnum_reg(8, 0xFF, 1,
$_
[1]) }
sub
dec32_reg {
$_
[0]->_append_op32_regnum_reg(0xFF, 1,
$_
[1]) }
sub
dec16_reg {
$_
[0]->_append_op16_regnum_reg(0xFF, 1,
$_
[1]) }
sub
dec8_reg {
$_
[0]->_append_op8_regnum_reg (0xFE, 1,
$_
[1]) }
sub
dec64_mem {
$_
[0]->_append_op_reg_mem(
undef
, 8, 0xFF, 1,
$_
[1]) }
sub
dec32_mem {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xFF, 1,
$_
[1]) }
sub
dec16_mem {
$_
[0]->_append_op_reg_mem(
"\x66"
,0, 0xFF, 1,
$_
[1]) }
sub
dec8_mem {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xFE, 1,
$_
[1]) }
sub
inc {
splice
(
@_
,1,0,
'inc'
);
&_autodetect_signature_1
; }
sub
inc64_reg {
$_
[0]->_append_op64_regnum_reg(8, 0xFF, 0,
$_
[1]) }
sub
inc32_reg {
$_
[0]->_append_op32_regnum_reg(0xFF, 0,
$_
[1]) }
sub
inc16_reg {
$_
[0]->_append_op16_regnum_reg(0xFF, 0,
$_
[1]) }
sub
inc8_reg {
$_
[0]->_append_op8_regnum_reg (0xFE, 0,
$_
[1]) }
sub
inc64_mem {
$_
[0]->_append_op_reg_mem(
undef
, 8, 0xFF, 0,
$_
[1]) }
sub
inc32_mem {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xFF, 0,
$_
[1]) }
sub
inc16_mem {
$_
[0]->_append_op_reg_mem(
"\x66"
,0, 0xFF, 0,
$_
[1]) }
sub
inc8_mem {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xFE, 0,
$_
[1]) }
sub
not {
splice
(
@_
,1,0,
'not'
);
&_autodetect_signature_1
; }
sub
not64_reg {
$_
[0]->_append_op64_regnum_reg(8, 0xF7, 2,
$_
[1]) }
sub
not32_reg {
$_
[0]->_append_op32_regnum_reg(0xF7, 2,
$_
[1]) }
sub
not16_reg {
$_
[0]->_append_op16_regnum_reg(0xF7, 2,
$_
[1]) }
sub
not8_reg {
$_
[0]->_append_op8_regnum_reg (0xF6, 2,
$_
[1]) }
sub
not64_mem {
$_
[0]->_append_op_reg_mem(
undef
, 8, 0xF7, 2,
$_
[1]) }
sub
not32_mem {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xF7, 2,
$_
[1]) }
sub
not16_mem {
$_
[0]->_append_op_reg_mem(
"\x66"
,0, 0xF7, 2,
$_
[1]) }
sub
not8_mem {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xF6, 2,
$_
[1]) }
sub
neg {
splice
(
@_
,1,0,
'neg'
);
&_autodetect_signature_1
; }
sub
neg64_reg {
$_
[0]->_append_op64_regnum_reg(8,0xF7, 3,
$_
[1]) }
sub
neg32_reg {
$_
[0]->_append_op32_regnum_reg(0xF7, 3,
$_
[1]) }
sub
neg16_reg {
$_
[0]->_append_op16_regnum_reg(0xF7, 3,
$_
[1]) }
sub
neg8_reg {
$_
[0]->_append_op8_regnum_reg (0xF6, 3,
$_
[1]) }
sub
neg64_mem {
$_
[0]->_append_op_reg_mem(
undef
, 8, 0xF7, 3,
$_
[1]) }
sub
neg32_mem {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xF7, 3,
$_
[1]) }
sub
neg16_mem {
$_
[0]->_append_op_reg_mem(
"\x66"
,0, 0xF7, 3,
$_
[1]) }
sub
neg8_mem {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xF6, 3,
$_
[1]) }
sub
div {
splice
(
@_
,1,0,
'div'
);
&_autodetect_signature_1
; }
sub
idiv {
splice
(
@_
,1,0,
'idiv'
);
&_autodetect_signature_1
; }
sub
div64_reg {
$_
[0]->_append_op64_regnum_reg(8, 0xF7, 6,
$_
[1]) }
sub
div32_reg {
$_
[0]->_append_op32_regnum_reg(0xF7, 6,
$_
[1]) }
sub
div16_reg {
$_
[0]->_append_op16_regnum_reg(0xF7, 6,
$_
[1]) }
sub
div8_reg {
$_
[0]->_append_op8_regnum_reg (0xF6, 6,
$_
[1]) }
sub
div64_mem {
$_
[0]->_append_op_reg_mem(
undef
, 8, 0xF7, 6,
$_
[1]) }
sub
div32_mem {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xF7, 6,
$_
[1]) }
sub
div16_mem {
$_
[0]->_append_op_reg_mem(
"\x66"
,0, 0xF7, 6,
$_
[1]) }
sub
div8_mem {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xF6, 6,
$_
[1]) }
sub
idiv64_reg {
$_
[0]->_append_op64_regnum_reg(8,0xF7, 7,
$_
[1]) }
sub
idiv32_reg {
$_
[0]->_append_op32_regnum_reg(0xF7, 7,
$_
[1]) }
sub
idiv16_reg {
$_
[0]->_append_op16_regnum_reg(0xF7, 7,
$_
[1]) }
sub
idiv8_reg {
$_
[0]->_append_op8_regnum_reg (0xF6, 7,
$_
[1]) }
sub
idiv64_mem {
$_
[0]->_append_op_reg_mem(
undef
, 8, 0xF7, 7,
$_
[1]) }
sub
idiv32_mem {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xF7, 7,
$_
[1]) }
sub
idiv16_mem {
$_
[0]->_append_op_reg_mem(
"\x66"
,0, 0xF7, 7,
$_
[1]) }
sub
idiv8_mem {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0xF6, 7,
$_
[1]) }
sub
mul64_dxax_reg {
shift
->_append_op64_regnum_reg(8, 0xF7, 5,
@_
) }
sub
mul32_dxax_reg {
shift
->_append_op32_regnum_reg(0, 0xF7, 5,
@_
) }
sub
mul16_dxax_reg {
shift
->_append_op16_regnum_reg(0, 0xF7, 5,
@_
) }
sub
mul8_ax_reg {
shift
->_append_op8_regnum_reg (0, 0xF6, 5,
@_
) }
sub
sign_extend_al_ax {
$_
[0]{_buf} .=
"\x66\x98"
;
$_
[0] }
*cbw
=
*sign_extend_al_ax
;
sub
sign_extend_ax_eax {
$_
[0]{_buf} .=
"\x98"
;
$_
[0] }
*cwde
=
*sign_extend_ax_eax
;
sub
sign_extend_eax_rax {
$_
[0]{_buf} .=
"\x48\x98"
;
$_
[0] }
*cdqe
=
*sign_extend_eax_rax
;
sub
sign_extend_ax_dx {
$_
[0]{_buf} .=
"\x66\x99"
;
$_
[0] }
*cwd
=
*sign_extend_ax_dx
;
sub
sign_extend_eax_edx {
$_
[0]{_buf} .=
"\x99"
;
$_
[0] }
*cdq
=
*sign_extend_eax_edx
;
sub
sign_extend_rax_rdx {
$_
[0]{_buf} .=
"\x48\x99"
;
$_
[0] }
*cqo
=
*sign_extend_rax_rdx
;
my
@_carry_flag_op
= (
"\xF5"
,
"\xF8"
,
"\xF9"
);
sub
flag_carry {
$_
[0]{_buf} .=
$_carry_flag_op
[
$_
[1] + 1];
$_
[0] }
sub
clc {
$_
[0]{_buf} .=
"\xF8"
;
$_
[0] }
sub
cmc {
$_
[0]{_buf} .=
"\xF5"
;
$_
[0] }
sub
stc {
$_
[0]{_buf} .=
"\xF9"
;
$_
[0] }
INIT {
eval
q|sub push { splice(@_,1,0,'push' ); &_autodetect_signature_1; }|
};
sub
push64_reg {
my
(
$self
,
$reg
)=
@_
;
$reg
= (
$regnum64
{
$reg
} // croak(
"$reg is not a 64-bit register"
));
$self
->{_buf} .= (
$reg
&8)?
pack
(
'CC'
, 0x41, 0x50+(
$reg
&7)) :
pack
(
'C'
, 0x50+(
$reg
&7));
$self
;
}
sub
push64_imm {
my
(
$self
,
$imm
)=
@_
;
my
$val
=
ref
$imm
? 0x7FFFFFFF :
$imm
;
$self
->{_buf} .= ((
$val
>> 7) == (
$val
>> 8))?
pack
(
'Cc'
, 0x6A,
$val
) :
pack
(
'CV'
, 0x68,
$val
);
$self
->_mark_unresolved(-4,
encoder
=>
'_repack'
,
bits
=> 32,
value
=>
$imm
)
if
ref
$imm
;
$self
;
}
sub
push64_mem {
shift
->_append_op_reg_mem(
undef
, 0, 0xFF, 6,
shift
) }
INIT {
eval
q|sub pop { splice(@_,1,0,'pop' ); &_autodetect_signature_1; }|
};
sub
pop64_reg {
my
(
$self
,
$reg
)=
@_
;
$reg
= (
$regnum64
{
$reg
} // croak(
"$reg is not a 64-bit register"
));
$self
->{_buf} .= (
$reg
&8)?
pack
(
'CC'
, 0x41, 0x58+(
$reg
&7)) :
pack
(
'C'
, 0x58+(
$reg
&7));
$self
;
}
sub
pop64_mem {
$_
[0]->_append_op_reg_mem(
undef
, 0, 0x8F, 0,
$_
[1]) }
sub
enter {
my
(
$self
,
$varspace
,
$nesting
)=
@_
;
$nesting
//= 0;
if
(!
ref
$varspace
&& !
ref
$nesting
) {
$self
->{_buf} .=
pack
(
'CvC'
, 0xC8,
$varspace
,
$nesting
);
}
else
{
$self
->{_buf} .=
pack
(
'Cv'
, 0xC8,
ref
$varspace
? 0 :
$varspace
);
$self
->_mark_unresolved(-2,
encoder
=>
'_repack'
,
bits
=> 16,
value
=>
$varspace
)
if
ref
$varspace
;
$self
->{_buf} .=
pack
(
'C'
,
ref
$nesting
? 0 :
$nesting
);
$self
->_mark_unresolved(-1,
encoder
=>
'_repack'
,
bits
=> 8,
value
=>
$nesting
)
if
ref
$nesting
;
}
$self
}
sub
leave {
$_
[0]{_buf} .=
"\xC9"
;
$_
[0] }
sub
syscall
{
$_
[0]{_buf} .=
"\x0F\x05"
;
$_
[0] }
sub
rep {
$_
[0]{_buf} .=
"\xF3"
;
$_
[0] }
*repe
=
*repz
=
*rep
;
sub
repnz {
$_
[0]{_buf} .=
"\xF2"
;
$_
[0] }
*repne
=
*repnz
;
my
@_direction_flag_op
= (
"\xFC"
,
"\xFD"
);
sub
flag_direction {
$_
[0]{_buf} .=
$_direction_flag_op
[0+!!
$_
[1]];
$_
[0] }
sub
cld {
$_
[0]{_buf} .=
"\xFC"
;
$_
[0] }
sub
std {
$_
[0]{_buf} .=
"\xFD"
;
$_
[0] }
sub
movs64 {
$_
[0]{_buf} .=
"\x48\xA5"
;
$_
[0] }
*movsq
=
*movs64
;
sub
movs32 {
$_
[0]{_buf} .=
"\xA5"
;
$_
[0] }
sub
movs16 {
$_
[0]{_buf} .=
"\x66\xA5"
;
$_
[0] }
*movsw
=
*movs16
;
sub
movs8 {
$_
[0]{_buf} .=
"\xA4"
;
$_
[0] }
*movsb
=
*movs8
;
sub
cmps64 {
$_
[0]{_buf}.=
"\x48\xA7"
;
$_
[0] }
*cmpsq
=
*cmps64
;
sub
cmps32 {
$_
[0]{_buf}.=
"\xA7"
;
$_
[0] }
*cmpsd
=
*cmps32
;
sub
cmps16 {
$_
[0]{_buf}.=
"\x66\xA7"
;
$_
[0] }
*cmpsw
=
*cmps16
;
sub
cmps8 {
$_
[0]{_buf}.=
"\xA6"
;
$_
[0] }
*cmpsb
=
*cmps8
;
sub
scas64 {
$_
[0]{_buf} .=
"\x48\xAF"
;
$_
[0] }
*scasq
=
*scas64
;
sub
scas32 {
$_
[0]{_buf} .=
"\xAF"
;
$_
[0] }
*scasd
=
*scas32
;
sub
scas16 {
$_
[0]{_buf} .=
"\x66\xAF"
;
$_
[0] }
*scasw
=
*scas16
;
sub
scas8 {
$_
[0]{_buf} .=
"\xAE"
;
$_
[0] }
*scasb
=
*scas8
;
sub
mfence {
$_
[0]{_buf} .=
"\x0F\xAE\xF0"
;
$_
[0];
}
sub
lfence {
$_
[0]{_buf} .=
"\x0F\xAE\xE8"
;
$_
[0];
}
sub
sfence {
$_
[0]{_buf} .=
"\x0F\xAE\xF8"
;
$_
[0];
}
sub
movd {
splice
@_
, 1, 0,
'movd'
;
&_autodetect_signature_2
}
sub
movd_xreg_reg {
$_
[0]->_append_op128_xreg_reg(
"\x66"
, 0, 0x0F6E,
$_
[1],
$_
[2]) }
sub
movd_xreg_mem {
$_
[0]->_append_op128_xreg_mem(
"\x66"
, 0, 0x0F6E,
$_
[1],
$_
[2]) }
sub
movd_reg_xreg {
$_
[0]->_append_op128_xreg_reg(
"\x66"
, 0, 0x0F7E,
$_
[2],
$_
[1]) }
sub
movd_mem_xreg {
$_
[0]->_append_op128_xreg_mem(
"\x66"
, 0, 0x0F7E,
$_
[2],
$_
[1]) }
sub
movq {
splice
@_
, 1, 0,
'movq'
;
&_autodetect_signature_2
}
sub
movq_xreg_xreg {
$_
[0]->_append_op128_xreg_xreg(
"\xF3"
, 0, 0x0F7E,
$_
[1],
$_
[2]) }
sub
movq_xreg_mem {
$_
[0]->_append_op128_xreg_mem(
"\xF3"
, 0, 0x0F7E,
$_
[1],
$_
[2]) }
sub
movq_mem_xreg {
$_
[0]->_append_op128_xreg_mem(
"\x66"
, 0, 0x0FD6,
$_
[2],
$_
[1]) }
sub
movq_xreg_reg {
$_
[0]->_append_op128_xreg_reg(
"\x66"
, 8, 0x0F6E,
$_
[1],
$_
[2]) }
sub
movq_reg_xreg {
$_
[0]->_append_op128_xreg_reg(
"\x66"
, 8, 0x0F7E,
$_
[2],
$_
[1]) }
sub
movss {
splice
@_
, 1, 0,
'movss'
;
&_autodetect_signature_2
}
sub
movss_xreg_xreg {
$_
[0]->_append_op128_xreg_xreg(
"\xF3"
, 0, 0x0F10,
$_
[1],
$_
[2]) }
sub
movss_xreg_mem {
$_
[0]->_append_op128_xreg_mem(
"\xF3"
, 0, 0x0F10,
$_
[1],
$_
[2]) }
sub
movss_mem_xreg {
$_
[0]->_append_op128_xreg_mem(
"\xF3"
, 0, 0x0F11,
$_
[2],
$_
[1]) }
sub
movsd {
goto
\
&movs32
if
@_
== 1;
splice
@_
, 1, 0,
'movsd'
;
&_autodetect_signature_2
}
sub
movsd_xreg_xreg {
$_
[0]->_append_op128_xreg_xreg(
"\xF2"
, 0, 0x0F10,
$_
[1],
$_
[2]) }
sub
movsd_xreg_mem {
$_
[0]->_append_op128_xreg_mem(
"\xF2"
, 0, 0x0F10,
$_
[1],
$_
[2]) }
sub
movsd_mem_xreg {
$_
[0]->_append_op128_xreg_mem(
"\xF2"
, 0, 0x0F11,
$_
[2],
$_
[1]) }
sub
_encode_op_reg_reg {
my
(
$self
,
$rex
,
$opcode
,
$reg1
,
$reg2
,
$immed_pack
,
$immed
)=
@_
;
$rex
|= ((
$reg1
& 8) >> 1) | ((
$reg2
& 8) >> 3);
return
$rex
?
(
defined
$immed
?
pack
(
'CCC'
.
$immed_pack
, 0x40|
$rex
,
$opcode
, 0xC0 | ((
$reg1
& 7) << 3) | (
$reg2
& 7),
$immed
)
:
pack
(
'CCC'
, 0x40|
$rex
,
$opcode
, 0xC0 | ((
$reg1
& 7) << 3) | (
$reg2
& 7))
)
: (
defined
$immed
?
pack
(
'CC'
.
$immed_pack
,
$opcode
, 0xC0 | ((
$reg1
& 7) << 3) | (
$reg2
& 7),
$immed
)
:
pack
(
'CC'
,
$opcode
, 0xC0 | ((
$reg1
& 7) << 3) | (
$reg2
& 7))
);
}
sub
_append_op64_reg_reg {
my
(
$self
,
$opcode
,
$reg1
,
$reg2
)=
@_
;
$reg1
= (
$regnum64
{
$reg1
} // croak(
"$reg1 is not a 64-bit register"
));
$reg2
= (
$regnum64
{
$reg2
} // croak(
"$reg2 is not a 64-bit register"
));
$self
->{_buf} .=
$self
->_encode_op_reg_reg(8,
$opcode
,
$reg1
,
$reg2
);
$self
;
}
sub
_append_op64_regnum_reg {
my
(
$self
,
$rex
,
$opcode
,
$regnum
,
$reg2
)=
@_
;
$reg2
= (
$regnum64
{
$reg2
} // croak(
"$reg2 is not a 64-bit register"
));
$self
->{_buf} .=
$self
->_encode_op_reg_reg(
$rex
,
$opcode
,
$regnum
,
$reg2
);
$self
;
}
sub
_append_op32_reg_reg {
my
(
$self
,
$opcode
,
$reg1
,
$reg2
)=
@_
;
$reg1
= (
$regnum32
{
$reg1
} // croak(
"$reg1 is not a 32-bit register"
));
$reg2
= (
$regnum32
{
$reg2
} // croak(
"$reg2 is not a 32-bit register"
));
$self
->{_buf} .=
$self
->_encode_op_reg_reg(0,
$opcode
,
$reg1
,
$reg2
);
$self
;
}
sub
_append_op32_regnum_reg {
my
(
$self
,
$opcode
,
$regnum
,
$reg2
)=
@_
;
$reg2
= (
$regnum32
{
$reg2
} // croak(
"$reg2 is not a 32-bit register"
));
$self
->{_buf} .=
$self
->_encode_op_reg_reg(0,
$opcode
,
$regnum
,
$reg2
);
$self
;
}
sub
_append_op16_reg_reg {
my
(
$self
,
$opcode
,
$reg1
,
$reg2
)=
@_
;
$reg1
= (
$regnum16
{
$reg1
} // croak(
"$reg1 is not a 16-bit register"
));
$reg2
= (
$regnum16
{
$reg2
} // croak(
"$reg2 is not a 16-bit register"
));
$self
->{_buf} .=
"\x66"
.
$self
->_encode_op_reg_reg(0,
$opcode
,
$reg1
,
$reg2
);
$self
;
}
sub
_append_op16_regnum_reg {
my
(
$self
,
$opcode
,
$regnum
,
$reg2
)=
@_
;
$reg2
= (
$regnum16
{
$reg2
} // croak(
"$reg2 is not a 16-bit register"
));
$self
->{_buf} .=
"\x66"
.
$self
->_encode_op_reg_reg(0,
$opcode
,
$regnum
,
$reg2
);
$self
;
}
sub
_append_op8_regnum_reg {
_append_op8_reg_reg(
@_
,
$_
[2]);
}
sub
_append_op8_reg_reg {
my
(
$self
,
$opcode
,
$reg1
,
$reg2
,
$reg1num
)=
@_
;
$reg1num
//=
$regnum8
{
$reg1
} // croak
"$reg1 is not a valid 8-bit register"
;
my
$reg2num
=
$regnum8
{
$reg2
} // croak
"$reg2 is not a valid 8-bit register"
;
my
$uses_high_byte
= (
$reg1num
|
$reg2num
)&0x10;
my
$uses_extended_reg
= (
$reg1num
|
$reg2num
)&0x20;
my
$mod_rm
= 0xC0 | ((
$reg1num
& 7) << 3) | (
$reg2num
& 7);
if
(
$uses_extended_reg
) {
croak
"Can't combine $reg1 with $reg2 in same instruction"
if
$uses_high_byte
;
$self
->{_buf} .=
pack
(
'CCC'
, 0x40|((
$reg1num
& 8) >> 1) | ((
$reg2num
& 8) >> 3),
$opcode
,
$mod_rm
)
}
else
{
$self
->{_buf} .=
pack
(
'CC'
,
$opcode
,
$mod_rm
);
}
$self
;
}
sub
_append_op128_xreg_xreg {
my
(
$self
,
$prefix
,
$rex
,
$opcode
,
$xreg1
,
$xreg2
)=
@_
;
$xreg1
=
$regnum128
{
$xreg1
} // croak(
"$xreg1 is not a 128-bit register"
);
$xreg2
=
$regnum128
{
$xreg2
} // croak(
"$xreg2 is not a 128-bit register"
);
$rex
|= (
$xreg1
& 8) >> 1 | (
$xreg2
& 8) >> 3;
my
$modrm
= 0xC0 | (
$xreg1
& 7) << 3 | (
$xreg2
& 7);
$_
[0]{_buf} .= !
$rex
?
pack
(
'a*CCC'
,
$prefix
,
$opcode
>> 8,
$opcode
& 0xFF,
$modrm
)
:
pack
(
'a*CCCC'
,
$prefix
, 0x40|
$rex
,
$opcode
>> 8,
$opcode
& 0xFF,
$modrm
);
$_
[0]
}
sub
_append_op128_xreg_reg {
my
(
$self
,
$prefix
,
$rex
,
$opcode
,
$xreg
,
$reg
)=
@_
;
$xreg
=
$regnum128
{
$xreg
} // croak(
"$xreg is not a 128-bit register"
);
if
(
defined
(
my
$regid
=
$regnum64
{
$reg
})) {
$rex
|= 8;
$reg
=
$regid
;
}
elsif
(
defined
(
$regid
=
$regnum32
{
$reg
})) {
$reg
=
$regid
;
}
else
{
croak(
"$reg is not a 32 or 64-bit register"
);
}
$rex
|= (
$xreg
& 8) >> 1 | (
$reg
& 8) >> 3;
my
$modrm
= 0xC0 | (
$xreg
& 7) << 3 | (
$reg
& 7);
$_
[0]{_buf} .= !
$rex
?
pack
(
'a*CCC'
,
$prefix
,
$opcode
>> 8,
$opcode
& 0xFF,
$modrm
)
:
pack
(
'a*CCCC'
,
$prefix
, 0x40|
$rex
,
$opcode
>> 8,
$opcode
& 0xFF,
$modrm
);
$_
[0]
}
sub
_append_op128_xreg_mem {
my
(
$self
,
$prefix
,
$rex
,
$opcode
,
$reg
,
$mem
)=
@_
;
$reg
=
$regnum128
{
$reg
} // croak
"$reg is not a valid 128-bit register"
;
$self
->_append_op_reg_mem(
$prefix
,
$rex
,
$opcode
,
$reg
,
$mem
);
}
sub
_append_op64_reg_mem {
my
(
$self
,
$rex
,
$opcode
,
$reg
,
$mem
)=
@_
;
$reg
=
$regnum64
{
$reg
} // croak
"$reg is not a valid 64-bit register"
if
defined
$reg
;
$self
->_append_op_reg_mem(
undef
,
$rex
,
$opcode
,
$reg
,
$mem
);
}
sub
_append_op32_reg_mem {
my
(
$self
,
$rex
,
$opcode
,
$reg
,
$mem
)=
@_
;
$reg
=
$regnum32
{
$reg
} // croak
"$reg is not a valid 32-bit register"
if
defined
$reg
;
$self
->_append_op_reg_mem(
undef
,
$rex
,
$opcode
,
$reg
,
$mem
);
}
sub
_append_op16_reg_mem {
my
(
$self
,
$rex
,
$opcode
,
$reg
,
$mem
)=
@_
;
$reg
=
$regnum16
{
$reg
} // croak
"$reg is not a valid 16-bit register"
if
defined
$reg
;
$self
->_append_op_reg_mem(
"\x66"
,
$rex
,
$opcode
,
$reg
,
$mem
);
}
sub
_append_op8_reg_mem {
my
(
$self
,
$rex
,
$opcode
,
$reg
,
$mem
)=
@_
;
my
$regnum
=
$regnum8
{
$reg
} // croak
"$reg is not a valid 8-bit register"
;
$rex
|= 0x40
if
$regnum
& 0x20;
if
(
$regnum
& 0x10) {
my
(
$base_reg
,
$disp
,
$index_reg
,
$scale
)=
@$mem
;
croak
"Cannot use $reg in instruction with REX prefix"
if
$rex
|| ((
$regnum64
{
$base_reg
//
''
}//0) & 8) || ((
$regnum64
{
$index_reg
//
''
}//0) & 8);
}
$self
->_append_op_reg_mem(
undef
,
$rex
,
$opcode
,
$regnum
,
$mem
);
}
sub
_append_op_reg_mem {
my
(
$self
,
$prefix
,
$rex
,
$opcode
,
$regnum
,
$mem
)=
@_
;
my
(
$base_reg
,
$disp
,
$index_reg
,
$scale
)=
@$mem
;
$index_reg
=
$regnum64
{
$index_reg
} // croak
"$index_reg is not a valid 64-bit register"
if
defined
$index_reg
;
my
$rip
;
if
(
defined
$base_reg
) {
$base_reg
=
$regnum64
{
$base_reg
} // croak
"$base_reg is not a valid 64-bit register"
;
if
((
$base_reg
& 0x40) &&
ref
$disp
) {
$disp
=
defined
$$disp
?
$self
->get_label(
$$disp
) : (
$$disp
=
$self
->get_label)
if
ref
$disp
eq
'SCALAR'
;
$disp
=
bless
{
target
=>
$disp
},
'CPU::x86_64::InstructionWriter::RipRelative'
;
}
}
$self
->{_buf} .=
$prefix
if
defined
$prefix
;
$self
->_append_possible_unknown(
'_encode_op_reg_mem'
, [
$rex
,
$opcode
,
$regnum
,
$base_reg
,
$disp
,
$index_reg
,
$scale
], 4, 7);
$self
->label(
$rip
)
if
defined
$rip
;
$self
;
}
sub
_append_op8_const_to_mem {
my
(
$self
,
$opcode
,
$opreg
,
$value
,
$mem
)=
@_
;
my
(
$base_reg
,
$disp
,
$index_reg
,
$scale
)=
@$mem
;
$base_reg
= (
$regnum64
{
$base_reg
} // croak
"$base_reg is not a 64-bit register"
)
if
defined
$base_reg
;
$index_reg
= (
$regnum64
{
$index_reg
} // croak
"$index_reg is not a 64-bit register"
)
if
defined
$index_reg
;
$self
->_append_possible_unknown(
'_encode_op_reg_mem'
, [ 0,
$opcode
,
$opreg
,
$base_reg
,
$disp
,
$index_reg
,
$scale
,
'C'
,
$value
],
ref
$disp
? 4 : 8,
defined
$disp
? 16:12);
}
sub
_append_op16_const_to_mem {
my
(
$self
,
$opcode
,
$opreg
,
$value
,
$mem
)=
@_
;
my
(
$base_reg
,
$disp
,
$index_reg
,
$scale
)=
@$mem
;
$base_reg
= (
$regnum64
{
$base_reg
} // croak
"$base_reg is not a 64-bit register"
)
if
defined
$base_reg
;
$index_reg
= (
$regnum64
{
$index_reg
} // croak
"$index_reg is not a 64-bit register"
)
if
defined
$index_reg
;
$self
->{_buf} .=
"\x66"
;
$self
->_append_possible_unknown(
'_encode_op_reg_mem'
, [ 0,
$opcode
,
$opreg
,
$base_reg
,
$disp
,
$index_reg
,
$scale
,
'v'
,
$value
],
ref
$disp
? 4 : 8,
defined
$disp
? 16:12);
}
sub
_append_op32_const_to_mem {
my
(
$self
,
$opcode
,
$opreg
,
$value
,
$mem
)=
@_
;
my
(
$base_reg
,
$disp
,
$index_reg
,
$scale
)=
@$mem
;
$base_reg
= (
$regnum64
{
$base_reg
} // croak
"$base_reg is not a 64-bit register"
)
if
defined
$base_reg
;
$index_reg
= (
$regnum64
{
$index_reg
} // croak
"$index_reg is not a 64-bit register"
)
if
defined
$index_reg
;
$self
->_append_possible_unknown(
'_encode_op_reg_mem'
, [ 0,
$opcode
,
$opreg
,
$base_reg
,
$disp
,
$index_reg
,
$scale
,
'V'
,
$value
],
ref
$disp
? 4 : 8,
defined
$disp
? 16:12);
}
sub
_append_op64_const_to_mem {
my
(
$self
,
$opcode
,
$opreg
,
$value
,
$mem
)=
@_
;
my
(
$base_reg
,
$disp
,
$index_reg
,
$scale
)=
@$mem
;
$base_reg
= (
$regnum64
{
$base_reg
} // croak
"$base_reg is not a 64-bit register"
)
if
defined
$base_reg
;
$index_reg
= (
$regnum64
{
$index_reg
} // croak
"$index_reg is not a 64-bit register"
)
if
defined
$index_reg
;
$self
->_append_possible_unknown(
'_encode_op_reg_mem'
, [ 8,
$opcode
,
$opreg
,
$base_reg
,
$disp
,
$index_reg
,
$scale
,
'V'
,
$value
],
ref
$disp
? 4 : 8,
defined
$disp
? 16:12);
}
my
%SIB_scale
= (
1
=> 0x00,
2
=> 0x40,
4
=> 0x80,
8
=> 0xC0
);
sub
_encode_op_reg_mem {
my
(
$self
,
$rex
,
$opcode
,
$reg
,
$base_reg
,
$disp
,
$index_reg
,
$scale
,
$immed_pack
,
$immed
)=
@_
;
$rex
|= (
$reg
& 8) >> 1;
$opcode
=
$opcode
<= 0xFF?
pack
(
'C'
,
$opcode
) :
pack
(
'CC'
,
$opcode
>> 8,
$opcode
& 0xFF);
my
$tail
;
if
(
defined
$base_reg
) {
if
(
$base_reg
== 0x845) {
defined
$disp
or croak
"RIP-relative address requires displacement"
;
defined
$scale
||
defined
$immed
and croak
"RIP-relative address cannot have scale or immediate-value"
;
return
$rex
?
pack
(
'Ca*CV'
, (
$rex
|0x40),
$opcode
, ((
$reg
& 7) << 3)|5,
$disp
)
:
pack
(
'a*CV'
,
$opcode
, ((
$reg
& 7) << 3)|5,
$disp
);
}
$rex
|= (
$base_reg
& 8) >> 3;
my
(
$mod_rm
,
$suffix
)= !
$disp
? ( (
$base_reg
&7) == 5? (0x40,
"\0"
) : (0x00,
''
) )
: ((
$disp
>> 7) == (
$disp
>> 8))? (0x40,
pack
(
'c'
,
$disp
))
: ((
$disp
>> 31) == (
$disp
>> 31 >> 1))? (0x80,
pack
(
'V'
,
$disp
))
: croak
"address displacement out of range: $disp"
;
if
(
defined
$index_reg
) {
my
$scale
=
$SIB_scale
{
$scale
// 1} // croak
"invalid index multiplier $scale"
;
$index_reg
!= 4 or croak
"RSP cannot be used as index register"
;
$rex
|= (
$index_reg
& 8) >> 2;
$tail
=
pack
(
'CCa*'
,
$mod_rm
| ((
$reg
& 7) << 3) | 4,
$scale
| ((
$index_reg
& 7) << 3) | (
$base_reg
& 7),
$suffix
);
}
elsif
((
$base_reg
&7) == 4) {
$tail
=
pack
(
'CCa*'
,
$mod_rm
| ((
$reg
& 7) << 3) | 4, 0x24,
$suffix
);
}
else
{
$tail
=
pack
(
'Ca*'
,
$mod_rm
| ((
$reg
& 7) << 3) | (
$base_reg
& 7),
$suffix
);
}
}
else
{
((
$disp
>> 31) == (
$disp
>> 31 >> 1))
or croak
"address displacement out of range: $disp"
;
if
(
defined
$index_reg
) {
my
$scale
=
$SIB_scale
{
$scale
// 1} // croak
"invalid index multiplier $scale"
;
croak
"RSP cannot be used as index register"
if
(
$index_reg
& 0xF) == 4;
$rex
|= (
$index_reg
& 8) >> 2;
$tail
=
pack
(
'CCV'
, ((
$reg
& 7) << 3) | 4,
$scale
| ((
$index_reg
& 7) << 3) | 5,
$disp
);
}
else
{
$tail
=
pack
(
'CCV'
, ((
$reg
& 7) << 3) | 4, 0x25,
$disp
);
}
}
$tail
.=
pack
(
$immed_pack
,
$immed
)
if
defined
$immed
;
return
$rex
?
pack
(
'Ca*a*'
, (
$rex
|0x40),
$opcode
,
$tail
)
:
$opcode
.
$tail
;
}
sub
_append_mathop64_const {
my
(
$self
,
@args
)=
@_
;
$args
[4]=
$regnum64
{
$args
[4]} // croak(
"$args[4] is not a 64-bit register"
);
$self
->_append_possible_unknown(
'_encode_mathop64_imm'
, \
@args
, 5, 7);
}
sub
_encode_mathop64_imm {
my
(
$self
,
$opcodeAX32
,
$opcode8
,
$opcode32
,
$opcode_reg
,
$reg
,
$value
)=
@_
;
my
$rex
= 0x48 | ((
$reg
& 8)>>3);
defined
$opcode8
&& ((
$value
>> 7) == (
$value
>> 8))?
pack
(
'CCCc'
,
$rex
,
$opcode8
, 0xC0 | (
$opcode_reg
<< 3) | (
$reg
& 7),
$value
)
: ((
$value
>> 31) == (
$value
>> 31 >> 1))? (
(
$reg
&0xF)?
pack
(
'CCCV'
,
$rex
,
$opcode32
, 0xC0 | (
$opcode_reg
<< 3) | (
$reg
& 7),
$value
)
:
pack
(
'CCV'
,
$rex
,
$opcodeAX32
,
$value
)
)
: croak
"$value is wider than 32-bit"
;
}
sub
_append_mathop32_const {
my
(
$self
,
@args
)=
@_
;
$args
[4]=
$regnum32
{
$args
[4]} // croak(
"$args[4] is not a 32-bit register"
);
$self
->_append_possible_unknown(
'_encode_mathop32_imm'
, \
@args
, 5, 7);
}
sub
_encode_mathop32_imm {
my
(
$self
,
$opcodeAX32
,
$opcode8
,
$opcode32
,
$opcode_reg
,
$reg
,
$value
)=
@_
;
my
$rex
= ((
$reg
& 8)>>3);
defined
$opcode8
&& ((
$value
>> 7) == (
$value
>> 8) or (
$value
>> 8 == 0xFFFFFF))?
(
$rex
?
pack
(
'CCCC'
, 0x40|
$rex
,
$opcode8
, 0xC0 | (
$opcode_reg
<< 3) | (
$reg
& 7),
$value
&0xFF)
:
pack
(
'CCC'
,
$opcode8
, 0xC0 | (
$opcode_reg
<< 3) | (
$reg
& 7),
$value
&0xFF)
)
: ((
$value
>> 31 >> 1) == (
$value
>> 31 >> 2))? (
$rex
?
pack
(
'CCCV'
, 0x40|
$rex
,
$opcode32
, 0xC0 | (
$opcode_reg
<< 3) | (
$reg
& 7),
$value
)
: (
$reg
&0xF)?
pack
(
'CCV'
,
$opcode32
, 0xC0 | (
$opcode_reg
<< 3) | (
$reg
& 7),
$value
)
:
pack
(
'CV'
,
$opcodeAX32
,
$value
)
)
: croak
"$value is wider than 32-bit"
;
}
sub
_append_mathop16_const {
my
(
$self
,
@args
)=
@_
;
$args
[4]=
$regnum16
{
$args
[4]} // croak(
"$args[4] is not a 16-bit register"
);
$self
->_append_possible_unknown(
'_encode_mathop16_imm'
, \
@args
, 5, 8);
}
sub
_encode_mathop16_imm {
my
(
$self
,
$opcodeAX16
,
$opcode8
,
$opcode16
,
$opcode_reg
,
$reg
,
$value
)=
@_
;
my
$rex
= ((
$reg
& 8)>>3);
defined
$opcode8
&& ((
$value
>> 7) == (
$value
>> 8) or (
$value
>> 8 == 0xFF))?
(
$rex
?
pack
(
'CCCCC'
, 0x66, 0x40|
$rex
,
$opcode8
, 0xC0 | (
$opcode_reg
<< 3) | (
$reg
& 7),
$value
&0xFF)
:
pack
(
'CCCC'
, 0x66,
$opcode8
, 0xC0 | (
$opcode_reg
<< 3) | (
$reg
& 7),
$value
&0xFF)
)
: ((
$value
>> 16) == (
$value
>> 17))? (
$rex
?
pack
(
'CCCCv'
, 0x66, 0x40|
$rex
,
$opcode16
, 0xC0 | (
$opcode_reg
<< 3) | (
$reg
& 7),
$value
&0xFFFF)
: (
$reg
&0xF)?
pack
(
'CCCv'
, 0x66,
$opcode16
, 0xC0 | (
$opcode_reg
<< 3) | (
$reg
& 7),
$value
&0xFFFF)
:
pack
(
'CCv'
, 0x66,
$opcodeAX16
,
$value
)
)
: croak
"$value is wider than 16-bit"
;
}
sub
_append_mathop8_const {
my
(
$self
,
$opcodeAX8
,
$opcode8
,
$opcode_reg
,
$reg
,
$immed
)=
@_
;
$reg
=
$regnum8
{
$reg
} // croak(
"$reg is not a 8-bit register"
);
my
$value
=
ref
$immed
? 0x00 :
$immed
;
((
$value
>> 8) == (
$value
>> 9)) or croak
"$value is wider than 8 bits"
;
if
(
$reg
& 0x10) {
$self
->{_buf} .=
pack
(
'CCC'
,
$opcode8
, 0xC0 | (
$opcode_reg
<<3) | (
$reg
& 7),
$value
&0xFF);
}
elsif
(!(
$reg
&0xF)) {
$self
->{_buf} .=
pack
(
'CC'
,
$opcodeAX8
,
$value
&0xFF);
}
elsif
(
$reg
& 0x20) {
$self
->{_buf} .=
pack
(
'CCCC'
, 0x40|((
$reg
& 8)>>3),
$opcode8
, 0xC0 | (
$opcode_reg
<< 3) | (
$reg
& 7),
$value
&0xFF);
}
else
{
$self
->{_buf} .=
pack
(
'CCC'
,
$opcode8
, 0xC0 | (
$opcode_reg
<< 3) | (
$reg
& 7),
$value
&0xFF);
}
$self
->_mark_unresolved(-1,
encoder
=>
'_repack'
,
bits
=> 8,
value
=>
$immed
)
if
ref
$immed
;
$self
;
}
sub
_append_mathop64_const_to_mem {
my
(
$self
,
$opcode8
,
$opcode32
,
$opcode_reg
,
$value
,
$mem
)=
@_
;
my
(
$base_reg
,
$disp
,
$index_reg
,
$scale
)=
@$mem
;
$base_reg
= (
$regnum64
{
$base_reg
} // croak
"$base_reg is not a 64-bit register"
)
if
defined
$base_reg
;
$index_reg
= (
$regnum64
{
$index_reg
} // croak
"$index_reg is not a 64-bit register"
)
if
defined
$index_reg
;
$self
->_append_possible_unknown(
'_encode_mathop64_mem_immed'
, [
$opcode8
,
$opcode32
,
$opcode_reg
,
$value
,
$base_reg
,
$disp
,
$index_reg
,
$scale
], 3,
defined
$disp
? 9:12);
}
sub
_encode_mathop64_mem_immed {
my
(
$self
,
$opcode8
,
$opcode32
,
$opcode_reg
,
$value
,
$base_reg
,
$disp
,
$index_reg
,
$scale
)=
@_
;
defined
$opcode8
&& ((
$value
>> 7) == (
$value
>> 8))?
$self
->_encode_op_reg_mem(8,
$opcode8
,
$opcode_reg
,
$base_reg
,
$disp
,
$index_reg
,
$scale
,
'C'
,
$value
&0xFF)
: ((
$value
>> 31) == (
$value
>> 31 >> 1))?
$self
->_encode_op_reg_mem(8,
$opcode32
,
$opcode_reg
,
$base_reg
,
$disp
,
$index_reg
,
$scale
,
'V'
,
$value
&0xFFFFFFFF)
: croak
"$value is wider than 31-bit"
;
}
sub
_append_mathop32_const_to_mem {
my
(
$self
,
$opcode8
,
$opcode32
,
$opcode_reg
,
$value
,
$mem
)=
@_
;
my
(
$base_reg
,
$disp
,
$index_reg
,
$scale
)=
@$mem
;
$base_reg
= (
$regnum64
{
$base_reg
} // croak
"$base_reg is not a 64-bit register"
)
if
defined
$base_reg
;
$index_reg
= (
$regnum64
{
$index_reg
} // croak
"$index_reg is not a 64-bit register"
)
if
defined
$index_reg
;
$self
->_append_possible_unknown(
'_encode_mathop32_mem_immed'
, [
$opcode8
,
$opcode32
,
$opcode_reg
,
$value
,
$base_reg
,
$disp
,
$index_reg
,
$scale
], 3,
defined
$disp
? 12:8);
}
sub
_encode_mathop32_mem_immed {
my
(
$self
,
$opcode8
,
$opcode32
,
$opcode_reg
,
$value
,
$base_reg
,
$disp
,
$index_reg
,
$scale
)=
@_
;
defined
$opcode8
&& ((
$value
>> 7) == (
$value
>> 8) or (
$value
>> 8 == 0xFFFFFF))?
$self
->_encode_op_reg_mem(0,
$opcode8
,
$opcode_reg
,
$base_reg
,
$disp
,
$index_reg
,
$scale
).
pack
(
'C'
,
$value
&0xFF)
: ((
$value
>> 30 >> 2) == (
$value
>> 30 >> 3))?
$self
->_encode_op_reg_mem(0,
$opcode32
,
$opcode_reg
,
$base_reg
,
$disp
,
$index_reg
,
$scale
).
pack
(
'V'
,
$value
&0xFFFFFFFF)
: croak
"$value is wider than 32-bit"
;
}
sub
_append_mathop16_const_to_mem {
my
(
$self
,
$opcode8
,
$opcode16
,
$opcode_reg
,
$value
,
$mem
)=
@_
;
my
(
$base_reg
,
$disp
,
$index_reg
,
$scale
)=
@$mem
;
$base_reg
= (
$regnum64
{
$base_reg
} // croak
"$base_reg is not a 64-bit register"
)
if
defined
$base_reg
;
$index_reg
= (
$regnum64
{
$index_reg
} // croak
"$index_reg is not a 64-bit register"
)
if
defined
$index_reg
;
$self
->{_buf} .=
"\x66"
;
$self
->_append_possible_unknown(
'_encode_mathop16_mem_immed'
, [
$opcode8
,
$opcode16
,
$opcode_reg
,
$value
,
$base_reg
,
$disp
,
$index_reg
,
$scale
], 3,
defined
$disp
? 10:6);
}
sub
_encode_mathop16_mem_immed {
my
(
$self
,
$opcode8
,
$opcode16
,
$opcode_reg
,
$value
,
$base_reg
,
$disp
,
$index_reg
,
$scale
)=
@_
;
defined
$opcode8
&& ((
$value
>> 7) == (
$value
>> 8) or (
$value
>> 8 == 0xFF))?
$self
->_encode_op_reg_mem(0,
$opcode8
,
$opcode_reg
,
$base_reg
,
$disp
,
$index_reg
,
$scale
).
pack
(
'C'
,
$value
&0xFF)
: ((
$value
>> 16) == (
$value
>> 17))?
$self
->_encode_op_reg_mem(0,
$opcode16
,
$opcode_reg
,
$base_reg
,
$disp
,
$index_reg
,
$scale
).
pack
(
'v'
,
$value
&0xFFFF)
: croak
"$value is wider than 16-bit"
;
}
sub
_append_mathop8_const_to_mem {
my
(
$self
,
$opcode8
,
$opcode_reg
,
$value
,
$mem
)=
@_
;
my
(
$base_reg
,
$disp
,
$index_reg
,
$scale
)=
@$mem
;
$base_reg
= (
$regnum64
{
$base_reg
} // croak
"$base_reg is not a 64-bit register"
)
if
defined
$base_reg
;
$index_reg
= (
$regnum64
{
$index_reg
} // croak
"$index_reg is not a 64-bit register"
)
if
defined
$index_reg
;
$self
->_append_possible_unknown(
'_encode_mathop8_mem_immed'
, [
$opcode8
,
$opcode_reg
,
$value
,
$base_reg
,
$disp
,
$index_reg
,
$scale
], 2,
defined
$disp
? 10:6);
}
sub
_encode_mathop8_mem_immed {
my
(
$self
,
$opcode8
,
$opcode_reg
,
$value
,
$base_reg
,
$disp
,
$index_reg
,
$scale
)=
@_
;
((
$value
>> 8) == (
$value
>> 9)) or croak
"$value is wider than 8 bit"
;
$self
->_encode_op_reg_mem(0,
$opcode8
,
$opcode_reg
,
$base_reg
,
$disp
,
$index_reg
,
$scale
).
pack
(
'C'
,
$value
&0xFF);
}
sub
_append_possible_unknown {
my
(
$self
,
$encoder
,
$encoder_args
,
$unknown_pos
,
$estimated_length
)=
@_
;
my
$u
=
$encoder_args
->[
$unknown_pos
];
if
(
ref
$u
&& !looks_like_number(
$u
)) {
$u
=
defined
$$u
?
$self
->get_label(
$$u
) : (
$$u
=
$self
->get_label)
if
ref
$u
eq
'SCALAR'
;
ref
(
$u
)->can(
'value'
)
or croak
"Expected object with '->value' method"
;
$self
->_mark_unresolved(
$estimated_length
,
encoder
=>
sub
{
my
(
$self
,
$lazy_enc
)=
@_
;
my
@args
=
@$encoder_args
;
$args
[
$unknown_pos
]=
$lazy_enc
->unknown->value
// croak
"Value '"
.
$lazy_enc
->unknown->name.
"' is still unresolved"
;
$self
->
$encoder
(
@args
);
},
unknown
=>
$u
,
);
$u
->instruction(
$self
->_unresolved->[-1])
if
$u
->can(
'instruction'
);
}
else
{
$self
->{_buf} .=
$self
->
$encoder
(
@$encoder_args
);
}
$self
;
}
sub
_mark_unresolved {
my
(
$self
,
$location
)= (
shift
,
shift
);
my
$offset
=
length
(
$self
->{_buf});
if
(
$location
< 0) {
$location
= -
$location
;
$offset
-=
$location
;
}
else
{
$self
->{_buf} .=
"\0"
x
$location
;
}
if
(
$self
->{debug}) {
my
(
$i
,
@caller
);
while
(
@caller
=
caller
(++
$i
)) {
last
if
$caller
[0] ne __PACKAGE__;
}
push
@_
,
caller
=> \
@caller
;
}
push
@{
$self
->_unresolved },
bless
{
relative_to
=>
$self
->start_address,
offset
=>
$offset
,
len
=>
$location
,
@_
},
'CPU::x86_64::InstructionWriter::LazyEncode'
;
$self
;
}
sub
_repack {
my
(
$self
,
$params
)=
@_
;
my
$v
=
$params
->{value}->value;
defined
$v
or croak
"Placeholder $params->{value} has not been assigned"
;
my
$bits
=
$params
->{bits};
my
$pack
=
$bits
<= 8?
'C'
:
$bits
<= 16?
'v'
:
$bits
<= 32?
'V'
:
$bits
<= 64?
'Q<'
:
die
"Unhandled bits $bits\n"
;
$bits
== 64 || ((
$v
>>
$bits
) == (
$v
>> (
$bits
+1))) or croak
"$v is wider than $bits bits"
;
return
pack
(
$pack
,
$v
& ~(~0 <<
$bits
));
}
sub
_resolve {
my
$self
=
shift
;
my
$changed_len
= 1;
while
(
$changed_len
) {
$changed_len
= 0;
my
$ofs
= 0;
for
my
$p
(@{
$self
->_unresolved }) {
$p
->{offset} +=
$ofs
if
$ofs
;
my
$fn
=
$p
->can(
'encoder'
) &&
$p
->encoder
or
next
;
eval
{
my
$enc
=
$self
->
$fn
(
$p
);
substr
(
$self
->{_buf},
$p
->{offset},
$p
->{len})=
$enc
;
if
(
length
(
$enc
) !=
$p
->{len}) {
$changed_len
= 1;
$ofs
+= (
length
(
$enc
) -
$p
->{len});
$p
->{len}=
length
(
$enc
);
}
1
} or
do
{
if
(
my
$caller
=
$p
->
caller
) {
(
my
$op
=
$caller
->[3]) =~ s/.*:://;
croak
"Failed to encode instruction $op from $caller->[1] line $caller->[2]:\n $@"
;
}
else
{
croak
"Failed to encode instruction (enable diagnostics with ->debug(1) ): $@"
;
}
}
}
}
}
1;