mirror of
https://github.com/mytechnotalent/Embedded-Hacking.git
synced 2026-05-25 00:44:10 +02:00
Refactor E and S
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 3
|
||||
Embedded System Analysis: Understanding the RP2350 Architecture w/ Comprehensive Firmware Analysis
|
||||
|
||||
### Non-Credit Practice Exercise 1 Solution: Trace a Reset
|
||||
|
||||
#### Answers
|
||||
|
||||
##### Instruction Trace (First 10 Instructions from 0x1000015c)
|
||||
|
||||
| # | Address | Instruction | What It Does | Key Register Change |
|
||||
|---|-------------|----------------------------------------|------------------------------------------------------|---------------------|
|
||||
| 1 | 0x1000015c | `mov.w r0, #0xd0000000` | Loads SIO base address into r0 | r0 = 0xd0000000 |
|
||||
| 2 | 0x10000160 | `ldr r0, [r0, #0]` | Reads CPUID register at SIO base + 0 | r0 = 0 (Core 0) |
|
||||
| 3 | 0x10000162 | `cbz r0, 0x1000016a` | If CPUID == 0 (Core 0), branch to data copy section | PC = 0x1000016a |
|
||||
| 4 | 0x1000016a | `ldr r0, [pc, #...]` | Loads source address for data copy (flash) | r0 = flash addr |
|
||||
| 5 | 0x1000016c | `ldr r1, [pc, #...]` | Loads destination address for data copy (RAM) | r1 = RAM addr |
|
||||
| 6 | 0x1000016e | `ldr r2, [pc, #...]` | Loads end address for data copy | r2 = end addr |
|
||||
| 7 | 0x10000170 | `cmp r1, r2` | Checks if all data has been copied | Flags updated |
|
||||
| 8 | 0x10000172 | `bhs 0x10000178` | If done (dest >= end), skip to BSS clear | PC conditional |
|
||||
| 9 | 0x10000174 | `ldm r0!, {r3}` | Loads 4 bytes from flash source, auto-increments r0 | r3 = data, r0 += 4 |
|
||||
| 10| 0x10000176 | `stm r1!, {r3}` | Stores 4 bytes to RAM destination, auto-increments r1| r1 += 4 |
|
||||
|
||||
##### GDB Session
|
||||
|
||||
```gdb
|
||||
(gdb) b *0x1000015c
|
||||
(gdb) monitor reset halt
|
||||
(gdb) c
|
||||
Breakpoint 1, 0x1000015c in _reset_handler ()
|
||||
(gdb) si
|
||||
(gdb) disas $pc,+2
|
||||
(gdb) info registers r0
|
||||
```
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **Why does the reset handler check the CPUID before doing anything else?**
|
||||
The RP2350 has two Cortex-M33 cores that share the same reset handler entry point. The CPUID check at SIO base `0xd0000000` returns 0 for Core 0 and 1 for Core 1. Only Core 0 should perform the one-time initialization (data copy, BSS clear, calling `main()`). Without this check, both cores would simultaneously try to initialize RAM, causing data corruption and race conditions.
|
||||
|
||||
2. **What would happen if Core 1 tried to run the same initialization code as Core 0?**
|
||||
Both cores would simultaneously write to the same RAM locations during the data copy and BSS clear phases. This would cause data corruption due to race conditions—values could be partially written or overwritten unpredictably. The `cbz` instruction at `0x10000162` prevents this: if CPUID != 0, the code branches to `hold_non_core0_in_bootrom`, which sends Core 1 back to the bootrom to wait until Core 0 explicitly launches it later.
|
||||
|
||||
3. **Which registers are used in the first 10 instructions, and why those specific ones?**
|
||||
The first 10 instructions use `r0`, `r1`, `r2`, and `r3`. These are the ARM "caller-saved" scratch registers (r0–r3) that don't need to be preserved across function calls. Since the reset handler is the very first code to run (no caller to preserve state for), using these low registers is both efficient (16-bit Thumb encodings) and safe. `r0` handles CPUID check and source pointer, `r1` is the destination pointer, `r2` is the end marker, and `r3` is the data transfer register.
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 3
|
||||
Embedded System Analysis: Understanding the RP2350 Architecture w/ Comprehensive Firmware Analysis
|
||||
|
||||
### Exercise 1: Trace a Reset
|
||||
### Non-Credit Practice Exercise 1: Trace a Reset
|
||||
|
||||
#### Objective
|
||||
Single-step through the first 10 instructions of the reset handler to understand exactly what happens when the RP2350 powers on or resets.
|
||||
@@ -119,7 +119,7 @@ For each of the 10 instructions:
|
||||
- Use `disas $pc,+20` to see upcoming instructions without stepping through them
|
||||
- Use `info registers` to see all register values at any point
|
||||
- If you step past where you wanted to stop, just `monitor reset halt` and start over
|
||||
- Keep notes as you go—this is detective work!
|
||||
- Keep notes as you go—this is detective work!
|
||||
|
||||
#### Next Steps
|
||||
- Try stepping all the way through to the data copy loop
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 3
|
||||
Embedded System Analysis: Understanding the RP2350 Architecture w/ Comprehensive Firmware Analysis
|
||||
|
||||
### Non-Credit Practice Exercise 2 Solution: Find the Stack Size
|
||||
|
||||
#### Answers
|
||||
|
||||
##### Initial Stack Pointer
|
||||
|
||||
```gdb
|
||||
(gdb) x/x 0x10000000
|
||||
0x10000000 <__vectors>: 0x20082000
|
||||
```
|
||||
|
||||
The initial stack pointer is **0x20082000**, stored as the first entry in the vector table.
|
||||
|
||||
##### Stack Limit
|
||||
|
||||
The stack limit is **0x20078000**, defined by the linker script.
|
||||
|
||||
```gdb
|
||||
(gdb) info symbol __StackLimit
|
||||
__StackLimit in section .stack
|
||||
```
|
||||
|
||||
##### Stack Size Calculation
|
||||
|
||||
| Value | Hex | Decimal |
|
||||
|-----------------|-------------|---------------|
|
||||
| Stack Top | 0x20082000 | 537,108,480 |
|
||||
| Stack Limit | 0x20078000 | 537,067,520 |
|
||||
| **Stack Size** | 0x0000A000 | **40,960 bytes** |
|
||||
|
||||
```
|
||||
Stack Size = 0x20082000 - 0x20078000 = 0xA000 = 40,960 bytes
|
||||
40,960 ÷ 1,024 = 40 KB
|
||||
```
|
||||
|
||||
##### Memory Region Verification
|
||||
|
||||
| Region | Start | End | Size |
|
||||
|-----------|-------------|-------------|--------|
|
||||
| RAM | 0x20000000 | 0x20080000 | 512 KB |
|
||||
| SCRATCH_X | 0x20080000 | 0x20081000 | 4 KB |
|
||||
| SCRATCH_Y | 0x20081000 | 0x20082000 | 4 KB |
|
||||
| **Stack** | 0x20078000 | 0x20082000 | **40 KB** |
|
||||
|
||||
The stack spans from the upper portion of main RAM through SCRATCH_X and into SCRATCH_Y.
|
||||
|
||||
##### Runtime Stack Usage at main()
|
||||
|
||||
```gdb
|
||||
(gdb) b main
|
||||
(gdb) c
|
||||
(gdb) info registers sp
|
||||
sp 0x20081fc8 0x20081fc8
|
||||
```
|
||||
|
||||
Stack used at `main()` entry: `0x20082000 - 0x20081fc8 = 0x38 = 56 bytes`
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **Why is the stack 40 KB instead of just fitting in the 4 KB SCRATCH_Y region?**
|
||||
The stack pointer is initialized at the top of SCRATCH_Y (`0x20082000`), but the stack grows **downward**. As functions are called and local variables are allocated, the stack pointer decreases past the SCRATCH_Y boundary (`0x20081000`), through SCRATCH_X, and into the upper portion of main RAM. The linker sets the stack limit at `0x20078000` to give the stack 40 KB of room, which is sufficient for typical embedded applications with nested function calls.
|
||||
|
||||
2. **What happens if the stack grows beyond 0x20078000?**
|
||||
A **stack overflow** occurs. The stack would collide with the heap or global data stored in the lower portion of RAM. This can corrupt variables, overwrite heap metadata, or cause a HardFault if memory protection is enabled. On the RP2350 with Cortex-M33 MPU support, a MemManage fault could be triggered if stack guard regions are configured.
|
||||
|
||||
3. **How would you detect a stack overflow during runtime?**
|
||||
Several methods exist: (a) Write a known "canary" pattern (e.g., `0xDEADBEEF`) at the stack limit and periodically check if it's been overwritten. (b) Use the Cortex-M33 MPU to set a guard region at `0x20078000` that triggers a MemManage fault on access. (c) In GDB, use a watchpoint: `watch *(int*)0x20078000` to break if the stack reaches the limit. (d) Use the `stackusage` GDB macro from the exercise to monitor usage.
|
||||
|
||||
4. **Why does the stack grow downward instead of upward?**
|
||||
This is an ARM architecture convention inherited from early processor designs. Growing downward allows the stack and heap to grow toward each other from opposite ends of RAM, maximizing memory utilization without needing to know each region's size in advance. The stack starts at the highest address and grows down, while the heap starts at a lower address and grows up. If they meet, memory is exhausted—but this layout gives both regions the maximum possible space.
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 3
|
||||
Embedded System Analysis: Understanding the RP2350 Architecture w/ Comprehensive Firmware Analysis
|
||||
|
||||
### Exercise 2: Find the Stack Size
|
||||
### Non-Credit Practice Exercise 2: Find the Stack Size
|
||||
|
||||
#### Objective
|
||||
Calculate the size of the stack by examining the vector table, understanding the linker script's memory layout, and performing manual calculations.
|
||||
@@ -92,7 +92,7 @@ Let's convert to decimal:
|
||||
##### Step 5: Convert to Kilobytes
|
||||
|
||||
```
|
||||
Bytes to KB = 40,960 ÷ 1,024 = 40 KB
|
||||
Bytes to KB = 40,960 ÷ 1,024 = 40 KB
|
||||
```
|
||||
|
||||
So the stack is **40 KB** in size.
|
||||
@@ -102,7 +102,7 @@ So the stack is **40 KB** in size.
|
||||
Cross-check with the memory layout:
|
||||
- **RAM**: `0x20000000` - `0x20080000` (512 KB)
|
||||
- **SCRATCH_X**: `0x20080000` - `0x20081000` (4 KB)
|
||||
- **SCRATCH_Y**: `0x20081000` - `0x20082000` (4 KB) ← Stack lives here
|
||||
- **SCRATCH_Y**: `0x20081000` - `0x20082000` (4 KB) ? Stack lives here
|
||||
- **Stack range**: `0x20078000` - `0x20082000` (40 KB)
|
||||
|
||||
The stack extends from SCRATCH_Y down into the upper portion of main RAM.
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 3
|
||||
Embedded System Analysis: Understanding the RP2350 Architecture w/ Comprehensive Firmware Analysis
|
||||
|
||||
### Non-Credit Practice Exercise 3 Solution: Examine All Vectors
|
||||
|
||||
#### Answers
|
||||
|
||||
##### Vector Table Dump
|
||||
|
||||
```gdb
|
||||
(gdb) x/16x 0x10000000
|
||||
0x10000000 <__vectors>: 0x20082000 0x1000015d 0x1000011b 0x1000011d
|
||||
0x10000010 <__vectors+16>: 0x1000011f 0x10000121 0x10000123 0x00000000
|
||||
0x10000020 <__vectors+32>: 0x00000000 0x00000000 0x00000000 0x10000125
|
||||
0x10000030 <__vectors+48>: 0x00000000 0x00000000 0x10000127 0x10000129
|
||||
```
|
||||
|
||||
##### Complete Vector Table Map
|
||||
|
||||
| Offset | Vector # | Raw Value | Address Type | Actual Addr | Handler Name |
|
||||
|--------|----------|-------------|---------------|-------------|------------------|
|
||||
| 0x00 | — | 0x20082000 | Stack Pointer | N/A | __StackTop |
|
||||
| 0x04 | 1 | 0x1000015d | Code (Thumb) | 0x1000015c | _reset_handler |
|
||||
| 0x08 | 2 | 0x1000011b | Code (Thumb) | 0x1000011a | isr_nmi |
|
||||
| 0x0C | 3 | 0x1000011d | Code (Thumb) | 0x1000011c | isr_hardfault |
|
||||
| 0x10 | 4 | 0x1000011f | Code (Thumb) | 0x1000011e | isr_memmanage |
|
||||
| 0x14 | 5 | 0x10000121 | Code (Thumb) | 0x10000120 | isr_busfault |
|
||||
| 0x18 | 6 | 0x10000123 | Code (Thumb) | 0x10000122 | isr_usagefault |
|
||||
| 0x1C | 7 | 0x00000000 | Reserved | N/A | (Reserved) |
|
||||
| 0x20 | 8 | 0x00000000 | Reserved | N/A | (Reserved) |
|
||||
| 0x24 | 9 | 0x00000000 | Reserved | N/A | (Reserved) |
|
||||
| 0x28 | 10 | 0x00000000 | Reserved | N/A | (Reserved) |
|
||||
| 0x2C | 11 | 0x10000125 | Code (Thumb) | 0x10000124 | isr_svcall |
|
||||
| 0x30 | 12 | 0x00000000 | Reserved | N/A | (Reserved) |
|
||||
| 0x34 | 13 | 0x00000000 | Reserved | N/A | (Reserved) |
|
||||
| 0x38 | 14 | 0x10000127 | Code (Thumb) | 0x10000126 | isr_pendsv |
|
||||
| 0x3C | 15 | 0x10000129 | Code (Thumb) | 0x10000128 | isr_systick |
|
||||
|
||||
##### Handler Verification
|
||||
|
||||
```gdb
|
||||
(gdb) info symbol 0x1000015c
|
||||
_reset_handler in section .text
|
||||
(gdb) info symbol 0x1000011a
|
||||
isr_nmi in section .text
|
||||
(gdb) x/3i 0x1000011a
|
||||
0x1000011a <isr_nmi>: bkpt 0x0000
|
||||
0x1000011c <isr_hardfault>: bkpt 0x0000
|
||||
0x1000011e <isr_svcall>: bkpt 0x0000
|
||||
```
|
||||
|
||||
Most default handlers are single `bkpt` instructions—they halt the processor if triggered unexpectedly.
|
||||
|
||||
##### Summary Statistics
|
||||
|
||||
| Category | Count |
|
||||
|---------------------|-------|
|
||||
| Stack Pointer entry | 1 |
|
||||
| Valid code entries | 10 |
|
||||
| Reserved (0x0) | 5 |
|
||||
| **Total entries** | **16** |
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **Why do all the code addresses end in odd numbers (LSB = 1)?**
|
||||
ARM Cortex-M processors exclusively execute in Thumb mode. The least significant bit (LSB) of vector table entries indicates the instruction set: LSB = 1 means Thumb mode. Since Cortex-M33 only supports Thumb/Thumb-2, all code addresses must have bit 0 set to 1. The actual instruction address is the value with bit 0 cleared (e.g., `0x1000015d` → instruction at `0x1000015c`). This is a hardware requirement—loading an even address into the PC on Cortex-M would cause a HardFault.
|
||||
|
||||
2. **What happens if an exception occurs for a reserved/null vector entry?**
|
||||
If the processor attempts to invoke a handler through a vector entry containing `0x00000000`, it tries to fetch instructions from address `0x00000000` (in the bootrom region). This would either execute bootrom code unexpectedly or trigger a HardFault because the address lacks the Thumb bit (LSB = 0). In practice, a HardFault would occur, and if the HardFault handler itself is invalid, the processor enters a lockup state.
|
||||
|
||||
3. **Why do most exception handlers just contain `bkpt` instructions?**
|
||||
The Pico SDK provides these as **default weak handlers**. They are intentionally minimal—a `bkpt` (breakpoint) instruction halts the processor immediately so a debugger can catch the event. This is a safety mechanism: rather than letting an unhandled exception corrupt state or silently continue, the default handlers stop execution so the developer can diagnose the problem. Application code can override any handler by defining a function with the matching name (the weak linkage gets replaced).
|
||||
|
||||
4. **How would you replace a default handler with your own custom handler?**
|
||||
Define a C function with the exact handler name (e.g., `void isr_hardfault(void)`) in your application code. Because the SDK declares these handlers as `__attribute__((weak))`, the linker will use your strong definition instead of the default `bkpt` stub. The new function's address (with Thumb bit set) will automatically appear in the vector table at the correct offset. No linker script modification is needed.
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 3
|
||||
Embedded System Analysis: Understanding the RP2350 Architecture w/ Comprehensive Firmware Analysis
|
||||
|
||||
### Exercise 3: Examine All Vectors
|
||||
### Non-Credit Practice Exercise 3: Examine All Vectors
|
||||
|
||||
#### Objective
|
||||
Examine the first 16 entries of the vector table to understand the exception handler layout, identify valid code addresses, and recognize the Thumb mode addressing convention.
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 3
|
||||
Embedded System Analysis: Understanding the RP2350 Architecture w/ Comprehensive Firmware Analysis
|
||||
|
||||
### Non-Credit Practice Exercise 4 Solution: Find Your Main Function and Trace Back
|
||||
|
||||
#### Answers
|
||||
|
||||
##### Main Function Location
|
||||
|
||||
```gdb
|
||||
(gdb) info functions main
|
||||
0x10000234 int main(void);
|
||||
```
|
||||
|
||||
`main()` is at address **0x10000234**.
|
||||
|
||||
##### Disassembly of main()
|
||||
|
||||
```gdb
|
||||
(gdb) x/10i 0x10000234
|
||||
0x10000234 <main>: push {r7, lr}
|
||||
0x10000236 <main+2>: sub sp, #8
|
||||
0x10000238 <main+4>: add r7, sp, #0
|
||||
0x1000023a <main+6>: bl 0x100012c4 <stdio_init_all>
|
||||
0x1000023e <main+10>: movw r0, #404 @ 0x194
|
||||
0x10000242 <main+14>: movt r0, #4096 @ 0x1000
|
||||
0x10000246 <main+18>: bl 0x1000023c <__wrap_puts>
|
||||
0x1000024a <main+22>: b.n 0x1000023e <main+10>
|
||||
```
|
||||
|
||||
##### First Function Call
|
||||
|
||||
The first function call is at offset +6:
|
||||
```
|
||||
0x1000023a <main+6>: bl 0x100012c4 <stdio_init_all>
|
||||
```
|
||||
|
||||
`stdio_init_all()` initializes all standard I/O systems (USB CDC, UART) so `printf()` and `puts()` can output to the serial console.
|
||||
|
||||
##### Link Register (Caller Identification)
|
||||
|
||||
```gdb
|
||||
(gdb) b main
|
||||
(gdb) c
|
||||
(gdb) info registers lr
|
||||
lr 0x1000018b 268435851
|
||||
```
|
||||
|
||||
LR = **0x1000018b** (Thumb address), actual return address = **0x1000018a**.
|
||||
|
||||
##### Caller Disassembly
|
||||
|
||||
```gdb
|
||||
(gdb) x/10i 0x10000186
|
||||
0x10000186 <platform_entry>: ldr r1, [pc, #80]
|
||||
0x10000188 <platform_entry+2>: blx r1 → calls runtime_init()
|
||||
0x1000018a <platform_entry+4>: ldr r1, [pc, #80] → LR points here (return from main)
|
||||
0x1000018c <platform_entry+6>: blx r1 → THIS called main()
|
||||
0x1000018e <platform_entry+8>: ldr r1, [pc, #80]
|
||||
0x10000190 <platform_entry+10>: blx r1 → calls exit()
|
||||
0x10000192 <platform_entry+12>: bkpt 0x0000
|
||||
```
|
||||
|
||||
##### Complete Boot Chain
|
||||
|
||||
```
|
||||
Power On
|
||||
→ Bootrom (0x00000000)
|
||||
→ Vector Table (0x10000000)
|
||||
→ _reset_handler (0x1000015c)
|
||||
→ Data Copy & BSS Clear
|
||||
→ platform_entry (0x10000186)
|
||||
→ runtime_init() (first blx)
|
||||
→ main() (second blx) ← 0x10000234
|
||||
→ stdio_init_all() (first call in main)
|
||||
→ puts() loop (infinite)
|
||||
```
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **Why does the link register point 4 bytes after the `blx` instruction that called main?**
|
||||
The LR stores the **return address**—the instruction to execute after `main()` returns. The `blx r1` instruction at `0x10000188` (which calls `runtime_init`) is 2 bytes, and the `blx r1` at `0x1000018c` (which calls `main`) is also 2 bytes. The LR is set to the instruction immediately following the `blx` that called `main()`, which is `0x1000018a` (the `ldr` after `runtime_init`'s call). Actually, `platform_entry+4` at `0x1000018a` is where execution resumes, and the actual `blx` that calls main is at `+6` (`0x1000018c`), so LR = `0x1000018e` + Thumb bit. The key point: LR always points to the next instruction after the branch, so the caller can resume where it left off.
|
||||
|
||||
2. **What would happen if `main()` tried to return (instead of looping forever)?**
|
||||
Execution would return to `platform_entry` at the address stored in LR. Looking at the disassembly, `platform_entry` would proceed to execute the third `blx r1` at offset +10, which calls `exit()`. The `exit()` function would perform cleanup and ultimately halt the processor. After `exit()`, there's a `bkpt` instruction as a safety net. In practice on bare-metal embedded systems, returning from `main()` is generally avoided because there's no OS to return to.
|
||||
|
||||
3. **How can you tell from the disassembly that main contains an infinite loop?**
|
||||
The instruction at `0x1000024a` is `b.n 0x1000023e <main+10>`, which is an **unconditional branch** back to the `movw r0, #404` instruction that loads the string address. This is a `b` (branch) with no condition code, meaning it always jumps backward. There is no path that reaches the end of the function or a `pop {r7, pc}` (return). The `push {r7, lr}` at the start saves the return address but it's never restored—the function loops forever.
|
||||
|
||||
4. **Why is `stdio_init_all()` called before the printf loop?**
|
||||
`stdio_init_all()` configures the hardware interfaces (USB CDC and/or UART) that `printf()`/`puts()` uses for output. Without this initialization, the serial output drivers are not set up—writes to stdout would go nowhere or cause a fault. It must be called exactly once before any I/O operation. Calling it inside the loop would repeatedly reinitialize the hardware, wasting time and potentially disrupting active connections.
|
||||
+17
-17
@@ -1,10 +1,10 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 3
|
||||
Embedded System Analysis: Understanding the RP2350 Architecture w/ Comprehensive Firmware Analysis
|
||||
|
||||
### Exercise 4: Find Your Main Function and Trace Back
|
||||
### Non-Credit Practice Exercise 4: Find Your Main Function and Trace Back
|
||||
|
||||
#### Objective
|
||||
Locate the `main()` function, examine its first instructions, identify the first function call, and trace backward to discover where `main()` was called from.
|
||||
@@ -129,8 +129,8 @@ Subtract 1 to remove the Thumb bit and disassemble:
|
||||
```
|
||||
0x10000186 <platform_entry>: ldr r1, [pc, #80]
|
||||
0x10000188 <platform_entry+2>: blx r1
|
||||
0x1000018a <platform_entry+4>: ldr r1, [pc, #80] ← LR points here
|
||||
0x1000018c <platform_entry+6>: blx r1 ← This called main
|
||||
0x1000018a <platform_entry+4>: ldr r1, [pc, #80] ? LR points here
|
||||
0x1000018c <platform_entry+6>: blx r1 ? This called main
|
||||
0x1000018e <platform_entry+8>: ldr r1, [pc, #80]
|
||||
0x10000190 <platform_entry+10>: blx r1
|
||||
0x10000192 <platform_entry+12>: bkpt 0x0000
|
||||
@@ -142,11 +142,11 @@ Working backward from `main()`:
|
||||
|
||||
```
|
||||
platform_entry (0x10000186)
|
||||
↓ calls (blx at +2)
|
||||
? calls (blx at +2)
|
||||
runtime_init() (0x1000024c)
|
||||
↓ calls (blx at +6)
|
||||
main() (0x10000234) ← We are here
|
||||
↓ will call (blx at +6)
|
||||
? calls (blx at +6)
|
||||
main() (0x10000234) ? We are here
|
||||
? will call (blx at +6)
|
||||
stdio_init_all() (0x100012c4)
|
||||
```
|
||||
|
||||
@@ -173,21 +173,21 @@ You've now traced the complete path:
|
||||
|
||||
```
|
||||
1. Reset (Power-on)
|
||||
↓
|
||||
?
|
||||
2. Bootrom (0x00000000)
|
||||
↓
|
||||
?
|
||||
3. Vector Table (0x10000000)
|
||||
↓
|
||||
?
|
||||
4. _reset_handler (0x1000015c)
|
||||
↓
|
||||
?
|
||||
5. Data Copy & BSS Clear
|
||||
↓
|
||||
?
|
||||
6. platform_entry (0x10000186)
|
||||
↓
|
||||
?
|
||||
7. runtime_init() (first call)
|
||||
↓
|
||||
8. main() (second call) ← Exercise focus
|
||||
↓
|
||||
?
|
||||
8. main() (second call) ? Exercise focus
|
||||
?
|
||||
9. stdio_init_all() (first line of main)
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user