Skip to main content

Wine - Starting Simple

“If you wish to make apple pie from scratch, you must first invent the universe.” ~ Carl Sagan

This also applies to Wine.

Contents

Preamble

Wine (originally an acronym for “Wine is Not an Emulator” as its own site states) is a suite of software for running Windows applications on Linux. It effectively translates, to a format that Linux understands, Windows API calls and supporting elements which make up a pseudo operating system, that is “layered” on top of Linux.

Note: Wine isn’t exclusively Linux, it translates to POSIX calls so works with macOS, FreeBSD, and other systems which are compliant.

Wine aims to provide enough of a Windows-adjacent installation to a wide range of Windows software, including complex games.

A very crude example is the system call for opening a file, if you use Wine to run a program that opens a file, because it’s a format that’s unique to Windows (for whatever reason) then the running application calls what it thinks is the Windows call to open a file, but it is then intercepted by Wine, which forwards it to Linux in a way that Linux understands.

Sometimes these calls are simple, and sometimes they’re very complex; sometimes it’s enough just to fake a response to the application, or even define a function as a stub for the purpose of dynamic linking (but have it error if that stub is actually called).

Installation

A lot of the modern installations of Wine on Linux are handled and managed by some sort of additional application, say Lutris, Steam, Bottles, Heroic, and the like. However, to understand the underpinnings of Wine, it’s important to start at first principles, before we layer more complexity onto an already complex system.

Fedora Focus

My distribution of choice.

In Fedora, there are quite a few Wine packages, and it is possible to install specific components, or just subsets, instead of the entire suite. If you want to avoid what some people might uncharitably call “bloat.”

You might find that only wine-core is installed on your system, as this is a subset of the larger “wine” package and is effectively just the “basic” stuff required to get Wine working. It’s the singular largest package in the list of Wine components.

In Fedora, wine is a “meta-package” which includes everything and the kitchen sink, so the full wine suite. On top of the Wine binaries and libraries, it includes packages for fonts, network shares, audio codecs, DXVK (see below,) LDAP, and much more besides. If your application is complaining about a missing component that it requires to get working, say LDAP to enable Authentication, then you may need to either install one of the other subset packages, or just install the meta-package itself.

For now, we’ll stick with simple:

$ sudo dnf install wine-core

Installed Elements

Focusing on wine-core for a second, you can get a detailed list of what a package installs with dnf repoquery --list wine-core. It’s effectively the wine binaries and libraries required for running Windows software, with no bells and whistles.

$ dnf repoquery --list wine-core | sort -u | wc -l
Last metadata expiration check: 0:09:48 ago on Sun 23 Apr 2023 13:49:50 IST.
1797

That’s a lot of files!

It includes:

  • Wine’s version of Windows DLLs
  • The wine binaries and supporting applications (like wineserver)
  • Unix libraries to interact with the “host system” (e.g. winevulkan.so)

By way of a second example, if we look at wine-dxvk we can see this is a sub-package that adds the upstream DXVK project libraries, which aren’t included in Wine by default because they have different goals and good reasons for not being (note that this isn’t a bad thing, it just means that other houses, like Valve, have to couple the two into consistent bundles).

$ dnf rq --list wine-dxvk
Last metadata expiration check: 0:38:57 ago on Sun 23 Apr 2023 13:18:10 IST.
/usr/lib/wine/i386-windows/dxvk-d3d10.dll
/usr/lib/wine/i386-windows/dxvk-d3d10_1.dll
/usr/lib/wine/i386-windows/dxvk-d3d10core.dll
/usr/lib/wine/i386-windows/dxvk-d3d11.dll
/usr/share/doc/wine-dxvk
/usr/share/doc/wine-dxvk/README.md
/usr/share/licenses/wine-dxvk
/usr/share/licenses/wine-dxvk/LICENSE
/usr/lib64/wine/x86_64-windows/dxvk-d3d10.dll
/usr/lib64/wine/x86_64-windows/dxvk-d3d10_1.dll
/usr/lib64/wine/x86_64-windows/dxvk-d3d10core.dll
/usr/lib64/wine/x86_64-windows/dxvk-d3d11.dll
/usr/share/doc/wine-dxvk
/usr/share/doc/wine-dxvk/README.md
/usr/share/licenses/wine-dxvk
/usr/share/licenses/wine-dxvk/LICENSE

Note: DXVK is a “translation layer” which takes DirectX 9/10/11 calls and translates them to Vulkan equivalents, which is supported on Linux… and Windows actually, you can use DXVK on Windows to get better performance out of games and solve problems. Intel use it for their Arc cards when used to run older games ‘well’ on Windows.

