【问题标题】:Alignment issue with C pointer to C++ class containing aligned C structure指向包含对齐 C 结构的 C++ 类的 C 指针的对齐问题
【发布时间】:2020-05-09 14:06:36
【问题描述】:

我有以下代码:

#include <iostream>
#include <stdint.h>
#include <stdio.h>

// Structure from 3rd party that cannot be modified
typedef struct  {
   uint32_t flags;
   uint32_t len;
   uint8_t padding[120];
} mystruct_t __attribute__ ((aligned(128)));

// Classes used internally
class Matcher {
};

class MatcherEQ : public Matcher {
  mystruct_t _bson;
};

Matcher* factory() {
  return new MatcherEQ();
}

// Structure used for C/C++ interface
typedef void* Match_t;

// Pure C API
extern "C" {
void CreateMatch(Match_t *pm) {
  *pm = NULL;
  Matcher *b = factory();
  *pm = (Match_t)b;
}
}

int main(void) {

  // C style code
  puts("C\n");
  Match_t m;

  CreateMatch(&m);

  // C++ code
  std::cout << "C++" << std::endl;
  Matcher *pm = factory();

  return 0;
}

我使用使用 gcc 4.3.2 + Sanitizer 的工作环境的内部工具链编译它。生成的命令行如下所示:

g++ -fsanitize=undefined -fsanitize=address -fno-omit-frame-pointer -fsanitize-recover=address -Wduplicated-cond -Wduplicated-branches -Wlogical-op -Wnull-dereference -Wno-unused-local-typedefs -Wno-deprecated -Wformat=2 -D_GLIBCXX_USE_CXX11_ABI=0 -std=c++98 -DDISABLE_MEMORY_ALLOCATORS=1 -march=k8 -pipe -fPIC -fno-strict-aliasing -Wall -Wpointer-arith -Wshadow -Wcast-align -Wimplicit -fno-tree-sra -g -Wno-write-strings -Wno-cast-qual -g3 -O0 -D__UNIX64__ -D__64BIT__ -D__LINUX64__ -D_LP64 -DSLES_10 alignissue.cpp

运行它时,我有以下输出:

C

../Sources/Tests/C/alignissue.cpp:48: runtime error: store to misaligned address 0x613000000040 for type 'struct MatcherEQ', which requires 128 byte alignment
0x613000000040: note: pointer points here
 01 00 00 62  be be be be be be be be  be be be be be be be be  be be be be be be be be  be be be be
              ^ 
../Sources/Tests/C/alignissue.cpp:41:7: runtime error: member access within misaligned address 0x613000000040 for type 'struct MatcherEQ', which requires 128 byte alignment
0x613000000040: note: pointer points here
 01 00 00 62  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00
              ^ 
../Sources/Tests/C/alignissue.cpp:41:7: runtime error: member access within misaligned address 0x613000000040 for type 'struct MatcherEQ', which requires 128 byte alignment
0x613000000040: note: pointer points here
 01 00 00 62  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  d8 c3 12 a3
              ^ 
C++

=================================================================
==21777==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 384 byte(s) in 1 object(s) allocated from:
    #0 0x7f4aa347500f in operator new(unsigned long) (../../libasan.so+0x10c00f)
    #1 0x403d05 in factory() ../Sources/Tests/C/alignissue.cpp:48
    #2 0x40402e in main ../Sources/Tests/C/alignissue.cpp:75
    #3 0x7f4aa19f5c35 in __libc_start_main (/lib64/libc.so.6+0x1ec35)

Direct leak of 384 byte(s) in 1 object(s) allocated from:
    #0 0x7f4aa347500f in operator new(unsigned long) (../../libasan.so+0x10c00f)
    #1 0x403d05 in factory() ../Sources/Tests/C/alignissue.cpp:48
    #2 0x403e31 in CreateMatch ../Sources/Tests/C/alignissue.cpp:60
    #3 0x403f17 in main ../Sources/Tests/C/alignissue.cpp:71
    #4 0x7f4aa19f5c35 in __libc_start_main (/lib64/libc.so.6+0x1ec35)

SUMMARY: AddressSanitizer: 768 byte(s) leaked in 2 allocation(s).

内存泄漏的原因很明显。

但我想了解并解决地址未对齐问题。

typedef void* Match_t 在这里是因为在我的工作环境中我只能导出纯 C API。所以这是我发现导出在 C++ 类上工作的函数的一种解决方法。

gcc 编译器版本我也别无选择 :-(

【问题讨论】:

  • 嗯,在 Godbolt 上没有 this 执行错误的最早的 gcc 版本上,我明白了:godbolt.org/z/zJRjEs
  • 对齐属性可能只会被堆栈上的对象认可。具有 C++17 新/删除(堆)存储的更新版本的 gcc 应该遵循 alignas 关键字。
  • edit您的问题并添加详细信息,您如何编译、链接和运行您的程序。我刚刚在我的 Ubuntu 系统上使用默认的 g++ 8.3.0 进行了尝试,但无法重现该问题。如stackoverflow.com/questions/37970758/…中所述,我使用了-fsanitize=address-lasan
  • 我用-fsanitize=address 尝试了相同的代码,但没有收到任何错误。但是-fsanitize=undefined 也有类似的错误。用细节更新问题
  • @Bodo 命令行已添加。如您所见,我坚持使用 gcc 4.3.2 和 C++98

标签: c++ c pointers alignment


【解决方案1】:

GCC 4.7.3(目前在 Godbolt 上支持执行的最早版本的 gcc)有 (https://godbolt.org/z/Ndvkkf):

  • alignof(MatcherEQ) = 128

  • alignof(max_align_t) = 16

(使用 C++11 alignof 运算符,未指定特定平台)。

除了 C++2003 标准中的“适当对齐”之外,我找不到任何有关对齐的相关措辞。在 C++11 中(当你的 gcc 4.3.2 发布时还没有完成),我们有这样的措辞(强调我的):

[basic.align]/3

扩展对齐由大于alignof(std::max_align_t) 的对齐表示。 是否支持任何扩展对齐以及支持它们的上下文(7.6.2)由实现定义。具有扩展对齐要求的类型是过度对齐类型时间>。 [注意:每个过度对齐的类型都是或包含一个应用扩展对齐的类类型(可能通过非静态数据成员)。- 结束说明]

虽然这对您的情况尚无定论(因为 C++11 不适用于 gcc 4.3.2),但可以安全地假设 gcc 根本不支持过度对齐的类型,至少对于动态分配的情况(请注意,stack allocation 似乎没有问题。

【讨论】:

  • 谢谢!所以对于 C++98,似乎我唯一的解决方案是实现一个支持过度对齐的堆分配器......如果可能的话。
猜你喜欢
  • 2015-03-26
  • 2017-08-16
  • 1970-01-01
  • 2021-01-12
  • 1970-01-01
  • 2011-01-20
  • 2011-01-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多