I have been struggling with an IA32 assembly language question I received during a university lab. I can't quite firgure out how to go about it, and was wondering if anyone here could give me a hand.
The question says: Draw the runtime stack at the following points during execution, starting from the parameter passed to fact_do and ending with the top of the stack. In each case label each 4 byte memory location and indicate the contents when known. Show where %ebp and %esp point to.
The point I can't quite draw the stack trace for is: "Just before the leave instruction in fact_do (leave is equivalent to movl %ebp, %esp followed by popl %ebp)."
I think what is expected is something along the lines of:
_____________
| | <- %ebp
| . |
| . |
| . |
|_____________|
|_5___________|
|_Rtn_Address_| <- %esp
The following is the code that is to be traced:
.file "fact.c"
.section .rodata
.LC0:
.string "%d! = %d\n" #string is initialized
.text
.globl main
.type main, #function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
movl $5, (%esp) #(%esp) <- 5 [(%esp) = n]
call fact_do
movl $.LC0, %edx #set up print statement
movl %eax, 8(%esp) #8(%esp) <- %eax [%eax = 120, return value of fact_do]
movl $5, 4(%esp) #4(%esp) <- 5
movl %edx, (%esp) #print statement location
call printf #print output
movl $0, %eax #%eax <- 0
leave
ret
.size main, .-main
.globl fact_do
.type fact_do, #function
fact_do:
pushl %ebp #push %ebp onto stack
movl %esp, %ebp #%ebp <- %esp
subl $16, %esp #%esp <- %esp - 16
movl $1, -4(%ebp) #-4(%ebp) <- 1 [-4(%ebp) = result]
.L4:
movl -4(%ebp), %eax #%eax <- -4(%ebp)
imull 8(%ebp), %eax #%eax <- %eax * 8(%ebp) [8(%ebp) = n]
movl %eax, -4(%ebp) #-4(%ebp) <- %eax [result <- result*n]
subl $1, 8(%ebp) #8(%ebp)-- [n--]
cmpl $1, 8(%ebp) #8(%ebp) > 1 ? [n > 1]
jg .L4 #Repeat loop if 8(%ebp) > 1
movl -4(%ebp), %eax #%eax <- -4(%ebp) [%eax <- 120, result]
leave
ret
.size fact_do, .-fact_do
.ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-4)"
.section .note.GNU-stack,"",#progbits
Optimized:
.file "fact.c"
.text
.globl fact_do
.type fact_do, #function
fact_do:
pushl %ebp #push %ebp (for return)
movl %esp, %ebp #%ebp <- %esp
movl 8(%ebp), %edx #%edx <- 8(%ebp) [%edx = n]
movl $1, %eax #%eax <- 1 [%eax = result]
.L2:
imull %edx, %eax #%eax <- %edx * %eax [result <- n * result]
subl $1, %edx #%edx-- [n--]
cmpl $1, %edx #%edx > 1 ? [n > 1]
jg .L2 #repeat if %edx > 1 [if n > 1]
popl %ebp #%ebp <- top value on stack (for return)
ret
.size fact_do, .-fact_do
.section .rodata.str1.1,"aMS",#progbits,1
.LC0:
.string "%d! = %d\n" #prepare string
.text
.globl main
.type main, #function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
movl $5, (%esp) #%esp <- 5
call fact_do
movl %eax, 8(%esp) #8(%esp) <- %eax [%eax = result]
movl $5, 4(%esp) #4(%esp) <- 5
movl $.LC0, (%esp) #set up print statement
call printf #print
movl $0, %eax #%eax <- 0
leave
ret
.size main, .-main
.ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-4)"
.section .note.GNU-stack,"",#progbits
Which translates to the following in C:
#include <stdio.h>
int fact_do(int n);
int main(){
printf("%d! = %d\n", 5, fact_do(5));
return 0;
}
int fact_do(int n){
int result = 1;
do {
result *= n;
n = n-1;
} while (n > 1);
return result;
}
Related
I am writing a problem to calculate smallest common multiple in assembly. GDB shows sigsegv exception at the very first line pushl %ebp. Can you tell me how to fix this?
test.c
#include <stdio.h>
extern int nww(int a, int b); // Funkcja z asm
int main()
{
int a = 10, b = 3;
int nw= nww(a,b);
printf("NWW %d i %d = %d ",a,b,nw);
return 0;
}
nww.s
.text
.global nww
.type nww, #function
nww:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
movl 12(%ebp), %ebx
nwd:
movl $0, %edx # miejsce na reszte
divl %ebx # podziel a przez b, reszta w d
movl %ebx, %eax
movl %edx, %ebx # przygotowanie nastepnego dzielenia
cmp $0, %edx
jnz nww
movl %eax, %edx # NWD do dx
movl 8(%ebp), %eax # nowe A i B
movl 12(%ebp), %ebx
imull %ebx, %eax
divl %edx
exit:
movl %ebp, %esp
popl %ebp
ret
.file "calcnew.c"
.text
.globl calcnew
.type calcnew, #function
calcnew:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %edx
movl 12(%ebp), %ecx
leal 0(,%ecx,8), %eax
subl %ecx, %eax
leal (%edx,%edx,2), %edx
addl %edx, %eax
imull $14, 16(%ebp), %edx
addl %edx, %eax
popl %ebp
ret
.size calcnew, .-calcnew
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",#progbits
I want to remove all leal and imull operations in this assembly code and replace their functionality with only sall addl and subl operations. Here is my attempt:
.file "calcnew.c"
.text
.globl calcnew
.type calcnew, #function
calcnew:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %edx
movl 12(%ebp), %ecx
sall $3, %ecx
movl %ecx, %eax
;leal 0(,%ecx,8), %eax
subl %ecx, %eax
movl %edx, %ecx
sall $1, %edx
addl %ecx, %edx
;leal (%edx,%edx,2), %edx
addl %edx, %eax
movl 16(%ebp), %edx
movl %edx, %ecx
sall $4, %edx
sall $1, %ecx
subl %ecx, %edx
;imull $14, 16(%ebp), %edx
addl %edx, %eax
popl %ebp
ret
.size calcnew, .-calcnew
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",#progbits
The output when compiling my c code with the new assembly file should be the same as compiling with the original assembly file (using leal and mull operations), however, my output is wrong. What did I do wrong?
Here is the C code calling the assembler file:
#include <stdio.h>
int calcnew(int x, int y, int z);
int main()
{
int x = 2;
int y = 6;
int z = 11;
int result;
result = calcnew(x,y,z);
printf("x=%d, y=%d, z=%d, result=%d\n",x,y,z,result);
}
You replace
leal 0(,%ecx,8), %eax
With
sall $3, %ecx
movl %ecx, %eax
But this clobbers the value of ecx, which is live out of those instructions. Instead:
movl %ecx, %eax
sall $3, %eax
.file "calc.c"
.text
.globl calc
.type calc, #function
calc:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %edx
movl 16(%ebp), %ecx
leal (%edx,%edx,2), %edx
movl 12(%ebp), %eax
leal (%edx,%eax,2), %eax
movl %ecx, %edx
sall $4, %edx
subl %ecx, %edx
addl %edx, %eax
popl %ebp
ret
.size calc, .-calc
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",#progbits
I'm trying to understand what's going on with this assembly code. I created it by typing gcc -O1 -S calc.c which generated a calc.s assembly file.
Can someone explain (in terms of the addition and multiplication in calc.c) what is going on, line by line?
The original C code is:
int calc(int x, int y, int z)
{
return 3*x + 2*y + 15*z;
}
Ok, now it does something, I'll annotate it for you
calc:
pushl %ebp ; \
movl %esp, %ebp ; / set up basic stack frame
movl 8(%ebp), %edx ; load x
movl 16(%ebp), %ecx ; load z
leal (%edx,%edx,2), %edx ; calculate x + 2 * x
movl 12(%ebp), %eax ; load y
leal (%edx,%eax,2), %eax ; calculate (x + 2 * x) + (2 * y)
movl %ecx, %edx ; make a temp copy of z
sall $4, %edx ; calculate z * 16
subl %ecx, %edx ; calculate (z * 16) - z
addl %edx, %eax ; calculate final sum
popl %ebp
ret
I'm trying to understand the assembly code during a recursive function call.
#include<stdio.h>
int recursive(int no){
if(no > 1){
no--;
recursive(no);
printf("\n %d \n",no);
}
else if(no == 1){
return 1;
}
}
int main(){
int a = 10;
recursive(a);
return 0;
}
disassembly :
.file "sample2.c"
.section .rodata
.LC0:
.string "\n %d \n"
.text
.globl recursive
.type recursive, #function
recursive:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
cmpl $1, 8(%ebp)
jle .L2
subl $1, 8(%ebp)
movl 8(%ebp), %eax
movl %eax, (%esp)
call recursive
movl $.LC0, %eax
movl 8(%ebp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
jmp .L5
.L2:
cmpl $1, 8(%ebp)
jne .L5
movl $1, %eax
movl %eax, %edx
movl %edx, %eax
jmp .L4
.L5:
.L4:
leave
ret
.size recursive, .-recursive
.globl main
.type main, #function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $10, 28(%esp)
movl 28(%esp), %eax
movl %eax, (%esp)
call recursive
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
.section .note.GNU-stack,"",#progbits
I could understand .LC0 always holds the string literals. But I dont know what it really means. Would like to understand the code during the function call recursion was made.
I could not understand what this piece of assembly code does,
subl $24, %esp
cmpl $1, 8(%ebp)
jle .L2
subl $1, 8(%ebp)
movl 8(%ebp), %eax
movl %eax, (%esp)
call recursive
movl $.LC0, %eax
movl 8(%ebp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
jmp .L5
.L2:
cmpl $1, 8(%ebp)
jne .L5
movl $1, %eax
movl %eax, %edx
movl %edx, %eax
jmp .L4
Q1:
The recursive function contains 1 parameter. so after the padding alignment, it has to be 8. why is it 24.
Also in .L2 ,
movl $1, %eax
movl %eax, %edx
movl %edx, %eax
jmp .L4
Q2:
we have moved '1' to the accumulater, why are we moving again to data register and then back to the accumulator.
Q3:
Are we popping out of stack. If leave is used for popping out of stack, are we not popping the rest of the 8 stack frames ?
To answer the only thing in your post that matches your title:
Why are we not popping out from the stack and only push instruction in the assembly.
Because leave is equivalent to:
movl %ebp, %esp
popl %ebp
The C codes is:
int rSum(int *Start, int Count)
{
if (Count <= 0)
return 0;
return *Start + rSum(Start+1, Count-1);
}
the corresponding Assemble codes is:
.file "test98.c"
.text
.globl rSum
.type rSum, #function
rSum:
pushl %ebp
movl %esp, %ebp
pushl %ebx
subl $20, %esp
cmpl $0, 12(%ebp)
jg .L2
movl $0, %eax
jmp .L3
.L2:
movl 8(%ebp), %eax
movl (%eax), %ebx
movl 12(%ebp), %eax
leal -1(%eax), %edx
movl 8(%ebp), %eax
addl $4, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call rSum
leal (%ebx,%eax), %eax
.L3:
addl $20, %esp
popl %ebx
popl %ebp
ret
.size rSum, .-rSum
.ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
.section .note.GNU-stack,"",#progbits
I can't understand this instrutions "subl $20, %esp", and Why is $20 ?
subl $20, %esp subtracts 20 from the stack pointer (esp). This allocates 20 bytes of space on the stack that can be used for local variables.