C++プログラムコンパイル手順の詳細
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("Hello World!
");
return 0;
}
gccでコンパイルすると、1つのコマンドだけで実行可能ファイルhwを生成できます.
xiaosuo@gentux hw $ gcc -o hw hw.c xiaosuo@gentux hw $ ./hw Hello World!
gccが背後でどのような動作をしているのかを-vパラメータで見ることができます.
Reading specs from /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/specs
Configured with: /var/tmp/portage/sys-devel/gcc-3.4.6-r2/work/gcc-3.4.6/configure--prefix=/usr --bindir=/usr/i686-pc-linux-gnu/gcc-bin/3.4.6--includedir=/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/include--datadir=/usr/share/gcc-data/i686-pc-linux-gnu/3.4.6--mandir=/usr/share/gcc-data/i686-pc-linux-gnu/3.4.6/man--infodir=/usr/share/gcc-data/i686-pc-linux-gnu/3.4.6/info--with-gxx-include-dir=/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/include/g++-v3--host=i686-pc-linux-gnu --build=i686-pc-linux-gnu --disable-altivec --enable-nls--without-included-gettext --with-system-zlib --disable-checking --disable-werror--enable-secureplt --disable-libunwind-exceptions --disable-multilib --disable-libgcj--enable-languages=c,c++,f77 --enable-shared --enable-threads=posix--enable-__cxa_atexit --enable-clocale=gnu
Thread model: posix
gcc version 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)
/usr/libexec/gcc/i686-pc-linux-gnu/3.4.6/cc1 -quiet -v hw.c -quiet -dumpbase hw.c-mtune=pentiumpro -auxbase hw -version -o /tmp/ccYB6UwR.s
ignoring nonexistent directory "/usr/local/include"
ignoring nonexistent directory "/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/http://www.cnblogs.com/http://www.cnblogs.com/i686-pc-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/include
/usr/include
End of search list.
GNU C version 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10) (i686-pc-linux-gnu)
compiled by GNU C version 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.9).
GGC heuristics: --param ggc-min-expand=81 --param ggc-min-heapsize=97004
/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/../../../../i686-pc-linux-gnu/bin/as -V -Qy -o /tmp/ccq8uGED.o /tmp/ccYB6UwR.s
GNU assembler version 2.17 (i686-pc-linux-gnu) using BFD version 2.17
/usr/libexec/gcc/i686-pc-linux-gnu/3.4.6/collect2 --eh-frame-hdr -m elf_i386-dynamic-linker /lib/ld-linux.so.2 -o hw /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/../../../crt1.o /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/../../../crti.o /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtbegin.o-L/usr/lib/gcc/i686-pc-linux-gnu/3.4.6 -L/usr/lib/gcc/i686-pc-linux-gnu/3.4.6-L/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/../../../../i686-pc-linux-gnu/lib-L/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/../../.. /tmp/ccq8uGED.o -lgcc --as-needed-lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtend.o /usr/lib/gcc/i686-pc-linux-gnu/3.4.6/../../../crtn.o
少し整理して、冗長情報をいくつか削除した後、以下のようにします:
cc1 hw.c -o /tmp/ccYB6UwR.s
as -o /tmp/ccq8uGED.o /tmp/ccYB6UwR.s
ld -o hw /tmp/ccq8uGED.o
の3つのコマンドは、コンパイルステップの +コンパイル、アセンブリ、 にそれぞれ します. とコンパイルは1つのコマンド(cc 1)で い、
に び することができます.cpp -o hw.i hw.c
cc1 hw.i -o /tmp/ccYB6UwR.s
のhwをコンパイルできる されたものである.cファイルのMakefileは の りです..PHONY: clean
all: hw
hw: hw.o
ld -dynamic-linker /lib/ld-linux.so.2 -o hw /usr/lib/crt1.o \
/usr/lib/crti.o \
/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtbegin.o \
hw.o -lc \
/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtend.o \
/usr/lib/crtn.o
hw.o: hw.s
as -o hw.o hw.s
hw.s: hw.i
/usr/libexec/gcc/i686-pc-linux-gnu/3.4.6/cc1 -o hw.s hw.c
hw.i: hw.c
cpp -o hw.i hw.c
clean:
rm -rf hw.i hw.s hw.o
もちろん、 のMakefileのいくつかのパスは のシステムの な で、あなたは とは うかもしれません. に、コンパイラの ステップが をしているかをコンパイル に てみましょう.まず、 、 のファイルhwである.i: # 1 "hw.c"
# 1 "<built-in>"
# 1 "<command line>"
...
__extension__ typedef __quad_t __off64_t;
__extension__ typedef int __pid_t;
__extension__ typedef struct { int __val[2]; } __fsid_t;
...
extern int remove (__const char *__filename) __attribute__ ((__nothrow__));
extern int rename (__const char *__old, __const char *__new) __attribute__((__nothrow__));
...
int main(int argc, char *argv[])
{
printf("Hello World!
");
return 0;
}
:ファイルが きいため、 な はほんの しか っていません.プリプロセッサは、 のCソースファイルに まれるすべてのファイル( に まれるファイルを む)の を し、 ファイルに するほか、すべてのマクロ を しているので、プリプロセッサの ファイルにはマクロが つかりません.これにより、マクロ を する な も されます. 2 「コンパイル」は、C/C++コードをアセンブリコードに「 」することです..file "hw.c"
.section .rodata
.LC0:
.string "Hello World!
"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
addl $15, %eax
addl $15, %eax
shrl $4, %eax
sall $4, %eax
subl %eax, %esp
subl $12, %esp
pushl $.LC0
call printf
addl $16, %esp
movl $0, %eax
leave
ret
.size main, .-main
.section .note.GNU-stack,"",@progbits
.ident "GCC: (GNU) 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)"
このアセンブリファイルは のC/C++ファイルよりずっと さく、 していないタイプ や など、 なものがたくさん されています. 3ステップ「アセンブリ」は、 2ステップで されるアセンブリコードを のフォーマットに したマシンコードに し、Linux では にELFターゲットファイルとして される.xiaosuo@gentux hw $ file hw.o
hw.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
のステップ「 」は、 のステップで したターゲットファイルとシステムライブラリのターゲットファイルとライブラリファイルを し、 に のプラットフォームで な ファイルを します.なぜシステムライブラリのターゲットファイル(crt 1.o,crti.oなど)に するのですか?これらのターゲットファイルは、スタックメモリ り てコンテキスト の など、Cランタイム を または するために され、 にはcrtもC RunTimeの である.これはもう つの を している:プログラムはmain から されるのではなく、crtの のあるエントリから され、Linux のこのエントリは_start. のMakefileは、 に された ファイルを します. に された ファイルを するには、Makefileの なセグメントを する があります.hw: hw.o
ld -m elf_i386 -static -o hw /usr/lib/crt1.o \
/usr/lib/crti.o \
/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtbeginT.o \
-L/usr/lib/gcc/i686-pc-linux-gnu/3.4.6 \
-L/usr/i686-pc-linux-gnu/lib \
-L/usr/lib/ \
hw.o --start-group -lgcc -lgcc_eh -lc --end-group \
/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/crtend.o \
/usr/lib/gcc/i686-pc-linux-gnu/3.4.6/../../../crtn.o
これで、 ファイルが に されます. のプロジェクトではコンパイルプロセスをこのように かく ける はありません. の3つのステップは に されています.Makefileでは のように されています.hw.o: hw.c
gcc -o hw.o -c hw.c
、hw.cがどのような を ったのか、 の3つのステップはほとんど けられない.だから らを に くのも くありません. に--pipeパラメータでコンパイラに ファイルの わりにパイプを うことを えて、コンパイルの を めることができます.
:ソースhttp://www.xxlinux.com/linux/article/development/soft/20070424/8267.html