tut*_*uen 7 c linux libc system-calls vdso
我在最后一个libc中剖析了syscall调用:
git clone git://sourceware.org/git/glibc.git
Run Code Online (Sandbox Code Playgroud)
我在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")
Run Code Online (Sandbox Code Playgroud)
如果我理解这段代码,那么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
Run Code Online (Sandbox Code Playgroud)
在ebx,ecx,edx,esi,edx和ebp寄存器中加载参数的代码在哪里?这是上面的代码吗?我不明白实施.以下代码加载ebx寄存器中的第6个参数?
register unsigned int _a6 asm ("ebp") = (unsigned int) (arg6);
Run Code Online (Sandbox Code Playgroud)
这段代码是什么:
ASMARGS_0 (), "b" ((unsigned int) (arg1))
Run Code Online (Sandbox Code Playgroud)
它加载ebx寄存器中的第一个参数?
然后"call*%% gs:%P2"跳转到VDSO代码?这段代码对应"call*gs:0x10"?
那么,这个写系统调用的下图,它很好吗?:
write(1, "A", 1) -----> LIBC -----> VDSO -----> KERNEL
load reg ?
jump to vdso
|---------------------------------------------------|--------------|
user land kernel land
Run Code Online (Sandbox Code Playgroud)
我不懂VDSO实用程序!vdso选择syscall方法(sysenter或int 0x80).
提前谢谢你的帮助.抱歉,我的英语非常糟糕.
glibc 系统调用中涉及的宏将扩展为如下所示,例如 exit 系统调用。
LOADREGS_1(args)
asm volatile (
"call *%%gs:%P2"
: "=a" (resultvar)
: "a" (__NR_exit), "i" (offsetof (tcbhead_t, sysinfo))
ASMARGS_1(args) : "memory", "cc")
Run Code Online (Sandbox Code Playgroud)
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")
Run Code Online (Sandbox Code Playgroud)
ASMARGS_*实际上并不执行代码本身- 它们是gcc确保某些值(例如(unsigned int) (arg1))位于某些寄存器(例如b,又名ebx)中的指令。因此,参数的组合asm volatile(当然,这不是一个函数,而只是一个 gcc 内置函数)只是指定应如何gcc准备系统调用以及系统调用完成后应如何继续。
现在,生成的程序集将如下所示:
; set up other registers...
movl $1, %eax
call *%gs:0x10
; tear down
Run Code Online (Sandbox Code Playgroud)
%gs是一个引用线程本地存储的段寄存器 - 具体来说,glibc 引用一个指向 VDSO 的保存值,当它第一次解析告诉它 VDSO 所在位置的 ELF 标头时,它存储在那里。
一旦代码进入VDSO,我们不知道到底会发生什么——它根据内核版本而变化——但我们确实知道它使用最有效的可用机制来运行系统调用,例如指令或sysenter指令int 0x80。
所以,是的,你的图表是准确的:
write(1, "A", 1) -----> LIBC -----> VDSO -----> KERNEL
load reg ?
jump to vdso
|---------------------------------------------------|--------------|
user land kernel land
Run Code Online (Sandbox Code Playgroud)
下面是一个更简单的代码示例,用于从我维护的名为libsyscall的库中调用 VDSO,特别是单参数系统调用:
_lsc_syscall1:
xchgl 8(%esp), %ebx
movl 4(%esp), %eax
call *_lsc_vdso_ptr(,1)
movl 8(%esp), %ebx
# pass %eax out
ret
Run Code Online (Sandbox Code Playgroud)
这只是将参数从堆栈移动到寄存器中,通过从内存加载的指针调用 VDSO,将其他寄存器恢复到之前的状态,然后返回系统调用的结果。
| 归档时间: |
|
| 查看次数: |
373 次 |
| 最近记录: |