Linux syscall,libc,VDSO和实现剖析 [英] Linux syscall, libc, VDSO and implementation dissection

查看:118
本文介绍了Linux syscall,libc,VDSO和实现剖析的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在最后一个libc中剖析了syscall调用:

I dissects the syscall call in the last libc:

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

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

And I have this code in 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中.

If I understand well this code, the LOADREGS_##nr(args) macro loads the argument in the registers ebx, ecx, edx, esi, edx and ebp.

sysdeps/unix/sysv/linux/i386/sysdep.h

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的代码在哪里?这是上面的代码吗?我不了解执行情况. 以下代码将第6个参数加载到ebx寄存器中?

Where is the code which load the argument in the registers ebx, ecx, edx, esi, edx and ebp? it's this code above? I don't understand the implementation. the following code load the 6th argument in the ebx register?

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

这段代码是什么:

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

它将第一个参数加载到ebx寄存器中吗?

It loads the first argument in the ebx register?

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

Then the "call *%%gs:%P2" jump to the VDSO code ? this code correspond to "call *gs:0x10"?

那么,下面的用于写系统调用的图表就好了吗?:

so, this following diagram for the write syscall, it's good?:

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

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

I doesn't understand the VDSO utility! the vdso choose the syscall method (sysenter or int 0x80).

在此先感谢您的帮助.抱歉,我的英语非常不好.

Thank's you in advance for your help. And sorry my inglish is very bad.

推荐答案

glibc的系统调用中涉及的宏将扩展为以下内容,例如退出系统调用的示例.

The macros involved in glibc's syscalls will expand to something like the following, for the example of the exit syscall.

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_*(...)仅在提供更多参数时才需要调整寄存器.

LOADREGS_1(args) will expand to LOADREGS_0(), which will expand to nothing - LOADREGS_*(...) only need to adjust registers when more parameters are provided.

ASMARGS_1(args)将扩展为ASMARGS_0 (), "b" ((unsigned int) (arg1)),然后将扩展为, "b" ((unsigned int) (arg1).

ASMARGS_1(args) will expand to ASMARGS_0 (), "b" ((unsigned int) (arg1)), which will expand to , "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应该如何为系统调用做准备以及在系统调用完成后如何继续.

ASMARGS_* don't actually execute code per se - they're instructions to gcc to make sure that certain values (such as (unsigned int) (arg1)) are in certain registers (such as b, aka ebx). As such, the combination of parameters to asm volatile (which isn't a function, of course, but just a gcc builtin) simply specify how gcc should prepare for the syscall and how it should continue after the syscall completes.

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

Now, the generated assembly will look something like this:

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

%gs是一个段寄存器,它引用线程本地存储-特别地,glibc引用了一个指向VDSO的保存值,该值在第一次解析告诉它VDSO所在位置的ELF标头时存储在该值中.

%gs is a segment register that references thread-local storage - specifically, glibc is referencing a saved value that points to the VDSO, which it stored there when it first parsed the ELF headers that told it where the VDSO was at.

一旦代码进入VDSO,我们将无法确切知道会发生什么-它取决于内核版本-但我们确实知道它使用最有效的可用机制来运行syscall,例如sysenter指令或int 0x80指令.

Once the code enters the VDSO, we don't know exactly what happens - it varies depending on the kernel version - but we do know that it uses the most efficient available mechanism to run a syscall, such as the sysenter instruction or the int 0x80 instruction.

是的,您的图表是准确的:

So, yes, your diagram is accurate:

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

这是一个更简单的代码示例,可从我维护的名为 libsyscall :

Here's a simpler example of code to call into the VDSO, specifically for one-parameter syscalls, from a library that I maintain called libsyscall:

_lsc_syscall1:
    xchgl 8(%esp), %ebx
    movl 4(%esp), %eax
    call *_lsc_vdso_ptr(,1)
    movl 8(%esp), %ebx
    # pass %eax out
    ret

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

This simply moves parameters from the stack into registers, calls into the VDSO via a pointer loaded from memory, restores the other registers to their previous state, and returns the result of the syscall.

这篇关于Linux syscall,libc,VDSO和实现剖析的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