用clang转储内存布局

3

你好,我正在寻找一种使用clang来转储类/结构体/数据类型内存布局的方法。我有一个基于这个教程的简单应用程序。

我还添加了这个函数:

 bool VisitFieldDecl(FieldDecl *F)
{
  F->dump();
  std::cerr << F->getQualifiedNameAsString()  << " " << F->getBitWidthValue(*Context) << " " << std::endl;
  F->dump() ;
  std::cerr << "-----------------------------------------" << std::endl;
  return true;
}

很遗憾,对于我的类型,getBitWidthValue也返回零。

我需要递归地获取每个类和所有嵌套类型的完整内存布局。包括大小/偏移量。

也许AST不是正确的位置,我需要其他钩子来开始吗?


为什么你需要它?标准没有定义内存布局,所以通过这种方式可能会引入一整套新的问题。另一方面,有时你别无选择。 - Alexander Oh
我们有两个库,分别叫做typeliborogen,它们提供了Ruby绑定和(反)序列化信息。目前使用的是gcc-xml。由于我们对此不满意,因此正在寻找替代方案。你说的“标准没有定义”是什么意思? - Matthias Goldhoorn
这意味着,如果您有两个相邻的数据结构(在数组中),编译器选择的填充量是任意的。每当我需要编写串行化程序时,我都会确保不进行类型转换,而是从串行流中复制值,反之亦然。 - Alexander Oh
理论上你是对的,但我们使用gccxml设置已经五年了,没有任何问题。即使在一般情况下未定义数据结构的库之间交换数据也通常正常工作。 - Matthias Goldhoorn
那是一个提示,你应该小心。字节布局应该在你使用的ABI中指定。如果你可以限制编译器的数量并确保ABI兼容,那么这是可能的。还要考虑64位与32位ABI不兼容性。请注意,使用gccxml实现序列化程序的人可能会遇到问题和限制。因此,在代码中可能有解决ABI不兼容性的地方,以便跨其他编译器工作。确保在支持的编译器(和不同的ABI)上测试你的(反)序列化。 - Alexander Oh
显示剩余2条评论
1个回答

1
一种方法是使用 llvm/clang-3.4 中给定的 const clang::CXXRecordDecl* decl 的 "AST Record Layout":
const clang::ASTRecordLayout &typeLayout(decl->getASTContext().getASTRecordLayout(decl));
std::cout << "record '" << decl->getQualifiedNameAsString() << "' with " <<  typeLayout.getSize().getQuantity() << "bytes\n";

for(clang::RecordDecl::field_iterator fit = decl->field_begin(); fit != decl->field_end(); fit++) {
    const clang::QualType qualType = fit->getType().getLocalUnqualifiedType().getCanonicalType();
    size_t fieldOffset = typeLayout.getFieldOffset(fit->getFieldIndex());
    std::cout << "member '" << qualType.getAsString() << "' with " << fieldOffset/8 << " bytes offset\n";
}

免责声明:代码是一起复制的,这里没有经过测试 -- 但应该可以正常工作... (商标)

示例:

struct EXAMPLE
{
    char a;
    int b;
    long c;
    long long d;
    float e;
    double f;
};

输出:

record 'EXAMPLE' with 40 bytes
member 'char' with 0 bytes offset
member 'int' with 4 bytes offset
member 'long' with 8 bytes offset
member 'long long' with 16 bytes offset
member 'float' with 24 bytes offset
member 'double' with 32 bytes offset

更多内容请参见:
https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html
https://clang.llvm.org/doxygen/classclang_1_1ASTRecordLayout.html


1
以上代码中的fieldOffset应该除以8,因为getFieldOffset()返回值是以位为单位而不是字节。 - leo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接