前文已經向大家介紹了用腳本封裝系統調用的方法。在本文中,我将向大家介紹使用.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, , name, buf);
{
struct stat64 buf64;
INTERNAL_SYSCALL_DECL (err);
result = INTERNAL_SYSCALL (stat64, err, , 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) >= u)
如果傳回值大于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, , args)
#define INTERNAL_SYSCALL_MAIN_1(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, , args)
#define INTERNAL_SYSCALL_MAIN_2(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, , args)
#define INTERNAL_SYSCALL_MAIN_3(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, , args)
#define INTERNAL_SYSCALL_MAIN_4(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, , args)
#define INTERNAL_SYSCALL_MAIN_5(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, , args)
# define INTERNAL_SYSCALL_MAIN_6(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, , 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 = ;
int
__brk (void *addr)
{
INTERNAL_SYSCALL_DECL (err);
void *newbrk = (void *) INTERNAL_SYSCALL (brk, err, , addr);
__curbrk = newbrk;
if (newbrk < addr)
return INLINE_SYSCALL_ERROR_RETURN_VALUE (ENOMEM);
return ;
}
weak_alias (__brk, brk)
brk函數在調用brk系統調用的同時設定了全局變量__curbrk 。