The other review hit the most important parts, but there are a few more things to consider.
Consider using the standard C interface
If the code is instead written like this:
; IN: rdi points to NUL-terminated string
; OUT: rax contains string length
xor rax, rax
cmp byte [rdi + rax], 0
This would have the advantage of being callable from C.
Use named constants
Instead of having "magic numbers" littering the code, it's better to define named constants. For example the number
1 is used in two different ways; once for the
WRITE syscall, and once for the
stdout file handle. I'd recommend defining and using one named constant for each.
Consider more general usage
As you mention in a comment, the only difference between
fputs is the file handle. In this case, one could get both
fputs very cheaply like this:
mov rdi, 1 ; fd for stdout
mov rax, 1 ; WRITE syscall
Note that this uses your existing calling convention rather than the C calling convention.
Consider using macros
You may find it useful to define some macros for common things like this:
%macro SYSTEM 1
mov rax, %1
WRITE: equ 1