3
votes

I have this C code portion:

#include <stdio.h>
void main()
{
int n, array[1000], c, d, t, e;
char step;

puts("Enter a number\n");
scanf("%d", &n);

puts("any help? [y/n]");
scanf(" %c",&step);

puts("Enter integers\n");
}

the corresponding assembly code of mine:

push prompt1
call _printf
add esp, 4        ;print and add 4 bytes to esp
push num
push sc1    
call _scanf
add esp, 8       ;scan and add 8 bytes to esp

section .data
prompt1 db "Enter a number",13,10,0
sc2     db " %c",0 ;other constants

And the resulting assembly code after converting the C file to NASM format in command prompt.

mov dword [esp], ?_001       ;?_001 is prompt1                    
call    _puts                                   
lea     eax, [esp+0FBCH]                        
mov     dword [esp+4H], eax                     
mov     dword [esp], ?_002   ;?_002 is sc1                 
call    _scanf                                  
mov     dword [esp], ?_003                  
call    _puts               

full assembly code: http://pastebin.com/R6UHRw8x However,I did not understand the converted assembly code because mov dword [esp+4H], eax uses [esp + 4] and the next line uses only [esp]. Shouldn't it be [esp] first then [esp + 4]? I don't understand and I saw many occurrences of it in the file. Besides push and mov, how is the generated code different from mine regarding esp?

1
Unrelated to your question and just for your information: When using scanf (and family) then any white-space in the format matches any and any number of white-space in the input. So you don't need to use "\n" to match an enter, a normal space " " works just as well. And that single space matched all consecutive white-space. - Some programmer dude
printf has been optimized to puts by the compiler. Replace all your printfs by puts and try again. - Jabberwocky
Please show the whole assembly code of the main function generated by the compiler. - Jabberwocky
I gather your target is Windows. The stack needs to be 16-bytes aligned when making any function call. The compiler has chosen to maintain proper alignment by determining the maximum number of parameters to any function call in your main is 2 DWORDS. It reserves bytes needed for all your local variables plus 2 DWORDS rounded to a 16-bye boundary. The total space allocated is 4032 which is divisible by 16. The stack is manually aligned to 16 bytes upon entry to main. The bottom 2 DWORDS of the currently allocated stack area get used as place for function call arguments. - Michael Petch
pushes are avoided to maintain proper stack alignment. mov's are used to put the values on the stack instead. Because push isn't used to place arguments on the stack (because the space is pre-allocated) it also doesn't need to add to esp after each call. - Michael Petch

1 Answers

3
votes

The compiler output passes scanf pointers to stack space, instead of to static storage.

It lets args build up on the stack instead of popping after every call (so it can use mov). Neither way avoids inserting stack-engine synchronization uops, unfortunately.

According to the ABI/calling convention, the first arg (in C order) is at the lowest address, just above the return address. So it's correct to put esp+0FBCH into [esp+0x4], and the format-string pointer into [esp]. See the tag wiki for links.