/* * Copyright (c) 2022 Trail of Bits, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #if defined(__x86_64__) # include "remill/Arch/X86/Runtime/State.h" # define REMILL_HYPERCALL_AMD64 1 #elif defined(__i386__) || defined(_M_X86) # include "remill/Arch/X86/Runtime/State.h" # define REMILL_HYPERCALL_X86 1 #elif defined(__arm__) # include "remill/Arch/AArch32/Runtime/State.h" # define REMILL_HYPERCALL_ARM 1 #elif defined(__aarch64__) # include "remill/Arch/AArch64/Runtime/State.h" # define REMILL_HYPERCALL_AARCH64 1 #elif defined(__sparc__) # if ADDRESS_SIZE_BITS == 32 # include "remill/Arch/SPARC32/Runtime/State.h" # define REMILL_HYPERCALL_SPARC32 1 # elif ADDRESS_SIZE_BITS == 64 # include "remill/Arch/SPARC64/Runtime/State.h" # define REMILL_HYPERCALL_SPARC64 1 # else # error "Cannot deduce hyper call SPARC variant" # endif #elif defined(__PPC__) # include "remill/Arch/PPC/Runtime/State.h" # define REMILL_HYPERCALL_PPC 1 #else # error "Cannot deduce hyper call architecture" #endif #include "remill/Arch/Runtime/Intrinsics.h" Memory *__remill_sync_hyper_call(State &state, Memory *mem, SyncHyperCall::Name call) { #if REMILL_HYPERCALL_X86 register uint32_t esp asm("esp") = state.gpr.rsp.dword; register uint32_t ebp asm("ebp") = state.gpr.rbp.dword; #elif REMILL_HYPERCALL_AMD64 register uint64_t rsp asm("rsp") = state.gpr.rsp.qword; register uint64_t rbp asm("rbp") = state.gpr.rbp.qword; register uint64_t r8 asm("r8") = state.gpr.r8.qword; register uint64_t r9 asm("r9") = state.gpr.r9.qword; register uint64_t r10 asm("r10") = state.gpr.r10.qword; register uint64_t r11 asm("r11") = state.gpr.r11.qword; register uint64_t r12 asm("r12") = state.gpr.r12.qword; register uint64_t r13 asm("r13") = state.gpr.r13.qword; register uint64_t r14 asm("r14") = state.gpr.r14.qword; register uint64_t r15 asm("r15") = state.gpr.r15.qword; #endif switch (call) { #if REMILL_HYPERCALL_X86 || REMILL_HYPERCALL_AMD64 case SyncHyperCall::kX86CPUID: asm volatile("cpuid" : "=a"(state.gpr.rax.aword), "=b"(state.gpr.rbx.aword), "=c"(state.gpr.rcx.aword), "=d"(state.gpr.rdx.aword) : "a"(state.gpr.rax.aword), "b"(state.gpr.rbx.aword), "c"(state.gpr.rcx.aword), "d"(state.gpr.rdx.aword)); break; case SyncHyperCall::kX86ReadTSC: asm volatile("rdtsc" : "=a"(state.gpr.rax.aword), "=d"(state.gpr.rdx.aword)); break; case SyncHyperCall::kX86ReadTSCP: asm volatile("rdtscp" : "=a"(state.gpr.rax.aword), "=c"(state.gpr.rcx.aword), "=d"(state.gpr.rdx.aword) : "a"(state.gpr.rax.aword), "c"(state.gpr.rcx.aword), "d"(state.gpr.rdx.aword)); break; case SyncHyperCall::kX86XGetBV: { uint32_t eax = 0; uint32_t edx = 0; const auto selector = static_cast(state.addr_to_load); asm volatile("xgetbv" : "=a"(eax), "=d"(edx) : "c"(selector)); state.gpr.rax.aword = eax; state.gpr.rdx.aword = edx; break; } case SyncHyperCall::kX86LoadGlobalDescriptorTable: { const auto read = __remill_read_memory_64(mem, static_cast(state.addr_to_load)); struct GdtrRecord { uint16_t *length; void *base; } __attribute__((packed)); const auto *gdtr = reinterpret_cast(&read); asm volatile("lgdt %0" : : "m"(gdtr)); break; } case SyncHyperCall::kX86LoadInterruptDescriptorTable: { const auto read = __remill_read_memory_64(mem, static_cast(state.addr_to_load)); struct IdtrRecord { uint16_t length; void *base; } __attribute__((packed)); const auto *idtr = reinterpret_cast(&read); asm volatile("lidt %0" : : "m"(idtr)); break; } case SyncHyperCall::kX86LoadAccessRights: { const auto selector = static_cast(state.addr_to_load); uint8_t zf = 0; #if REMILL_HYPERCALL_X86 uint32_t result = 0; #else uint64_t result = 0; #endif asm volatile("lar %[src], %[dst] \n\t" "setz %[zf]" : [dst] "=r"(result), [zf] "=qm"(zf) : [src] "rm"(selector) : "cc"); state.addr_to_store = result; state.rflag.zf = zf; state.aflag.zf = zf; break; } case SyncHyperCall::kX86VerifySegmentReadable: { const auto selector = static_cast(state.addr_to_load); uint8_t zf = 0; asm volatile("verr %[src] \n\t" "setz %[zf]" : [zf] "=qm"(zf) : [src] "rm"(selector) : "cc"); state.rflag.zf = zf; state.aflag.zf = zf; break; } case SyncHyperCall::kX86ReadModelSpecificRegister: asm volatile("rdmsr" : "=c"(state.gpr.rcx.dword) : "a"(state.gpr.rax.dword), "d"(state.gpr.rdx.dword)); break; case SyncHyperCall::kX86WriteModelSpecificRegister: asm volatile("wrmsr" : "=c"(state.gpr.rcx.dword) : "a"(state.gpr.rax.dword), "d"(state.gpr.rdx.dword)); break; case SyncHyperCall::kX86WriteBackInvalidate: asm volatile("wbinvd" :); break; case SyncHyperCall::kX86SetSegmentES: mem = __remill_x86_set_segment_es(mem); break; case SyncHyperCall::kX86SetSegmentSS: mem = __remill_x86_set_segment_ss(mem); break; case SyncHyperCall::kX86SetSegmentDS: mem = __remill_x86_set_segment_ds(mem); break; case SyncHyperCall::kX86SetSegmentFS: mem = __remill_x86_set_segment_fs(mem); break; case SyncHyperCall::kX86SetSegmentGS: mem = __remill_x86_set_segment_gs(mem); break; # if REMILL_HYPERCALL_X86 case SyncHyperCall::kX86SetDebugReg: mem = __remill_x86_set_debug_reg(mem); break; case SyncHyperCall::kX86SetControlReg0: mem = __remill_x86_set_control_reg_0(mem); break; case SyncHyperCall::kX86SetControlReg1: mem = __remill_x86_set_control_reg_1(mem); break; case SyncHyperCall::kX86SetControlReg2: mem = __remill_x86_set_control_reg_2(mem); break; case SyncHyperCall::kX86SetControlReg3: mem = __remill_x86_set_control_reg_3(mem); break; case SyncHyperCall::kX86SetControlReg4: mem = __remill_x86_set_control_reg_4(mem); break; #if defined(__clang_major__) && __clang_major__ >= 20 case SyncHyperCall::kX86SysCall: case SyncHyperCall::kX86SysEnter: case SyncHyperCall::kX86SysExit: // LLVM 20+ rejects RSP/RBP constraints in inline asm // These hypercalls are not executed in normal test workflows anyway __builtin_debugtrap(); break; #else case SyncHyperCall::kX86SysCall: asm volatile("syscall" : "=a"(state.gpr.rax.dword), "=r"(esp) : "a"(state.gpr.rax.dword), "b"(state.gpr.rbx.dword), "c"(state.gpr.rcx.dword), "d"(state.gpr.rdx.dword), "S"(state.gpr.rsi.dword), "D"(state.gpr.rdi.dword), "r"(esp), "r"(ebp)); break; case SyncHyperCall::kX86SysEnter: asm volatile("sysenter" : "=a"(state.gpr.rax.dword), "=r"(esp) : "a"(state.gpr.rax.dword), "b"(state.gpr.rbx.dword), "c"(state.gpr.rcx.dword), "d"(state.gpr.rdx.dword), "S"(state.gpr.rsi.dword), "D"(state.gpr.rdi.dword), "r"(esp), "r"(ebp)); break; case SyncHyperCall::kX86SysExit: asm volatile("sysexit" : "=a"(state.gpr.rax.dword), "=r"(esp) : "a"(state.gpr.rax.dword), "b"(state.gpr.rbx.dword), "c"(state.gpr.rcx.dword), "d"(state.gpr.rdx.dword), "S"(state.gpr.rsi.dword), "D"(state.gpr.rdi.dword), "r"(esp), "r"(ebp)); break; #endif # elif REMILL_HYPERCALL_AMD64 case SyncHyperCall::kAMD64SetDebugReg: mem = __remill_amd64_set_debug_reg(mem); break; case SyncHyperCall::kAMD64SetControlReg0: mem = __remill_amd64_set_control_reg_0(mem); break; case SyncHyperCall::kAMD64SetControlReg1: mem = __remill_amd64_set_control_reg_1(mem); break; case SyncHyperCall::kAMD64SetControlReg2: mem = __remill_amd64_set_control_reg_2(mem); break; case SyncHyperCall::kAMD64SetControlReg3: mem = __remill_amd64_set_control_reg_3(mem); break; case SyncHyperCall::kAMD64SetControlReg4: mem = __remill_amd64_set_control_reg_4(mem); break; case SyncHyperCall::kAMD64SetControlReg8: mem = __remill_amd64_set_control_reg_8(mem); break; #if defined(__clang_major__) && __clang_major__ >= 20 case SyncHyperCall::kX86SysCall: case SyncHyperCall::kX86SysEnter: case SyncHyperCall::kX86SysExit: // LLVM 20+ rejects RSP/RBP constraints in inline asm // These hypercalls are not executed in normal test workflows anyway __builtin_debugtrap(); break; #else case SyncHyperCall::kX86SysCall: asm volatile("syscall" : "=a"(state.gpr.rax.qword), "=r"(rsp) : "a"(state.gpr.rax.qword), "b"(state.gpr.rbx.qword), "c"(state.gpr.rcx.qword), "d"(state.gpr.rdx.qword), "S"(state.gpr.rsi.qword), "D"(state.gpr.rdi.qword), "r"(rsp), "r"(rbp), "r"(r8), "r"(r9), "r"(r10), "r"(r11), "r"(r12), "r"(r13), "r"(r14), "r"(r15)); break; case SyncHyperCall::kX86SysEnter: asm volatile("sysenter" : "=a"(state.gpr.rax.qword), "=r"(rsp) : "a"(state.gpr.rax.qword), "b"(state.gpr.rbx.qword), "c"(state.gpr.rcx.qword), "d"(state.gpr.rdx.qword), "S"(state.gpr.rsi.qword), "D"(state.gpr.rdi.qword), "r"(rsp), "r"(rbp), "r"(r8), "r"(r9), "r"(r10), "r"(r11), "r"(r12), "r"(r13), "r"(r14), "r"(r15)); break; case SyncHyperCall::kX86SysExit: asm volatile("sysexit" : "=a"(state.gpr.rax.qword), "=r"(rsp) : "a"(state.gpr.rax.qword), "b"(state.gpr.rbx.qword), "c"(state.gpr.rcx.qword), "d"(state.gpr.rdx.qword), "S"(state.gpr.rsi.qword), "D"(state.gpr.rdi.qword), "r"(rsp), "r"(rbp), "r"(r8), "r"(r9), "r"(r10), "r"(r11), "r"(r12), "r"(r13), "r"(r14), "r"(r15)); break; #endif # endif #elif REMILL_HYPERCALL_ARM case SyncHyperCall::kAArch32EmulateInstruction: mem = __remill_aarch32_emulate_instruction(mem); break; case SyncHyperCall::kAArch32CheckNotEL2: mem = __remill_aarch32_check_not_el2(mem); break; #elif REMILL_HYPERCALL_AARCH64 case SyncHyperCall::kAArch64EmulateInstruction: mem = __remill_aarch64_emulate_instruction(mem); break; case SyncHyperCall::kAArch64Breakpoint: # ifdef __has_builtin # if __has_builtin(__builtin_debugtrap) __builtin_debugtrap(); # define REMILL_USED_DEBUG_TRAP # endif # endif # ifndef REMILL_USED_DEBUG_TRAP asm volatile("brk #0"); # endif # undef REMILL_USED_DEBUG_TRAP break; #elif REMILL_HYPERCALL_SPARC32 || REMILL_HYPERCALL_SPARC64 case SyncHyperCall::kSPARCSetAsiRegister: mem = __remill_sparc_set_asi_register(mem); break; case SyncHyperCall::kSPARCUnimplementedInstruction: mem = __remill_sparc_unimplemented_instruction(mem); break; case SyncHyperCall::kSPARCUnhandledDCTI: mem = __remill_sparc_unhandled_dcti(mem); break; case SyncHyperCall::kSPARCWindowUnderflow: mem = __remill_sparc_window_underflow(mem); break; case SyncHyperCall::kSPARCTrapCondA: mem = __remill_sparc_trap_cond_a(mem); break; case SyncHyperCall::kSPARCTrapCondN: mem = __remill_sparc_trap_cond_n(mem); break; case SyncHyperCall::kSPARCTrapCondNE: mem = __remill_sparc_trap_cond_ne(mem); break; case SyncHyperCall::kSPARCTrapCondE: mem = __remill_sparc_trap_cond_e(mem); break; case SyncHyperCall::kSPARCTrapCondG: mem = __remill_sparc_trap_cond_g(mem); break; case SyncHyperCall::kSPARCTrapCondLE: mem = __remill_sparc_trap_cond_le(mem); break; case SyncHyperCall::kSPARCTrapCondGE: mem = __remill_sparc_trap_cond_ge(mem); break; case SyncHyperCall::kSPARCTrapCondL: mem = __remill_sparc_trap_cond_l(mem); break; case SyncHyperCall::kSPARCTrapCondGU: mem = __remill_sparc_trap_cond_gu(mem); break; case SyncHyperCall::kSPARCTrapCondLEU: mem = __remill_sparc_trap_cond_leu(mem); break; case SyncHyperCall::kSPARCTrapCondCC: mem = __remill_sparc_trap_cond_cc(mem); break; case SyncHyperCall::kSPARCTrapCondCS: mem = __remill_sparc_trap_cond_cs(mem); break; case SyncHyperCall::kSPARCTrapCondPOS: mem = __remill_sparc_trap_cond_pos(mem); break; case SyncHyperCall::kSPARCTrapCondNEG: mem = __remill_sparc_trap_cond_neg(mem); break; case SyncHyperCall::kSPARCTrapCondVC: mem = __remill_sparc_trap_cond_vc(mem); break; case SyncHyperCall::kSPARCTrapCondVS: mem = __remill_sparc_trap_cond_vs(mem); break; # if defined(REMILL_HYPERCALL_SPARC32) case SyncHyperCall::kSPARC32EmulateInstruction: mem = __remill_sparc32_emulate_instruction(mem); break; # elif defined(REMILL_HYPERCALL_SPARC64) case SyncHyperCall::kSPARC64EmulateInstruction: mem = __remill_sparc64_emulate_instruction(mem); break; # endif #elif defined(REMILL_HYPERCALL_PPC) case SyncHyperCall::kPPCEmulateInstruction: mem = __remill_ppc_emulate_instruction(mem); break; case SyncHyperCall::kPPCSysCall: mem = __remill_ppc_syscall(mem); break; #endif default: __builtin_unreachable(); break; } return mem; }