我看到网上很多人参考
arch/x86/entry/syscalls/syscall_64.tbl
对于系统调用表,效果很好。不过还有很多人参考
/include/uapi/asm-generic/unistd.h
这通常在 headers 包中找到。怎么syscall_64.tbl
显示,
0 common read sys_read
正确答案,并unistd.h
表明,
#define __NR_io_setup 0
__SC_COMP(__NR_io_setup, sys_io_setup, compat_sys_io_setup)
然后它显示__NR_read
为
#define __NR_read 63
__SYSCALL(__NR_read, sys_read)
为什么是 63,而不是 1?我该如何理解 out of /include/uapi/asm-generic/unistd.h
?里面/usr/include/asm/
还有
/usr/include/asm/unistd_x32.h
#define __NR_read (__X32_SYSCALL_BIT + 0)
#define __NR_write (__X32_SYSCALL_BIT + 1)
#define __NR_open (__X32_SYSCALL_BIT + 2)
#define __NR_close (__X32_SYSCALL_BIT + 3)
#define __NR_stat (__X32_SYSCALL_BIT + 4)
/usr/include/asm/unistd_64.h
#define __NR_read 0
#define __NR_write 1
#define __NR_open 2
#define __NR_close 3
#define __NR_stat 4
/usr/include/asm/unistd_32.h
#define __NR_restart_syscall 0
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
unistd
有人可以告诉我这些文件之间的区别吗?解释一下如何unistd.h
运作?查找系统调用表的最佳方法是什么?
答案1
当我调查这种事情时,我发现直接询问编译器很有用(请参阅在终端中打印标准 C/GCC 预定义宏详情):
printf SYS_read | gcc -include sys/syscall.h -E -
这表明涉及的标头(在 Debian 上)是/usr/include/x86_64-linux-gnu/sys/syscall.h
、/usr/include/x86_64-linux-gnu/asm/unistd.h
、/usr/include/x86_64-linux-gnu/asm/unistd_64.h
和/usr/include/x86_64-linux-gnu/bits/syscall.h
,并打印 的系统调用号read
,在 x86-64 上为 0。
如果您安装了适当的系统头文件(在交叉编译器环境中),您可以找到其他体系结构的系统调用号。对于 32 位 x86,这很简单:
printf SYS_read | gcc -include sys/syscall.h -m32 -E -
其中涉及/usr/include/asm/unistd_32.h
其他头文件,并打印数字 3。
因此从用户空间的角度来看,32 位 x86 系统调用在 中定义asm/unistd_32.h
,64 位 x86 系统调用在 中定义asm/unistd_64.h
。asm/unistd_x32.h
用于x32 ABI。
uapi/asm-generic/unistd.h
列出了默认系统调用,这些调用用于没有特定于体系结构的系统调用表的体系结构。
在内核中,引用略有不同,并且是特定于体系结构的(同样,对于不使用通用系统调用表的体系结构)。这就是诸如此类的文件arch/x86/entry/syscalls/syscall_64.tbl
出现的地方(它们最终会生成在用户空间unistd_64.h
等中使用的头文件)。您将在有关该主题的两篇 LWN 文章中找到有关系统调用的更多详细信息,系统调用剖析第 1 部分和系统调用剖析第 2 部分。
答案2
不同的体系结构在不同的文件中定义了不同的系统调用号
每个架构的系统调用号都不同,例如:
x86_64:
arch/x86/entry/syscalls/syscall_64.tbl
: 读取为0手臂64:
include/uapi/asm-generic/unistd.h
:读取为 63,另请参阅:https://reverseengineering.stackexchange.com/questions/16917/arm64-syscalls-table/18834#18834手臂:
arch/arm/tools/syscall.tbl
,读取为3
include/uapi/asm-generic/unistd.h
与arch/*
定义
我认为这include/uapi/asm-generic/unistd.h
是统一所有架构的系统调用号的新尝试。
但由于系统调用编号无法更改以不破坏系统调用 API,因此统一工作之前的旧架构(包括 x86、x86_64 和 arm)保留了在arch/
. arm64 较新,并且获得了新的unistd.h
API。
这个相关问题需要一种自动方式来获取完整的系统调用列表,包括参数:https://stackoverflow.com/questions/6604007/how-can-i-get-a-list-of-linux-system-calls-and-number-of-args-they-take-automati
strace
源代码
我信任该工具,并且他们将数据保持整洁linux/
,例如:
- x86_64:https://github.com/strace/strace/blob/v4.26/linux/x86_64/syscallent.h
- aarch64:https://github.com/strace/strace/blob/27739ebccc96c0f70cd2aeb37fd7b0ecfef21724/linux/aarch64/syscallent.h
请注意,aarch64#include
与 arch 无关64/syscallent.h
我之前提到过。
这些表包含参数的数量,但不包含实际的参数类型,我想知道strace
它们在哪里编码。
glibc
源代码
答案3
我有一个页面列出了每个 Linux 支持的体系结构的所有系统调用:
https://marcin.juszkiewicz.com.pl/download/tables/syscalls.html
答案4
这个答案不会涉及asm-generic
的版本unistd.h
,因为没有任何内容包含它。1
如中所述syscalls(2)
:
粗略地说,属于中定义的编号为__NR_xxx的系统调用的代码
/usr/include/asm/unistd.h
可以在Linux内核源代码的例程sys_xxx
()中找到。
也就是说,正确的系统调用号将在 中找到/usr/include/asm/unistd.h
。现在,在典型的 x86 系统上,这将仅包含asm/unistd_*.h
取决于目标的文件之一。
适用于 64 位程序的系统调用号位于 中asm/unistd_64.h
,适用于 32 位程序的系统调用号位于asm/unistd_32.h
(或几乎等效的_x32.h
变体)中。两者是不同的,因为 32 位和 64 位架构实际上是完全不同的操作系统。由于各种原因,它们共享同一组系统调用,但顺序不同。
其中大多数也有 C 语言包装器,因此很少需要syscall(2)
直接使用。
1因为我不知道它的用途是什么。