diff --git a/section_1/regs/ldr2.md b/section_1/regs/ldr2.md index f41872c..46de610 100644 --- a/section_1/regs/ldr2.md +++ b/section_1/regs/ldr2.md @@ -2,34 +2,34 @@ ## Overview -In this chapter we examine the difference between loading the address of (a pointer to) -a data label versus loading the data at the label. Both use the `ldr` instruction -however, the assembler actually does some trickery behind the scenes to accomplish -the loads. +In this chapter we examine the difference between loading the address of +(a pointer to) a data label versus loading the data at the label. Both +use the `ldr` instruction however, the assembler actually does some +trickery behind the scenes to accomplish the loads. ## Length of Instructions -All AARCH64 instructions are 4 bytes in width. +**All** AARCH64 instructions are 4 bytes in width. ## Length of Pointers -All AARCH64 pointers are 8 bytes in width. +**All** AARCH64 pointers are 8 bytes in width. ## How to Specify an Address Too Big to Fit in an Instruction? -The title of this section sets the table for the need for trickery. All labels -refer to addresses. Addresses are 8 bytes in width but all instructions are 4 -bytes in width. Clearly, we cannot fit the full address of a label in an -instruction. +The title of this section sets the table for the need for trickery. All +labels refer to addresses. Addresses are 8 bytes in width but all +instructions are 4 bytes in width. Clearly, we cannot fit the full +address of a label in an instruction. -Some ISA's (not ARM) have variable length instructions. The instruction may be -four bytes wide but it tells the CPU that the next eight bytes are an operand -of the instruction. Thus the true instruction width is 12 bytes. This is not -true of the ARM ISA. +Some ISAs (not ARM) have variable length instructions. The instruction +may be four bytes wide but it tells the CPU that the next eight bytes +are an operand of the instruction. Thus the true instruction width is 12 +bytes. This is not true of the ARM ISA. **All instructions are 4 bytes wide. All of them.** -## "`ldr` xregister, =label" is a Pseudo Instruction +## "`ldr` x_register, =label" is a Pseudo Instruction When you assemble an instruction looking like: @@ -37,21 +37,22 @@ When you assemble an instruction looking like: ldr x1, =label ``` -the assembler puts the address of the label into a special region of memory -fancily called a "literal pool." What matters is this region of memory is placed -immediately after (therefore nearby) your code. +the assembler puts the address of the label into a special region of +memory fancily called a "literal pool." What matters is this region of +memory is placed immediately after (therefore nearby) your code. -Then, the assembler computes the difference between the address of the current -instruction (the `ldr` itself) and the address of the data in the literal pool -made from the labeled data. +Then, the assembler computes the difference between the address of the +current instruction (the `ldr` itself) and the address of the data in +the literal pool made from the labeled data. -The assembler generates a different `ldr` instruction which uses the difference -(or offset) of the data relative to the program counter (`pc`). The `pc` is -non-other the address of the current instruction. +The assembler generates a different `ldr` instruction which uses the +difference (or offset) of the data relative to the program counter +(`pc`). The `pc` is non-other the address of the current instruction. -Because the literal pool for your code is located nearby your code, the offset -from the current instruction to the data in the pool is a relatively **small** -number. Small enough, to fit inside a four byte `ldr` instruction. +Because the literal pool for your code is located nearby your code, the +offset from the current instruction to the data in the pool is a +relatively **small** number. Small enough, to fit inside a four byte +`ldr` instruction. ```text ldr x1, [pc, offset to data in literal pool] @@ -66,7 +67,7 @@ between: ldr x1, =q ``` -and +and ```text ldr x1, q @@ -112,32 +113,32 @@ above source code will include: ```text 0000000000007a0
: - 7a0: f81f0ffe str x30, [sp, #-16]! - 7a4: 58000160 ldr x0, 7d0 - 7a8: 58000181 ldr x1, 7d8 - 7ac: f9400022 ldr x2, [x1] - 7b0: 97ffffb4 bl 680 - 7b4: 580000e0 ldr x0, 7d0 - 7b8: 580842c1 ldr x1, 11010 - 7bc: f9400022 ldr x2, [x1] - 7c0: 97ffffb0 bl 680 - 7c4: f84107fe ldr x30, [sp], #16 - 7c8: 2a1f03e0 mov w0, wzr - 7cc: d65f03c0 ret + 7a0: f81f0ffe str x30, [sp, #-16]! + 7a4: 58000160 ldr x0, 7d0 + 7a8: 58000181 ldr x1, 7d8 + 7ac: f9400022 ldr x2, [x1] + 7b0: 97ffffb4 bl 680 + 7b4: 580000e0 ldr x0, 7d0 + 7b8: 580842c1 ldr x1, 11010 + 7bc: f9400022 ldr x2, [x1] + 7c0: 97ffffb0 bl 680 + 7c4: f84107fe ldr x30, [sp], #16 + 7c8: 2a1f03e0 mov w0, wzr + 7cc: d65f03c0 ret ``` and ```text 000000000011010 : - 11010: 55667788 - 11014: 11223344 + 11010: 55667788 + 11014: 11223344 ``` Let's examine the second snippet first. -It says `000000000011010 :`. This means that what comes next is the data -corresponding to what is labeled `q` in our source code. Notice the +It says `000000000011010 :`. This means that what comes next is the +data corresponding to what is labeled `q` in our source code. Notice the relocatable address of `11010`. We will explain "relocatable address" below. @@ -160,33 +161,45 @@ to accomplish their nefarious purposes. This image shows `gdb` in `layout regs` at the time our program is loaded. -![1](./1_prior_to_running.png) +
+ +
Prior to launchr
+
Notice that all of the addresses match the disassemblies given above. For example `main()` starts at `7a0`. Now watch what happens the the program is actually launched: -![2](./2_after_b_and_run.png) - -NOTE NOTE NOTE NOTE NOTE - -THERE IS MUCH HERE THAT SHOULD BE REWRITTEN!!! - -COME BACK TO THIS!!! +
+ +
After breakpoint and launch
+
Suddenly all the address change to much larger values. -**In fact, the addresses all seem to be six bytes long! OK, so they aren't -eight bytes long but this can be explained by choices about how much of the -full "virtual" address space that will be used. The salient point is that -even six bytes is far too large to fit in a four byte instruction. GDB is -masking the pseudo instruction and showing what the effective addresses are.** +**In fact, the addresses all seem to be six bytes long!** -Now lets step forward to see the results of the first `ldr` of the +Why are these addresses only six bytes long when all pointers are +8 bytes long? + +Sixty four bit ARM Linux kernels allocate 39, 42 or 48 bits for the size +of a process's virtual address space. Notice 42 and 48 bit values +require 6 bytes to hold them. A virtual address space is all of the +addresses a process can generate / use. Further, all addresses used +by processes are virtual addresses. + +The salient point is that even six bytes is far too large to fit in a +four byte instruction. GDB is masking the pseudo instruction and showing +what the effective addresses are.** + +Now lets step forward to see the results of the first `ldr` of the `printf()` template / format string into `x0`. -![3](./3_results_of_first_ldr.png) +
+ +
Results of first ldr
+
There is a pointer in `x0` ending in `b018`. Notice this is **NOT** the value encoded in the instruction ending in `a7d0`. @@ -195,31 +208,45 @@ has been modified to use some calculated offset from the `pc`. To finish, here is how we confirm `x0` is indeed correct. -![4](./4_confirm_x0_is_correct.png) +
+ +
Confirming x0 is correct
+
Notice down below the `x/s $x0` prints the value in memory corresponding to the address contained in `x0`. Finally: -![5](./5_confirm_x2_is_correct.png) +
+ +
Confirming x2 is correct
+
-At the outset of this discussion we said that this program will crash on source code `line 15`. -See if you can work out why. Take a moment before reading further. +At the outset of this discussion we said that this program will crash on +source code `line 15`. See if you can work out why. Take a moment before +reading further. -Now that you have a hypothesis in mind, take a look at this screenshot showing -the state of `x1` after this instruction: `ldr x1, q` is executed. +Now that you have a hypothesis in mind, take a look at this screenshot +showing the state of `x1` after this instruction: `ldr x1, q` is +executed. -![6](./after_bad_load.png) +
+ +
After bad load
+
-Notice that what is in `x1` this time looks very different from the previous attempt -at printing. Notice still more that the value now in `x1` is the value of `q`, not -its address. +Notice that what is in `x1` this time looks very different from the +previous attempt at printing. Notice still more that the value now in +`x1` is the value of `q`, not its address. -Naturally, the next instruction which tries to dereference the value of `q` rather -than its address, causes a crash. +Naturally, the next instruction which tries to dereference the value of +`q` rather than its address, causes a crash. -![7](./after_crash.png) +
+ +
After crash
+
## Summary @@ -232,13 +259,14 @@ memory at those labels can be retrieved. | ldr r, =label | Load the address of the label into r | | ldr r, label | Load the value found at the label into r | -In both cases, the assembler will likely do some magical translation of your -simple `ldr` instruction into something involving offsets so that the resulting -offset can fit into an instruction where the full address cannot. +In both cases, the assembler will likely do some magical translation of +your simple `ldr` instruction into something involving offsets so that +the resulting offset can fit into an instruction where the full address +cannot. -To store a value back to memory at the address given by a label, the address -corresponding to the label will have first been loaded as -is described above. Then, once the address is in a register, an `str` +To store a value back to memory at the address given by a label, the +address corresponding to the label will have first been loaded as is +described above. Then, once the address is in a register, an `str` instruction can be used to properly locate the values to be written. ## Questions