静的ライブラリと動的ライブラリ

静的ライブラリとか動的ライブラリがよく理解できていなかったのでオライリーC++クックブックで勉強した。その際のメモ。

論理

コンパイラ
  • 入力 : C++ソースファイル。
  • 出力 : オブジェクトファイル。「実行可能コード」「関数やデータへのシンボリック参照」が含まれている。
アーカイバ
  • 入力 : オブジェクトファイルのコレクション。
  • 出力 : 静的ライブラリまたはアーカイブ(オブジェクトファイルを使いやすいようにまとめたもの)。
リンカ
  • 入力 : オブジェクトファイルとライブラリのコレクション。
  • それらのシンボリック参照を解決 : 実行可能ファイルまたは動的ライブラリ。「シンボリック参照を解決」とは使用されているシンボルをその定義と照合すること。

リンク、、、

  • する側 : 実行可能ファイルまたは動的ライブラリ
  • される側 : 実行可能ファイルや動的ライブラリをビルドするために使用されるライブラリ

実践

ディレクトリ構成

作業ディレクトリを以降では便宜的に$WORKとしています。

$ ls -R $WORK
$WORK/johnpaul:
john.cpp
john.hpp
johnpaul.cpp
johnpaul.hpp
paul.cpp
paul.hpp

$WORK/georgeringo:
george.cpp
george.hpp
georgeringo.cpp
georgeringo.hpp
ringo.cpp
ringo.hpp

$WORK/hellobeatles:
hellobeatles.cpp
ファイルの内容

$WORK/johnpaul/john.hpp

#ifndef JOHN_HPP_INCLUDED
#define JOHN_HPP_INCLUDED

void john(); // Prints "John, "

#endif // JOHN_HPP_INCLUDED

$WORK/johnpaul/john.cpp

#include <iostream>
#include "john.hpp"

void john()
{
  std::cout << "John, ";
}

$WORK/johnpaul/paul.hpp

#ifndef PAUL_HPP_INCLUDED
#define PAUL_HPP_INCLUDED

void paul(); // Prints "Paul, "

#endif // PAUL_HPP_INCLUDED

$WORK/johnpaul/paul.cpp

#include <iostream>
#include "paul.hpp"

void paul()
{
  std::cout << "Paul, ";
}

$WORK/johnpaul/johnpaul.hpp

#ifndef JOHNPAUL_HPP_INCLUDED
#define JOHNPAUL_HPP_INCLUDED

void johnpaul(); // Prints "John, Paul, "

#endif // JOHNPAUL_HPP_INCLUDED

$WORK/johnpaul/johnpaul.cpp

#include "john.hpp"
#include "paul.hpp"
#include "johnpaul.hpp"

void johnpaul()
{
  john();
  paul();
}

$WORK/georgeringo/george.hpp

#ifndef GEORGE_HPP_INCLUDED
#define GEORGE_HPP_INCLUDED

void george(); // Prints "George, "

#endif // GEORGE_HPP_INCLUDED

$WORK/georgeringo/george.cpp

#include <iostream>
#include "george.hpp"

void george()
{
  std::cout << "George, ";
}

$WORK/georgeringo/ringo.hpp

#ifndef RINGO_HPP_INCLUDED
#define RINGO_HPP_INCLUDED

void ringo(); // Prints "and Ringo"

#endif // RINGO_HPP_INCLUDED

$WORK/georgeringo/ringo.cpp

#include <iostream>
#include "ringo.hpp"

void ringo()
{
  std::cout << "and Ringo\n";
}

$WORK/georgeringo/georgeringo.hpp

#ifndef GEORGERINGO_HPP_INCLUDED
#define GEORGERINGO_HPP_INCLUDED

// libgeorgeringo.dllをビルドするときにGEORGERINGO_DLLを定義する
# if defined(_WIN32) && !defined(__GNUC__)
#   ifdef GEORGERINGO_DLL
#     define GEORGERINGO_DECL __declspec(dllexport)
#   else
#     define GEORGERINGO_DECL __declspec(dllimport)
#   endif
# endif // WIN32

# ifndef GEORGERINGO_DECL
#   define GEORGERINGO_DECL
# endif

// Prints "George, and Ringo\n"
#ifdef __MWERKS__
#pragma export on
#endif

