Se o .NET funcionar em qualquer lugar, no Windows 3.11 e no DOS também

Costumo repetir que o .NET Core é um código aberto e funciona "em qualquer lugar". MonoGame, Unity, Apple Watch, Raspberry Pi e microcontroladores, uma dúzia de Linux, Windows e assim por diante. Já é bastante.

Mas alguém ainda não é suficiente. Michal Strehowski quer lançar o C # em qualquer lugar .


C # no Windows 3.11

Ele executou o código C # em dois sistemas "impossíveis" que agora complementam nossa definição de "funciona em qualquer lugar". Embora sejam experiências divertidas (não as repita na produção), elas enfatizam as habilidades técnicas de Michal e a flexibilidade da plataforma base.

Executando C # no Windows 3.11


Em sete tweets, Michal fala sobre como ele conseguiu executar o código C # no Windows 3.11. O aplicativo é simples, há apenas uma chamada para a função MessageBoxA com a exibição da caixa de diálogo correspondente, que está no Windows desde os primeiros dias. Para chamar a função e obter o resultado , DllImport / PInvoke é usado.

Eu mostrei esse aplicativo para o Windows 3.11 pela primeira vez porque é legal. Mas, na realidade, o autor começou no local onde terminou seu experimento com o DOS. Ele compila o código C # nativo e, depois disso, as regras não existem mais.

Neste exemplo, ele é executado na plataforma Win16, não no Win32. No entanto, em 1992 (sim, eu vivi e programei na época e a usei em meus projetos!) Havia uma certa ponte tecnológica chamada Win32s : um subconjunto das APIs do Windows NT que foram portadas para o Windows 3.11. Portanto, levando em conta algumas restrições, você pode escrever um código de 32 bits e acessar o Win32 no Win16.

Michal percebeu que os arquivos de objeto criados pelo compilador CoreRT AOT em 2020 poderiam ser montados pelo vinculador a partir da amostra do Visual C ++ 2.0 de 1994. O resultado é um código de máquina vinculado às interfaces do Win32s em execução no Windows 3.11 de 16 bits. A magia Respeite Michalu.


Aplicativo Hello World C # simples

Executando C # em 8K no DOS


Eu escrevi sobre executáveis ​​independentes do .NET Core 3.x antes, sou um grande fã disso. Meu aplicativo já foi reduzido para 28 megabytes. Isso é um pouco, já que inclui o tempo de execução do .NET e muitos outros recursos. Obviamente, não se deve julgar VM / tempo de execução pelo tamanho do menor programa possível, mas Michal queria ver até que limite você pode ir - e definir um alvo de 8000 bytes!

O programa funciona no modo de texto, o que, na minha opinião, é ótimo . Também elimina a necessidade de um coletor de lixo, pois não há alocação de recursos. Isso significa que você não pode usar novo em nenhum lugar. Nenhum tipo de referência.

Para declarar matrizes estáticas, ele usa campos fixed char [] : eles devem viver na pilha e temos uma pequena pilha.

Obviamente, quando você tenta executar algum tipo de .NET independente, você obtém inicialmente um arquivo de 65 megabytes, que inclui o aplicativo, o tempo de execução e as bibliotecas padrão.

dotnet publish -r win-x64 -c Release

Você pode usar o ILLinker e o PublishedTrimmed para otimizar o aparamento em árvore do .NET Core 3.x, mas dessa maneira reduz o arquivo para apenas 25 megabytes.

Ele tentou usar o Mono e o mkbundle, aumentando o tamanho para 18,2 megabytes, mas depois encontrou um erro. E o tempo de execução ainda está lá.

Portanto, o único tempo de execução adequado foi o CoreRT, que não inclui uma máquina virtual, mas apenas funções auxiliares.

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

Então ele conseguiu 4,7 megabytes, mas ainda é demais. Com algumas configurações, você pode caminhar até 3 megabytes. Você pode estender completamente a reflexão e atingir 1,2 megabytes. Agora caberá em um disquete!

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

Esse tamanho de um megabyte parece ser um limite rígido apenas para o .NET SDK.

É aqui que Michal se afasta das ferramentas padrão . Ele cria um esboço de reimplementação para os tipos básicos de sistema ! Em seguida, recompila com algumas opções mágicas para que apenas a versão IL do executável seja liberada.

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

Em seguida, passa isso ao CoreRT para obter o código nativo.

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

E aqui estamos nós.

“Agora temos o zerosnake.obj - um arquivo de objeto padrão, diferente dos arquivos de objetos criados por outros compiladores nativos, como C ou C ++. O último passo é montá-lo.

Mais alguns truques - e a saída é 27 KB! Em seguida, ele remove várias opções do vinculador para desativar e remover várias coisas usando os mesmos métodos usados ​​pelos desenvolvedores do assembler, resultando em 8176 bytes. Suspense épico.

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

Siga Michal no Twitter e aplaude-o.

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


All Articles