How To Print Out Register Values With Shifts In Gdb
ARM uses a load-store model for retention access which ways that only load/store (LDR and STR) instructions tin can access retentivity. While on x86 most instructions are allowed to straight operate on data in memory, on ARM data must be moved from memory into registers earlier being operated on. This ways that incrementing a 32-bit value at a particular memory address on ARM would crave three types of instructions (load, increase, and store) to first load the value at a item address into a annals, increase it within the register, and store it back to the memory from the register.
To explain the fundamentals of Load and Store operations on ARM, we start with a basic example and continue with 3 bones start forms with three dissimilar address modes for each offset class. For each case we will use the same piece of assembly code with a different LDR/STR starting time class, to keep it simple. The best style to follow this function of the tutorial is to run the lawmaking examples in a debugger (GDB) on your lab environs.
- Offset grade: Immediate value as the outset
- Addressing way: Showtime
- Addressing style: Pre-indexed
- Addressing manner: Mail-indexed
- Offset class: Register as the first
- Addressing mode: Offset
- Addressing style: Pre-indexed
- Addressing mode: Post-indexed
- Outset form: Scaled register as the offset
- Addressing mode: Offset
- Addressing way: Pre-indexed
- Addressing mode: Post-indexed
First bones case
By and large, LDR is used to load something from memory into a register, and STR is used to store something from a register to a memory accost.
LDR R2, [R0] @ [R0] - origin address is the value establish in R0. STR R2, [R1] @ [R1] - destination address is the value found in R1.
LDR operation: loads thevalue at the address establish in R0 to the destination register R2.
STR performance: stores the value found in R2 to the memory address found in R1.
This is how information technology would wait like in a functional associates program:
.information /* the .data section is dynamically created and its addresses cannot be easily predicted */ var1: .word iii /* variable 1 in retentiveness */ var2: .discussion four /* variable 2 in retention */ .text /* start of the text (code) section */ .global _start _start: ldr r0, adr_var1 @ load the retentiveness address of var1 via label adr_var1 into R0 ldr r1, adr_var2 @ load the memory address of var2 via label adr_var2 into R1 ldr r2, [r0] @ load the value (0x03) at memory address found in R0 to register R2 str r2, [r1] @ store the value found in R2 (0x03) to the memory accost institute in R1 bkpt adr_var1: .word var1 /* address to var1 stored here */ adr_var2: .word var2 /* address to var2 stored here */
At the lesser nosotros accept our Literal Puddle (a memory area in the same lawmaking department to store constants, strings, or offsets that others tin can reference in a position-independent manner) where we shop the retentivity addresses of var1 and var2 (defined in the data section at the top) using the labels adr_var1 and adr_var2. The outset LDR loads the address of var1 into register R0. The second LDR does the same for var2 and loads it to R1. Then we load the value stored at the retentiveness accost found in R0 to R2, and store the value found in R2 to the memory accost constitute in R1.
When nosotros load something into a annals, the brackets ([ ]) mean: the value found in the register between these brackets is a memory address nosotros desire to load something from.
When we shop something to a retention location, the brackets ([ ]) mean: the value plant in the register betwixt these brackets is a retentiveness address we want to store something to.
This sounds more complicated than it actually is, then hither is a visual representation of what'south going on with the retentivity and the registers when executing the code to a higher place in a debugger:
Allow'due south look at the same lawmaking in a debugger.
gef> disassemble _start Dump of assembler lawmaking for office _start: 0x00008074 <+0>: ldr r0, [pc, #12] ; 0x8088 <adr_var1> 0x00008078 <+4>: ldr r1, [pc, #12] ; 0x808c <adr_var2> 0x0000807c <+8>: ldr r2, [r0] 0x00008080 <+12>: str r2, [r1] 0x00008084 <+sixteen>: bx lr Cease of assembler dump.
The labels we specified with the get-go 2 LDR operations changed to [pc, #12]. This is called PC-relative addressing. Because we used labels, the compiler calculated the location of our values specified in the Literal Pool (PC+12). You lot tin can either calculate the location yourself using this exact approach, or yous can utilize labels similar we did previously. The only difference is that instead of using labels, yous need to count the exact position of your value in the Literal Pool. In this example, it is iii hops (4+4+4=12) away from the effective PC position. More near PC-relative addressing later in this chapter.
Side note: In case you lot forgot why the effective PC is located 2 instructions alee of the current ane, it is described in Function 2 [… During execution, PC stores the address of the current instruction plus 8 (two ARM instructions) in ARM land, and the current instruction plus 4 (2 Pollex instructions) in Thumb country. This is unlike from x86 where PC always points to the side by side instruction to exist executed…].
1.Commencement form: Immediate value every bit the starting time
STR Ra, [Rb, imm] LDR Ra, [Rc, imm]
Here we use an firsthand (integer) as an first. This value is added or subtracted from the base annals (R1 in the instance below) to access data at an offset known at compile time.
.data var1: .word 3 var2: .word four .text .global _start _start: ldr r0, adr_var1 @ load the memory accost of var1 via label adr_var1 into R0 ldr r1, adr_var2 @ load the memory address of var2 via label adr_var2 into R1 ldr r2, [r0] @ load the value (0x03) at memory address constitute in R0 to register R2 str r2, [r1, #ii] @ address way: beginning. Store the value establish in R2 (0x03) to the retentiveness accost found in R1 plus 2. Base annals (R1) unmodified. str r2, [r1, #four]! @ address fashion: pre-indexed. Store the value institute in R2 (0x03) to the retention accost establish in R1 plus 4. Base annals (R1) modified: R1 = R1+4 ldr r3, [r1] , #4 @ accost mode: post-indexed. Load the value at retention accost establish in R1 to register R3. Base register (R1) modified: R1 = R1+4 bkpt adr_var1: .give-and-take var1 adr_var2: .word var2
Let'due south phone call this program ldr.s, compile it and run information technology in GDB to see what happens.
$ every bit ldr.s -o ldr.o $ ld ldr.o -o ldr $ gdb ldr
In GDB (with global environment facility) nosotros prepare a break point at _start and run the programme.
global environment facility> intermission _start gef> run ... gef> nexti iii /* to run the next three instructions */
The registers on my system are now filled with the following values (go along in mind that these addresses might be different on your system):
$r0 : 0x00010098 -> 0x00000003 $r1 : 0x0001009c -> 0x00000004 $r2 : 0x00000003 $r3 : 0x00000000 $r4 : 0x00000000 $r5 : 0x00000000 $r6 : 0x00000000 $r7 : 0x00000000 $r8 : 0x00000000 $r9 : 0x00000000 $r10 : 0x00000000 $r11 : 0x00000000 $r12 : 0x00000000 $sp : 0xbefff7e0 -> 0x00000001 $lr : 0x00000000 $pc : 0x00010080 -> <_start+12> str r2, [r1] $cpsr : 0x00000010
The adjacent instruction that will be executed a STR operation with the start address mode . Information technology volition store the value from R2 (0x00000003) to the retention address specified in R1 (0x0001009c) + the offset (#two) = 0x1009e.
global environment facility> nexti gef> x/west 0x1009e 0x1009e <var2+2>: 0x3
The adjacent STR functioning uses the pre-indexed address way . Yous tin can recognize this manner by the exclamation mark (!). The only difference is that the base register will be updated with the terminal memory address in which the value of R2 will be stored. This ways, nosotros store the value constitute in R2 (0x3) to the memory address specified in R1 (0x1009c) + the offset (#4) = 0x100A0, and update R1 with this verbal address.
gef> nexti gef> 10/w 0x100A0 0x100a0: 0x3 gef> info register r1 r1 0x100a0 65696
The last LDR performance uses the post-indexed address way . This means that the base of operations annals (R1) is used as the terminal address, then updated with the first calculated with R1+four. In other words, it takes the value institute in R1 (non R1+four), which is 0x100A0 and loads it into R3, and so updates R1 to R1 (0x100A0) + offset (#4) = 0x100a4.
gef> info register r1 r1 0x100a4 65700 global environment facility> info annals r3 r3 0x3 3
Hither is an abstruse illustration of what'southward happening:
2.Get-go form: Register as the offset.
STR Ra, [Rb, Rc] LDR Ra, [Rb, Rc]
This outset course uses a register as an starting time. An example usage of this kickoff class is when your code wants to access an assortment where the index is computed at run-time.
.information var1: .word iii var2: .discussion 4 .text .global _start _start: ldr r0, adr_var1 @ load the memory address of var1 via characterization adr_var1 to R0 ldr r1, adr_var2 @ load the memory accost of var2 via characterization adr_var2 to R1 ldr r2, [r0] @ load the value (0x03) at memory accost constitute in R0 to R2 str r2, [r1, r2] @ accost mode: offset. Shop the value institute in R2 (0x03) to the retentivity address found in R1 with the offset R2 (0x03). Base register unmodified. str r2, [r1, r2]! @ address mode: pre-indexed. Shop value found in R2 (0x03) to the retentiveness accost plant in R1 with the outset R2 (0x03). Base of operations register modified: R1 = R1+R2. ldr r3, [r1], r2 @ address mode: post-indexed. Load value at retention address constitute in R1 to annals R3. Then modify base of operations annals: R1 = R1+R2. bx lr adr_var1: .word var1 adr_var2: .word var2
After executing the first STR performance with the get-go address way , the value of R2 (0x00000003) volition be stored at memory address 0x0001009c + 0x00000003 = 0x0001009F.
global environment facility> 10/w 0x0001009F 0x1009f <var2+iii>: 0x00000003
The 2nd STR operation with the pre-indexed accost mode will do the same, with the departure that it will update the base register (R1) with the calculated retentivity address (R1+R2).
gef> info register r1 r10x1009f 65695
The last LDR operation uses the mail service-indexed address mode and loads the value at the memory accost found in R1 into the annals R2, then updates the base register R1 (R1+R2 = 0x1009f + 0x3 = 0x100a2).
global environment facility> info register r1 r1 0x100a2 65698 global environment facility> info register r3 r30x3 3
3.Offset course: Scaled register as the offset
LDR Ra, [Rb, Rc, <shifter>] STR Ra, [Rb, Rc, <shifter>]
The third offset course has a scaled annals as the kickoff. In this case, Rb is the base register and Rc is an immediate offset (or a annals containing an immediate value) left/right shifted (<shifter>) to scale the immediate. This means that the barrel shifter is used to scale the start. An example usage of this start class would be for loops to iterate over an array. Hither is a uncomplicated example you can run in GDB:
.information var1: .give-and-take iii var2: .discussion 4 .text .global _start _start: ldr r0, adr_var1 @ load the retentiveness accost of var1 via label adr_var1 to R0 ldr r1, adr_var2 @ load the memory address of var2 via characterization adr_var2 to R1 ldr r2, [r0] @ load the value (0x03) at memory address found in R0 to R2 str r2, [r1, r2, LSL#2] @ address manner: beginning. Shop the value plant in R2 (0x03) to the memory address found in R1 with the beginning R2 left-shifted by 2. Base annals (R1) unmodified. str r2, [r1, r2, LSL#two]! @ address style: pre-indexed. Store the value found in R2 (0x03) to the retentivity address institute in R1 with the start R2 left-shifted by 2. Base of operations register modified: R1 = R1 + R2<<2 ldr r3, [r1], r2, LSL#two @ address mode: post-indexed. Load value at retention address found in R1 to the register R3. Then modifiy base register: R1 = R1 + R2<<2 bkpt adr_var1: .word var1 adr_var2: .word var2
The start STR operation uses the offset address way and stores the value plant in R2 at the memory location calculated from [r1, r2, LSL#2], which ways that information technology takes the value in R1 every bit a base of operations (in this case, R1 contains the memory address of var2), then it takes the value in R2 (0x3), and shifts it left past 2. The picture beneath is an endeavor to visualize how the memory location is calculated with [r1, r2, LSL#ii].
The second STR operation uses the pre-indexed accost mode . This means, it performs the aforementioned action as the previous operation, with the divergence that information technology updates the base annals R1 with the calculated memory accost afterwards. In other words, information technology will offset store the value found at the memory address R1 (0x1009c) + the starting time left shifted by #ii (0x03 LSL#2 = 0xC) = 0x100a8, and update R1 with 0x100a8.
gef> info register r1 r1 0x100a8 65704
The last LDR operation uses the postal service-indexed address fashion . This means, it loads the value at the memory address found in R1 (0x100a8) into register R3, and so updates the base of operations register R1 with the value calculated with r2, LSL#2. In other words, R1 gets updated with the value R1 (0x100a8) + the showtime R2 (0x3) left shifted by #2 (0xC) = 0x100b4.
global environment facility> info register r1 r10x100b4 65716
Summary
Recollect the three offset modes in LDR/STR:
- beginning mode uses an immediate as offset
- ldr r3, [r1, #iv]
- offset mode uses a register as kickoff
- ldr r3, [r1, r2]
- get-go mode uses a scaled register as beginning
- ldr r3, [r1, r2, LSL#2]
How to retrieve the unlike accost modes in LDR/STR:
- If there is a !, information technology's prefix accost mode
- ldr r3, [r1, #4]!
- ldr r3, [r1, r2]!
- ldr r3, [r1, r2, LSL#2]!
- If the base of operations annals is in brackets by itself, it's postfix address style
- ldr r3, [r1], #4
- ldr r3, [r1], r2
- ldr r3, [r1], r2, LSL#2
- Anything else is offset address mode.
- ldr r3, [r1, #4]
- ldr r3, [r1, r2]
- ldr r3, [r1, r2, LSL#2]
LDR is not just used to load data from memory into a register. Sometimes you will encounter syntax similar this:
.section .text .global _start _start: ldr r0, =bound /* load the accost of the function label jump into R0 */ ldr r1, =0x68DB00AD /* load the value 0x68DB00AD into R1 */ jump: ldr r2, =511 /* load the value 511 into R2 */ bkpt
These instructions are technically called pseudo-instructions. We tin utilize this syntax to reference data in the literal pool. The literal pool is a memory area in the same department (because the literal pool is function of the code) to store constants, strings, or offsets. In the example above we use these pseudo-instructions to reference an starting time to a office, and to motion a 32-flake constant into a annals in 1 teaching. The reason why we sometimes need to apply this syntax to motion a 32-fleck constant into a register in ane instruction is because ARM can only load a 8-chip value in one go. What? To understand why, you need to know how immediate values are existence handled on ARM.
Loading firsthand values in a register on ARM is not every bit straightforward as it is on x86. There are restrictions on which immediate values you tin use. What these restrictions are and how to bargain with them isn't the well-nigh heady part of ARM assembly, but bear with me, this is just for your agreement and there are tricks you can employ to bypass these restrictions (hint: LDR).
We know that each ARM didactics is 32bit long, and all instructions are provisional. At that place are 16 status codes which we can use and i status lawmaking takes up 4 $.25 of the instruction. Then we demand two bits for the destination register. two bits for the get-go operand annals, and i bit for the set-condition flag, plus an assorted number of bits for other matters like the bodily opcodes. The point here is, that after assigning bits to pedagogy-type, registers, and other fields, there are simply 12 bits left for firsthand values, which volition simply allow for 4096 unlike values.
This ways that the ARM teaching is only able to use a limited range of immediate values with MOV straight. If a number can't be used directly, it must exist separate into parts and pieced together from multiple smaller numbers.
But there is more. Instead of taking the 12 bits for a single integer, those 12 bits are carve up into an 8bit number (n) beingness able to load any 8-fleck value in the range of 0-255, and a 4bit rotation field (r) being a right rotate in steps of two between 0 and 30. This means that the full firsthand value v is given by the formula: v = n ror ii*r. In other words, the just valid immediate values are rotated bytes (values that can be reduced to a byte rotated by an even number).
Here are some examples of valid and invalid immediate values:
Valid values: #256 // 1 ror 24 --> 256 #384 // 6 ror 26 --> 384 #484 // 121 ror xxx --> 484 #16384 // ane ror xviii --> 16384 #2030043136 // 121 ror 8 --> 2030043136 #0x06000000 // 6 ror eight --> 100663296 (0x06000000 in hex) Invalid values: #370 // 185 ror 31 --> 31 is not in range (0 – xxx) #511 // 1 1111 1111 --> bit-design tin can't fit into ane byte #0x06010000 // 1 1000 000one.. --> flake-blueprint can't fit into one byte
This has the outcome that it is not possible to load a full 32bit address in one go. We tin featherbed this restrictions by using one of the following two options:
- Construct a larger value out of smaller parts
- Instead of using MOV r0, #511
- Split 511 into two parts: MOV r0, #256, and Add together r0, #255
- Utilize a load construct 'ldr r1,=value' which the assembler will happily convert into a MOV, or a PC-relative load if that is not possible.
- LDR r1, =511
If you lot try to load an invalid immediate value the assembler will mutter and output an error saying: Error: invalid constant. If yous come across this error, you now know what it ways and what to exercise about it.
Let'southward say you want to load #511 into R0.
.section .text .global _start _start: mov r0, #511 bkpt
If you try to assemble this code, the assembler volition throw an error:
azeria@labs:~$ every bit test.s -o test.o exam.southward: Assembler messages: test.s:v: Error: invalid constant (1ff) afterward fixup
You need to either split 511 in multiple parts or y'all use LDR equally I described before.
.section .text .global _start _start: mov r0, #256 /* 1 ror 24 = 256, so it'south valid */ add together r0, #255 /* 255 ror 0 = 255, valid. r0 = 256 + 255 = 511 */ ldr r1, =511 /* load 511 from the literal puddle using LDR */ bkpt
If you need to figure out if a certain number can be used as a valid immediate value, you don't need to summate it yourself. You lot tin utilise my fiddling python script called rotator.py which takes your number as an input and tells you if it can be used as a valid immediate number.
azeria@labs:~$ python rotator.py Enter the value you want to check: 511 Lamentable, 511 cannot be used as an firsthand number and has to exist split. azeria@labs:~$ python rotator.py Enter the value you want to check: 256 The number 256 can be used every bit a valid immediate number. 1 ror 24 --> 256
Source: https://azeria-labs.com/memory-instructions-load-and-store-part-4/
Posted by: woldwilill.blogspot.com
0 Response to "How To Print Out Register Values With Shifts In Gdb"
Post a Comment