Difference between revisions of "Porting to x64"

From AlliedModders Wiki
Jump to: navigation, search
m (Fix category naming???)
m (Add Windows x64 gamedata key name and format table)
 
(One intermediate revision by one other user not shown)
Line 12: Line 12:
  
 
== I'm having an issue! ==
 
== I'm having an issue! ==
 +
 +
In general, '''prototypes need to be exactly correct''' (including class statics like copy/destruct/constructors) as there are now significantly more quirks that will cause ABIs to be laid out differently.
 +
 
Here are some things to check for that may be tripping you up:
 
Here are some things to check for that may be tripping you up:
  
 
* The type you thought was an ''int'' was actually a ''size_t''!
 
* The type you thought was an ''int'' was actually a ''size_t''!
* Passing pointers as ''int''s when they should be ''void*'' (why would you do this??)
+
* The type you put down as an int or pointer was actually a ''float''!
 +
* Handling pointers as ''int''s when they should be ''void*'' or ''size_t'' (why would you do this??)
 
* '''The calling convention has changed.''' Make sure to account for this in detours and patches. Make sure your prototypes are exactly correct!
 
* '''The calling convention has changed.''' Make sure to account for this in detours and patches. Make sure your prototypes are exactly correct!
 +
* '''In patches:''' Did you forget the 64-bit opcode prefix when assembling your patch? EAX and RAX are accessed using different opcode prefixes!
 +
 +
=== Linux Calling Convention Quirks ===
 +
 +
