# 機能設計書 140-シンボル検索

## 概要

uv_dlsym()により動的ライブラリからシンボル（関数やデータのアドレス）を検索する機能の設計書である。

### 本機能の処理概要

本機能は、uv_dlopen()で読み込み済みの動的ライブラリから、指定された名前のシンボル（関数ポインタやグローバル変数のアドレス）を検索・取得するためのクロスプラットフォームAPIを提供する。

**業務上の目的・背景**：プラグインアーキテクチャにおいて、動的に読み込んだライブラリから特定の関数やデータにアクセスするためには、シンボル名によるアドレス解決が必要である。POSIXのdlsym()とWindowsのGetProcAddress()は引数の型やエラー処理が異なるため、libuvが統一的なインターフェースを提供する。特にWindows版では、関数ポインタをvoid*にキャストする際のpedantic warning抑制のためuintptr_tを経由するトリックが使用されている。

**機能の利用シーン**：プラグインのエントリーポイント関数の取得、動的に読み込んだライブラリのAPI関数へのアクセス、オプショナルなOS APIの動的解決（libuv内部でのSetThreadDescriptionの解決など）に利用される。

**主要な処理内容**：
1. uv_dlsym()によるシンボル名からアドレスへの解決
2. エラーメッセージの管理（lib->errmsgへの格納）

**関連システム・外部連携**：Unix系ではdlsym()、WindowsではGetProcAddress()を使用する。

**権限による制御**：特段の権限制御はない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 27 | 共有ライブラリ | 主機能 | uv_dlsym()による動的ライブラリからのシンボル検索のAPI定義 |

## 機能種別

ユーティリティ操作（シンボル解決）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| lib | uv_lib_t* | Yes | uv_dlopen()で取得したライブラリハンドル | NULLの場合は未定義動作 |
| name | const char* | Yes | 検索するシンボル名（NUL終端） | NULLの場合は未定義動作 |
| ptr | void** | Yes | シンボルアドレスの格納先 | NULLの場合は未定義動作 |

### 入力データソース

呼び出し元アプリケーションからの直接指定。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | int | 成功時0、失敗時-1 |
| *ptr | void* | シンボルのアドレス（成功時） |
| lib->errmsg | char* | 失敗時のエラーメッセージ |

### 出力先

呼び出し元への戻り値およびptrへのアドレス書き込み、uv_lib_t構造体のerrmsg更新。

## 処理フロー

### 処理シーケンス

```
uv_dlsym():
1. [Unix]
   a. dlerror()でエラーステータスをリセット
   b. *ptr = dlsym(lib->handle, name)
   c. *ptrがNULL → uv__dlerror()でエラーメッセージ取得、-1返却
   d. *ptrが非NULL → 0返却
2. [Windows]
   a. *ptr = (void*)(uintptr_t) GetProcAddress(lib->handle, name)
   b. *ptrがNULL → uv__dlerror(lib, "", GetLastError())で-1返却
   c. *ptrが非NULL → uv__dlerror(lib, "", 0)で0返却
```

### フローチャート