GEORGERINGO_DECL void georgeringo();
#ifdef __MWERKS__
#pragma export off
#endif

#endif // GEORGERINGO_HPP_INCLUDED

$WORK/georgeringo/georgeringo.cpp

#include "george.hpp"
#include "ringo.hpp"
#include "georgeringo.hpp"

void georgeringo()
{
  george();
  ringo();
}

$WORK/hellobeatles/hellobeatles.cpp

#include "johnpaul/johnpaul.hpp"
#include "georgeringo/georgeringo.hpp"

int main()
{
  // Prints "John, Paul, George and Ringo\n"
  johnpaul();
  georgeringo();
}

Macでやってみる

g++ の属性確認。Command Line Tools for Xcode に同梱されているやつです。

$ g++ -v
Using built-in specs.
Target: i686-apple-darwin11
Configured with: /private/var/tmp/llvmgcc42/llvmgcc42-2336.9~22/src/configure --disable-checking --enable-werror --prefix=/Applications/Xcode.app/Contents/Developer/usr/llvm-gcc-4.2 --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-prefix=llvm- --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build=i686-apple-darwin11 --enable-llvm=/private/var/tmp/llvmgcc42/llvmgcc42-2336.9~22/dst-llvmCore/Developer/usr/local --program-prefix=i686-apple-darwin11- --host=x86_64-apple-darwin11 --target=i686-apple-darwin11 --with-gxx-include-dir=/usr/include/c++/4.2.1
Thread model: posix
gcc version 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.9.00)
"johnpaul"静的ライブラリの生成
$ cd $WORK/johnpaul

# オブジェクトファイルを生成。
$ g++ -c -o john.o john.cpp
$ g++ -c -o paul.o paul.cpp
$ g++ -c -o johnpaul.o johnpaul.cpp

# アーカイブ。
$ ar ru libjohnpaul.a john.o paul.o johnpaul.o
ar: creating archive libjohnpaul.a
$ ranlib libjohnpaul.a
"georgeringo"動的ライブラリの生成
$ cd $WORK/georgeringo

# オブジェクトファイルを生成。
$ g++ -c -o george.o george.cpp
$ g++ -c -o ringo.o ringo.cpp
$ g++ -c -o georgeringo.o georgeringo.cpp

# 動的ライブラリ生成。
$ g++ -dynamiclib -fPIC -o libgeorgeringo.dylib george.o ringo.o georgeringo.o
"hellobeatles"実行可能ファイルの作成。
$ cd $WORK/hellobeatles

# アプリケーションのcppファイルをオブジェクトファイルにコンパイル。
$ g++ -c -o hellobeatles.o hellobeatles.cpp -I ../

# リンカを使用してオブジェクトファイルとライブラリから実行可能ファイルを生成する。
$ g++ -o hellobeatles1 hellobeatles.o -L ../johnpaul -L ../georgeringo -ljohnpaul -lgeorgeringo

# 実行してみるもエラー。
$ ./hellobeatles1
dyld: Library not loaded: libgeorgeringo.dylib
  Referenced from: $WORK/hellobeatles/./hellobeatles1
  Reason: image not found
Trace/BPT trap: 5

# 別の方法でリンカを使用してオブジェクトファイルとライブラリから実行可能ファイルを生成する。
$ g++ -o hellobeatles2 hellobeatles.o ../johnpaul/libjohnpaul.a ../georgeringo/libgeorgeringo.dylib

# 実行してみるもやっぱりエラー。
$ ./hellobeatles2
dyld: Library not loaded: libgeorgeringo.dylib
  Referenced from: $WORK/hellobeatles/./hellobeatles2
  Reason: image not found
Trace/BPT trap: 5

# DYLD_LIBRARY_PATH を指定してやるとOK。
$ env DYLD_LIBRARY_PATH=$WORK/georgeringo ./hellobeatles1
John, Paul, George, and Ringo
$ env DYLD_LIBRARY_PATH=$WORK/georgeringo ./hellobeatles2
John, Paul, George, and Ringo

# リンクを確認。相対パスになってる。
$ otool -L hellobeatles1
hellobeatles1:
  libgeorgeringo.dylib (compatibility version 0.0.0, current version 0.0.0)
  /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 52.0.0)
  /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
$ otool -L hellobeatles2
hellobeatles2:
  libgeorgeringo.dylib (compatibility version 0.0.0, current version 0.0.0)
  /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 52.0.0)
  /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)