Fedora uses the alternatives system to create a specific symlink based on existing criteria, the alternatives entry (created by a script when installing wine-dxvk and only applied if you have a capable GPU) looks like the following:

$ cat /var/lib/alternatives/wine-d3d11\(x86-64\) 
auto
/usr/lib64/wine/x86_64-windows/d3d11.dll

/usr/lib64/wine/x86_64-windows/wine-d3d11.dll
10
/usr/lib64/wine/x86_64-windows/dxvk-d3d11.dll
20

The higher the priority (20 here) the higher the precedence.

In practice, what this means is that /usr/lib64/wine/x86_64-windows/d3d11.dll is a symlink to /usr/lib64/wine/x86_64-windows/dxvk-d3d11.dll and it’s this file which gets placed in the vaunted d3d11.dll spot in the WINEPREFIX when created:

$ md5sum ~/.test/drive_c/windows/syswow64/d3d11.dll 
fe95e027a4ba886294280b63efc5836b
$ md5sum ~/.test/drive_c/windows/syswow64/dxvk-d3d11.dll 
fe95e027a4ba886294280b63efc5836b
$ md5sum /usr/lib/wine/i386-windows/dxvk-d3d11.dll 
fe95e027a4ba886294280b63efc5836b

Note the use of a 32-bit library here, and the -still confusing- fact that syswow64 is actually the ‘Windows 32-bit on Windows 64-bit’ folder, whereas system32 has the 64-bit DLLs… this is why software engineers shouldn’t be allowed to name things.

On Fedora, with wine-dxvk installed, you don’t need to set any “Native” or “Builtin” options to use DXVK (at least for 10 & 11, 9 seems to still use wine-d3d9.dll at the time of writing.) This is because any application looking to load d3d10.dll, for example, will get the DXVK version.

Basic Example

Now that we have Wine installed (with wine-core) we can have a go at installing some Windows software. For the sake of sanity, I’m going to deliberately use the Windows version of Sauerbraten, but the concepts hold true for other software which doesn’t also have a Linux build.

Once we’ve downloaded an executable from http://sauerbraten.org/ we can run it from the command line (GUIs are available, but again, simple).

wine64 ~/Downloads/sauerbraten_2020_12_21_windows.exe

Note: We’re using wine64 to create a 64-bit Wine Prefix, which can only be set at creation time (of the prefix) and can’t be changed. We want to avoid creating a 32-bit prefix by accident, and then discovering we want a 64-bit one later.

If this is your first time installing anything, specifically in this prefix, you will likely be prompted with a Mono installer. I typically don’t install it unless I discover I need it. Mono is a .NET implementation, which many applications depend upon. Fedora also has a wine-mono sub-package which can be installed to provide this, and it is recommended that if you do need Mono, you install your distribution’s version.

Click ‘Cancel’ on the Mono installer and click through the Sauerbraten install. I leave everything enabled for this example.

It will install to C:\Program Files (x86)\Sauerbraten which is actually our prefix, the default of which is in ~/.wine on Fedora (though this can be changed.)

We can run our Sauerbraten install once it’s completed, by expressly using wine64 again:

$ wine64 ~/.wine/drive_c/Program\ Files\ \(x86\)/Sauerbraten/Sauerbraten.lnk

It should open!

When we want a bit more information about the graphics stack of the game, we can also still use applications like mangohud by prefixing our command:

$ MANGOHUD_CONFIG=full mangohud --dlsym \
    wine64 ~/.wine/drive_c/Program\ Files\ \(x86\)/Sauerbraten/Sauerbraten.lnk

Or if we wanted to use Zink (to use Mesa’s OpenGL -> Vulkan layer) we can even add to our command, and use:

$ MESA_LOADER_DRIVER_OVERRIDE=zink \
    MANGOHUD_CONFIG=full mangohud --dlsym \
    wine64 ~/.wine/drive_c/Program\ Files\ \(x86\)/Sauerbraten/Sauerbraten.lnk

Useful Titbits

Here’s some elements of Wine which it’s handy to be aware of, and which aren’t made overly clear in the documentation, or at first-use.

wineserver

It would be remiss to continue without mentioning the wineserver process. If the Wine DLLs provide reimplementations of Windows libraries, then wineserver provides a reimplementation of system services (and the NT Kernel elements required to run software). It isn’t a complete rewrite of the Windows NT kernel, but it’s a lot.

You can add debug messages (at a normal level if invoked this way) with WINEDEBUG="+server" to get wineserver logs to stderr.

WINEARCH

We’ve already touched briefly on wine64 but it’s worth noting that you can also specify the architecture with WINEARCH=win64 (or 32) and the regular wine command if required, i.e.:

