あなたが尋ねようとしている質問は次のようなものだと思いprintf
ますscanf
。しかし、コンパイラや IDE にプログラムを C ランタイム ライブラリにリンクするように指示しなくても、それらを使用できます。なぜ私はそれをする必要がないのですか?」</p>
その質問に対する答えは次のとおりです。ライブラリ関数を明示的に使用しない場合でも、起動コードが必要であり、コンパイラは内部でmemcpy
、浮動小数点エミュレーション関数などの呼び出しを発行する可能性があります。したがって、便宜上、コンパイラーは、ユーザーがそうしないように指示しない限り、プログラムを C ランタイム・ライブラリーに自動的にリンクします。」</p>
C ランタイム ライブラリにリンクしないようにコンパイラに指示する方法については、コンパイラのドキュメントを参照する必要があります。GCC は-nostdlib
コマンドライン オプションを使用します。以下では、それを機能させるためにジャンプしなければならないフープを示します...
$ cat > test.c
#include <stdio.h>
int main(void) { puts("hello world"); return 0; }
^D
$ gcc -nostdlib test.c && { ./a.out; echo $?; }
/usr/bin/ld: warning: cannot find entry symbol _start
/tmp/cc8svIx5.o: In function ‘main’:
test.c:(.text+0xa): undefined reference to ‘puts’
collect2: error: ld returned 1 exit status
puts
は明らかに C ライブラリにありますが、この謎の「エントリ シンボル_start
」も同様です。Cライブラリをオフにすると、それも自分で提供する必要があります...
$ cat > test.c
int _start(void) { return 0; }
^D
$ gcc -nostdlib test.c && { ./a.out; echo $?; }
Segmentation fault
139
現在はリンクしていますが、_start
戻る場所がないため、セグメンテーション違反が発生します! オペレーティング システムは、それが を呼び出すことを期待しています_exit
。よし、そうしよう...
$ cat > test.c
extern void _exit(int);
void _start(void) { _exit(0); }
^D
$ gcc -nostdlib test.c && { ./a.out; echo $?; }
/tmp/ccuDrMQ9.o: In function `_start':
test.c:(.text+0xa): undefined reference to `_exit'
collect2: error: ld returned 1 exit status
...ナッツ、_exit
Cランタイムライブラリの関数でもあります! 生のシステムコール時間...
$ cat > test.c
#include <unistd.h>
#include <sys/syscall.h>
void _start(void) { syscall(SYS_exit, 0); }
^D
$ gcc -nostdlib test.c && { ./a.out; echo $?; }
/tmp/cchtZnbP.o: In function `_start':
test.c:(.text+0x14): undefined reference to `syscall'
collect2: error: ld returned 1 exit status
...いや、C ランタイムの関数でsyscall
もあります。アセンブリを使用する必要があると思います。
$ cat > test.S
#include <sys/syscall.h>
.text
.globl _start
.type _start, @function
_start:
movq $SYS_exit, %rax
movq $0, %rdi
syscall
$ gcc -nostdlib test.S && { ./a.out; echo $?; }
0
そして、それは最終的に機能します。私のコンピューターで。システムコールのアセンブリレベルの規則が異なる別のオペレーティングシステムでは機能しません。
-nostdlib
システム コールを実行するためだけにアセンブリ言語を使用する必要があるとしたら、いったい何の役に立つのだろうかと疑問に思うかもしれません。これは、ブートローダー、カーネル、および C ランタイム自体 (の一部) など、完全に自己完結型の低レベル システム プログラムをコンパイルするときに使用することを目的としています。
ゼロからやり直す必要がある場合、syscall ラッパー、言語に依存しないプロセスの起動コード、および任意の言語のコンパイラが使用する可能性がある関数だけを使用して、低レベルの言語に依存しないランタイムを分離することは理にかなっています。 「ボンネットの下」(、、、そのようなもの)を呼び出す必要があります。そのアイデアの問題点は、急速にミッション クリープが発生すること です。ジェネリック スレッド プリミティブ? (どれが、どのセマンティクスで?) 動的リンカ? の実装で、上記のもののどれが必要ですか? Windowsはこの概念として始まり、Windows 10 ではディスク上で 1.8MB であり、 +よりも(わずかに)大きいmemcpy
_Unwind_RaiseException
__muldi3
errno
malloc
ntdll.dll
libc.so
ld.so
私のLinuxパーティションで。また、Microsoft であっても、のみを使用するプログラムを作成することはまれであり、困難ntdll.dll
です (私が確信している唯一の例は でありcsrss.exe
、これはカーネル コンポーネントである可能性もあります)。