我可以用错误的签名调用用dlsym()导入的函数,为什么
I can call a function imported with dlsym() with a wrong signature, why?
host.cpp具有:
int main (void)
{
void * th = dlopen("./p1.so", RTLD_LAZY);
void * fu = dlsym(th, "fu");
((void(*)(int, const char*)) fu)(2, "rofl");
return 0;
}
p1.cpp有:
#include <iostream>
extern "C" bool fu (float * lol)
{
std::cout << "fuuuuuuuu!!!n";
return true;
}
(我故意留下错误检查)
当执行host时,"fuuuuuuuu!!"会正确打印,即使我用完全不同的函数签名将void指针键入到符号。
为什么会发生这种情况?不同编译器之间的这种行为是否一致?
发生这种情况是因为UB,而且这种行为与任何事情都不一致,无论出于何种原因。
因为在void指针中没有关于函数签名的信息。或者除了地址之外的任何信息。如果你开始使用参数,你可能会遇到麻烦。
这实际上不是一个创建失败案例的好例子,因为:
- 您从不使用函数
fu
中的参数 - 函数
fu
的参数(或者激活帧本身的内存占用空间较小)比您要转换的函数指针类型少,因此您永远不会出现fu
试图访问调用方设置的激活记录之外的内存的情况
最终,你所做的仍然是未定义的行为,但你没有做任何事情来创建可能导致问题的违规行为,因此它最终会成为一个无声的错误。
不同编译器之间的这种行为是否一致?
否。如果您的平台/编译器使用了一个调用约定,该约定要求被调用者清理堆栈,那么oops,如果被调用者和调用者期望的激活记录大小不匹配,您很可能会感到沮丧。。。当被调用者返回时,堆栈指针将被移动到错误的位置,这可能会损坏堆栈,并完全扰乱任何堆栈指针的相对寻址。
刚刚发生了,
C
使用cdecl
调用转换(因此调用者清除堆栈)- 函数不使用给定的实参实参
因此您的呼叫似乎工作正常。
但实际上行为是不明确的。更改签名或使用参数将导致程序崩溃:
添加:
例如,考虑stdcall
调用转换,其中被调用者桅杆清除堆栈。在这种情况下,即使你为调用者和被调用者声明了正确的调用转换,你的程序仍然会崩溃,因为你的堆栈会被破坏,因为被调用者会根据它的签名清除它,但调用者会根据另一个签名填充:
#include <iostream>
#include <string>
extern "C" __attribute__((stdcall)) __attribute__((noinline)) bool fu (float * lol)
{
std::cout << "fuuuuuuuu!!!n";
return true;
}
void x()
{
(( __attribute__((stdcall)) void(*)(int, const char*)) fu)(2, "rofl");
}
int main (void)
{
void * th = reinterpret_cast<void*>(&fu);
std::string s = "hello";
x();
std::cout << s;
return 0;
}
相关文章:
- 是否可以将函数导入命名空间,但不能导出它?
- 使用导入的函数从嵌入式v8调用webassembly
- C ++ DLL导入:函数调用返回错误的结果
- 编译器如何知道 dll 导入的函数?
- 导入的库函数是否可以在内存中移动
- 在C++中导入 DLL 函数
- 我将类型库(.tlb)导入到Delphi中,但函数参数似乎不正确.我应该如何解决它
- 使用dllimport属性导入的函数参数
- 除了两个构造函数外,C++库导入也可以工作
- 当函数通过.def文件导出时,如何使用dllImport导入函数
- 当在导入函数之前添加__declspec(dllexport)时,它会做什么
- 从我的 DLL 导入 C++ 函数不会导出库
- 从库导入函数./ DLL 文件
- 导入函数 Matlab 编码器和C++可执行文件
- 从DLL C++导入函数时出现问题.LNK 2001
- 如何阻止GNU GCC破坏dll导入函数名
- 从C++dll导入函数时出现问题,错误LNK 2019
- 从不同的C文件导入函数
- 如何通过Boost从python文件导入函数.蟒蛇
- 访问冲突调用导入函数