A small header file to aid printing strings and numbers to the console in assembly (using NASM, AMD64 instruction set).
This header contains 3 functions that aid printing to the console:-
push_ASCII
: For ASCII stringspush_int32_as_ASCII
: For int32 valuesclean_stack
: To clean stack after the previous two functions
Include the library like so:-
%include "print2.asm"
To push strings to the stack, we need to first declare a string. This we shall do in the read-only data segment
section .rodata
string0 DB "Hello, World!"
We also need to define its length
section .rodata
string0 DB "Hello, World!"
.len EQU $ - string0
We can now write code in the text segment, inside our _start
function.
We first clear the R8
register.
section .text
_start:
xor r8, r8
Now, finally we push a number using push_int32_as_ASCII
and the string using push_ASCII
.
(Remember to push in LIFO)
mov rdi, 69
call push_int32_as_ASCII
And,
mov rsi, string0
mov rdx, string0.len
call push_ASCII
We're ready to print both on the console.
We do this by using the kernel's sys_write
function.
mov rax, 1
mov rdi, 1
lea rsi, [rsp + rbx]
mov rdx, r8
syscall
Finally, let's clean the stack
call clean_stack
The whole code is as follows:-
global _start
%include "print2.asm"
section .rodata
string0 DB "Hello, World!"
.len EQU $ - string0
section .text
_start:
push rbp
xor r8, r8
mov rdi, 69
call push_int32_as_ASCII
mov rsi, string0
mov rdx, string0.len
call push_ASCII
mov rax, 1
mov rdi, 1
lea rsi, [rsp + rbx]
mov rdx, r8
syscall
call clean_stack
pop rbp
mov rax, 60
xor rdi, rdi
syscall
The two push functions pack data and start populating the stack from where the RSP
is pointing till it runs out of bytes to populate.
The RSP
decrements by 8 bytes (on 64-bit architecture), and a total of ceil(total_length_of_string/8) push operations are performed.
Thus, it's possible for the RSP
to point at memory filled with 0's (null characters) after return from the first two functions.
The RBX
register is used for this reason to offset the RSP
while accessing the string on the stack.
If during a function call to the two push functions, there's existing string on the stack, the functions automatically pack data on to the stack to avoid any null character in the middle of the string on the stack.
After push_ASCII
or push_int32_as_ASCII
has been called, the stack is ready and a sys_write
function can be performed using RSP + RBX
for the address of the string, with R8
for the number of bytes to print.
Once a sys_write
has been performed, the pushed string can be popped using clean_stack
. This function pops as many bytes as indicated by R8
. Again since the RSP
only moves by 8 bytes (64-bit architecture), it performs a total of ceil(R8
/8) pop operations.
It is important to note that these family of functions make alterations to the caller's stack frame, and so leave a "Positive SP" or "Negative SP" after their function returns.
The code to generate ASCII string from a 32-bit integer was built upon the code obtained from https://codereview.stackexchange.com/q/142842.