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:
sysdeps/unix/sysv/linux/sysdep.h
:#define INTERNAL_SYSCALL_ERROR_P(val) ((unsigned long int)(val) > -4096UL)
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.
syscall
doesn't seem to set the carry flag.
Otherwise, like we can see on this syscall
manual, if rax
is negative (lower than 0), it indicates an error.
Since rax
in struct user_regs_struct
is unsigned, we just have to cast it in signed, and we can know if syscall returned and error, and what error (-rax
to get errno code).
int main(int argc, const char *argv[])
{
struct user_regs_struct regs;
// strace logic ...
if ((long long)regs.rax < 0)
printf(" = -1 %s (%s)\n", strerrorname_np((int)-regs.rax), strerror((int)-regs.rax));
else
printf(" = %llu\n", regs.rax);
// ...
}