1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

C言語ソースコードをGCCを利用せずに手動でコンパイルする

Last updated at Posted at 2020-12-15

C言語の知識、経験が全く無く、C言語のソースを手動でコンパイル、リンクしたら、実行時にセグフォが発生。
動かない原因の調査と動くところまでを確認したログ。

環境情報

Linux カーネル

Linux version 4.19.121-linuxkit (root@buildkitsandbox) (gcc version 9.2.0 (Alpine 9.2.0)) #1 SMP Tue Dec 1 17:50:32 UTC 2020

GCC

gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39)

NASM

NASM version 2.10.07 compiled on Jun  9 2014

GCCを利用したコンパイル

ソースコード

a.c

#include<stdio.h>

void main(void) {
    printf("hello, world\n");
}

コンパイル

実行ファイル作成

# gcc a.c

実行

# ./a.out
hello, world

GCCを利用すると当たり前に動く

GCC(コンパイラドライバ)を利用しないコンパイル

まずはGCCの実行内容を確認

# gcc -v a.c
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
gcc version 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC)
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'
 /usr/libexec/gcc/x86_64-redhat-linux/4.8.5/cc1 -quiet -v a.c -quiet -dumpbase a.c -mtune=generic -march=x86-64 -auxbase a -version -o /tmp/cclk9k31.s
GNU C (GCC) version 4.8.5 20150623 (Red Hat 4.8.5-39) (x86_64-redhat-linux)
        compiled by GNU C version 4.8.5 20150623 (Red Hat 4.8.5-39), GMP version 6.0.0, MPFR version 3.1.1, MPC version 1.0.1
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/include
 /usr/local/include
 /usr/include
End of search list.
GNU C (GCC) version 4.8.5 20150623 (Red Hat 4.8.5-39) (x86_64-redhat-linux)
        compiled by GNU C version 4.8.5 20150623 (Red Hat 4.8.5-39), GMP version 6.0.0, MPFR version 3.1.1, MPC version 1.0.1
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: edd9a53947039836c859e437e8c9af72
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'
 as -v --64 -o /tmp/ccDvQV5t.o /tmp/cclk9k31.s
GNU assembler version 2.27 (x86_64-redhat-linux) using BFD version version 2.27-43.base.el7_8.1
COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'
 /usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. /tmp/ccDvQV5t.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o

抜粋

C言語ソースコード => アセンブリ言語

/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/cc1 -quiet -v a.c -quiet -dumpbase a.c -mtune=generic -march=x86-64 -auxbase a -version -o /tmp/cclk9k31.s

アセンブリ言語 => オブジェクトファイル

as -v --64 -o /tmp/ccDvQV5t.o /tmp/cclk9k31.s

オブジェクトファイル => 実行ファイル(ELF)

/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. /tmp/ccDvQV5t.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o

ざっくりやっていることは C言語 => アセンブリ => オブジェクトファイル => 実行ファイル
各ステップで利用しているコマンドを確認したので手で実行する

コンパイル

C言語ソースコード => アセンブリ言語

# /usr/libexec/gcc/x86_64-redhat-linux/4.8.5/cc1 a.c

アセンブリ言語 => オブジェクトファイル

# as a.s

オブジェクトファイル => 実行ファイル(ELF)

# ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a a.out \
/usr/lib64/crt1.o \
/usr/lib64/crti.o \
/usr/lib64/crtn.o \
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o \
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o \
-lc

実行

# ./a
hello, world

これはcrt-lc(libc)をリンクしているので、動く

躓いた点として、リンク対象にcrt-lcを含めずに実行ファイルを作成、実行、_startputsがないと言われた

# ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a a.out
ld: warning: cannot find entry symbol _start; defaulting to 00000000004000b0
a.out: In function `main':
a.c:(.text+0xa): undefined reference to `puts'

crt はスタートアップルーチンが含まれるオブジェクト
-lc はlibc(基本的な機能のライブラリ、putsやexit)

なので一緒にリンクしてあげる

スタートアップルーチン、libcをリンクしない実行ファイルの作成

ソースコード

a.c

void print(char *str, int length);

void main(void) {
    char *str = "Hello, World!\n";
    print(str, 14);
}

b.asm

global _start, print
extern main

exit:
    mov eax, 60
    syscall

print:
    mov rdx, rsi
    mov esi, edi
    mov eax, 1
    mov edi, 1
    syscall
    ret

_start:
    call main
    call exit

ソースコードの説明

ldはデフォルトで_startがプログラムの開始位置となり、スタートアップルーチンをリンクしないため自前でアセンブリに関数を用意
libcをリンクしないため、直接アセンブリから文字出力を実行

コンパイル

C言語ソースコード => アセンブリ言語

/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/cc1 a.c

アセンブリ言語 => オブジェクトファイル

as a.s

アセンブリ言語 => オブジェクトファイル※nasmの書き方しか分からないので

nasm -f elf64 b.asm

オブジェクトファイル => 実行ファイル(ELF)

ld -o a a.out b.o

実行

# ./a
hello, world

できた

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?