How Dart Loads Objects from Its Object Pool on ARM64 and ARM32 🛠️

Posted on Jul 14, 2024

Dart’s object loading mechanism differs slightly depending on the architecture it’s running on, be it ARM64 (ARMv8) or ARM32 (ARMv7). In this guide, we’ll explore how Dart loads objects from its object pool on these architectures and provide examples to make it clearer.


1️⃣ Dart Object Loading on ARM64 (ARMv8)

In ARM64, Dart loads objects using the following pattern:

Example:

If an object is located at address 0x95a7, the loading process would look like this:

add x16, x27, 0x9, lsl #12
ldr x16, [x16, 0x5a7]

Usual Format:

add x(anyReg*), PP, 0x9, lsl #12
ldr x(anyReg*), [x(anyReg*), 0x5a7]

Here:

  • x(anyReg*): This can be any general-purpose register.
  • PP: Pool Pointer register, which is x27 for ARM64.

📌 Important: The add instruction calculates the address by shifting the immediate value. The ldr instruction then loads the object from the calculated address by adding it to the Pool Pointer (PP).


2️⃣ Dart Object Loading on ARM32 (ARMv7)

In ARM32, the object loading mechanism is a bit different but follows a similar pattern.

Example:

If an object is located at address 0x95a7, the loading process would look like this:

add r0, r5, 0x9000
ldr r0, [r0, 0x5a7]

Usual Format:

add r(anyReg*), PP, 0x9000
ldr r(anyReg*), [r(anyReg*), 0x5a7]

Here:

  • r(anyReg*): This can be any general-purpose register.
  • PP: Pool Pointer register, which is r5 for ARM32.

📌 Important: The add instruction calculates the address by adding an immediate value to the Pool Pointer (PP). The ldr instruction then loads the object from the calculated address.


📚 Register and Pool Pointer Details

  • ARM64 (ARMv8): The Pool Pointer register (PP) is x27. => Source
  • ARM32 (ARMv7): The Pool Pointer register (PP) is r5. => Source

🧮 Calculating Offsets for Object Addresses

Dart uses an index-based calculation to determine object addresses inside the object pool. The offset calculation is different for ARM64 and ARM32:

General Calculation:

  • ARM64: PP + (index * 8) + [0-7]
  • ARM32: PP + (index * 4) + [0-7]

This calculation provides the object address, which is then used in the assembly code to locate the object.

Normal Calculation:

  • ARM64: PP + (index * 8) + 0
  • ARM32: PP + (index * 4) + 3

This is a more specific calculation where the final constant is used to find the precise object address in the assembly code.


🎥 Further Explanation and Resources

For a deeper dive into how ARM64 (ARMv8) handles object loading, you can watch this explanation video by @cryptax at nullcon 2024. It provides a more in-depth explanation of the assembly code and calculations involved in object loading on ARM64.

Additionally, you can check out this presentation slide deck for more details.


By understanding these patterns and calculations, you’ll gain better insights into how Dart handles object loading on different ARM architectures.

Happy reversing! 🔧💻