64-bit support for CSGO on Linux and macOS (#705)

This commit is contained in:
Scott Ehlert
2017-12-20 01:56:23 -06:00
committed by GitHub
parent 057a5790e2
commit ce1a4dcac0
90 changed files with 15682 additions and 947 deletions
+44 -5
View File
@@ -41,6 +41,8 @@
#define PAGE_EXECUTE_READWRITE PROT_READ|PROT_WRITE|PROT_EXEC
#endif
#include <jit/x86/x86_macros.h>
struct patch_t
{
patch_t()
@@ -68,13 +70,50 @@ inline void SetMemPatchable(void *address, size_t size)
ProtectMemory(address, (int)size, PAGE_EXECUTE_READWRITE);
}
inline void PatchRelJump32(unsigned char *target, void *callback)
{
SetMemPatchable(target, 5);
// jmp <32-bit displacement>
target[0] = IA32_JMP_IMM32;
*(int32_t *)(&target[1]) = int32_t((unsigned char *)callback - (target + 5));
}
inline void PatchAbsJump64(unsigned char *target, void *callback)
{
int i = 0;
SetMemPatchable(target, 14);
// push <lower 32-bits> ; allocates 64-bit stack space on x64
// mov [rsp+4], <upper 32-bits> ; unnecessary if upper bits are 0
// ret ; jump to address on stack
target[i++] = IA32_PUSH_IMM32;
*(int32_t *)(&target[i]) = int32_t(int64_t(callback));
i += 4;
if ((int64_t(callback) >> 32) != 0)
{
target[i++] = IA32_MOV_RM_IMM32;
target[i++] = ia32_modrm(MOD_DISP8, 0, kREG_SIB);
target[i++] = ia32_sib(NOSCALE, kREG_NOIDX, kREG_ESP);
target[i++] = 0x04;
*(int32_t *)(&target[i]) = (int64_t(callback) >> 32);
i += 4;
}
target[i] = IA32_RET;
}
inline void DoGatePatch(unsigned char *target, void *callback)
{
SetMemPatchable(target, 20);
target[0] = 0xFF; /* JMP */
target[1] = 0x25; /* MEM32 */
*(void **)(&target[2]) = callback;
#if defined(_WIN64) || defined(__x86_64__)
int64_t diff = int64_t(callback) - (int64_t(target) + 5);
int32_t upperBits = (diff >> 32);
if (upperBits == 0 || upperBits == -1)
PatchRelJump32(target, callback);
else
PatchAbsJump64(target, callback);
#else
PatchRelJump32(target, callback);
#endif
}
inline void ApplyPatch(void *address, int offset, const patch_t *patch, patch_t *restore)
+70 -10
View File
@@ -35,6 +35,58 @@
ISourcePawnEngine *CDetourManager::spengine = NULL;
IGameConfig *CDetourManager::gameconf = NULL;
// Push 64-bit value onto the stack using two instructions.
//
// Pushing 0xF00DF00DF00DF00D:
// push 0xF00DF00D
// mov [rsp+4], 0xF00DF00D
static inline void X64_Push_Imm64(JitWriter *jit, jit_int64_t val)
{
jit->write_ubyte(IA32_PUSH_IMM32);
jit->write_int32(jit_int32_t(val));
if ((val >> 32) != 0)
IA32_Mov_ESP_Disp8_Imm32(jit, 4, (val >> 32));
}
// Jump to absolute 64-bit address using multiple instructions.
//
// Jumping to address 0xF00DF00DF00DF00D:
// push 0xF00DF00D
// mov [rsp+4], 0xF00DF00D
// ret
static inline void X64_Jump_Abs(JitWriter *jit, void *dest)
{
X64_Push_Imm64(jit, jit_int64_t(dest));
IA32_Return(jit);
}
static inline void RelativeJump32(JitWriter *jit, void *target)
{
jitoffs_t call = IA32_Jump_Imm32(jit, 0);
IA32_Write_Jump32_Abs(jit, call, target);
}
#if defined(_WIN64) || defined(__x86_64__)
static inline bool IsShortJump(JitWriter *jit, void *target)
{
int64_t diff = int64_t(target) - (int64_t(jit->outbase) + jit->get_outputpos() + OP_JMP_SIZE);
int32_t upperBits = (diff >> 32);
return upperBits == 0 || upperBits == -1;
}
#endif
static inline void AbsJump(JitWriter *jit, void *target)
{
#if defined(_WIN64) || defined(__x86_64__)
if (IsShortJump(jit, target))
RelativeJump32(jit, target);
else
X64_Jump_Abs(jit, target);
#else
RelativeJump32(jit, target);
#endif
}
void CDetourManager::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf)
{
CDetourManager::spengine = spengine;
@@ -147,13 +199,12 @@ bool CDetour::CreateDetour()
return false;
}
detour_restore.bytes = copy_bytes((unsigned char *)detour_address, NULL, OP_JMP_SIZE+1);
/* First, save restore bits */
for (size_t i=0; i<detour_restore.bytes; i++)
{
detour_restore.patch[i] = ((unsigned char *)detour_address)[i];
}
#if defined(_WIN64) || defined(__x86_64__)
int shortBytes = copy_bytes((unsigned char *)detour_address, NULL, OP_JMP_SIZE);
detour_restore.bytes = copy_bytes((unsigned char *)detour_address, NULL, X64_ABS_SIZE);
#else
detour_restore.bytes = copy_bytes((unsigned char *)detour_address, NULL, OP_JMP_SIZE);
#endif
JitWriter wr;
JitWriter *jit = &wr;
@@ -167,13 +218,22 @@ jit_rewind:
/* Patch old bytes in */
if (wr.outbase != NULL)
{
#if defined(_WIN64) || defined(__x86_64__)
wr.outptr += shortBytes;
bool isShort = IsShortJump(jit, detour_address);
wr.outptr -= shortBytes;
if (isShort)
detour_restore.bytes = shortBytes;
#endif
/* Save restore bits */
memcpy(detour_restore.patch, detour_address, detour_restore.bytes);
copy_bytes((unsigned char *)detour_address, (unsigned char*)wr.outptr, detour_restore.bytes);
}
wr.outptr += detour_restore.bytes;
/* Return to the original function */
jitoffs_t call = IA32_Jump_Imm32(jit, 0);
IA32_Write_Jump32_Abs(jit, call, (unsigned char *)detour_address + detour_restore.bytes);
AbsJump(jit, (unsigned char *)detour_address + detour_restore.bytes);
if (wr.outbase == NULL)
{
@@ -211,7 +271,7 @@ void CDetour::EnableDetour()
{
if (!detoured)
{
DoGatePatch((unsigned char *)detour_address, &detour_callback);
DoGatePatch((unsigned char *)detour_address, detour_callback);
detoured = true;
}
}