0

I just discovered something really surprising.

I can compile a simple C# hello world on my windows computer using csc (from Visual Studio), copy the resulting exe file to my Linux computer, and execute it with mono helloworld.exe. So far, everything makes sense to me: according to this SO post, on Windows, helloworld.exe is basically just a trick that ends up starting the C# runtime, and the CIL bytecode is just read from some data section later on in the exe file. Likewise, I imagine that, on Linux, doing mono helloworld.exe just starts the C# runtime and directly reads the bytecode without bothering with the exe trickery.

For posterity, here is the source, which I got from Charles Petzold's excellent free C# book:

//---------------------------------------------
// FirstProgram.cs (c) 2006 by Charles Petzold
//---------------------------------------------
class FirstProgram
{
    public static void Main()
    {
        System.Console.WriteLine("Hello, Microsoft .NET Framework!");
    }
}

But here's where things get interesting: on Linux (uname -r on my machine gives 4.14.188-1-MANJARO) I can simply do ./helloworld.exe and it works!

I started doing some sleuthing, and here are the first few lines from running strace ./helloworld.exe:

execve("./hw.exe", ["./hw.exe"], 0x7fffae8e6070 /* 61 vars */) = 0
[ Process PID=3381 runs in 32 bit mode. ]
brk(NULL)                               = 0x7eedb000
arch_prctl(0x3001 /* ARCH_??? */, 0xffb19948) = -1 EINVAL (Invalid argument)
readlink("/proc/self/exe", "/usr/bin/wine", 4096) = 13
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf7faa000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/bin/../lib32/tls/i686/sse2/libwine.so.1", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/usr/bin/../lib32/tls/i686/sse2", 0xffb18e00) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/bin/../lib32/tls/i686/libwine.so.1", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/usr/bin/../lib32/tls/i686", 0xffb18e00) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/bin/../lib32/tls/sse2/libwine.so.1", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/usr/bin/../lib32/tls/sse2", 0xffb18e00) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/bin/../lib32/tls/libwine.so.1", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/usr/bin/../lib32/tls", 0xffb18e00) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/bin/../lib32/i686/sse2/libwine.so.1", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat64("/usr/bin/../lib32/i686/sse2", 0xffb18e00) = -1 ENOENT (No such file or directory)

I would have expected some error saying "this file is not a valid executable", since I only expect the Linux program loader to understand ELF files instead of Windows's PE format. Instead, it seems that somehow the system is smart enough to start looking for wine (in the strace output, you can see it start to look for wine libraries, and because I've installed wine on my Linux machine, it does eventually find them later on).

So what's going on? Is the execve call smart enough to try using wine if it detects a PE file, or is this something that bash is doing? Or is it something else entirely different?

Mahkoe
  • 113

1 Answers1

1

You most definitely have either Wine or/and Mono installed via a package manager and along with it a special configuration file which tells the kernel what to do once the user launches an exe file.

More on it here: https://en.wikipedia.org/wiki/Binfmt_misc

Stephen Kitt
  • 434,908
  • Wow, I had no idea that existed! I looked it up and found this: https://www.mono-project.com/docs/faq/technical/. The folks at mono recommend that you avoid this option because it isn't portable, but it's still very cool that it works. – Mahkoe Aug 14 '20 at 17:42
  • Sorry, I didn’t pay attention to the strace, it is indeed Wine taking care of things here (on top of binfmt_misc), but either Wine or Mono would do the trick. – Stephen Kitt Aug 14 '20 at 18:41