分享

Android执行系统调用的过程

 wythe 2023-05-06 发布于广东

bionic libc实现了Linux系统调用。事实上,bionic libc是Android操作系统中用户空间程序使用的C库,它提供了许多与GNU libc类似的C库函数,同时还实现了系统调用,在Android操作系统中充当了与GNU libc类似的角色。

在Android中,Linux内核提供了大量的系统调用,它们是由Linux内核代码中的sys_*函数实现的,bionic libc通过使用这些系统调用接口,可以与内核进行通信并获取所需的服务。

在调用系统调用时,bionic libc使用了与GNU libc相同的标准syscall()函数用于调用内核的系统调用接口,并使用与Linux相同的系统调用号。由于Android系统是基于Linux内核开发的,所以bionic libc中的系统调用实现和Linux中的实现方式类似。


在Android系统源码中,bionic\libc\arch-arm\syscall\execve.S,这一层源码实现在libc中,运行在用户态,下面以execve系统调用微粒子

#include <private/bionic_asm.h>

ENTRY(execve)
    mov     ip, r7
    .cfi_register r7, ip
    ldr     r7, =__NR_execve
    swi     #0
    mov     r7, ip
    .cfi_restore r7
    cmn     r0, #(MAX_ERRNO + 1)
    bxls    lr
    neg     r0, r0
    b       __set_errno_internal
END(execve)
这段代码是 ARM 架构下的 execve系统调用的汇编代码。这段汇编代码是编译器生成的,用于将 execve 系统调用的 C 语言调用转换为汇编语言。

该代码包含以下主要步骤:

1. 将 r7 中的参数保存到 ip 寄存器中,以便将其保存到栈帧中。

2. 将系统调用号 __NR_execve的值加载到 r7寄存器中。

3. 使用 swi汇编指令触发操作系统中断,将系统调用转发到内核。

