系统调用-进程创建函数glibc部分2

* 本文的系统调用的分析基于ARM体系结构
* CrossTool:gcc-3.4.5,libc库:glibc-2.3.6
* 本文的内容来自glibc源代码

上一篇文章中讲述了用户进程如何使用fork、vfork、clone、pthread_create函数,本文将基于glibc-2.3.6源码来跟踪
这些系统调用是怎么使调用用户进程从用户态切换到内核态的。

系统调用是操作系统提供的服务,用户程序通过各种系统调用,来引用内核提供的各种服务,系统调用的执行让
用户程序陷入内核,该陷入动作由swi软中断完成。当用户态的进程调用一个系统调用时,CPU通过软中断切换到
SVC模式并开始执行一个内核函数,完成用户态进程需要完成的工作。

fork():
        path: glibc-2.3.6/nptl/sysdeps/unix/sysv/linux/pt-fork.c
        #include <unistd.h>                        // include __libc_fork()函数声明

        pid_t __fork (void)
        {
          return __libc_fork ();
        }
        strong_alias (__fork, fork)      // ./include/libc-symbols.h
                                                                    // 实际上这里告诉编译器fork这个名字就代表着__fork这个名字,如果这里没有
                                                                    // __fork()的定义,编译器就会报错。
                                                                    // 类似地,存在weak_alias(XXX, xxx),也是取别名,但是编译器在XXX没有定义
                                                                    // 的时候,不会报错。
                                                                    // 可以参考强符号、弱符号,强引用和弱引用来理解它

vfork():
        path: glibc-2.3.6/sysdeps/generic/vfork.c
        #include <errno.h>
        #include <unistd.h>
        /* If we don't have vfork, fork is close enough.  */
       
        __pid_t  __vfork (void)
        {
          return __fork ();
        }
        libc_hidden_def (__vfork)
       
        weak_alias (__vfork, vfork)
        可以看出调用vfork是__vfork的别名,并且实际上是调用了__fork函数来实现__vfork函数的
       
clone():
        path: glibc-2.3.6/sysdeps/unix/sysv/linux/arm/clone.S
        该文件中是用汇编代码实现了__clone函数,存在:
        weak_alias (__clone, clone)
       
        __clone()函数声明如下:
        extern int __clone (int (*__fn) (void *__arg), void *__child_stack, int __flags, void *__arg, ...);
        path: glibc-2.3.6/include/sched.h

pthread_create():
        该函数的源码位置比较特殊,位于目录linuxthread目录下。
        path:linuxthread/pthread.c
       
        ...
        __pthread_create_2_1()
        __pthread_create_2_0(),最终还是调用__pthread_create_2_1()来实现的。

*********************
__libc_fork():
        path: glibc-2.3.6/nptl/sysdeps/unix/sysv/linux/fork.c
        ...
        #ifdef ARCH_FORK
          pid = ARCH_FORK ();
        #else
            # error "ARCH_FORK must be defined so that the CLONE_SETTID flag is used"
          pid = INLINE_SYSCALL (fork, 0);
        #endif
        ...
        这里pid = INLINE_SYSCALL (fork, 0);语句会得到执行,下面是3个重要的头文件:
        sysdeps/unix/sysv/linux/arm/sysdep.h
        sysdeps/unix/arm/sysdep.h
        sysdeps/unix/sysdep.h
        sysdeps/arm/sysdep.h
        INLINE_SYSCALL这个宏定义于文件sysdeps/unix/sysv/linux/arm/sysdep.h中,该处的宏定义很复杂:
...   
#define SYS_ify(syscall_name)    (__NR_##syscall_name)                                                // 获取中断号
// 中断号定义在:arm平台文件系统(制作文件系统时,需要拷贝交叉工具链中的头文件和库)中include/asm/unistd.h
/***
#if defined(__thumb__) || defined(__ARM_EABI__)
#define __NR_SYSCALL_BASE   0
#else
#define __NR_SYSCALL_BASE   0x900000
#endif

/*

 * This file contains the system call numbers.

 */
