【问题标题】:Manually calling a function from VTable using pointers creates segmentation fault [closed]使用指针从 VTable 手动调用函数会产生分段错误 [关闭]
【发布时间】:2021-06-11 16:34:54
【问题描述】:

当手动调用second() 函数时,我得到一个分段错误。我认为问题在于,在使用int (*pfunSecond)(int) = (int (*)(int)) *(((void***)pb)[0] + 1); 中的+1 向前移动地址时,程序丢失了处理first() 函数在构造D 类的内存中的位置的信息。到目前为止我还没有修复它。

#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;
}

【问题讨论】:

  • 这里有一个修复 - 只需通过 pb-&gt;second() 致电 second。无论您在这里尝试做什么,看起来很大程度上取决于您的特定编译器的实现细节。除非你真的想深入研究(汇编代码等),否则不要这样做。
  • 您通过尝试访问 v-table 导致未定义行为,这是一个实现细节,在标准 C++ 中无法访问。这个问题没有正确或安全的答案,但如果您想快速解释您观察到的行为,您应该提及您的编译器、它的版本以及您的运行方式。

标签: c++ segmentation-fault virtual-functions


【解决方案1】:

不用仔细观察,您就是通过非成员函数指针调用这些成员函数(虚函数)。使用垃圾 this 指针调用函数。

【讨论】:

    【解决方案2】:

    你的问题是:

    1. 您对 vtable 所做的一切都是未定义的行为。但我想你知道这一点,并且愿意接受。

    我假设你的虚函数表布局是正确的;缺少特定的编译器我别无选择。一元 * 似乎有问题,但我不知道 vtable 在您的系统上是如何布局的。

    1. 您在假成员函数指针上缺少隐式 this 指针。

    2. 你放弃了调用约定。

    所以:

     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);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-26
      • 1970-01-01
      • 1970-01-01
      • 2018-05-09
      相关资源
      最近更新 更多