Rust 静态调用 C++

使用 cmake 的局限性

.lib 合并问题

cmake 不会将多个 .lib 合并, 因此可能需要使用 add_custom_command 命令手动使用 MSVC 工具 lib.exe 来创建最终具有 C ABI 的 .lib 静态库文件供Rust调用.

set(Target "output")
add_library("${Target}" STATIC lib.cpp)

target_include_directories("${App}" PUBLIC "${CMAKE_HOME_DIRECTORY}/src")
target_link_libraries("${App}" Win32Helper)

很遗憾, output.lib 中对另一个静态库Win32Helper的调用是未寻址的! 可以使用cygwin工具 nm 来查看符号:
U表示“未定义”——对象有对符号的引用但没有定义
T表示在文本段中全局定义——对象定义并导出符号

nm output.lib | grep win32_init
                 U win32_init

其它的解决办法:

  1. 合并代码, 不拆分成多个库的形式
  2. 尝试在Rust中将多个.lib都进行链接 (繁琐, 实验后未成功)
set(Target "output")
file(GLOB_RECURSE files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" ../Win32Helper/*.c)
add_library("${Target}" STATIC lib.cpp ${files})

Win32 API 符号链接问题

由于 cmake 导出 .lib 时忽略静态库和动态库依赖, 造成 API 调用在 Rust 中形成 fatal error LNK1120 链接错误, 意味着找不到符号.

解决办法:

  1. 在C++代码中使用内联编译注释指定导入库, 微软 API 文档有说明每个 API 所在的头文件和Library等信息.
#pragma comment(lib, "User32.lib") // MessageBoxW
#pragma comment(lib, "Shell32.lib") // Shell_NotifyIconW 
  1. 在Rust中进行链接
#[link(name = "User32", kind = "dylib")]
#[link(name = "Shell32", kind = "dylib")]
#[link(name = "Z:\Github\Win32Helper\build\src\Rust-ffi\Debug\output", kind = "static")]
extern "C" {
    pub fn test() -> c_int;
}

动态调用符号

#[link(name = "User32", kind = "dylib")]
extern "C" {
    fn GetModuleHandleA(lpModuleName: *const u8) -> *mut u8;
    fn GetProcAddress(hModule: *mut u8, lpProcName: *const u8) -> *mut u8;
}

#[no_mangle]
extern "C" fn DllMain(_: *mut u8, reason: u32, _: *mut u8) -> u32 {
    match reason {
        1 => {
            println!("连接到进程!");
            intro();
        }
        0 => {
            println!("检测到进程退出");
        }
        _ => ()
    }
    return 1;
}

fn intro() {
    unsafe {
        let module = GetModuleHandleA("Kernel32".as_ptr());
        println!("得到模块 -> {:?}.", module);
        let symbol = GetProcAddress(module, b"ExitProcess".as_ptr());
        println!("得到符号 -> {:?}.", symbol);

        type ExitProcess = unsafe extern "C" fn(u32);
        let exit_process: ExitProcess = std::mem::transmute(symbol);
        exit_process(0);
    }
}

将Rust动态库注入到node进程,提示node版本

use std::ptr::null_mut;

#[link(name = "User32", kind = "dylib")]
extern "C" {
    fn GetModuleHandleA(lpModuleName: *const u8) -> *mut u8;
    fn GetProcAddress(hModule: *mut u8, lpProcName: *const u8) -> *mut u8;
    fn MessageBoxA(hWnd: *mut u8, lpText: *const u8, lpCaption: *const u8, uType: u32) -> u32;
}

#[no_mangle]
extern "C" fn DllMain(_: *mut u8, reason: u32, _: *mut u8) -> u32 {
    match reason {
        1 => {
            println!("连接到进程!");
            intro();
        }
        0 => {
            println!("检测到进程退出");
        }
        _ => ()
    }
    return 1;
}

fn intro() {
    unsafe {
        let module = GetModuleHandleA("".as_ptr());
        println!("得到模块 -> {:?}.", module);
        let symbol = GetProcAddress(module, b"uv_version_string".as_ptr());
        println!("得到符号 -> {:?}.", symbol);

        type Func = extern "C" fn() -> *const u8;
        let uv_version: Func = std::mem::transmute(symbol);
        let version = uv_version();
        println!("得到版本 -> {:?}.", version);
        MessageBoxA(null_mut(), version, "Got version!".as_ptr(), 0);
    }
}

node将打印以下输出并弹出消息框

连接到进程!
得到模块 -> 0x0.
得到符号 -> 0x7ff6d72a1440.
得到版本 -> 0x7ff6d83a5674.
exiting...
检测到进程退出

Rust 静态调用 C++

END

原文链接: https://www.cnblogs.com/develon/p/15978530.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    Rust 静态调用 C++

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/188143

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月12日 下午2:01
下一篇 2023年2月12日 下午2:03

相关推荐