Porting to x64

From AlliedModders Wiki
Revision as of 08:19, 1 August 2024 by Nosoop (talk | contribs) (Recompiling Extensions: Mention usage of --targets)
Jump to: navigation, search

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!)

Recompiling Extensions

The short version is that, assuming your target project uses AMBuild, pull in a modern revision of the build scripts, then specify the --targets argument to build the extension for one or more supported architectures.

Beyond that, as long as the code isn't architecture-specific, most extensions should compile fine with 64-bit support with slight adjustments at most. If you are using detours, you may need to make further adjustments to build the new safetyhook dependency.