Again I want to use inline assembly with GCC. In my previous question I asked about clobbers, now I want to ask about the returned values from system calls.
So let's consider the "write" system call on 64bit Linux. The RAX register takes the number of the system call (classic), the RDI register takes the file descriptor to write in, the RSI takes the pointer to the data (string) to write and the RDX takes the number of bytes to write. The code will be the following (in GCC inline assembly):
Code:
#include "stdio.h"
void write(int fd, const char* buf, unsigned long len) {
asm (
"syscall"
: : "a" (1), "D" (1), "S" (buf), "d" (len)
: "memory", "rcx"
);
}
int main() {
const char* hello = "Hello world!\n";
write(1, hello, 13);
return 0;
}
This will write "Hello world!" to the stdout. Now I just happened to have played a little bit with DDD which is a (very outdated) graphical frontend for GDB. There, I used breakpoints to check the value of the registers after each line. For my surprise, I found out that the value of RAX was changed after the system call and tbh at that time, I didn't knew why that was happening. I was always wondering how system calls return values and where the place them. As you can see, I'm not so smart and I can't connect pieces so easily but at least I'm trying...
As you probably know, "unistd.h" defines function declarations for system calls to be used with the C programing language. The actual definition is in libc. I was looking at the "write" system call and I saw that it returns a "size_t" value. This is when I realized that the value that RAX got after the system call was probably the returned value from the system call so here is how system calls return values (again I'm not so smart). I checked it out and it is indeed true! Let's now consider the following code:
Code:
#include "stdio.h"
unsigned long write(int fd, const char* buf, unsigned long len) {
unsigned long val;
asm (
"syscall"
: "=a"(val) // Output
: "a" (1), "D" (1), "S" (buf), "d" (len) // Input
: "memory", "rcx"
);
return val;
}
int main() {
const char* hello = "Hello world!\n";
const char* name = "John\n";
unsigned long n;
n = write(1, hello, 13);
printf("System call done! You have written %lu characters!\n\n", n);
n = write(1, name, 5);
printf("System call done! You have written %lu characters!\n", n);
return 0;
}
Compiling and executing this code, we will output saying the the first called has written 13 characters and the second has written 5 characters.
Now my question. How would I know which value each system call returns? Now of course I can use the debugger but it is not available in my main home machine and this is tedious. Everything I see seems to talk about "unistd.h" and the libc interface rather than the system call itself. Is there an official documentation? For 64-bit Linux at least...