【问题标题】:Coredump occurred in linux c program (Redhat 8.3.1)linux c程序(Redhat 8.3.1)中发生Coredump
【发布时间】:2021-10-14 01:03:18
【问题描述】:

在我的程序中,在 linux 上的嵌套结构上使用 _Decimal128 时会发生 coredump。
当满足以下所有条件时,就会发生这种情况。

首先,#pragma pack(8) 声明。
第二,第三个嵌套结构
第三,_Decimal128类型地址作为函数的参数。

我正在寻找导致核心转储的原因。
开发环境为Redhat 8.3.1,编译成gcc。
示例代码如下。

#include <stdio.h>
#pragma pack(8)   // without this line, it is success

struct _c {
    int c1;
   _Decimal128 c2;
};
struct _b {
   int b1;
   _Decimal128 b2; 
   struct _c b3;      //3rd nested structure
};
struct _a {
   int a1;
   _Decimal128 a2;
   struct _b a3;     // 2nd nested structure
};

void func1(struct _c *cptr)
{
   if (cptr->c2 == 0)     // if argument type is structure, it is success
      printf("[func1]\n");
}

void func2(_Decimal128 *ptr)
{
   if (*ptr == 0)        // if argument type is _Decimal128, it occurs coredump
      printf("[func2]\n");
}

int main()
{
   struct _a a;
   struct _b b;
   _Decimal128 t = 0;
   b.b2 = t;
   b.b3.c2 = t;
   a.a3 = b;

   func1(&a.a3.b3);  // if argument is 3rd structure address, it is success
   func2(&a.a3.b2);  // if argument is _Decimal128 address of 2rd structure, it is sunccess
   func2(&a.a3.b3.c2);  // if argument is _Decimal128 address of 3rd structure, it occurs coredump
}      

结果是,

[func1]
[func2]
Memory fault(coredump)

如果我从上面的源代码中删除#pragma pack(8)这一行,结果如下。

[func1]
[func2]
[func2]

【问题讨论】:

  • GCC 的哪个版本?似乎在没有更改 Godbolt 的 gcc 11.2 的情况下工作正常。 godbolt.org/z/9o75efq4e
  • 不确定编译后的汇编代码,但似乎是因为 1. #pragma pack(8) 破坏了 _Decimal128 的对齐规则。 2.使用结构指针很好,因为它通知编译器_Decimal128由于未对齐而不能直接使用,应该像复制一样正确处理。 3. 使用_Decimal128* 并不顺利,因为编译器会假定这是_Decimal128 的有效(对齐)指针,而实际上不是。
  • @AKX 看起来优化非常聪明,它省略了函数的调用。
  • @AKX 您的示例仍然很有趣,因为它表明 movdqu(不会导致未对齐错误)在函数 func1 中用于从内存中加载值,而 movdqa (导致错位错误)在函数func2中使用。
  • @AKX 参数在使用movdqa-O0 之前被复制到正确对齐的堆栈中。

标签: c linux decimal redhat coredump


【解决方案1】:

#pragma pack(8) 会告诉编译器结构的成员应该对齐到 8 个字节,忽略每种类型的对齐要求。

_Decimal128 可能是 16 字节类型,其对齐要求可能是 16 字节。

使用指向结构 struct _c *cptr 的指针,编译器可以知道成员使用了非常规的对齐方式,并且可能需要一些特殊的方式来处理 _Decimal128 成员。

另一方面,直接使用指向_Decimal128 的指针,编译器将假定该指针是指向_Decimal128 成员的有效(通常对齐)指针。 因此,当传递的不是指向_Decimal128 的普通指针时,它可能会失败。

让我们用一些例子来验证这个陈述。

首先,让我们检查对齐要求和_Decimal128成员的地址:

#include <stdio.h>
#pragma pack(8)   // without this line, it is success

struct _c {
    int c1;
   _Decimal128 c2;
};
struct _b {
   int b1;
   _Decimal128 b2; 
   struct _c b3;      //3rd nested structure
};
struct _a {
   int a1;
   _Decimal128 a2;
   struct _b a3;     // 2nd nested structure
};

int main(void) {
    struct _a a;
    printf("sizeof(_Decimal128) = %d\n", (int)sizeof(_Decimal128));
    printf("_Alignof(_Decimal128) = %d\n", (int)_Alignof(_Decimal128));
    printf("&a = %p\n", (void*)&a);
    printf("&a.a3.b3.c2 = %p\n", (void*)&a.a3.b3.c2);
    return 0;
}

An example of output:

sizeof(_Decimal128) = 16
_Alignof(_Decimal128) = 16
&a = 0x7fffee270d70
&a.a3.b3.c2 = 0x7fffee270da8

在这种环境中,_Decimal128 的对齐要求是 16 字节,但成员 _Decimal128 c 放置的地址不能被 16 整除。

其次,这里是@AKX's example,它表明Decimal128 可以通过函数func1(使用指向结构的指针)和函数func2(使用指向_Decimal128 的指针)进行不同的处理:

func1:
        subq    $8, %rsp
        movdqa  .LC1(%rip), %xmm1
        movdqu  8(%rdi), %xmm0
func2:
        subq    $8, %rsp
        movdqa  .LC1(%rip), %xmm1
        movdqa  (%rdi), %xmm0

在本例中,the movdqu instruction 用于在函数func1 中加载_Decimal128 值。 movdqu 可以使用未对齐的内存。

另一方面,the movdqa instruction 用于函数func2 中的_Decimal128 值。 movdqa 在尝试使用未对齐的内存时生成一般保护异常。

这表明func1 可以使用未对齐的内存,而func2 在此示例中不能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-18
    • 2014-09-27
    • 2013-04-05
    • 1970-01-01
    • 2012-02-03
    • 1970-01-01
    • 2014-03-09
    相关资源
    最近更新 更多