How to Trace a System Call in Linux

How to see system call that executed in current time by process?

proc offers some information about what the kernel is currently doing "for" a process


/proc/${pid}/syscall
/proc/${pid}/stack

More information:

  • http://man7.org/linux/man-pages/man5/proc.5.html
  • http://blog.tanelpoder.com/2013/02/21/peeking-into-linux-kernel-land-using-proc-filesystem-for-quickndirty-troubleshooting/

Can strace tell me where in my code a syscall is called?

Using gdb, you can set conditional syscall catchpoints based on the args to the system call (analogous to the way you'd set conditional breakpoints on entry to a function based on the args to the function call). Then, when the catchpoint is triggered, you can see where the caller is (file name, line number, and source code).

Here's an example for x86_64.

$ cat gtest.c
#include <unistd.h>
int main()
{
write(1, "text\n", 5);
write(2, "text2\n", 6);
write(2, "\n", 1);
return 0;
}

$ cc gtest.c -g -o gtest


$ gdb -q gtest
Reading symbols from gtest...done.
(gdb) list
1 #include <unistd.h>
2 int main()
3 {
4 write(1, "text\n", 5);
5 write(2, "text2\n", 6);
6 write(2, "\n", 1);
7 return 0;
8 }
(gdb) catch syscall write
Catchpoint 1 (syscall 'write' [1])
(gdb) condition 1 $rdi == 2 && *(char *)$rsi == '\n' && $rdx == 1
(gdb) r
Starting program: /home/mp/gtest
text
text2

Catchpoint 1 (call to syscall write), 0x00007fffff13b970 in __write_nocancel ()
at ../sysdeps/unix/syscall-template.S:84
84 ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) bt
#0 0x00007fffff13b970 in __write_nocancel () at ../sysdeps/unix/syscall-template.S:84
#1 0x00000000080006f6 in main () at gtest.c:6
(gdb) up
#1 0x00000000080006f6 in main () at gtest.c:6
6 write(2, "\n", 1);
(gdb)

Tracing a C blocking system call

Whenever a process issues a system call (such as a blocking read(2)), the process starts to execute in kernel mode, that is, the kernel code that handles the particular system call is invoked.

After that, depending on the underlying device and the driver, the process can be suspended and put in a wait-queue. When a key is pressed, the kernel code that handles interrupts is invoked and from there it is deducted which key is pressed.

The kernel then resumes the process that is waiting for this input, and delivers the data by copying it from the kernel address space to the particular process' address space.

How to retrieve arguments passed to linux system call in C?

The arguments passed to open (and any other case a system call receives a string) are pointers to the string's location in memory.

Unfortunately for you, these pointers are for the debugee's address space, and cannot be directly referenced from the debugger's address space.

The ptrace documentation would have you repeatedly call ptrace(PTRACE_PEEKDATA...) to obtain the string, 4 (or 8, depending on platform) bytes at a time. You can see an example of how to do that in the old fakeroot-ng code. Note that that code is under the GPL, so don't copy verbatim unless you are writing code of a compatible license.

Fortunately, there is a method of getting the data that is both simpler and faster. It is less portable, but if you're writing ptrace code, you usually don't care.

The tracer thread can open the file /proc/pid/mem (where pid is, of course, the numerical pid of the debugee). Note that only the ptracer of pid can open that file.

Once that file is open, you can pread(2) from it at the offset of the address you want, and get the data directly from the debugee's address space. Since the read interface isn't limited to four bytes, you can guess at the string's length, read that much bytes in, and then find the terminating NULL and know the precise string.

Since you are invoking less system calls, this method is not only easier, it is also much faster.



Related Topics



Leave a reply



Submit