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

.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

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

Understanding assembly generated by C function call

.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

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

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.

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