glibc原始碼分析(二)系統呼叫
1.6 .c封裝
glibc中許多系統呼叫是用.c封裝的方式封裝的。
.c封裝都是藉助嵌入式彙編,按照系統呼叫的封裝規則進行封裝的。
可以檢視stat64函式的實現,來探究.c封裝。
#undef stat64 int attribute_hidden stat64 (const char *file, struct stat64 *buf) { return __xstat64 (_STAT_VER, file, buf); } int ___xstat64 (int vers, const char *name, struct stat64 *buf) { int result; result = INLINE_SYSCALL (stat64, 2, name, buf); return result; }
原始碼中使用了INLINE_SYSCALL 巨集封裝了stat64系統呼叫。
1.6.1 INLINE_SYSCALL
# define INLINE_SYSCALL(name, nr, args...) \ ({ \ unsigned int resultvar = INTERNAL_SYSCALL (name, , nr, args); \ __glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (resultvar, )) \ ? __syscall_error (-INTERNAL_SYSCALL_ERRNO (resultvar, )) \ : (int) resultvar; })
INLINE_SYSCALL巨集首先執行INTERNAL_SYSCALL巨集。INTERNAL_SYSCALL巨集呼叫了系統呼叫,並將返回值放入resultvar中。然後判斷系統呼叫是否執行錯誤。
#undef INTERNAL_SYSCALL_ERROR_P
#define INTERNAL_SYSCALL_ERROR_P(val, err) \
((unsigned int) (val) >= 0xfffff001u)
#define __glibc_unlikely(cond)(cond)
如果執行錯誤,則呼叫__syscall_error 設定errno,並返回-1。如果執行成功,則返回resultvar。
1.6.2 INTERNAL_SYSCALL
#define INTERNAL_SYSCALL(name, err, nr, args...) \
({ \
register unsigned int resultvar; \
INTERNAL_SYSCALL_MAIN_##nr (name, err, args); \
(int) resultvar; })
INTERNAL_SYSCALL巨集根據系統呼叫引數的個數nr呼叫INTERNAL_SYSCALL_MAIN_0至INTERNAL_SYSCALL_MAIN_6巨集。
當nr為0,呼叫INTERNAL_SYSCALL_MAIN_0巨集。
#define INTERNAL_SYSCALL_MAIN_0(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 0, args)
# 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 LOADREGS_0()
# define ASMARGS_0()
設定eax暫存器為系統呼叫號,執行call *_dl_sysinfo執行系統呼叫。
當nr為1,呼叫INTERNAL_SYSCALL_MAIN_1巨集。
#define INTERNAL_SYSCALL_MAIN_1(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 1, args)
# define LOADREGS_1(arg1) \
LOADREGS_0 ()
# define ASMARGS_1(arg1) \
ASMARGS_0 (), "b" ((unsigned int) (arg1))
設定ebx暫存器為引數1,設定eax暫存器為系統呼叫號,執行call *_dl_sysinfo執行系統呼叫。
當nr為2,呼叫INTERNAL_SYSCALL_MAIN_2巨集。
#define INTERNAL_SYSCALL_MAIN_2(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 2, args)
# define LOADREGS_2(arg1, arg2) \
LOADREGS_1 (arg1)
# define ASMARGS_2(arg1, arg2) \
ASMARGS_1 (arg1), "c" ((unsigned int) (arg2))
設定ecx暫存器為蠶食2,設定ebx暫存器為引數1,設定eax暫存器為系統呼叫號,執行call *_dl_sysinfo執行系統呼叫。
當nr為3,呼叫INTERNAL_SYSCALL_MAIN_3巨集。
#define INTERNAL_SYSCALL_MAIN_3(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 3, args)
# define LOADREGS_3(arg1, arg2, arg3) \
LOADREGS_2 (arg1, arg2)
# define ASMARGS_3(arg1, arg2, arg3) \
ASMARGS_2 (arg1, arg2), "d" ((unsigned int) (arg3))
設定edx暫存器為引數3,設定ecx暫存器為引數2,設定ebx暫存器為引數1,設定eax暫存器為系統呼叫號,執行call *_dl_sysinfo執行系統呼叫。
當nr為4,呼叫INTERNAL_SYSCALL_MAIN_4巨集。
#define INTERNAL_SYSCALL_MAIN_4(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 4, args)
# 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))
設定esi為引數4,設定edx暫存器為引數3,設定ecx暫存器為引數2,設定ebx暫存器為引數1,設定eax暫存器為系統呼叫號,執行call *_dl_sysinfo執行系統呼叫。
當nr為5,呼叫INTERNAL_SYSCALL_MAIN_5巨集。
#define INTERNAL_SYSCALL_MAIN_5(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 5, args)
# 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))
設定edi為引數5,設定esi為引數4,設定edx暫存器為引數3,設定ecx暫存器為引數2,設定ebx暫存器為引數1,設定eax暫存器為系統呼叫號,執行call *_dl_sysinfo執行系統呼叫。
當nr為6,呼叫INTERNAL_SYSCALL_MAIN_6巨集。
# define INTERNAL_SYSCALL_MAIN_6(name, err, args...) \
INTERNAL_SYSCALL_MAIN_INLINE(name, err, 6, args)
# 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)
設定ebp暫存器為引數6,設定edi為引數5,設定esi為引數4,設定edx暫存器為引數3,設定ecx暫存器為引數2,設定ebx暫存器為引數1,設定eax暫存器為系統呼叫號,執行call *_dl_sysinfo執行系統呼叫。
通過嵌入式彙編,.c檔案完成了系統呼叫的封裝。