コンパイルからプログラム実行ファイル生成までの流れ(C言語)
データベース使用するライブラリを動作させるためにC言語のコンパイラをインストールしなければならないことがあり、これまでよりも低レイヤーの学習しておこうという気になった。 今回はC言語本格入門のコンパイルからプログラム実行ファイル生成までの流れをまとめておく。
環境
Debian GNU/Linux 11 (bullseye)
GCC
C言語のコンパイラ。gccは単にコンパイルをするのではなく、中で色々な処理を行なっている。 その処理とはプリプロセス、コンパイル、アセンブル、リンクという流れで行っている。 プリプロセスはその名の通り、コンパイルの前処理。 コンパイルとはソースコードをアセンブリ言語のソースコードに変換する動作。
拡張子の整理
コンパイル(アセンブリ言語ソース)への変換
nop.c
(空のC言語処理を書いたファイル)を用意する
root@xxx:~/studyc# gcc -S nop.c
上記のようにコマンドを打つとnop.s
が生成される。環境によって生成されるコードは異なる。
.file "nop.c" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl %edi, -4(%rbp) movq %rsi, -16(%rbp) movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Debian 10.2.1-6) 10.2.1 20210110" .section .note.GNU-stack,"",@progbits
プリプロセス処理の確認
以下のコマンドを打つとプリプロセスした結果をテキストファイルに保存する
root@xxx:~/studyc# gcc -E nop.c > nop.txt
nop.txt
の中身をcatするなどして確認するとnop.c
に記載されていたはずの#include <stdio.h>
の記述が消えている。これはプリプロセスの結果加工され、stdio.h
の内容を表示させているため。このstdio.h
自体は以下のように/usr/include
に入っていることが確認できる。
root@xxx:/usr/include# find . -name stdio.h ./x86_64-linux-gnu/bits/stdio.h ./stdio.h ./c++/10/tr1/stdio.h
アセンブル(機械語の生成)
gcc
コマンドをそのまま使うと実行ファイルの生成まで行ってしまうが、gcc -c
コマンドを使うとリンクまでは行わないでおける。(実行ファイルを生成せずに機械語の生成にとどめる)
root@xxx:~/studyc# gcc -c nop.s
こうするとnop.o
が生成される。
root@xxx:~/studyc# ls nop.c nop.o nop.s nop.txt
実行しようとすると
root@xxx:~/studyc# ./nop.o bash: ./nop.o: cannot execute binary file: Exec format error
実行できない。これはnop.o
は機械語ではあるが、実行するための情報やC言語でプログラムを動かすための情報が不足しているため。
ここでnop.o
の容量を確認しておく
root@xxx:~/studyc# ls -l nop.o -rwxr-xr-x 1 root root 1232 Sep 19 07:43 nop.o
リンカ
これらはリンクを行うことで、自動で付加されて機械語のファイル(実行ファイル)になる。この実行できない機械語のファイルをオブジェクトファイルという。 オブジェクトファイルはDebian系では以下のようにリンクを使って実行ファイルを作成できる
root@xxx:/lib/x86_64-linux-gnu# cd ~/studyc root@xxx:~/studyc# ld nop.o -dynamic-linker /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/{crt1.o,crti.o,crtn.o} -lc
実行ファイルの容量を見るとかなり増えていることがわかる。 この増えた分が実行ファイルのための情報や、C言語プログラムを動かすための情報になる。
root@xxx:~/studyc# ls -l a.out -rwxr-xr-x 1 root root 15232 Sep 19 07:56 a.out
スタートアップルーチン
リンクにより増えた実行ファイルの中で、C言語でプログラムを動かすための情報をスタートアップルーチンと呼ぶ。
このスタートアップルーチンが含まれたファイルをCランタイムと呼び、プログラムが動作するためにはCランタイムの他にlibcと呼ばれるC言語で書いたプログラムを動かすためのライブラリが必要。
libcには画面に文字を表示するなどに必要なプログラムが格納されている。リンクという作業はこれらを結合するための作業なのでリンクと呼ぶ。
この記事のリンカセクションで実行したコマンドの中で、crt
から始まるファイルがスタートアップルーチンを含んでいるファイル。
main関数はスタートアップルーチンから呼び出される。