From e6a01472c853459d39b1329550c9e52d39c91374 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Thu, 23 Oct 2025 13:47:54 -0700 Subject: [PATCH] layer for x64 --- src/raddbg/raddbg_main.c | 2 + src/x64/x64.c | 184 ++++++++++++++++++++++++++++++ src/x64/x64.h | 234 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 420 insertions(+) create mode 100644 src/x64/x64.c create mode 100644 src/x64/x64.h diff --git a/src/raddbg/raddbg_main.c b/src/raddbg/raddbg_main.c index a83bd0b6..021be7f5 100644 --- a/src/raddbg/raddbg_main.c +++ b/src/raddbg/raddbg_main.c @@ -194,6 +194,7 @@ //- rjf: [h] #include "base/base_inc.h" +#include "x64/x64.h" #include "linker/hash_table.h" #include "os/os_inc.h" #include "artifact_cache/artifact_cache.h" @@ -242,6 +243,7 @@ //- rjf: [c] #include "base/base_inc.c" +#include "x64/x64.c" #include "linker/hash_table.c" #include "os/os_inc.c" #include "artifact_cache/artifact_cache.c" diff --git a/src/x64/x64.c b/src/x64/x64.c new file mode 100644 index 00000000..66ad5961 --- /dev/null +++ b/src/x64/x64.c @@ -0,0 +1,184 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal void +x64_cpuid(U32 leaf, U32 *eax, U32 *ebx, U32 *ecx, U32 *edx) +{ + U32 info[4] = {0}; +#if COMPILER_MSVC + __cpuid(info, leaf); + if (eax) { *eax = info[0]; } + if (ebx) { *ebx = info[1]; } + if (ecx) { *ecx = info[2]; } + if (edx) { *edx = info[3]; } +#elif COMPILER_CLANG || COMPILER_GCC + if (!eax) { eax = &info[0]; } + if (!ebx) { ebx = &info[1]; } + if (!ecx) { ecx = &info[2]; } + if (!edx) { edx = &info[3]; } + __get_cpuid(leaf, eax, ebx, ecx, edx); +#else +# error "cpuid is not defined for this compiler" +#endif +} + +internal void +x64_cpuid_ex(U32 leaf, U32 sub_leaf, U32 *eax, U32 *ebx, U32 *ecx, U32 *edx) +{ + U32 info[4]; +#if COMPILER_MSVC + __cpuidex(info, leaf, sub_leaf); + if (eax) { *eax = info[0]; } + if (ebx) { *ebx = info[1]; } + if (ecx) { *ecx = info[2]; } + if (edx) { *edx = info[3]; } +#elif COMPILER_CLANG || COMPILER_GCC + if (!eax) { eax = &info[0]; } + if (!ebx) { ebx = &info[1]; } + if (!ecx) { ecx = &info[2]; } + if (!edx) { edx = &info[3]; } + __get_cpuid_count(leaf, sub_leaf, eax, ebx, ecx, edx); +#else +# error "cpuid_count is not defined for this compiler" +#endif +} + +internal B32 +x64_is_xsave_supported(void) +{ + U32 ecx = 0; + x64_cpuid(1, 0, 0, &ecx, 0); + return !!(ecx & (1 << 26)); +} + +internal U32 +x64_get_xsave_size(void) +{ + U32 xsave_size = 0; + x64_cpuid_ex(0xd, 0, 0, &xsave_size, 0, 0); + return xsave_size; +} + +internal U16 +x64_xsave_tag_word_from_real_tag_word(U16 ftw) +{ + U16 compact = 0; + for EachIndex(fpr, 8) { + U32 tag = (ftw >> (fpr * 2)) & 3; + if (tag != 3) { + compact |= (1 << fpr); + } + } + return compact; +} + +internal U32 +x64_xsave_offset_from_feature_flag(U64 xcr0, X64_XStateComponentIdx comp_idx) +{ + U32 offset = 0; + if (xcr0 & (1 << comp_idx)) { + if(comp_idx == X64_XStateComponentIdx_FP) { + offset = 0; + } else if(comp_idx == X64_XStateComponentIdx_SSE) { + offset = 160; + } else { + x64_cpuid_ex(0xd, comp_idx, 0, &offset, 0, 0); + } + } + return offset; +} + +internal X64_XSaveLayout +x64_get_xsave_layout(U64 xcr0) +{ + X64_XSaveLayout xsave_layout = {0}; + xsave_layout.x87_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_FP); + xsave_layout.sse_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_SSE); + xsave_layout.avx_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_AVX); + xsave_layout.bndregs_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_BNDREGS); + xsave_layout.bndcfg_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_BNDCSR); + xsave_layout.opmask_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_OPMASK); + xsave_layout.zmm_h_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_ZMM_H); + xsave_layout.zmm_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_ZMM); + xsave_layout.pt_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_PT); + xsave_layout.pkru_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_PKRU); + xsave_layout.pasid_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_PASID); + xsave_layout.cet_u_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_CETU); + xsave_layout.cet_s_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_CETS); + xsave_layout.hdc_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_HDC); + xsave_layout.uintr_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_UINTR); + xsave_layout.lbr_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_LBR); + xsave_layout.hwp_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_HWP); + xsave_layout.tile_cfg_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_TILECFG); + xsave_layout.tile_data_offset = x64_xsave_offset_from_feature_flag(xcr0, X64_XStateComponentIdx_TILEDATA); + return xsave_layout; +} + +internal void +x64_set_debug_break(U64 *drs, U64 trap_idx, U64 addr, U64 size, X64_BreakpointType bp_type, X64_DebugBreakType break_type) +{ + // set breakpoint address + switch (trap_idx) { + case 0: { drs[0] = addr; } break; + case 1: { drs[1] = addr; } break; + case 2: { drs[2] = addr; } break; + case 3: { drs[3] = addr; } break; + default: { InvalidPath; } break; + } + + // enable breakpoint + drs[7] &= ~(3 << (trap_idx * 2)); + switch (bp_type) + { + case X64_BreakpointType_Null: break; + case X64_BreakpointType_Local: { drs[7] |= 1 << (trap_idx * 2); } break; + case X64_BreakpointType_Global: { drs[7] |= 2 << (trap_idx * 2); } break; + default: { InvalidPath; } break; + } + + // set break access type + switch (break_type) { + case X64_DebugBreakType_Exec: + Assert(size == 0); + case X64_DebugBreakType_Null: { + drs[7] &= ~((1u << 16) << (trap_idx * 4)); + drs[7] &= ~((1u << 17) << (trap_idx * 4)); + } break; + case X64_DebugBreakType_Write: { + drs[7] |= (1u << 16) << (trap_idx * 4); + drs[7] &= ~((1u << 17) << (trap_idx * 4)); + } break; + case X64_DebugBreakType_ReadWriteIO: { + drs[7] &= ~((1u << 16) << (trap_idx * 4)); + drs[7] |= (1u << 17) << (trap_idx * 4); + } break; + case X64_DebugBreakType_ReadWriteNoFetch: { + drs[7] |= (1u << 16) << (trap_idx * 4); + drs[7] |= (1u << 17) << (trap_idx * 4); + } break; + default: { InvalidPath; } break; + } + + // set breakpoint size + switch (size) { + case 0: + case 1: { + drs[7] &= ~((1u << 18) << (trap_idx * 4)); + drs[7] &= ~((1u << 19) << (trap_idx * 4)); + } break; + case 2: { + drs[7] |= (1 << 18) << (trap_idx * 4); + drs[7] &= ~((1 << 19) << (trap_idx * 4)); + } break; + case 4: { + drs[7] |= (1 << 18) << (trap_idx * 4); + drs[7] |= (1 << 19) << (trap_idx * 4); + } break; + case 8: { + drs[7] &= ~((1 << 18) << (trap_idx * 4)); + drs[7] |= (1 << 19) << (trap_idx * 4); + } break; + default: { InvalidPath; } + } +} + diff --git a/src/x64/x64.h b/src/x64/x64.h new file mode 100644 index 00000000..93dfe433 --- /dev/null +++ b/src/x64/x64.h @@ -0,0 +1,234 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef X64_H +#define X64_H + +//////////////////////////////// +//~ RFlags + +typedef U64 X64_RFlags; +enum +{ + X64_RFlag_Carry = (1 << 0), + X64_RFlag_Parity = (1 << 2), + X64_RFlag_AuxCarry = (1 << 4), + X64_RFlag_Zero = (1 << 6), + X64_RFlag_Sign = (1 << 7), + X64_RFlag_Trap = (1 << 8), + X64_RFlag_Interrupt = (1 << 9), + X64_RFlag_Direction = (1 << 10), + X64_RFlag_Overflow = (1 << 11), + X64_RFlag_IoPrivilege = (3 << 12), + X64_RFlag_NT = (1 << 14), + X64_RFlag_MD = (1 << 15), + X64_RFlag_RF = (1 << 16), + X64_RFlag_VM = (1 << 17), + X64_RFlag_AC = (1 << 18), + X64_RFlag_VIF = (1 << 19), + X64_RFlag_VIP = (1 << 20), + X64_RFlag_AES = (1 << 30), + X64_RFlag_AI = (1 << 31), +}; + +//////////////////////////////// +//~ fxsave/xsave + +typedef struct AlignType(16) X64_FXSave +{ + U16 fcw; + U16 fsw; + U16 ftw; + U16 fop; + union { + struct { + U64 fip; + U64 fdp; + } b64; + struct { + U32 fip; + U16 fcs, _pad0; + U32 fdp; + U16 fds, _pad1; + } b32; + }; + U32 mxcsr; + U32 mxcsr_mask; + U128 st_space[8]; + U128 xmm_space[16]; + U8 padding[96]; +} X64_FXSave; +StaticAssert(sizeof(X64_FXSave) == 512, g_x64_xsave_legacy_size_check); + +typedef struct X64_XSaveHeader +{ + U64 xstate_bv; + U64 xcomp_bv; + U8 reserved[48]; +} X64_XSaveHeader; +StaticAssert(sizeof(X64_XSaveHeader) == 64, g_x64_xsave_header_size_check); + +typedef struct AlignType(64) X64_XSave +{ + X64_FXSave fxsave; + X64_XSaveHeader header; + // U8 ext_area[0]; +} X64_XSave; +StaticAssert(sizeof(X64_XSave) == 576, g_x64_xsave_size_check); + +typedef U32 X64_XStateComponentIdx; +enum +{ + X64_XStateComponentIdx_FP = 0, + X64_XStateComponentIdx_SSE = 1, + X64_XStateComponentIdx_AVX = 2, + X64_XStateComponentIdx_BNDREGS = 3, + X64_XStateComponentIdx_BNDCSR = 4, + X64_XStateComponentIdx_OPMASK = 5, + X64_XStateComponentIdx_ZMM_H = 6, + X64_XStateComponentIdx_ZMM = 7, + X64_XStateComponentIdx_PT = 8, + X64_XStateComponentIdx_PKRU = 9, + X64_XStateComponentIdx_PASID = 10, + X64_XStateComponentIdx_CETU = 11, + X64_XStateComponentIdx_CETS = 12, + X64_XStateComponentIdx_HDC = 13, + X64_XStateComponentIdx_UINTR = 14, + X64_XStateComponentIdx_LBR = 15, + X64_XStateComponentIdx_HWP = 16, + X64_XStateComponentIdx_TILECFG = 17, + X64_XStateComponentIdx_TILEDATA = 18, +}; + +enum +{ + X64_XStateComponentFlag_FP = (1 << X64_XStateComponentIdx_FP), + X64_XStateComponentFlag_SSE = (1 << X64_XStateComponentIdx_SSE), + X64_XStateComponentFlag_AVX = (1 << X64_XStateComponentIdx_AVX), + X64_XStateComponentFlag_BNDREGS = (1 << X64_XStateComponentIdx_BNDREGS), + X64_XStateComponentFlag_BNDCSR = (1 << X64_XStateComponentIdx_BNDCSR), + X64_XStateComponentFlag_OPMASK = (1 << X64_XStateComponentIdx_OPMASK), + X64_XStateComponentFlag_ZMM_H = (1 << X64_XStateComponentIdx_ZMM_H), + X64_XStateComponentFlag_ZMM = (1 << X64_XStateComponentIdx_ZMM), + X64_XStateComponentFlag_PT = (1 << X64_XStateComponentIdx_PT), + X64_XStateComponentFlag_PKRU = (1 << X64_XStateComponentIdx_PKRU), + X64_XStateComponentFlag_PASID = (1 << X64_XStateComponentIdx_PASID), + X64_XStateComponentFlag_CETU = (1 << X64_XStateComponentIdx_CETU), + X64_XStateComponentFlag_CETS = (1 << X64_XStateComponentIdx_CETS), + X64_XStateComponentFlag_HDC = (1 << X64_XStateComponentIdx_HDC), + X64_XStateComponentFlag_UINTR = (1 << X64_XStateComponentIdx_UINTR), + X64_XStateComponentFlag_LBR = (1 << X64_XStateComponentIdx_LBR), + X64_XStateComponentFlag_HWP = (1 << X64_XStateComponentIdx_HWP), + X64_XStateComponentFlag_TILECFG = (1 << X64_XStateComponentIdx_TILECFG), + X64_XStateComponentFlag_TILEDATA = (1 << X64_XStateComponentIdx_TILEDATA), +}; + +typedef struct +{ + U64 x87_offset; + U64 sse_offset; + U64 avx_offset; + U64 bndregs_offset; + U64 bndcfg_offset; + U64 opmask_offset; + U64 zmm_h_offset; + U64 zmm_offset; + U64 pt_offset; + U64 pkru_offset; + U64 pasid_offset; + U64 cet_u_offset; + U64 cet_s_offset; + U64 hdc_offset; + U64 uintr_offset; + U64 lbr_offset; + U64 hwp_offset; + U64 tile_cfg_offset; + U64 tile_data_offset; +} X64_XSaveLayout; + +//////////////////////////////// +//~ Debug Status Register + +typedef enum +{ + X64_DebugStatusFlag_B0 = (1 << 0), + X64_DebugStatusFlag_B1 = (1 << 1), + X64_DebugStatusFlag_B2 = (1 << 2), + X64_DebugStatusFlag_B3 = (1 << 3), + X64_DebugStatusFlag_BusLock = (1 << 11), + X64_DebugStatusFlag_DebugRegisterAccess = (1 << 13), + X64_DebugStatusFlag_SingleStep = (1 << 14), + X64_DebugStatusFlag_TaskSwitch = (1 << 15), + X64_DebugStatusFlag_RTM = (1 << 16), +} X64_DebugStatusFlags; + +//////////////////////////////// +//~ Debug Control Register + +typedef enum X64_BreakpointType { + X64_BreakpointType_Null, + X64_BreakpointType_Local, + X64_BreakpointType_Global, +} X64_BreakpointType; + +typedef enum X64_DebugBreakType +{ + X64_DebugBreakType_Null, + X64_DebugBreakType_Exec, + X64_DebugBreakType_Write, + X64_DebugBreakType_ReadWriteIO, + X64_DebugBreakType_ReadWriteNoFetch, +} X64_DebugBreakType; + +typedef enum X64_DebugControlFlags +{ + X64_DebugControlFlag_L0 = (1 << 0), + X64_DebugControlFlag_L1 = (1 << 2), + X64_DebugControlFlag_L2 = (1 << 4), + X64_DebugControlFlag_L3 = (1 << 6), + + X64_DebugControlFlag_G0 = (1 << 1), + X64_DebugControlFlag_G1 = (1 << 3), + X64_DebugControlFlag_G3 = (1 << 5), + X64_DebugControlFlag_G4 = (1 << 7), + + X64_DebugControlFlag_LE = (1 << 8), + X64_DebugControlFlag_GE = (1 << 9), + + X64_DebugControlFlag_RTM = (1 << 11), + + X64_DebugControlFlag_GD = (1 << 13), + + X64_DebugControlFlag_RW0 = (3 << 16), + X64_DebugControlFlag_RW1 = (3 << 20), + X64_DebugControlFlag_RW2 = (3 << 24), + X64_DebugControlFlag_FW3 = (3 << 26), + + X64_DebugControlFlag_LEN0 = (3 << 18), + X64_DebugControlFlag_LEN1 = (3 << 22), + X64_DebugControlFlag_LEN2 = (3 << 26), + X64_DebugControlFlag_LEN3 = (3 << 30), +} X64_DebugControlFlags; + +//////////////////////////////// +//~ cpuid + +internal void x64_cpuid(U32 leaf, U32 *eax, U32 *ebx, U32 *ecx, U32 *edx); +internal void x64_cpuid_ex(U32 leaf, U32 sub_leaf, U32 *eax, U32 *ebx, U32 *ecx, U32 *edx); + +//////////////////////////////// +//~ fxsave/xsave + +internal B32 x64_is_xsave_supported(void); +internal U32 x64_get_xsave_size(void); +internal U16 x64_xsave_tag_word_from_real_tag_word(U16 ftw); +internal U32 x64_xsave_offset_from_feature_flag(U64 xcr0, X64_XStateComponentIdx comp_idx); +internal X64_XSaveLayout x64_get_xsave_layout(U64 xcr0); + +//////////////////////////////// +//~ Debug Control Register + +internal void x64_set_debug_break(U64 *drs, U64 trap_idx, U64 addr, U64 size, X64_BreakpointType bp_type, X64_DebugBreakType break_type); + +#endif // BASE_X64_H +