Printing to the Terminal
The C Standard Library contains a function called printf
, which can be used for printing to the terminal (stdout
). You can call the function directly from your program and the linker will make sure the call goes to the actual subroutine once your program is built.
Format String
The function takes a variable number of arguments, but at least one. The first argument is always an ASCII string, commonly called the format string. A string is simply an array of characters, terminated with a zero byte.
As you may have noted, arguments are passed to a subroutine through 64-bit registers. So how do you fit a whole string in a register? You don't. Instead, printf
expects the first argument to be a memory address specifying the location of the first byte of a string.
In Assembly, you can define a string as part of your .text
section with the assembler directive .ascii
(not zero terminated) or .asciz
(zero terminated). The address of that location can then be retrieved using the leaq
(Load Effective Address) instruction.
Example: Simple Printf Call
The following call in a C program:
would look something like this in Assembly:
Let's break down what is happening here:
.text
: this directive tells the assembler that the following lines should be placed in the text section of the program - remember that the text section is a read-only section mainly holding the program instructionshello_world
: this is simply a label, such that the position in the code can be referred to by name - any usage of the label will be replaced by the actual address by the assembler.asciz
: this directive tells the assembler that the following string should be placed in the text section as ASCII-encoded characters, followed by a zero byteleaq hello_world(%rip)
: this instruction will load the address of thehello_world
label into the registerrdi
- so as the first argument forprintf
movb $0, %al
: asprintf
takes a variable number of arguments, the number of vector registers used (e.g., for floating point values) needs to be specified - in the case of the mandatory assignments, this will always be 0.
You may note that the instruction to load the address looks a bit confusing. Specifically the use of (%rip)
. This is needed due to something called Position-Independent Executables (PIE). The full explanation goes beyond the scope of this Manual, however, the gist is that your program may be loaded at different memory addresses (especially in the context of shared libraries). Thereby, the address of the string is specified relative to the instruction pointer, as the relative distance between the two will be the same no matter where in memory the program is.
Additional Arguments
In addition to "simple" printing, printf
can further be used to print strings that include variables, which are passed as additional arguments. To indicate where in the string such variables should be placed, printf
has so-called format specifiers. The most commonly used format specifiers are:
%d
/%u
: signed/unsigned integer (32 bits on most systems)%ld
/%lu
: signed/unsigned long (64 bits on most systems)%x
/%lx
: hexadecimal integer/long%f
: floating point number (even though%lf
also exists,%f
equally refers to double-precision floats in most implementations)%s
: zero-terminated ASCII string
Anytime such a format specifier is encountered in the format string, printf
will take the next argument and substitute its string representation into the final output.
Example: Printf with Additional Arguments
The following call in a C program:
would look something like this in Assembly:
and produce the following output:
Last updated