为什么C++中对象有NULL vtable指针?

问题描述 投票:0回答:1

我正在使用从源代码构建的 libTooling(git 标签:

llvmorg-16.0.6
)来搜索 AST 级别的差异。

使用我构建的产品时,出现分段错误。

通过gdb检查,发现这是来自libTooling, 并且在调用

A->getSourceRange()
(source)

时发生分段错误
if (auto *A = dyn_cast<AccessSpecDecl>(D)) {
    CharSourceRange Range(A->getSourceRange(), false);
    return std::string(
        Lexer::getSourceText(Range, AST.getSourceManager(), AST.getLangOpts()));
}

此外,gdb 显示

A
的 vtable 指针(
_vptr.Decl
)是
0x0

(gdb) p *A
$1 = {<clang::Decl> = {_vptr.Decl = 0x0, NextInContextAndBits = {Value = 0}, 
    DeclCtx = {<llvm::pointer_union_detail::PointerUnionMembers<llvm::PointerUnion<clang::DeclContext*, clang::Decl::MultipleDC*>, llvm::PointerIntPair<void*, 1, int, llvm::pointer_union_detail::PointerUnionUIntTraits<clang::DeclContext*, clang::Decl::MultipleDC*>, llvm::PointerIntPairInfo<void*, 1, llvm::pointer_union_detail::PointerUnionUIntTraits<clang::DeclContext*, clang::Decl::MultipleDC*> > >, 0, clang::DeclContext*, clang::Decl::MultipleDC*>> = {<llvm::pointer_union_detail::PointerUnionMembers<llvm::PointerUnion<clang::DeclContext*, clang::Decl::MultipleDC*>, llvm::PointerIntPair<void*, 1, int, llvm::pointer_union_detail::PointerUnionUIntTraits<clang::DeclContext*, clang::Decl::MultipleDC*>, llvm::PointerIntPairInfo<void*, 1, llvm::pointer_union_detail::PointerUnionUIntTraits<clang::DeclContext*, clang::Decl::MultipleDC*> > >, 1, clang::Decl::MultipleDC*>> = {<llvm::pointer_union_detail::PointerUnionMembers<llvm::PointerUnion<clang::DeclContext*, clang::Decl::MultipleDC*>, llvm::PointerIntPair<void*, 1, int, llvm::pointer_union_detail::PointerUnionUIntTraits<clang::DeclContext*, clang::Decl::MultipleDC*>, llvm::PointerIntPairInfo<void*, 1, llvm::pointer_union_detail::PointerUnionUIntTraits<clang::DeclContext*, clang::Decl::MultipleDC*> > >, 2>> = {Val = {Value = 0}}, <No data fields>}, <No data fields>}, <No data fields>}, Loc = {ID = 0}, DeclKind = 0, InvalidDecl = 0, HasAttrs = 0, Implicit = 0, Used = 0, Referenced = 0, 
    TopLevelDeclInObjCContainer = 0, static StatisticsEnabled = false, Access = 0, FromASTFile = 0, IdentifierNamespace = 0, CacheValidAndLinkage = 0}, ColonLoc = {ID = 0}}

我将编译器从

/usr/bin/c++
切换到
clang++
,但问题没有解决。我还使用 libTooling 和来自
apt install
的 clang-15 进行编译,但它也会引发似乎发生在其他位置的分段错误(对于从源代码构建的 clang 在
clang::diff::SyntaxTree::Impl::getDeclValue
引发,对于来自 apt 的 clang-15 在
clang::diff::SyntaxTree::Impl::getStmtValue
引发)

因为两个编译器的二进制文件(

c++
clang++
)导致相同的分段错误,我怀疑我做错了什么,但我不知道是什么。

请教我为什么会有 NULL vtable 指针,以及如何解决这个问题。

用于复制的存储库位于此处

c++ clang++ libtooling
1个回答
0
投票

vtable 指针如何

NULL
这个基本问题已经是 在这里提问和回答:

但是,该问题的答案中提出的假设是 与这里的症状并不完美匹配,并且 libtooling 角度已经 一些新颖的方面,所以我不会称其为重复,即使 虽然很接近。

全是零

gdb
输出中,我们可以看到一切为零,而不仅仅是 虚函数表指针。这表明指针指向 在某个不应该的地方,在恰好包含全零的内存中。 也有可能有什么东西覆盖了整个 AST 内存 零。

作为演示,如果我们有一个带有 vtable 的类:

class C {
public:
  int m_x, m_y;
  virtual ~C() {}
};

并创建一个指向零的指针:

  unsigned char arr[80];
  std::memset(arr, 0, sizeof(arr));
  C *p = reinterpret_cast<C*>(&arr[0]);

然后

gdb
将报告零 vtable:

(gdb) print *p
$1 = {_vptr.C = 0x0, m_x = 0, m_y = 0}

不知何故,你做了一些事情来造成类似的情况。

libtooling 连接

所讨论的指针表面上指向一个 lib工具

Decl
AST 节点。但一切都是零,包括
DeclKind

在此条件下发生崩溃的原因是

DeclKind
为零恰好是使用的值 AccessSpecDecl 表明自己的身份
dyn_cast
.

通话时崩溃的原因是

A->getSourceRange()
getSourceRange
是一个
virtual
函数,但 vtable 指针是
NULL
,因此取消引用该指针会导致崩溃。

如何调试这个?

在这种情况下,崩溃站点本身并不能提供太多帮助 信息,你想开始从你的程序中删除尽可能多的信息 你可以,同时保留它以这种方式失败的事实。 大概您是从一些 libtooling hello-world 教程示例开始的 不表现出这种行为。删除程序的各个部分, 一点一点,直到你除了原始教程代码之外什么都没有。在 某个时刻,您将移除一块,从而使问题消失;放 回来并继续移除其他部分。

如果通过这样做,您解决了问题,那就太好了,您就完成了。 否则你将拥有一个最小的复制器,它可以作为 新问题。 (但不要只链接到 github 存储库。问题 应该是独立的。)

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.