# 機能設計書: 57-dynlib（動的ライブラリ）

## 1. 概要

### 1.1 業務上の目的・背景

dynlibモジュールは、共有ライブラリ（動的ライブラリ）からシンボルにアクセスする機能を提供する。POSIXではdlsym機構、WindowsではLoadLibrary APIを使用して、実行時にライブラリをロードし、関数や変数のアドレスを取得する。

### 1.2 機能の利用シーン

- プラグインシステムの実装
- 実行時の機能拡張
- C/C++ライブラリとの動的連携
- ランタイムでの言語ライブラリ切り替え
- オプショナルな依存関係の遅延ロード

### 1.3 主要な処理内容

1. ライブラリロード: `loadLib` - DLL/SOのロード
2. シンボル取得: `symAddr`, `checkedSymAddr` - 関数/変数アドレス取得
3. ライブラリアンロード: `unloadLib` - リソース解放
4. パターンマッチロード: `loadLibPattern` - 複数候補からロード

### 1.4 関連するデータ/ファイル

- 共有ライブラリファイル（.dll, .so, .dylib）
- シンボル名（関数名、変数名）

### 1.5 バッチ/オンライン区分

オンライン（ランタイムライブラリロード）

## 2. 関連画面

- プラグイン管理画面
- 拡張機能設定
- ドライバロード

## 3. 機能種別

- [x] データ操作
- [ ] 帳票出力
- [x] 外部連携
- [ ] バッチ処理
- [x] リアルタイム処理

## 4. 入力仕様

### 4.1 loadLib関数パラメータ

| 項目名 | 型 | 説明 | 必須 | デフォルト |
|--------|-----|------|------|-----------|
| path | string | ライブラリパス | Yes | - |
| globalSymbols | bool | グローバルシンボル公開 | No | false |

### 4.2 symAddr関数パラメータ

| 項目名 | 型 | 説明 | 必須 |
|--------|-----|------|------|
| lib | LibHandle | ライブラリハンドル | Yes |
| name | cstring | シンボル名 | Yes |

### 4.3 loadLibPattern関数パラメータ

| 項目名 | 型 | 説明 | 必須 |
|--------|-----|------|------|
| pattern | string | ライブラリ名パターン | Yes |
| globalSymbols | bool | グローバルシンボル公開 | No |

### 4.4 ライブラリ名パターン

| パターン例 | 展開結果 |
|-----------|---------|
| `lib(x|y).so` | `libx.so`, `liby.so` |
| `mylib(1|2|3).dll` | `mylib1.dll`, `mylib2.dll`, `mylib3.dll` |

### 4.5 入力チェック仕様

| チェック項目 | 内容 | エラー処理 |
|-------------|------|-----------|
| ライブラリ存在 | パスが有効なライブラリであること | nilを返す |
| シンボル存在 | シンボルが存在すること | nilを返す（checkedSymAddrは例外） |

## 5. 出力仕様

### 5.1 LibHandle型

| 型 | 説明 |
|-----|------|
| pointer | 動的にロードされたライブラリへのハンドル |

### 5.2 戻り値

| 関数 | 成功時 | 失敗時 |
|------|--------|--------|
| loadLib | LibHandle | nil |
| symAddr | pointer（シンボルアドレス） | nil |
| checkedSymAddr | pointer（シンボルアドレス） | LibraryError例外 |
| loadLibPattern | LibHandle | nil |

## 6. 処理フロー

### 6.1 ライブラリロードフロー

```mermaid
flowchart TD
    A[loadLib呼び出し] --> B{プラットフォーム判定}
    B -->|POSIX| C[dlopen]
    B -->|Windows| D[LoadLibraryA]
    B -->|Nintendo Switch| E[OSError例外]
    B -->|Genode| F[OSError例外]
    C --> G{globalSymbols?}
    G -->|Yes| H[RTLD_NOW or RTLD_GLOBAL]
    G -->|No| I[RTLD_NOW]
    H --> J[LibHandle返却]
    I --> J
    D --> J
    E --> K[例外処理]
    F --> K
```

### 6.2 シンボルアドレス取得フロー

```mermaid
flowchart TD
    A[symAddr/checkedSymAddr呼び出し] --> B{プラットフォーム判定}
    B -->|POSIX| C[dlsym]
    B -->|Windows| D[GetProcAddress]
    C --> E{成功?}
    D --> E
    E -->|Yes| F[pointer返却]
    E -->|No| G{checkedSymAddr?}
    G -->|Yes| H[LibraryError発生]
    G -->|No| I[nil返却]
```

### 6.3 パターンマッチロードフロー

```mermaid
flowchart TD
    A[loadLibPattern呼び出し] --> B[libCandidates呼び出し]
    B --> C{パターンに括弧あり?}
    C -->|Yes| D[展開して再帰呼び出し]
    C -->|No| E[候補リストに追加]
    D --> C
    E --> F[候補ループ]
    F --> G[loadLib試行]
    G --> H{成功?}
    H -->|Yes| I[LibHandle返却]
    H -->|No| J{次の候補あり?}
    J -->|Yes| F
    J -->|No| K[nil返却]
```

## 7. ビジネスルール

### 7.1 プラットフォーム別実装

| プラットフォーム | 使用API |
|-----------------|---------|
| POSIX（Linux, macOS, BSD等） | dlopen, dlclose, dlsym |
| Windows | LoadLibraryA, FreeLibrary, GetProcAddress |
| Nintendo Switch | 非対応（OSError発生） |
| Genode（POSIXなし） | 非対応（OSError発生） |

