< 返回版块

jellybobbin 发表于 2024-06-28 11:21

Tags:ffi dylib c

在调用一个第三方动态库 xxx.so 时,分别用 c++rust 实现了一次,但发现它们的结果或者说行为表现不一致,调用 xxx.so 中的外部函数,这个函数除了参数以外,关键在于一个回调函数,而这个回调函数的参数是一个指针,对这个指针读取数据时,发现 rustc++ 获取结果不一样,话不多说:

C++ :

struct APP_HEAD
{
    int	nHeadSize;
    int	nItemCount;
    int	nItemSize;
};

struct DATA_MSG
{
    unsigned short  	    sFlags;
    int  	            nDataType;
    int			    nDataLen;
    int			    nServerTime;
    int     		    nOrder;
    int                     nConnectId;
    APP_HEAD*               pAppHead;
    void*                   pData;
};

// 回调函数
void RecvData(DATA_MSG* data)
{
    printf("\nflags:%d type:%d len:%d time:%d order:%d conId:%d \n",
        data->sFlags,
        data->nDataType, 
        data->nDataLen,
        data->nServerTime,
        data->nOrder,
        data->nConnectId);
}
  • 以上 printf 会输出:
flags:24577 type:-90 len:400 time:22908321 order:0 conId:0

Rust

#[repr(C)]
pub struct APP_HEAD {
    pub nHeadSize: i32,
    pub nItemCount: i32,
    pub nItemSize: i32,
}

#[repr(C)]
pub struct DATA_MSG {
    pub sFlags: u16,
    pub nDataType: i32,
    pub nDataLen: i32,
    pub nServerTime: i32,
    pub nOrder: i32,
    pub nConnectId: i32,
    pub pAppHead: *mut APP_HEAD,
    pub pData: *mut c_void,
}

// 回调函数
extern "C" fn recv_data(data_msg: *mut DATA_MSG)
{
    let data: &DATA_MSG = unsafe { &*data_msg };

    eprintln!("nflags:{} type:{} len:{} time:{} order:{} conId:{}", 
        data.sFlags, 
        data.nDataType, 
        data.nDataLen,
        data.nServerTime,
        data.nOrder,
        data.nConnectId);
}
  • 以上 eprintln! 会输出:
flags:24577 type:26279935 len:1889665024 time:1582 order:0 conId:1701838848

可以从输出结果中看到 rustc++ 的差异,除了第一个字段 flags 的结果相同外,其它字段不同(rust 结果是错的)

修改类型

  • rust 中的第二个字段 nDataType 类型改为 i16 输出结果:
flags:24577 type:-90 len:26279935 time:1514143744 order:1683 conId0

flagstype 都是 c++ 的相同了;但后面几个字段经过多次测试修改成其它类型,最终结果都和 c++ 不一样

问题讨论

带着疑问的心情,首先想到的时内存对齐,所以后续尝试了 #[repr(align(4))]#[repr(align(8))],未能如愿; 其次想着是不是 xxx.so 是32位的跟我运行环境不匹配,被否定;最后通过 objdum 工具,得知 xxx.so 是由 c++ 编译而来,会不会跟此有关系呢?

复盘(2024-07-02)

测试帐号有问题,后续补齐所有资料,如对齐方式、布局及偏移、hex序列等

首先感谢各路朋友的回复,问题最终在昨天上午解决了,因为后续一直在忙其它事情,所以就没来得及在这里写内容,这会儿才稍空。上面说的补齐的东西其实也不需要了,因为根本原因就是内存对齐的问题,第三方的 so 里面使用了 packed 的方式,虽然在群里经过群友们的讨论和帮助(尤其是 @Kevin Wang)后,我在 rust 这边也使用一致的方式: #[repr(packed)], 但是我在开始时犯了一个错误,就是在 struct 嵌套时,没有对所有外层的 struct#[repr(packed)],导致结果不是预期结果。另外就是现在调用so的函数比较少,暂时没有出现问题,但居 so 提供者说他们是不兼容 C 的,所以在后面调用更多的函数时,会不会出现问题目前还不好说,就此埋个点,到时候有问题也会贴上来;共勉。再次真诚感谢大家!

评论区

写评论
作者 jellybobbin 2024-07-02 14:27

c++ 代码是Ok的,是rust这边内存布局和so不一致。

--
👇
araraloren: 最明显的问题就是你的c++代码越界了

araraloren 2024-06-28 22:57

最明显的问题就是你的c++代码越界了

作者 jellybobbin 2024-06-28 21:09

今天第三方测试帐号出问题了,到时候我把结果贴在这里,感觉大家的回复。

zylthinking 2024-06-28 13:37

另外你打印一下 data_msg 地址, 别是它自己的地址没有满足对齐要求

zylthinking 2024-06-28 13:35

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e2d08a582e9227bbd42f933a63235107

我试了一下, rust 偏移符合预期; 我估计是 C++ 的你贴出的并不是实际定义

--
👇
zylthinking: 还是将内存 dump 出来看看吧 我比较了下, 发现 -90 无论如何也解释不成 26279935;

因此要么是 repr(C) 出 bug 了, 要么就是你贴出的定义不是真实定义

另外也可以将各个字段 offset 的值都分别打印一下, 应该能看出 rust 中的各个字段偏移是不是预期的

zylthinking 2024-06-28 13:26

还是将内存 dump 出来看看吧 我比较了下, 发现 -90 无论如何也解释不成 26279935;

因此要么是 repr(C) 出 bug 了, 要么就是你贴出的定义不是真实定义

另外也可以将各个字段 offset 的值都分别打印一下, 应该能看出 rust 中的各个字段偏移是不是预期的

Bai-Jinlin 2024-06-28 12:57

cpp不等于c,cpp的abi和c不一样

1 共 7 条评论, 1 页