Linux uses the System-V x64 ABI. There are many great sources of documentation all over the internet: [https://wiki.osdev.org/System_V_ABI OSDev Wiki] [https://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions Wikipedia]
 +
* All calling conventions are now an up to 8-register fastcall depending on the types (!!)
 +
* RCX can be a thisptr, first argument, or stack return pointer depending on the context
 +
* '''Integer arguments and float arguments are passed in different registers!''' (generic for ints, SIMD registers for floats)
 +
* Varargs are now more complex to call when floats are included in the vararg
 +
 +
The exact value of return types with C++ shenanigans (destructors, etc) still needs to be figured out by someone. However, in general.
 +
* The return argument is always passed in RCX, and will bump the thisptr to RDX (todo: confirm this)
 +
* Some return types that are 128-bits will be returned in both RAX and RDX??
 +
 +
=== MSVC Calling Convention Quirks ===
 +
Microsoft has some excellent official documentation [https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170 in their MSVC docs].
 +
 +
* All calling conventions are now a four-register fastcall
 +
* RCX can be a thisptr OR the first arg! It is not always a thisptr now. RCX can also be the return value (see below), bumping the thisptr to RDX.
 +
* '''Integer arguments and float arguments are passed in different registers!''' (generic for ints, SIMD registers for floats)
 +
* All objects larger than 8 bytes are now passed exclusively by reference (even on the stack??)
 +
* Float arguments passed to varargs need to be put in both the generic and SIMD register for the appropiate argument index. Varargs are still fastcalls... somehow...
 +
* '''setjmp has new behavior''' and destroys objects as if the scope has been exited. (if you ''are'' using setjmp, good luck)
 +
 +
Return types also have their usual obscure shenanigans. More specifically,
 +
* The return argument is always passed in RCX, and will bump the thisptr to RDX (todo: confirm this)
 +
* Returns with all types larger than 8 bytes occur on the stack
 +
* Returns with any type that has a user-defined base classes, virtual methods, constructor, destructor, or copy operation happens on the stack (''regardless of size'')
  
  
Line 23: Line 55:
 
=== Gamedata ===
 
=== Gamedata ===
  
All signatures will need to be updated, and likely many offsets. "linux" is for 32-bit linux and "linux64" is for 64-bit linux.
+
All signatures will need to be updated, and likely many offsets.
 +
 
 +
==== Gamedata key names ====
  
Todo: What is the equivalent for windows?
+
{| class="wikitable"
 +
|-
 +
! scope="row"| Arch/OS
 +
! scope="col"| Linux
 +
! scope="col"| Windows
 +
|-
 +
! scope="row"| x86
 +
| linux
 +
| windows
 +
|-
 +
! scope="row"| x64
 +
| linux64
 +
| windows64
 +
|}
  
 
=== Address Natives ===
 
=== Address Natives ===

Latest revision as of 21:19, 8 May 2024

Overview

Many plugins are already compatible with the x64 architecture, but many that manipulate game memory (such as with hooks or patches) will require extensive revisions to run under the new 64-bit servers. Overall, if your plugin:

  • Uses the address natives or the Address type,
  • Uses dhooks
  • Uses sdkcalls or sdktools (not sdkhooks)

...you will need to update your plugin for 64 bits.

Extensions may be good to go with just a recompile for 64-bits, but some may need more extensives changes.

I'm having an issue!

In general, prototypes need to be exactly correct (including class statics like copy/destruct/constructors) as there are now significantly more quirks that will cause ABIs to be laid out differently.

Here are some things to check for that may be tripping you up:

  • The type you thought was an int was actually a size_t!
  • The type you put down as an int or pointer was actually a float!
  • Handling pointers as ints when they should be void* or size_t (why would you do this??)
  • The calling convention has changed. Make sure to account for this in detours and patches. Make sure your prototypes are exactly correct!
  • In patches: Did you forget the 64-bit opcode prefix when assembling your patch? EAX and RAX are accessed using different opcode prefixes!

Linux Calling Convention Quirks

Linux uses the System-V x64 ABI. There are many great sources of documentation all over the internet: OSDev Wiki Wikipedia

  • All calling conventions are now an up to 8-register fastcall depending on the types (!!)
  • RCX can be a thisptr, first argument, or stack return pointer depending on the context
  • Integer arguments and float arguments are passed in different registers! (generic for ints, SIMD registers for floats)
  • Varargs are now more complex to call when floats are included in the vararg

The exact value of return types with C++ shenanigans (destructors, etc) still needs to be figured out by someone. However, in general.

  • The return argument is always passed in RCX, and will bump the thisptr to RDX (todo: confirm this)
  • Some return types that are 128-bits will be returned in both RAX and RDX??

MSVC Calling Convention Quirks

Microsoft has some excellent official documentation in their MSVC docs.

  • All calling conventions are now a four-register fastcall
  • RCX can be a thisptr OR the first arg! It is not always a thisptr now. RCX can also be the return value (see below), bumping the thisptr to RDX.
  • Integer arguments and float arguments are passed in different registers! (generic for ints, SIMD registers for floats)
  • All objects larger than 8 bytes are now passed exclusively by reference (even on the stack??)
  • Float arguments passed to varargs need to be put in both the generic and SIMD register for the appropiate argument index. Varargs are still fastcalls... somehow...
  • setjmp has new behavior and destroys objects as if the scope has been exited. (if you are using setjmp, good luck)

Return types also have their usual obscure shenanigans. More specifically,

  • The return argument is always passed in RCX, and will bump the thisptr to RDX (todo: confirm this)
  • Returns with all types larger than 8 bytes occur on the stack
  • Returns with any type that has a user-defined base classes, virtual methods, constructor, destructor, or copy operation happens on the stack (regardless of size)


Porting Plugins

Gamedata

All signatures will need to be updated, and likely many offsets.

Gamedata key names

Arch/OS Linux Windows
x86 linux windows
x64 linux64 windows64

Address Natives

New address natives that support 64-bits are still under development

DHooks

DHooks does not support 64-bits at all for the time being. It will need heavy revisions to work under the new architecture, starting with better SourceHook support for the 64-bit architecture (see: hookmangen)

SDKCalls

SDKCalls will need to be updated to learn how to accept a thisptr. However, all non-raw calls (eg, those to entities or gamerules that does not require a thisptr) should be fine as long as none of the arguments are 64-bits (including 64-bit ints!)