Este código
long long a1[] = {1, 2}, a2[2];
a2[0] = a1[0];
a2[1] = a1[1];
compila para
movdqa .LC0(%rip), %xmm0
leaq -24(%rsp), %rax
movaps %xmm0, -24(%rsp)
e ocorre uma falha de segmentação na última instrução, o que, pelo que entendi, ocorre porque ela -24(%rsp)
não está em um limite de 16 bytes.
Por que isso aconteceria? Existe UB em algum lugar?
Originalmente, eu estava atribuindo matrizes de bytes (com minha própria função memcpy de 3 linhas), este é apenas um exemplo mínimo reproduzível.
Aqui estão todos os detalhes:
-----> /my/working/directory $ cat m.c
static void dontoptimize(void *p) {
asm volatile("" : : "g"(p) : "memory");
}
void _start() {
long long a1[] = {1, 2}, a2[2];
a2[0] = a1[0];
a2[1] = a1[1];
dontoptimize(a2);
while (1) {}
}
-----> /my/working/directory $ gcc --version | head -1
gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
-----> /my/working/directory $ cat r.sh
set -x -e
gcc -nostdlib -static -fno-builtin -O2 -march=x86-64 m.c
objdump -d a.out
gdb -q a.out
-----> /my/working/directory $ sh r.sh
+ gcc -nostdlib -static -fno-builtin -O2 -march=x86-64 m.c
+ objdump -d a.out
a.out: file format elf64-x86-64
Disassembly of section .text:
0000000000401000 <_start>:
401000: f3 0f 1e fa endbr64
401004: 66 0f 6f 05 f4 0f 00 movdqa 0xff4(%rip),%xmm0 # 402000 <_start+0x1000>
40100b: 00
40100c: 48 8d 44 24 e8 lea -0x18(%rsp),%rax
401011: 0f 29 44 24 e8 movaps %xmm0,-0x18(%rsp)
401016: eb fe jmp 401016 <_start+0x16>
+ gdb -q a.out
Reading symbols from a.out...
(No debugging symbols found in a.out)
(gdb) run
Starting program: /my/working/directory/a.out
Program received signal SIGSEGV, Segmentation fault.
0x0000000000401011 in _start ()
(gdb) x/i 0x0000000000401011
=> 0x401011 <_start+17>: movaps %xmm0,-0x18(%rsp)
(gdb) print $rsp
$1 = (void *) 0x7fffffffdf40
(gdb) print (0x7fffffffdf40 - 0x18) % 16
$2 = 8
-march
foi definido x86-64
para mais especificidade, originalmente eu compilei comnative
No que diz respeito à especificação da linguagem C, há UB em todos os lugares . A semântica da linguagem C de um programa C em execução em um ambiente hospedado -- que é como você está construindo e executando seu programa * -- começa com a entrada em
main()
. A especificação da linguagem não tem nada a dizer sobre o comportamento do seu programa, se é que podemos chamá-lo assim, porque ele nem tem umamain()
função, muito menos entra nomain()
que ele não tem.Se ou como você poderia escrever tal
_start
função em C e compilá-la de forma que ela funcione como você pretende é totalmente dependente da implementação, mas uma alternativa pode ser compilá-la com menos ou nenhuma otimização. Por exemplo, no nível de otimização-O
, seu compilador produz código para essa função que não depende demovaps
. Claro, isso não descarta outros problemas semelhantes.Supondo que você tenha caracterizado a natureza e a fonte do erro corretamente, isso deve ser porque as suposições do compilador sobre o valor do ponteiro de pilha na entrada de uma função não são satisfeitas na entrada em
_start()
. O que não é muito surpreendente, dada a posição única do_start()
quadro de no fundo da pilha, e essa função não ter sido inserida por meio de uma chamada de outra função.* Construir com
-nostdlib
não direciona o binário resultante para um ambiente autônomo. E executá-lo por meio dos recursos padrão do seu SO, como você faria com qualquer outro programa, é executá-lo em um ambiente hospedado, não autônomo.