LinuxでCプログラムをコンパイルしようとしています。しかし、好奇心から、私はいくつかのステップを手作業で実行しようとしています。
今、私はリンク部分で立ち往生しています。
プログラムは非常に基本的な「Helloworld」です。
#include <stdio.h>
int main() {
printf("Hello\n");
return 0;
}
次のコマンドを使用して、アセンブリコードを生成します。
gcc hello.c -S -masm=intel
コンパイル後に終了し、Intel構文でアセンブリコードをダンプするようにgccに指示しています。
次に、th GNUアセンブラを使用して、オブジェクトファイルを生成します。
as -o hello.o hello.s
次に、ldを使用して最終的な実行可能ファイルを作成してみます。
ld hello.o /usr/lib/libc.so /usr/lib/crt1.o -o hello
しかし、次のエラーメッセージが表示され続けます。
/usr/lib/crt1.o: In function `_start':
(.text+0xc): undefined reference to `__libc_csu_fini'
/usr/lib/crt1.o: In function `_start':
(.text+0x11): undefined reference to `__libc_csu_init'
シンボル__libc_csu_fini/init
はglibcの一部のようですが、どこにも見つかりません。 libcに対して静的に(/usr/lib/libc.a
に対して)リンクしようとしましたが、同じ結果になりました。
問題は何でしょうか?
私は見つけました 別の投稿 手がかりが含まれています:-dynamic-linker /lib/ld-linux.so.2
。
これを試して:
$ gcc hello.c -S -masm=intel
$ as -o hello.o hello.s
$ ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o hello.o -lc /usr/lib/crtn.o
$ ./hello
hello, world
$
/usr/lib/libc.so
は、共有ライブラリ/lib/libc.so.6
と非共有部分/usr/lib/libc_nonshared.a
をプルするようにリンカーに指示するリンカースクリプトです。
__libc_csu_init
と__libc_csu_fini
は/usr/lib/libc_nonshared.a
から来ています。非共有ライブラリ内のシンボルへの参照は、リンカー行でそれらを定義するアーカイブの前に表示する必要があるため、これらは見つかりません。あなたの場合、/usr/lib/crt1.o
(それらを参照する)はafter/usr/lib/libc.so
(それらを引き込む)の後に表示されるため、機能しません。
リンク行の順序を修正すると、もう少し先に進むことができますが、__libc_csu_init
と__libc_csu_fini
(現在は検出されています)が_init
と_fini
を検出できないという新しい問題が発生する可能性があります。 Cライブラリ関数を呼び出すには、/usr/lib/crti.o
(crt1.o
の後、ただしbeforeCライブラリ)と/usr/lib/crtn.o
(afterCライブラリ)。初期化コードとファイナライズコードが含まれています。
それらを追加すると、正常にリンクされた実行可能ファイルが得られます。動的リンカーが何であるかを指定せずに動的にリンクされたCライブラリを使用するため、stillは機能しません。 -dynamic-linker /lib/ld-linux.so.2
(少なくとも32ビットx86の場合、標準のダイナミックリンカーの名前はプラットフォームによって異なります)のようなものを使用して、リンカーにもそのことを伝える必要があります。
これらすべてを実行すると(基本的にRobの回答に従って)、単純なケースで機能するものが得られます。ただし、GCCは、コードが特定の機能を使用する場合に必要になる可能性のある独自のライブラリルーチンをいくつか提供しているため、より複雑なコードでさらに問題が発生する可能性があります。これらはGCCインストールディレクトリの奥深くに埋め込まれます...
gcc
が何をしているのかを確認するには、-v
オプション(実行時に呼び出すコマンドが表示されます)または-###
オプション(実行するコマンドを出力するだけです)を使用して実行します。引数の引用符がありますが、実際には何も実行されません)。出力は、通常、独自のコンポーネントの1つを介して間接的にld
を呼び出すことを知らない限り、混乱します collect2
(これは、適切なポイントでC++コンストラクター呼び出しを接着するために使用されます)。
gcc -o hello hello.c
の通常の呼び出しで動作するビルドが生成されると仮定して、次のコマンドを実行します。
gcc --verbose -o hello hello.c
そしてgccはそれが物事をどのようにリンクしているかを教えてくれます。それはあなたがあなたのリンクステップで説明する必要があるかもしれないすべての良い考えをあなたに与えるはずです。
Ubuntu 14.04(GCC 4.8)では、最小限のリンクコマンドは次のとおりです。
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
/usr/lib/x86_64-linux-gnu/crt1.o \
/usr/lib/x86_64-linux-gnu/crti.o \
-L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \
-lc -lgcc -lgcc_s \
hello.o \
/usr/lib/x86_64-linux-gnu/crtn.o
必須ではない場合もありますが、-lgcc
および-lgcc_s
にもリンクする必要があります。これは、GCCが、ハードウェアがネイティブに実装していない操作のために、これらのライブラリに存在する関数への呼び出しを発行する場合があるためです。 long long int
32ビットでの操作。参照: 本当にlibgccが必要ですか?
追加する必要がありました:
-L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \
デフォルトのリンカスクリプトにはそのディレクトリが含まれておらず、そこにlibgcc.a
が配置されていたためです。
Michael Burrが述べたように、パスはgcc -v
で見つけることができます。より正確には、次のものが必要です。
gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'
これは私がubuntu11.10でそれを修正した方法です:
apt-get remove libc-dev
はいと言ってすべてのパッケージを削除しますが、リストをコピーして後で再インストールします。
apt-get install libc-dev
リンクプロセスを手動で行っているため、Cランタイム初期化子またはそれが呼び出されるものをリンクするのを忘れています。
プラットフォームにリンクする場所と内容の詳細に触れないようにするには、Intel asmファイルを取得した後、gccを使用して実行可能ファイルを生成(コンパイルおよびリンク)します。
単にgcc hello.c -o hello
は機能するはずです。