接着浅尝辄止21-Linux系统调用0,接着寻找pwrite的系统调用之路,线索就在浅尝辄止22-C语言属性-alias。
pwrite的多个身份
执行git grep "alias.*pwrite"
,只关注Linux ARM32相关的就可以看到下面的关键信息。
sysdeps/unix/sysv/linux/pwrite.c:strong_alias (__libc_pwrite, __pwrite)
sysdeps/unix/sysv/linux/pwrite.c:weak_alias (__libc_pwrite, pwrite)
在libc-symbols.h中可以找到如下信息,两种alias都是产生别名而已,只是其中一个是weak symbol。因此,我们可以知道__libc_pwrite
,__pwrite
和pwrite
这些名字实际上是一回事。
/* Define ALIASNAME as a strong alias for NAME. */
# define strong_alias(name, aliasname) _strong_alias(name, aliasname)
# define _strong_alias(name, aliasname) \
extern __typeof (name) aliasname __attribute__ ((alias (#name)));
...
/* Define ALIASNAME as a weak alias for NAME.
If weak aliases are not available, this defines a strong alias. */
# define weak_alias(name, aliasname) _weak_alias (name, aliasname)
# define _weak_alias(name, aliasname) \
extern __typeof (name) aliasname __attribute__ ((weak, alias (#name)));
pwrite的实现
进入pwrite.c看一下,pwrite
的实现就靠一个宏SYSCALL_CANCEL
。
ssize_t
__libc_pwrite (int fd, const void *buf, size_t count, off_t offset)
{
return SYSCALL_CANCEL (pwrite, fd, buf, count, SYSCALL_LL_PRW (offset));
}
SYSCALL_CANCEL的展开
根据libc-symbols.h和sysdep.h中的定义,可以展开如下
SYSCALL_CANCEL (pwrite, fd, buf, count, SYSCALL_LL_PRW (offset))
=SYSCALL_CANCEL (pwrite, fd, buf, count, offset)
展开并去掉不关心的内容=>
INLINE_SYSCALL_CALL (pwrite, fd, buf, count, offset);
=>
__INLINE_SYSCALL_DISP (__INLINE_SYSCALL, pwrite, fd, buf, count, offset)
=>
__SYSCALL_CONCAT (__INLINE_SYSCALL,__INLINE_SYSCALL_NARGS(pwrite, fd, buf, count, offset))(pwrite, fd, buf, count, offset)
=>
__SYSCALL_CONCAT_X (__INLINE_SYSCALL, __INLINE_SYSCALL_NARGS(pwrite, fd, buf, count, offset))(pwrite, fd, buf, count, offset)
=>
__INLINE_SYSCALL__INLINE_SYSCALL_NARGS(pwrite, fd, buf, count, offset))(pwrite, fd, buf, count, offset)
=>
__INLINE_SYSCALL__INTERNAL_SYSCALL_NARGS_X (pwrite, fd, buf, count, offset,7,6,5,4,3,2,1,0,)(pwrite, fd, buf, count, offset)
=>
__INLINE_SYSCALL4(pwrite, fd, buf, count, offset)
=>
INLINE_SYSCALL (pwrite, 4, fd, buf, count, offset)
展开并去掉不关心的内容=>
INTERNAL_SYSCALL (pwrite, , 4, fd, buf, count, offset);
=>
INTERNAL_SYSCALL_RAW(SYS_ify(pwrite), , 4, fd, buf, count, offset)
=>
INTERNAL_SYSCALL_RAW(__NR_pwrite, , 4, fd, buf, count, offset)
=>
({ \
register int _a1 asm ("r0"), _nr asm ("r7"); \
LOAD_ARGS_4 (fd, buf, count, offset) \
_nr = __NR_pwrite; \
asm volatile ("swi 0x0 @ syscall " "__NR_pwrite" \
: "=r" (_a1) \
: "r" (_nr) ASM_ARGS_4 \
: "memory"); \
_a1; })
=>
({ \
register int _a1 asm ("r0"), _nr asm ("r7"); \
int _a4tmp = (int) (offset); \
int _a3tmp = (int) (count); \
int _a2tmp = (int) (buf); \
int _a1tmp = (int) (fd); \
_a1 = _a1tmp; \
register int _a2 asm ("buf") = _a2tmp; \
register int _a3 asm ("count") = _a3tmp; \
register int _a4 asm ("offset") = _a4tmp; \
_nr = __NR_pwrite; \
asm volatile ("swi 0x0 @ syscall " "__NR_pwrite" \
: "=r" (_a1) \
: "r" (_nr), "r" (_a1), "r" (_a2), "r" (_a3), "r" (_a4) \
: "memory"); \
_a1; })
最后这一大坨就是Linux ARM32的pwrite
在glibc里面的最终实现了,下面逐行注释解说
//将{}用()包起来,这个括号就成了一个表达式,值就是最后面的表达式的值,即_a1
({ \
//声明2个int型变量
//_a1用r0寄存器,用来传递系统调用的第一个参数
//_nr用r7寄存器,用来传递著名的系统调用号
register int _a1 asm ("r0"), _nr asm ("r7"); \
//依次声明4个变量,分别将4个参数赋给它们
int _a4tmp = (int) (offset); \
int _a3tmp = (int) (count); \
int _a2tmp = (int) (buf); \
int _a1tmp = (int) (fd); \
//将第一个参数赋给寄存器r0
_a1 = _a1tmp; \
//将参数依次赋给寄存器,会顺次分配r1,r2,r3寄存器
register int _a2 asm ("buf") = _a2tmp; \
register int _a3 asm ("count") = _a3tmp; \
register int _a4 asm ("offset") = _a4tmp; \
//将系统调用号__NR_pwrite给_nr
_nr = __NR_pwrite; \
//以下四句gcc内联汇编
//swi 0x0,产生软件中断,这条指令就会触发内核的中断处理,从这条指令到内核中断处理,就是所谓的陷入内核过程了
//"=r" (_a1) 是将内联汇编的输出结果存到_a1,即系统调用的返回值
// "r" (_nr), "r" (_a1), "r" (_a2), "r" (_a3), "r" (_a4) 是输入参数列表,会通过r7,r0,r1,r2,r3传递
// "memory"是被更改的资源,即内存有可能会被改变
asm volatile ("swi 0x0 @ syscall " "__NR_pwrite" \
: "=r" (_a1) \
: "r" (_nr), "r" (_a1), "r" (_a2), "r" (_a3), "r" (_a4) \
: "memory"); \
//内联汇编的输出结果会存到_a1,这就是这整个({...})表达式的值,即系统调用返回值
_a1; })
__NR_pwrite是哪来的?