前文已经向大家介绍了用脚本封装系统调用的方法。在本文中,我将向大家介绍使用.c文件封装系统调用的方法。
使用.c文件封装系统调用一般出于以下两个原因:一,系统调用已过时,可以使用新的系统调用替换旧的系统调用。系统调用的语义不变。二,封装后的函数改变了系统调用的语义。
stat系统调用的封装便是原因一的一个例子。
stat系统调用是106号系统调用。使用stat系统调用可以获取文件的属性,但是获取的属性值都是32位的,这对于现在的64位文件系统来说是有问题的。所以linux又定义了stat64系统调用,用于获取64位文件属性。所以,Linux内核中关于stat的系统调用有两个,且stat64系统调用可以替换stat系统调用。而在glibc中,stat系统调用也是使用stat64来封装的。
让我们观察stat系统调用的封装代码:
#undef stat
int
attribute_hidden
__stat (const char *file, struct stat *buf)
{
return __xstat (_STAT_VER, file, buf);
}
weak_hidden_alias (__stat, stat)
stat函数调用了__xstat函数完成函数的封装。
int
__xstat (int vers, const char *name, struct stat *buf)
{
int result;
if (vers == _STAT_VER_KERNEL)
return INLINE_SYSCALL (stat, 2, name, buf);
{
struct stat64 buf64;
INTERNAL_SYSCALL_DECL (err);
result = INTERNAL_SYSCALL (stat64, err, 2, name, &buf64);
if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result, err)))
return INLINE_SYSCALL_ERROR_RETURN_VALUE (INTERNAL_SYSCALL_ERRNO (result,
err));
else
return __xstat32_conv (vers, &buf64, buf);
}
}
#undef INTERNAL_SYSCALL_DECL
#define INTERNAL_SYSCALL_DECL(err) do { } while (0)
# define __glibc_unlikely(cond) (cond)
#undef INTERNAL_SYSCALL_ERROR_P
#define INTERNAL_SYSCALL_ERROR_P(val, err) \
((unsigned int) (val) >= 0xfffff001u)
如果返回值大于0xfffff001u,则表示系统调用出错了。
#undef INTERNAL_SYSCALL_ERRNO
#define INTERNAL_SYSCALL_ERRNO(val, err) (-(val))
#define INLINE_SYSCALL_ERROR_RETURN_VALUE(resultvar) \
__syscall_error (-(resultvar))
调用__syscall_error 处理出错。
如果参数vers为_STAT_VER_KERNEL则直接调用stat系统调用,但此处vers为_STAT_VER。
__xstat先使用INTERNAL_SYSCALL宏调用了stat64系统调用,然后判断是否出错,如果出错,则返回-1,并设置errno。如果执行正确,则调用__xstat32_conv函数,转换数据类型并返回。
INTERNAL_SYSCAL宏定义如下:
#define INTERNAL_SYSCALL(name, err, nr, args...) \
({ \
register unsigned int resultvar; \
INTERNAL_SYSCALL_MAIN_##nr (name, err, args); \
(int) resultvar; })
根据nr系统调用个数的不同调用不同的宏。
参数为0,调用INTERNAL_SYSCALL_MAIN_0宏
参数为1,调用INTERNAL_SYSCALL_MAIN_1宏
参数为2,调用INTERNAL_SYSCALL_MAIN_2宏
参数为3,调用INTERNAL_SYSCALL_MAIN_3宏
参数为4,调用INTERNAL_SYSCALL_MAIN_4宏
参数为5,调用INTERNAL_SYSCALL_MAIN_5宏
参数为6,调用INTERNAL_SYSCALL_MAIN_6宏
#undef INTERNAL_SYSCALL
#define INTERNAL_SYSCALL_MAIN_0(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 0, args)
#define INTERNAL_SYSCALL_MAIN_1(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 1, args)
#define INTERNAL_SYSCALL_MAIN_2(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 2, args)
#define INTERNAL_SYSCALL_MAIN_3(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 3, args)
#define INTERNAL_SYSCALL_MAIN_4(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 4, args)
#define INTERNAL_SYSCALL_MAIN_5(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 5, args)
# define INTERNAL_SYSCALL_MAIN_6(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 6, args)
调用INTERNAL_SYSCALL_MAIN_INLINE宏
# define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
LOADREGS_##nr(args) \
asm volatile ( \
"call *_dl_sysinfo" \
: "=a" (resultvar) \
: "a" (__NR_##name) ASMARGS_##nr(args) : "memory", "cc")
使用嵌入式汇编,封装系统调用。
# define LOADARGS_1
# define LOADARGS_2
# define LOADARGS_3
# define LOADARGS_4
# define LOADARGS_5
# 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)
设置寄存器的值。
系统调用个数为0,不设置寄存器。
系统调用个数为1,设置ebx寄存器。
系统调用个数为2,设置ebx,ecx寄存器。
系统调用个数为3,设置ebx,ecx,edx寄存器。
系统调用个数为4,设置ebx,ecx,edx,esi寄存器。
系统调用个数为5,设置ebx,ecx,edx,esi,edi寄存器。
系统调用个数为6,设置ebx,ecx,edx,esi,edi,ebp寄存器。
brk系统调用的封装便是原因二的一个例子。
void *__curbrk = 0;
int
__brk (void *addr)
{
INTERNAL_SYSCALL_DECL (err);
void *newbrk = (void *) INTERNAL_SYSCALL (brk, err, 1, addr);
__curbrk = newbrk;
if (newbrk < addr)
return INLINE_SYSCALL_ERROR_RETURN_VALUE (ENOMEM);
return 0;
}
weak_alias (__brk, brk)
brk函数在调用brk系统调用的同时设置了全局变量__curbrk 。