Understanding assembly generated by C function call - c

.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

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

Segmentation fault when pushing to stack in assembly

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

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

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

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.

Resources