Si .NET fonctionne partout, alors sur Windows 3.11 et DOS aussi

Je répète souvent que .NET Core est une source ouverte et qu'il fonctionne "partout". MonoGame, Unity, Apple Watch, Raspberry Pi et microcontrôleurs, une dizaine de Linux, Windows et ainsi de suite. Déjà beaucoup.

Mais quelqu'un ne suffit toujours pas. Michal Strehowski veut lancer C # vraiment partout .


C # sur Windows 3.11

Il a exécuté le code C # sur deux systèmes «impossibles» qui complètent désormais notre définition de «fonctionne partout». Bien qu'il s'agisse d'expériences amusantes (ne les répétez pas en production), elles mettent l'accent à la fois sur les capacités techniques de Michal et sur la flexibilité de la plateforme de base.

Exécution de C # sous Windows 3.11


Dans sept tweets, Michal explique comment il a réussi à exécuter du code C # sous Windows 3.11. L'application est simple, il n'y a qu'un appel à la fonction MessageBoxA avec l'affichage de la boîte de dialogue correspondante, qui est sur Windows dès les premiers jours. Pour appeler la fonction et obtenir le résultat , DllImport / PInvoke est utilisé.

J'ai d'abord montré cette application pour Windows 3.11 car elle est cool. Mais en réalité, l'auteur est parti de l'endroit où son expérience avec DOS s'est terminée. Il compile le code C # natif, et après cela, les règles n'existent plus.

Dans cet exemple, il s'exécute sur la plate-forme Win16, pas Win32. Cependant, en 1992 (oui, j'ai vécu et programmé à l'époque, et je l'ai utilisé dans mes projets!) Il y avait un certain pont technologique appelé Win32s : un sous-ensemble des API Windows NT qui ont été portées de nouveau vers Windows 3.11. Par conséquent, en tenant compte de certaines restrictions, vous pouvez écrire du code 32 bits et accéder à Win32 à partir de Win16.

Michal a réalisé que les fichiers objets créés par le compilateur CoreRT AOT en 2020 pouvaient être assemblés par l'éditeur de liens à partir de l'exemple Visual C ++ 2.0 de 1994. Le résultat est un code machine lié aux interfaces Win32s s'exécutant sur Windows 3.11 16 bits. La magie. Respectez Michalu.


Application simple Hello World C #

Exécution de C # à 8K sous DOS


J'ai déjà écrit sur les exécutables .NET Core 3.x autonomes , j'en suis un grand fan. Ma demande est déjà réduite à 28 mégaoctets. C'est un peu, étant donné qu'il comprend le runtime .NET et de nombreuses autres ressources. Bien sûr, il ne faut pas juger VM / runtime par la taille du plus petit programme possible, mais Michal voulait voir jusqu'à quelle limite vous pouvez aller - et fixer un objectif de 8000 octets!

Le programme fonctionne en mode texte, ce qui, à mon avis, est excellent . Il élimine également le besoin d'un ramasse-miettes, car il n'y a pas d'allocation de ressources. Cela signifie que vous ne pouvez pas utiliser new n'importe où. Aucun type de référence.

Pour déclarer des tableaux statiques, il utilise fixed char [] champs fixed char [] : ils doivent vivre sur la pile, et nous avons une petite pile.

Bien sûr, lorsque vous essayez de créer une sorte d'exécutable .NET autonome, vous obtenez initialement un fichier de 65 mégaoctets, qui comprend l'application, le runtime et les bibliothèques standard.

dotnet publish -r win-x64 -c Release

Vous pouvez utiliser ILLinker et PublishedTrimmed pour optimiser l'arborescence à partir de .NET Core 3.x, mais de cette façon, vous réduisez le fichier à seulement 25 mégaoctets.

Il a essayé d'utiliser Mono et mkbundle, portant la taille à 18,2 mégaoctets, mais a ensuite détecté une erreur. Et le runtime est toujours là.

Ainsi, le seul runtime approprié était CoreRT, qui n'inclut pas de machine virtuelle, mais uniquement des fonctions auxiliaires.

dotnet publish -r win-x64 -c Release /p:Mode=CoreRT

Il a donc obtenu 4,7 mégaoctets, mais c'est encore trop. Avec certains paramètres, vous pouvez marcher jusqu'à 3 mégaoctets. Vous pouvez étendre complètement la réflexion et atteindre 1,2 mégaoctets. Maintenant, il tiendra sur une disquette!

dotnet publish -r win-x64 -c Release /p:Mode=CoreRT-ReflectionFree

Cette taille d'un mégaoctet semble être une limite stricte pour le SDK .NET uniquement.

C'est là que Michal s'éloigne des outils standard . Il fait un stub de réimplémentation pour les types de base du système ! Ensuite, il se recompile avec quelques commutateurs magiques afin que seule la version IL de l'exécutable soit publiée.

csc.exe /debug /O /noconfig /nostdlib /runtimemetadataversion:v4.0.30319 MiniBCL.cs Game\FrameBuffer.cs Game\Random.cs Game\Game.cs Game\Snake.cs Pal\Thread.Windows.cs Pal\Environment.Windows.cs Pal\Console.Windows.cs /out:zerosnake.ilexe /langversion:latest /unsafe

Ensuite, il passe cela à CoreRT pour obtenir le code natif.

ilc.exe zerosnake.ilexe -o zerosnake.obj --systemmodule zerosnake --Os -g

Et nous y voilà.

«Nous avons maintenant zerosnake.obj - un fichier objet standard, pas différent des fichiers objet créés par d'autres compilateurs natifs, tels que C ou C ++. La dernière étape consiste à l'assembler. »

Encore quelques astuces - et la sortie est de 27 Ko! Il supprime ensuite plusieurs commutateurs de l'éditeur de liens pour désactiver et supprimer diverses choses en utilisant les mêmes méthodes que les développeurs d'assembleurs utilisent, ce qui donne 8176 octets. Thriller épique.

link.exe /debug:full /subsystem:console zerosnake.obj /entry:__managed__Main kernel32.lib ucrt.lib /merge:.modules=.rdata /merge:.pdata=.rdata /incremental:no /DYNAMICBASE:NO /filealign:16 /align:16

Suivez Michal sur Twitter et applaudissez-le.

Source: https://habr.com/ru/post/fr485268/


All Articles