C/C++のオブジェクトファイルをldコマンドでリンクする方法
C/C++のオブジェクトファイルをldコマンドでリンクする方法を紹介します。
1.問題点
下記のプログラムを作りました。
sample.h
class Sample {
public:
Sample();
void foo(int);
};
sample.cc
#include "stdio.h"
#include "sample.h"
Sample::Sample(){
}
void Sample::foo() {
printf( "Hello, World!\n" );
}
test.h
class Test {
public:
Test();
void hoge();
};
test.cc
#include "test.h"
#include "sample.h"
Test::Test(){
}
void Test::hoge() {
Sample a;
a.foo();
}
int main () {
Test test;
test.hoge();
}
これをコンパイルして、オブジェクトファイルsample.o、test.oを作ります。
% g++ -I. -c sample.cc test.cc
このあと、g++ではなくldコマンドでリンケージを実行すると、「`puts' に対する定義されていない参照です」というエラーになります。
% ld -o main sample.o test.o
ld: 警告: エントリシンボル _start が見つかりません。デフォルトとして 00000000004000b0 を使用します
sample.o: 関数 `Sample::foo()' 内:
sample.cc:(.text+0x1c): `puts' に対する定義されていない参照です
ちなみに、g++コマンドでは正常にリンクできます。
% g++ -o main sample.o test.o
色々調べてみて"-lc"および"-lm"オプションを追加してみたところ、下記のコマンドラインで実行ファイル作成までできました。
% ld -o main -lc -lm sample.o test.o
ただ、実行すると「-bash: ./main: /lib/ld64.so.1: bad ELF interpreter: そのようなファイルやディレクトリはありません」というエラーになってしまいます。
% ./main
-bash: ./main: /lib/ld64.so.1: bad ELF interpreter: そのようなファイルやディレクトリはありません
lddコマンドで使われているライブラリを調べてみました。
g++でリンクした実行ファイル
% ldd main
linux-vdso.so.1 => (0x00007fff19ba3000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f21983a5000)
libm.so.6 => /lib64/libm.so.6 (0x00007f21980a3000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f2197e8c000)
libc.so.6 => /lib64/libc.so.6 (0x00007f2197acb000)
/lib64/ld-linux-x86-64.so.2 (0x00007f21986c2000)
ldでリンクした実行ファイル
% ldd main
linux-vdso.so.1 => (0x00007ffd1fc4f000)
libc.so.6 => /lib64/libc.so.6 (0x00007f6f19b6f000)
libm.so.6 => /lib64/libm.so.6 (0x00007f6f1986d000)
/lib/ld64.so.1 => /lib64/ld-linux-x86-64.so.2 (0x00007f6f19f45000)
出力結果より、オプションで指定するライブラリが足りないようですが、具体的な指定方法が分かりません。
ということで、ldコマンドでリンクする方法を紹介します。
2.ldコマンドでリンクする
ldコマンドでリンクするには、一旦g++コマンドに"-v"オプションをつけてリンクします。
% g++ -v -o main sample.o test.o
組み込み spec を使用しています。
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
ターゲット: x86_64-redhat-linux
configure 設定: ../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
スレッドモデル: posix
gcc バージョン 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)
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' '-o' 'main' '-shared-libgcc' '-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 -o main /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/../../.. sample.o test.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /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
"-v"は、コンパイルの各ステージで実行されるコマンドを表示するオプションです。
で、上記の最後の行に表示された"/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2"に--helpをつけて実行すると、
% /usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --help
Usage: collect2 [options]
Wrap linker and generate constructor code if needed.
Options:
-debug Enable debug output
--help Display this information
-v, --version Display this program's version number
Overview: http://gcc.gnu.org/onlinedocs/gccint/Collect2.html
Report bugs: <http://bugzilla.redhat.com/bugzilla>
使用法: /usr/bin/ld [options] file...
(後略)
どうやらこれがldコマンドらしいので、これに続く内容(「--build-id」以降)をまるっとコピーして、ldコマンドのオプションに利用します。
% ld -o main --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o main /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/../../.. sample.o test.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /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
これで正常な実行ファイルが作成できました。
3.参考サイト
参考サイトは下記です。ありがとうございました。
- Overwriting Symbols in the GNU Linker File | MCU on Eclipse
- How to link builti-in functions such as ``puts'' with ``ld''?
- How to link C++ object files with ld
- --defsym option