標準出力

新しいもの、変わらないこと 自分の頭を通して考えてみました (stdout)

(広義の)コンパイルの工程

分かっていなかった事が分かったので
コンパイルの工程を復習するために hello.cに依存しているtest.cのコンパイル過程を
FreeBSD 9.1 RELEASE で出力してみた。

-hello.h-
#ifndef HELLO_H
#define HELLO_H

int sayHello(void);

#endif

-hello.c-
#include "stdio.h"
#include "hello.h"

int
sayHello(void)
{
  printf("hello\n");
  return 0;
}

-test.c-
#include
#include "hello.h"

int
main(int argc, char **argv)
{
  sayHello();
  return 0;
}


なお、あらかじめhello.cは中間ファイル(オブジェクトコード)hello.oを生成済み。

$ gcc -o test -save-temps -v hello.o test.c

-save-temps … 通常は、削除される中間ファイルをカレントディレクトリに残す
-v … 途中コマンドを標準エラー出力にコマンドのバージョン情報も含めて出力する

以下が出力したコマンド

Using built-in specs.
Target: amd64-undermydesk-freebsd
Configured with: FreeBSD/amd64 system compiler
Thread model: posix
gcc version 4.2.1 20070831 patched [FreeBSD]
 /usr/libexec/cc1 -E -quiet -v -D_LONGLONG test.c -fpch-preprocess -o test.i #1
#include "..." search starts here:
#include <...> search starts here:
 /usr/include/gcc/4.2
 /usr/include
End of search list.
 /usr/libexec/cc1 -fpreprocessed test.i -quiet -dumpbase test.c -auxbase test -version -o test.s #2
GNU C version 4.2.1 20070831 patched [FreeBSD] (amd64-undermydesk-freebsd)
compiled by GNU C version 4.2.1 20070831 patched [FreeBSD].
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: a8daf30220ba0c32c21af96787b683e6
 /usr/bin/as -V -Qy -o test.o test.s #3
GNU assembler version 2.17.50 [FreeBSD] 2007-07-03 (x86_64-unknown-freebsd) using BFD version 2.17.50 [FreeBSD] 2007-07-03
 /usr/bin/ld --eh-frame-hdr -V -dynamic-linker /libexec/ld-elf.so.1 -o test /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtbegin.o -L/usr/lib -L/usr/lib hello.o test.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/crtend.o /usr/lib/crtn.o #4
GNU ld 2.17.50 [FreeBSD] 2007-07-03
  Supported emulations:
   elf_x86_64_fbsd
   elf_i386_fbsd


#1 プリプロセス
cc1 がコンパイラ本体らしい
以下が出力されたtest.i の一部
マクロ展開とインクルードファイルの中身の出力が行われていることが確認できる。

# 1 "test.c"
# 1 ""
# 1 ""
# 1 "test.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 39 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/sys/cdefs.h" 1 3 4
# 40 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/include/sys/_null.h" 1 3 4
# 41 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/include/sys/_types.h" 1 3 4
# 33 "/usr/include/sys/_types.h" 3 4
# 1 "/usr/include/machine/_types.h" 1 3 4
# 51 "/usr/include/machine/_types.h" 3 4
typedef signed char __int8_t;
typedef unsigned char __uint8_t;
typedef short __int16_t;
typedef unsigned short __uint16_t;
typedef int __int32_t;

=中略=

# 2 "test.c" 2
# 1 "hello.h" 1



int sayHello(void);
# 3 "test.c" 2

int
main(int argc, char **argv)
{
  sayHello();
  return 0;
}

#2 アセンブリコード生成


-test.s-
.file "test.c"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB3:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
subq $16, %rsp
.LCFI2:
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
call sayHello
movl $0, %eax
leave
ret
.LFE3:
.size main, .-main
.section .eh_frame,"a",@progbits
.Lframe1:
.long .LECIE1-.LSCIE1
.LSCIE1:
.long 0x0
.byte 0x1
.string "zR"
.uleb128 0x1
.sleb128 -8
.byte 0x10
.uleb128 0x1
.byte 0x3
.byte 0xc
.uleb128 0x7
.uleb128 0x8
=以下略=


#3 アセンブル
アセンブルのコードを機械語に変換 オブジェクトファイル(*.o)を出力する


#4 リンク
必要なライブラリをリンクして、最終的な実行形式ファイル(ELF)を出力する。
以下は、出力されたバイナリに依存する共有ライブラリを表示

$ ldd test

test:
libc.so.7 => /lib/libc.so.7 (0x800819000)