Pretty obviously read
returns a length in RAX. This is totally normal for functions, and makes sense for a macro, too.
In this case, yes, it's just a (buggy) wrapper around the 32-bit int 0x80
ABI for read()
which as the man page explains returns a length. Linux system calls do return in RAX. It also strangely hard-codes the max length as 20.
(It's buggy because it only works if EAX, EBX, and EDX are already zero. It only writes the low 16 bits of those registers before invoking the 32-bit ABI. Writing the full RCX with an address is useless; int 0x80
only uses the low 32 bits. What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?)
AL
is the low byte of RAX, and this code only saves the low byte of the length for printing. IDK why they save it in memory instead of another register like a normal person. Especially when they copy the whole 64-bit length to RCX instead of a normal mov ecx, eax
to zero-extend a 32-bit value into RCX.
Also note that the later mov ax,1
(32-bit __NR_exit
) is risky and a bad idea; it potentially leaves garbage in the high bytes of RAX leading to -ENOSYS
instead of _exit(0)
,
With the small buffer size read will fault (or return -EFAULT
before it can return more than 4096, so replacing only the low 16 bits of RAX is safe in this case.
Unless read
returns a negative error code; then this program will crash instead of exiting when the last int 0x80
returns -ENOSYS
instead of exiting.
Try running it with ./a.out <&-
to close stdin, leading to -EBADF
for a read from stdin, then a crash.
Also, this is 64-bit code so using the 32-bit int 0x80
Linux system-call ABI is not a good idea. A few Linux systems (including WSL) don't have CONFIG_IA32_EMULATION
and will just fault on that like they would for int 0x81
or any other software interrupt. What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?