ARM Linux系统调用的原理
g O_LARGEFILE, mode);
}
libc_hidden_def(__libc_open64)
最终__libc_open64又调用了__libc_open函数,这个函数在文件libc/sysdeps/linux/common/open.c中定义:
libc_hidden_proto(__libc_open)
int __libc_open(const char *file, intoflag, ...)
{
mode_tmode = 0;
if(oflag & O_CREAT) {
va_listarg;
va_start(arg, oflag);
mode= va_arg (arg, mode_t);
va_end (arg);
}
return__syscall_open(file, oflag, mode);
}
libc_hidden_def(__libc_open)
这个函数,也是仅仅根据打开标志oflag的值,来判断是否有第三个参数,若由,则获得其值。之后,便用获得的参数来调用__syscall_open(file,oflag, mode)。
__syscall_open在同一个文件中定义:
static __inline__ _syscall3(int,__syscall_open, const char *, file,
int,flags, __kernel_mode_t, mode)
在文件libc/sysdeps/linux/arm/bits/syscalls.h文件中可以看到:
#undef _syscall3
#define_syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
type name(type1 arg1,type2 arg2,type3arg3) \
{ \
return (type) (INLINE_SYSCALL(name, 3,arg1, arg2, arg3)); \
}
这个宏实际上完成定义一个函数的工作,宏的第一个参数是函数的返回值类型,第二个参数是函数名,之后的参数就如同它们的参数名所表明的那样,分别是函数的参数类型及参数名。__syscall_open实际上为:
int __syscall_open (const char * file,intflags, __kernel_mode_t mode)
{
return (int) (INLINE_SYSCALL(__syscall_open,3, file, flags, mode));
}
INLINE_SYSCALL为同一个文件中定义的宏:
#undef INLINE_SYSCALL
#define INLINE_SYSCALL(name, nr,args...) \
({ unsigned int _inline_sys_result = INTERNAL_SYSCALL (name, , nr,args); \
if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (_inline_sys_result, ),0)) \
{ \
__set_errno (INTERNAL_SYSCALL_ERRNO(_inline_sys_result, )); \
_inline_sys_result = (unsigned int) -1; \
} \
(int) _inline_sys_result; })
INLINE_SYSCALL宏中最值得注意的是INTERNAL_SYSCALL,其定义如下:
#undef INTERNAL_SYSCALL
#if !defined(__thumb__)
#if defined(__ARM_EABI__)
#define INTERNAL_SYSCALL(name, err, nr,args...) \
({unsigned int __sys_result; \
{ \
register int _a1 __asm__ ("r0"), _nr __asm__ ("r7"); \
LOAD_ARGS_##nr (args) \
_nr = SYS_ify(name); \
__asm__ __volatile__ ("swi 0x0 @ syscall " #name \
: "=r" (_a1) \
: "r" (_nr) ASM_ARGS_##nr \
: "memory"); \
__sys_result = _a1; \
} \
(int) __sys_result; })
#else /* defined(__ARM_EABI__) */
#define INTERNAL_SYSCALL(name, err, nr,args...) \
({ unsigned int __sys_result; \
{ \
register int _a1 __asm__ ("a1"); \
LOAD_ARGS_##nr (args) \
__asm__ __volatile__ ("swi %1 @ syscall " #name \
: "=r" (_a1) \
: "i" (SYS_ify(name))ASM_ARGS_##nr \
: "memory"); \
__sys_result = _a1; \
} \
(int) __sys_result; })
#endif
这里也将同文件中的LOAD_ARGS宏的定义贴出来:
#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)
这几个宏用来在寄存器中加载相应的参数,参数传递的方式和普通的C函数也没有什么太大的区别,同样都是将参数列表中的参数依次放入寄存器r0、r1、r2、r3…中。
上面的SYS_ify(name)宏,是用来获得系统调用号的。
#define SYS_ify(syscall_name) (__NR_##syscall_name)
也就是__NR___syscall_open,在libc/sysdeps/linux/common/open.c中可以看到这个宏的定义:
#define __NR___syscall_open __NR_open
__NR_open在内核代码的头文件中有定义。
在这里我们忽略定义__thumb__的情况,而假设我们编译出来的库函数使用的都是ARM指令集。在上面的代码中,我们看到,根据是否定义宏__ARM_EABI__,INTERNAL_SYSCALL会被展开为两种不同的版本。关于这一点,与应用二进制接口ABI有关,不同的ABI,则会有不同的传递系统调用号的方法。对于比较新的EABI,则在r7寄存器保存系统调用号,通过
ARMLinux系统调 相关文章:
- ARM Linux下添加新的系统调用(11-21)
- 《ARM与Linux些许问题》第四章:ARM平台系统调用原理分析(11-09)
- Arm linux 系统调用分析(11-09)
- Android arm linux 系统调用实现(11-09)
- arm linux 系统调用实现(11-09)
- Arm Linux系统调用流程详细解析SWI(11-09)
