1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 07:37:46 +00:00

LibX86: Add basic x86-64 support

Most of the 64-bit instructions default to 32-bit operands and select
64-bit using REX.W prefixes. Because of that instead of defining new
instruction formats, this reuses the 32-bit formats and changes them
to take the REX prefixes into account when necessary.

Additionally this removes, adds or modifies the instruction
descriptors in the 64-bit table, where they are different from 32-bit.

Using 'disasm' these changes seem to cover pretty much all of our
64-bit binaries (except for AVX) :^)

Note that UserspaceEmulator will need to account for these prefixed
versions in its 32-bit instruction handlers before being usable on
x86-64.
This commit is contained in:
Simon Wanner 2022-03-27 23:15:32 +02:00 committed by Andreas Kling
parent 735fd5f5db
commit 2ae228dac7
3 changed files with 295 additions and 67 deletions

View file

@ -26,7 +26,7 @@ public:
return Instruction::from_stream(m_stream, OperandSize::Size32, AddressSize::Size32); return Instruction::from_stream(m_stream, OperandSize::Size32, AddressSize::Size32);
#else #else
# if ARCH(X86_64) # if ARCH(X86_64)
return Instruction::from_stream(m_stream, OperandSize::Size64, AddressSize::Size64); return Instruction::from_stream(m_stream, OperandSize::Size32, AddressSize::Size64);
# else # else
dbgln("Unsupported platform"); dbgln("Unsupported platform");
return {}; return {};

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2022, the SerenityOS developers.
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -32,7 +33,7 @@ static bool opcode_has_register_index(u8 op)
return false; return false;
} }
static void build(InstructionDescriptor* table, u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler handler, IsLockPrefixAllowed lock_prefix_allowed) static void build_in_table(InstructionDescriptor* table, u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler handler, IsLockPrefixAllowed lock_prefix_allowed)
{ {
InstructionDescriptor& d = table[op]; InstructionDescriptor& d = table[op];
@ -221,7 +222,7 @@ static void build_slash(InstructionDescriptor* table, u8 op, u8 slash, char cons
if (!d.slashes) if (!d.slashes)
d.slashes = new InstructionDescriptor[8]; d.slashes = new InstructionDescriptor[8];
build(d.slashes, slash, mnemonic, format, handler, lock_prefix_allowed); build_in_table(d.slashes, slash, mnemonic, format, handler, lock_prefix_allowed);
} }
static void build_slash_rm(InstructionDescriptor* table, u8 op, u8 slash, u8 rm, char const* mnemonic, InstructionFormat format, InstructionHandler handler) static void build_slash_rm(InstructionDescriptor* table, u8 op, u8 slash, u8 rm, char const* mnemonic, InstructionFormat format, InstructionHandler handler)
@ -242,28 +243,31 @@ static void build_slash_rm(InstructionDescriptor* table, u8 op, u8 slash, u8 rm,
} }
} }
build(d.slashes, rm & 7, mnemonic, format, handler, LockPrefixNotAllowed); build_in_table(d.slashes, rm & 7, mnemonic, format, handler, LockPrefixNotAllowed);
} }
template<auto table> template<auto table>
static void build_base(u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed) static void build_base(u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
{ {
build(table[to_underlying(OperandSize::Size16)], op, mnemonic, format, impl, lock_prefix_allowed); build_in_table(table[to_underlying(OperandSize::Size16)], op, mnemonic, format, impl, lock_prefix_allowed);
build(table[to_underlying(OperandSize::Size32)], op, mnemonic, format, impl, lock_prefix_allowed); build_in_table(table[to_underlying(OperandSize::Size32)], op, mnemonic, format, impl, lock_prefix_allowed);
build_in_table(table[to_underlying(OperandSize::Size64)], op, mnemonic, format, impl, lock_prefix_allowed);
} }
template<auto table> template<auto table>
static void build_base(u8 op, char const* mnemonic, InstructionFormat format16, InstructionHandler impl16, InstructionFormat format32, InstructionHandler impl32, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed) static void build_base(u8 op, char const* mnemonic, InstructionFormat format16, InstructionHandler impl16, InstructionFormat format32, InstructionHandler impl32, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
{ {
build(table[to_underlying(OperandSize::Size16)], op, mnemonic, format16, impl16, lock_prefix_allowed); build_in_table(table[to_underlying(OperandSize::Size16)], op, mnemonic, format16, impl16, lock_prefix_allowed);
build(table[to_underlying(OperandSize::Size32)], op, mnemonic, format32, impl32, lock_prefix_allowed); build_in_table(table[to_underlying(OperandSize::Size32)], op, mnemonic, format32, impl32, lock_prefix_allowed);
build_in_table(table[to_underlying(OperandSize::Size64)], op, mnemonic, format32, impl32, lock_prefix_allowed);
} }
template<auto table> template<auto table>
static void build_base(u8 op, char const* mnemonic16, InstructionFormat format16, InstructionHandler impl16, char const* mnemonic32, InstructionFormat format32, InstructionHandler impl32, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed) static void build_base(u8 op, char const* mnemonic16, InstructionFormat format16, InstructionHandler impl16, char const* mnemonic32, InstructionFormat format32, InstructionHandler impl32, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
{ {
build(table[to_underlying(OperandSize::Size16)], op, mnemonic16, format16, impl16, lock_prefix_allowed); build_in_table(table[to_underlying(OperandSize::Size16)], op, mnemonic16, format16, impl16, lock_prefix_allowed);
build(table[to_underlying(OperandSize::Size32)], op, mnemonic32, format32, impl32, lock_prefix_allowed); build_in_table(table[to_underlying(OperandSize::Size32)], op, mnemonic32, format32, impl32, lock_prefix_allowed);
build_in_table(table[to_underlying(OperandSize::Size64)], op, mnemonic32, format32, impl32, lock_prefix_allowed);
} }
template<auto table> template<auto table>
@ -271,6 +275,7 @@ static void build_slash_base(u8 op, u8 slash, char const* mnemonic, InstructionF
{ {
build_slash(table[to_underlying(OperandSize::Size16)], op, slash, mnemonic, format, impl, lock_prefix_allowed); build_slash(table[to_underlying(OperandSize::Size16)], op, slash, mnemonic, format, impl, lock_prefix_allowed);
build_slash(table[to_underlying(OperandSize::Size32)], op, slash, mnemonic, format, impl, lock_prefix_allowed); build_slash(table[to_underlying(OperandSize::Size32)], op, slash, mnemonic, format, impl, lock_prefix_allowed);
build_slash(table[to_underlying(OperandSize::Size64)], op, slash, mnemonic, format, impl, lock_prefix_allowed);
} }
template<auto table> template<auto table>
@ -278,6 +283,7 @@ static void build_slash_base(u8 op, u8 slash, char const* mnemonic, InstructionF
{ {
build_slash(table[to_underlying(OperandSize::Size16)], op, slash, mnemonic, format16, impl16, lock_prefix_allowed); build_slash(table[to_underlying(OperandSize::Size16)], op, slash, mnemonic, format16, impl16, lock_prefix_allowed);
build_slash(table[to_underlying(OperandSize::Size32)], op, slash, mnemonic, format32, impl32, lock_prefix_allowed); build_slash(table[to_underlying(OperandSize::Size32)], op, slash, mnemonic, format32, impl32, lock_prefix_allowed);
build_slash(table[to_underlying(OperandSize::Size64)], op, slash, mnemonic, format32, impl32, lock_prefix_allowed);
} }
template<typename... Args> template<typename... Args>
@ -308,6 +314,7 @@ static void build_slash_rm(u8 op, u8 slash, u8 rm, char const* mnemonic, Instruc
{ {
build_slash_rm(s_table[to_underlying(OperandSize::Size16)], op, slash, rm, mnemonic, format, impl); build_slash_rm(s_table[to_underlying(OperandSize::Size16)], op, slash, rm, mnemonic, format, impl);
build_slash_rm(s_table[to_underlying(OperandSize::Size32)], op, slash, rm, mnemonic, format, impl); build_slash_rm(s_table[to_underlying(OperandSize::Size32)], op, slash, rm, mnemonic, format, impl);
build_slash_rm(s_table[to_underlying(OperandSize::Size64)], op, slash, rm, mnemonic, format, impl);
} }
static void build_slash_reg(u8 op, u8 slash, char const* mnemonic, InstructionFormat format, InstructionHandler impl) static void build_slash_reg(u8 op, u8 slash, char const* mnemonic, InstructionFormat format, InstructionHandler impl)
@ -320,14 +327,14 @@ static void build_sse_np(u8 op, char const* mnemonic, InstructionFormat format,
{ {
if (s_0f_table[to_underlying(OperandSize::Size32)][op].format == InvalidFormat) { if (s_0f_table[to_underlying(OperandSize::Size32)][op].format == InvalidFormat) {
build_0f(op, mnemonic, format, impl, lock_prefix_allowed); build_0f(op, mnemonic, format, impl, lock_prefix_allowed);
build(s_sse_table_np, op, mnemonic, format, impl, lock_prefix_allowed); build_in_table(s_sse_table_np, op, mnemonic, format, impl, lock_prefix_allowed);
return; return;
} }
if (s_0f_table[to_underlying(OperandSize::Size32)][op].format != __SSE) if (s_0f_table[to_underlying(OperandSize::Size32)][op].format != __SSE)
build_0f(op, "__SSE_temp", __SSE, nullptr, lock_prefix_allowed); build_0f(op, "__SSE_temp", __SSE, nullptr, lock_prefix_allowed);
VERIFY(s_0f_table[to_underlying(OperandSize::Size32)][op].format == __SSE); VERIFY(s_0f_table[to_underlying(OperandSize::Size32)][op].format == __SSE);
build(s_sse_table_np, op, mnemonic, format, impl, lock_prefix_allowed); build_in_table(s_sse_table_np, op, mnemonic, format, impl, lock_prefix_allowed);
} }
static void build_sse_66(u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed) static void build_sse_66(u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
@ -335,7 +342,7 @@ static void build_sse_66(u8 op, char const* mnemonic, InstructionFormat format,
if (s_0f_table[to_underlying(OperandSize::Size32)][op].format != __SSE) if (s_0f_table[to_underlying(OperandSize::Size32)][op].format != __SSE)
build_0f(op, "__SSE_temp", __SSE, nullptr, lock_prefix_allowed); build_0f(op, "__SSE_temp", __SSE, nullptr, lock_prefix_allowed);
VERIFY(s_0f_table[to_underlying(AddressSize::Size32)][op].format == __SSE); VERIFY(s_0f_table[to_underlying(AddressSize::Size32)][op].format == __SSE);
build(s_sse_table_66, op, mnemonic, format, impl, lock_prefix_allowed); build_in_table(s_sse_table_66, op, mnemonic, format, impl, lock_prefix_allowed);
} }
static void build_sse_f3(u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed) static void build_sse_f3(u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
@ -343,7 +350,7 @@ static void build_sse_f3(u8 op, char const* mnemonic, InstructionFormat format,
if (s_0f_table[to_underlying(OperandSize::Size32)][op].format != __SSE) if (s_0f_table[to_underlying(OperandSize::Size32)][op].format != __SSE)
build_0f(op, "__SSE_temp", __SSE, nullptr, lock_prefix_allowed); build_0f(op, "__SSE_temp", __SSE, nullptr, lock_prefix_allowed);
VERIFY(s_0f_table[to_underlying(OperandSize::Size32)][op].format == __SSE); VERIFY(s_0f_table[to_underlying(OperandSize::Size32)][op].format == __SSE);
build(s_sse_table_f3, op, mnemonic, format, impl, lock_prefix_allowed); build_in_table(s_sse_table_f3, op, mnemonic, format, impl, lock_prefix_allowed);
} }
static void build_sse_f2(u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed) static void build_sse_f2(u8 op, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
@ -353,7 +360,7 @@ static void build_sse_f2(u8 op, char const* mnemonic, InstructionFormat format,
VERIFY(s_0f_table[to_underlying(OperandSize::Size32)][op].format == __SSE); VERIFY(s_0f_table[to_underlying(OperandSize::Size32)][op].format == __SSE);
VERIFY(s_sse_table_f2[op].format == InvalidFormat); VERIFY(s_sse_table_f2[op].format == InvalidFormat);
build(s_sse_table_f2, op, mnemonic, format, impl, lock_prefix_allowed); build_in_table(s_sse_table_f2, op, mnemonic, format, impl, lock_prefix_allowed);
} }
static void build_sse_np_slash(u8 op, u8 slash, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed) static void build_sse_np_slash(u8 op, u8 slash, char const* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
@ -864,6 +871,8 @@ static void build_sse_66_slash(u8 op, u8 slash, char const* mnemonic, Instructio
build_0f_slash(0x18, 2, "PREFETCHT1", OP_RM8, &Interpreter::PREFETCHT1); build_0f_slash(0x18, 2, "PREFETCHT1", OP_RM8, &Interpreter::PREFETCHT1);
build_0f_slash(0x18, 3, "PREFETCHT2", OP_RM8, &Interpreter::PREFETCHT2); build_0f_slash(0x18, 3, "PREFETCHT2", OP_RM8, &Interpreter::PREFETCHT2);
build_0f_slash(0x1f, 0, "NOP", OP_RM32, &Interpreter::NOP);
// FIXME: Technically NoPrefix (sse_np_slash?) // FIXME: Technically NoPrefix (sse_np_slash?)
build_0f_slash(0xAE, 2, "LDMXCSR", OP_RM32, &Interpreter::LDMXCSR); build_0f_slash(0xAE, 2, "LDMXCSR", OP_RM32, &Interpreter::LDMXCSR);
build_0f_slash(0xAE, 3, "STMXCSR", OP_RM32, &Interpreter::STMXCSR); build_0f_slash(0xAE, 3, "STMXCSR", OP_RM32, &Interpreter::STMXCSR);
@ -1203,11 +1212,59 @@ static void build_sse_66_slash(u8 op, u8 slash, char const* mnemonic, Instructio
build_0f(0xFD, "PADDW", OP_mm1_mm2m64, &Interpreter::PADDW_mm1_mm2m64); build_0f(0xFD, "PADDW", OP_mm1_mm2m64, &Interpreter::PADDW_mm1_mm2m64);
build_0f(0xFE, "PADDD", OP_mm1_mm2m64, &Interpreter::PADDD_mm1_mm2m64); build_0f(0xFE, "PADDD", OP_mm1_mm2m64, &Interpreter::PADDD_mm1_mm2m64);
build_0f(0xFF, "UD0", OP, &Interpreter::UD0); build_0f(0xFF, "UD0", OP, &Interpreter::UD0);
// Changes between 32-bit and 64-bit. These are marked with i64/d64/f64 in the Intel manual's opcode tables
auto* table64 = s_table[to_underlying(OperandSize::Size64)];
table64[0x06] = {}; // PUSH ES
table64[0x07] = {}; // POP ES
table64[0x16] = {}; // PUSH SS
table64[0x17] = {}; // POP SS
table64[0x27] = {}; // DAA
table64[0x37] = {}; // AAA
for (u8 rex = 0x40; rex < 0x50; rex++)
table64[rex] = {}; // INC/DEC, replaced by REX prefixes
for (u8 pushPop = 0x50; pushPop < 0x60; pushPop++)
table64[pushPop].long_mode_default_64 = true; // PUSH/POP general register
for (u8 i = 0x60; i < 0x68; i++)
table64[i] = {}; // PUSHA{D}, POPA{D}, BOUND
// ARPL replaced by MOVSXD
build_in_table(table64, 0x63, "MOVSXD", OP_RM32_reg32, nullptr, LockPrefixNotAllowed);
table64[0x68].long_mode_default_64 = true; // PUSH
table64[0x6A].long_mode_default_64 = true; // PUSH
for (u8 jmp = 0x70; jmp < 0x80; jmp++)
table64[jmp].long_mode_force_64 = true; // Jcc
table64[0x9A] = {}; // far CALL
table64[0x9C].long_mode_default_64 = true; // PUSHF/D/Q
table64[0x9D].long_mode_default_64 = true; // POPF/D/Q
build_in_table(table64, 0xB8, "MOV", OP_regW_immW, &Interpreter::MOV_reg32_imm32, LockPrefixNotAllowed);
table64[0xC2].long_mode_force_64 = true; // near RET
table64[0xC3].long_mode_force_64 = true; // near RET
table64[0xC4] = {}; // LES
table64[0xC5] = {}; // LDS
table64[0xC9].long_mode_default_64 = true; // LEAVE
table64[0xCE].long_mode_default_64 = true; // INTO
table64[0xD4] = {}; // AAM
table64[0xD5] = {}; // AAD
for (u8 i = 0; i < 4; i++) {
table64[0xE0 | i].long_mode_force_64 = true; // LOOPN[EZ], LOOP[EZ], LOOP, JrCXZ
table64[0xE8 | i].long_mode_force_64 = true; // near CALL, {near,far,short} JMP
}
auto* table64_0f = s_0f_table[to_underlying(OperandSize::Size64)];
build_in_table(table64_0f, 0x05, "SYSCALL", OP, nullptr, LockPrefixNotAllowed);
build_in_table(table64_0f, 0x07, "SYSRET", OP, nullptr, LockPrefixNotAllowed);
for (u8 i = 0x80; i < 0x90; i++)
table64_0f[i].long_mode_force_64 = true; // Jcc
table64_0f[0xA0].long_mode_default_64 = true; // PUSH FS
table64_0f[0xA1].long_mode_default_64 = true; // POP FS
table64_0f[0xA8].long_mode_default_64 = true; // PUSH GS
table64_0f[0xA9].long_mode_default_64 = true; // POP GS
} }
static StringView register_name(RegisterIndex8); static StringView register_name(RegisterIndex8);
static StringView register_name(RegisterIndex16); static StringView register_name(RegisterIndex16);
static StringView register_name(RegisterIndex32); static StringView register_name(RegisterIndex32);
static StringView register_name(RegisterIndex64);
static StringView register_name(FpuRegisterIndex); static StringView register_name(FpuRegisterIndex);
static StringView register_name(SegmentRegister); static StringView register_name(SegmentRegister);
static StringView register_name(MMXRegisterIndex); static StringView register_name(MMXRegisterIndex);
@ -1228,6 +1285,11 @@ StringView Instruction::reg32_name() const
return register_name(static_cast<RegisterIndex32>(register_index())); return register_name(static_cast<RegisterIndex32>(register_index()));
} }
StringView Instruction::reg64_name() const
{
return register_name(static_cast<RegisterIndex64>(register_index()));
}
String MemoryOrRegisterReference::to_string_o8(Instruction const& insn) const String MemoryOrRegisterReference::to_string_o8(Instruction const& insn) const
{ {
if (is_register()) if (is_register())
@ -1249,6 +1311,13 @@ String MemoryOrRegisterReference::to_string_o32(Instruction const& insn) const
return String::formatted("[{}]", to_string(insn)); return String::formatted("[{}]", to_string(insn));
} }
String MemoryOrRegisterReference::to_string_o64(Instruction const& insn) const
{
if (is_register())
return register_name(reg64());
return String::formatted("[{}]", to_string(insn));
}
String MemoryOrRegisterReference::to_string_fpu_reg() const String MemoryOrRegisterReference::to_string_fpu_reg() const
{ {
VERIFY(is_register()); VERIFY(is_register());
@ -1312,7 +1381,7 @@ String MemoryOrRegisterReference::to_string(Instruction const& insn) const
case AddressSize::Size64: case AddressSize::Size64:
return to_string_a64(); return to_string_a64();
case AddressSize::Size32: case AddressSize::Size32:
return to_string_a32(); return insn.mode() == ProcessorMode::Long ? to_string_a64() : to_string_a32();
case AddressSize::Size16: case AddressSize::Size16:
return to_string_a16(); return to_string_a16();
} }
@ -1371,7 +1440,7 @@ String MemoryOrRegisterReference::to_string_a16() const
return String::formatted("{}{}", base, displacement_string); return String::formatted("{}{}", base, displacement_string);
} }
String MemoryOrRegisterReference::sib_to_string(ProcessorMode) const String MemoryOrRegisterReference::sib_to_string(ProcessorMode mode) const
{ {
String scale; String scale;
String index; String index;
@ -1390,16 +1459,16 @@ String MemoryOrRegisterReference::sib_to_string(ProcessorMode) const
break; break;
} }
if (m_sib_index != 4) if (m_sib_index != 4)
index = register_name(RegisterIndex32(m_sib_index)); index = mode == ProcessorMode::Long ? register_name(RegisterIndex64(m_sib_index)) : register_name(RegisterIndex32(m_sib_index));
if (m_sib_base == 5) { if (m_sib_base == 5) {
switch (m_reg) { switch (m_reg) {
case 1: case 1:
case 2: case 2:
base = "ebp"; base = mode == ProcessorMode::Long ? "rbp" : "ebp";
break; break;
} }
} else { } else {
base = register_name(RegisterIndex32(m_sib_base)); base = mode == ProcessorMode::Long ? register_name(RegisterIndex64(m_sib_base)) : register_name(RegisterIndex32(m_sib_base));
} }
StringBuilder builder; StringBuilder builder;
if (base.is_empty()) { if (base.is_empty()) {
@ -1418,7 +1487,7 @@ String MemoryOrRegisterReference::sib_to_string(ProcessorMode) const
String MemoryOrRegisterReference::to_string_a64() const String MemoryOrRegisterReference::to_string_a64() const
{ {
if (is_register()) if (is_register())
return register_name(static_cast<RegisterIndex32>(m_register_index)); return register_name(static_cast<RegisterIndex64>(m_register_index));
bool has_displacement = false; bool has_displacement = false;
switch (mod()) { switch (mod()) {
@ -1436,7 +1505,7 @@ String MemoryOrRegisterReference::to_string_a64() const
switch (m_rm) { switch (m_rm) {
case 5: case 5:
if (mod() == 0) if (mod() == 0)
base = String::formatted("{:#08x}", m_displacement32); base = "rip";
else else
base = "rbp"; base = "rbp";
break; break;
@ -1444,7 +1513,7 @@ String MemoryOrRegisterReference::to_string_a64() const
base = sib_to_string(ProcessorMode::Long); base = sib_to_string(ProcessorMode::Long);
break; break;
default: default:
base = register_name(RegisterIndex32(m_rm)); base = register_name(RegisterIndex64(m_rm));
} }
if (!has_displacement) if (!has_displacement)
@ -1583,9 +1652,13 @@ void Instruction::to_string_internal(StringBuilder& builder, u32 origin, SymbolP
auto append_rm8 = [&] { builder.append(m_modrm.to_string_o8(*this)); }; auto append_rm8 = [&] { builder.append(m_modrm.to_string_o8(*this)); };
auto append_rm16 = [&] { builder.append(m_modrm.to_string_o16(*this)); }; auto append_rm16 = [&] { builder.append(m_modrm.to_string_o16(*this)); };
auto append_rm32 = [&] { builder.append(m_modrm.to_string_o32(*this)); }; auto append_rm32 = [&] {
// FIXME: Registers in long-mode if (m_operand_size == OperandSize::Size64)
auto append_rm64 = [&] { builder.append(m_modrm.to_string_o32(*this)); }; builder.append(m_modrm.to_string_o64(*this));
else
builder.append(m_modrm.to_string_o32(*this));
};
auto append_rm64 = [&] { builder.append(m_modrm.to_string_o64(*this)); };
auto append_fpu_reg = [&] { builder.append(m_modrm.to_string_fpu_reg()); }; auto append_fpu_reg = [&] { builder.append(m_modrm.to_string_fpu_reg()); };
auto append_fpu_mem = [&] { builder.append(m_modrm.to_string_fpu_mem(*this)); }; auto append_fpu_mem = [&] { builder.append(m_modrm.to_string_fpu_mem(*this)); };
auto append_fpu_ax16 = [&] { builder.append(m_modrm.to_string_fpu_ax16()); }; auto append_fpu_ax16 = [&] { builder.append(m_modrm.to_string_fpu_ax16()); };
@ -1609,16 +1682,24 @@ void Instruction::to_string_internal(StringBuilder& builder, u32 origin, SymbolP
}; };
auto append_reg8 = [&] { builder.append(reg8_name()); }; auto append_reg8 = [&] { builder.append(reg8_name()); };
auto append_reg16 = [&] { builder.append(reg16_name()); }; auto append_reg16 = [&] { builder.append(reg16_name()); };
auto append_reg32 = [&] { builder.append(reg32_name()); }; auto append_reg32 = [&] {
if (m_operand_size == OperandSize::Size64)
builder.append(reg64_name());
else
builder.append(reg32_name());
};
auto append_seg = [&] { builder.append(register_name(segment_register())); }; auto append_seg = [&] { builder.append(register_name(segment_register())); };
auto append_creg = [&] { builder.appendff("cr{}", register_index()); }; auto append_creg = [&] { builder.appendff("cr{}", register_index()); };
auto append_dreg = [&] { builder.appendff("dr{}", register_index()); }; auto append_dreg = [&] { builder.appendff("dr{}", register_index()); };
auto append_relative_addr = [&] { auto append_relative_addr = [&] {
if (m_address_size == AddressSize::Size32) { switch (m_address_size) {
formatted_address(origin + 6, x32, i32(imm32())); case AddressSize::Size16:
} else if (m_address_size == AddressSize::Size16) {
formatted_address(origin + 4, x32, i32(imm16())); formatted_address(origin + 4, x32, i32(imm16()));
} else { break;
case AddressSize::Size32:
formatted_address(origin + 6, x32, i32(imm32()));
break;
default:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
}; };
@ -1653,7 +1734,9 @@ void Instruction::to_string_internal(StringBuilder& builder, u32 origin, SymbolP
auto append = [&](auto content) { builder.append(content); }; auto append = [&](auto content) { builder.append(content); };
auto append_moff = [&] { auto append_moff = [&] {
builder.append('['); builder.append('[');
if (m_address_size == AddressSize::Size32) { if (m_address_size == AddressSize::Size64) {
append_imm64();
} else if (m_address_size == AddressSize::Size32) {
append_imm32(); append_imm32();
} else if (m_address_size == AddressSize::Size16) { } else if (m_address_size == AddressSize::Size16) {
append_imm16(); append_imm16();
@ -2428,20 +2511,26 @@ StringView register_name(SegmentRegister index)
StringView register_name(RegisterIndex8 register_index) StringView register_name(RegisterIndex8 register_index)
{ {
static constexpr StringView names[] = { "al"sv, "cl"sv, "dl"sv, "bl"sv, "ah"sv, "ch"sv, "dh"sv, "bh"sv }; static constexpr StringView names[] = { "al"sv, "cl"sv, "dl"sv, "bl"sv, "ah"sv, "ch"sv, "dh"sv, "bh"sv, "r8b"sv, "r9b"sv, "r10b"sv, "r11b"sv, "r12b"sv, "r13b"sv, "r14b"sv, "r15b"sv };
return names[register_index & 7]; return names[register_index & 15];
} }
StringView register_name(RegisterIndex16 register_index) StringView register_name(RegisterIndex16 register_index)
{ {
static constexpr StringView names[] = { "ax"sv, "cx"sv, "dx"sv, "bx"sv, "sp"sv, "bp"sv, "si"sv, "di"sv }; static constexpr StringView names[] = { "ax"sv, "cx"sv, "dx"sv, "bx"sv, "sp"sv, "bp"sv, "si"sv, "di"sv, "r8w"sv, "r9w"sv, "r10w"sv, "r11w"sv, "r12w"sv, "r13w"sv, "r14w"sv, "r15w"sv };
return names[register_index & 7]; return names[register_index & 15];
} }
StringView register_name(RegisterIndex32 register_index) StringView register_name(RegisterIndex32 register_index)
{ {
static constexpr StringView names[] = { "eax"sv, "ecx"sv, "edx"sv, "ebx"sv, "esp"sv, "ebp"sv, "esi"sv, "edi"sv }; static constexpr StringView names[] = { "eax"sv, "ecx"sv, "edx"sv, "ebx"sv, "esp"sv, "ebp"sv, "esi"sv, "edi"sv, "r8d"sv, "r9d"sv, "r10d"sv, "r11d"sv, "r12d"sv, "r13d"sv, "r14d"sv, "r15d"sv };
return names[register_index & 7]; return names[register_index & 15];
}
StringView register_name(RegisterIndex64 register_index)
{
static constexpr StringView names[] = { "rax"sv, "rcx"sv, "rdx"sv, "rbx"sv, "rsp"sv, "rbp"sv, "rsi"sv, "rdi"sv, "r8"sv, "r9"sv, "r10"sv, "r11"sv, "r12"sv, "r13"sv, "r14"sv, "r15"sv };
return names[register_index & 15];
} }
StringView register_name(FpuRegisterIndex register_index) StringView register_name(FpuRegisterIndex register_index)
@ -2458,8 +2547,8 @@ StringView register_name(MMXRegisterIndex register_index)
StringView register_name(XMMRegisterIndex register_index) StringView register_name(XMMRegisterIndex register_index)
{ {
static constexpr StringView names[] = { "xmm0"sv, "xmm1"sv, "xmm2"sv, "xmm3"sv, "xmm4"sv, "xmm5"sv, "xmm6"sv, "xmm7"sv }; static constexpr StringView names[] = { "xmm0"sv, "xmm1"sv, "xmm2"sv, "xmm3"sv, "xmm4"sv, "xmm5"sv, "xmm6"sv, "xmm7"sv, "xmm8"sv, "xmm9"sv, "xmm10"sv, "xmm11"sv, "xmm12"sv, "xmm13"sv, "xmm14"sv, "xmm15"sv };
return names[register_index & 7]; return names[register_index & 15];
} }
} }

View file

@ -226,6 +226,8 @@ struct InstructionDescriptor {
bool has_rm { false }; bool has_rm { false };
unsigned imm1_bytes { 0 }; unsigned imm1_bytes { 0 };
unsigned imm2_bytes { 0 }; unsigned imm2_bytes { 0 };
bool long_mode_default_64 { false };
bool long_mode_force_64 { false };
// Addressed by the 3 REG bits in the MOD-REG-R/M byte. // Addressed by the 3 REG bits in the MOD-REG-R/M byte.
// Some slash instructions have further subgroups when MOD is 11, // Some slash instructions have further subgroups when MOD is 11,
@ -233,10 +235,10 @@ struct InstructionDescriptor {
// a non-null slashes member that's indexed by the three R/M bits. // a non-null slashes member that's indexed by the three R/M bits.
InstructionDescriptor* slashes { nullptr }; InstructionDescriptor* slashes { nullptr };
unsigned imm1_bytes_for_address_size(AddressSize size) const unsigned imm1_bytes_for(AddressSize address_size, OperandSize operand_size) const
{ {
if (imm1_bytes == CurrentAddressSize) { if (imm1_bytes == CurrentAddressSize) {
switch (size) { switch (address_size) {
case AddressSize::Size64: case AddressSize::Size64:
return 8; return 8;
case AddressSize::Size32: case AddressSize::Size32:
@ -246,13 +248,24 @@ struct InstructionDescriptor {
} }
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
if (imm1_bytes == CurrentOperandSize) {
switch (operand_size) {
case OperandSize::Size64:
return 8;
case OperandSize::Size32:
return 4;
case OperandSize::Size16:
return 2;
}
VERIFY_NOT_REACHED();
}
return imm1_bytes; return imm1_bytes;
} }
unsigned imm2_bytes_for_address_size(AddressSize size) const unsigned imm2_bytes_for(AddressSize address_size, OperandSize operand_size) const
{ {
if (imm2_bytes == CurrentAddressSize) { if (imm2_bytes == CurrentAddressSize) {
switch (size) { switch (address_size) {
case AddressSize::Size64: case AddressSize::Size64:
return 8; return 8;
case AddressSize::Size32: case AddressSize::Size32:
@ -262,6 +275,17 @@ struct InstructionDescriptor {
} }
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
if (imm2_bytes == CurrentOperandSize) {
switch (operand_size) {
case OperandSize::Size64:
return 8;
case OperandSize::Size32:
return 4;
case OperandSize::Size16:
return 2;
}
VERIFY_NOT_REACHED();
}
return imm2_bytes; return imm2_bytes;
} }
@ -276,6 +300,8 @@ extern InstructionDescriptor s_sse_table_f3[256];
struct Prefix { struct Prefix {
enum Op { enum Op {
REX_Mask = 0xf0,
REX_Base = 0x40,
OperandSizeOverride = 0x66, OperandSizeOverride = 0x66,
AddressSizeOverride = 0x67, AddressSizeOverride = 0x67,
REP = 0xf3, REP = 0xf3,
@ -304,7 +330,15 @@ enum RegisterIndex8 {
RegisterAH, RegisterAH,
RegisterCH, RegisterCH,
RegisterDH, RegisterDH,
RegisterBH RegisterBH,
RegisterR8B,
RegisterR9B,
RegisterR10B,
RegisterR11B,
RegisterR12B,
RegisterR13B,
RegisterR14B,
RegisterR15B,
}; };
enum RegisterIndex16 { enum RegisterIndex16 {
@ -315,7 +349,15 @@ enum RegisterIndex16 {
RegisterSP, RegisterSP,
RegisterBP, RegisterBP,
RegisterSI, RegisterSI,
RegisterDI RegisterDI,
RegisterR8W,
RegisterR9W,
RegisterR10W,
RegisterR11W,
RegisterR12W,
RegisterR13W,
RegisterR14W,
RegisterR15W,
}; };
enum RegisterIndex32 { enum RegisterIndex32 {
@ -326,7 +368,34 @@ enum RegisterIndex32 {
RegisterESP, RegisterESP,
RegisterEBP, RegisterEBP,
RegisterESI, RegisterESI,
RegisterEDI RegisterEDI,
RegisterR8D,
RegisterR9D,
RegisterR10D,
RegisterR11D,
RegisterR12D,
RegisterR13D,
RegisterR14D,
RegisterR15D,
};
enum RegisterIndex64 {
RegisterRAX = 0,
RegisterRCX,
RegisterRDX,
RegisterRBX,
RegisterRSP,
RegisterRBP,
RegisterRSI,
RegisterRDI,
RegisterR8,
RegisterR9,
RegisterR10,
RegisterR11,
RegisterR12,
RegisterR13,
RegisterR14,
RegisterR15,
}; };
enum FpuRegisterIndex { enum FpuRegisterIndex {
@ -359,7 +428,15 @@ enum XMMRegisterIndex {
RegisterXMM4, RegisterXMM4,
RegisterXMM5, RegisterXMM5,
RegisterXMM6, RegisterXMM6,
RegisterXMM7 RegisterXMM7,
RegisterXMM8,
RegisterXMM9,
RegisterXMM10,
RegisterXMM11,
RegisterXMM12,
RegisterXMM13,
RegisterXMM14,
RegisterXMM15,
}; };
class LogicalAddress { class LogicalAddress {
@ -444,6 +521,7 @@ public:
String to_string_o8(Instruction const&) const; String to_string_o8(Instruction const&) const;
String to_string_o16(Instruction const&) const; String to_string_o16(Instruction const&) const;
String to_string_o32(Instruction const&) const; String to_string_o32(Instruction const&) const;
String to_string_o64(Instruction const&) const;
String to_string_fpu_reg() const; String to_string_fpu_reg() const;
String to_string_fpu_mem(Instruction const&) const; String to_string_fpu_mem(Instruction const&) const;
String to_string_fpu_ax16() const; String to_string_fpu_ax16() const;
@ -458,6 +536,7 @@ public:
bool is_register() const { return m_register_index != 0x7f; } bool is_register() const { return m_register_index != 0x7f; }
unsigned register_index() const { return m_register_index; } unsigned register_index() const { return m_register_index; }
RegisterIndex64 reg64() const { return static_cast<RegisterIndex64>(register_index()); }
RegisterIndex32 reg32() const { return static_cast<RegisterIndex32>(register_index()); } RegisterIndex32 reg32() const { return static_cast<RegisterIndex32>(register_index()); }
RegisterIndex16 reg16() const { return static_cast<RegisterIndex16>(register_index()); } RegisterIndex16 reg16() const { return static_cast<RegisterIndex16>(register_index()); }
RegisterIndex8 reg8() const { return static_cast<RegisterIndex8>(register_index()); } RegisterIndex8 reg8() const { return static_cast<RegisterIndex8>(register_index()); }
@ -507,11 +586,11 @@ private:
String to_string_a64() const; String to_string_a64() const;
template<typename InstructionStreamType> template<typename InstructionStreamType>
void decode(InstructionStreamType&, AddressSize); void decode(InstructionStreamType&, AddressSize, bool has_rex_r, bool has_rex_x, bool has_rex_b);
template<typename InstructionStreamType> template<typename InstructionStreamType>
void decode16(InstructionStreamType&); void decode16(InstructionStreamType&);
template<typename InstructionStreamType> template<typename InstructionStreamType>
void decode32(InstructionStreamType&); void decode32(InstructionStreamType&, bool has_rex_r, bool has_rex_x, bool has_rex_b);
template<typename CPU> template<typename CPU>
LogicalAddress resolve16(const CPU&, Optional<SegmentRegister>); LogicalAddress resolve16(const CPU&, Optional<SegmentRegister>);
template<typename CPU> template<typename CPU>
@ -613,6 +692,8 @@ public:
u8 cc() const { return has_sub_op() ? m_sub_op & 0xf : m_op & 0xf; } u8 cc() const { return has_sub_op() ? m_sub_op & 0xf : m_op & 0xf; }
AddressSize address_size() const { return m_address_size; } AddressSize address_size() const { return m_address_size; }
OperandSize operand_size() const { return m_operand_size; }
ProcessorMode mode() const { return m_mode; }
String to_string(u32 origin, SymbolProvider const* = nullptr, bool x32 = true) const; String to_string(u32 origin, SymbolProvider const* = nullptr, bool x32 = true) const;
@ -625,6 +706,7 @@ private:
StringView reg8_name() const; StringView reg8_name() const;
StringView reg16_name() const; StringView reg16_name() const;
StringView reg32_name() const; StringView reg32_name() const;
StringView reg64_name() const;
InstructionDescriptor* m_descriptor { nullptr }; InstructionDescriptor* m_descriptor { nullptr };
mutable MemoryOrRegisterReference m_modrm; mutable MemoryOrRegisterReference m_modrm;
@ -638,9 +720,14 @@ private:
u8 m_rep_prefix { 0 }; u8 m_rep_prefix { 0 };
OperandSize m_operand_size { OperandSize::Size16 }; OperandSize m_operand_size { OperandSize::Size16 };
AddressSize m_address_size { AddressSize::Size16 }; AddressSize m_address_size { AddressSize::Size16 };
ProcessorMode m_mode { ProcessorMode::Protected };
bool m_has_lock_prefix : 1 { false }; bool m_has_lock_prefix : 1 { false };
bool m_has_operand_size_override_prefix : 1 { false }; bool m_has_operand_size_override_prefix : 1 { false };
bool m_has_address_size_override_prefix : 1 { false }; bool m_has_address_size_override_prefix : 1 { false };
bool m_has_rex_w : 1 { false };
bool m_has_rex_r : 1 { false };
bool m_has_rex_x : 1 { false };
bool m_has_rex_b : 1 { false };
}; };
template<typename CPU> template<typename CPU>
@ -723,7 +810,7 @@ ALWAYS_INLINE u32 MemoryOrRegisterReference::evaluate_sib(const CPU& cpu, Segmen
u32 index = 0; u32 index = 0;
switch (m_sib_index) { switch (m_sib_index) {
case 0 ... 3: case 0 ... 3:
case 5 ... 7: case 5 ... 15:
index = cpu.const_gpr32((RegisterIndex32)m_sib_index).value(); index = cpu.const_gpr32((RegisterIndex32)m_sib_index).value();
break; break;
case 4: case 4:
@ -734,7 +821,7 @@ ALWAYS_INLINE u32 MemoryOrRegisterReference::evaluate_sib(const CPU& cpu, Segmen
u32 base = m_displacement32; u32 base = m_displacement32;
switch (m_sib_base) { switch (m_sib_base) {
case 0 ... 3: case 0 ... 3:
case 6 ... 7: case 6 ... 15:
base += cpu.const_gpr32((RegisterIndex32)m_sib_base).value(); base += cpu.const_gpr32((RegisterIndex32)m_sib_base).value();
break; break;
case 4: case 4:
@ -752,7 +839,6 @@ ALWAYS_INLINE u32 MemoryOrRegisterReference::evaluate_sib(const CPU& cpu, Segmen
break; break;
default: default:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
break;
} }
break; break;
} }
@ -920,21 +1006,29 @@ ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, OperandSiz
: m_operand_size(operand_size) : m_operand_size(operand_size)
, m_address_size(address_size) , m_address_size(address_size)
{ {
VERIFY(operand_size != OperandSize::Size64);
// Use address_size as the hint to switch into long mode, m_address_size refers to the default
// size of displacements/immediates, which is 32 even in long mode, with the exception of moffset (see below).
if (address_size == AddressSize::Size64) {
m_operand_size = OperandSize::Size32;
m_address_size = AddressSize::Size64;
m_mode = ProcessorMode::Long;
}
u8 prefix_bytes = 0; u8 prefix_bytes = 0;
for (;; ++prefix_bytes) { for (;; ++prefix_bytes) {
u8 opbyte = stream.read8(); u8 opbyte = stream.read8();
if (opbyte == Prefix::OperandSizeOverride) { if (opbyte == Prefix::OperandSizeOverride) {
if (operand_size == OperandSize::Size32) if (m_operand_size == OperandSize::Size32)
m_operand_size = OperandSize::Size16; m_operand_size = OperandSize::Size16;
else if (operand_size == OperandSize::Size16) else if (m_operand_size == OperandSize::Size16)
m_operand_size = OperandSize::Size32; m_operand_size = OperandSize::Size32;
m_has_operand_size_override_prefix = true; m_has_operand_size_override_prefix = true;
continue; continue;
} }
if (opbyte == Prefix::AddressSizeOverride) { if (opbyte == Prefix::AddressSizeOverride) {
if (address_size == AddressSize::Size32) if (m_address_size == AddressSize::Size32)
m_address_size = AddressSize::Size16; m_address_size = AddressSize::Size16;
else if (address_size == AddressSize::Size16) else if (m_address_size == AddressSize::Size16)
m_address_size = AddressSize::Size32; m_address_size = AddressSize::Size32;
m_has_address_size_override_prefix = true; m_has_address_size_override_prefix = true;
continue; continue;
@ -947,6 +1041,15 @@ ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, OperandSiz
m_has_lock_prefix = true; m_has_lock_prefix = true;
continue; continue;
} }
if (m_mode == ProcessorMode::Long && (opbyte & Prefix::REX_Mask) == Prefix::REX_Base) {
m_has_rex_w = opbyte & 8;
if (m_has_rex_w)
m_operand_size = OperandSize::Size64;
m_has_rex_r = opbyte & 4;
m_has_rex_x = opbyte & 2;
m_has_rex_b = opbyte & 1;
continue;
}
auto segment_prefix = to_segment_prefix(opbyte); auto segment_prefix = to_segment_prefix(opbyte);
if (segment_prefix.has_value()) { if (segment_prefix.has_value()) {
m_segment_prefix = (u8)segment_prefix.value(); m_segment_prefix = (u8)segment_prefix.value();
@ -956,11 +1059,14 @@ ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, OperandSiz
break; break;
} }
u8 table_index = to_underlying(m_operand_size);
if (m_mode == ProcessorMode::Long && m_operand_size == OperandSize::Size32)
table_index = to_underlying(OperandSize::Size64);
if (m_op == 0x0f) { if (m_op == 0x0f) {
m_sub_op = stream.read8(); m_sub_op = stream.read8();
m_descriptor = &s_0f_table[to_underlying(m_operand_size)][m_sub_op]; m_descriptor = &s_0f_table[table_index][m_sub_op];
} else { } else {
m_descriptor = &s_table[to_underlying(m_operand_size)][m_op]; m_descriptor = &s_table[table_index][m_op];
} }
if (m_descriptor->format == __SSE) { if (m_descriptor->format == __SSE) {
@ -977,13 +1083,21 @@ ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, OperandSiz
if (m_descriptor->has_rm) { if (m_descriptor->has_rm) {
// Consume ModR/M (may include SIB and displacement.) // Consume ModR/M (may include SIB and displacement.)
m_modrm.decode(stream, m_address_size); m_modrm.decode(stream, m_address_size, m_has_rex_r, m_has_rex_x, m_has_rex_b);
m_register_index = m_modrm.reg(); m_register_index = m_modrm.reg();
} else { } else {
if (has_sub_op()) if (has_sub_op())
m_register_index = m_sub_op & 7; m_register_index = m_sub_op & 7;
else else
m_register_index = m_op & 7; m_register_index = m_op & 7;
if (m_has_rex_b)
m_register_index |= 8;
}
if (m_mode == ProcessorMode::Long && (m_descriptor->long_mode_force_64 || m_descriptor->long_mode_default_64)) {
m_operand_size = OperandSize::Size64;
if (!m_descriptor->long_mode_force_64 && m_has_operand_size_override_prefix)
m_operand_size = OperandSize::Size32;
} }
bool has_slash = m_descriptor->format == MultibyteWithSlash; bool has_slash = m_descriptor->format == MultibyteWithSlash;
@ -1010,8 +1124,22 @@ ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, OperandSiz
return; return;
} }
auto imm1_bytes = m_descriptor->imm1_bytes_for_address_size(m_address_size); // 2.2.1.3 Direct Memory-Offset MOVs
auto imm2_bytes = m_descriptor->imm2_bytes_for_address_size(m_address_size); auto effective_address_size = m_address_size;
if (m_mode == ProcessorMode::Long) {
switch (m_descriptor->format) {
case OP_AL_moff8: // A0 MOV AL, moffset
case OP_EAX_moff32: // A1 MOV EAX, moffset
case OP_moff8_AL: // A2 MOV moffset, AL
case OP_moff32_EAX: // A3 MOV moffset, EAX
effective_address_size = AddressSize::Size64;
break;
default:
break;
}
}
auto imm1_bytes = m_descriptor->imm1_bytes_for(effective_address_size, m_operand_size);
auto imm2_bytes = m_descriptor->imm2_bytes_for(effective_address_size, m_operand_size);
// Consume immediates if present. // Consume immediates if present.
switch (imm2_bytes) { switch (imm2_bytes) {
@ -1024,6 +1152,9 @@ ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, OperandSiz
case 4: case 4:
m_imm2 = stream.read32(); m_imm2 = stream.read32();
break; break;
case 8:
m_imm2 = stream.read64();
break;
default: default:
VERIFY(imm2_bytes == 0); VERIFY(imm2_bytes == 0);
break; break;
@ -1039,6 +1170,9 @@ ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, OperandSiz
case 4: case 4:
m_imm1 = stream.read32(); m_imm1 = stream.read32();
break; break;
case 8:
m_imm1 = stream.read64();
break;
default: default:
VERIFY(imm1_bytes == 0); VERIFY(imm1_bytes == 0);
break; break;
@ -1055,7 +1189,7 @@ ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, OperandSiz
} }
template<typename InstructionStreamType> template<typename InstructionStreamType>
ALWAYS_INLINE void MemoryOrRegisterReference::decode(InstructionStreamType& stream, AddressSize address_size) ALWAYS_INLINE void MemoryOrRegisterReference::decode(InstructionStreamType& stream, AddressSize address_size, bool has_rex_r, bool has_rex_x, bool has_rex_b)
{ {
u8 mod_rm_byte = stream.read8(); u8 mod_rm_byte = stream.read8();
m_mod = mod_rm_byte >> 6; m_mod = mod_rm_byte >> 6;
@ -1063,7 +1197,7 @@ ALWAYS_INLINE void MemoryOrRegisterReference::decode(InstructionStreamType& stre
m_rm = mod_rm_byte & 7; m_rm = mod_rm_byte & 7;
if (address_size == AddressSize::Size32) { if (address_size == AddressSize::Size32) {
decode32(stream); decode32(stream, has_rex_r, has_rex_x, has_rex_b);
switch (m_displacement_bytes) { switch (m_displacement_bytes) {
case 0: case 0:
break; break;
@ -1118,8 +1252,10 @@ ALWAYS_INLINE void MemoryOrRegisterReference::decode16(InstructionStreamType&)
} }
template<typename InstructionStreamType> template<typename InstructionStreamType>
ALWAYS_INLINE void MemoryOrRegisterReference::decode32(InstructionStreamType& stream) ALWAYS_INLINE void MemoryOrRegisterReference::decode32(InstructionStreamType& stream, bool has_rex_r, bool has_rex_x, bool has_rex_b)
{ {
m_reg |= has_rex_r << 3;
switch (m_mod) { switch (m_mod) {
case 0b00: case 0b00:
if (m_rm == 5) { if (m_rm == 5) {
@ -1134,6 +1270,7 @@ ALWAYS_INLINE void MemoryOrRegisterReference::decode32(InstructionStreamType& st
m_displacement_bytes = 4; m_displacement_bytes = 4;
break; break;
case 0b11: case 0b11:
m_rm |= has_rex_b << 3;
m_register_index = rm(); m_register_index = rm();
return; return;
} }
@ -1142,8 +1279,8 @@ ALWAYS_INLINE void MemoryOrRegisterReference::decode32(InstructionStreamType& st
if (m_has_sib) { if (m_has_sib) {
u8 sib_byte = stream.read8(); u8 sib_byte = stream.read8();
m_sib_scale = sib_byte >> 6; m_sib_scale = sib_byte >> 6;
m_sib_index = (sib_byte >> 3) & 7; m_sib_index = (has_rex_x << 3) | ((sib_byte >> 3) & 7);
m_sib_base = sib_byte & 7; m_sib_base = (has_rex_b << 3) | (sib_byte & 7);
if (m_sib_base == 5) { if (m_sib_base == 5) {
switch (mod()) { switch (mod()) {
case 0b00: case 0b00:
@ -1159,6 +1296,8 @@ ALWAYS_INLINE void MemoryOrRegisterReference::decode32(InstructionStreamType& st
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
} }
} else {
m_rm |= has_rex_b << 3;
} }
} }