Jump to content
int3

SIMD e o "Stack Align"

Recommended Posts

int3

Boa tarde programadores!

Alguém me pode explicar mais ou menos o que é o "stack align"?

Vejamos neste exemplo: (isto foi feito sobre linux com a versao gdb: "GNU gdb (GDB) 7.5.91.20130417-cvs-ubuntu" e o gcc:"gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-1ubuntu1)"

C:

#include <stdio.h>
main()
{
int a,b,c;
a=0xbeef;
b=0xdead;
c=0xfaceb00c;
a=a+b;
printf("%d-%d",a,c);
}

Agora, vamos ver no debugger gdb:

0x0804841c <+0>: push %ebp
0x0804841d <+1>: mov %esp,%ebp
0x0804841f <+3>: and $0xfffffff0,%esp
0x08048422 <+6>: sub $0x20,%esp
0x08048425 <+9>: movl $0xbeef,0x14(%esp)
0x0804842d <+17>: movl $0xdead,0x18(%esp)
0x08048435 <+25>: movl $0xfaceb00c,0x1c(%esp)
0x0804843d <+33>: mov 0x18(%esp),%eax
0x08048441 <+37>: add %eax,0x14(%esp)
0x08048445 <+41>: mov 0x1c(%esp),%eax
0x08048449 <+45>: mov %eax,0x8(%esp)
0x0804844d <+49>: mov 0x14(%esp),%eax
0x08048451 <+53>: mov %eax,0x4(%esp)
0x08048455 <+57>: movl $0x8048500,(%esp)
0x0804845c <+64>: call 0x80482f0 <printf@plt>
0x08048461 <+69>: leave
0x08048462 <+70>: ret

Dizem que a instrução "

and	$0xfffffff0,%esp

" é para fazer o data alignment para ser mais rápido ou lá como é. Intel usa isto na tecnologia desde o pentium 3 acho eu, chama-se SIMD.

Mas ainda não entendi realmente o que se passa na stack. então no offset +6 do main está a fazer já o sub 0x20 do esp para reservar espaço para as variàveis porque é que é preciso fazer o stack alignment? Ainda não entendi onde isto mete sobre performance ao assunto :/

Estou disposto a ouvir qualquer um :D

Share this post


Link to post
Share on other sites
pwseo

Com a instrução and estás a alinhar a stack em limites de 16 bytes (que é obrigatório para usares instruções SIMD, caso contrário o teu programa irá segfaultar) -- isto permite que os dados sejam acedidos em endereços que são múltiplos de 16, e é isso que aumenta a performance (e impede os crashes caso uses SIMD).

Tendo em conta que a stack já foi previamente alinhada, basta reservar espaço para as variáveis locais em múltiplos de 16 bytes: tu tens 3 inteiros (presumivelmente, cada um ocupa 8 bytes), o que implica reservar 8×3 = 24 bytes de espaço na stack. Ora, 24 não é um múltiplo de 16, por isso arredonda-se para cima, para 32 (que é 0x20 em hexadecimal).

Penso que é isto; assembly também não é o meu forte.

  • Vote 1

Share this post


Link to post
Share on other sites
int3

Pois o interessante agora que percebi melhor é que o compilador mete sempre a reservar 0x20 de espaço em relação ao main.

Neste exemplo:

 short a=0xbeef;
printf("%d",a);

fica:

 0x0804841c <+0>: push %ebp
0x0804841d <+1>: mov %esp,%ebp
0x0804841f <+3>: and $0xfffffff0,%esp
0x08048422 <+6>: sub $0x20,%esp
0x08048425 <+9>: movw $0xbeef,0x1e(%esp)
0x0804842c <+16>: movswl 0x1e(%esp),%eax
0x08048431 <+21>: mov %eax,0x4(%esp)
0x08048435 <+25>: movl $0x80484e0,(%esp)
0x0804843c <+32>: call 0x80482f0 <printf@plt>
0x08048441 <+37>: leave
0x08048442 <+38>: ret 

nesse até usei um short que é de 16bits. supostamente devia de reservar ao certo nao?

EDIT: Esquece xD o short é de 16bits logo é tem 2 bytes. significa que se reservar 20h=32d ou seja se dividir fica 32/16=2, 2bytes :)

no "0x08048425 <+9>: movw $0xbeef,0x1e(%esp)" vê-se que está a colocar a 30bytes. mas assim sobra 2bytes de espaço na frame. é por isto que faz o alinhamento?

Edited by int3

Share this post


Link to post
Share on other sites
pwseo

Se tem 2 bytes, só precisarias de reservar 16 bytes. De facto não sei por que motivo o GCC reserva sempre pelo menos 32 bytes mesmo que 16 sejam suficientes. Se esquecermos esse pormenor, sabemos que é garantido que aumenta a stack em blocos de 16 bytes consoante as variáveis locais que declarares (isto, obviamente, depende da configuração do compilador, mas por defeito este alinha tudo a 16 bytes).

De qualquer modo, o código que mostras deixa 30 bytes livres entre a tua variável e %esp (e não apenas 2)... Não sei ao certo por que motivo são reservados logo 32 bytes quando a variável cabia perfeitamente num bloco de 16 -- se eu escrevesse isso à mão, faria algo como:

...
and $0xfffffff0, %esp
sub $16, %esp            ; 16 bytes chegam perfeitamente para um short de 2 bytes
movw $0xBEEF, 14(%esp)  ; "subimos" 14 bytes e escrevemos os nossos 2 bytes nos 2 restantes
...

Posto isto... Alguém que perceba de assembly provavelmente poderá esclarecer-nos melhor.

EDIT:

Ligando as optimizações, o GCC reserva apenas 16 bytes na stack.

Reparei também que há bocado te disse que os inteiros ocupavam 8 bytes, mas isso seria verdade se tivesses um processador de 64-bits. Agora que vi melhor, reparei que não é bem assim, uma vez que tens endereços que ocupam 4 bytes:

...
0x08048431 <+21>: mov %eax,0x4(%esp)
0x08048435 <+25>: movl $0x80484e0,(%esp)
0x0804843c <+32>: call 0x80482f0 <printf@plt>
...

Edited by pwseo

Share this post


Link to post
Share on other sites
int3

Exato, estou com linux mint de 32bits. embora diga "i686 GNU/Linux" e tenha 4GB de RAM sem PAE ainda não entendi como se está apenas a endereçar 32bits e consigo ter acesso a 4GB. talvez 2³² bits é 4GB. mas no windows apenas tenho 3GB se tiver 32bits outra coisa que não entendo do linux para o windows.

De qualquer forma o que acontece então se o endereço onde começa o main seja um múltiplo de 16? já não é necessário alinhar a stack? :)

Share this post


Link to post
Share on other sites
pwseo

Estamos a misturar coisas.

Primeiro, i686 é precisamente uma arquitectura de 32-bits, o que significa que consegues endereçar até 4GB de RAM (232bits). Em Linux, com PAE consegues ter acesso aos 4GB na sua totalidade, algo que não consegues com Windows a 32-bits.

Relativamente ao alinhamento, o pré-requisito é que a stack seja alinhada a 16-bytes. Os endereços das funções também podem ser alinhados a 16-bytes, mas não é disso que estávamos a falar, nem podemos assumir que o alinhamento das funções implica o alinhamento da stack ou vice-versa, penso eu.

Por outro lado, a minha experiência em assembly é extremamente reduzida, por isso gostaria de ver opiniões de outras pessoas também.

Share this post


Link to post
Share on other sites
int3

oh pois é. xD estava a misturar x64 com i686 ehehe

Também gostava de ver alguém que soubesse exatamente do que se trata :D

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.