首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Linux系统调用、libc、VDSO及其实现剖析

Linux系统调用、libc、VDSO及其实现剖析
EN

Stack Overflow用户
提问于 2016-01-31 23:19:00
回答 1查看 545关注 0票数 7

我分析了最后一个libc中的syscall调用:

代码语言:javascript
复制
git clone git://sourceware.org/git/glibc.git

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

代码语言:javascript
复制
#   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

代码语言:javascript
复制
# 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寄存器中的第六个参数?

代码语言:javascript
复制
register unsigned int _a6 asm ("ebp") = (unsigned int) (arg6);

这段代码是做什么的:

代码语言:javascript
复制
ASMARGS_0 (), "b" ((unsigned int) (arg1))

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

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

那么,下面的图表对于写入syscall,它是好的吗?

代码语言:javascript
复制
write(1, "A", 1)  ----->   LIBC   ----->   VDSO   -----> KERNEL
                          load reg           ?   
                        jump to vdso 
|---------------------------------------------------|--------------|
       user land                                       kernel land

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

提前感谢您的帮助。对不起,我的英语很差。

EN

回答 1

Stack Overflow用户

发布于 2016-05-30 08:19:40

glibc的syscall中涉及的宏将扩展为如下所示,例如退出syscall。

代码语言:javascript
复制
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)

x86上的__NR_exit为1。

因此,代码将扩展为类似以下内容:

代码语言:javascript
复制
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应该如何准备系统调用,以及在系统调用完成后它应该如何继续。

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

代码语言:javascript
复制
; set up other registers...
movl $1, %eax
call *%gs:0x10
; tear down

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

一旦代码进入VDSO,我们不知道到底发生了什么-它取决于内核版本-但我们知道它使用最有效的可用机制来运行系统调用,例如sysenter指令或int 0x80指令。

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

代码语言:javascript
复制
write(1, "A", 1)  ----->   LIBC   ----->   VDSO   -----> KERNEL
                          load reg           ?   
                        jump to vdso 
|---------------------------------------------------|--------------|
       user land                                       kernel land

下面是一个更简单的代码示例,它是从我维护的一个名为libsyscall的库中调用到VDSO的,特别是针对单参数syscall

代码语言:javascript
复制
_lsc_syscall1:
    xchgl 8(%esp), %ebx
    movl 4(%esp), %eax
    call *_lsc_vdso_ptr(,1)
    movl 8(%esp), %ebx
    # pass %eax out
    ret

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

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35115470

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档