首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >GCC4.6上的Seg错误当试图移动一个大位集时,这是一个编译器错误吗?

GCC4.6上的Seg错误当试图移动一个大位集时,这是一个编译器错误吗?
EN

Stack Overflow用户
提问于 2015-05-21 11:53:36
回答 1查看 223关注 0票数 7

当位集很大时,由于返回位集的rvalue,我遇到了崩溃的问题。这是编译器的错误,还是我做错了导致未定义行为的事情?

下面的代码在GCC 4.6.3上崩溃,设置了-std=c++0x标志。

代码语言:javascript
复制
#include <bitset>

// typedef std::bitset<0xffff> uut;
typedef std::bitset<0xffffff> uut;

struct foo {
  foo(uut b)
  : b_(std::move(b))
  {
  }

  uut b_;
};

uut make_bits(int)
{
  uut bits;

  // Only works for 0xffff:
  return std::move(bits);
  // Works for both 0xffff and 0xffffff:
  //return bits;
}

int main()
{
  foo(make_bits(0));
}

奇怪的是,如果我移除int参数,可能会导致函数内联?

正如@unwind建议的那样,下面是在valgrind ./a.out下运行的输出

代码语言:javascript
复制
==24780== Memcheck, a memory error detector
==24780== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==24780== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==24780== Command: ./a.out
==24780== 
==24780== Warning: client switching stacks?  SP change: 0x7ff000068 --> 0x7fea00058
==24780==          to suppress, use: --max-stackframe=6291472 or greater
==24780== Invalid write of size 8
==24780==    at 0x4005E5: main (in /home/sam/scratch/a.out)
==24780==  Address 0x7fea00058 is on thread 1's stack
==24780== 
==24780== Warning: client switching stacks?  SP change: 0x7fea00050 --> 0x7fe800040
==24780==          to suppress, use: --max-stackframe=2097168 or greater
==24780== Invalid write of size 8
==24780==    at 0x40056F: make_bits(int) (in /home/sam/scratch/a.out)
==24780==    by 0x4005E9: main (in /home/sam/scratch/a.out)
==24780==  Address 0x7fe800048 is on thread 1's stack
==24780== 
==24780== 
==24780== Process terminating with default action of signal 11 (SIGSEGV)
==24780==  Access not within mapped region at address 0x7FE800048
==24780==    at 0x40056F: make_bits(int) (in /home/sam/scratch/a.out)
==24780==  If you believe this happened as a result of a stack
==24780==  overflow in your program's main thread (unlikely but
==24780==  possible), you can try to increase the size of the
==24780==  main thread stack using the --main-stacksize= flag.
==24780==  The main thread stack size used in this run was 8388608.
==24780== 
==24780== Process terminating with default action of signal 11 (SIGSEGV)
==24780==  Access not within mapped region at address 0x7FE800039
==24780==    at 0x4A255A0: _vgnU_freeres (in /usr/lib/valgrind/vgpreload_core-amd64-linux.so)
==24780==  If you believe this happened as a result of a stack
==24780==  overflow in your program's main thread (unlikely but
==24780==  possible), you can try to increase the size of the
==24780==  main thread stack using the --main-stacksize= flag.
==24780==  The main thread stack size used in this run was 8388608.
==24780== 
==24780== HEAP SUMMARY:
==24780==     in use at exit: 0 bytes in 0 blocks
==24780==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==24780== 
==24780== All heap blocks were freed -- no leaks are possible
==24780== 
==24780== For counts of detected and suppressed errors, rerun with: -v
==24780== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2)

对于valgrind --max-stacksize=99999999 ./a.out,正如val差伦所提示的那样:

