[llvm-dev] Handling of the x18 register in Wine on AArch64

Martin Storsjö via llvm-dev llvm-dev at lists.llvm.org
Tue May 14 06:22:52 PDT 2019


Hi,

I'm sending this discussion to both wine-devel and llvm-dev, to try to 
keep the discussion open for both sides, to try to find a workable 
compromise. This was preliminarily discussed on llvm-dev already a few 
weeks ago.

One of the major unresolved issues with Wine for AArch64 is how to handle 
the platform specific register x18. 
(https://bugs.winehq.org/show_bug.cgi?id=38780) As Windows on AArch64 is 
commercially available for over a year now, it'd be nice to get this issue 
settled by at least some sort of compromise.

Background:

On AArch64, x18 is a platform specific register; some platforms treat it 
as reserved for platform specific use, while others (Linux) don't and 
treat it as any free temporary register. On Windows, it is used to hold 
the TEB pointer.

When calling Wine builtin functions from the Windows native code, the Wine 
builtin functions can clobber the x18 register, as the Wine code is built 
for the ABI and calling conventions used on Linux. This part is easy to 
work around by compiling Wine with the flag -ffixed-x18, which makes the 
compiler treat this register as reserved, avoiding clobbering it.

However, as the Wine code may end up calling functions in the surrounding 
host environment (mainly the libc, but potentially also other feature 
libraries that are used), and these libraries have not been built with the 
-ffixed-x18 flag.

There are a few different options for going about this matter:


1) Rebuild the surrounding host environment (the whole linux distribution) 
with -ffixed-x18. This would be the perfect solution, but is highly 
impractical for general use of Wine by regular users in their existing 
setups. (Convincing major distributions to start configuring their 
compilers in this way also doesn't seem to be happening.)


2) Force relays on entry to Wine builtins, where the relay can restore x18 
to the right TEB pointer on return. This is pretty simple and 
straightforward, but not very elegant. One benefit is that this doesn't 
need to back up the original value of x18 but can always refetch the right 
TEB pointer value and set it. A patch to this effect have been sent before 
(https://source.winehq.org/patches/data/137759) but wasn't picked up.

One major limitation is that while this ensures the right value in x18 on 
return from the Wine functions, it doesn't help restoring x18 to the right 
value before calling callback functions.

I've run with this approach for nearly two years, without running into any 
issues with it (with the limited amount of code I run in wine; I don't 
think any of the command line executables I run in wine use callbacks 
though).


3) Enhance the compiler to automatically back up x18 on entry to functions 
marked with __attribute__((ms_abi)) and restore it on return. This would 
have the same effect as 2) above (but more elegantly), but also with the 
same limitations wrt callbacks.

(Using this with Wine requires changing where x18 is initialized, because 
when starting a process, x18 can get clobbered after signal_init_thread is 
called, before handing control over to the native code. A patch for this 
has been sent at https://source.winehq.org/patches/data/164651.)

A proof of concept/RFC patch for LLVM to implement this feature has been 
sent at https://reviews.llvm.org/D61892.


4) Enhance the compiler to back up x18 in every function (if necessary) to 
restore it after every function call. (Except function calls to functions 
in the same translation unit, which can be expected to not clobber the 
register.) This should achieve almost everything; x18 is maintained 
containing the right value throughout the Wine code as far as possible, 
like when doing callbacks.

If callbacks to native code is made from within a callback from an 
external library (e.g. qsort in libc), x18 can be lost though. I guess 
this isn't a common pattern for libc functions at least, but I have no 
idea if it's more common for other external feature libraries that Wine 
uses.

A proof of concept/RFC patch for LLVM to implement this feature has been 
sent at https://reviews.llvm.org/D61894. Do note that this approach can be 
pretty controversial to upstream to LLVM.

This also requires the same Wine patch as 3), but for a different reason. 
When signal_init_thread is called to initialize x18, the caller will 
restore the register on return. The most robust way to intitialize it is 
right before handing control over to native code.


5) Enclose every callback call in Wine with a wrapper/thunk that sets up 
the register correctly. This would be a perfect solution, but is 
practically unfeasible. As far as I know, this is the approach that was 
used for Win16 back in the day, calling WOWCallback16Ex every time Wine 
code should call back into Win16 code. Given the size of Wine today and 
the number of different places where callbacks are made (where the 
function pointers are called without any extra wrapping), this is 
unfeasible (and I have a very hard time seeing such a patch accepted into 
Wine).


What are the potential acceptable compromises for taking this matter 
forward?


// Martin



More information about the llvm-dev mailing list