How to call Linux code from a Wine process

But why?

There has been a longstanding shitpost that "Win32 is the stable Linux ABI". A huge amount of software (including/especially older games) designed for Windows works just as well (or sometimes better) on Linux when running via Wine/Proton. Meanwhile, the solutions for robustly packaging user-facing software for Linux continue to remain in flux with various technical issues that "someone" really ought to fix.

As a complementary solution to fixing Linux software distribution problems, suppose you want to enhance support for Linux for a program that is otherwise built for Windows. It turns out that Wine has some really useful mechanisms for this, but they're all poorly documented. I have gathered what is hopefully all of the useful and necessary information related to this all in one place.

Wine Is Not an Emulator

When running a Windows program under Wine on x86_64, both Windows code and Linux code exist in the same address space in a single process. You can see this by viewing /proc/xxx/maps for a Wine process:

$ cat /proc/138689/maps
7ff60000-7ffd8000 ---p 00000000 00:00 0
7ffd8000-7ffe0000 rw-p 00000000 00:00 0
7ffe0000-7ffe1000 r--s 00000000 00:01 176                                /memfd:wine-mapping (deleted)
7ffe1000-7ffe2000 rw-p 00000000 00:00 0
7ffe2000-7fff0000 ---p 00000000 00:00 0
140000000-140001000 r--p 00000000 08:03 2259098                          /usr/lib/wine/x86_64-windows/notepad.exe
140001000-140008000 r-xp 00001000 08:03 2259098                          /usr/lib/wine/x86_64-windows/notepad.exe
140008000-140009000 rw-p 00008000 08:03 2259098                          /usr/lib/wine/x86_64-windows/notepad.exe
140009000-14000d000 r--p 00009000 08:03 2259098                          /usr/lib/wine/x86_64-windows/notepad.exe
14000d000-14000f000 rw-p 00000000 00:00 0
14000f000-140045000 r--p 0000d000 08:03 2259098                          /usr/lib/wine/x86_64-windows/notepad.exe
140045000-140071000 rw-p 00000000 00:00 0
140071000-140072000 r--p 00043000 08:03 2259098                          /usr/lib/wine/x86_64-windows/notepad.exe
55555755c000-5555578b9000 rw-p 00000000 00:00 0                          [heap]
6ffffadb0000-6ffffadb1000 r--p 00000000 08:03 2259267                    /usr/lib/wine/x86_64-windows/uxtheme.dll
6ffffadb1000-6ffffadc6000 r-xp 00001000 08:03 2259267                    /usr/lib/wine/x86_64-windows/uxtheme.dll
6ffffadc6000-6ffffadc7000 rw-p 00016000 08:03 2259267                    /usr/lib/wine/x86_64-windows/uxtheme.dll
6ffffadc7000-6ffffade3000 r--p 00017000 08:03 2259267                    /usr/lib/wine/x86_64-windows/uxtheme.dll
6ffffade3000-6ffffade4000 rw-p 00000000 00:00 0
6ffffade4000-6ffffade9000 r--p 00033000 08:03 2259267                    /usr/lib/wine/x86_64-windows/uxtheme.dll
6ffffade9000-6ffffae82000 rw-p 00000000 00:00 0
6ffffae82000-6ffffae83000 r--p 00038000 08:03 2259267                    /usr/lib/wine/x86_64-windows/uxtheme.dll
6ffffaea0000-6ffffaea1000 r--p 00000000 08:03 2257757                    /usr/lib/wine/x86_64-windows/comdlg32.dll
6ffffaea1000-6ffffaed0000 r-xp 00001000 08:03 2257757                    /usr/lib/wine/x86_64-windows/comdlg32.dll
6ffffaed0000-6ffffaed1000 rw-p 00030000 08:03 2257757                    /usr/lib/wine/x86_64-windows/comdlg32.dll
6ffffaed1000-6ffffaeee000 r--p 00031000 08:03 2257757                    /usr/lib/wine/x86_64-windows/comdlg32.dll
6ffffaeee000-6ffffaeef000 rw-p 00000000 00:00 0
6ffffaeef000-6ffffafa3000 r--p 0004e000 08:03 2257757                    /usr/lib/wine/x86_64-windows/comdlg32.dll
6ffffafa3000-6ffffb101000 rw-p 00000000 00:00 0
6ffffb101000-6ffffb102000 r--p 00102000 08:03 2257757                    /usr/lib/wine/x86_64-windows/comdlg32.dll
6ffffb120000-6ffffb121000 r--p 00000000 08:03 2259113                    /usr/lib/wine/x86_64-windows/ole32.dll
6ffffb121000-6ffffb179000 r-xp 00001000 08:03 2259113                    /usr/lib/wine/x86_64-windows/ole32.dll
6ffffb179000-6ffffb17b000 rw-p 00059000 08:03 2259113                    /usr/lib/wine/x86_64-windows/ole32.dll
6ffffb17b000-6ffffb1af000 r--p 0005b000 08:03 2259113                    /usr/lib/wine/x86_64-windows/ole32.dll
6ffffb1af000-6ffffb1b0000 rw-p 00000000 00:00 0
6ffffb1b0000-6ffffb1df000 r--p 0008f000 08:03 2259113                    /usr/lib/wine/x86_64-windows/ole32.dll
6ffffb1df000-6ffffb538000 rw-p 00000000 00:00 0
6ffffb538000-6ffffb539000 r--p 000be000 08:03 2259113                    /usr/lib/wine/x86_64-windows/ole32.dll
6ffffb890000-6ffffb891000 r--p 00000000 08:03 2259023                    /usr/lib/wine/x86_64-windows/msimg32.dll
6ffffb891000-6ffffb892000 r-xp 00001000 08:03 2259023                    /usr/lib/wine/x86_64-windows/msimg32.dll
6ffffb892000-6ffffb893000 rw-p 00002000 08:03 2259023                    /usr/lib/wine/x86_64-windows/msimg32.dll
6ffffb893000-6ffffb897000 r--p 00003000 08:03 2259023                    /usr/lib/wine/x86_64-windows/msimg32.dll
6ffffb897000-6ffffb898000 rw-p 00000000 00:00 0
6ffffb898000-6ffffb89b000 r--p 00007000 08:03 2259023                    /usr/lib/wine/x86_64-windows/msimg32.dll
6ffffb89b000-6ffffb8a9000 rw-p 00000000 00:00 0
6ffffb8a9000-6ffffb8aa000 r--p 0000a000 08:03 2259023                    /usr/lib/wine/x86_64-windows/msimg32.dll
6ffffb8c0000-6ffffb8c1000 r--p 00000000 08:03 2259355                    /usr/lib/wine/x86_64-windows/winex11.drv
6ffffb8c1000-6ffffb8c2000 r-xp 00001000 08:03 2259355                    /usr/lib/wine/x86_64-windows/winex11.drv
6ffffb8c2000-6ffffb8c3000 rw-p 00002000 08:03 2259355                    /usr/lib/wine/x86_64-windows/winex11.drv
6ffffb8c3000-6ffffb8c7000 r--p 00003000 08:03 2259355                    /usr/lib/wine/x86_64-windows/winex11.drv
6ffffb8c7000-6ffffb8c8000 rw-p 00000000 00:00 0
6ffffb8c8000-6ffffb8cb000 r--p 00007000 08:03 2259355                    /usr/lib/wine/x86_64-windows/winex11.drv
6ffffb8cb000-6ffffb8d4000 rw-p 00000000 00:00 0
6ffffb8d4000-6ffffb8d5000 r--p 0000a000 08:03 2259355                    /usr/lib/wine/x86_64-windows/winex11.drv
6ffffb8f0000-6ffffb8f1000 r--p 00000000 08:03 2257758                    /usr/lib/wine/x86_64-windows/coml2.dll
6ffffb8f1000-6ffffb8f5000 r-xp 00001000 08:03 2257758                    /usr/lib/wine/x86_64-windows/coml2.dll
6ffffb8f5000-6ffffb8f6000 rw-p 00005000 08:03 2257758                    /usr/lib/wine/x86_64-windows/coml2.dll
6ffffb8f6000-6ffffb90a000 r--p 00006000 08:03 2257758                    /usr/lib/wine/x86_64-windows/coml2.dll
6ffffb90a000-6ffffb90b000 rw-p 00000000 00:00 0
6ffffb90b000-6ffffb90e000 r--p 0001a000 08:03 2257758                    /usr/lib/wine/x86_64-windows/coml2.dll
6ffffb90e000-6ffffb94f000 rw-p 00000000 00:00 0
6ffffb94f000-6ffffb950000 r--p 0001d000 08:03 2257758                    /usr/lib/wine/x86_64-windows/coml2.dll
6ffffb960000-6ffffb961000 r--p 00000000 08:03 2257753                    /usr/lib/wine/x86_64-windows/combase.dll
6ffffb961000-6ffffb98b000 r-xp 00001000 08:03 2257753                    /usr/lib/wine/x86_64-windows/combase.dll
6ffffb98b000-6ffffb98c000 rw-p 0002b000 08:03 2257753                    /usr/lib/wine/x86_64-windows/combase.dll
6ffffb98c000-6ffffb9ad000 r--p 0002c000 08:03 2257753                    /usr/lib/wine/x86_64-windows/combase.dll
6ffffb9ad000-6ffffb9ae000 rw-p 00000000 00:00 0
6ffffb9ae000-6ffffb9b4000 r--p 0004d000 08:03 2257753                    /usr/lib/wine/x86_64-windows/combase.dll
6ffffb9b4000-6ffffbb23000 rw-p 00000000 00:00 0
6ffffbb23000-6ffffbb24000 r--p 00053000 08:03 2257753                    /usr/lib/wine/x86_64-windows/combase.dll
6ffffbb40000-6ffffbb41000 r--p 00000000 08:03 2259115                    /usr/lib/wine/x86_64-windows/oleaut32.dll
6ffffbb41000-6ffffbba1000 r-xp 00001000 08:03 2259115                    /usr/lib/wine/x86_64-windows/oleaut32.dll
6ffffbba1000-6ffffbba4000 rw-p 00061000 08:03 2259115                    /usr/lib/wine/x86_64-windows/oleaut32.dll
6ffffbba4000-6ffffbbd9000 r--p 00064000 08:03 2259115                    /usr/lib/wine/x86_64-windows/oleaut32.dll
6ffffbbd9000-6ffffbc12000 rw-p 00000000 00:00 0
6ffffbc12000-6ffffbc1f000 r--p 00099000 08:03 2259115                    /usr/lib/wine/x86_64-windows/oleaut32.dll
6ffffbc1f000-6ffffbed6000 rw-p 00000000 00:00 0
6ffffbed6000-6ffffbed7000 r--p 000a6000 08:03 2259115                    /usr/lib/wine/x86_64-windows/oleaut32.dll
6ffffbef0000-6ffffbef1000 r--p 00000000 08:03 2257756                    /usr/lib/wine/x86_64-windows/comctl32_v6.dll
6ffffbef1000-6ffffbfa9000 r-xp 00001000 08:03 2257756                    /usr/lib/wine/x86_64-windows/comctl32_v6.dll
6ffffbfa9000-6ffffbfaa000 rw-p 000b9000 08:03 2257756                    /usr/lib/wine/x86_64-windows/comctl32_v6.dll
6ffffbfaa000-6ffffbfe0000 r--p 000ba000 08:03 2257756                    /usr/lib/wine/x86_64-windows/comctl32_v6.dll
6ffffbfe0000-6ffffbfe2000 rw-p 00000000 00:00 0
6ffffbfe2000-6ffffc03a000 r--p 000f0000 08:03 2257756                    /usr/lib/wine/x86_64-windows/comctl32_v6.dll
6ffffc03a000-6ffffc4c1000 rw-p 00000000 00:00 0
6ffffc4c1000-6ffffc4c2000 r--p 00148000 08:03 2257756                    /usr/lib/wine/x86_64-windows/comctl32_v6.dll
6ffffc4e0000-6ffffc4e1000 r--p 00000000 08:03 2259205                    /usr/lib/wine/x86_64-windows/shcore.dll
6ffffc4e1000-6ffffc4ec000 r-xp 00001000 08:03 2259205                    /usr/lib/wine/x86_64-windows/shcore.dll
6ffffc4ec000-6ffffc4ed000 rw-p 0000c000 08:03 2259205                    /usr/lib/wine/x86_64-windows/shcore.dll
6ffffc4ed000-6ffffc4f6000 r--p 0000d000 08:03 2259205                    /usr/lib/wine/x86_64-windows/shcore.dll
6ffffc4f6000-6ffffc4f7000 rw-p 00000000 00:00 0
6ffffc4f7000-6ffffc4fa000 r--p 00016000 08:03 2259205                    /usr/lib/wine/x86_64-windows/shcore.dll
6ffffc4fa000-6ffffc53f000 rw-p 00000000 00:00 0
6ffffc53f000-6ffffc540000 r--p 00019000 08:03 2259205                    /usr/lib/wine/x86_64-windows/shcore.dll
6ffffc550000-6ffffc551000 r--p 00000000 08:03 2259210                    /usr/lib/wine/x86_64-windows/shlwapi.dll
6ffffc551000-6ffffc571000 r-xp 00001000 08:03 2259210                    /usr/lib/wine/x86_64-windows/shlwapi.dll
6ffffc571000-6ffffc572000 rw-p 00021000 08:03 2259210                    /usr/lib/wine/x86_64-windows/shlwapi.dll
6ffffc572000-6ffffc590000 r--p 00022000 08:03 2259210                    /usr/lib/wine/x86_64-windows/shlwapi.dll
6ffffc590000-6ffffc591000 rw-p 00000000 00:00 0
6ffffc591000-6ffffc5a2000 r--p 00040000 08:03 2259210                    /usr/lib/wine/x86_64-windows/shlwapi.dll
6ffffc5a2000-6ffffc676000 rw-p 00000000 00:00 0
6ffffc676000-6ffffc677000 r--p 00051000 08:03 2259210                    /usr/lib/wine/x86_64-windows/shlwapi.dll
6ffffc690000-6ffffc691000 r--p 00000000 08:03 2259208                    /usr/lib/wine/x86_64-windows/shell32.dll
6ffffc691000-6ffffc721000 r-xp 00001000 08:03 2259208                    /usr/lib/wine/x86_64-windows/shell32.dll
6ffffc721000-6ffffc723000 rw-p 00091000 08:03 2259208                    /usr/lib/wine/x86_64-windows/shell32.dll
6ffffc723000-6ffffc76a000 r--p 00093000 08:03 2259208                    /usr/lib/wine/x86_64-windows/shell32.dll
6ffffc76a000-6ffffc76b000 rw-p 00000000 00:00 0
6ffffc76b000-6ffffcfde000 r--p 000da000 08:03 2259208                    /usr/lib/wine/x86_64-windows/shell32.dll
6ffffcfde000-6ffffd4b9000 rw-p 00000000 00:00 0
6ffffd4b9000-6ffffd4ba000 r--p 0094d000 08:03 2259208                    /usr/lib/wine/x86_64-windows/shell32.dll
6ffffd910000-6ffffd911000 r--p 00000000 08:03 2258356                    /usr/lib/wine/x86_64-windows/imm32.dll
6ffffd911000-6ffffd920000 r-xp 00001000 08:03 2258356                    /usr/lib/wine/x86_64-windows/imm32.dll
6ffffd920000-6ffffd921000 rw-p 00010000 08:03 2258356                    /usr/lib/wine/x86_64-windows/imm32.dll
6ffffd921000-6ffffd92a000 r--p 00011000 08:03 2258356                    /usr/lib/wine/x86_64-windows/imm32.dll
6ffffd92a000-6ffffd92b000 rw-p 00000000 00:00 0
6ffffd92b000-6ffffd931000 r--p 0001a000 08:03 2258356                    /usr/lib/wine/x86_64-windows/imm32.dll
6ffffd931000-6ffffd997000 rw-p 00000000 00:00 0
6ffffd997000-6ffffd998000 r--p 00020000 08:03 2258356                    /usr/lib/wine/x86_64-windows/imm32.dll
6ffffd9b0000-6ffffd9b1000 r--p 00000000 08:03 2259299                    /usr/lib/wine/x86_64-windows/win32u.dll
6ffffd9b1000-6ffffd9c8000 r-xp 00001000 08:03 2259299                    /usr/lib/wine/x86_64-windows/win32u.dll
6ffffd9c8000-6ffffd9c9000 rw-p 00018000 08:03 2259299                    /usr/lib/wine/x86_64-windows/win32u.dll
6ffffd9c9000-6ffffd9da000 r--p 00019000 08:03 2259299                    /usr/lib/wine/x86_64-windows/win32u.dll
6ffffd9da000-6ffffd9db000 rw-p 00000000 00:00 0
6ffffd9db000-6ffffd9eb000 r--p 0002a000 08:03 2259299                    /usr/lib/wine/x86_64-windows/win32u.dll
6ffffd9eb000-6ffffd9fe000 rw-p 00000000 00:00 0
6ffffd9fe000-6ffffd9ff000 r--p 0003a000 08:03 2259299                    /usr/lib/wine/x86_64-windows/win32u.dll
6ffffda10000-6ffffda11000 r--p 00000000 08:03 2258320                    /usr/lib/wine/x86_64-windows/gdi32.dll
6ffffda11000-6ffffda63000 r-xp 00001000 08:03 2258320                    /usr/lib/wine/x86_64-windows/gdi32.dll
6ffffda63000-6ffffda64000 rw-p 00053000 08:03 2258320                    /usr/lib/wine/x86_64-windows/gdi32.dll
6ffffda64000-6ffffda88000 r--p 00054000 08:03 2258320                    /usr/lib/wine/x86_64-windows/gdi32.dll
6ffffda88000-6ffffda89000 rw-p 00000000 00:00 0
6ffffda89000-6ffffda96000 r--p 00078000 08:03 2258320                    /usr/lib/wine/x86_64-windows/gdi32.dll
6ffffda96000-6ffffdcce000 rw-p 00000000 00:00 0
6ffffdcce000-6ffffdccf000 r--p 00085000 08:03 2258320                    /usr/lib/wine/x86_64-windows/gdi32.dll
6ffffdce0000-6ffffdce1000 r--p 00000000 08:03 2259263                    /usr/lib/wine/x86_64-windows/user32.dll
6ffffdce1000-6ffffdd7c000 r-xp 00001000 08:03 2259263                    /usr/lib/wine/x86_64-windows/user32.dll
6ffffdd7c000-6ffffdd7d000 rw-p 0009c000 08:03 2259263                    /usr/lib/wine/x86_64-windows/user32.dll
6ffffdd7d000-6ffffddb3000 r--p 0009d000 08:03 2259263                    /usr/lib/wine/x86_64-windows/user32.dll
6ffffddb3000-6ffffddb4000 rw-p 00000000 00:00 0
6ffffddb4000-6ffffdeaa000 r--p 000d3000 08:03 2259263                    /usr/lib/wine/x86_64-windows/user32.dll
6ffffdeaa000-6ffffe305000 rw-p 00000000 00:00 0
6ffffe305000-6ffffe306000 r--p 001c9000 08:03 2259263                    /usr/lib/wine/x86_64-windows/user32.dll
6ffffe5e0000-6ffffe5e1000 r--p 00000000 08:03 2257769                    /usr/lib/wine/x86_64-windows/cryptbase.dll
6ffffe5e1000-6ffffe5e3000 r-xp 00001000 08:03 2257769                    /usr/lib/wine/x86_64-windows/cryptbase.dll
6ffffe5e3000-6ffffe5e4000 rw-p 00003000 08:03 2257769                    /usr/lib/wine/x86_64-windows/cryptbase.dll
6ffffe5e4000-6ffffe5e9000 r--p 00004000 08:03 2257769                    /usr/lib/wine/x86_64-windows/cryptbase.dll
6ffffe5e9000-6ffffe5eb000 rw-p 00000000 00:00 0
6ffffe5eb000-6ffffe5ee000 r--p 00009000 08:03 2257769                    /usr/lib/wine/x86_64-windows/cryptbase.dll
6ffffe5ee000-6ffffe608000 rw-p 00000000 00:00 0
6ffffe608000-6ffffe609000 r--p 0000c000 08:03 2257769                    /usr/lib/wine/x86_64-windows/cryptbase.dll
6ffffe620000-6ffffe621000 r--p 00000000 08:03 2259168                    /usr/lib/wine/x86_64-windows/rpcrt4.dll
6ffffe621000-6ffffe672000 r-xp 00001000 08:03 2259168                    /usr/lib/wine/x86_64-windows/rpcrt4.dll
6ffffe672000-6ffffe673000 rw-p 00052000 08:03 2259168                    /usr/lib/wine/x86_64-windows/rpcrt4.dll
6ffffe673000-6ffffe6a2000 r--p 00053000 08:03 2259168                    /usr/lib/wine/x86_64-windows/rpcrt4.dll
6ffffe6a2000-6ffffe6a3000 rw-p 00000000 00:00 0
6ffffe6a3000-6ffffe6ac000 r--p 00082000 08:03 2259168                    /usr/lib/wine/x86_64-windows/rpcrt4.dll
6ffffe6ac000-6ffffe8f4000 rw-p 00000000 00:00 0
6ffffe8f4000-6ffffe8f5000 r--p 0008b000 08:03 2259168                    /usr/lib/wine/x86_64-windows/rpcrt4.dll
6ffffe910000-6ffffe911000 r--p 00000000 08:03 2259252                    /usr/lib/wine/x86_64-windows/ucrtbase.dll
6ffffe911000-6ffffe9a3000 r-xp 00001000 08:03 2259252                    /usr/lib/wine/x86_64-windows/ucrtbase.dll
6ffffe9a3000-6ffffe9a5000 rw-p 00093000 08:03 2259252                    /usr/lib/wine/x86_64-windows/ucrtbase.dll
6ffffe9a5000-6ffffe9d2000 r--p 00095000 08:03 2259252                    /usr/lib/wine/x86_64-windows/ucrtbase.dll
6ffffe9d2000-6ffffe9d5000 rw-p 00000000 00:00 0
6ffffe9d5000-6ffffe9e7000 r--p 000c2000 08:03 2259252                    /usr/lib/wine/x86_64-windows/ucrtbase.dll
6ffffe9e7000-6ffffed5a000 rw-p 00000000 00:00 0
6ffffed5a000-6ffffed5b000 r--p 000d4000 08:03 2259252                    /usr/lib/wine/x86_64-windows/ucrtbase.dll
6ffffed70000-6ffffed71000 r--p 00000000 08:03 2259194                    /usr/lib/wine/x86_64-windows/sechost.dll
6ffffed71000-6ffffed80000 r-xp 00001000 08:03 2259194                    /usr/lib/wine/x86_64-windows/sechost.dll
6ffffed80000-6ffffed81000 rw-p 00010000 08:03 2259194                    /usr/lib/wine/x86_64-windows/sechost.dll
6ffffed81000-6ffffed8c000 r--p 00011000 08:03 2259194                    /usr/lib/wine/x86_64-windows/sechost.dll
6ffffed8c000-6ffffed8d000 rw-p 00000000 00:00 0
6ffffed8d000-6ffffed91000 r--p 0001c000 08:03 2259194                    /usr/lib/wine/x86_64-windows/sechost.dll
6ffffed91000-6ffffee00000 rw-p 00000000 00:00 0
6ffffee00000-6ffffee01000 r--p 00020000 08:03 2259194                    /usr/lib/wine/x86_64-windows/sechost.dll
6ffffee20000-6ffffee21000 r--p 00000000 08:03 2259067                    /usr/lib/wine/x86_64-windows/msvcrt.dll
6ffffee21000-6ffffee96000 r-xp 00001000 08:03 2259067                    /usr/lib/wine/x86_64-windows/msvcrt.dll
6ffffee96000-6ffffee98000 rw-p 00076000 08:03 2259067                    /usr/lib/wine/x86_64-windows/msvcrt.dll
6ffffee98000-6ffffeeb6000 r--p 00078000 08:03 2259067                    /usr/lib/wine/x86_64-windows/msvcrt.dll
6ffffeeb6000-6ffffeeb8000 rw-p 00000000 00:00 0
6ffffeeb8000-6ffffeec3000 r--p 00096000 08:03 2259067                    /usr/lib/wine/x86_64-windows/msvcrt.dll
6ffffeec3000-6fffff1be000 rw-p 00000000 00:00 0
6fffff1be000-6fffff1bf000 r--p 000a1000 08:03 2259067                    /usr/lib/wine/x86_64-windows/msvcrt.dll
6fffff1d0000-6fffff1d1000 r--p 00000000 08:03 2257664                    /usr/lib/wine/x86_64-windows/advapi32.dll
6fffff1d1000-6fffff1f7000 r-xp 00001000 08:03 2257664                    /usr/lib/wine/x86_64-windows/advapi32.dll
6fffff1f7000-6fffff1f8000 rw-p 00027000 08:03 2257664                    /usr/lib/wine/x86_64-windows/advapi32.dll
6fffff1f8000-6fffff209000 r--p 00028000 08:03 2257664                    /usr/lib/wine/x86_64-windows/advapi32.dll
6fffff209000-6fffff20a000 rw-p 00000000 00:00 0
6fffff20a000-6fffff215000 r--p 00039000 08:03 2257664                    /usr/lib/wine/x86_64-windows/advapi32.dll
6fffff215000-6fffff30f000 rw-p 00000000 00:00 0
6fffff30f000-6fffff310000 r--p 00044000 08:03 2257664                    /usr/lib/wine/x86_64-windows/advapi32.dll
6fffff320000-6fffff321000 r--p 00000000 08:03 2258377                    /usr/lib/wine/x86_64-windows/kernelbase.dll
6fffff321000-6fffff3b2000 r-xp 00001000 08:03 2258377                    /usr/lib/wine/x86_64-windows/kernelbase.dll
6fffff3b2000-6fffff3b4000 rw-p 00092000 08:03 2258377                    /usr/lib/wine/x86_64-windows/kernelbase.dll
6fffff3b4000-6fffff3e4000 r--p 00094000 08:03 2258377                    /usr/lib/wine/x86_64-windows/kernelbase.dll
6fffff3e4000-6fffff3e7000 rw-p 00000000 00:00 0
6fffff3e7000-6fffff5e2000 r--p 000c4000 08:03 2258377                    /usr/lib/wine/x86_64-windows/kernelbase.dll
6fffff5e2000-6fffff9a0000 rw-p 00000000 00:00 0
6fffff9a0000-6fffff9a1000 r--p 002bf000 08:03 2258377                    /usr/lib/wine/x86_64-windows/kernelbase.dll
6fffff9c0000-6fffff9c1000 r--p 00000000 08:03 2258376                    /usr/lib/wine/x86_64-windows/kernel32.dll
6fffff9c1000-6fffff9f5000 r-xp 00001000 08:03 2258376                    /usr/lib/wine/x86_64-windows/kernel32.dll
6fffff9f5000-6fffff9f6000 rw-p 00035000 08:03 2258376                    /usr/lib/wine/x86_64-windows/kernel32.dll
6fffff9f6000-6fffffa07000 r--p 00036000 08:03 2258376                    /usr/lib/wine/x86_64-windows/kernel32.dll
6fffffa07000-6fffffa08000 rw-p 00000000 00:00 0
6fffffa08000-6fffffa24000 r--p 00047000 08:03 2258376                    /usr/lib/wine/x86_64-windows/kernel32.dll
6fffffa24000-6fffffb8e000 rw-p 00000000 00:00 0
6fffffb8e000-6fffffb8f000 r--p 00063000 08:03 2258376                    /usr/lib/wine/x86_64-windows/kernel32.dll
6fffffba0000-6fffffba1000 r--p 00000000 08:03 2259103                    /usr/lib/wine/x86_64-windows/ntdll.dll
6fffffba1000-6fffffc22000 r-xp 00001000 08:03 2259103                    /usr/lib/wine/x86_64-windows/ntdll.dll
6fffffc22000-6fffffc23000 rw-p 00082000 08:03 2259103                    /usr/lib/wine/x86_64-windows/ntdll.dll
6fffffc23000-6fffffc4d000 r--p 00083000 08:03 2259103                    /usr/lib/wine/x86_64-windows/ntdll.dll
6fffffc4d000-6fffffc51000 rw-p 00000000 00:00 0
6fffffc51000-6fffffc5f000 r--p 000ad000 08:03 2259103                    /usr/lib/wine/x86_64-windows/ntdll.dll
6fffffc5f000-6ffffffe0000 rw-p 00000000 00:00 0
6ffffffe0000-6ffffffe1000 r--p 000bb000 08:03 2259103                    /usr/lib/wine/x86_64-windows/ntdll.dll
7f5831a5b000-7f5832200000 rw-p 00000000 00:00 0
7f5832200000-7f583229a000 r--p 00000000 08:03 1969967                    /usr/lib/libstdc++.so.6.0.35
7f583229a000-7f583241c000 r-xp 0009a000 08:03 1969967                    /usr/lib/libstdc++.so.6.0.35
7f583241c000-7f58324bc000 r--p 0021c000 08:03 1969967                    /usr/lib/libstdc++.so.6.0.35
7f58324bc000-7f58324cd000 r--p 002bb000 08:03 1969967                    /usr/lib/libstdc++.so.6.0.35
7f58324cd000-7f58324ce000 rw-p 002cc000 08:03 1969967                    /usr/lib/libstdc++.so.6.0.35
7f58324ce000-7f58324d2000 rw-p 00000000 00:00 0
7f5832600000-7f5834594000 r--p 00000000 08:03 1987363                    /usr/lib/libicudata.so.78.3
7f5834594000-7f5834595000 r--p 01f93000 08:03 1987363                    /usr/lib/libicudata.so.78.3
7f5834600000-7f5834648000 r--p 00000000 08:03 1987378                    /usr/lib/libicuuc.so.78.3
7f5834648000-7f5834765000 r-xp 00048000 08:03 1987378                    /usr/lib/libicuuc.so.78.3
7f5834765000-7f58347f8000 r--p 00165000 08:03 1987378                    /usr/lib/libicuuc.so.78.3
7f58347f8000-7f583480b000 r--p 001f8000 08:03 1987378                    /usr/lib/libicuuc.so.78.3
7f583480b000-7f583480c000 rw-p 0020b000 08:03 1987378                    /usr/lib/libicuuc.so.78.3
7f583480c000-7f583480e000 rw-p 00000000 00:00 0
7f5834850000-7f5834874000 r--p 00000000 08:03 2259503                    /usr/share/wine/fonts/tahoma.ttf
7f5834874000-7f5834877000 r--p 00000000 08:03 2008698                    /usr/lib/libXcursor.so.1.0.2
7f5834877000-7f583487d000 r-xp 00003000 08:03 2008698                    /usr/lib/libXcursor.so.1.0.2
7f583487d000-7f583487f000 r--p 00009000 08:03 2008698                    /usr/lib/libXcursor.so.1.0.2
7f583487f000-7f5834880000 r--p 0000a000 08:03 2008698                    /usr/lib/libXcursor.so.1.0.2
7f5834880000-7f5834881000 rw-p 0000b000 08:03 2008698                    /usr/lib/libXcursor.so.1.0.2
7f5834881000-7f583489b000 r--p 00000000 08:03 1987461                    /usr/lib/libxml2.so.16.1.3
7f583489b000-7f583496f000 r-xp 0001a000 08:03 1987461                    /usr/lib/libxml2.so.16.1.3
7f583496f000-7f58349ac000 r--p 000ee000 08:03 1987461                    /usr/lib/libxml2.so.16.1.3
7f58349ac000-7f58349b5000 r--p 0012b000 08:03 1987461                    /usr/lib/libxml2.so.16.1.3
7f58349b5000-7f58349b6000 rw-p 00134000 08:03 1987461                    /usr/lib/libxml2.so.16.1.3
7f58349b6000-7f58349b7000 rw-p 00000000 00:00 0
7f58349b7000-7f58349ba000 r--p 00000000 08:03 2010188                    /usr/lib/libXi.so.6.1.0
7f58349ba000-7f58349c7000 r-xp 00003000 08:03 2010188                    /usr/lib/libXi.so.6.1.0
7f58349c7000-7f58349ca000 r--p 00010000 08:03 2010188                    /usr/lib/libXi.so.6.1.0
7f58349ca000-7f58349cb000 r--p 00012000 08:03 2010188                    /usr/lib/libXi.so.6.1.0
7f58349cb000-7f58349cc000 rw-p 00013000 08:03 2010188                    /usr/lib/libXi.so.6.1.0
7f58349cc000-7f58349ce000 r--p 00000000 08:03 2010266                    /usr/lib/libXrandr.so.2.2.0
7f58349ce000-7f58349d5000 r-xp 00002000 08:03 2010266                    /usr/lib/libXrandr.so.2.2.0
7f58349d5000-7f58349d7000 r--p 00009000 08:03 2010266                    /usr/lib/libXrandr.so.2.2.0
7f58349d7000-7f58349d8000 r--p 0000a000 08:03 2010266                    /usr/lib/libXrandr.so.2.2.0
7f58349d8000-7f58349d9000 rw-p 0000b000 08:03 2010266                    /usr/lib/libXrandr.so.2.2.0
7f58349d9000-7f58349dc000 r--p 00000000 08:03 1981786                    /usr/lib/libnss_resolve.so.2
7f58349dc000-7f5834a0f000 r-xp 00003000 08:03 1981786                    /usr/lib/libnss_resolve.so.2
7f5834a0f000-7f5834a27000 r--p 00036000 08:03 1981786                    /usr/lib/libnss_resolve.so.2
7f5834a27000-7f5834a2e000 r--p 0004e000 08:03 1981786                    /usr/lib/libnss_resolve.so.2
7f5834a2e000-7f5834a2f000 rw-p 00055000 08:03 1981786                    /usr/lib/libnss_resolve.so.2
7f5834a2f000-7f5834a33000 r--p 00000000 08:03 1981784                    /usr/lib/libnss_mymachines.so.2
7f5834a33000-7f5834a84000 r-xp 00004000 08:03 1981784                    /usr/lib/libnss_mymachines.so.2
7f5834a84000-7f5834a9e000 r--p 00055000 08:03 1981784                    /usr/lib/libnss_mymachines.so.2
7f5834a9e000-7f5834aa1000 r--p 0006f000 08:03 1981784                    /usr/lib/libnss_mymachines.so.2
7f5834aa1000-7f5834aa2000 rw-p 00072000 08:03 1981784                    /usr/lib/libnss_mymachines.so.2
7f5834aa2000-7f5834aae000 r--p 00000000 08:03 2003629                    /usr/lib/libxcb.so.1.1.0
7f5834aae000-7f5834ac2000 r-xp 0000c000 08:03 2003629                    /usr/lib/libxcb.so.1.1.0
7f5834ac2000-7f5834acb000 r--p 00020000 08:03 2003629                    /usr/lib/libxcb.so.1.1.0
7f5834acb000-7f5834acc000 r--p 00028000 08:03 2003629                    /usr/lib/libxcb.so.1.1.0
7f5834acc000-7f5834acd000 rw-p 00029000 08:03 2003629                    /usr/lib/libxcb.so.1.1.0
7f5834acd000-7f5834ae4000 r--p 00000000 08:03 2007847                    /usr/lib/libX11.so.6.4.0
7f5834ae4000-7f5834b79000 r-xp 00017000 08:03 2007847                    /usr/lib/libX11.so.6.4.0
7f5834b79000-7f5834c08000 r--p 000ac000 08:03 2007847                    /usr/lib/libX11.so.6.4.0
7f5834c08000-7f5834c0b000 r--p 0013a000 08:03 2007847                    /usr/lib/libX11.so.6.4.0
7f5834c0b000-7f5834c0f000 rw-p 0013d000 08:03 2007847                    /usr/lib/libX11.so.6.4.0
7f5834c0f000-7f5834c16000 r--p 00000000 08:03 2257650                    /usr/lib/wine/x86_64-unix/winex11.so
7f5834c16000-7f5834c61000 r-xp 00007000 08:03 2257650                    /usr/lib/wine/x86_64-unix/winex11.so
7f5834c61000-7f5834c7b000 r--p 00052000 08:03 2257650                    /usr/lib/wine/x86_64-unix/winex11.so
7f5834c7b000-7f5834c7f000 r--p 0006c000 08:03 2257650                    /usr/lib/wine/x86_64-unix/winex11.so
7f5834c7f000-7f5834c80000 rw-p 00070000 08:03 2257650                    /usr/lib/wine/x86_64-unix/winex11.so
7f5834c80000-7f5834c84000 rw-p 00000000 00:00 0
7f5834c84000-7f5834c86000 r--p 00000000 08:03 1991032                    /usr/lib/libexpat.so.1.12.2
7f5834c86000-7f5834ca6000 r-xp 00002000 08:03 1991032                    /usr/lib/libexpat.so.1.12.2
7f5834ca6000-7f5834caf000 r--p 00022000 08:03 1991032                    /usr/lib/libexpat.so.1.12.2
7f5834caf000-7f5834cb1000 r--p 0002a000 08:03 1991032                    /usr/lib/libexpat.so.1.12.2
7f5834cb1000-7f5834cb2000 rw-p 0002c000 08:03 1991032                    /usr/lib/libexpat.so.1.12.2
7f5834cb2000-7f5834cb5000 r--p 00000000 08:03 1983832                    /usr/lib/libpcre2-8.so.0.15.0
7f5834cb5000-7f5834d33000 r-xp 00003000 08:03 1983832                    /usr/lib/libpcre2-8.so.0.15.0
7f5834d33000-7f5834d5e000 r--p 00081000 08:03 1983832                    /usr/lib/libpcre2-8.so.0.15.0
7f5834d5e000-7f5834d5f000 r--p 000ab000 08:03 1983832                    /usr/lib/libpcre2-8.so.0.15.0
7f5834d5f000-7f5834d60000 rw-p 000ac000 08:03 1983832                    /usr/lib/libpcre2-8.so.0.15.0
7f5834d60000-7f5834d7e000 r--p 00000000 08:03 1990044                    /usr/lib/libglib-2.0.so.0.8800.2
7f5834d7e000-7f5834e2c000 r-xp 0001e000 08:03 1990044                    /usr/lib/libglib-2.0.so.0.8800.2
7f5834e2c000-7f5834ec0000 r--p 000cc000 08:03 1990044                    /usr/lib/libglib-2.0.so.0.8800.2
7f5834ec0000-7f5834ec1000 r--p 00160000 08:03 1990044                    /usr/lib/libglib-2.0.so.0.8800.2
7f5834ec1000-7f5834ec2000 rw-p 00161000 08:03 1990044                    /usr/lib/libglib-2.0.so.0.8800.2
7f5834ec2000-7f5834ec3000 rw-p 00000000 00:00 0
7f5834ec3000-7f5834ed0000 r--p 00000000 08:03 2002582                    /usr/lib/libharfbuzz.so.0.61421.0
7f5834ed0000-7f5834fc6000 r-xp 0000d000 08:03 2002582                    /usr/lib/libharfbuzz.so.0.61421.0
7f5834fc6000-7f5834ffa000 r--p 00103000 08:03 2002582                    /usr/lib/libharfbuzz.so.0.61421.0
7f5834ffa000-7f5834ffc000 r--p 00136000 08:03 2002582                    /usr/lib/libharfbuzz.so.0.61421.0
7f5834ffc000-7f5834ffd000 rw-p 00138000 08:03 2002582                    /usr/lib/libharfbuzz.so.0.61421.0
7f5834ffd000-7f5835003000 r--p 00000000 08:03 2008793                    /usr/lib/libfreetype.so.6.20.6
7f5835003000-7f583509b000 r-xp 00006000 08:03 2008793                    /usr/lib/libfreetype.so.6.20.6
7f583509b000-7f58350c4000 r--p 0009e000 08:03 2008793                    /usr/lib/libfreetype.so.6.20.6
7f58350c4000-7f58350cc000 r--p 000c6000 08:03 2008793                    /usr/lib/libfreetype.so.6.20.6
7f58350cc000-7f58350cd000 rw-p 000ce000 08:03 2008793                    /usr/lib/libfreetype.so.6.20.6
7f58350cd000-7f58350dd000 r--p 00000000 08:03 1969163                    /usr/lib/libm.so.6
7f58350dd000-7f5835176000 r-xp 00010000 08:03 1969163                    /usr/lib/libm.so.6
7f5835176000-7f58351fe000 r--p 000a9000 08:03 1969163                    /usr/lib/libm.so.6
7f58351fe000-7f58351ff000 r--p 00130000 08:03 1969163                    /usr/lib/libm.so.6
7f58351ff000-7f5835200000 rw-p 00131000 08:03 1969163                    /usr/lib/libm.so.6
7f5835200000-7f583520b000 r--p 00000000 08:03 2257637                    /usr/lib/wine/x86_64-unix/win32u.so
7f583520b000-7f5835394000 r-xp 0000b000 08:03 2257637                    /usr/lib/wine/x86_64-unix/win32u.so
7f5835394000-7f5835405000 r--p 00194000 08:03 2257637                    /usr/lib/wine/x86_64-unix/win32u.so
7f5835405000-7f583540f000 r--p 00205000 08:03 2257637                    /usr/lib/wine/x86_64-unix/win32u.so
7f583540f000-7f583541d000 rw-p 0020f000 08:03 2257637                    /usr/lib/wine/x86_64-unix/win32u.so
7f583541d000-7f583548b000 rw-p 00000000 00:00 0
7f583548f000-7f5835491000 r--p 00000000 08:03 2008689                    /usr/lib/libXfixes.so.3.1.0
7f5835491000-7f5835494000 r-xp 00002000 08:03 2008689                    /usr/lib/libXfixes.so.3.1.0
7f5835494000-7f5835495000 r--p 00005000 08:03 2008689                    /usr/lib/libXfixes.so.3.1.0
7f5835495000-7f5835496000 r--p 00005000 08:03 2008689                    /usr/lib/libXfixes.so.3.1.0
7f5835496000-7f5835497000 rw-p 00006000 08:03 2008689                    /usr/lib/libXfixes.so.3.1.0
7f5835497000-7f5835499000 r--p 00000000 08:03 2008694                    /usr/lib/libXrender.so.1.3.0
7f5835499000-7f58354a0000 r-xp 00002000 08:03 2008694                    /usr/lib/libXrender.so.1.3.0
7f58354a0000-7f58354a1000 r--p 00009000 08:03 2008694                    /usr/lib/libXrender.so.1.3.0
7f58354a1000-7f58354a2000 r--p 00009000 08:03 2008694                    /usr/lib/libXrender.so.1.3.0
7f58354a2000-7f58354a3000 rw-p 0000a000 08:03 2008694                    /usr/lib/libXrender.so.1.3.0
7f58354a3000-7f58354a9000 r--p 00000000 08:03 2003257                    /usr/lib/libfontconfig.so.1.17.0
7f58354a9000-7f58354dd000 r-xp 00006000 08:03 2003257                    /usr/lib/libfontconfig.so.1.17.0
7f58354dd000-7f58354f6000 r--p 0003a000 08:03 2003257                    /usr/lib/libfontconfig.so.1.17.0
7f58354f6000-7f58354ff000 r--p 00053000 08:03 2003257                    /usr/lib/libfontconfig.so.1.17.0
7f58354ff000-7f5835500000 rw-p 0005c000 08:03 2003257                    /usr/lib/libfontconfig.so.1.17.0
7f5835500000-7f5835800000 rw-p 00000000 00:00 0
7f5835800000-7f5835b40000 r--p 00000000 08:03 1999889                    /usr/lib/locale/locale-archive
7f5835b40000-7f5835b42000 r--p 00000000 08:03 2009133                    /usr/lib/libxkbregistry.so.0.13.2
7f5835b42000-7f5835b46000 r-xp 00002000 08:03 2009133                    /usr/lib/libxkbregistry.so.0.13.2
7f5835b46000-7f5835b48000 r--p 00006000 08:03 2009133                    /usr/lib/libxkbregistry.so.0.13.2
7f5835b48000-7f5835b49000 r--p 00008000 08:03 2009133                    /usr/lib/libxkbregistry.so.0.13.2
7f5835b49000-7f5835b4a000 rw-p 00009000 08:03 2009133                    /usr/lib/libxkbregistry.so.0.13.2
7f5835b4a000-7f5835b4b000 rw-p 00000000 00:00 0
7f5835b4b000-7f5835b4f000 r--p 00000000 08:03 2008762                    /usr/lib/libXext.so.6.4.0
7f5835b4f000-7f5835b5a000 r-xp 00004000 08:03 2008762                    /usr/lib/libXext.so.6.4.0
7f5835b5a000-7f5835b5d000 r--p 0000f000 08:03 2008762                    /usr/lib/libXext.so.6.4.0
7f5835b5d000-7f5835b5e000 r--p 00011000 08:03 2008762                    /usr/lib/libXext.so.6.4.0
7f5835b5e000-7f5835b5f000 rw-p 00012000 08:03 2008762                    /usr/lib/libXext.so.6.4.0
7f5835b5f000-7f5835b61000 r--p 00000000 08:03 2002544                    /usr/lib/libgraphite2.so.3.3.1
7f5835b61000-7f5835b7a000 r-xp 00002000 08:03 2002544                    /usr/lib/libgraphite2.so.3.3.1
7f5835b7a000-7f5835b7f000 r--p 0001b000 08:03 2002544                    /usr/lib/libgraphite2.so.3.3.1
7f5835b7f000-7f5835b81000 r--p 0001f000 08:03 2002544                    /usr/lib/libgraphite2.so.3.3.1
7f5835b81000-7f5835b82000 rw-p 00021000 08:03 2002544                    /usr/lib/libgraphite2.so.3.3.1
7f5835b82000-7f5835b83000 r--p 00000000 08:03 1973631                    /usr/lib/libbrotlicommon.so.1.2.0
7f5835b83000-7f5835b84000 r-xp 00001000 08:03 1973631                    /usr/lib/libbrotlicommon.so.1.2.0
7f5835b84000-7f5835ba3000 r--p 00002000 08:03 1973631                    /usr/lib/libbrotlicommon.so.1.2.0
7f5835ba3000-7f5835ba4000 r--p 00021000 08:03 1973631                    /usr/lib/libbrotlicommon.so.1.2.0
7f5835ba4000-7f5835ba5000 rw-p 00022000 08:03 1973631                    /usr/lib/libbrotlicommon.so.1.2.0
7f5835ba5000-7f5835ba6000 r--p 00000000 08:03 1973634                    /usr/lib/libbrotlidec.so.1.2.0
7f5835ba6000-7f5835baf000 r-xp 00001000 08:03 1973634                    /usr/lib/libbrotlidec.so.1.2.0
7f5835baf000-7f5835bb2000 r--p 0000a000 08:03 1973634                    /usr/lib/libbrotlidec.so.1.2.0
7f5835bb2000-7f5835bb3000 r--p 0000c000 08:03 1973634                    /usr/lib/libbrotlidec.so.1.2.0
7f5835bb3000-7f5835bb4000 rw-p 0000d000 08:03 1973634                    /usr/lib/libbrotlidec.so.1.2.0
7f5835bb4000-7f5835bba000 r--p 00000000 08:03 2002558                    /usr/lib/libpng16.so.16.58.0
7f5835bba000-7f5835be5000 r-xp 00006000 08:03 2002558                    /usr/lib/libpng16.so.16.58.0
7f5835be5000-7f5835bef000 r--p 00031000 08:03 2002558                    /usr/lib/libpng16.so.16.58.0
7f5835bef000-7f5835bf0000 r--p 0003a000 08:03 2002558                    /usr/lib/libpng16.so.16.58.0
7f5835bf0000-7f5835bf1000 rw-p 0003b000 08:03 2002558                    /usr/lib/libpng16.so.16.58.0
7f5835bf1000-7f5835bf4000 r--p 00000000 08:03 1973293                    /usr/lib/libz.so.1.3.2
7f5835bf4000-7f5835c04000 r-xp 00003000 08:03 1973293                    /usr/lib/libz.so.1.3.2
7f5835c04000-7f5835c0a000 r--p 00013000 08:03 1973293                    /usr/lib/libz.so.1.3.2
7f5835c0a000-7f5835c0b000 r--p 00018000 08:03 1973293                    /usr/lib/libz.so.1.3.2
7f5835c0b000-7f5835c0c000 rw-p 00019000 08:03 1973293                    /usr/lib/libz.so.1.3.2
7f5835c0c000-7f5835d0c000 rw-p 00000000 00:00 0
7f5835d0c000-7f5835d10000 r--p 00000000 08:03 1969961                    /usr/lib/libgcc_s.so.1
7f5835d10000-7f5835d33000 r-xp 00004000 08:03 1969961                    /usr/lib/libgcc_s.so.1
7f5835d33000-7f5835d37000 r--p 00027000 08:03 1969961                    /usr/lib/libgcc_s.so.1
7f5835d37000-7f5835d38000 r--p 0002b000 08:03 1969961                    /usr/lib/libgcc_s.so.1
7f5835d38000-7f5835d39000 rw-p 0002c000 08:03 1969961                    /usr/lib/libgcc_s.so.1
7f5835d39000-7f5835d41000 r--p 00000000 08:03 2257630                    /usr/lib/wine/x86_64-unix/ntdll.so
7f5835d41000-7f5835db0000 r-xp 00008000 08:03 2257630                    /usr/lib/wine/x86_64-unix/ntdll.so
7f5835db0000-7f5835dd1000 r--p 00077000 08:03 2257630                    /usr/lib/wine/x86_64-unix/ntdll.so
7f5835dd1000-7f5835dd3000 r--p 00098000 08:03 2257630                    /usr/lib/wine/x86_64-unix/ntdll.so
7f5835dd3000-7f5835dd5000 rw-p 0009a000 08:03 2257630                    /usr/lib/wine/x86_64-unix/ntdll.so
7f5835dd5000-7f5835e00000 rw-p 00000000 00:00 0
7f5835e00000-7f5835e24000 r--p 00000000 08:03 1969152                    /usr/lib/libc.so.6
7f5835e24000-7f5835f99000 r-xp 00024000 08:03 1969152                    /usr/lib/libc.so.6
7f5835f99000-7f583600e000 r--p 00199000 08:03 1969152                    /usr/lib/libc.so.6
7f583600e000-7f5836012000 r--p 0020d000 08:03 1969152                    /usr/lib/libc.so.6
7f5836012000-7f5836014000 rw-p 00211000 08:03 1969152                    /usr/lib/libc.so.6
7f5836014000-7f583601c000 rw-p 00000000 00:00 0
7f583601c000-7f583601e000 r--p 00000000 08:03 1983584                    /usr/lib/libbz2.so.1.0.8
7f583601e000-7f583602b000 r-xp 00002000 08:03 1983584                    /usr/lib/libbz2.so.1.0.8
7f583602b000-7f583602d000 r--p 0000f000 08:03 1983584                    /usr/lib/libbz2.so.1.0.8
7f583602d000-7f583602e000 r--p 00010000 08:03 1983584                    /usr/lib/libbz2.so.1.0.8
7f583602e000-7f583602f000 rw-p 00011000 08:03 1983584                    /usr/lib/libbz2.so.1.0.8
7f583603d000-7f583603f000 r--p 00000000 08:03 2259510                    /usr/share/wine/fonts/vgasys.fon
7f583603f000-7f5836044000 rw-p 00000000 00:00 0
7f5836045000-7f5836047000 r--p 00000000 08:03 2003540                    /usr/lib/libXdmcp.so.6.0.0
7f5836047000-7f5836049000 r-xp 00002000 08:03 2003540                    /usr/lib/libXdmcp.so.6.0.0
7f5836049000-7f583604b000 r--p 00004000 08:03 2003540                    /usr/lib/libXdmcp.so.6.0.0
7f583604b000-7f583604c000 r--p 00005000 08:03 2003540                    /usr/lib/libXdmcp.so.6.0.0
7f583604c000-7f583604d000 rw-p 00006000 08:03 2003540                    /usr/lib/libXdmcp.so.6.0.0
7f583604d000-7f583604e000 r--p 00000000 08:03 2003544                    /usr/lib/libXau.so.6.0.0
7f583604e000-7f583604f000 r-xp 00001000 08:03 2003544                    /usr/lib/libXau.so.6.0.0
7f583604f000-7f5836050000 r--p 00002000 08:03 2003544                    /usr/lib/libXau.so.6.0.0
7f5836050000-7f5836051000 r--p 00002000 08:03 2003544                    /usr/lib/libXau.so.6.0.0
7f5836051000-7f5836052000 rw-p 00003000 08:03 2003544                    /usr/lib/libXau.so.6.0.0
7f5836052000-7f5836053000 rw-p 00000000 00:00 0
7f5836053000-7f5836054000 rw-p 00000000 00:00 0
7f5836054000-7f5836055000 r--p 00000000 08:03 1969141                    /usr/lib/ld-linux-x86-64.so.2
7f5836055000-7f583607f000 r-xp 00001000 08:03 1969141                    /usr/lib/ld-linux-x86-64.so.2
7f583607f000-7f583608e000 r--p 0002b000 08:03 1969141                    /usr/lib/ld-linux-x86-64.so.2
7f583608e000-7f5836090000 r--p 00039000 08:03 1969141                    /usr/lib/ld-linux-x86-64.so.2
7f5836090000-7f5836091000 rw-p 0003b000 08:03 1969141                    /usr/lib/ld-linux-x86-64.so.2
7f5836091000-7f5836092000 rw-p 00000000 00:00 0
7f5836092000-7f5836093000 r--p 00000000 08:03 2257638                    /usr/lib/wine/x86_64-unix/wine
7f5836093000-7f5836094000 r-xp 00001000 08:03 2257638                    /usr/lib/wine/x86_64-unix/wine
7f5836094000-7f5836095000 r--p 00002000 08:03 2257638                    /usr/lib/wine/x86_64-unix/wine
7f5836095000-7f5836096000 r--p 00002000 08:03 2257638                    /usr/lib/wine/x86_64-unix/wine
7f5836096000-7f5836097000 rw-p 00003000 08:03 2257638                    /usr/lib/wine/x86_64-unix/wine
7f5836097000-7f583609b000 r--p 00000000 00:00 0                          [vvar]
7f583609b000-7f583609d000 r--p 00000000 00:00 0                          [vvar_vclock]
7f583609d000-7f583609f000 r-xp 00000000 00:00 0                          [vdso]
7f583609f000-7f58360a0000 r--p 00000000 08:03 2257639                    /usr/lib/wine/x86_64-unix/wine-preloader
7f58360a0000-7f58360a2000 r-xp 00001000 08:03 2257639                    /usr/lib/wine/x86_64-unix/wine-preloader
7f58360a2000-7f58360a3000 r--p 00003000 08:03 2257639                    /usr/lib/wine/x86_64-unix/wine-preloader
7f58360a3000-7f58360a5000 rw-p 00003000 08:03 2257639                    /usr/lib/wine/x86_64-unix/wine-preloader
7ffee008a000-7ffee00ab000 rw-p 00000000 00:00 0                          [stack]
7ffffe000000-7ffffe110000 rw-p 00000000 00:00 0
7ffffe110000-7ffffe112000 ---p 00000000 00:00 0
7ffffe112000-7ffffe310000 rw-p 00000000 00:00 0
7ffffe310000-7ffffe328000 r--p 00000000 08:03 2257670                    /usr/lib/wine/x86_64-windows/apisetschema.dll
7ffffe328000-7ffffe330000 ---p 00000000 00:00 0
7ffffe330000-7ffffe34a000 rw-p 00000000 00:00 0
7ffffe34a000-7ffffe350000 ---p 00000000 00:00 0
7ffffe350000-7ffffe413000 r--p 00000000 08:03 2259588                    /usr/share/wine/nls/locale.nls
7ffffe413000-7ffffe420000 ---p 00000000 00:00 0
7ffffe420000-7ffffe422000 r--p 00000000 08:03 2259587                    /usr/share/wine/nls/l_intl.nls
7ffffe422000-7ffffe430000 ---p 00000000 00:00 0
7ffffe430000-7ffffe441000 r--p 00000000 08:03 2259538                    /usr/share/wine/nls/c_1252.nls
7ffffe441000-7ffffe450000 ---p 00000000 00:00 0
7ffffe450000-7ffffe461000 r--p 00000000 08:03 2259562                    /usr/share/wine/nls/c_437.nls
7ffffe461000-7ffffe470000 ---p 00000000 00:00 0
7ffffe470000-7ffffe570000 rw-p 00000000 00:00 0
7ffffe570000-7ffffe8ad000 r--p 00000000 08:03 2259594                    /usr/share/wine/nls/sortdefault.nls
7ffffe8ad000-7ffffe8b0000 ---p 00000000 00:00 0
7ffffe8b0000-7ffffe8bf000 r--p 00000000 08:03 2259590                    /usr/share/wine/nls/normnfc.nls
7ffffe8bf000-7ffffe8c0000 ---p 00000000 00:00 0
7ffffe8c0000-7ffffe8da000 rw-p 00000000 00:00 0
7ffffe8da000-7ffffe8e0000 ---p 00000000 00:00 0
7ffffe8e0000-7ffffe8f1000 r--p 00000000 08:03 2259546                    /usr/share/wine/nls/c_20127.nls
7ffffe8f1000-7ffffe900000 ---p 00000000 00:00 0
7ffffe900000-7ffffe930000 rw-p 00000000 00:00 0
7ffffe930000-7ffffea00000 ---p 00000000 00:00 0
7ffffea00000-7ffffeb48000 r--p 00000000 00:01 177                        /memfd:wine-mapping (deleted)
7ffffeb48000-7ffffeb50000 ---p 00000000 00:00 0
7ffffeb50000-7ffffecd0000 rw-p 00000000 00:00 0
7ffffecd0000-7ffffece1000 r--p 00000000 08:03 2259519                    /usr/share/wine/nls/c_10000.nls
7ffffece1000-7ffffecf0000 ---p 00000000 00:00 0
7ffffecf0000-7ffffed01000 rw-p 00000000 00:00 0
7ffffed01000-7ffffed10000 ---p 00000000 00:00 0
7ffffed10000-7ffffed11000 rw-p 00000000 00:00 0
7ffffed11000-7ffffed20000 ---p 00000000 00:00 0
7ffffed20000-7fffff45e000 r--p 00000000 08:03 144041                     /home/r/.wine/drive_c/windows/resources/themes/aero/aero.msstyles
7fffff45e000-7fffff460000 ---p 00000000 00:00 0
7fffff460000-7fffff461000 rw-p 00000000 00:00 0
7fffff461000-7fffff470000 ---p 00000000 00:00 0
7fffff470000-7fffff471000 rw-p 00000000 00:00 0
7fffff471000-7fffff480000 ---p 00000000 00:00 0
7fffff480000-7fffff491000 r--p 00000000 08:03 2259538                    /usr/share/wine/nls/c_1252.nls
7fffff491000-7fffff4a0000 ---p 00000000 00:00 0
7fffff4a0000-7fffff4a1000 rw-p 00000000 00:00 0
7fffff4a1000-7fffff4b0000 ---p 00000000 00:00 0
7fffff4b0000-7fffff4b1000 rw-p 00000000 00:00 0
7fffff4b1000-7fffff4c0000 ---p 00000000 00:00 0
7fffff4c0000-7fffff4c5000 rw-p 00000000 00:00 0
7fffff4c5000-7fffff4d0000 ---p 00000000 00:00 0
7fffff4d0000-7fffff4d4000 rw-p 00000000 00:00 0
7fffff4d4000-7fffff4e0000 ---p 00000000 00:00 0
7fffff4e0000-7fffff4e1000 rw-p 00000000 00:00 0
7fffff4e1000-7fffff4f0000 ---p 00000000 00:00 0
7fffff4f0000-7fffff4f1000 rw-p 00000000 00:00 0
7fffff4f1000-7fffff500000 ---p 00000000 00:00 0
7fffff500000-7fffff501000 rw-p 00000000 00:00 0
7fffff501000-7fffffdb0000 ---p 00000000 00:00 0
7fffffdb0000-7fffffff0000 rw-p 00000000 00:00 0
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]

