在调用一个第三方动态库 xxx.so
时,分别用 c++
和 rust
实现了一次,但发现它们的结果或者说行为表现不一致,调用 xxx.so
中的外部函数,这个函数除了参数以外,关键在于一个回调函数,而这个回调函数的参数是一个指针,对这个指针读取数据时,发现 rust
和 c++
获取结果不一样,话不多说:
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
可以从输出结果中看到
rust
和c++
的差异,除了第一个字段flags
的结果相同外,其它字段不同(rust 结果是错的)
修改类型
- 把
rust
中的第二个字段nDataType
类型改为i16
输出结果:
flags:24577 type:-90 len:26279935 time:1514143744 order:1683 conId0
flags
和type
都是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
的,所以在后面调用更多的函数时,会不会出现问题目前还不好说,就此埋个点,到时候有问题也会贴上来;共勉。再次真诚感谢大家!
评论区
写评论c++ 代码是Ok的,是rust这边内存布局和so不一致。
--
👇
araraloren: 最明显的问题就是你的c++代码越界了
最明显的问题就是你的c++代码越界了
今天第三方测试帐号出问题了,到时候我把结果贴在这里,感觉大家的回复。
另外你打印一下 data_msg 地址, 别是它自己的地址没有满足对齐要求
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e2d08a582e9227bbd42f933a63235107
我试了一下, rust 偏移符合预期; 我估计是 C++ 的你贴出的并不是实际定义
--
👇
zylthinking: 还是将内存 dump 出来看看吧 我比较了下, 发现 -90 无论如何也解释不成 26279935;
因此要么是 repr(C) 出 bug 了, 要么就是你贴出的定义不是真实定义
另外也可以将各个字段 offset 的值都分别打印一下, 应该能看出 rust 中的各个字段偏移是不是预期的
还是将内存 dump 出来看看吧 我比较了下, 发现 -90 无论如何也解释不成 26279935;
因此要么是 repr(C) 出 bug 了, 要么就是你贴出的定义不是真实定义
另外也可以将各个字段 offset 的值都分别打印一下, 应该能看出 rust 中的各个字段偏移是不是预期的
cpp不等于c,cpp的abi和c不一样