Jak magiczny jest Mono?

149

Uczę się języka C #, więc stworzyłem mały program w języku C #, który mówi Hello, World!, a następnie skompilowałem go mono-csci uruchomiłem z mono:

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

Zauważyłem, że kiedy uderzy TABw bash, Hello.execechował wykonywalny. Rzeczywiście, działa tylko przez powłokę ładującą nazwę pliku!

Hello.exeto nie jest plik ELF z zabawnym rozszerzenie pliku:

$ 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..............

MZoznacza, że ​​jest to plik wykonywalny połączony statycznie z Microsoft Windows. Upuść go w oknie systemu Windows i będzie (powinien) działać.

Mam winezainstalowany, ale winebędąc warstwa kompatybilności z aplikacjami Windows trwa około 5x tak długo, aby uruchomić Hello.exejak monoi wykonywanie go bezpośrednio nie, więc nie jest to wine, że prowadzi go.

Zakładam, że jest monozainstalowany moduł jądra, monoktóry przechwytuje execsyscall / s lub przechwytuje pliki binarne, które zaczynają się od 4D 5A, ale lsmod | grep monoznajomi zwracają błąd.

Co się tutaj dzieje i skąd jądro wie, że ten plik wykonywalny jest wyjątkowy?


Na dowód, że to nie moja magia działająca w powłoce, użyłem Crap Shell (aka sh), aby ją uruchomić i nadal działa natywnie.


Oto pełny program, ponieważ komentator był ciekawy:

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");
  }
}
kot
źródło
4
Chociaż otrzymałeś odpowiedź, ale jestem trochę zdezorientowany, jeśli uruchomisz polecenia takie jak php hello.phplub python hello.pylub perl hello.pllub w przypadku skompilowanego języka takiego jak java java hello, uruchomiliby się również, ponieważ nie są tam wykonywalne, ale program odczytuje i wykonuje plik. Jeśli jednak uruchomisz ./hello.exezamiast mono hello.exe, znalazłem twoje pytanie i zaakceptowałem bardziej sensowną odpowiedź (która w przypadku zdecydowanie skorzysta z obsługi binfmt.
kuldeep.kamboj
@ kuldeep.kamboj Nie jestem pewien, czy rozumiem, co mówisz, ale byłem zaskoczony, że plik wykonywalny systemu Windows działa „natywnie” na komputerze z systemem Linux.
kot
1
Właściwie to, co mówiłem, że każdy plik kodu (lub plik skompilowany) może być wykonywany przez swój program w formacie program codefile, w twoim przypadku program jest mono, podczas gdy moje przykłady obejmują php, python, perl i java. Podejrzewam, czy mono zezwala na rozszerzenie pliku innego niż .exe nadal wykonywanego za pomocą kodu. Nie powinno to wcale zależeć od systemu Windows, ponieważ w końcu jest to wykonywany skompilowany kod. Jednak jeśli plik źródłowy ma jakiś kod zależny, taki jak interfejsy API systemu Windows, to oczywiście potrzebujesz wina do uruchomienia tego pliku exe.
kuldeep.kamboj
4
Częścią cudownej ironii tego jest to, że /etc/magiclub /usr/share/file/magic(lub podobna magiczna lokalizacja) to plik zawierający informacje niezbędne do tego, aby to zrobić.
1
@cat to bardzo interesujący plik (gdybym miał ulubiony plik, prawdopodobnie byłby to mój pierwszy wybór). Zawiera informacje umożliwiające identyfikację wszelkiego rodzaju plików. Ale musisz najpierw ustalić, jaki jest plik, zanim będziesz mógł dowiedzieć się, jak go uruchomić. Tak właśnie działa plik i jego magia. Podobna rzecz - Java Binary Kernel Support dla Linuksa od jakiegoś czasu, więc możesz to zrobić $ foo.jarzamiast $ java -jar foo.jar- podobnie jak w przypadku mono.

Odpowiedzi:

183

To jest binfmt_misc w akcji: pozwala jądrze dowiedzieć się, jak uruchomić pliki binarne, o których nie wie. Spójrz na zawartość /proc/sys/fs/binfmt_misc; wśród plików, które tam widzisz, należy wyjaśnić, jak uruchomić pliki binarne Mono:

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

(w systemie Debian). To informuje jądro, że należy podać pliki binarne zaczynające się od MZ( 4d5a) run-detectors. Ten ostatni zastanawia się, czy użyć Mono czy Wine do uruchomienia pliku binarnego.

Typy binarne można dodawać, usuwać, włączać i wyłączać w dowolnym momencie; zobacz szczegóły w powyższej dokumentacji (semantyka jest zaskakująca, stosowany tutaj wirtualny system plików nie zachowuje się całkowicie jak standardowy system plików). /proc/sys/fs/binfmt_misc/statuspodaje status globalny, a każdy binarny „deskryptor” pokazuje swój indywidualny status. Innym sposobem wyłączenia binfmt_miscjest zwolnienie modułu jądra, jeśli jest on zbudowany jako moduł; oznacza to również, że można go umieścić na czarnej liście, aby całkowicie tego uniknąć.

Ta funkcja umożliwia obsługę nowych typów plików binarnych, takich jak pliki wykonywalne MZ (które obejmują pliki binarne Windows PE i PE +, ale także pliki binarne DOS i OS / 2!), Pliki Java JAR ... Umożliwia także obsługę znanych typów binarnych na nowe architektury, zwykle wykorzystujące Qemu; dzięki odpowiednim bibliotekom możesz transparentnie uruchamiać binaria ARM Linux na procesorze Intel!

Twoje pytanie wynikało z kompilacji krzyżowej, choć w sensie .NET, i to wywołuje zastrzeżenie binfmt_misc: niektóre skrypty konfiguracyjne źle zachowują się podczas próby kompilacji krzyżowej w systemie, który może uruchamiać binaria kompilowane krzyżowo. Zazwyczaj wykrywanie kompilacji krzyżowej wymaga zbudowania pliku binarnego i próby jego uruchomienia; jeśli to działa, nie kompilujesz się, a jeśli nie, jesteś (lub twój kompilator jest zepsuty). autoconfw tym przypadku skrypty można zwykle naprawić, wyraźnie określając architekturę kompilacji i hosta, ale czasami trzeba binfmt_misctymczasowo wyłączyć ...

Stephen Kitt
źródło
2
Dla tych, którzy są ciekawi / proc, możesz być zainteresowany tym, jak działa / proc / *? na superużytkowniku .
CVn