使用指针从 VTable 手动调用函数会产生分段错误
Manually calling a function from VTable using pointers creates segmentation fault
手动调用 second()
函数时出现分段错误。我认为问题在于,程序在使用 [=14= 向前移动地址时丢失了处理 first()
函数在构造的 D
class 内存中的位置的信息] 在 int (*pfunSecond)(int) = (int (*)(int)) *(((void***)pb)[0] + 1);
。到目前为止我还没有运气修复它。
#include <iostream>
class B{
public:
virtual int __cdecl first()=0;
virtual int __cdecl second(int)=0;
};
class D: public B{
public:
virtual int __cdecl first(){return 42;}
virtual int __cdecl second(int x){return first()+x;}
};
void myFunc(B* pb){
int (*pfunFirst)() = (int (*)()) *(((void***)pb)[0]);
int (*pfunSecond)(int) = (int (*)(int)) *(((void***)pb)[0] + 1);
std::cout << "Call first(): " << pfunFirst() << std::endl;
std::cout << "Call second(): " << pfunSecond(20) << std::endl;
}
int main(void){
myFunc(new D());
return 0;
}
无需仔细观察,您就是通过非成员函数指针调用这些成员函数(虚拟)。正在使用垃圾 this 指针调用这些函数。
您的问题是:
- 你对 vtable 所做的一切都是未定义的行为。但我想你知道这一点,并且愿意接受它。
我假设您对虚函数的布局 table 是正确的;缺少特定的编译器我别无选择。一元 *
看起来很奇怪,但我不知道 vtables 在你的系统上是如何布局的。
您在伪成员函数指针上缺少隐式 this 指针。
您放弃了调用约定。
所以:
int (__cdecl *pfunFirst)(B*) = (int (__cdecl *)(B*)) *(((void***)pb)[0]);
int (__cdecl *pfunSecond)(B*,int) = (int (__cdecl *)(B*,int)) *(((void***)pb)[0] + 1);
然后pfunFirst(b)
.
但我猜你的 vtable 布局实际上类似于
using cdecl_entry=void(__cdecl*)(void*);
struct vtable_t{
cdecl_entry functions[1];
};
struct has_vtable{
vtable_t const* vtable;
};
template<class R,class...Args>using cdecl_func=R(__cdecl *)(Args...);
template<class Base, class R, class...Args>
cdecl_func<R,Base*,Args...> get_vtable_entry(Base const* b, std::size_t n){
return reinterpret_cast<cdecl_func<R,Base*,Args...>(reinterpret_cast<has_vtable const*>(b)->vtable->functions[n]);
}
这仍然是 UB,但至少不包含 void***
s
auto pfunFirst = get_vtable_entry<Base, int>(pb,0);
手动调用 second()
函数时出现分段错误。我认为问题在于,程序在使用 [=14= 向前移动地址时丢失了处理 first()
函数在构造的 D
class 内存中的位置的信息] 在 int (*pfunSecond)(int) = (int (*)(int)) *(((void***)pb)[0] + 1);
。到目前为止我还没有运气修复它。
#include <iostream>
class B{
public:
virtual int __cdecl first()=0;
virtual int __cdecl second(int)=0;
};
class D: public B{
public:
virtual int __cdecl first(){return 42;}
virtual int __cdecl second(int x){return first()+x;}
};
void myFunc(B* pb){
int (*pfunFirst)() = (int (*)()) *(((void***)pb)[0]);
int (*pfunSecond)(int) = (int (*)(int)) *(((void***)pb)[0] + 1);
std::cout << "Call first(): " << pfunFirst() << std::endl;
std::cout << "Call second(): " << pfunSecond(20) << std::endl;
}
int main(void){
myFunc(new D());
return 0;
}
无需仔细观察,您就是通过非成员函数指针调用这些成员函数(虚拟)。正在使用垃圾 this 指针调用这些函数。
您的问题是:
- 你对 vtable 所做的一切都是未定义的行为。但我想你知道这一点,并且愿意接受它。
我假设您对虚函数的布局 table 是正确的;缺少特定的编译器我别无选择。一元 *
看起来很奇怪,但我不知道 vtables 在你的系统上是如何布局的。
您在伪成员函数指针上缺少隐式 this 指针。
您放弃了调用约定。
所以:
int (__cdecl *pfunFirst)(B*) = (int (__cdecl *)(B*)) *(((void***)pb)[0]);
int (__cdecl *pfunSecond)(B*,int) = (int (__cdecl *)(B*,int)) *(((void***)pb)[0] + 1);
然后pfunFirst(b)
.
但我猜你的 vtable 布局实际上类似于
using cdecl_entry=void(__cdecl*)(void*);
struct vtable_t{
cdecl_entry functions[1];
};
struct has_vtable{
vtable_t const* vtable;
};
template<class R,class...Args>using cdecl_func=R(__cdecl *)(Args...);
template<class Base, class R, class...Args>
cdecl_func<R,Base*,Args...> get_vtable_entry(Base const* b, std::size_t n){
return reinterpret_cast<cdecl_func<R,Base*,Args...>(reinterpret_cast<has_vtable const*>(b)->vtable->functions[n]);
}
这仍然是 UB,但至少不包含 void***
s
auto pfunFirst = get_vtable_entry<Base, int>(pb,0);