This article would study ARM instruction set and how special instruction works and how to implement the function call.
Thumb VS ARM
In this section, I will compare ARM instructions and Thumb instructions. First of all, I will present the code analysed.
1 |
|
And I utilized the cross-compile tools in Ubuntu
to compile the code.
1 | arm-linux-gnueabi-gcc test.c -o test.a |
Secondly, use the objdump
tool in cross-compile kit to inspect the assemble code.
1 | arm-linux-gnueabi-objdump -d test.a |
ARM code
1 | 0000840c <main>: |
As you can see, all instruction compose of 32 bits.
Thumb code
1 | 0000840c <main>: |
The instruction consist of 16 bits as our wish. Compare with ARM instructions, this code is only half length. But the number of instructions is similar. That means similar execution time but much shorter program length.
1 | -rwxrwxr-x 1 sea sea 8346 Mar 31 07:08 test.a |
However, the Thumb code executable is bit longer. Of course, the main part of a executable is other code which is prepared for operating system, that is ARM instructions.
Condition Instruction
In this section I will present how to construct the condition execution instructions. Because the compiler would generate normal instruction as a default approach, the optimization option must be galvanized. Of course, the target code must be demonstrated at first.
1 |
|
And the compile script.
1 | arm-linux-gnueabi-gcc test.c -o test.a -O |
Now we could see the condition instructions.
1 | 0000849c <main>: |
Register Shift
Let’s construct scenario that would generate register shift instruction. Target code is here.
1 | int main(void){ |
Of course, in order to avoid the compiler generate normal instruction we must use the optimization option. However, the optimization would eliminate every code because of the unuse of variable. Thus, input and output is introduced.
1 | arm-linux-gnueabi-gcc test.c -o test.a -O |
Dump it, check the machine code.
1 | 0000849c <main>: |
Load a 32-bits number
Now, I will show how the assigment is implemented.
1 |
|
Variable int a
would be assigned by a 32-bits number. Compile it, and check the assemble language.
1 | 00008494 <main>: |
As you can see, the constant is stored in bottom of code. And the content in the memory would be load into register directly by using instruction ldr
.
Function Call
Function call is a important part in every program. So that, I will study the function call mechanism in this section.
Let’s present the target code as ususal.
1 |
|
Then, compile it and inspect the machine code.
1 | 00008510 <f>: |
Return Address
The return address would be stored in register lr
when the instruction bl
is executed. And whenever the call is invoked, the return address would be pushed into stack.
Parameter Passing
The parameter is passed through register R0
and R1
.
Local Variable
The variable would be allocated when the function call is made. First of all, the local stack space woudl be allocated by frame pointer. After the return address is pushed to the stack, the local variable would stored sequentially.
Reigster Save
R0-R3 is saved by caller but the register above R4 is saved by callee.
BIC
In order to obtin the BIC instruction, we must do some operation that is similiar to the logic of this instruction.
1 |
|
And, the optimazation option is necessary.
1 | arm-linux-gnueabi-gcc test.c -o test.a -O |
Then, we could find out the BIC instruction.
1 | 0000849c <main>: |
Inline Assembly
In this section, I write a pure ARM assembly function. There is one thing deserved to be mentioned, which is we must store and load the return address, so that the function would be executed properly.
1 |
|
And I invoke the function puts
to print the string.