天天看点

glibc源码分析之系统调用(二)

前文已经向大家介绍了用脚本封装系统调用的方法。在本文中,我将向大家介绍使用.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 。