How is Mono magical?

10,719

This is binfmt_misc in action: it allows the kernel to be told how to run binaries it doesn't know about. Look at the contents of /proc/sys/fs/binfmt_misc; among the files you see there, one should explain how to run Mono binaries:

enabled
interpreter /usr/lib/binfmt-support/run-detectors
flags:
offset 0
magic 4d5a

(on a Debian system). This tells the kernel that binaries starting with MZ (4d5a) should be given to run-detectors. The latter figures out whether to use Mono or Wine to run the binary.

Binary types can be added, removed, enabled and disabled at any time; see the documentation above for details (the semantics are surprising, the virtual filesystem used here doesn't behave entirely like a standard filesystem). /proc/sys/fs/binfmt_misc/status gives the global status, and each binary "descriptor" shows its individual status. Another way of disabling binfmt_misc is to unload its kernel module, if it's built as a module; this also means it's possible to blacklist it to avoid it entirely.

This feature allows new binary types to be supported, such as MZ executables (which include Windows PE and PE+ binaries, but also DOS and OS/2 binaries!), Java JAR files... It also allows known binary types to be supported on new architectures, typically using Qemu; thus, with the appropriate libraries, you can transparently run ARM Linux binaries on an Intel processor!

Your question stemmed from cross-compilation, albeit in the .NET sense, and that brings up a caveat with binfmt_misc: some configuration scripts misbehave when you try to cross-compile on a system which can run the cross-compiled binaries. Typically, detecting cross-compilation involves building a binary and attempting to run it; if it runs, you're not cross-compiling, if it doesn't, you are (or your compiler's broken). autoconf scripts can usually be fixed in this case by explicitly specifying the build and host architectures, but sometimes you'll have to disable binfmt_misc temporarily...

Share:
10,719

Related videos on Youtube

cat
Author by

cat

Here's a shout-out to the hero of Stack Exchange! You're the best! I like Go, as a statically typed C-style-OOP systems programming language, but it doesn't have generics, and doesn't encourage functional practices. D encourages functional programming, proper OOP and fixes the problems of C and Go while being a better C++, and more productive / less dumb than Java. While I love Python, it feels too mundane; for everyday tasks I prefer Racket and Factor. /* this bit of chicanery is to prevent me from accidentally dereferencing null pointers you might think it's ugly but would you rather have segfault or slightly ugly code? */ // Just trying to create an array of integers for reference // because I keep forgetting how integers look like. (from here.)

Updated on September 18, 2022

Comments

  • cat
    cat over 1 year

    I'm learning C#, so I made a little C# program that says Hello, World!, then compiled it with mono-csc and ran it with mono:

    $ mono-csc Hello.cs
    $ mono Hello.exe
    Hello, World!
    

    I noticed that when I hit TAB in bash, Hello.exe was marked executable. Indeed, it runs by just a shell loading the filename!

    Hello.exe is not an ELF file with a funny file extension:

    $ readelf -a Hello.exe
    readelf: Error: Not an ELF file - it has the wrong magic bytes at the start
    $ xxd Hello.exe | head -n1
    00000000: 4d5a 9000 0300 0000 0400 0000 ffff 0000  MZ..............
    

    MZ means it's a Microsoft Windows statically linked executable. Drop it onto a Windows box, and it will (should) run.

    I have wine installed, but wine, being a compatibility layer for Windows apps, takes about 5x as long to run Hello.exe as mono and executing it directly do, so it's not wine that runs it.

    I'm assuming there's some mono kernel module installed with mono that intercepts the exec syscall/s, or catches binaries that begin with 4D 5A, but lsmod | grep mono and friends return an error.

    What's going on here, and how does the kernel know that this executable is special?


    Just for proof it's not my shell working magic, I used the Crap Shell (aka sh) to run it and it still runs natively.


    Here's the program in full, since a commenter was curious:

    using System;
    
    class Hello {
      /// <summary>
      ///   The main entry point for the application
      /// </summary>
      [STAThread]
      public static void Main(string[] args) {
        System.Console.Write("Hello, World!\n");
      }
    }
    
    • kuldeep.kamboj
      kuldeep.kamboj about 8 years
      While you got answer, But I am little bit confused, If you run the commands like php hello.php or python hello.py or perl hello.pl or in case of compiled language like java java hello they would also run as their are no executable there but a program read and execute file. However if you run ./hello.exe instead of mono hello.exe the I found your question and accepted answer more reasonable (which in case definitely use binfmt-support.
    • kuldeep.kamboj
      kuldeep.kamboj about 8 years
      Actually what I was saying that any code file (or compiled file ) can be executed via its program in format program codefile, in your case program is mono, while my examples includes php, python, perl and java. I suspect if mono allow extension to other than .exe file still executed via code. It should not depend at all on windows as finally this is execute a compiled code. However if your source file have some dependent code like windows only APIs then obviously you must need wine for running that exe file.
    • kuldeep.kamboj
      kuldeep.kamboj about 8 years
      However I checkout this code and able to run by using ./hello.exe which is without defining program and require it be executable, So there must be role of binfmt_misc to mark it as executable by mono.
    • Admin
      Admin about 8 years
      Part of the wonderful irony of this is that /etc/magic or /usr/share/file/magic (or similar magical location) is the file that contains the information necessary to be able to do this.
    • cat
      cat about 8 years
      @MichaelT I thought magic was just identifying native file types like media, I never realised it did interesting stuff like this :)
    • Admin
      Admin about 8 years
      @cat its a very interesting file (if I was to have a favorite file, it would probably be my first choice). It has the information for identifying all sorts of files. But you need to identify what the file is first before you can figure out how to run it. That's what file and its magic does. A similar thing - Java Binary Kernel Support for Linux from awhile back so you could do $ foo.jar rather than $ java -jar foo.jar - similar to what is done for mono.
    • Stephen Kitt
      Stephen Kitt about 8 years
      Just to clarify, binfmt_misc doesn't use magic, but as long as the magic bytes are within the first 128 you can use the information given in the magic files to write a binfmt_misc descriptor. If you want to find out more about magic files, look at libmagic.
    • rackandboneman
      rackandboneman about 8 years
      BTW, even ELF files usually specify their own interpreter in a way... that interpreter being the system's dynamic linker.
    • asmeurer
      asmeurer about 8 years
      Is Crap Shell a thing now?
    • cat
      cat about 8 years
      @asmeurer ya it is (8
  • user
    user about 8 years
    To those who are curious about /proc, you may be interested in How does /proc/* work? on Super User.