what's the meaning of this instructions(subl $20, %esp)? - c

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.

Related

What do these 2 instructions do in the following function?

So I'm pretty much a noob in IA32 assembly language. I tried compiling this C function into IA32 assembly (-mpreferred-stack-boundary=2):
__attribute__((cdecl))
int odd_sum(int a[], int n, int sum) {
if (n == 0) return sum;
else if ((a[n-1] % 2) == 0)
return odd_sum(a, n-1, sum);
else return odd_sum(a, n-1, sum + a[n-1]);
}
and the GCC outputs this:
.file "test.c"
.text
.globl _odd_sum
.def _odd_sum; .scl 2; .type 32; .endef
_odd_sum:
LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $12, %esp
cmpl $0, 12(%ebp)
jne L2
movl 16(%ebp), %eax
jmp L3
L2:
movl 12(%ebp), %eax
addl $1073741823, %eax
leal 0(,%eax,4), %edx
movl 8(%ebp), %eax
addl %edx, %eax
movl (%eax), %eax
andl $1, %eax
testl %eax, %eax
jne L4
movl 12(%ebp), %eax
leal -1(%eax), %edx
movl 16(%ebp), %eax
movl %eax, 8(%esp)
movl %edx, 4(%esp)
movl 8(%ebp), %eax
movl %eax, (%esp)
call _odd_sum
jmp L3
L4:
movl 12(%ebp), %eax
addl $1073741823, %eax
leal 0(,%eax,4), %edx
movl 8(%ebp), %eax
addl %edx, %eax
movl (%eax), %edx
movl 16(%ebp), %eax
addl %eax, %edx
movl 12(%ebp), %eax
subl $1, %eax
movl %edx, 8(%esp)
movl %eax, 4(%esp)
movl 8(%ebp), %eax
movl %eax, (%esp)
call _odd_sum
L3:
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE0:
.ident "GCC: (MinGW.org GCC-8.2.0-3) 8.2.0"
What I am not able to comprehend are these 2 lines:
addl $1073741823, %eax
leal 0(,%eax,4), %edx
I understand those 2 lines should have something to do with the a[n-1], but I can't seem to be able to understand what exactly they do in the process. Can someone help me with this problem please?
It is just a fancy way of computing the offset into the array a[n-1].
1073741823 is 0x3fffffff. If n is 3, for example, it will add them and get 0x40000002. Then it multiplies by 4 with the second instruction, which results in 0x00000008, discarding the top bits.
So we are left with an offset of 8 bytes, which is exactly the offset (in bytes) that you need for a[n-1], i.e. a[2] (when the size of an int is 4 bytes).
To get a more understandable output with the -S flag:
create assembler code:
c++ -S -fverbose-asm -g -O2 (other optimizaton flags) test.cc -o test.s
create asm interlaced with source lines:
as -alhnd test.s > test.lst

IA32 assembly language stack trace

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;
}

editing this assembly code to only use add/subtract/shift

.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

Allocation of bytes for local variables of a function and Is the POP instruction called only once

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

Assembly language program to find the GCD - Floating point exception

With this program, I am intending to find the GCD of two numbers. But the result I get is "Floating point exception(core dumped)". What is the problem?
The code I am trying to generate is
int main() {
int sml, lrg, rem;
read %d sml
read %d lrg
while (sml > 0){
rem = lrg % sml;
lrg = sml;
sml = rem;
}
print %d lrg;
return 0;
}
The assembly file generated by me is:
.file "gcd.c"
.section .rodata
.LC0:
.string "%d"
.LC1:
.string "%d\n"
.text
.globl main
.type main, #function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
leal -8(%ebp), %eax #scan a value
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call scanf
leal -12(%ebp), %eax #scan a value
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call scanf
.L2:
movl $0, %eax
cmpl -8(%ebp),%eax
jle .L0
jmp .L1
.L0:
movl -12(%ebp),%eax
movl -8(%ebp),%ecx
movl %eax,%edx
sarl $31, %edx
idivl %ecx
movl %edx,%eax
movl %eax, -16(%ebp)
movl -8(%ebp),%edx
movl %edx, -12(%ebp)
movl -16(%ebp),%edx
movl %edx, -8(%ebp)
jmp .L2
.L1:
movl -12(%ebp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call printf
movl $0, %edx
movl $0, %eax #end of program
leave
ret
.LFE2:
.size main, .-main
.ident "GCC: (GNU) 4.2.3 (4.2.3-6mnb1)"
.section .note.GNU-stack,"",#progbits
On the other hand, this assembly code works
.file "check.c"
.section .rodata
.LC0:
.string "%d"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $32, %esp
leal 20(%esp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call scanf
leal 24(%esp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call scanf
jmp .L2
.L3:
movl 24(%esp), %eax
movl 20(%esp), %ecx
movl %eax, %edx
sarl $31, %edx
idivl %ecx
movl %edx, 28(%esp)
movl 20(%esp), %eax
movl %eax, 24(%esp)
movl 28(%esp), %eax
movl %eax, 20(%esp)
.L2:
movl 20(%esp), %eax
testl %eax, %eax
jg .L3
movl 24(%esp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call printf
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",#progbits
You probably have a division by zero in this line:
idivl %ecx
With a 0 value in ecx register.
Your comparison and jump are wrong. The working code has:
testl %eax, %eax
jg .L3
The broken code has:
movl $0, %eax
cmpl -8(%ebp),%eax
jle .L0
jmp .L1
The former compares %eax (which contains the most recently computed residue) to zero, and it continues the loop (jumps to .L3) if the residue is positive (greater than zero).
The latter compares 0 to -8(%ebp) (the most recently computed residue). Note that the order is different; it compares 0 to -8(%ebp), not -8(%ebp) to 0. The testl instruction compares the value (after performing an AND) with zero. If the value is positive, it is “greater than” zero. The cmpl instruction compares its second operand to its first operand; if the second operand exceeds the first, the result is “greater than”. This is because, in Intel’s manuals and assembly language, instructions are written with their operands in the reverse order. E.g, moving 3 into %eax would be “mov %eax, $3”. However, the assembler you are using reverses all the operands from Intel’s order (due to legacy reasons).
So, the broken code continues the loop (jumps to .L0) if 0 is less than or equal to the residue. Thus, if the residue is zero, the loop continues. You can change the jle to jl:
jl .L0
Alternately, you could eliminate the redundant unconditional jump:
movl $0, %eax
cmpl -8(%ebp),%eax
jge .L1
Also, you probably want to change:
movl $.LC0, (%esp)
call _printf
to:
movl $.LC1, (%esp)
call _printf
so that you pass "%d\n" to printf instead of passing "%d".
Incidentally, the idivl instruction generates a divide error, not a floating-point exception. Your system is misreporting the error.

Resources