gccでlibc抜きでコンパイルを通す
Hello from a libc-free world! (Part 1) (Ksplice Blog) がおもしろかったのでやっていく。
libcありのとき
コード
よくあるhello worldのコード
$ cat hello_with_libc.c # include <stdio.h> int main() { printf("Hello World\n"); return 0; }
コンパイル
$ gcc hello_with_libc.c -o hello_with_libc $ ./hello_with_libc Hello World $ wc -c ./hello_with_libc 8561 ./hello_with_libc
もちろん問題なくコンパイルできる
libcなしのとき
コード
libcを使わないコードを書いた。
$ cat hello_no_libc.c int main() { char *str = "Hello World"; return 0; }
コンパイル
$ gcc -nostdlib hello_no_libc.c -o hello_no_libc /usr/bin/ld: 警告: エントリシンボル _start が見つかりません。デフォルトとして 0000000000400144 を使用します $ ./hello_no_libc Segmentation fault (コアダンプ)
-nostdlibをつけてlibcを含まずに、コンパイルするとエントリポイントが見つからないと怒られる。 ELF形式のエントリポイントはcrt1.oで定義されているのでリンクしてみる。
$ gcc -Os -c hello_no_libc.c $ ld /usr/lib/x86_64-linux-gnu/crt1.o -o hello_no_libc hello_no_libc.o ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 0 が無効なシンボル索引 11 を持っています ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 1 が無効なシンボル索引 12 を持っています ... ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 18 が無効なシンボル索引 13 を持っています ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 19 が無効なシンボル索引 21 を持っています ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_line): 再配置 0 が無効なシンボル索引 2 を持っています /usr/lib/x86_64-linux-gnu/crt1.o: 関数 `_start' 内: (.text+0x12): `__libc_csu_fini' に対する定義されていない参照です /usr/lib/x86_64-linux-gnu/crt1.o: 関数 `_start' 内: (.text+0x19): `__libc_csu_init' に対する定義されていない参照です /usr/lib/x86_64-linux-gnu/crt1.o: 関数 `_start' 内: (.text+0x25): `__libc_start_main' に対する定義されていない参照です
libc_csu_fini、libc_csu_init、libc_start_mainがないと怒られた。これらはmain関数が呼ばれる前に呼ばれる関数でlibcのセットアップを行う。crt1.oに頼らず、main関数を呼ぶエントリポイントを書く必要がある。
$ cat stubstart.s .globl _start _start: call main mov $60, %eax # exit xor %rdi, %rdi syscall
stubstart.sはmain関数を呼んだ後、exitのシステムコールを呼び出し終了する_start関数を書いたものである。 これがエントリポイントとなる。
$ gcc -nostdlib stubstart.s hello_no_libc.c -o hello_no_libc $ ./hello_no_libc $ wc -c ./hello_no_libc 1613 ./hello_no_libc
無事にコンパイルが通り実行できるようになった。冒頭でコンパイルしたHello worldの8561byteに比べ、libcがない分、1613byteとサイズが小さくなったことも確認できる。