在 Rust 中,可以通過手動控制導出來實現僅通過索引(序數)導出 DLL 函數的功能。以下是具體方法和完整步驟:
解決方案
通過結合 .def
文件(模塊定義文件)和 MSVC 鏈接器參數來實現函數名隱藏,只暴露序數編號。
具體步驟
1. 創建 Rust 動態庫項目
在 Cargo.toml
中配置 cdylib
類型:
[lib]
crate-type = ["cdylib"]
2. 編寫 Rust 函數
使用 #[no_mangle]
和 extern "C"
定義導出函數:
// src/lib.rs
#[no_mangle]
pub extern "C" fn secret_function1() -> i32 {42
}#[no_mangle]
pub extern "C" fn secret_function2(x: i32) -> i32 {x * 2
}
3. 創建模塊定義文件(.def
)
創建 exports.def
文件,用 NONAME
隱藏函數名并分配序數:
EXPORTS; 語法: 函數名 @序數 NONAMEsecret_function1 @1 NONAMEsecret_function2 @2 NONAME
4. 設置編譯鏈接參數
修改 .cargo/config.toml
,添加 MSVC 鏈接器標志:
# .cargo/config.toml
[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "link-args=/DEF:exports.def /EXPORT:NONE"]
注意:路徑需根據項目結構調整(也可用絕對路徑)
5. 編譯項目
cargo build --release
生成的 target/release/your_lib.dll
將隱藏函數名。
驗證導出結果
使用 dumpbin
工具檢查導出表(確保 VS Developer Command Prompt 中運行):
dumpbin /EXPORTS target/release/your_lib.dll
輸出應類似:
ordinal hint RVA name1 0 00001000 [NONAME]2 1 00001010 [NONAME]
從調用方通過索引加載
在 C/C++ 中通過序數加載函數(示例):
#include <windows.h>
#include <stdio.h>typedef int (*Func1)();
typedef int (*Func2)(int);int main() {HINSTANCE hDll = LoadLibraryA("your_lib.dll");if (!hDll) return 1;// 通過序數1加載第一個函數Func1 f1 = (Func1)GetProcAddress(hDll, (LPCSTR)1);// 通過序數2加載第二個函數Func2 f2 = (Func2)GetProcAddress(hDll, (LPCSTR)2);printf("f1: %d\n", f1()); // 輸出 42printf("f2: %d\n", f2(10)); // 輸出 20FreeLibrary(hDll);return 0;
}
關鍵點說明
-
NONAME
關鍵字
在.def
文件中強制使用序數導出,隱藏函數名稱。 -
/EXPORT:NONE
禁止 Rust 的默認名稱導出規則,確保只有.def
中的定義生效。 -
序數分配
序數必須唯一且從 1 開始(序數 0 保留)。 -
工具鏈限制
此方案僅適用于 MSVC 工具鏈(x86_64-pc-windows-msvc
)。如需 GNU 工具鏈,需改用dlltool
(流程較復雜)。
替代方案
如果需要在 GNU 工具鏈(如 x86_64-pc-windows-gnu
)實現:
- 使用
dlltool
生成.a
導入庫 - 手動控制
.def
和dlltool
參數
但 GNU 方案較為復雜,推薦優先使用 MSVC 鏈。
通過以上步驟,可在 Rust 中編譯出僅通過索引導出的 DLL 文件,有效隱藏內部符號名稱。