说明:本文所使用的代码为glibc的master分支代码,版本>2.33。
1. 简介
系统调用是操作系统内核提供一系列具备预定功能的函数接口供应用程序调用。系统调用把应用程序的请求传给内核,内核调用相应的函数完成所需的处理,再将处理结果返回给应用程序。
应用程序运行在用户态下,其诸多操作都受到限制。而系统调用是运行在内核态的,那么运行在用户态的应用程序如何运行内核态的代码呢?操作系统一般是通过中断来从用户态切换到内核态的。
中断分为硬件中断和软件中断。其中软件中断通常是一条指令,使用这条指令用户可以手动触发某个中断。中断一般有两个属性,一个是中断号,一个是中断处理程序。不同的中断有不同的中断号,每个中断号都对应了一个中断处理程序。中断号是有限的,所以不会用一个中断来对应一个系统调用。对于每个系统调用都有一个系统调用号,在触发中断之前,会将系统调用号放入到一个固定的寄存器,中断处理程序会读取该寄存器的值,然后决定执行哪个系统调用的代码。
2. 包装器
wiki主页:https://sourceware.org/glibc/wiki/SyscallWrappers
glibc 对操作系统内核的系统调用使用了三种包装器:汇编、宏和定制。
2.1. 汇编系统调用
glibc 中的简单内核系统调用从名称列表转换为汇编包装器,然后进行编译。
在构建目录中反汇编socket系统调用,将看到syscall-template.S包装器:
maminjie@fedora ~/w/g/build> objdump -ldr socket/socket.o
socket/socket.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <__socket>:
__socket():
/mnt/hgfs/projects/linux/glibc/socket/../sysdeps/unix/syscall-template.S:120
0: b8 29 00 00 00 mov $0x29,%eax
5: 0f 05 syscall
7: 48 3d 01 f0 ff ff cmp $0xfffffffffffff001,%rax
d: 0f 83 00 00 00 00 jae 13 <__socket+0x13>
f: R_X86_64_PLT32 __syscall_error-0x4
/mnt/hgfs/projects/linux/glibc/socket/../sysdeps/unix/syscall-template.S:122
13: c3 retq
maminjie@fedora ~/w/g/build>
使用包装器的系统调用列表保存在syscalls.list文件中:
maminjie@fedora /m/h/p/l/glibc (master)> find . -name syscalls.list
./sysdeps/unix/bsd/syscalls.list
./sysdeps/unix/syscalls.list
./sysdeps/unix/sysv/linux/alpha/syscalls.list
./sysdeps/unix/sysv/linux/arc/syscalls.list
./sysdeps/unix/sysv/linux/arm/syscalls.list
./sysdeps/unix/sysv/linux/csky/syscalls.list
./sysdeps/unix/sysv/linux/generic/syscalls.list
./sysdeps/unix/sysv/linux/generic/wordsize-32/syscalls.list
./sysdeps/unix/sysv/linux/hppa/syscalls.list
./sysdeps/unix/sysv/linux/i386/syscalls.list
./sysdeps/unix/sysv/linux/ia64/syscalls.list
./sysdeps/unix/sysv/linux/m68k/syscalls.list
./sysdeps/unix/sysv/linux/microblaze/syscalls.list
./sysdeps/unix/sysv/linux/mips/mips32/syscalls.list
./sysdeps/unix/sysv/linux/mips/mips64/n32/syscalls.list
./sysdeps/unix/sysv/linux/mips/mips64/n64/syscalls.list
./sysdeps/unix/sysv/linux/mips/syscalls.list
./sysdeps/unix/sysv/linux/powerpc/powerpc32/syscalls.list
./sysdeps/unix/sysv/linux/s390/s390-32/syscalls.list
./sysdeps/unix/sysv/linux/sh/syscalls.list
./sysdeps/unix/sysv/linux/sparc/sparc32/syscalls.list
./sysdeps/unix/sysv/linux/sparc/sparc64/syscalls.list
./sysdeps/unix/sysv/linux/syscalls.list
./sysdeps/unix/sysv/linux/wordsize-64/syscalls.list
./sysdeps/unix/sysv/linux/x86_64/syscalls.list
./sysdeps/unix/sysv/linux/x86_64/x32/syscalls.list
sysdep 目录排序有助于决定哪些系统调用被应用。因此,例如在 x86_64 上,以下文件将被应用:
./sysdeps/unix/sysv/linux/syscalls.list
./sysdeps/unix/sysv/linux/wordsize-64/syscalls.list
./sysdeps/unix/sysv/linux/x86_64/syscalls.list
处理系统调用包装器的 makefile 规则在sysdeps/unix/Makefile 中,例如:
...
ifndef avoid-generated
$(common-objpfx)sysd-syscalls: $(..)sysdeps/unix/make-syscalls.sh \
$(wildcard $(+sysdep_dirs:%=%/syscalls.list)) \
$(wildcard $(+sysdep_dirs:%=%/arch-syscall.h)) \
$(common-objpfx)libc-modules.stmp
for dir in $(+sysdep_dirs); do \
test -f $$dir/syscalls.list && \
{
sysdirs='$(sysdirs)' \
asm_CPP='$(COMPILE.S) -E -x assembler-with-cpp' \
$(SHELL) $(dir $<)$(notdir $<) $$dir || exit 1; }; \
test $$dir = $(..)sysdeps/unix && break; \
done > $@T
mv -f $@T $@
endif
...
syscalls.list 文件由名为 sysdeps/unix/make-syscalls.sh 的脚本处理,该脚本的注释描述了 syscalls.list 文件的格式。
该脚本使用名为syscall-template.S 的模板生成汇编文件,该文件使用特定于机器的宏来构建系统调用的包装器。机器可以用自己的副本覆盖syscall-template.S,因为它也是根据 sysdep 目录顺序选择的。
最后,每台机器的宏由sysdep.h头文件提供:
maminjie@fedora /m/h/p/l/glibc (master)> find . -name sysdep.h
./sysdeps/aarch64/sysdep.h
./sysdeps/arc/sysdep.h
./sysdeps/arm/sysdep.h
./sysdeps/csky/sysdep.h
./sysdeps/generic/sysdep.h
./sysdeps/hppa/sysdep.h
./sysdeps/i386/sysdep.h
./sysdeps/ia64/sysdep.h
./sysdeps/m68k/coldfire/sysdep.h
./sysdeps/m68k/m680x0/sysdep.h
./sysdeps/m68k/sysdep.h
./sysdeps/mach/i386/sysdep.h
./sysdeps/mach/sysdep.h
./sysdeps/microblaze/sysdep.h
./sysdeps/nios2/sysdep.h
./sysdeps/powerpc/powerpc32/sysdep.h
./sysdeps/powerpc/powerpc64/sysdep.h
./sysdeps/powerpc/sysdep.h
./sysdeps/s390/s390-32/sysdep.h
./sysdeps/s390/s390-64/sysdep.h
./sysdeps/sh/sysdep.h
./sysdeps/sparc/sysdep.h
./sysdeps/unix/arm/sysdep.h
./sysdeps/unix/i386/sysdep.h
./sysdeps/unix/mips/mips32/sysdep.h
./sysdeps/unix/mips/mips64/sysdep.h
./sysdeps/unix/mips/sysdep.h
./sysdeps/unix/powerpc/sysdep.h
./sysdeps/unix/sh/sysdep.h
./sysdeps/unix/sysdep.h
./sysdeps/unix/sysv/linux/aarch64/sysdep.h
./sysdeps/unix/sysv/linux/alpha/sysdep.h
./sysdeps/unix/sysv/linux/arc/sysdep.h
./sysdeps/unix/sysv/linux/arm/sysdep.h
./sysdeps/unix/sysv/linux/csky/sysdep.h
./sysdeps/unix/sysv/linux/generic/sysdep.h
./sysdeps/unix/sysv/linux/hppa/sysdep.h
./sysdeps/unix/sysv/linux/i386/sysdep.h
./sysdeps/unix/sysv/linux/ia64/sysdep.h
./sysdeps/unix/sysv/linux/m68k/coldfire/sysdep.h
./sysdeps/unix/sysv/linux/m68k/m680x0/sysdep.h
./sysdeps/unix/sysv/linux/m68k/sysdep.h
./sysdeps/unix/sysv/linux/microblaze/sysdep.h
./sysdeps/unix/sysv/linux/mips/mips32/sysdep.h
./sysdeps/unix/sysv/linux/mips/mips64/sysdep.h
./sysdeps/unix/sysv/linux/mips/sysdep.h
./sysdeps/unix/sysv/linux/nios2/sysdep.h
./sysdeps/unix/sysv/linux/powerpc/powerpc64/sysdep.h
./sysdeps/unix/sysv/linux/powerpc/sysdep.h
./sysdeps/unix/sysv/linux/riscv/sysdep.h
./sysdeps/unix/sysv/linux/s390/s390-32/sysdep.h
./sysdeps/unix/sysv/linux/s390/s390-64/sysdep.h
./sysdeps/unix/sysv/linux/s390/sysdep.h
./sysdeps/unix/sysv/linux/sh/sh4/sysdep.h
./sysdeps/unix/sysv/linux/sh/sysdep.h
./sysdeps/unix/sysv/linux/sparc/sparc32/sysdep.h
./sysdeps/unix/sysv/linux/sparc/sparc64/sysdep.h
./sysdeps/unix/sysv/linux/sparc/sysdep.h
./sysdeps/unix/sysv/linux/sysdep.h
./sysdeps/unix/sysv/linux/x86_64/sysdep.h
./sysdeps/unix/sysv/linux/x86_64/x32/sysdep.h
./sysdeps/unix/x86_64/sysdep.h
./sysdeps/x86/sysdep.h
./sysdeps/x86_64/sysdep.h
./sysdeps/x86_64/x32/sysdep.h
maminjie@fedora /m/h/p/l/glibc (master)>
所有这些部分一起产生一个包装器的编译,类似这样:
…
(echo ‘#define SYSCALL_NAME socket’;
echo ‘#define SYSCALL_NARGS 3’;
echo ‘#define SYSCALL_SYMBOL __socket’;
echo ‘#define SYSCALL_CANCELLABLE 0’;
echo ‘#define SYSCALL_NOERRNO 0’;
echo ‘#define SYSCALL_ERRVAL 0’;
echo ‘#include <syscall-template.S>’;
echo ‘weak_alias (__socket, socket)’;
echo ‘hidden_weak (socket)’;
) | /opt/cross/x86_64-linux-gnu/bin/x86_64-glibc-linux-gnu-gcc -c -I…/include -I/home/azanella/Projects/glibc/build/x86_64-linux-gnu/socket -I/home/azanella/Projects/glibc/build/x86_64-linux-gnu -I…/sysdeps/unix/sysv/linux/x86_64/64 -I…/sysdeps/unix/sysv/linux/x86_64 -I…/sysdeps/unix/sysv/linux/x86 -I…/sysdeps/x86/nptl -I…/sysdeps/unix/sysv/linux/wordsize-64 -I…/sysdeps/x86_64/nptl -I…/sysdeps/unix/sysv/linux/include -I…/sysdeps/unix/sysv/linux -I…/sysdeps/nptl -I…/sysdeps/pthread -I…/sysdeps/gnu -I…/sysdeps/unix/inet -I…/sysdeps/unix/sysv -I…/sysdeps/unix/x86_64 -I…/sysdeps/unix -I…/sysdeps/posix -I…/sysdeps/x86_64/64 -I…/sysdeps/x86_64/fpu/multiarch -I…/sysdeps/x86_64/fpu -I…/sysdeps/x86/fpu/include -I…/sysdeps/x86/fpu -I…/sysdeps/x86_64/multiarch -I…/sysdeps/x86_64 -I…/sysdeps/x86 -I…/sysdeps/ieee754/float128 -I…/sysdeps/ieee754/ldbl-96/include -I…/sysdeps/ieee754/ldbl-96 -I…/sysdeps/ieee754/dbl-64/wordsize-64 -I…/sysdeps/ieee754/dbl-64 -I…/sysdeps/ieee754/flt-32 -I…/sysdeps/wordsize-64 -I…/sysdeps/ieee754 -I…/sysdeps/generic -I… -I…/libio -I. -D_LIBC_REENTRANT -include /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc-modules.h -DMODULE_NAME=libc -include …/include/libc-symbols.h -DPIC -DSHARED -DTOP_NAMESPACE=glibc -DASSEMBLER -g -Werror=undef -Wa,–noexecstack -o /home/azanella/Projects/glibc/build/x86_64-linux-gnu/socket/socket.os -x assembler-with-cpp - -MD -MP -MF /home/azanella/Projects/glibc/build/x86_64-linux-gnu/socket/socket.os.dt -MT /home/azanella/Projects/glibc/build/x86_64-linux-gnu/socket/socket.os
…
注意-x assembler-with-cpp 的使用,因此这些包装器应该只使用汇编。
注意:GLIBC 2.26 和之前的版本用于通过使用包含所需步骤的宏的辅助头文件sysdep-cancel.h来定义取消系统调用(在nopic中调用__{libc,pthread,librt}_{enable,disable}_asynccancel函数/图片模式)。GLIBC 2.27 及更高版本只需要默认的sysdep.h汇编宏,并且所有取消系统调用都使用 SYSCALL_CANCEL 宏在 C 文件中实现。
2.2. 宏系统调用
宏系统调用由比简单包装器复杂得多的*.c文件处理。
一些系统调用可能需要将内核结果改组(shuffling )到用户空间结构中,因此 glibc 需要一种在 C 代码中进行内联系统调用的方法。
这由sysdep.h文件中定义的宏处理。
这些宏都被称为INTERNAL_和INLINE_,并提供了几个供源代码使用的变体。
例如,可以在wait函数实现 (sysdeps/unix/sysv/linux/wait4.c) 中看到这些宏的使用:
...
pid_t
__wait4_time64 (pid_t pid, int *stat_loc, int options, struct __rusage64 *usage)
{
#ifdef __NR_wait4
# if __KERNEL_OLD_TIMEVAL_MATCHES_TIMEVAL64
return SYSCALL_CANCEL (wait4, pid, stat_loc, options, usage);
# else
pid_t ret;
struct __rusage32 usage32;
ret = SYSCALL_CANCEL (wait4, pid, stat_loc, options,
usage != NULL ? &usage32 : NULL);
if (ret > 0 && usage != NULL)
rusage32_to_rusage64 (&usage32, usage);
return ret;
# endif
#elif defined (__ASSUME_WAITID_PID0_P_PGID)
idtype_t idtype = P_PID;
...
函数__wait4_time64 调用宏SYSCALL_CANCEL,其定义在sysdeps/unix/sysdep.h中,如下所示:
#define SYSCALL_CANCEL(...) \
({
\
long int sc_ret; \
if (NO_SYSCALL_CANCEL_CHECKING) \
sc_ret = INLINE_SYSCALL_CALL (__VA_ARGS__); \
else \
{
\
int sc_cancel_oldtype = LIBC_CANCEL_ASYNC (); \
sc_ret = INLINE_SYSCALL_CALL (__VA_ARGS__); \
LIBC_CANCEL_RESET (sc_cancel_oldtype); \
} \
sc_ret; \
})
LIBC_CANCEL_ASYNC调用__ {libc,pthread,librt} _enable_asynccancel在系统调用之前原子地使能异步取消(cancellation )模式。在另一个句柄LIBC_CANCEL_RESET 中,通过调用__{libc,pthread,librt}_disable_asynccancel原子地禁用异步取消模式,并根据需要采取相应的行动。
2.3. 定制系统调用
英国(British)术语 “bespoke” 意味着它是根据买方的要求定制或定制的。glibc 中有一些地方进行了系统调用,它们不使用标准汇编或 C 代码宏。
最好的例子是 fork 和 vfork 实现,这需要 Linux 上的特定调用约定,具体取决于体系结构。例如对于 x86_64 (sysdeps/unix/sysv/linux/x86_64/vfork.S):
/* Clone the calling process, but without copying the whole address space.
The calling process is suspended until the new process exits or is
replaced by a call to `execve'. Return -1 for errors, 0 to the new process,
and the process ID of the new process to the old process. */
ENTRY (__vfork)
/* Pop the return PC value into RDI. We need a register that
is preserved by the syscall and that we're allowed to destroy. */
popq %rdi
cfi_adjust_cfa_offset(-8)
cfi_register(%rip, %rdi)
/* Stuff the syscall number in RAX and enter into the kernel. */
movl $SYS_ify (vfork), %eax
syscall
/* Push back the return PC. */
pushq %rdi
cfi_adjust_cfa_offset(8)
cmpl $-4095, %eax
jae SYSCALL_ERROR_LABEL /* Branch forward if it failed. */
#if SHSTK_ENABLED
/* Check if shadow stack is in use. */
xorl %esi, %esi
rdsspq %rsi
testq %rsi, %rsi
/* Normal return if shadow stack isn't in use. */
je L(no_shstk)
testl %eax, %eax
/* In parent, normal return. */
jnz L(no_shstk)
/* NB: In child, jump back to caller via indirect branch without
popping shadow stack which is shared with parent. Keep shadow
stack mismatched so that child returns in the vfork-calling
function will trigger SIGSEGV. */
popq %rdi
cfi_adjust_cfa_offset(-8)
jmp *%rdi
L(no_shstk):
#endif
/* Normal return. */
ret
PSEUDO_END (__vfork)
它使用sysdep.h宏进行函数返回 (SYSCALL_ERROR_LABEL),但是由于一些特定的 ABI 和语义约束,它需要一些特定的汇编实现。
事情的真相是,大多数定制案例可能都应该全部清理以使用宏。
3. 汇编系统调用详解
通过“包装器->汇编系统调用”的介绍,我们知道汇编系统调用主要由三部分组成:make-syscall.sh、syscall-template.S、syscalls.list。其中make-syscall.sh文件是shell脚本文件。该脚本文件读取syscalls.list文件内容,对syscalls.list文件中每一行数据进行解析。syscall-template.S文件是系统调用封装的模板文件,包含了封装代码。
3.1. syscalls.list
下面以sysdeps/unix/syscalls.list为例,来理解syscalls.list的内容:
# File name Caller Syscall name Args Strong name Weak names
accept - accept Ci:iBN __libc_accept accept
access - access i:si __access access
acct - acct i:S acct
adjtime - adjtime i:pp __adjtime adjtime
bind - bind i:ipi __bind bind
chdir - chdir i:s __chdir chdir
...
syscalls.list文件由许多行组成,每一行都对应一个系统调用。每一行可分为6列:
- File name: 生成系统调用目标文件的文件名
- Caller:调用者
- Syscall name:系统调用的名字
- Args:系统调用的参数类型和个数以及返回值的类型
冒号(:)前面表示返回值类型,后面表示参数类型和个数。
系统调用签名前缀:
E: errno 和返回值不是由调用设置
V: errno 未设置,但调用返回 errno 或零(成功)
C: 未知
系统调用签名关键字母:
a:未经检查的地址(例如,mmap的第1个参数)
b:非空缓冲区(例如,read的第2个参数,mmap的返回值)
B:可选的 NULL 缓冲区(例如,getsockopt 的第 4 个参数)
f:2 个整数的缓冲区(例如,socketpair的第4个参数)
F:fcntl的第3个参数
i:标量(任何符号和大小:int、long、long long、enum,等等)
I:ioctl 的第3个参数
n:标量缓冲区长度(例如,read的第3个参数)
N:指向值/返回标量缓冲区长度的指针(例如, recvfrom 的第 6 个参数)
p:指向类型对象的非 NULL 指针(例如,任何非 void* arg)
P:可选的指向类型对象的 NULL 指针(例如,sigaction 的第3个参数)
s:非空字符串(例如,open的第1个参数)
S:可选的 NULL 字符串(例如,acct的第1个参数)
U:unsigned long int(32 位类型零扩展为 64 位类型)
v:vararg 标量(例如,open的可选的第3个参数)
V:每页字节向量(mincore的第3个参数)
W:等待状态,可选的指向 int 的 NULL 指针(例如,wait4 的第2个参数)
(说明:上面释义来自sysdeps/unix/make-syscalls.sh中的注释) - Strong name:系统调用对应函数的名字
- Weak names:系统调用对应函数的名字的别称。可以使用别称来调用函数
3.2. assembly syscall wrappers
再来看看sysdeps/unix/Makefile 中的规则:
...
ifndef avoid-generated
$(common-objpfx)sysd-syscalls: $(..)sysdeps/unix/make-syscalls.sh \
$(wildcard $(+sysdep_dirs:%=%/syscalls.list)) \
$(wildcard $(+sysdep_dirs:%=%/arch-syscall.h)) \
$(common-objpfx)libc-modules.stmp
for dir in $(+sysdep_dirs); do \
test -f $$dir/syscalls.list && \
{
sysdirs='$(sysdirs)' \
asm_CPP='$(COMPILE.S) -E -x assembler-with-cpp' \
$(SHELL) $(dir $<)$(notdir $<) $$dir || exit 1; }; \
test $$dir = $(..)sysdeps/unix && break; \
done > $@T
mv -f $@T $@
endif
...
该部分在编译时,被解析成如下形式:
touch /home/maminjie/work/glibc/tmp-build/libc-modules.stmp
for dir in /home/maminjie/work/glibc/tmp-build sysdeps/unix/sysv/linux/x86_64/64 sysdeps/unix/sysv/linux/x86_64 sysdeps/unix/sysv/linux/x86 sysdeps/x86/nptl sysdeps/unix/sysv/linux/wordsize-64 sysdeps/x86_64/nptl sysdeps/unix/sysv/linux sysdeps/nptl sysdeps/pthread sysdeps/gnu sysdeps/unix/inet sysdeps/unix/sysv sysdeps/unix/x86_64 sysdeps/unix sysdeps/posix sysdeps/x86_64/64 sysdeps/x86_64/fpu/multiarch sysdeps/x86_64/fpu sysdeps/x86/fpu sysdeps/x86_64/multiarch sysdeps/x86_64 sysdeps/x86 sysdeps/ieee754/float128 sysdeps/ieee754/ldbl-96 sysdeps/ieee754/dbl-64 sysdeps/ieee754/flt-32 sysdeps/wordsize-64 sysdeps/ieee754 sysdeps/generic; do \
test -f $dir/syscalls.list && \
{
sysdirs='sysdeps/unix/sysv/linux/x86_64/64 sysdeps/unix/sysv/linux/x86_64 sysdeps/unix/sysv/linux/x86 sysdeps/x86/nptl sysdeps/unix/sysv/linux/wordsize-64 sysdeps/x86_64/nptl sysdeps/unix/sysv/linux sysdeps/nptl sysdeps/pthread sysdeps/gnu sysdeps/unix/inet sysdeps/unix/sysv sysdeps/unix/x86_64 sysdeps/unix sysdeps/posix sysdeps/x86_64/64 sysdeps/x86_64/fpu/multiarch sysdeps/x86_64/fpu sysdeps/x86/fpu sysdeps/x86_64/multiarch sysdeps/x86_64 sysdeps/x86 sysdeps/ieee754/float128 sysdeps/ieee754/ldbl-96 sysdeps/ieee754/dbl-64 sysdeps/ieee754/flt-32 sysdeps/wordsize-64 sysdeps/ieee754 sysdeps/generic' \
asm_CPP='gcc -c -Iinclude -I/home/maminjie/work/glibc/tmp-build -Isysdeps/unix/sysv/linux/x86_64/64 -Isysdeps/unix/sysv/linux/x86_64 -Isysdeps/unix/sysv/linux/x86/include -Isysdeps/unix/sysv/linux/x86 -Isysdeps/x86/nptl -Isysdeps/unix/sysv/linux/wordsize-64 -Isysdeps/x86_64/nptl -Isysdeps/unix/sysv/linux/include -Isysdeps/unix/sysv/linux -Isysdeps/nptl -Isysdeps/pthread -Isysdeps/gnu -Isysdeps/unix/inet -Isysdeps/unix/sysv -Isysdeps/unix/x86_64 -Isysdeps/unix -Isysdeps/posix -Isysdeps/x86_64/64 -Isysdeps/x86_64/fpu/multiarch -Isysdeps/x86_64/fpu -Isysdeps/x86/fpu -Isysdeps/x86_64/multiarch -Isysdeps/x86_64 -Isysdeps/x86/include -Isysdeps/x86 -Isysdeps/ieee754/float128 -Isysdeps/ieee754/ldbl-96/include -Isysdeps/ieee754/ldbl-96 -Isysdeps/ieee754/dbl-64 -Isysdeps/ieee754/flt-32 -Isysdeps/wordsize-64 -Isysdeps/ieee754 -Isysdeps/generic -Ilibio -I. -D_LIBC_REENTRANT -include /home/maminjie/work/glibc/tmp-build/libc-modules.h -DMODULE_NAME=libc -include include/libc-symbols.h -DTOP_NAMESPACE=glibc -DASSEMBLER -g -Werror=undef -Wa,--noexecstack -E -x assembler-with-cpp' \
/bin/sh sysdeps/unix/make-syscalls.sh $dir || exit 1; }; \
test $dir = sysdeps/unix && break; \
done > /home/maminjie/work/glibc/tmp-build/sysd-syscallsT
mv -f /home/maminjie/work/glibc/tmp-build/sysd-syscallsT /home/maminjie/work/glibc/tmp-build/sysd-syscalls
sysdeps/unix/make-syscalls.sh遍历相关目录下的syscalls.list,最后将内容输出到sysd-syscalls文件中。
sysd-syscalls文件内容如下:
#### DIRECTORY = sysdeps/unix/sysv/linux/x86_64
#### SYSDIRS = sysdeps/unix/sysv/linux/x86_64/64
#### CALL=arch_prctl NUMBER=158 ARGS=i:ii SOURCE=-
ifeq (,$(filter arch_prctl,$(unix-syscalls)))
unix-syscalls += arch_prctl
unix-extra-syscalls += arch_prctl
$(foreach p,$(sysd-rules-targets),$(foreach o,$(object-suffixes),$(objpfx)$(patsubst %,$p,arch_prctl)$o)): \
$(..)sysdeps/unix/make-syscalls.sh
$(make-target-directory)
(echo '#define SYSCALL_NAME arch_prctl'; \
echo '#define SYSCALL_NARGS 2'; \
echo '#define SYSCALL_ULONG_ARG_1 0'; \
echo '#define SYSCALL_ULONG_ARG_2 0'; \
echo '#define SYSCALL_SYMBOL __arch_prctl'; \
echo '#define SYSCALL_NOERRNO 0'; \
echo '#define SYSCALL_ERRVAL 0'; \
echo '#include <syscall-template.S>'; \
echo 'weak_alias (__arch_prctl, arch_prctl)'; \
echo 'hidden_weak (arch_prctl)'; \
) | $(compile-syscall) $(foreach p,$(patsubst %arch_prctl,%,$(basename $(@F))),$($(p)CPPFLAGS))
endif
...
#### DIRECTORY = sysdeps/unix/sysv/linux/wordsize-64
#### SYSDIRS = sysdeps/unix/sysv/linux/x86_64/64 sysdeps/unix/sysv/linux/x86_64 sysdeps/unix/sysv/linux/x86 sysdeps/x86/nptl
#### CALL=sendfile NUMBER=40 ARGS=i:iipi SOURCE=-
ifeq (,$(filter sendfile,$(unix-syscalls)))
unix-syscalls += sendfile
$(foreach p,$(sysd-rules-targets),$(foreach o,$(object-suffixes),$(objpfx)$(patsubst %,$p,sendfile)$o)): \
$(..)sysdeps/unix/make-syscalls.sh
$(make-target-directory)
(echo '#define SYSCALL_NAME sendfile'; \
echo '#define SYSCALL_NARGS 4'; \
echo '#define SYSCALL_ULONG_ARG_1 0'; \
echo '#define SYSCALL_ULONG_ARG_2 0'; \
echo '#define SYSCALL_SYMBOL sendfile'; \
echo '#define SYSCALL_NOERRNO 0'; \
echo '#define SYSCALL_ERRVAL 0'; \
echo '#include <syscall-template.S>'; \
echo 'weak_alias (sendfile, sendfile64)'; \
echo 'hidden_weak (sendfile64)'; \
) | $(compile-syscall) $(foreach p,$(patsubst %sendfile,%,$(basename $(@F))),$($(p)CPPFLAGS))
endif
...
#### DIRECTORY = sysdeps/unix/sysv/linux
#### SYSDIRS = sysdeps/unix/sysv/linux/x86_64/64 sysdeps/unix/sysv/linux/x86_64 sysdeps/unix/sysv/linux/x86 sysdeps/x86/nptl sysdeps/unix/sysv/linux/wordsize-64 sysdeps/x86_64/nptl
#### CALL=alarm NUMBER=37 ARGS=i:i SOURCE=-
ifeq (,$(filter alarm,$(unix-syscalls)))
unix-syscalls += alarm
$(foreach p,$(sysd-rules-targets),$(foreach o,$(object-suffixes),$(objpfx)$(patsubst %,$p,alarm)$o)): \
$(..)sysdeps/unix/make-syscalls.sh
$(make-target-directory)
(echo '#define SYSCALL_NAME alarm'; \
echo '#define SYSCALL_NARGS 1'; \
echo '#define SYSCALL_ULONG_ARG_1 0'; \
echo '#define SYSCALL_ULONG_ARG_2 0'; \
echo '#define SYSCALL_SYMBOL alarm'; \
echo '#define SYSCALL_NOERRNO 0'; \
echo '#define SYSCALL_ERRVAL 0'; \
echo '#include <syscall-template.S>'; \
) | $(compile-syscall) $(foreach p,$(patsubst %alarm,%,$(basename $(@F))),$($(p)CPPFLAGS))
endif
...
以chdir为例,如下所示:
#### CALL=chdir NUMBER=80 ARGS=i:s SOURCE=-
ifeq (,$(filter chdir,$(unix-syscalls)))
unix-syscalls += chdir
$(foreach p,$(sysd-rules-targets),$(foreach o,$(object-suffixes),$(objpfx)$(patsubst %,$p,chdir)$o)): \
$(..)sysdeps/unix/make-syscalls.sh
$(make-target-directory)
(echo '#define SYSCALL_NAME chdir'; \
echo '#define SYSCALL_NARGS 1'; \
echo '#define SYSCALL_ULONG_ARG_1 0'; \
echo '#define SYSCALL_ULONG_ARG_2 0'; \
echo '#define SYSCALL_SYMBOL __chdir'; \
echo '#define SYSCALL_NOERRNO 0'; \
echo '#define SYSCALL_ERRVAL 0'; \
echo '#include <syscall-template.S>'; \
echo 'weak_alias (__chdir, chdir)'; \
echo 'hidden_weak (chdir)'; \
) | $(compile-syscall) $(foreach p,$(patsubst %chdir,%,$(basename $(@F))),$($(p)CPPFLAGS))
endif
实际上,最后chdir的汇编系统调用代码(assembly syscall wrappers)被解析成如下临时内容,然后进行编译:
#define SYSCALL_NAME chdir
#define SYSCALL_NARGS 1
#define SYSCALL_ULONG_ARG_1 0
#define SYSCALL_ULONG_ARG_2


3061

被折叠的 条评论
为什么被折叠?



