¿Por qué el bucle vacío provoca un error de bus al compilar el programa C con clang -O2 en macOS?

 C Programming >> Programación C >  >> Tags >> Clang
¿Por qué el bucle vacío provoca un error de bus al compilar el programa C con clang -O2 en macOS?


Estoy en macOS High Sierra.


$ uname -v
Darwin Kernel Version 17.2.0: Fri Sep 29 18:27:05 PDT 2017; root:xnu-4570.20.62~3/RELEASE_X86_64

Tengo el siguiente programa sintetizado.


void nop1() {
for (;;);
}
void nop2() {
while (1);
}
void nop3() {
int i = 0;
while(1) {
i++;
}
}
void nop4() {
static int i = 0;
while(1) {
i++;
};
}
int main() {
nop1();
return 0;
}

Edición 2: Ahora he compilado explícitamente con clang en los siguientes ejemplos.


Cuando compilo y ejecuto el siguiente programa en C con clang -O2 Recibo un error de bus cuando main() llama al nop1() , nop2() , nop3() pero no para nop4() .


$ ./a.out
[1] 93029 bus error (core dumped) ./a.out

Al compilar sin -O2 todas las versiones se ejecutan sin error de bus. Supongo que el optimizador transforma nop3() a nop2() . Me gustaría entender qué causa el error del bus en cada caso y por qué usar una variable estática en nop4() no provoca un error de bus.


Esta es mi versión clang:


$ clang -v
Apple LLVM version 8.1.0 (clang-802.0.42)
Target: x86_64-apple-darwin17.2.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

También probé con gcc en Linux:


$ uname -a
Linux trygger 4.4.0-112-generic #135-Ubuntu SMP Fri Jan 19 11:48:36 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

, y los programas funcionan bien para todos los nop -funciones, con y sin -O2 .


Esta es mi versión gcc en Linux.


$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.6' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.6)

Editar 4


Tal vez la salida de otool sea más fácil de analizar. Primero con -O2 .


$ clang -O2 segfault.c
$ otool -vt a.out
a.out:
(__TEXT,__text) section
_nop1:
0000000100000f30 pushq %rbp
0000000100000f31 movq %rsp, %rbp
0000000100000f34 nopw %cs:(%rax,%rax)
0000000100000f40 jmp 0x100000f40
0000000100000f42 nopw %cs:(%rax,%rax)
_nop2:
0000000100000f50 pushq %rbp
0000000100000f51 movq %rsp, %rbp
0000000100000f54 nopw %cs:(%rax,%rax)
0000000100000f60 jmp 0x100000f60
0000000100000f62 nopw %cs:(%rax,%rax)
_nop3:
0000000100000f70 pushq %rbp
0000000100000f71 movq %rsp, %rbp
0000000100000f74 nopw %cs:(%rax,%rax)
0000000100000f80 jmp 0x100000f80
0000000100000f82 nopw %cs:(%rax,%rax)
_nop4:
0000000100000f90 pushq %rbp
0000000100000f91 movq %rsp, %rbp
0000000100000f94 nopw %cs:(%rax,%rax)
0000000100000fa0 jmp 0x100000fa0
0000000100000fa2 nopw %cs:(%rax,%rax)
_main:
0000000100000fb0 pushq %rbp
0000000100000fb1 movq %rsp, %rbp

Y sin -O2 .


$ clang segfault.c
$ otool -vt a.out
a.out:
(__TEXT,__text) section
_nop1:
0000000100000f30 pushq %rbp
0000000100000f31 movq %rsp, %rbp
0000000100000f34 jmp 0x100000f39
0000000100000f39 jmp 0x100000f39
0000000100000f3e nop
_nop2:
0000000100000f40 pushq %rbp
0000000100000f41 movq %rsp, %rbp
0000000100000f44 jmp 0x100000f49
0000000100000f49 jmp 0x100000f49
0000000100000f4e nop
_nop3:
0000000100000f50 pushq %rbp
0000000100000f51 movq %rsp, %rbp
0000000100000f54 movl $0x0, -0x4(%rbp)
0000000100000f5b movl -0x4(%rbp), %eax
0000000100000f5e addl $0x1, %eax
0000000100000f61 movl %eax, -0x4(%rbp)
0000000100000f64 jmp 0x100000f5b
0000000100000f69 nopl (%rax)
_nop4:
0000000100000f70 pushq %rbp
0000000100000f71 movq %rsp, %rbp
0000000100000f74 jmp 0x100000f79
0000000100000f79 movl 0x81(%rip), %eax
0000000100000f7f addl $0x1, %eax
0000000100000f82 movl %eax, 0x78(%rip)
0000000100000f88 jmp 0x100000f79
0000000100000f8d nopl (%rax)
_main:
0000000100000f90 pushq %rbp
0000000100000f91 movq %rsp, %rbp
0000000100000f94 subq $0x10, %rsp
0000000100000f98 movl $0x0, -0x4(%rbp)
0000000100000f9f callq 0x100000f40
0000000100000fa4 xorl %eax, %eax
0000000100000fa6 addq $0x10, %rsp
0000000100000faa popq %rbp
0000000100000fab retq

Editar 3


Según lo solicitado por @Olaf, agregué el ensamblado generado por clang -S.


 .section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 12
.globl _nop1
.p2align 4, 0x90
_nop1: ## @nop1
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
jmp LBB0_1
LBB0_1: ## =>This Inner Loop Header: Depth=1
jmp LBB0_1
.cfi_endproc
.globl _nop2
.p2align 4, 0x90
_nop2: ## @nop2
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp3:
.cfi_def_cfa_offset 16
Ltmp4:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp5:
.cfi_def_cfa_register %rbp
jmp LBB1_1
LBB1_1: ## =>This Inner Loop Header: Depth=1
jmp LBB1_1
.cfi_endproc
.globl _nop3
.p2align 4, 0x90
_nop3: ## @nop3
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp6:
.cfi_def_cfa_offset 16
Ltmp7:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp8:
.cfi_def_cfa_register %rbp
movl $0, -4(%rbp)
LBB2_1: ## =>This Inner Loop Header: Depth=1
movl -4(%rbp), %eax
addl $1, %eax
movl %eax, -4(%rbp)
jmp LBB2_1
.cfi_endproc
.globl _nop4
.p2align 4, 0x90
_nop4: ## @nop4
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp9:
.cfi_def_cfa_offset 16
Ltmp10:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp11:
.cfi_def_cfa_register %rbp
jmp LBB3_1
LBB3_1: ## =>This Inner Loop Header: Depth=1
movl _nop4.i(%rip), %eax
addl $1, %eax
movl %eax, _nop4.i(%rip)
jmp LBB3_1
.cfi_endproc
.globl _main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp12:
.cfi_def_cfa_offset 16
Ltmp13:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp14:
.cfi_def_cfa_register %rbp
subq $16, %rsp
movl $0, -4(%rbp)
callq _nop1
xorl %eax, %eax
addq $16, %rsp
popq %rbp
retq
.cfi_endproc
.zerofill __DATA,__bss,_nop4.i,4,2 ## @nop4.i
.subsections_via_symbols

Respuestas:


Este es un error conocido en LLVM. El comportamiento que ve es válido para C++ pero no para C.


Consulte el informe de errores n.º 965 de 2006 aquí.


Recientemente, este problema volvió a surgir debido a que Rust se vio afectado por esto.


Hay un parche con una solución aquí, que se fusionó en noviembre de 2017, pero no sé en qué versión se lanzará.


Vea también una discusión en la lista de correo aquí.