现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

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

2019-10-22 18:59 工业·编程 ⁄ 共 4638字 ⁄ 字号 暂无评论

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

给我留言

留言无头像?