# 実行可能ファイルをパスの通りそうなところにコピーしてみる。
$ cp -iv hellobeatles1 ../georgeringo/
hellobeatles1 -> ../georgeringo/hellobeatles1
$ cp -iv hellobeatles2 ../georgeringo/
hellobeatles2 -> ../georgeringo/hellobeatles2
$ cd $WORK/georgeringo/

# コピーしたものを実行するとOK。
$ ./hellobeatles1
John, Paul, George, and Ringo
$ ./hellobeatles2
John, Paul, George, and Ringo

Ubuntuでやってみる

g++ の属性確認

$ g++ -v
組み込み spec を使用しています。
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-linux-gnu/4.6/lto-wrapper
ターゲット: i686-linux-gnu
configure 設定: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.6.3-1ubuntu5' --with-bugurl=file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.6 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.6 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --enable-targets=all --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=i686-linux-gnu --host=i686-linux-gnu --target=i686-linux-gnu
スレッドモデル: posix
gcc バージョン 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) 
"johnpaul"静的ライブラリの生成
$ cd $WORK/johnpaul
$ g++ -c -o john.o john.cpp
$ g++ -c -o paul.o paul.cpp
$ g++ -c -o johnpaul.o johnpaul.cpp
$ ar ru libjohnpaul.a john.o paul.o johnpaul.o
ar: creating libjohnpaul.a
$ ranlib libjohnpaul.a
"georgeringo"動的ライブラリの生成
$ cd $WORK/georgeringo
$ g++ -c -o george.o george.cpp
$ g++ -c -o ringo.o ringo.cpp
$ g++ -c -o georgeringo.o georgeringo.cpp
$ g++ -shared -fPIC -o libgeorgeringo.so george.o ringo.o georgeringo.o
"hellobeatles"実行可能ファイルの作成
$ cd $WORK/hellobeatles

# アプリケーションのcppファイルをオブジェクトファイルにコンパイル。
$ g++ -c -o hellobeatles.o hellobeatles.cpp -I ../

# リンカを使用してオブジェクトファイルとライブラリから実行可能ファイルを生成する。
$ g++ -o hellobeatles1 hellobeatles.o -L ../johnpaul -L ../georgeringo -ljohnpaul -lgeorgeringo

# 実行してみるもエラー
$ ./hellobeatles1
./hellobeatles1: error while loading shared libraries: libgeorgeringo.so: cannot open shared object file: No such file or directory

# リンクを確認。見つからないと言われる。
$ ldd hellobeatles1
        linux-gate.so.1 =>  (0xb777b000)
        libgeorgeringo.so => not found
        libstdc++.so.6 => /usr/lib/i386-linux-gnu/libstdc++.so.6 (0xb7689000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb74e3000)
        libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xb74b7000)
        /lib/ld-linux.so.2 (0xb777c000)
        libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0xb7499000)

# リンカを使用してオブジェクトファイルとライブラリから実行可能ファイルを生成する。
$ g++ -o hellobeatles2 hellobeatles.o ../johnpaul/libjohnpaul.a ../georgeringo/libgeorgeringo.so

# 実行してみるとOK。
$ ./hellobeatles2
John, Paul, George, and Ringo

# リンクを確認。ちゃんと認識されている。
$ ldd hellobeatles2
        linux-gate.so.1 =>  (0xb77a9000)
        ../georgeringo/libgeorgeringo.so (0xb77a4000)
        libstdc++.so.6 => /usr/lib/i386-linux-gnu/libstdc++.so.6 (0xb76b4000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb750e000)
        libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xb74e2000)
        /lib/ld-linux.so.2 (0xb77aa000)
        libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0xb74c4000)

まとめ

  • 静的ライブラリは実行可能ファイルに取り込まれる。
  • 動的ライブラリは実行可能ファイルに取り込まれない。よってパスがちゃんと通ってないと困ることになる。パスの通し方はもう要検証。
  • LinuxならlddOSXならotoolで動的ライブラリのパスを確認できる。

C++クックブック
C++クックブック
posted with amazlet at 12.09.19
D. Ryan Stephens Christopher Diggins Jonathan Turkanis Jeff Cogswell
オライリー・ジャパン
売り上げランキング: 633899