### 7.2 globalSymbolsオプション

| 値 | POSIX動作 | Windows動作 |
|----|-----------|-------------|
| false | RTLD_NOW | 無視される |
| true | RTLD_NOW or RTLD_GLOBAL | 無視される |

### 7.3 loadLib()（引数なし）

| プラットフォーム | 動作 |
|-----------------|------|
| POSIX | 現在の実行ファイルのハンドルを取得 |
| Windows | nilでLoadLibraryAを呼び出し |

## 8. データベース操作仕様

該当なし（動的ライブラリ操作のみ）

## 9. エラー処理

### 9.1 エラー一覧

| エラー条件 | 発生関数 | 処理 |
|-----------|---------|------|
| ライブラリロード失敗 | loadLib | nilを返す |
| シンボル未検出 | symAddr | nilを返す |
| シンボル未検出 | checkedSymAddr | LibraryError発生 |
| プラットフォーム非対応 | 全関数 | OSError発生 |

### 9.2 例外型

| 例外 | 説明 |
|------|------|
| LibraryError | シンボルが見つからない場合 |
| OSError | プラットフォーム非対応の場合 |

## 10. トランザクション仕様

該当なし（ライブラリ操作はトランザクション対象外）

## 11. パフォーマンス要件

| 項目 | 特性 |
|------|------|
| ライブラリロード | ディスクI/O + シンボル解決のオーバーヘッド |
| シンボル取得 | ハッシュテーブル参照程度の軽量処理 |
| アンロード | 参照カウントがゼロになるまで遅延される場合あり |

## 12. セキュリティ考慮事項

### 12.1 ライブラリロード

| リスク | 対策 |
|--------|------|
| DLLハイジャック | 絶対パスでライブラリを指定 |
| 悪意のあるライブラリ | 信頼できるパスからのみロード |
| シンボル衝突 | globalSymbols=falseを使用 |

### 12.2 コード実行

| リスク | 対策 |
|--------|------|
| 任意コード実行 | ロードするライブラリを検証 |
| 署名検証 | OSレベルのコード署名機能を使用 |

## 13. コードリーディングガイド

### 13.1 関連ファイル一覧

| ファイルパス | 概要 | 行数 |
|-------------|------|------|
| lib/pure/dynlib.nim | 動的ライブラリモジュール | 197行 |

### 13.2 プログラム呼び出し階層

```
dynlib.nim
├── LibHandle型定義 (49行)
├── loadLib (51-57行, 111-118行, 186-189行)
│   ├── [POSIX] dlopen (116行, 118行)
│   └── [Windows] LoadLibraryA (187行, 189行)
├── unloadLib (59-60行, 119行, 190行)
│   ├── [POSIX] dlclose (119行)
│   └── [Windows] FreeLibrary (190行)
├── symAddr (66-68行, 120行, 192-193行)
│   ├── [POSIX] dlsym (120行)
│   └── [Windows] GetProcAddress (193行)
├── checkedSymAddr (70-74行)
│   └── symAddr → raiseInvalidLibrary
├── raiseInvalidLibrary (62-64行)
├── libCandidates (76-86行)
│   └── 再帰的パターン展開
└── loadLibPattern (88-98行)
    └── libCandidates → loadLib
```

### 13.3 データフロー図

```
[ライブラリパス/パターン]
       │
       ▼
┌─────────────────────────────────────────┐
│ loadLib / loadLibPattern                 │
│  ├─ [POSIX] dlopen                       │
│  │   ├─ RTLD_NOW                         │
│  │   └─ RTLD_GLOBAL（オプション）          │
│  └─ [Windows] LoadLibraryA               │
└─────────────────────────────────────────┘
       │
       ▼
   [LibHandle]
       │
       ▼
┌─────────────────────────────────────────┐
│ symAddr / checkedSymAddr                 │
│  ├─ [POSIX] dlsym                        │
│  └─ [Windows] GetProcAddress             │
└─────────────────────────────────────────┘
       │
       ▼
   [関数ポインタ]
       │
       ▼
   cast[ProcType]
       │
       ▼
   [関数呼び出し]
       │
       ▼
   unloadLib
   (リソース解放)
```

### 13.4 読み解くべき重要なコード

1. **LibHandle型定義（49行）**: ポインタとしてのハンドル型
2. **POSIX実装（100-120行）**: dlopen/dlclose/dlsymの使用
3. **Windows実装（170-193行）**: LoadLibraryA/FreeLibrary/GetProcAddress
4. **libCandidates（76-86行）**: パターン展開の再帰ロジック
5. **checkedSymAddr（70-74行）**: 例外を発生させるシンボル取得

### 13.5 推奨リーディング順序

1. LibHandle型定義（49行）- 基本データ型
2. loadLib/unloadLib宣言（51-60行）- インターフェース理解
3. POSIX実装（100-120行）- dlfcn APIの使用方法
4. Windows実装（170-193行）- Windows APIの使用方法
5. libCandidates + loadLibPattern（76-98行）- パターンマッチング
6. 使用例（25-43行）- 実際の使い方

### 13.6 使用例

```nim
type
  GreetFunction = proc (): cstring {.gcsafe, stdcall.}

proc loadGreet(lang: string) =
  let lib =
    case lang
    of "french": loadLib("french.dll")
    else: loadLib("english.dll")
  assert lib != nil, "Error loading library"

  let greet = cast[GreetFunction](lib.symAddr("greet"))
  assert greet != nil, "Error loading 'greet' function"

  echo greet()

  unloadLib(lib)
```