We can see the main program, nodepad.exe, loaded at address 0x140000000. Wine appears to have loaded its DLLs (which are in Windows PE format) at addresses 0x6… while loading Linux libraries (in ELF format) and data files at addresses 0x7….

In this example, because we've launched a GUI application, we can see both "very Windows-y" functionality such as uxtheme.dll as well as "very Linux-y" functionality such as X11.

Brute-forcing your way to Linux

When a program needs a service from the operating system, it makes a system call to request this. In Linux, the low-level ABI for doing this is stable, and so in older versions of Wine it was possible to directly use a syscall opcode inside a Windows binary to make Linux syscalls.

However, this is not possible any more in recent versions of Wine (and has not been possible in Proton for much longer). Wine now uses a feature called syscall user dispatch to emulate Windows programs that directly make raw Windows syscalls. This is not an officially-supported thing for programs to do, but enough games and DRM tools do this that it is worth emulating.

There is only one syscall opcode, so syscall user dispatch determines whether the program is trying to make a Windows syscall or a Linux syscall by looking at the memory address where the syscall opcode is found. Anything in the "Windows/PE" address range will be treated as a Windows syscall and won't be able to directly access the host system. This isn't a security boundary, so if we can somehow discover an address of some Linux-side code, we can still call it. That code will be outside of the emulation address range and can make native Linux syscalls.