WINEARCH=win64 \
   wine ~/.wine/drive_c/Program\ Files\ \(x86\)/Sauerbraten/Sauerbraten.lnk

WINEPREFIX

The prefix, or pseudo C drive, is the “virtual” (pseudo) Windows installation into which our applications are installed.

When a new prefix is created, the Wine DLLs are copied into it, meaning that yes, you can end up with multiple versions of the same DLLs on your hard drive.

By default, as suggested above, a single prefix is created in your /home user directory, typically. This might be desirable, and might not be, as sometimes you want to use “clean” prefixes for certain software. If you’ve had to install a lot of additional libraries and applications as dependencies for a single application, you might want to create a completely separate prefix for another application, to ensure no strange behaviour thanks to those previous changes.

Note: Purists will point out that you can set individual configuration for different applications inside the same prefix (Valve do this for specific games, if you’ve ever looked at Proton configuration) but I find it adds unnecessary complexity when you’re not shipping a solution.

One example might be that you’d like a clean prefix if you’ve installed the Microsoft version of d3dcompiler_47, and want to use the “builtin” Wine version for another application.

Different prefixes are accomplished with the WINEPREFIX environment variable, if we’d wanted to install Sauerbraten in its own prefix, we could have done so with:

$ WINEPREFIX=~/.sauer wine64 ~/Downloads/sauerbraten_2020_12_21_windows.exe

To delete this prefix, you would simply rm -r ~/.sauer and the application is gone.

WINEDLLOVERRIDES

I hate this subject, because it’s very unfriendly and tricky to explain to newcomers.

Native Dynamic Link Libraries (DLLs) are those which typically come bundled with your application, are sourced from third parties, or which might have been taken from Windows. In a word they’re the “real” version of a DLL, rather than the rewritten one from the Wine developers. These are not included with Wine.

Builtin DLLs are those which the Wine developers have written to be compatible (or as close as they can get) with the native DLLs. These are the ones which are always used by default. They might also offer different functionality where appropriate, such as visual elements which “fit better” on Linux than Windows, but which the application doesn’t care about.

You override which DLL is loaded by an application either with winecfg or at the CLI with WINEDLLOVERRIDES. From the man page:

WINEDLLOVERRIDES="comdlg32,shell32=n,b" 
  Try  to  load  comdlg32 and shell32 as native windows dll first and try the
  builtin version if the native load fails.  

It’s worth noting that if you use an application like winetricks to install native DLLs, you should then instruct the load order of the DLLs. winetricks will install to the prefix, though Wine can still load its builtin DLLs from elsewhere on your filesystem.

Note: Native DLLs are usually loaded from the prefix’s local C:\windows\system32 directory, winetricks for example, will overwrite the copied “builtin” DLLs here, originally from your /usr/lib/wine directory.

I personally think these labels can be misleading.

The Wine User guide has a useful section on DLL overrides.

It also includes this usual DLL load order:

  1. The directory the program was started from.
  2. The current directory.
  3. The Windows system directory.
  4. The Windows directory.
  5. The PATH variable directories.

To see which DLLs are being loaded, you can use WINEDEBUG=+loaddll:

$ WINEDEBUG=+loaddll \
    wine64 ~/.wine/drive_c/Program\ Files\ \(x86\)/Sauerbraten/Sauerbraten.lnk \
    2>&1 | tee sauer.debug

MinGW and System DLLs Aside

Parsing the resulting file, we can check for ’native’ loaded DLLs with something like grep "native" sauer.debug.

In our example, we can see that wine is loading the following from elsewhere on our hard drive (Z: in a prefix is a map to your “host” filesystem):

  • zlib1.dll
  • libssp-0.dll
  • libpng16-16.dll
  • libjpeg-62.dll
  • libtiff-5.dll

Sauerbraten actually ships with these DLLs anyway, so why is Wine loading them from outside the prefix? Specifically from:

/usr/x86_64-w64-mingw32/sys-root/mingw/bin/lib*.dll

For context, these DLLs are provided as part of the mingw64-filesystem package in Fedora, usually used by developers to build Windows packages, on their Linux installation.

MinGW, as a project, encompasses various elements to allow applications to compile and run on Windows systems. Here, it’s providing DLLs for basic functionality that allow Sauerbraten (and Wine) to run whilst using these “Windows” DLLs.

Note: For developers, MinGW can be used to provide a minimal implementation of Windows APIs to those developers using GCC, so they don’t have to make many changes when building their application, or game, for Windows and Linux.

