dynamic-linking について
Linuxの動的リンクを扱うエージェント向けのスキルです。共有ライブラリ、検索パス、プラグイン、シンボル可視性を解説します。
主な機能
- 01共有ライブラリの作成と soname 管理
- 02RPATH と RUNPATH の設定
- 03dlopen と dlsym によるプラグイン読み込み
- 04LD_PRELOAD による関数のインターセプト
- 05エクスポートシンボルの可視性制御
- 06一般的なリンクエラーの診断
活用シーン
- 01共有ライブラリが見つからないエラーの解決
- 02アプリケーションの再配置可能なライブラリパス設定
- 03実行時プラグイン読み込み機構の構築
- 04シンボル不足や ABI バージョン問題の調査
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill dynamic-linkingインストール後、Claude Code で/dynamic-linkingを入力して読み込まれたか確認します。
同梱ファイルreferences/ld-rpath-soname.md
詳細
動的リンク
目的
Linux の動的リンクについてエージェントをガイドします。共有ライブラリの作成、RPATH/RUNPATH の設定、soname のバージョニング、dlopen/dlsym を使ったプラグインのパターン、LD_PRELOAD によるインターポジション、シンボルの可視性制御を扱います。
トリガー
- 「Cannot open shared object file: No such file or directory」
- 「バイナリから共有ライブラリを見つけられるように RPATH を設定するには?」
- 「プラグインシステムで dlopen/dlsym を使うには?」
- 「RPATH と RUNPATH の違いは?」
- 「関数をインターセプトするために LD_PRELOAD を使うには?」
- 「soname を使って共有ライブラリをバージョニングするには?」
ワークフロー
1. 共有ライブラリの作成
# Compile with -fPIC (position-independent code)
gcc -fPIC -c src/mylib.c -o mylib.o
# Link shared library with soname
gcc -shared -Wl,-soname,libmylib.so.1 \
mylib.o -o libmylib.so.1.2.3
# Create symlinks (standard convention)
ln -s libmylib.so.1.2.3 libmylib.so.1 # soname link (used by ldconfig)
ln -s libmylib.so.1 libmylib.so # link link (used at compile time)
# Register with ldconfig (system-wide)
sudo cp libmylib.so.1.2.3 /usr/local/lib/
sudo ldconfig
2. Soname のバージョニング規約
libfoo.so.MAJOR.MINOR.PATCH
│
└── soname = libfoo.so.MAJOR
| バージョンの変更 | タイミング |
|---|---|
| PATCH | バグ修正、ABI に変更なし |
| MINOR | 新しいシンボルの追加、後方互換性あり |
| MAJOR | ABI の破壊的変更 — 既存のバイナリは動作しなくなります |
soname を確認します。
readelf -d libmylib.so.1.2.3 | grep SONAME
objdump -p libmylib.so.1.2.3 | grep SONAME
3. RPATH と RUNPATH
Both embed a library search path in the binary.
RPATH → searched BEFORE LD_LIBRARY_PATH
RUNPATH → searched AFTER LD_LIBRARY_PATH (controllable at runtime)
Recommendation: prefer RUNPATH (-Wl,--enable-new-dtags)
for deployment flexibility.
# Embed RPATH (old default)
gcc main.c -L./lib -lmylib \
-Wl,-rpath,'$ORIGIN/../lib' -o myapp
# Embed RUNPATH (new default with --enable-new-dtags)
gcc main.c -L./lib -lmylib \
-Wl,-rpath,'$ORIGIN/../lib' \
-Wl,--enable-new-dtags -o myapp
# Inspect
readelf -d myapp | grep -E 'RPATH|RUNPATH'
chrpath -l myapp # show
chrpath -r '/new/path' myapp # modify existing
$ORIGIN は実行時にバイナリのディレクトリとして解決されます。移動可能なインストールで使用してください。
4. ライブラリの検索順序
1. DT_RPATH (if no DT_RUNPATH present)
2. LD_LIBRARY_PATH (env var, ignored for suid binaries)
3. DT_RUNPATH
4. /etc/ld.so.cache (populated by ldconfig from /etc/ld.so.conf)
5. /lib, /usr/lib
次のコマンドでデバッグします。
LD_DEBUG=libs ./myapp # trace library loading decisions
ldd myapp # show resolved libraries
ldd -v myapp # verbose with version requirements
5. dlopen / dlsym を使ったプラグインのパターン
#include <dlfcn.h>
typedef int (*plugin_fn_t)(const char *input);
void load_plugin(const char *path) {
// RTLD_NOW: resolve all symbols immediately (fail fast)
// RTLD_LAZY: resolve on first call (default)
// RTLD_LOCAL: symbols not visible to other loaded libs
// RTLD_GLOBAL: symbols visible globally
void *handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
if (!handle) {
fprintf(stderr, "dlopen: %s\n", dlerror());
return;
}
// Clear previous errors
dlerror();
plugin_fn_t fn = (plugin_fn_t)dlsym(handle, "plugin_run");
const char *err = dlerror();
if (err) {
fprintf(stderr, "dlsym: %s\n", err);
dlclose(handle);
return;
}
fn("hello");
dlclose(handle);
}
-ldl を指定してリンクします。
gcc main.c -ldl -o myapp
6. LD_PRELOAD によるインターポジション
LD_PRELOAD はほかのすべてのライブラリより先にライブラリを読み込むため、そのシンボルがアプリケーションのシンボルをオーバーライドします。
// myinterpose.c — intercept malloc
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
void *malloc(size_t size) {
static void *(*real_malloc)(size_t) = NULL;
if (!real_malloc)
real_malloc = dlsym(RTLD_NEXT, "malloc"); // find next malloc in chain
void *ptr = real_malloc(size);
fprintf(stderr, "malloc(%zu) = %p\n", size, ptr);
return ptr;
}
gcc -shared -fPIC -o myinterpose.so myinterpose.c -ldl
# Apply to any binary
LD_PRELOAD=./myinterpose.so ./myapp
LD_PRELOAD=/path/to/libfaketime.so ./myapp # time manipulation
7. シンボルの可視性制御
エクスポートするシンボルを制限して、バイナリサイズを小さくし、衝突を避けます。
// Mark default: visible to linker
__attribute__((visibility("default")))
int public_api(void) { return 42; }
// Hidden: internal, not exported
__attribute__((visibility("hidden")))
static int internal_helper(void) { return 0; }
または、リンカーバージョンスクリプトを使用します。
# mylib.map
MYLIB_1.0 {
global:
mylib_init;
mylib_process;
local:
*; # hide everything else
};
gcc -shared -fPIC -Wl,--version-script=mylib.map \
-o libmylib.so mylib.c
# Check exported symbols
nm -D --defined-only libmylib.so
objdump -T libmylib.so
デフォルトで -fvisibility=hidden を指定してビルドし、公開 API を明示的に指定します。
gcc -shared -fPIC -fvisibility=hidden \
mylib.c -o libmylib.so
8. よくあるエラー
| エラー | 原因 | 解決方法 |
|---|---|---|
cannot open shared object file | ライブラリが検索パスにありません | RPATH、LD_LIBRARY_PATH を設定するか、ldconfig を実行します |
symbol lookup error: undefined symbol | ライブラリが不足しているか、バージョンが違います | ldd を確認し、-l フラグを追加するか、リンク順序を修正します |
FATAL: kernel too old | バージョン要件が一致していません | 古い glibc に対して再ビルドします |
relocation R_X86_64_32 against .rodata | 共有ライブラリ内のコードが PIC ではありません | コンパイルに -fPIC を追加します |
version 'GLIBC_2.29' not found | 新しい glibc 上でバイナリがビルドされています | 古いシステム上で再ビルドするか、-static を使用します |
RPATH、soname、ld.so の設定の詳細については、references/ld-rpath-soname.md を参照してください。
関連スキル
- 共有ライブラリのセクションとシンボルの確認には
skills/binaries/elf-inspectionを使用します - リンカーフラグとシンボル解決には
skills/binaries/linkers-ltoを使用します - 共有ライブラリで
nm、objdump、stripを使用するにはskills/binaries/binutilsを使用します -fPIC、-sharedおよび関連するコンパイラフラグにはskills/compilers/gccを使用します
コメント
Markdown 対応