代码语言:javascript
复制
==24790== Memcheck, a memory error detector
==24790== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==24790== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==24790== Command: ./a.out
==24790== 
==24790== Warning: client switching stacks?  SP change: 0x7ff000068 --> 0x7fea00058
==24790==          to suppress, use: --max-stackframe=6291472 or greater
==24790== Invalid write of size 8
==24790==    at 0x4005E5: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fea00058 is on thread 1's stack
==24790== 
==24790== Warning: client switching stacks?  SP change: 0x7fea00050 --> 0x7fe800040
==24790==          to suppress, use: --max-stackframe=2097168 or greater
==24790== Invalid write of size 8
==24790==    at 0x40056F: make_bits(int) (in /home/sam/scratch/a.out)
==24790==    by 0x4005E9: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fe800048 is on thread 1's stack
==24790== 
==24790== Invalid write of size 4
==24790==    at 0x400576: make_bits(int) (in /home/sam/scratch/a.out)
==24790==    by 0x4005E9: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fe800044 is on thread 1's stack
==24790== 
==24790== Invalid write of size 8
==24790==    at 0x400590: make_bits(int) (in /home/sam/scratch/a.out)
==24790==    by 0x4005E9: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fe800038 is on thread 1's stack
==24790== 
==24790== Invalid write of size 4
==24790==    at 0x4C2E0E0: memset (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24790==    by 0x400594: make_bits(int) (in /home/sam/scratch/a.out)
==24790==    by 0x4005E9: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fe800050 is on thread 1's stack
==24790== 
==24790== Invalid write of size 4
==24790==    at 0x4C2E0EB: memset (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24790==    by 0x400594: make_bits(int) (in /home/sam/scratch/a.out)
==24790==    by 0x4005E9: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fe800058 is on thread 1's stack
==24790== 
==24790== Invalid read of size 8
==24790==    at 0x4C2E10E: memset (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24790==    by 0x4005E9: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fe800038 is on thread 1's stack
==24790== 
==24790== Invalid read of size 8
==24790==    at 0x4005A7: make_bits(int) (in /home/sam/scratch/a.out)
==24790==    by 0x4005E9: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fe800048 is on thread 1's stack
==24790== 
==24790== Invalid write of size 8
==24790==    at 0x4C2D10D: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24790==    by 0x4005C0: make_bits(int) (in /home/sam/scratch/a.out)
==24790==    by 0x4005E9: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fee00058 is on thread 1's stack
==24790== 
==24790== Invalid read of size 8
==24790==    at 0x4C2D11A: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24790==    by 0x4005C0: make_bits(int) (in /home/sam/scratch/a.out)
==24790==    by 0x4005E9: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fe9fffc8 is on thread 1's stack
==24790== 
==24790== Invalid read of size 8
==24790==    at 0x4C2D108: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24790==    by 0x4005C0: make_bits(int) (in /home/sam/scratch/a.out)
==24790==    by 0x4005E9: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fe9fffc0 is on thread 1's stack
==24790== 
==24790== Invalid read of size 8
==24790==    at 0x4005C1: make_bits(int) (in /home/sam/scratch/a.out)
==24790==    by 0x4005E9: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fe800048 is on thread 1's stack
==24790== 
==24790== Warning: client switching stacks?  SP change: 0x7fe800040 --> 0x7fea00050
==24790==          to suppress, use: --max-stackframe=2097168 or greater
==24790==          further instances of this message will not be shown.
==24790== Invalid read of size 8
==24790==    at 0x4005C9: make_bits(int) (in /home/sam/scratch/a.out)
==24790==    by 0x4E5376C: (below main) (libc-start.c:226)
==24790==  Address 0x7fea00058 is on thread 1's stack
==24790== 
==24790== Invalid read of size 8
==24790==    at 0x4C2D000: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24790==    by 0x40060A: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fec00060 is on thread 1's stack
==24790== 
==24790== Invalid write of size 8
==24790==    at 0x4C2D004: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24790==    by 0x40060A: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fea00060 is on thread 1's stack
==24790== 
==24790== Invalid read of size 8
==24790==    at 0x4C2D00F: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24790==    by 0x40060A: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fec00070 is on thread 1's stack
==24790== 
==24790== Invalid read of size 8
==24790==    at 0x4C2D108: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24790==    by 0x400650: foo::foo(std::bitset<16777215ul>) (in /home/sam/scratch/a.out)
==24790==    by 0x400612: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fec00058 is on thread 1's stack
==24790== 
==24790== Invalid read of size 8
==24790==    at 0x4C2D11A: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24790==    by 0x400650: foo::foo(std::bitset<16777215ul>) (in /home/sam/scratch/a.out)
==24790==    by 0x400612: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7fec00048 is on thread 1's stack
==24790== 
==24790== Invalid write of size 8
==24790==    at 0x4C2D10D: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24790==    by 0x400650: foo::foo(std::bitset<16777215ul>) (in /home/sam/scratch/a.out)
==24790==    by 0x400612: main (in /home/sam/scratch/a.out)
==24790==  Address 0x7feffffe0 is on thread 1's stack
==24790== 
==24790== 
==24790== HEAP SUMMARY:
==24790==     in use at exit: 0 bytes in 0 blocks
==24790==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==24790== 
==24790== All heap blocks were freed -- no leaks are possible
==24790== 
==24790== For counts of detected and suppressed errors, rerun with: -v
==24790== ERROR SUMMARY: 2097097 errors from 19 contexts (suppressed: 2 from 2)
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-05-21 13:12:30

通过用-S编译这两种情况,我们可以确切地看到GCC在幕后所做的事情:

代码语言:javascript
复制
g++-4.6 -std=c++0x test.cc -S -fverbose-asm 

然后使用diff对输出进行比较:

代码语言:javascript
复制
diff -rNu move.s ret.s |c++filt     
--- move.s  2015-05-21 14:00:49.097524035 +0100
+++ ret.s   2015-05-21 14:00:40.021510019 +0100
@@ -79,23 +79,13 @@
    .cfi_offset 5, -8
    movl    %esp, %ebp  #,
    .cfi_def_cfa_register 5
-   subl    $2097176, %esp  #,
-   leal    -2097160(%ebp), %eax    #, tmp60
+   subl    $24, %esp   #,
+   movl    8(%ebp), %eax   # .result_ptr, tmp59
    movl    $2097152, %edx  #, tmp61
    movl    %edx, 8(%esp)   # tmp61,
    movl    $0, 4(%esp) #,
    movl    %eax, (%esp)    # tmp60,
    call    memset  #
-   leal    -2097160(%ebp), %eax    #, tmp64
-   movl    %eax, (%esp)    # tmp64,
-   call    std::remove_reference<std::bitset<16777215u>&>::type&& std::move<std::bitset<16777215u>&>(std::bitset<16777215u>&)  #
-   movl    %eax, %edx  #, D.21547
-   movl    8(%ebp), %eax   # .result_ptr, tmp65
-   movl    $2097152, %ecx  #, tmp68
-   movl    %ecx, 8(%esp)   # tmp68,
-   movl    %edx, 4(%esp)   # tmp67,
-   movl    %eax, (%esp)    # tmp66,
-   call    memcpy  #
    movl    8(%ebp), %eax   # .result_ptr,
    leave
    .cfi_restore 5

(标记为+的行仅存在于“按值返回”的情况下,-只存在于“移动”的情况下)。

在迁移案例中,还有很多堆栈指针操作正在进行(并且有一些非常大的数字)。关键的是,最后会有一个memcpy调用,该调用将结果复制回堆栈。

我对它的分析是,对于按值返回的情况,实际上发生了另一种优化,这意味着主管道内未使用的临时值被完全忽略,因为返回是值情况,而不是移动情况。

通过对-O0禁用所有优化并查看发生什么情况的按值返回情况执行相同的分析,我们可以进一步确认这一点:

代码语言:javascript
复制
diff -Nru noopt.s ret.s
--- noopt.s 2015-05-21 14:06:14.798028762 +0100
+++ ret.s   2015-05-21 14:00:40.021510019 +0100
@@ -3,7 +3,7 @@
 #  compiled by GNU C version 4.6.4, GMP version 5.1.3, MPFR version 3.1.2-p3, MPC version 1.0.1
 # GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
 # options passed:  -imultilib . -imultiarch i386-linux-gnu -D_GNU_SOURCE
-# test.cc -mtune=generic -march=i686 -O0 -std=c++0x -fverbose-asm
+# test.cc -mtune=generic -march=i686 -std=c++0x -fverbose-asm
 # -fstack-protector
 # options enabled:  -fasynchronous-unwind-tables -fauto-inc-dec
 # -fbranch-count-reg -fcommon -fdelete-null-pointer-checks -fdwarf2-cfi-asm
@@ -79,23 +79,13 @@
    .cfi_offset 5, -8
    movl    %esp, %ebp  #,
    .cfi_def_cfa_register 5
-   subl    $2097176, %esp  #,
-   leal    -2097160(%ebp), %eax    #, tmp60
+   subl    $24, %esp   #,
+   movl    8(%ebp), %eax   # .result_ptr, tmp59
    movl    $2097152, %edx  #, tmp61
    movl    %edx, 8(%esp)   # tmp61,
    movl    $0, 4(%esp) #,
    movl    %eax, (%esp)    # tmp60,
    call    memset  #
-   leal    -2097160(%ebp), %eax    #, tmp64
-   movl    %eax, (%esp)    # tmp64,
-   call    _ZSt4moveIRSt6bitsetILj16777215EEEONSt16remove_referenceIT_E4typeEOS4_  #
-   movl    %eax, %edx  #, D.21547
-   movl    8(%ebp), %eax   # .result_ptr, tmp65
-   movl    $2097152, %ecx  #, tmp68
-   movl    %ecx, 8(%esp)   # tmp68,
-   movl    %edx, 4(%esp)   # tmp67,
-   movl    %eax, (%esp)    # tmp66,
-   call    memcpy  #
    movl    8(%ebp), %eax   # .result_ptr,
    leave
    .cfi_restore 5

同样,在返回值的情况下,在禁用优化的情况下,也会发生相同的堆栈指针流形和复制。因此,看起来这两种情况下都有堆栈溢出,但在按值返回的情况下,由于其他优化,测试用例不足以实际观察它。

解决方案:在堆上分配,或者在Linux上使用pthread_attr_setstacksizeclone获得一个更大的堆栈。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30372986

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档