1 Getting Started with VIXL for AArch32 2 ===================================== 3 4 5 This guide will show you how to use the VIXL framework for AArch32. We will see 6 how to set up the VIXL assembler and generate some code. We will also go into 7 details on a few useful features provided by VIXL and see how to run the 8 generated code. 9 10 The 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 14 Creating the macro assembler. 15 ----------------------------- 16 17 First of all you need to make sure that the header files for the assembler are 18 included. You should have the following lines at the beginning of your source 19 file: 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 32 In our case, those files are included by "examples.h". 33 34 All VIXL components are declared in the `vixl::aarch32` namespace, so let's add 35 this to the beginning of the file for convenience (once again, done in 36 "examples.h"): 37 38 using namespace vixl::aarch32; 39 40 Now we are ready to create and initialise the different components. 41 42 First of all we need to create a macro assembler object. 43 44 MacroAssembler masm; 45 46 47 Generating some code. 48 --------------------- 49 50 We are now ready to generate some code. The macro assembler provides methods 51 for all the instructions that you can use. As it's a macro assembler, 52 the instructions that you tell it to generate may not directly map to a single 53 hardware instruction. Instead, it can produce a short sequence of instructions 54 that has the same effect. 55 56 Before looking at how to generate some code, let's introduce a simple but handy 57 macro: 58 59 #define __ masm-> 60 61 It allows us to write `__ Mov(r0, 42);` instead of `masm->Mov(r0, 42);` to 62 generate code. 63 64 Now we are going to write a C++ function to generate our first assembly 65 code fragment. 66 67 void GenerateDemo(MacroAssembler *masm) { 68 __ Ldr(r1, 0x12345678); 69 __ And(r0, r0, r1); 70 __ Bx(lr); 71 } 72 73 The generated code corresponds to a function with the following C prototype: 74 75 uint32_t demo(uint32_t x); 76 77 This function doesn't perform any useful operation. It loads the value 78 0x12345678 into r1 and performs a bitwise `and` operation with 79 the function's argument (stored in r0). The result of this `and` operation 80 is returned by the function in r0. 81 82 Now in our program main function, we only need to create a label to represent 83 the entry point of the assembly function and to call `GenerateDemo` to 84 generate the code. 85 86 Label demo; 87 masm.Bind(&demo); 88 GenerateDemo(&masm); 89 masm.Finalize(); 90 91 Now we are going to learn a bit more on a couple of interesting VIXL features 92 which are used in this example. 93 94 ### Label 95 96 VIXL's assembler provides a mechanism to represent labels with `Label` objects. 97 They are easy to use: simply create the C++ object and bind it to a location in 98 the generated instruction stream. 99 100 Creating a label is easy, since you only need to define the variable and bind it 101 to 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 106 The target of a branch using a label will be the address to which it has been 107 bound. 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 116 If we run this code fragment the `Mov(r0, 42)` will never be executed since 117 the first thing this code does is to jump to `foo`, which correspond to the 118 `Mov(r1, 0xc001)` instruction. 119 120 When working with labels you need to know that they are only to be used for 121 local branches, and should be passed around with care. The major reason is 122 that they cannot safely be passed or returned by value because this can trigger 123 multiple constructor and destructor calls. The destructor has assertions 124 to check that we don't try to branch to a label that hasn't been bound. 125 126 127 ### Literal Pool 128 129 On AArch32 instructions are 16 or 32 bits long, thus immediate values encoded in 130 the instructions have limited size. If you want to load a constant bigger than 131 this limit you have two possibilities: 132 133 1. 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 144 2. 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 163 Running the code. 164 ----------------- 165 166 We 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 173 Then 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 178 Now, we can call this function pointer exactly as if it were a pointer on a C 179 function: 180 181 uint32_t input_value = 0x89abcdef; 182 uint32_t output_value = (*demo_function)(input_value); 183 184 A little trace: 185 186 printf("native: abs(%08x) = %08x\n", input_value, output_value); 187 188 189 The example shown in this tutorial is very simple, because the goal was to 190 demonstrate the basics of the VIXL framework. There are more complex code 191 examples in the VIXL `examples/aarch32` directory showing more features of both the 192 macro assembler and the AArch32 architecture. 193 194 Disassembling the generated code. 195 --------------------------------- 196 197 Once you have generated something with the macro-assembler, you may want to 198 disassemble it. 199 200 First, you must include iostream. 201 202 #include <iostream> 203 204 And the disassembler header file: 205 206 #include "aarch32/disasm-aarch32.h" 207 208 Then you have to define the pc used to disassemble (the one which is used to 209 display the addresses not the location of the instructions): 210 211 uint32_t display_pc = 0x1000; 212 213 Or, 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 217 Then 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 223 If you generated T32 code instead of A32 code, you must use 224 DisassembleT32Buffer. Warning: if your buffer contains some data or contains 225 mixed T32 and A32 code, the result won't be accurate (everything will be 226 disassembled as T32 or A32 code). 227 228 Example 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