Question

I want to write the following C code in Assembly:

int main(void)
{
    int x,y;
    scanf("%d%d",&x,&y);
    printf("%d%d",x,y);
    return 0;
}

Firstly I tried it with only one integer to scan/print:

.section    .rodata #read only data section
fmt:    .string "%d%d"
    .text   
.globl  main    
    .type   main, @function
main:   
pushl   %ebp    #save the old frame pointer
movl    %esp,   %ebp    #create the new frame pointer

pushl %esp #location of x
pushl $fmt
call scanf

    #stack should now have exactly the scanned number x and then the format, as needed for printf.
call printf

movl    $0, %eax
movl    %ebp,   %esp    #restore the old stack pointer - release all used memory.
popl    %ebp    #restore old frame pointer (the caller function frame)
ret 

But it did not work. For some reason, the following trick made it work (added before the printf):

addl $4,%esp #pop format
pushl 4(%esp)
pushl $fmt

I don't understand why would pushl 4(%esp) make it work, so for my first question I am asking for a clarification on that matter. Then I tried doing the same with two variables:

fmt:    .string "%d%d"
[...]
    pushl %esp #location of x
    pushl %esp #location of y
    pushl $fmt
    call scanf

But it caused a segmentation error. It didn't even reached the printf part, where I would have tried something like this:

addl $4,%esp #pop format    
pushl 8(%esp)
pushl 8(%esp)
pushl $fmt

call printf

(Following the same logic as with the pushl 4(%esp) before. So my second question is how can I make it work with two variables. Thank you!

edit: Why would the following code won't work for scanning two variables?

subl $8,%esp #leave place for two vars
pushl -4(%ebp) #location of x
pushl -8(%ebp) #location of y
pushl $fmt
call scanf
Was it helpful?

Solution

"It should subtract %esp by 4 and then save %esp at the location esp is pointing to"

That would have happened on 8086 CPU.From 80286, it stores ESP internally, then subtracts, then writes value stored internally. You have to allocate variable first (one push or one esp-4) then store address of this variable (second push). For first question you have to make 3 pushes or one sub and 2 pushes. In your case, esp points to stack location where old EBP was stored. You can use

push eax
push esp
push fmt

this would work as well.

Also, about the second problem, you referred to the lines which didn't even matter,

Oh yeah, I copied over wrong code lines, sorry. I was reffering to this piece:

pushl %esp #location of x
pushl %esp #location of y
pushl $fmt
call scanf

I pointed out, why your code is incorrect. You have to push 2 addresses of variables. Instead you push address of old EBP, then address of cell in stack with prev parameter (that points to old EBP); As result, on read one parameter get messed up, receiving value you entered on scanf. When it wants to write another value, instead of address of a cell, it has previous int.

Lastly, could you explain your suggested code? Why would I move edx and eax into esp, I don't even know what's in them

Sorry, this is an Intel syntax, hence mov eax, esp means "write esp to eax". It is not a very good code, indeed. Just for an example.

I allocate one variable on stack, get address of it in eax. Then allocate another var, store its address in edx. Then push both addresses, then push offset of fmt.

You have to allocate space first. And you do not need frame unless you are going to address local vars using EBP relative addresses. You can push ebp - 4 etc. Just compile your code and look how it works in any debugger (I used ollydbg to check your code); Finally, you can ask C compiler to generate asm listing and look, how compiler does this.

OTHER TIPS

With this:

pushl %esp #location of x
pushl $fmt
call scanf

you overwrite EBP.

First, CPU remembers value of register (old esp value), then subtracts 4, then saves old ESP value. In this case, this is old EBP. When you first subtract 4, you allocate a variable on stack (better to PUSH EAX - it is shorter, 1 byte only);

Similar problem with second case:

addl $4,%esp #pop format    
pushl 8(%esp)
pushl 8(%esp)
pushl $fmt

Here first parameter points not to X, but to second parameter. Second points to EBP. You have to allocate variables on stack first:

push ebp
mov ebp, esp
push eax
mov edx, esp
push eax
mov eax, esp
push eax
push edx
push offset fmt
call xyz

And even more: if you are not working with local vars, you do not need to push ebp, no need in creating frame pointer. Alternatively, after allocating vars on stack, you could use this:

LEA eax, [EBP - 4]
LEA edx, [EBP - 8]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top