So we understand that these DLLs are here so that Windows applications can be built on Linux, why is Wine loading them, especially when it’s got copies in the Sauerbraten prefix at ~/.wine/drive_c/Program Files/(x86)/Sauerbraten/bin & bin64?

I had to jump onto the #winehq IRC to solve this one, but effectively some DLLs are fixed by Wine at compile-time, and can’t really be overwritten. This patch goes a long way to explaining things. Thanks to Zebediah at CodeWeavers for the pointers.

Fedora sets this value in their wine builds to:

--with-system-dllpath=/usr/x86_64-w64-mingw32/sys-root/mingw/bin

See: https://src.fedoraproject.org/rpms/wine/blob/rawhide/f/wine.spec#_746

How it Works

Now that we’ve proven Wine works by getting our game installed (and having it actually playable) we should take a look into how it works.

a.exe Example

We’re not immediately going to use Sauerbraten for this, because it’s large and complex, instead we’re going to use a small program (compiled locally with the i686-w64-mingw32-gcc compiler) that outputs “Hello, Wine!”:

#include <stdio.h>
int main() {
   printf("Hello, Wine!\n");
   return 0;
}

Like so:

$ WINEDEBUG="fixme-all" wine a.exe 
Hello, Wine!

Note: Removal of the fixme log lines is purely to take some noise out.

Remember that this will use our default prefix, which unless you’ve changed anything, will still be ~/.wine.

Recall earlier I spoke about Windows applications making calls that it expects the underlying “Windows” system to respond to, with answers. In the case of Wine, the application makes calls which are translated into Linux (POSIX) calls, and then responded to, this “interception” is the translation which Wine performs.

We can trace the messages between Wine modules, including DLLs that Wine does with WINEDEBUG=+relay.

Our full command looks like this:

WINEDEBUG="fixme-all,+timestamp,+server,+loaddll,+relay" \
  wine a.exe > a.wine.log 2>&1`

Breaking this down a bit, the WINEDEBUG entries disable the noisy fixme messages; add timestamps to our log line; gather wineserver logs; log the DLLs that are loaded; and enable those relay logs. We’re then saving all stderr and stdout output to a file called a.wine.log.

Note: Relay options can be narrowed down further, by adding Registry entries inside your prefix that deliberately exclude the logs for calls that you name. This is very useful for reducing the size of the log you’re debugging.

Remember that WINEDEBUG and especially +relay will slow things down considerably.

We’re not going to take everything from this output, but we will look at the pertinent messages that surround printing Hello, Wine! to our screen:

22853.760:010c:Call KERNEL32.WriteFile(...) ret=656a16a1
22853.760:010c:Call ntdll.NtWriteFile(...) ret=7b0204b4
Hello, Wine!
22853.760:010c:Ret  ntdll.NtWriteFile() retval=00000000 ret=7b0204b4
22853.760:010c:Ret  KERNEL32.WriteFile() retval=00000001 ret=656a16a1

Note that the functions ntdll.NtWriteFile() and KERNEL32.WriteFile() were invoked.

WriteFile is defined here.

NtWriteFile is defined here.

Our program is built using i686-w64-mingw32-gcc as stated above, so will be built against the MinGW versions of our includes, to work with Windows systems. This means that the WriteFile function defined in this standard set (though it obviously doesn’t have to be the GNU flavour) will be called by our program.

This call is acted upon by the WriteFile logic in the KERNEL32 DLL, then the NtWriteFile ntdll entry in turn, which forwards the request to wineserver here.

wineserver is then responsible for writing our “Hello, Wine!” to the screen.

All of this wouldn’t be possible without the wine binary itself, whose job it is to arrange the program’s instructions and necessary libraries, in memory, prior to execution. wine is compiled to work on your system, so it knows what to act upon and how, when the programs it executes try to perform some function. It’s like a little, temporary, Windows-ish enclave on your running system.

Further Reading

I intend to write more about the various elements of Wine and its related projects, but this is entirely to get my own head around the system. If I’ve made incorrect assumptions or misunderstood any elements, I’m happy to update this document (and my understanding by extension!)

The example given of writing output to a screen above is just one example of a rewritten function that Wine performs for an application. There are several more besides and they can work very differently (like OpenGL) for example.

Wine is a large project with very talented people working on it, and I would support anyone who wants to contribute, financially or with their own time.

CodeWeavers provide an officially supported version of Wine.

References

PE Migration - https://www.winehq.org/pipermail/wine-devel/2022-April/213677.html
IRC - #winehq on Libera.Chat
Debugging Wine - https://wiki.winehq.org/Wine_Developer%27s_Guide/Debugging_Wine
Debug Channels - https://wiki.winehq.org/Debug_Channels#Useful_Channels