Disclaimer
This is not intended to be a guide towards using inline assembly, and therefore does not cover the topic in full. This can be treated as merely a motivation towards using inline assembly. To explore the topic in depth, please read this.
I am, and always was really scared of assembly language. When I was an under-grad student, my x86 assignments were mostly codes written in C and submission of the disassembly. Of course, with C being pretty low-level, one does not need to dirty their hands (no offence meant to assembly programmers) with the syntax of mnemonics and opcodes.
However, there are times when C fails to meet some requirements of a programmer. An example has already been put up in my last post, where a integer variable needs to be rotated bitwise. Or to perform particularly arithmetic or logical shifts to a variable, since the bitwise right shift operation in C (>>) is implementation specific.
This is where inline assembly saves the day for a C programmer. A few lines of assembly code can be embedded effortlessly in a C program to carry out specific architecture dependent tasks. The basic format for inline assembly is
__asm__ volatile ( "your first assembly statement here\n\t"
"your second assembly statement here\n\t" )
Of course I could have used the keyword asm instead of __asm__ , and the volatile keyword specifies that since I am using inline assembly, I know exactly what I intend to do, so no optimizing tricks, please!
So, now I can go for a logical shift to 0xAAAAAAAA to obtain 0x55555555, and I can be sure that I get what I want. A small code for this can be written as
1: #include <stdio.h>
2:
3: int var1;
4: int var2;
5:
6: int main(){
7: var=0xaaaaaaaa;
8: __asm__ volatile ("pushl %eax\n\t"
9: "pushl %ebx\n\t"
10: "mov $var1, %ebx\n\t"
11: "movl (%ebx), %eax\n\t"
12: "shrl $1, %eax\n\t"
13: "mov $var2, %ebx\n\t"
14: "mov %eax, (%ebx)\n\t"
15: "popl %ebx\n\t"
16: "popl %eax\n\t"
17: );
18:
19: printf("%p after 1 bit logical shift = %p\n",(unsigned char *)var1, (unsigned char *)var2);
20: }
and the output is certainly
Needless to say, the size of the main function is increased significantly due to this. Much can be accounted to the push and pop statements (which are required to save the registers to be used in the inline assembly) and moving the data (0xAAAAAAAA in this case) to and from memory. Also, the variable var was declared global for making it accessible to the assembly code.
To get around these shortcomings, we generally use extended assembly, and the same code can there be written as
1: #include <stdio.h>
2:
3: int main(){
4: int var1=0xaaaaaaaa,var2; /*local variables now*/
5:
6: __asm__ volatile ("movl %0, %%eax\n\t"
7: "shrl $1, %%eax\n\t"
8: "movl %%eax, %1\n\t"
9: :"=r"(var2):"r"(var1):"%eax"
10: );
11:
12: printf("%p after 1 bit logical shift = %p\n",(unsigned char *)var1, (unsigned char *)var2);
13: }
to obtain
What was the code, anyway? Well, lets explain it in bits and pieces.
- First of all, the %0 and %1 are placeholders. %0 is filled in from input variable var1 which will be stored in a register, as denoted by the constraint "r" preceding it.
- %1 placeholder represents an output variable in register (denoted by "=r") and will be placed in var2. The registers to be used for storing the variables is decided in compile time (in my case it was edx).
- In the entire operation, only the value placed in eax will be tinkered with, and hence it is placed in the list of clobbered registers. This instructs gcc not to use eax in other parts of the code, or to save the value before entering and restore the value after leaving the assembly code.
The disassembly of the main function looks like this
1: pushl %ebp
2: movl %esp, %ebp
3: andl $-16, %esp
4: subl $32, %esp
5: movl $-1431655766, 28(%esp)
6: movl 28(%esp), %edx
7:
8: movl %edx, %eax
9: shrl $1, %eax
10: movl %eax, %edx
11:
12: movl %edx, 24(%esp)
13: movl 24(%esp), %ecx
14: movl 28(%esp), %edx
15: movl $.LC0, %eax
16: movl %ecx, 8(%esp)
17: movl %edx, 4(%esp)
18: movl %eax, (%esp)
19: call printf
20: leave
21: ret
Hope this motivates you towards inline assembly. For a complete understanding, please read here. For a complete reference to x86 instructions and opcodes, check this out.
No comments:
Post a Comment