4. 针对 r0寄存器中返回值的检查和处理。cmn指令表示比较操作,bxls 指令表示根据结果跳转到 lr 寄存器中保存的地址,neg 指令表示对寄存器中的值取反然后加1,b 指令表示跳转到代码 __set_errno_internal`处对错误码进行处理。

5. 完成处理后跳转回 lr寄存器中保存的地址,结束系统调用。

总之,这段汇编代码对 execve 系统调用的请求进行了处理,并将其传递给内核,在处理后还通过汇编指令实现了对调用结果和错误码的处理和返回。

通过swi #0进入到了内核空间运行,中断触发了系统调用中断,swi中断的总向量会跳转到内核sys_call_table处,系统调用的内核函数都是通过这个表定义,表格里是指向系统调用向量

	.macro	syscall_table_start, sym
	.equ	__sys_nr, 0
	.type	\sym, #object
ENTRY(\sym)
	.endm

	.macro	syscall, nr, func
	.ifgt	__sys_nr - \nr
	.error	"Duplicated/unorded system call entry"
	.endif
	.rept	\nr - __sys_nr
	.long	sys_ni_syscall
	.endr
	.long	\func
	.equ	__sys_nr, \nr + 1
	.endm

	.macro	syscall_table_end, sym
	.ifgt	__sys_nr - __NR_syscalls
	.error	"System call table too big"
	.endif
	.rept	__NR_syscalls - __sys_nr
	.long	sys_ni_syscall
	.endr
	.size	\sym, . - \sym
	.endm

#define NATIVE(nr, func) syscall nr, func

/*
 * This is the syscall table declaration for native ABI syscalls.
 * With EABI a couple syscalls are obsolete and defined as sys_ni_syscall.
 */
	syscall_table_start sys_call_table
#define COMPAT(nr, native, compat) syscall nr, native
#ifdef CONFIG_AEABI
#include <calls-eabi.S>
#else
#include <calls-oabi.S>
#endif
#undef COMPAT
	syscall_table_end sys_call_table

宏定义,用于定义系统调用。根据传入的参数,宏会生成相应数量的sys_ni_syscall指令和一个调用指定函数的指令。在宏内部使用了一些条件判断和循环语句来确保系统调用号是有序的,避免了重复定义的错误。

其中calls-eabi.S是编译时,通过syscalltbl.sh脚本根据syscall.tbl里面定义的参数生成的

cmd_arch/arm/include/generated/calls-eabi.S := /bin/bash 'kernel/kernel4.14/arch/arm/tools/syscalltbl.sh' 'kernel/kernel4.14/arch/arm/tools/syscall.tbl' 'arch/arm/include/generated/calls-eabi.S' 'common,eabi'

calls-eabi.S内容:

NATIVE(0, sys_restart_syscall)
NATIVE(1, sys_exit)
NATIVE(2, sys_fork)
NATIVE(3, sys_read)
NATIVE(4, sys_write)
NATIVE(5, sys_open)
NATIVE(6, sys_close)
NATIVE(8, sys_creat)
NATIVE(9, sys_link)
NATIVE(10, sys_unlink)
NATIVE(11, sys_execve)
NATIVE(12, sys_chdir)
NATIVE(14, sys_mknod)
NATIVE(15, sys_chmod)
  ...................

syscall.tbl中例子

0	common	restart_syscall		sys_restart_syscall
1	common	exit			sys_exit
2	common	fork			sys_fork
3	common	read			sys_read
4	common	write			sys_write
5	common	open			sys_open
6	common	close			sys_close
# 7 was sys_waitpid
8	common	creat			sys_creat
9	common	link			sys_link
10	common	unlink			sys_unlink
11	common	execve			sys_execve
12	common	chdir			sys_chdir
13	oabi	time			sys_time
14	common	mknod			sys_mknod
15	common	chmod			sys_chmod
16	common	lchown			sys_lchown16
.......................

根据NATIVE宏替换

	syscall_table_start sys_call_table
#define COMPAT(nr, native, compat) syscall nr, native
syscall  0, sys_restart_syscall
syscall 1, sys_exit
syscall 2, sys_fork
syscall 3, sys_read
syscall 4, sys_write
syscall 5, sys_open
syscall 6, sys_close
syscall 8, sys_creat
syscall 9, sys_link
syscall 10, sys_unlink
syscall 11, sys_execve
syscall 12, sys_chdir
syscall 14, sys_mknod
syscall 15, sys_chmod

//把宏替换掉后 .macro	syscall, nr, func
...........
#undef COMPAT
	syscall_table_end sys_call_table

通过上面宏定义的,在中断向量中放置不同的系统调用的向量,也就是不同内核系统调用的函数指针,通过CPU的寄存器传递对应的参数。

在内核源码中有大量的系统调用定义 include\linux\syscalls.h

asmlinkage long sys_gettid(void);
asmlinkage long sys_nanosleep(struct timespec __user *rqtp, struct timespec __user *rmtp);
asmlinkage long sys_alarm(unsigned int seconds);
asmlinkage long sys_getpid(void);
asmlinkage long sys_getppid(void);
asmlinkage long sys_getuid(void);
asmlinkage long sys_geteuid(void);
asmlinkage long sys_getgid(void);
asmlinkage long sys_getegid(void);
asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __user *suid);
asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __user *sgid);
asmlinkage long sys_getpgid(pid_t pid);
asmlinkage long sys_getpgrp(void);
asmlinkage long sys_getsid(pid_t pid);
asmlinkage long sys_getgroups(int gidsetsize, gid_t __user *grouplist);

asmlinkage long sys_setregid(gid_t rgid, gid_t egid);
asmlinkage long sys_setgid(gid_t gid);
asmlinkage long sys_setreuid(uid_t ruid, uid_t euid);
asmlinkage long sys_setuid(uid_t uid);
asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid);
asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid);
asmlinkage long sys_setfsuid(uid_t uid);
asmlinkage long sys_setfsgid(gid_t gid);
asmlinkage long sys_setpgid(pid_t pid, pid_t pgid);
asmlinkage long sys_setsid(void);
asmlinkage long sys_setgroups(int gidsetsize, gid_t __user *grouplist);

//在内核源码中我们会看到大量
SYSCALL_DEFINE3(execve,
		const char __user *, filename,
		const char __user *const __user *, argv,
		const char __user *const __user *, envp)
{
	return do_execve(getname(filename), argv, envp);
}

SYSCALL_DEFINE5(execveat,
		int, fd, const char __user *, filename,
		const char __user *const __user *, argv,
		const char __user *const __user *, envp,
		int, flags)

通过一下宏展开,我们会发现,asmlinkage long sys_execve(const char __user *, filename,

                                                                    const char __user *const __user *, argv,

                                                                const char __user *const __user *, envp))

SYSCALL_DEFINEx宏定义如下:

#define SYSCALL_DEFINE0(sname)						SYSCALL_METADATA(_##sname, 0);					asmlinkage long sys_##sname(void)

#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)

#define SYSCALL_DEFINE_MAXARGS	6

#define SYSCALL_DEFINEx(x, sname, ...)					SYSCALL_METADATA(sname, x, __VA_ARGS__)				__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

#define __PROTECT(...) asmlinkage_protect(__VA_ARGS__)
#define __SYSCALL_DEFINEx(x, name, ...)						asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))			__attribute__((alias(__stringify(SyS##name))));			static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));		asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__));		asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))		{										long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__));			__MAP(x,__SC_TEST,__VA_ARGS__);						__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));			return ret;							}									static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))

Android系统的系统调用实现是通过libc库来实现,通过swi中断使得系统陷入内核,根据不同中断向量+系统调用号,选择不同系统调用函数调用。

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多