Linux系统调用,libc,VDSO和实现解析

时间:2016-01-31 15:19:00

标签: c linux system-calls libc vdso

我在最后一个libc中解析了系统调用:

git clone git://sourceware.org/git/glibc.git

我在sysdeps / unix / sysv / linux / i386 / sysdep.h中有这段代码:

#   define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
LOADREGS_##nr(args)                         \
asm volatile (                          \
"call *%%gs:%P2"                            \
: "=a" (resultvar)                          \
: "a" (__NR_##name), "i" (offsetof (tcbhead_t, sysinfo))        \
  ASMARGS_##nr(args) : "memory", "cc")

如果我理解这段代码,LOADREGS _ ## nr(args)宏会将参数加载到寄存器ebx,ecx,edx,esi,edx和ebp中。

sysdeps / UNIX / SYSV / LINUX / I386 / sysdep.h中

# define LOADREGS_0()
# define ASMARGS_0()
# define LOADREGS_1(arg1) \
    LOADREGS_0 ()
# define ASMARGS_1(arg1) \
    ASMARGS_0 (), "b" ((unsigned int) (arg1))
# define LOADREGS_2(arg1, arg2) \
    LOADREGS_1 (arg1)
# define ASMARGS_2(arg1, arg2) \
    ASMARGS_1 (arg1), "c" ((unsigned int) (arg2))
# define LOADREGS_3(arg1, arg2, arg3) \
    LOADREGS_2 (arg1, arg2)
# define ASMARGS_3(arg1, arg2, arg3) \
    ASMARGS_2 (arg1, arg2), "d" ((unsigned int) (arg3))
# define LOADREGS_4(arg1, arg2, arg3, arg4) \
    LOADREGS_3 (arg1, arg2, arg3)
# define ASMARGS_4(arg1, arg2, arg3, arg4) \
    ASMARGS_3 (arg1, arg2, arg3), "S" ((unsigned int) (arg4))
# define LOADREGS_5(arg1, arg2, arg3, arg4, arg5) \
    LOADREGS_4 (arg1, arg2, arg3, arg4)
# define ASMARGS_5(arg1, arg2, arg3, arg4, arg5) \
    ASMARGS_4 (arg1, arg2, arg3, arg4), "D" ((unsigned int) (arg5))
# define LOADREGS_6(arg1, arg2, arg3, arg4, arg5, arg6) \
    register unsigned int _a6 asm ("ebp") = (unsigned int) (arg6); \
    LOADREGS_5 (arg1, arg2, arg3, arg4, arg5)
# define ASMARGS_6(arg1, arg2, arg3, arg4, arg5, arg6) \
    ASMARGS_5 (arg1, arg2, arg3, arg4, arg5), "r" (_a6)
#endif /* GCC 5  */
    enter code here

在ebx,ecx,edx,esi,edx和ebp寄存器中加载参数的代码在哪里?它上面的代码是什么?我不了解实施情况。 以下代码加载ebx寄存器中的第6个参数?

register unsigned int _a6 asm ("ebp") = (unsigned int) (arg6);

这段代码是什么:

ASMARGS_0 (), "b" ((unsigned int) (arg1))

它加载ebx寄存器中的第一个参数?

然后"调用* %% gs:%P2"跳转到VDSO代码?此代码对应于"调用* gs:0x10"?

所以,这个写系统调用的下图,它有用吗?:

write(1, "A", 1)  ----->   LIBC   ----->   VDSO   -----> KERNEL
                          load reg           ?   
                        jump to vdso 
|---------------------------------------------------|--------------|
       user land                                       kernel land

我不了解VDSO实用程序! vdso选择syscall方法(sysenter或int 0x80)。

先谢谢你的帮助。抱歉,我的英语非常糟糕。

1 个答案:

答案 0 :(得分:3)

对于退出系统调用的例子,glibc的系统调用中涉及的宏将扩展为类似以下的内容。

LOADREGS_1(args)
asm volatile (
"call *%%gs:%P2"
: "=a" (resultvar)
: "a" (__NR_exit), "i" (offsetof (tcbhead_t, sysinfo))
  ASMARGS_1(args) : "memory", "cc")

LOADREGS_1(args)将扩展为LOADREGS_0(),这将扩展为空 - LOADREGS_*(...)只需在提供更多参数时调整寄存器。

ASMARGS_1(args)将扩展为ASMARGS_0 (), "b" ((unsigned int) (arg1)),这将扩展为, "b" ((unsigned int) (arg1)

__NR_exit在x86上为1。

因此,代码将扩展为:

asm volatile (
"call *%%gs:%P2"
: "=a" (resultvar)
: "a" (1), "i" (offsetof (tcbhead_t, sysinfo))
, "b" ((unsigned int) (arg1) : "memory", "cc")

ASMARGS_*实际上并不执行代码本身 - 它们是gcc的指示,以确保某些值(例如(unsigned int) (arg1))是在某些寄存器中(例如b,又名ebx)。因此,asm volatile的参数组合(当然,这不是一个函数,而只是一个内置的gcc)只是指定了gcc应该如何为系统调用做准备以及它应该如何继续系统调用完成。

现在,生成的程序集将如下所示:

; set up other registers...
movl $1, %eax
call *%gs:0x10
; tear down

%gs是一个引用线程本地存储的段寄存器 - 具体来说,glibc引用一个指向VDSO的保存值,它在第一次解析ELF头时存储在那里,告诉它VDSO在哪里是的。

一旦代码进入VDSO,我们就不确切知道发生了什么 - 它根据内核版本而有所不同 - 但我们知道它使用最有效的可用机制来运行系统调用,例如{{1指令或sysenter指令。

所以,是的,你的图表是准确的:

int 0x80

这是一个更简单的代码调用VDSO,特别是对于单参数系统调用,来自我称之为libsyscall的库:

write(1, "A", 1)  ----->   LIBC   ----->   VDSO   -----> KERNEL
                          load reg           ?   
                        jump to vdso 
|---------------------------------------------------|--------------|
       user land                                       kernel land

这只是将参数从堆栈移到寄存器中,通过从内存加载的指针调用VDSO,将其他寄存器恢复到之前的状态,并返回系统调用的结果。

相关问题