Linux系统调用,libc,VDSO和实现解析
我在最后一个libc中剖析了syscall调用:
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,arg3) ASMARGS_2 (arg1,arg2),"d" ((unsigned int) (arg3)) # define LOADREGS_4(arg1,arg3,arg4) LOADREGS_3 (arg1,arg3) # define ASMARGS_4(arg1,arg4) ASMARGS_3 (arg1,arg3),"S" ((unsigned int) (arg4)) # define LOADREGS_5(arg1,arg4,arg5) LOADREGS_4 (arg1,arg4) # define ASMARGS_5(arg1,arg5) ASMARGS_4 (arg1,arg4),"D" ((unsigned int) (arg5)) # define LOADREGS_6(arg1,arg5,arg6) register unsigned int _a6 asm ("ebp") = (unsigned int) (arg6); LOADREGS_5 (arg1,arg5) # define ASMARGS_6(arg1,arg6) ASMARGS_5 (arg1,arg5),"r" (_a6) #endif /* GCC 5 */ enter code here 在ebx,edx和ebp寄存器中加载参数的代码在哪里?这是上面的代码吗?我不明白实施. register unsigned int _a6 asm ("ebp") = (unsigned int) (arg6); 这段代码是什么: ASMARGS_0 (),"b" ((unsigned int) (arg1)) 它加载ebx寄存器中的第一个参数? 然后“call * %% gs:%P2”跳转到VDSO代码?这段代码对应“call * gs:0x10”? 那么,这个写系统调用的下图,它很好吗?: write(1,"A",1) -----> LIBC -----> VDSO -----> KERNEL load reg ? jump to vdso |---------------------------------------------------|--------------| user land kernel land 我不懂VDSO实用程序! vdso选择syscall方法(sysenter或int 0x80). 提前谢谢你的帮助.抱歉,我的英语非常糟糕. 解决方法
对于退出系统调用的例子,glibc的系统调用中涉及的宏将扩展为类似下面的内容.
LOADREGS_1(args) asm volatile ( "call *%%gs:%P2" : "=a" (resultvar) : "a" (__NR_exit),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. 因此,代码将扩展为: asm volatile ( "call *%%gs:%P2" : "=a" (resultvar) : "a" (1),sysinfo)),"b" ((unsigned int) (arg1) : "memory","cc") ASMARGS_ *实际上并不执行代码本身 – 它们是gcc的指令,以确保某些值(例如(unsigned int)(arg1))在某些寄存器中(例如b,aka ebx).因此,asm volatile的参数组合(当然,这不是一个函数,只是一个gcc内置函数)只是简单地指定gcc应该如何为系统调用做准备以及在系统调用完成后它应该如何继续. 现在,生成的程序集将如下所示: ; set up other registers... movl $1,%eax call *%gs:0x10 ; tear down %gs是一个引用线程本地存储的段寄存器 – 具体来说,glibc引用一个指向VDSO的保存值,当它首次解析说明VDSO所在位置的ELF头时,它存储在那里. 一旦代码进入VDSO,我们就不确切知道发生了什么 – 它取决于内核版本 – 但我们知道它使用最有效的可用机制来运行系统调用,例如sysenter指令或int 0x80指令. 所以,是的,你的图表是准确的: write(1,1) -----> LIBC -----> VDSO -----> KERNEL load reg ? jump to vdso |---------------------------------------------------|--------------| user land kernel land 这是一个更简单的代码调用VDSO,特别是对于单参数系统调用,来自我维护的名为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,将其他寄存器恢复到先前的状态,并返回系统调用的结果. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- linux – 无法加载应用程序或执行命令’Microsoft.AspNet.S
- LINUX教程:Linux下OpenSSL静态库编译及使用
- 如何在Linux和Windows中从命令行查找浏览器版本?
- linux – 是否可以从命令行完全备份mysql数据库服务器?
- linux – Vim:映射ctrl pgup和ctrl pgdn(CTRL Page Up / D
- 8.1-roscomm详解
- Android PC上的Android工作室更快吗?
- linux – 使用更多修饰符重新键入键盘
- linux – SSH连接错误:ssh_exchange_identification:读取
- linux – 丢弃传入的UDP数据包而不读取