```mermaid
flowchart TD
    A[uv_dlsym呼び出し] --> B{プラットフォーム}
    B -->|Unix| C[dlerror リセット]
    C --> D[dlsym]
    D --> E{*ptr != NULL?}
    E -->|Yes| F[0返却]
    E -->|No| G[uv__dlerror: エラー設定]
    G --> H[-1返却]
    B -->|Windows| I[GetProcAddress]
    I --> J{*ptr != NULL?}
    J -->|Yes| K[uv__dlerror 0: エラークリア]
    K --> F
    J -->|No| L[uv__dlerror GetLastError]
    L --> H
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-140-01 | NULLシンボル | シンボルが見つからない場合、*ptrにはNULLが設定されることもあるが、NULLは必ずしもエラーではない（Unix dlsymの仕様） | Unix |
| BR-140-02 | 型キャスト | Windows版ではGetProcAddressの戻り値（FARPROC）をuintptr_t経由でvoid*にキャストし、pedantic warningを抑制 | Windows |
| BR-140-03 | エラークリア | Windows版では成功時もuv__dlerror(errorno=0)を呼び、前回のエラーメッセージをクリアする | Windows |
| BR-140-04 | 戻り値形式 | 失敗時は-1を返す（libuvの他APIとは異なるエラー規約） | 常時 |

### 計算ロジック

特になし。

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| -1 (戻り値) | シンボル未発見 | 指定名のシンボルがライブラリに存在しない | uv_dlerror()でメッセージ確認、シンボル名を修正 |

### リトライ仕様

リトライは不要。シンボル名が正しいかを確認する。

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

該当なし。

## パフォーマンス要件

dlsym/GetProcAddressはシンボルテーブルのハッシュ検索であり、一般的に高速。頻繁に呼び出す場合は結果をキャッシュすることを推奨。

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

取得した関数ポインタの呼び出しは任意コード実行につながるため、信頼できるライブラリからのみシンボルを取得すべきである。

## 備考

- uv_dlsym()の戻り値はuv_dlopen()と同様に失敗時-1であり、libuvの一般的なAPIとは異なるエラーコード規約を持つ。エラー詳細はuv_dlerror()で取得する。
- Unix版ではdlsymがNULLを返してもdlerrorがNULLの場合、シンボルの値自体がNULLである可能性がある。libuvはこのケースをエラーとして扱うことに注意。
- Windows版でのuintptr_tを経由するキャストは、FARPROC（関数ポインタ）→void*のキャストがC標準では未定義動作であることを回避するためのワークアラウンド。

---

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

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | unix.h | `include/uv/unix.h` | uv_lib_tの構造体定義（L212-215） |
| 1-2 | win.h | `include/uv/win.h` | uv_lib_tの構造体定義（L314-317） |
| 1-3 | uv.h | `include/uv.h` | API宣言（L1844） |

**読解のコツ**: uv_dlsym()はuv_lib_tのhandleメンバーを使用してシンボル検索を行う。エラーメッセージはerrmsgメンバーに格納される。

#### Step 2: Unix版実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | dl.c (Unix) | `src/unix/dl.c` | uv_dlsym()（L53-57） |
| 2-2 | dl.c (Unix) | `src/unix/dl.c` | uv__dlerror()（L65-80） |

**主要処理フロー**:
- **L53-57 (dlsym)**: dlerror()リセット → dlsym(lib->handle, name) → NULLならuv__dlerror
- **L65-80 (uv__dlerror)**: 前回のerrmsgをfree → dlerror()でメッセージ取得 → uv__strdup()で複製

#### Step 3: Windows版実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | dl.c (Win) | `src/win/dl.c` | uv_dlsym()（L65-69） |
| 3-2 | dl.c (Win) | `src/win/dl.c` | uv__dlerror()（L92-135） |

**主要処理フロー**:
- **L65-69 (dlsym)**: GetProcAddress → uintptr_t経由でvoid*キャスト → uv__dlerrorでエラー判定（成功時errorno=0、失敗時GetLastError()）
- **L92-135 (uv__dlerror)**: errorno==0なら前回エラークリアして0返却、それ以外はFormatMessageAでエラーメッセージ設定して-1返却

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

```
uv_dlsym(lib, name, ptr)
    |
    +-- [Unix]
    |       +-- dlerror()  // リセット
    |       +-- *ptr = dlsym(lib->handle, name)
    |       +-- [*ptr == NULL] uv__dlerror(lib)
    |               +-- dlerror() → uv__strdup() → lib->errmsg
    |
    +-- [Windows]
            +-- *ptr = (void*)(uintptr_t) GetProcAddress(lib->handle, name)
            +-- uv__dlerror(lib, "", *ptr ? 0 : GetLastError())
                    +-- [errorno==0] lib->errmsg クリア → return 0
                    +-- [errorno!=0] FormatMessageA → lib->errmsg → return -1
```

### データフロー図

```
[入力]                         [処理]                    [出力]

lib->handle (void*/HMODULE) --> dlsym() /             --> *ptr（シンボルアドレス）
name (const char*)              GetProcAddress()           lib->errmsg（失敗時）
ptr (void**)                                               戻り値: 0 / -1
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダ | API宣言（L1844） |
| unix.h | `include/uv/unix.h` | ヘッダ | uv_lib_t型定義（L212-215） |
| win.h | `include/uv/win.h` | ヘッダ | uv_lib_t型定義（L314-317） |
| dl.c (Unix) | `src/unix/dl.c` | ソース | Unix版dlsym実装（L53-57）、uv__dlerror（L65-80） |
| dl.c (Win) | `src/win/dl.c` | ソース | Windows版dlsym実装（L65-69）、uv__dlerror（L92-135） |
