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中断使得系统陷入内核,根据不同中断向量+系统调用号,选择不同系统调用函数调用。 |
|
来自: wythe > 《Android系统》