First of all, don't use int 0x80
in 64-bit code; use the syscall
interface instead.
The write()
system call requires a pointer to the data to be written. If you put 0x41
in that register (rcx
in your code but should be rsi
with syscall
), it will attempt to write whatever data is at address 0x41, which of course will fail because that page is not mapped. If you check the return value from the system call, which is always a good idea, you'll see -EFAULT
.
This might seem slightly inefficient for writing a single byte. You might wish there were a separate system call similar to C's putc
which writes just a single character, passed in a register. But this is a rare enough case that it likely wouldn't be worth the trouble of implementing it; most applications will buffer their writes so as not to have to make a system call for each byte. Anyway, the overhead of using memory is negligible compared to the much greater overhead of making a system call in the first place.
So your letter A
has to be in memory somewhere. But it doesn't necessarily have to be in the .data
section. Any other section of static data would be fine: .rodata
or .bss
if you initialize it at runtime, or even in .text
if you're careful not to execute it.
Another perfectly good option is to put it on the stack.
push 0x41 ; actually pushes 8 bytes but that's okay,
; `0x41` is the low byte because x86 is little-endian
mov rsi, rsp ; `rsp` points at what was most recently pushed
mov edi, 1 ; file descriptor
; it's ok to use edi instead of rdi because writes to 32-bit
; registers are zero extended
mov edx, 1 ; number of bytes to write
mov eax, 1 ; system call number for write()
; syscall uses different numbers than int 0x80
syscall
add rsp, 8 ; clean up the stack.
; pop rdi or any other unneeded register would work too, and be shorter