msvcrt.dllに依存しない実行ファイルを作ることにあまり意味はないですが、気になったためやってみました。
void WriteFile(void *, void *, int, void *, void *);
void *GetStdHandle(int);
void f() { WriteFile(GetStdHandle(-11), "Hello,World!", 12, 0, 0); }
これがHelloWorldを表示するコードです。main関数がありませんが、その代わりにf関数から実行開始します。
$ clang hello.c -e f -nostartfiles -nostdlib -lkernel32
コンパイル時にはf関数から実行開始することを指定します。また、C言語の標準ライブラリを使用しないことを指定し、kernel32.dllを使用することを指定します。
$ ./a.exe
Hello,World!
作成された実行ファイル(a.exe)を実行すると ”Hello,World!” が表示されます。
$ objdump -d a.exe
a.exe: file format pei-x86-64
Disassembly of section .text:
0000000140001000 <f>:
140001000: 48 83 ec 28 sub $0x28,%rsp
140001004: b9 f5 ff ff ff mov $0xfffffff5,%ecx
140001009: e8 32 00 00 00 call 140001040 <GetStdHandle>
14000100e: 48 89 c1 mov %rax,%rcx
140001011: 48 8d 15 e8 0f 00 00 lea 0xfe8(%rip),%rdx # 140002000 <__data_end__>
140001018: 41 b8 0c 00 00 00 mov $0xc,%r8d
14000101e: 31 c0 xor %eax,%eax
140001020: 41 89 c1 mov %eax,%r9d
140001023: 48 c7 44 24 20 00 00 movq $0x0,0x20(%rsp)
14000102a: 00 00
14000102c: e8 07 00 00 00 call 140001038 <WriteFile>
140001031: 90 nop
140001032: 48 83 c4 28 add $0x28,%rsp
140001036: c3 ret
140001037: 90 nop
0000000140001038 <WriteFile>:
140001038: ff 25 0a 40 00 00 jmp *0x400a(%rip) # 140005048 <__imp_WriteFile>
14000103e: 90 nop
14000103f: 90 nop
0000000140001040 <GetStdHandle>:
140001040: ff 25 fa 3f 00 00 jmp *0x3ffa(%rip) # 140005040 <__IAT_start__>
140001046: 90 nop
140001047: 90 nop
140001048: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
14000104f: 00
実行ファイルの機械語は余計なコードがなく非常にシンプルです。
$ objdump -p a.exe | grep -i -E "\.dll"
DLL Name: KERNEL32.dll
動的リンクしているDLLを確認すると、kernel32.dllのみリンクしています。
msvcrt.dllはリンクしていません。
$ strip -s a.exe
stripコマンドで実行ファイル(a.exe)から余計な情報を削除します。
そうすることで、実行ファイルは4kB程度の小さなものとなります。
WinAPIのWriteFile関数、ReadFile関数など使えば、printfやscanfなどの入出力機能、fopenやfread,fwriteなどのファイル入出力機能を実装することが可能です(浮動小数点数を文字列を変換するためにはRyuアルゴリズムなどを実装することが必要です。)
また、VirtualAllocEx関数、VirtualFreeEx関数など使えばmallocやfreeなどのメモリアロケーターを実装することが可能です(VirtualAllocEx関数である程度大きなメモリ領域を確保し、そのメモリ領域をglibcのmalloc/freeのような高速なアルゴリズムで管理するコードを自力で書く必要があります。)
頑張れば低レベルなWinAPIのみを使ってC言語標準ライブラリにある関数を自作することは可能ですが、車輪の再発明も甚だしいですね。素直にmsvcrt.dllを使えばいいと思います。