Brute-forcing your way to Linux (for real)

In Wine's default configuration, the Windows Z: drive is mapped to the / root directory of the host system. This means that Z:\proc should give access to procfs, including /proc/self/maps.

Unfortunately, if we try this, we discover that this opens the maps file for the wineserver process and not our actual process. In order to implement Windows file handle semantics (sharing, locking, etc.), opening a file gets routed through the server. For normal files, this will not matter much, but procfs is one of the situations where it does.

(Note that Windows will typically relocate "system" DLLs such as kernel32.dll and ntdll.dll to the same base addresses across all processes in a system, even when ASLR is in effect. (The base address does change across reboots.) This allows for abuses such as this method of injecting threads into another process that you don't control. Techniques like this do not work on Linux (due to a cascade of effects stemming from the very different design of ELF dynamic linking), so we really do need to get our own /proc/self/maps and not that of the wineserver.)

Even though this didn't quite work, doing this test does confirm that we can access procfs. If we somehow determine the native Linux process ID of our process, we will be able to grab useful pointers from the maps file.

One way to do this is to try to search for ourselves by name by iterating through procfs. However, I came up with a simpler but much more horrifyingly undocumented technique: the current thread ID (which should be equal to the process ID when we're running on the main thread) is stored in the struct pthread whch is accessible from the fs x86 segment register.

(Modern x86 and x86_64 operating systems use the fs and gs segment registers to store the base address of thread-local stuff (such as the last error that occurred). Conveniently, Windows and Linux use the opposite register from each other, so they never need to switch. Wine just leaves both of them accessible all the time.)

This field is in the non-ABI-stable implementation details of glibc, is not compatible with musl, and touching this is likely to earn you stern admonishments from libc maintainers. However, in practice, this field has not moved in many many years.

let tid: u32;
unsafe {
    let struct_pthread: *const ();
    std::arch::asm!("mov {}, fs:[0x10]", out(reg) struct_pthread);
    dbg!(struct_pthread);

    // Skip over the `tcbhead_t` and the `list_t list` to get a `pid_t`
    tid = *(struct_pthread.byte_add(0x2d0) as *const u32);
    dbg!(tid);
}

Once we have the thread/process ID, we can read the desired /proc/xxx/maps file:

let self_maps = std::fs::read_to_string(format!("Z:\\proc\\{tid}\\maps"))?;

We can now search for and possibly call, uh, "whatever we want". Historically I would have left the rest as "an exercise for the reader", but there are enough undocumented details here that I feel the need to explain further.

First, I search for the first and last lines that reference /usr/lib/libc.so.6 and assume that the resulting address range is valid and corresponds to the entiretly of the loaded libc. A more robust implementation may want to check for any unexpected gaps/holes. This is standard file parsing work and omitted for brevity.

Next, we want to search the loaded libc image to manually find useful exported functions. The most useful funtion to find is probably dlsym which allows dynamically looking up further functions the "proper" way (dlsym is analogous to GetProcAddress on Windows). This manual lookup reimplements logic that is normally part of the dynamic linker infrastructure (ld.so). Doing this is/was a common obfuscation/malcode technique on Windows but seems to be less commonly done on Linux, especially since ELF data structures are somewhat more complicated than PE.

Fortunately, the Rust ecosystem contains useful crates such as elf that help us with much of the complexity. Unfortunately, the typical entry point of elf::ElfBytes does not work on an already-loaded memory image (it is intended for bytes read into memory directly from a file, not via a loader). We need to manually reimplement a bunch of its funtionality while taking these differences into account.

Annoyingly, we first need to duplicate (more-or-less exactly) the logic of the private find_phdrs function. After doing that, we can search for the PT_DYNAMIC program header which contains the metadata that dynamic linking uses. While doing this, we need to use the memory address/size instead of the file offset/size:

let mut libc_dynamic = None;
if let Some(phdr) = libc_phdrs
    .iter()
    .find(|phdr| phdr.p_type == elf::abi::PT_DYNAMIC)
{
    // NOTE: changed from p_offset, p_filesz
    let start = phdr.p_vaddr as usize;
    let end = start + phdr.p_memsz as usize;

    let buf = &libc_bytes[start..end];
    libc_dynamic = Some(elf::dynamic::DynamicTable::new(
        libc_ehdr.endianness,
        libc_ehdr.class,
        buf,
    ));
}
let libc_dynamic = libc_dynamic.expect("libc expected .dynamic");

Once we have found this data, we can look through it for the exported symbol tables and the not-very-documented GNU symbol hash table. The elf crate prefers to use ELF section headers for this, but the actual runtime doesn't do this and uses only program headers. One reason the crate is doing this might be because section headers specify a max size, but PT_DYNAMIC entries do not. Nonetheless, we can just… not properly bound the size, which is still good enough for performing symbol lookups:

let mut libc_symtab = None;
let mut libc_strtab = None;
let mut libc_gnu_hash = None;
for dt in libc_dynamic.iter() {
    match dt.d_tag {
        elf::abi::DT_SYMTAB => {
            let start = dt.d_ptr() as usize - libc_bytes.as_ptr().addr();
            let buf = &libc_bytes[start..];
            libc_symtab = Some(elf::symbol::SymbolTable::new(
                libc_ehdr.endianness,
                libc_ehdr.class,
                buf,
            ));
        }
        elf::abi::DT_STRTAB => {
            let start = dt.d_ptr() as usize - libc_bytes.as_ptr().addr();
            let buf = &libc_bytes[start..];
            libc_strtab = Some(elf::string_table::StringTable::new(buf));
        }
        elf::abi::DT_GNU_HASH => {
            let start = dt.d_ptr() as usize - libc_bytes.as_ptr().addr();
            let buf = &libc_bytes[start..];
            libc_gnu_hash = Some(elf::hash::GnuHashTable::new(
                libc_ehdr.endianness,
                libc_ehdr.class,
                buf,
            ));
        }
        _ => {}
    }
}
let libc_symtab = libc_symtab.expect("libc expected symtab");
let libc_strtab = libc_strtab.expect("libc expected strtab");
let libc_gnu_hash = libc_gnu_hash
    .expect("libc expected GNU hash")
    .expect("libc expected GNU hash");

With all of this setup, we can finally search for the dlsym function:

let dlsym_sym = libc_gnu_hash
    .find(b"dlsym", &libc_symtab, &libc_strtab)
    .expect("libc expected dlsym");
let dlsym_sym = dlsym_sym.expect("libc expected dlsym").1;
dbg!(&dlsym_sym);

let dlsym_func = unsafe {
    std::mem::transmute::<_, extern "sysv64" fn(*const (), *const u8) -> *const ()>(
        libc_start_ptr.byte_add(dlsym_sym.st_value as usize),
    )
};
dbg!(dlsym_func);

Note that because this is a Linux function, it uses the "System V" calling convention rather than the Microsoft calling convention (remember, we're still running in a "Windows" program). Fortunately, Rust and LLVM seem to support transparently calling across ABIs as long as the functions are tagged correctly.

A potential issue can arise here in that the elf crate does not support looking up multiple versions of a symbol (even though it has some basic support for symbol versioning information, the symbol lookup functions only return one result). I have no idea if this can become a problem or not, but one workaround that might work is to use dlsym to look up itself (which also helps confirm that we have indeed committed enough horrifying crimes to actually make this code work):

let dlsym_func = unsafe {
    std::mem::transmute::<_, extern "sysv64" fn(*const (), *const u8) -> *const ()>(
        dlsym_func(std::ptr::null(), b"dlsym\x00".as_ptr()),
    )
};
dbg!(dlsym_func);

We can further confirm that this works by looking up a Linux-specific libc function such as uname:

// FFI definition for struct UtsName omitted

let uname = unsafe {
    let mut uname = MaybeUninit::<UtsName>::uninit();
    let uname_fn = std::mem::transmute::<
        _,
        extern "sysv64" fn(*mut UtsName) -> std::ffi::c_int,
    >(dlsym_func(std::ptr::null(), b"uname\x00".as_ptr()));
    dbg!(uname_fn);
    let ret = uname_fn(uname.as_mut_ptr());
    if ret < 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(uname.assume_init())
    }
}
.expect("uname failed");
println!("{}", uname);

Upon running this code, we can confirm that it does indeed work. Other than the horrifyingly dangerous method of looking up the current native process ID, this method is useful when only a few simple native Linux calls need to be made. (Suggestions for a more reliable way to get a native libc pointer are welcome.)

Using winelib

The first (i.e. older) officially-supported mechanism for interfacing between the Wine and Linux worlds is to use winelib. Beyond what its name might imply, winelib is a substantial bit of build infrastructure for bridging the Windows and Linux worlds. Using winelib, it is possible to make an ELF .so file that can be loaded by Wine as if it were a Windows DLL. This .so file can call both Win32 functions and native Linux functions. This is the only mechanism that allows this in a single module (other than the previous hack).

Unfortunately, much of the ABI details and "build system glue" for this is undocumented and/or out of date. In order to explore it, I will be doing a deep-dive and setting everything up by hand.

External links

Much of the "build system" logic for building winelib DLLs is encoded in the winebuild tool. winebuild is intended to integrate with winegcc which is a wrapper compiler/linker driver. This is needed for setting up preprocessor and ABI… stuff (I'm not exactly sure what, and compiler drivers always end up being messy). In our proof-of-concept, we will be using Rust and driving LLVM LLD by ourselves and won't use any of this.

The implementation of loading a winelib DLL is spread throughout Wine's ntdll (both the "Win32" and "native" sides), centering around the dlopen_dll function.

I first learned that this technique is possible from the OpenMPT music software, which experimentally supports doing this to improve audio latency/performance.

Initial hack: cross-compiling to x86_64-unknown-linux-gnu

Because winelib .so DLLs are Linux shared objects, we need to be able to build Linux shared objects (and not just statically link a single monolithic binary with musl libc). This is not officially supported when cross-compiling Rust, but there should be no technical reason why it cannot work (and projects such as Zig demonstrate that this is possible).

One of the causes of Linux userspace ABI issues is in fact the GNU/glibc symbol versioning feature mentioned earlier. If a binary is linked against newer symbol versions, it will not load against older glibc. A common workaround for this is to build binaries on old LTS Linux distros in order to maximize compatibility. This then sometimes causes further issues because older distros come with older toolchains that have fewer features.

I'm not sure whether this is a bigger problem with C++ code, but the Rust compiler is perfectly capable of using a new toolchain with old glibc (especially since the Rust standard library gets statically linked into cdylibs, and exposing Rust-ABI functions across various module boundaries is often just assumed to not work). We just need to acquire an old(-enough) glibc to link against.

To do this, I grabbed a sysroot for the Valve 'sniper' Steam Runtime to use as an "old distro" baseline, specifically com.valvesoftware.SteamRuntime.Sdk-amd64,i386-sniper-sysroot.tar.gz. I then had to manually fix the following symlinks to be relative rather than absolute:

  • lib64/ld-linux-x86-64.so.2
  • usr/lib/x86_64-linux-gnu/libpthread.so

(There are more absolute symlinks, but only these seem to affect this quick proof-of-concept.)

I then used the following .cargo/config.toml:

[build]
target = "x86_64-unknown-linux-gnu"

[target.x86_64-unknown-linux-gnu]
rustflags = [
    "-Clinker=rust-lld",
    "-L../../valve-sniper/usr/lib/gcc/x86_64-linux-gnu/10",
    "-L../../valve-sniper/usr/lib/x86_64-linux-gnu",
    "-L../../valve-sniper/lib/x86_64-linux-gnu",
    "-Clink-arg=--sysroot=../../valve-sniper",
    # More flags, will be explained later
]

For some reason, --sysroot is insufficient by itself and all of the additional -L options also seem to be required. With this, it's possible to build glibc-linking .so files from not-Linux.

Further remarks

Zig has independently invented functionality analogous to Apple's TAPI/.tbd files, storing only the exported symbols from ELF shared objects without needing any of the code. Rust is also in the process of implementing something similar.

The remainder of the complexity is enumerating symbols across distros, glibc versions, and other "critical" libraries (such as X11 or 3D graphics). "Someone" should probably do more of this. Tidying this area up is likely to substantially improve the experience of shipping binary software for Linux.

winelib PE headers

The main export from a winelib .so is a data symbol called __wine_spec_nt_header. This points to a slightly modified IMAGE_NT_HEADERS{32,64}.

Yes, PE metadata is embedded inside an ELF .so file. This is probably necessary so that Windows-side infrastructure can properly introspect the module. Normally, winebuild automatically generates and links in an assembly-language file containing this data.

The IMAGE_FILE_HEADER is entirely normal. The Characteristics field appears to default to IMAGE_FILE_DLL | IMAGE_FILE_LARGE_ADDRESS_AWARE.

The IMAGE_OPTIONAL_HEADER is mostly normal but has had the following tweaks:

  • AddressOfEntryPoint is an absolute address (at least after ELF relocation processing), not a RVA
  • ImageBase points to some reserved space (explained later)
  • RVAs are relative to __wine_spec_nt_header, not ImageBase. This will be adjusted by Wine's loader.
  • For IMAGE_OPTIONAL_HEADER64, the AddressOfEntryPoint field is expanded to 64 bits, and the following BaseOfCode field is omitted. (This keeps the resulting structure the same size.)

Exports, imports, and resources are stored in the appropriate PE data directories following the "optional" header.

exports

Unlike a normal PE, the "export address table" does not contain RVAs but instead contains absolute addresses. In addition, for a 64-bit binary, these are 64-bit values rather than 32-bit values. This is incompatible in size from a normal PE.

The export section is otherwise identical to a PE. The winebuild tool contains logic to build various kinds of forwarders and thunks, but this is not necessary for a simple demo.

imports

Imports are identical to a normal PE. It is possible for winelib DLLs to have both ELF-style imports and Win32-style imports at the same time.

In Rust, we can declare the Win32 imports like this:

unsafe extern "C" {
    static DisableThreadLibraryCalls: extern "win64" fn(HMODULE) -> BOOL;
    static GetVersionExW: extern "win64" fn(*mut OSVERSIONINFOEXW) -> BOOL;
}

This tells Rust that we have an extern static which has the type of a function pointer (rather than an extern function), which is what the PE IAT will contain after imports are resolved.

resources

I haven't looked into this, but I assume that this would be entirely standard.

winelib PE reserved space

The winebuild process reserves a 0x11000-byte block in the .init section. This is pointed to by the ImageBase field of the NT header.

This section is used by the loader to create a dummy DOS (MZ) header, PE section headers (.text, .data, etc.), as well as copying and fixing up the NT header data to finally yield a convincing loaded PE image.

For reasons which I have not fully investigated, this process breaks when using LLVM LLD's default section ordering which places .rodata before .init. (This is probably because some RVAs become negative and aren't properly re-sign-extended).

The workaround I am using for this is to steal and simplify a GNU binutils-style linker script to force section ordering, thus necessitating a -Clink-arg=-Tforce-ordering.ld entry in RUSTFLAGS.

Rust compiler fighting

Rust really does not like dealing with pointer values that are constant at link time but not clearly part of the "same allocation".

To get around this, I created all the relevant data structures using global_asm!. Unfortunately, doing that then runs into this issue because Rust is very smart and tries to carefully control what symbols are exported from a cdylib.

To stack workaround on top of workaround, I added -Clink-arg=--version-script=force-export-winelib to RUSTFLAGS, with the following contents inside that file:

{
global:
    __wine_spec_nt_header;
};

The resulting final cargo.toml is:

[build]
target = "x86_64-unknown-linux-gnu"

[target.x86_64-unknown-linux-gnu]
rustflags = [
    "-Clinker=rust-lld",
    "-L../../valve-sniper/usr/lib/gcc/x86_64-linux-gnu/10",
    "-L../../valve-sniper/usr/lib/x86_64-linux-gnu",
    "-L../../valve-sniper/lib/x86_64-linux-gnu",
    "-Clink-arg=--sysroot=../../valve-sniper",
    "-Clink-arg=--version-script=force-export-winelib",
    "-Clink-arg=-Tforce-ordering.ld",
]

DLL entry points

All exports, including DllMain, from winelib DLLs need to have the Microsoft calling convention. The winebuild tool manually constructs thunks for this (and also optionally adds the ability for Wine to trace/debug these calls), but we do not have to do that in Rust.

As long as we are willing to forgo the ability to trace these calls, we can directly write extern "win64" fn and have the appropriate ABI generated automagically.

Pre-main startup

The winebuild tool patches the ELF startup functions (DT_INIT_ARRAY, DT_INIT) so that they run at DllMain time rather than at ELF load time. I am not certain why this is necessary, but this MR implies that it allows global constructors to use Win32 APIs. Our proof-of-concept does not need this and so does not do this.

The Wine crt0 also adds .CRT* sections for its own startup and teardown logic. It is also not clear when or how much this is needed (i.e. is this only necessary for programs using Wine-msvcrt?).

Stack overflow?

For reasons which I have really not fully investigated, certain Rust operations such as println! are very prone to causing stack overflows. I have not yet investigated how to fix this or whether this is potentially a sign of something more dangerously ABI mismatched (maybe don't use this in production like this without further testing?).

Putting it all together

We can write a Rust function like this:

extern "win64" fn read_proc_version(out_len: *mut usize, out_capacity: *mut usize) -> *const u8 {
    let proc_version = std::fs::read_to_string("/proc/version");

    if proc_version.is_err() {
        return std::ptr::null();
    }
    let proc_version = proc_version.unwrap();

    unsafe {
        let (ptr, len, cap) = proc_version.into_raw_parts();
        *out_len = len;
        *out_capacity = cap;
        ptr
    }
}

We then export it with a big mess of manual assembly (not shown here).

After building this .so file, we rename it to end in .dll and can load it dynamically in a host program like this:

let our_dll = unsafe {
    windows_sys::Win32::System::LibraryLoader::LoadLibraryA(b"winelib.dll\x00".as_ptr())
};
dbg!(our_dll);
assert!(!our_dll.is_null(), "failed to load winelib");

let read_proc_version = unsafe {
    let fn_ = windows_sys::Win32::System::LibraryLoader::GetProcAddress(
        our_dll,
        "read_proc_version\x00".as_ptr(),
    );
    std::mem::transmute::<_, unsafe extern "C" fn(*mut usize, *mut usize) -> *const u8>(
        fn_.expect("failed to get unixlib function"),
    )
};
dbg!(read_proc_version);

// Call the function and do something useful

Using unixlib

The winelib mechanism is very ABI-fiddly and also has a side-effect of disabling syscall user dispatch. It also requires the bitness (32 or 64 bit) of the Linux ELF to match that of the Windows executable, which is a limitation as distros attempt to slowly remove support for 32-bit userspace. (Or at least, I think it does? It seems like a mixed-bitness winelib .so should be theoretically possible as long as it loads at a low address, but I have no idea if the necessary ABI exists to make it actually work.)

Wine itself has been moving towards a cleaner separation between "Win32" and "native" code. This refactoring is the actual benefit of the "PE conversion" effort. The new mechanism that Wine now uses internally appears to be called "unixlib".

The only existing example I could find that uses this mechanism (other than Wine itself) is Hoshino Lina's spout2pw project, but there is an explanation of the mechanism in the mailing list archives.

Under this mechanism, a PE DLL (with appropriate tagging, see later) is paired with a corresponding native ELF .so library. The ELF exports an array of functions which can be called from Win32 (or two arrays for a 32-bit-on-64-bit WOW64 process). Unlike winelib, this ELF no longer has magic PE metadata embedded inside and is "completely normal". Unlike a winelib, it is intentionally not possible to freely mix Win32 and Linux code in the same module.

unixlib PE ABI

A PE DLL can only make use of the unixlib mechanism if Wine thinks it is a "builtin". This is detected by checking for the null-terminated string Wine builtin DLL immediately after the DOS (MZ) header. In practice, the Wine tools seem to overwrite 32 bytes, filling the rest with 0. (The space that is overwritten is the dummy "This program cannot be run in DOS mode." message.) In my proof-of-concept, this is implemented using dd as a post-build step.

In order to retrieve a handle to the native Linux side of the DLL, NtQueryVirtualMemory is used with a special MemoryInformationClass of 1000. This retrieves a handle to the native library (which is in practice a pointer to the function array). This handle is always 64-bits wide.

To actually make a function call, we use the __wine_unix_call_dispatcher function from Wine's ntdll.dll. Despite being "private", it is possible to statically import it:

#[link(name = "ntdll", kind = "raw-dylib")]
unsafe extern "system" {
    static __wine_unix_call_dispatcher:
        unsafe extern "system" fn(unixlib_handle: u64, code: u32, ptr: *const c_void) -> u32;
}

code is the index into the function array, and all functions are assumed to have a C signature of

NTSTATUS WINAPI function(void *)

or in other words, a stdcall function taking in one single pointer-sized argument and returning a 32-bit value. All parameters are passed via the pointer argument.

unixlib ELF ABI

The ELF side of this interface is entirely normal and does not require any special Wine-related ABI other than exporting some known symbols.

unixlib ELFs export a data symbol __wine_unix_call_funcs which is the aforementioned array. It can also export __wine_unix_call_wow64_funcs for WOW64 support. Each of the functions in these arrays is a "normal" System-V-ABI function taking in a pointer and returning a 32-bit value. The call dispatcher (on the Wine/Win32 side) automatically handles ABI bridging (which is simple because all functions have the same signature and only one argument).

This mode has an additional complication that the WINEDLLPATH environment variable needs to be set to point to the directory containing the unixlib .so:

$ WINEDLLPATH=$(pwd) wine the-test-program.exe unixlib

unixlib ELFs do not have the ability to call back into arbitrary Win32 APIs (there is a mechanism for explicitly invoking callbacks using KeUserModeCallback, but I haven't fully looked into it yet).

Putting it all together

We can build a unixlib ELF in Rust with code like the following:

#[repr(transparent)]
struct WineUnixlibFnPtr(pub extern "C" fn(*const c_void) -> u32);
unsafe impl Sync for WineUnixlibFnPtr {}

// struct FFIString omitted

extern "C" fn read_proc_version(args: *const c_void) -> u32 {
    let proc_version = std::fs::read_to_string("/proc/version");

    if proc_version.is_err() {
        return 0xffffffff;
    }
    let proc_version = proc_version.unwrap();

    unsafe {
        let args = args as *mut FFIString;
        let (ptr, len, cap) = proc_version.into_raw_parts();
        (*args).ptr = ptr as *const u8;
        (*args).len = len;
        (*args).capacity = cap;
    };

    0
}

extern "C" fn free_string(args: *const c_void) -> u32 {
    let _str = unsafe {
        let args = args as *const FFIString;
        String::from_raw_parts((*args).ptr as *mut u8, (*args).len, (*args).capacity)
    };

    0
}

// Here is the important exported symbol
#[unsafe(no_mangle)]
static __wine_unix_call_funcs: [WineUnixlibFnPtr; 2] = [
    WineUnixlibFnPtr(read_proc_version),
    WineUnixlibFnPtr(free_string),
];

In the PE, we can do something like this:

// Global
unsafe extern "C" {
    static __ImageBase: std::ffi::c_void;
}
#[link(name = "ntdll", kind = "raw-dylib")]
unsafe extern "system" {
    static __wine_unix_call_dispatcher:
        unsafe extern "system" fn(unixlib_handle: u64, code: u32, ptr: *const c_void) -> u32;
}

#[unsafe(no_mangle)]
#[allow(non_snake_case)]
extern "system" fn DllMain(hinstDLL: HINSTANCE, fdwReason: u32, _: *mut ()) -> BOOL {
    // Other DllMain logic omitted

    // Load the unixlib ELF side
    let mut wine_unixlib_handle: u64 = 0;
    let ret = NtQueryVirtualMemory(
        GetCurrentProcess(),
        img_base,
        1000,
        &raw mut wine_unixlib_handle as *mut c_void,
        std::mem::size_of_val(&wine_unixlib_handle),
        std::ptr::null_mut(),
    );
    if ret != 0 {
        return 0;
    }
    println!("__wine_unixlib_handle = 0x{wine_unixlib_handle:016x}");
    // Store __wine_unixlib_handle in a global somewhere
}

// In this function we make a call to the other side
#[unsafe(no_mangle)]
pub extern "C" fn read_proc_version(args: *mut FFIString) -> u32 {
    unsafe {
        __wine_unix_call_dispatcher(__wine_unixlib_handle, 0, args as _)
    }
}

And finally, in the host program, we can LoadLibrary/GetProcAddress as normal.

Code

You can browse the full demonstration code here. And yes, all of these demos are technically Win64 and not Win32, fight me 😼

Summary

We have:

  • a hacky low-level raw interface, primarily demonstrating what is possible
  • an officially-supported mechanism full of fiddly ABI concerns (winelib)
  • a new, better-designed mechanism that may or may not be quite ready for public consumption (unixlib)