aarch64-pg
4.5 · Printing a variable from .data

Printing a variable from .data

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.

This lesson loads a value out of .data into a register and passes it to printf.

A review of printing with printf

Printing in Assembly always follows the same four steps.

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

Here the value comes out of a .data variable instead of an immediate, but it still ends up in the same argument register.

Loading values from .data

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 the variable

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...

regfile

N clearZ clearC clearV clear

x0–x30 are the integer registers.

X0arg00x0000000000000000
X1arg10x0000000000000000
X2arg20x0000000000000000
X3arg30x0000000000000000
X4arg40x0000000000000000
X5arg50x0000000000000000
X6arg60x0000000000000000
X7arg70x0000000000000000
X8ind0x0000000000000000
X90x0000000000000000
X100x0000000000000000
X110x0000000000000000
X120x0000000000000000
X130x0000000000000000
X140x0000000000000000
X150x0000000000000000
X16ip00x0000000000000000
X17ip10x0000000000000000
X18pr0x0000000000000000
X190x0000000000000000
X200x0000000000000000
X210x0000000000000000
X220x0000000000000000
X230x0000000000000000
X240x0000000000000000
X250x0000000000000000
X260x0000000000000000
X270x0000000000000000
X280x0000000000000000
X29fp0x0000000000000000
X30lr0x0000000000000000
SP0x0000000080000000
PC0x0000000000400000
console

Output prints here as your program runs.

Step with F10, run with F5, or feed stdin from the box below.

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. Use the format specifier and the register width that match the variable's type.

When calling printf, you must pass arguments whose data types match their corresponding format specifiers. A mismatch prints garbage.