#define __NR_restart_syscall      (__NR_SYSCALL_BASE+  0)
#define __NR_exit        (__NR_SYSCALL_BASE+  1)
#define __NR_fork        (__NR_SYSCALL_BASE+  2)
#define __NR_read        (__NR_SYSCALL_BASE+  3)
#define __NR_write       (__NR_SYSCALL_BASE+  4)
#define __NR_open        (__NR_SYSCALL_BASE+  5)
...
...
#define __NR_vfork        (__NR_SYSCALL_BASE+  190)
...
#define __NR_clone        (__NR_SYSCALL_BASE+  120)
...
...
***/
...
#undef INLINE_SYSCALL
#define INLINE_SYSCALL(name, nr, args...)                /
  ({ unsigned int _sys_result = INTERNAL_SYSCALL (name, , nr, args);    /                    // note1
     if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (_sys_result, ), 0))    /            // 软中断异常返回处理
       {                                /
     __set_errno (INTERNAL_SYSCALL_ERRNO (_sys_result, ));        /
     _sys_result = (unsigned int) -1;                /
       }                                /
     (int) _sys_result; })

#undef INTERNAL_SYSCALL_DECL
#define INTERNAL_SYSCALL_DECL(err) do { } while (0)

/**
讲解AT&T汇编与GCC内嵌汇编语法网址,虽然该处是基于pc机的讲解,但是和arm体系结构还是很相似的:
http://blog.chinaunix.net/u/21862/showart_141532.html

下面摘录关键部分
带有C/C++表达式的内联汇编格式为:
__asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify);

操作约束符号:
r  I,O   表示使用一个通用寄存器  寄存器约束
m  I,O   表示使用系统所支持的任何一种内存方式,不需要借助寄存器〔关键字m〕 内存约束
i  I     表示输入表达式是一个立即数(整数),不需要借助任何寄存器         立即数约束
F  I     表示输入表达式是一个立即数(浮点数),不需要借助任何寄存器   立即数约束
**/

#undef INTERNAL_SYSCALL                                                                                                                    // note1                       
#define INTERNAL_SYSCALL(name, err, nr, args...)        /
  ({ unsigned int _sys_result;                    /
     {                                /
       register int _a1 asm ("a1");                /                // 定义一个寄存器变量,存放在a1 <==> r0
       LOAD_ARGS_##nr (args)                    /
       asm volatile ("swi    %1    @ syscall " #name    /    // swi 产生一个软中断异常,%1代表第一个输入参数
             : "=r" (_a1)                /                                            // _a1 = rx
             : "i" (SYS_ify(name)) ASM_ARGS_##nr    /        // 输入参数:软中断号(立即数),参数列表
             : "memory");                /                                            // 告诉编译器内存可能被改变
       _sys_result = _a1;                    /
     }                                /
     (int) _sys_result; })

#undef INTERNAL_SYSCALL_ERROR_P
#define INTERNAL_SYSCALL_ERROR_P(val, err) /
  ((unsigned int) (val) >= 0xfffff001u)

#undef INTERNAL_SYSCALL_ERRNO
#define INTERNAL_SYSCALL_ERRNO(val, err)    (-(val))

#define LOAD_ARGS_0()
#define ASM_ARGS_0
#define LOAD_ARGS_1(a1)                /
  _a1 = (int) (a1);                /
  LOAD_ARGS_0 ()
#define ASM_ARGS_1    ASM_ARGS_0, "r" (_a1)
#define LOAD_ARGS_2(a1, a2)            /
  register int _a2 asm ("a2") = (int) (a2);    /
  LOAD_ARGS_1 (a1)
#define ASM_ARGS_2    ASM_ARGS_1, "r" (_a2)
#define LOAD_ARGS_3(a1, a2, a3)            /
  register int _a3 asm ("a3") = (int) (a3);    /
  LOAD_ARGS_2 (a1, a2)
#define ASM_ARGS_3    ASM_ARGS_2, "r" (_a3)
#define LOAD_ARGS_4(a1, a2, a3, a4)        /
  register int _a4 asm ("a4") = (int) (a4);    /
  LOAD_ARGS_3 (a1, a2, a3)
#define ASM_ARGS_4    ASM_ARGS_3, "r" (_a4)
#define LOAD_ARGS_5(a1, a2, a3, a4, a5)        /
  register int _v1 asm ("v1") = (int) (a5);    /
  LOAD_ARGS_4 (a1, a2, a3, a4)
