簡體   English   中英

返回局部變量地址的Function與不同版本的gcc的行為不同?

[英]Function that returns address of local variable acts differently with different versions of gcc?

我寫了這段代碼,發現它與不同版本的 gcc 的行為不同。

源代碼,

#include<stdio.h>

int *fun();

int main(int argc, char *argv[])
{
    int *ptr;

    ptr = fun();

    printf("%x", *ptr);
}

int *fun()
{
    int *ptr;
    int foo = 0xdeadbeef;
    ptr = &foo;

    return ptr;
}

代碼是錯誤的。 執行完fun()后,局部變量foo被釋放並且不存在。 但是main的 function 嘗試使用它,所以會導致分段錯誤。

但是我在三個版本的 gcc 上嘗試了相同的代碼,它們的行為不同。

在 10.2.0

╭─    ~ ································································ ✔ ─╮
╰─ gcc -v | bin/pbcopy                                                            ─╯
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: /build/gcc/src/gcc/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++,d --with-isl --with-linker-hash-style=gnu --with-system-zlib --enable-__cxa_atexit --enable-cet=auto --enable-checking=release --enable-clocale=gnu --enable-default-pie --enable-default-ssp --enable-gnu-indirect-function --enable-gnu-unique-object --enable-install-libiberty --enable-linker-build-id --enable-lto --enable-multilib --enable-plugin --enable-shared --enable-threads=posix --disable-libssp --disable-libstdcxx-pch --disable-libunwind-exceptions --disable-werror gdc_include_dir=/usr/include/dlang/gdc
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 10.2.0 (GCC)


╭─    ~ ································································ ✔ ─╮
╰─ gcc a.c && a.out                                                               ─╯
deadbeef%                                                                            

它打印deadbeef

它的匯編代碼:

(gdb) disassemble fun 
Dump of assembler code for function fun:
   0x000000000000119d <+23>:    movl   $0xdeadbeef,-0x14(%rbp)
   0x00000000000011a4 <+30>:    lea    -0x14(%rbp),%rax
   0x00000000000011a8 <+34>:    mov    %rax,-0x10(%rbp)
   0x00000000000011ac <+38>:    mov    -0x10(%rbp),%rax
   0x00000000000011b0 <+42>:    mov    -0x8(%rbp),%rdx
   0x00000000000011b4 <+46>:    sub    %fs:0x28,%rdx
   0x00000000000011bd <+55>:    je     0x11c4 <fun+62>
   0x00000000000011bf <+57>:    call   0x1030 <__stack_chk_fail@plt>
   0x00000000000011c4 <+62>:    leave  
   0x00000000000011c5 <+63>:    ret    
End of assembler dump.

(gdb) disass main

   0x000000000000116c <+35>:    mov    %eax,%esi
   0x000000000000116e <+37>:    lea    0xe8f(%rip),%rdi        # 0x2004
   0x0000000000001175 <+44>:    mov    $0x0,%eax
   0x000000000000117a <+49>:    call   0x1040 <printf@plt>

匯編代碼顯示 function 將0xdeadbeef存儲在%rax中,並且printf將其接收為%esi ,因此它打印0xdeadbeef

在 9.3.0 中

coolder@ASUS:~$ gcc -v                                                          [1/1]
Using built-in specs.                                                                
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:hsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 9.3.0-15' --with-bugurl=file:///usr/share/doc/gcc-9/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,gm2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-9 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-9-0xEOmg/gcc-9-9.3.0/debian/tmp-nvptx/usr,hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-mutexThread model: posix
gcc version 9.3.0 (Debian 9.3.0-15)


coolder@ASUS:~$ gcc a.c && ./a.out
a.c: In function ‘fun’:
a.c:14:9: warning: function returns address of local variable [-Wreturn-local-addr]
   14 |  return &a;
      |         ^~
0coolder@ASUS:~$ 

它打印 0。

它的匯編代碼,

(gdb) disassemble fun
Dump of assembler code for function fun:
   0x000055555555515e <+0>:     push   %rbp
   0x000055555555515f <+1>:     mov    %rsp,%rbp
   0x0000555555555162 <+4>:     movl   $0xdeadbeef,-0x4(%rbp)
   0x0000555555555169 <+11>:    mov    $0x0,%eax
   0x000055555555516e <+16>:    pop    %rbp
(gdb) disass main
   0x000055555555513e <+9>:     call   0x55555555515e <fun>
   0x0000555555555143 <+14>:    mov    %rax,%rsi
   0x0000555555555146 <+17>:    lea    0xeb7(%rip),%rdi        # 0x555555556004
   0x000055555555514d <+24>:    mov    $0x0,%eax

匯編代碼顯示它將0移動到%eax ,並且printf使用%eax作為%rsi ,因此它打印0

在 5.4.1 中

➜  ~ gcc a.c && ./a.out 
a.c: In function ‘fun’:
a.c:17:9: warning: function returns address of local variable [-Wreturn-local-addr]
  return &a;
         ^
[1]    3566 segmentation fault (core dumped)  ./a.out

正如我所料,它會出現分段錯誤。

它的匯編代碼,

(gdb) disassemble fun 
Dump of assembler code for function fun:
   0x08048448 <+0>:     push   %ebp
   0x08048449 <+1>:     mov    %esp,%ebp
   0x0804844b <+3>:     sub    $0x10,%esp
   0x0804844e <+6>:     movl   $0xdeadbeef,-0x4(%ebp)
   0x08048455 <+13>:    mov    $0x0,%eax
   0x0804845a <+18>:    leave  
   0x0804845b <+19>:    ret   

(gdb) disass main
   0x0804841d <+17>:    call   0x8048448 <fun>
   0x08048422 <+22>:    mov    %eax,-0xc(%ebp)
   0x08048425 <+25>:    mov    -0xc(%ebp),%eax
   0x08048428 <+28>:    mov    (%eax),%eax

匯編代碼顯示它將 0x0 移動到 %eax,並且 main 嘗試引用 %eax,因此這會導致分段錯誤。

那么為什么匯編代碼如此不同呢?

任何幫助將不勝感激。

返回局部變量的地址並在其生命周期結束后嘗試訪問它是未定義的行為,合理化引擎蓋下發生的事情是愚蠢的差事,因為沒有要遵循的標准規則(當然,從上述和鏈接的 UB 規則),不同的編譯器版本會改變處理這種情況的方式是很常見的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM