mcpField
D
dynamic-linking

dynamic-linking

github.com/mohitmishra786開発269611 回インストール18 週間前に更新
リポジトリ

dynamic-linking について

Linuxの動的リンクを扱うエージェント向けのスキルです。共有ライブラリ、検索パス、プラグイン、シンボル可視性を解説します。

主な機能

  1. 01共有ライブラリの作成と soname 管理
  2. 02RPATH と RUNPATH の設定
  3. 03dlopen と dlsym によるプラグイン読み込み
  4. 04LD_PRELOAD による関数のインターセプト
  5. 05エクスポートシンボルの可視性制御
  6. 06一般的なリンクエラーの診断

活用シーン

  1. 01共有ライブラリが見つからないエラーの解決
  2. 02アプリケーションの再配置可能なライブラリパス設定
  3. 03実行時プラグイン読み込み機構の構築
  4. 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新しいシンボルの追加、後方互換性あり
MAJORABI の破壊的変更 — 既存のバイナリは動作しなくなります

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 を使用します

コメント