How Wine 101 Works

Wine is a compatibility layer capable of running Windows applications on several POSIX-compliant operating systems, such as Linux, macOS and BSD (https://www.winehq.org).

If you've been using Linux for a while now, chances are you've used Wine before. Maybe to run that all-important Windows program that doesn't have a Linux version or maybe to play World of Warcraft or some other game. Fun fact, Valve's Steam Deck uses a Wine-based solution to run games (called Proton).

Last year I spent quite a bit of time working on a debugger that could debug both the Wine layer and the Windows application running on it. It was very interesting to learn more about the internals of Wine - I've used Wine many times before, but never found out how it worked. If you've ever wondered how it's possible to take a Windows executable and run it on Linux without any modifications, welcome to this article!

I have a rough knowledge of many things!

This article greatly oversimplifies reality and I don't pretend to know all the details. However, I hope the explanation here gives you a general understanding of how things like Wine work.

Before describing how Wine works, let's explore how it doesn't work. Wine is a recursive acronym and stands for "Wine Is Not an Emulator". Why isn't it? There are plenty of great emulators out there, both for older architectures and for modern consoles. Could Wine be implemented as an emulator? Yes, but there are good reasons not to. Let's take a look at how emulators generally work.

Imagine we have simple hardware, with two instructions:

push - pushes the given value to the stack setpxl - pulls three values ​​from the stack and draws a pixel with color arg1 to (arg2, arg3)

(should be enough to make some cool demo scenes, right?)

> dump-instructions game.rom ... # draw a red dot at (10,10) push 10 push 10 push 0xFF0000 setpxl # draw a green dot at (15,15) push 15 push 15 press 0x00FF00 setpxl

The game binary (or ROM cartridge) is a sequence of these instructions, which the hardware can load into memory and then execute. Real hardware can run them natively, but what if we want to play the game on our modern laptop? We are going to create a software emulator - a program that loads the ROM into memory and then executes its instructions. An interpreter or a virtual machine, if you prefer. The emulator implementation for our two-statement console can be quite simple:

enumopcode { Push (i32), SetPixel, }; let program: Vec = read_program("game.rom"); let mut window = create_new_window(160, 144); // Virtual screen of 160x144 pixels let mutstack = Vec::new(); // Stack to pass arguments for the opcode in the program { match opcode { Opcode::Push(value) => { stack.push(value); } Opcode::SetPixel => { let color = stack.pop(); let x = stack.pop(); let y = stack.pop(); window.set_pixel(x, y, color); } } }

Real emulators are much more complicated, but the basic idea is the same: keep some context (memory, registers, etc.), handle input (e.g. keyboard/mouse) and output ( for example, draw in a window), analyze input data (ROM) and execute the instructions one by one, applying their side effects.

This could be one way to implement Wine, but there are two reasons against it. First, emulators are "slow" - there is significant overhead on programmatically executing each instruction. This may be acceptable for older hardware, but not so much for state-of-the-art (and video games have always been one of the most demanding types of applications). The second reason is that there is no need! Linux/macOS are perfectly capable of running Windows binaries natively, they just need a little nudge…

Let's compile a simple program for Linux and Windows and compare the results:

int foo(int x) { return x * x; } main int(int argc) { int code = foo(argc); return code; }

image (left – Linux, right – Windows)

The results are visibly different, but the instruction set is actually the same: push, pop, mov, add, sub, imul, ret. So if we have...

How Wine 101 Works

Wine is a compatibility layer capable of running Windows applications on several POSIX-compliant operating systems, such as Linux, macOS and BSD (https://www.winehq.org).

If you've been using Linux for a while now, chances are you've used Wine before. Maybe to run that all-important Windows program that doesn't have a Linux version or maybe to play World of Warcraft or some other game. Fun fact, Valve's Steam Deck uses a Wine-based solution to run games (called Proton).

Last year I spent quite a bit of time working on a debugger that could debug both the Wine layer and the Windows application running on it. It was very interesting to learn more about the internals of Wine - I've used Wine many times before, but never found out how it worked. If you've ever wondered how it's possible to take a Windows executable and run it on Linux without any modifications, welcome to this article!

I have a rough knowledge of many things!

This article greatly oversimplifies reality and I don't pretend to know all the details. However, I hope the explanation here gives you a general understanding of how things like Wine work.

Before describing how Wine works, let's explore how it doesn't work. Wine is a recursive acronym and stands for "Wine Is Not an Emulator". Why isn't it? There are plenty of great emulators out there, both for older architectures and for modern consoles. Could Wine be implemented as an emulator? Yes, but there are good reasons not to. Let's take a look at how emulators generally work.

Imagine we have simple hardware, with two instructions:

push - pushes the given value to the stack setpxl - pulls three values ​​from the stack and draws a pixel with color arg1 to (arg2, arg3)

(should be enough to make some cool demo scenes, right?)

> dump-instructions game.rom ... # draw a red dot at (10,10) push 10 push 10 push 0xFF0000 setpxl # draw a green dot at (15,15) push 15 push 15 press 0x00FF00 setpxl

The game binary (or ROM cartridge) is a sequence of these instructions, which the hardware can load into memory and then execute. Real hardware can run them natively, but what if we want to play the game on our modern laptop? We are going to create a software emulator - a program that loads the ROM into memory and then executes its instructions. An interpreter or a virtual machine, if you prefer. The emulator implementation for our two-statement console can be quite simple:

enumopcode { Push (i32), SetPixel, }; let program: Vec = read_program("game.rom"); let mut window = create_new_window(160, 144); // Virtual screen of 160x144 pixels let mutstack = Vec::new(); // Stack to pass arguments for the opcode in the program { match opcode { Opcode::Push(value) => { stack.push(value); } Opcode::SetPixel => { let color = stack.pop(); let x = stack.pop(); let y = stack.pop(); window.set_pixel(x, y, color); } } }

Real emulators are much more complicated, but the basic idea is the same: keep some context (memory, registers, etc.), handle input (e.g. keyboard/mouse) and output ( for example, draw in a window), analyze input data (ROM) and execute the instructions one by one, applying their side effects.

This could be one way to implement Wine, but there are two reasons against it. First, emulators are "slow" - there is significant overhead on programmatically executing each instruction. This may be acceptable for older hardware, but not so much for state-of-the-art (and video games have always been one of the most demanding types of applications). The second reason is that there is no need! Linux/macOS are perfectly capable of running Windows binaries natively, they just need a little nudge…

Let's compile a simple program for Linux and Windows and compare the results:

int foo(int x) { return x * x; } main int(int argc) { int code = foo(argc); return code; }

image (left – Linux, right – Windows)

The results are visibly different, but the instruction set is actually the same: push, pop, mov, add, sub, imul, ret. So if we have...

What's Your Reaction?

like

dislike

love

funny

angry

sad

wow