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 C con clang -O2, obtengo un error de bus cuando main() llama a nop1(), nop2(), nop3() pero no para {{X5} }.

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

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

Esta es mi versión de 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 todas las funciones de nop, con y sin -O2.

Esta es mi versión de 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

Quizás 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
8
Karl Marklund 17 feb. 2018 a las 01:20

2 respuestas

La mejor respuesta

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

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

Recientemente, este problema surgió nuevamente 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é qué versión será lanzado en.

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

6
Ilya Popov 16 feb. 2018 a las 23:18

Parece que clang genera el prólogo de la función y nada más, dejando que la ejecución caiga en otro fragmento de código no relacionado. En mi máquina, produce:

0000000100000fa0 <_main>:
   100000fa0:   55                      push   rbp
   100000fa1:   48 89 e5                mov    rbp,rsp

Disassembly of section __TEXT.__unwind_info:

0000000100000fa4 <__TEXT.__unwind_info>:
   100000fa4:   01 00                   add    DWORD PTR [rax],eax
   100000fa6:   00 00                   add    BYTE PTR [rax],al
   100000fa8:   1c 00                   sbb    al,0x0
   100000faa:   00 00                   add    BYTE PTR [rax],al
   100000fac:   00 00                   add    BYTE PTR [rax],al

El error de bus es causado por la primera instrucción add, ya que rax apunta a _main, e intenta escribir en la memoria de solo lectura.

Curiosamente, colocar __asm__ volatile("nop\n"); como la primera línea en nop1 da un comportamiento correcto.

0
C_Elegans 16 feb. 2018 a las 22:34