I try to reproduce strace behaviour in C, using ptrace.
I would like to check if a syscall returned an error (and what error1), but I don't know how to do this?
Here is an example of an output of strace I try to reproduce:
execve("./exit", ["./exit"], 0x7ffff059f250 /* 62 vars */) = 0
read(42, NULL, 0) = -1 EBADF (Bad file descriptor)
exit(0) = ?
+++ exited with 0 +++
I tried to compare the return value of the syscall2 with -1, but they aren't equal.
I see in GLIBC syscall's implementation (sysdeps/unix/sysv/linux/syscall.c) that INTERNAL_SYSCALL_ERROR_P macro is used, which seems to be a macro for internal use of GLIBC. I found two implementations of this macro:
- First one, in
sysdeps/unix/sysv/linux/sysdep.h:
#define INTERNAL_SYSCALL_ERROR_P(val) ((unsigned long int)(val) > -4096UL)
- Second one, in
sysdeps/unix/sysv/linux/x86_64/x32/times.c:
#define INTERNAL_SYSCALL_ERROR_P(val) ((unsigned long long int)(val) >= -4095LL)
Since the first one is in a header directly related to the system (unlike the second one), I would tend to say that this is the one used by syscall. But I don't know if they are generics and so if I can use it.
1To recover the errno code, inverting the syscall return value (-rax) seems to work.
2With ptrace(PTRACE_GETREGS, ...), we can get the child registers, and store them in a struct user_regs_struct (defined in sys/user.h). Accessing the rax member of this structure, we have the return value of the syscall.
I think I found the solution.
syscalldoesn't seem to set the carry flag.Otherwise, like we can see on this
syscallmanual, ifraxis negative (lower than 0), it indicates an error.Since
raxinstruct user_regs_structis unsigned, we just have to cast it in signed, and we can know if syscall returned and error, and what error (-raxto get errno code).