1. 程式人生 > >glibc原始碼分析(二)系統呼叫

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檔案完成了系統呼叫的封裝。