• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1Getting Started with VIXL for AArch32
2=====================================
3
4
5This guide will show you how to use the VIXL framework for AArch32. We will see
6how to set up the VIXL assembler and generate some code. We will also go into
7details on a few useful features provided by VIXL and see how to run the
8generated code.
9
10The source code of the example developed in this guide can be found in the
11`examples/aarch32` directory (`examples/aarch32/getting-started.cc`).
12
13
14Creating the macro assembler.
15-----------------------------
16
17First of all you need to make sure that the header files for the assembler are
18included. You should have the following lines at the beginning of your source
19file:
20
21    // You may use <cstdint> if using C++11 or later.
22    extern "C" {
23    #include <stdint.h>
24    }
25
26    #include <cstdio>
27    #include <string>
28    #include "aarch32/constants-aarch32.h"
29    #include "aarch32/instructions-aarch32.h"
30    #include "aarch32/macro-assembler-aarch32.h"
31
32In our case, those files are included by "examples.h".
33
34All VIXL components are declared in the `vixl::aarch32` namespace, so let's add
35this to the beginning of the file for convenience (once again, done in
36"examples.h"):
37
38    using namespace vixl::aarch32;
39
40Now we are ready to create and initialise the different components.
41
42First of all we need to create a macro assembler object.
43
44    MacroAssembler masm;
45
46
47Generating some code.
48---------------------
49
50We are now ready to generate some code. The macro assembler provides methods
51for all the instructions that you can use. As it's a macro assembler,
52the instructions that you tell it to generate may not directly map to a single
53hardware instruction. Instead, it can produce a short sequence of instructions
54that has the same effect.
55
56Before looking at how to generate some code, let's introduce a simple but handy
57macro:
58
59    #define __ masm->
60
61It allows us to write `__ Mov(r0, 42);` instead of `masm->Mov(r0, 42);` to
62generate code.
63
64Now we are going to write a C++ function to generate our first assembly
65code fragment.
66
67    void GenerateDemo(MacroAssembler *masm) {
68      __ Ldr(r1, 0x12345678);
69      __ And(r0, r0, r1);
70      __ Bx(lr);
71    }
72
73The generated code corresponds to a function with the following C prototype:
74
75    uint32_t demo(uint32_t x);
76
77This function doesn't perform any useful operation. It loads the value
780x12345678 into r1 and performs a bitwise `and` operation with
79the function's argument (stored in r0). The result of this `and` operation
80is returned by the function in r0.
81
82Now in our program main function, we only need to create a label to represent
83the entry point of the assembly function and to call `GenerateDemo` to
84generate the code.
85
86    Label demo;
87    masm.Bind(&demo);
88    GenerateDemo(&masm);
89    masm.Finalize();
90
91Now we are going to learn a bit more on a couple of interesting VIXL features
92which are used in this example.
93
94### Label
95
96VIXL's assembler provides a mechanism to represent labels with `Label` objects.
97They are easy to use: simply create the C++ object and bind it to a location in
98the generated instruction stream.
99
100Creating a label is easy, since you only need to define the variable and bind it
101to a location using the macro assembler.
102
103    Label my_label;      // Create the label object.
104    __ Bind(&my_label);  // Bind it to the current location.
105
106The target of a branch using a label will be the address to which it has been
107bound. For example, let's consider the following code fragment:
108
109    Label foo;
110
111    __ B(&foo);     // Branch to foo.
112    __ Mov(r0, 42);
113    __ Bind(&foo);  // Actual address of foo is here.
114    __ Mov(r1, 0xc001);
115
116If we run this code fragment the `Mov(r0, 42)` will never be executed since
117the first thing this code does is to jump to `foo`, which correspond to the
118`Mov(r1, 0xc001)` instruction.
119
120When working with labels you need to know that they are only to be used for
121local branches, and should be passed around with care. The major reason is
122that they cannot safely be passed or returned by value because this can trigger
123multiple constructor and destructor calls. The destructor has assertions
124to check that we don't try to branch to a label that hasn't been bound.
125
126
127### Literal Pool
128
129On AArch32 instructions are 16 or 32 bits long, thus immediate values encoded in
130the instructions have limited size. If you want to load a constant bigger than
131this limit you have two possibilities:
132
1331. Use multiple instructions to load the constant in multiple steps. This
134  solution is already handled in VIXL. For instance you can write:
135
136  `__ Mov(r0, 0x12345678);`
137
138  The previous instruction would not be legal since the immediate value is too
139  big. However, VIXL's macro assembler will automatically rewrite this line into
140  multiple instructions efficiently generate the value, ultimately setting 'r0'
141  with the correct value.
142
143
1442. Store the constant in memory and load this value from the memory. The value
145  needs to be written near the code that will load it since we use a PC-relative
146  offset to indicate the address of this value. This solution has the advantage
147  of making the value easily modifiable at run-time; since it does not reside
148  in the instruction stream, it doesn't require cache maintenance when updated.
149
150  VIXL also provides a way to do this:
151
152  `__ Ldr(r0, 0x12345678);`
153
154  The assembler will store the immediate value in a "literal pool", a set of
155  constants embedded in the code. VIXL will emit the literal pool when needed.
156
157  The literal pool is emitted regularly, such that they are within range of the
158  instructions that refer to it. However, you can force the literal pool to be
159  emitted using `masm.EmitLiteralPool()`. It generates a branch to skip the
160  pool.
161
162
163Running the code.
164-----------------
165
166We first need to run a few operations to get executable code. The
167`ExecutableMemory` helper takes care of it:
168
169    byte* code = masm.GetBuffer().GetBuffer();
170    uint32_t code_size = masm.GetBuffer().GetSizeInBytes();
171    ExecutableMemory memory(code, code_size);
172
173Then we compute a pointer to the function we just generated and copy:
174
175    uint32_t (*demo_function)(uint32_t) =
176        memory.GetOffsetAddress<uint32_t (*)(uint32_t)>(0);
177
178Now, we can call this function pointer exactly as if it were a pointer on a C
179function:
180
181    uint32_t input_value = 0x89abcdef;
182    uint32_t output_value = (*demo_function)(input_value);
183
184A little trace:
185
186    printf("native: abs(%08x) = %08x\n", input_value, output_value);
187
188
189The example shown in this tutorial is very simple, because the goal was to
190demonstrate the basics of the VIXL framework. There are more complex code
191examples in the VIXL `examples/aarch32` directory showing more features of both the
192macro assembler and the AArch32 architecture.
193
194Disassembling the generated code.
195---------------------------------
196
197Once you have generated something with the macro-assembler, you may want to
198disassemble it.
199
200First, you must include iostream.
201
202    #include <iostream>
203
204And the disassembler header file:
205
206    #include "aarch32/disasm-aarch32.h"
207
208Then you have to define the pc used to disassemble (the one which is used to
209display the addresses not the location of the instructions):
210
211    uint32_t display_pc = 0x1000;
212
213Or, if you running on a 32 bit host, you can use the real address:
214
215    uint32_t display_pc = static_cast<uintptr_t>(masm.GetBuffer().GetBuffer());
216
217Then you can disassemble the macro assembler's buffer:
218
219    PrintDisassembler disasm(std::cout, display_pc);
220    disasm.DisassembleA32Buffer(
221        masm.GetBuffer().GetOffsetAddress<uint32_t*>(0), masm.GetCursorOffset());
222
223If you generated T32 code instead of A32 code, you must use
224DisassembleT32Buffer. Warning: if your buffer contains some data or contains
225mixed T32 and A32 code, the result won't be accurate (everything will be
226disassembled as T32 or A32 code).
227
228Example of disassembly:
229
230    0x00001000  e30f0fff	mov r0, #65535
231    0x00001004  e34f0fff	movt r0, #65535
232    0x00001008  e3041567	mov r1, #17767
233    0x0000100c  e3401123	movt r1, #291
234    0x00001010  e3a02000	mov r2, #0
235    0x00001014  e7c2001f	bfc r0, #0, #3
236    0x00001018  e7d4081f	bfc r0, #16, #5
237    0x0000101c  e7c72011	bfi r2, r1, #0, #8
238    0x00001020  e7df2811	bfi r2, r1, #16, #16
239    0x00001024  e1000070	hlt 0
240