aarch64-pg
4.5 · Printing Variables in ARMv8 AArch64 Assembly

Printing Variables in ARMv8 AArch64 Assembly

note

The Assembly language discussed in this lesson refers specifically to ARMv8 AArch64 Assembly.

prerequisite

Before starting this lesson, you should have a basic understanding of the following prerequisite lesson:

In many Assembly programs, data is stored in variables within the .data section before it is used. While printing values directly using immediate constants is straightforward, accessing and printing values stored in memory requires additional steps.

In this lesson, we will explore how to print variables defined in the .data section in Assembly. We will examine how to access stored values, load them into registers, and pass them correctly to the printf function for output.

A Review of Printing with printf

If you have reached this point, you should be familiar with the general process of printing in Assembly using the printf function. Although printing in Assembly requires more manual preparation compared to high-level programming languages, the overall workflow remains consistent.

The printing process can be summarized into the following steps:

  1. Define the string or format string.
  2. Load the address of the string or format string into x0.
  3. Place the corresponding value to be printed into the appropriate argument registers (if any).
  4. Call printf.

These steps remain the same when printing variables stored in the .data section. However, the main difference is found in step 3.

In previous lessons, the values being printed were provided directly as immediate values and moved into the appropriate argument registers. For example:

mov x1, 67

In this lesson, we will follow the same principle of placing the value to be printed into the appropriate argument register. However, instead of moving an immediate value directly into the register, we will first retrieve the value stored in a variable defined in the .data section and then pass it to printf.

Loading Values from the .data Section

Suppose we have an integer value of 85 stored in a variable named importantNumber within the .data section, as shown below:

    .data    importantNumber: .word 85
Open in playground

To load the value stored in the importantNumber variable defined in the .data section, we must first retrieve the address of the variable. Once the address has been obtained, we can use it to access and load the value stored at that memory location. The following code demonstrates this process:

// pseudo-ops and directivesmain:     // program prologue    ..    ldr x21, =importantNumber    ldr w1, [x21]    ..    // program epilogue
Open in playground

The ldr instruction is used to retrieve both the address of the variable and the value stored at that address. In this process, two ldr instructions are required.

The first ldr instruction retrieves the address of the variable and stores it in register x21. The second ldr instruction uses this address to access the value stored in memory and loads the retrieved value into register w1. A .word is 32 bits, so the load uses the 32-bit w form of the register; an x1 load would drag in the 4 bytes stored after the variable too. The register x0 is preserved for the address of the format string, which is passed to printf as the first argument.

note

The square brackets [] in the ldr instruction indicate memory access (dereferencing). They tell the processor to use the value inside the register as a memory address and access the data stored at that location.

Printing Out the Variable Content

Once the variable value and format string have been loaded into the appropriate argument registers, the printf function can be called to display the formatted output. The following code demonstrates the complete process:

loading editor...
figure 4.5.1runnable — step it and watch the registersOpen in playground

note

You can print variables of any data type using the process shown above. An important consideration is ensuring that you use the correct format specifier and the appropriate registers when retrieving values from variables defined in the .data section.

When calling printf, you must pass arguments whose data types match their corresponding format specifiers. Passing an argument whose type does not match the expected format specifier may result in unintended or incorrect output.