#define ASM_ARGS_5    ASM_ARGS_4, "r" (_v1)
#define LOAD_ARGS_6(a1, a2, a3, a4, a5, a6)    /
  register int _v2 asm ("v2") = (int) (a6);    /
  LOAD_ARGS_5 (a1, a2, a3, a4, a5)
#define ASM_ARGS_6    ASM_ARGS_5, "r" (_v2)
#define LOAD_ARGS_7(a1, a2, a3, a4, a5, a6, a7)    /
  register int _v3 asm ("v3") = (int) (a7);    /
  LOAD_ARGS_6 (a1, a2, a3, a4, a5, a6)
#define ASM_ARGS_7    ASM_ARGS_6, "r" (_v3)
       
APCS    是arm过程调用标准(ARM Procedure Call Standard)。
APCS 对我们通常称为 R0 到 R14 的寄存器起了不同的名字,如下:

Reg#  APCS   意义
R0    a1         工作寄存器
R1    a2         "
R2    a3         "
R3         a4         "
R4         v1         必须保护
R5         v2         "
R6         v3         "
R7         v4         "
R8         v5         "
R9         v6         "
R10     sl         栈限制
R11     fp         桢指针
R12     ip 
R13     sp         栈指针
R14     lr         连接寄存器
R15     pc         程序计数器
*********************

*********************
__clone()

/* int clone(int (*fn)(void *arg), void *child_stack, int flags, void *arg); */

        .text
ENTRY(__clone)
    @ sanity check args
    cmp    r0, #0
    cmpne    r1, #0
    moveq    r0, #-EINVAL
    beq    PLTJMP(syscall_error)

    @ insert the args onto the new stack
    str    r3, [r1, #-4]!                        // 线程函数参数入栈,(child_stack)
    str    r0, [r1, #-4]!                        // 线程函数指针入栈

    @ do the system call
    @ get flags
    mov    r0, r2                                        // flags 存入r0
    @ new sp is already in r1
    swi    SYS_ify(clone)                        // swi __NR_clone, 产生软中断异常
    movs    a1, a1
    blt    PLTJMP(C_SYMBOL_NAME(__syscall_error))
    RETINSTR(ne, lr)

    @ pick the function arg and call address off the stack and execute
    ldr    r0, [sp, #4]
    mov    lr, pc
    ldr     pc, [sp]

    @ and we are done, passing the return value through r0
    b    PLTJMP(_exit)
*********************

未完待续!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
glibc(GNU C Library)是Linux操作系统中的C语言库,提供了许多函数和组件供开发人员使用。在某些情况下,用户或开发人员可能需要将glibc-2.14降级到glibc-2.12。这可能是因为某些软件或应用程序与glibc-2.12兼容,但在glibc-2.14上运行出现问题。 要将glibc-2.14降级到glibc-2.12,首先需要查找并下载glibc-2.12的源代码。可以从官方网站或其他可信的软件仓库中获取源代码。然后,需要进行编译和安装。 在安装glibc-2.12之前,可能需要卸载或删除当前系统中已安装的glibc-2.14版本。卸载glibc-2.14可能会导致系统不稳定或无法启动,因此在执行此操作之前应备份系统或确认可恢复系统状态。 编译和安装glibc-2.12的过程可能会有一些复杂性,并需要具备一定的编程知识和技能。首先,需要进入glibc-2.12源代码所在的目录,并执行`configure`命令以生成编译环境。然后,使用`make`命令编译源代码,并使用`make install`命令安装glibc-2.12。 安装完成后,可能需要进行一些配置,例如更新动态链接器或库的路径。这样确保系统在运行时使用glibc-2.12而不是glibc-2.14。 需要注意的是,将glibc-2.14降级到glibc-2.12可能会导致系统稳定性或兼容性问题。降级glibc可能会影响系统中其他软件的功能和性能。因此,在执行此操作之前,建议先了解软件或应用程序对glibc版本的要求,并评估降级是否是唯一的解决方案。 总之,将glibc-2.14降级到glibc-2.12需要下载、编译和安装glibc-2.12的源代码,并进行一些系统配置。但需要谨慎操作,确保降级前后的系统稳定性和兼容性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值