【问题标题】:How to find which type of system call is used by a program如何查找程序使用哪种类型的系统调用
【发布时间】:2011-02-14 09:17:42
【问题描述】:

我正在使用 x86_64 机器。我的 linux 内核也是 64 位内核。由于有不同的方式来实现系统调用(int 80、syscall、sysenter),我想知道我的机器正在使用什么类型的系统调用。我是linux的新手。我写了一个演示程序。

#include <stdio.h>
int main()
{
  getpid();
  return 0;
}

getpid() 执行一次系统调用。谁能给我一个方法来找到我的机器将使用哪种类型的系统调用来执行这个程序..谢谢....

【问题讨论】:

    标签: linux system-calls


    【解决方案1】:

    在linux下,可以使用strace记录某个特定进程进行了哪些系统调用。

    【讨论】:

    • 这不能回答问题。 strace 将显示进行了哪些系统调用(在本例中为 getpid),但不会告诉用户具体的机制。
    • OP 正在询问如何确定具体的系统调用机制(int 80、syscall、sysenter)。 strace 只是告诉你正在进行的系统调用的名称。
    【解决方案2】:
    victory:~ # gcc getpid.c -o getpid -g
    victory:~ # gdb getpid
    <snip>
    (gdb) break main
    Breakpoint 1 at 0x400540: file getpid.c, line 4.
    (gdb) run
    Starting program: /root/getpid 
    
    Breakpoint 1, main () at getpid.c:4
    4     getpid();
    (gdb) disassemble
    Dump of assembler code for function main:
    0x000000000040053c <main+0>:    push   %rbp
    0x000000000040053d <main+1>:    mov    %rsp,%rbp
    0x0000000000400540 <main+4>:    mov    $0x0,%eax
    0x0000000000400545 <main+9>:    callq  0x400440 <getpid@plt>
    0x000000000040054a <main+14>:   mov    $0x0,%eax
    0x000000000040054f <main+19>:   leaveq 
    0x0000000000400550 <main+20>:   retq   
    End of assembler dump.
    

    看起来我们对 getpid() 的调用实际上是一个库调用。让我们在那里设置一个断点并继续。

    (gdb) break getpid
    Breakpoint 2 at 0x7ffff7b29c00
    (gdb) cont
    Continuing.
    
    Breakpoint 2, 0x00007ffff7b29c00 in getpid () from /lib64/libc.so.6
    (gdb) disassemble
    Dump of assembler code for function getpid:
    0x00007ffff7b29c00 <getpid+0>:  mov    %fs:0x94,%edx
    0x00007ffff7b29c08 <getpid+8>:  cmp    $0x0,%edx
    0x00007ffff7b29c0b <getpid+11>: mov    %edx,%eax
    0x00007ffff7b29c0d <getpid+13>: jle    0x7ffff7b29c11 <getpid+17>
    0x00007ffff7b29c0f <getpid+15>: repz retq 
    0x00007ffff7b29c11 <getpid+17>: jne    0x7ffff7b29c1f <getpid+31>
    0x00007ffff7b29c13 <getpid+19>: mov    %fs:0x90,%eax
    0x00007ffff7b29c1b <getpid+27>: test   %eax,%eax
    0x00007ffff7b29c1d <getpid+29>: jne    0x7ffff7b29c0f <getpid+15>
    0x00007ffff7b29c1f <getpid+31>: mov    $0x27,%eax
    0x00007ffff7b29c24 <getpid+36>: syscall 
    0x00007ffff7b29c26 <getpid+38>: test   %edx,%edx
    0x00007ffff7b29c28 <getpid+40>: mov    %rax,%rsi
    0x00007ffff7b29c2b <getpid+43>: jne    0x7ffff7b29c0f <getpid+15>
    0x00007ffff7b29c2d <getpid+45>: mov    %esi,%fs:0x90
    0x00007ffff7b29c35 <getpid+53>: mov    %esi,%eax
    0x00007ffff7b29c37 <getpid+55>: retq   
    End of assembler dump.
    

    隐藏在 getpid() 库中的是 syscall 汇编指令。这是一条 AMD64 指令,支持快速上下文切换到 ring0 以进行系统调用。

    【讨论】:

    【解决方案3】:

    一种方法是使用 gdb 单步执行机器代码(使用stepi),直到到达启动系统调用的指令。因为不同的机器把指令放在不同的地方(有时在系统调用包装器本身,有时在系统调用包装器调用的函数中),我无法预测指令的确切位置。

    例如,在一台旧机器上,getpid 本身执行了一个 int 0x80,而在一台较新的机器上,getpid 执行了一个 call *gs:0x10,这将它带到了 __kernel_vsyscall,它执行了一个 sysenter

    【讨论】:

    • 你的反引号出错了。
    • 在 amd64 中它不调用 *gs:0x10 它只调用 callq。它确实在进程空间上映射了一个 vdso 页面。但是 --kernel_vsyscall 没有被调用。从Mikey的回答中可以看出,它直接调用syscall而不经过__kernel_vsyscall....为什么会这样???
    • @bala1486 - libc 的不同版本。 MikeyB 很好地展示了他如何找出他的 libc/OS 版本的具体机制。但是你应该按照他的技术来看看你的 libc 版本是否做同样的事情。另外,假设你喜欢他的回答,你应该接受它。
    【解决方案4】:

    我为此创建了一个基于 strace 的简单工具。 它完全按照您的要求执行:

    ubuntu@pc:~$ ./syscalls whoami
    ubuntu
    The following syscalls were called:
    access
    arch_prctl
    brk
    close
    connect
    execve
    exit_group
    fstat
    geteuid
    lseek
    mmap
    mprotect
    munmap
    open
    read
    socket
    write
    The syscalls were saved to /home/ubuntu/syscalls.txt
    

    https://github.com/avilum/syscalls

    【讨论】:

    • 虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-03
    • 1970-01-01
    • 2015-10-22
    • 2013-12-18
    • 1970-01-01
    • 2012-04-19
    • 2020-01-04
    相关资源
    最近更新 更多