编辑
2025-01-22
工作知识
0
请注意,本文编写于 135 天前,最后修改于 135 天前,其中某些信息可能已经过时。

目录

一、定义系统调用号
二、声明系统调用函数
三、实现系统调用函数
3.1 预编译c
四、运行

我们知道vDSO这个东西的作用之后,为了加深了解vDSO的作用,这里先以编写一个系统调用为实验,逐步了解vDSO的基本作用。

一、定义系统调用号

首先我们找到系统调用头文件:

include/uapi/asm-generic/unistd.h

里面总共449个系统调用,如下:

#define __NR_syscalls 449

为了实现我们自己的系统调用,我们扩大一个系统调用号,如下:

# git diff include/uapi/asm-generic/unistd.h diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h index f7b735dabf35..ae1050e62691 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -862,8 +862,11 @@ __SYSCALL(__NR_process_madvise, sys_process_madvise) #define __NR_process_mrelease 448 __SYSCALL(__NR_process_mrelease, sys_process_mrelease) +#define __NR_kylin 449 + __SYSCALL(__NR_kylin, sys_kylin) + #undef __NR_syscalls -#define __NR_syscalls 449 +#define __NR_syscalls 450 /* * 32 bit systems traditionally used different

二、声明系统调用函数

我们知道系统调用号对应内核需要实现系统调用的函数实现,所以需要声明,我们找到函数声明的头文件,如下:

include/linux/syscalls.h

然后声明一个自己的系统调用的声明,这里我只传入一个参数char* words,如下:

# git diff include/linux/syscalls.h diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 1c170be3f746..64ff37b08e9d 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -309,6 +309,7 @@ static inline void addr_limit_user_check(void) * include the prototypes if CONFIG_ARCH_HAS_SYSCALL_WRAPPER is enabled. */ #ifndef CONFIG_ARCH_HAS_SYSCALL_WRAPPER +asmlinkage long sys_kylin(char* words); asmlinkage long sys_io_setup(unsigned nr_reqs, aio_context_t __user *ctx); asmlinkage long sys_io_destroy(aio_context_t ctx); asmlinkage long sys_io_submit(aio_context_t, long,

三、实现系统调用函数

函数声明好了,我们需要实现这个系统调用函数,这里我跑到了常规系统调用的c文件,如下:

kernel/sys.c

我们可以发现linux的syscall的声明都是宏定义,所以我们先简单了解一下SYSCALL_DEFINEx的逻辑

还是在文件:

include/linux/syscalls.h

我们可以发现

#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) #define SYSCALL_DEFINEx(x, sname, ...) \ SYSCALL_METADATA(sname, x, __VA_ARGS__) \ __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) #define SYSCALL_METADATA(sname, nb, ...) \ static const char *types_##sname[] = { \ __MAP(nb,__SC_STR_TDECL,__VA_ARGS__) \ }; \ static const char *args_##sname[] = { \ __MAP(nb,__SC_STR_ADECL,__VA_ARGS__) \ }; \ SYSCALL_TRACE_ENTER_EVENT(sname); \ SYSCALL_TRACE_EXIT_EVENT(sname); \ static struct syscall_metadata __used \ __syscall_meta_##sname = { \ .name = "sys"#sname, \ .syscall_nr = -1, /* Filled in at boot */ \ .nb_args = nb, \ .types = nb ? types_##sname : NULL, \ .args = nb ? args_##sname : NULL, \ .enter_event = &event_enter_##sname, \ .exit_event = &event_exit_##sname, \ .enter_fields = LIST_HEAD_INIT(__syscall_meta_##sname.enter_fields), \ }; \ static struct syscall_metadata __used \ __section("__syscalls_metadata") \ *__p_syscall_meta_##sname = &__syscall_meta_##sname; #ifndef __SYSCALL_DEFINEx #define __SYSCALL_DEFINEx(x, name, ...) \ __diag_push(); \ __diag_ignore(GCC, 8, "-Wattribute-alias", \ "Type aliasing is used to sanitize syscall arguments");\ asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ __attribute__((alias(__stringify(__se_sys##name)))); \ ALLOW_ERROR_INJECTION(sys##name, ERRNO); \ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ { \ long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ __MAP(x,__SC_TEST,__VA_ARGS__); \ __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ return ret; \ } \ __diag_pop(); \ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) #endif /* __SYSCALL_DEFINEx */

这里我们基本能够看到其如何定义的,但是为了避免计算,我们可以直接预编译得到结果,如下:

3.1 预编译c

我们知道kernel/sys.c在编译之后会生成kernel/.sys.o.cmd,这是留给我们调试用的,如下:

cmd_kernel/sys.o := /root/kernel/roc-rk3588s-pc/kernel/scripts/gcc-wrapper.py gcc -Wp,-MMD,kernel/.sys.o.d -nostdinc -isystem /opt/kpgcc_release/bin/../lib/gcc/aarch64-unknown-linux-gnu/9.3.1/include -I./arch/arm64/include -I./arch/arm64/include/generated -I./include -I./arch/arm64/include/uapi -I./arch/arm64/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -mlittle-endian -DCC_USING_PATCHABLE_FUNCTION_ENTRY -DKASAN_SHADOW_SCALE_SHIFT= -fmacro-prefix-map=./= -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Wno-format-security -std=gnu89 -mgeneral-regs-only -DCONFIG_CC_HAS_K_CONSTRAINT=1 -Wno-psabi -mabi=lp64 -fno-asynchronous-unwind-tables -fno-unwind-tables -mbranch-protection=none -Wa,-march=armv8.5-a -DARM64_ASM_ARCH='"armv8.5-a"' -DKASAN_SHADOW_SCALE_SHIFT= -fno-delete-null-pointer-checks -Wno-frame-address -Wno-format-truncation -Wno-format-overflow -Wno-address-of-packed-member -O2 -fno-allow-store-data-races -Wframe-larger-than=2048 -fstack-protector-strong -Werror -Wno-unused-but-set-variable -Wno-unused-const-variable -fno-omit-frame-pointer -fno-optimize-sibling-calls -g -fpatchable-function-entry=2 -Wdeclaration-after-statement -Wno-pointer-sign -Wno-stringop-truncation -Wno-array-bounds -Wno-stringop-overflow -Wno-restrict -Wno-maybe-uninitialized -fno-strict-overflow -fno-stack-check -fconserve-stack -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -Wno-packed-not-aligned -mstack-protector-guard=sysreg -mstack-protector-guard-reg=sp_el0 -mstack-protector-guard-offset=1344 -DKBUILD_MODFILE='"kernel/sys"' -DKBUILD_BASENAME='"sys"' -DKBUILD_MODNAME='"sys"' -D__KBUILD_MODNAME=kmod_sys -c -o kernel/sys.o kernel/sys.c

我们可以利用这个文件,直接预编译这个c,如下:

/root/kernel/roc-rk3588s-pc/kernel/scripts/gcc-wrapper.py gcc -Wp,-MMD,kernel/.sys.o.d -nostdinc -isystem /opt/kpgcc_release/bin/../lib/gcc/aarch64-unknown-linux-gnu/9.3.1/include -I./arch/arm64/include -I./arch/arm64/include/generated -I./include -I./arch/arm64/include/uapi -I./arch/arm64/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -mlittle-endian -DCC_USING_PATCHABLE_FUNCTION_ENTRY -DKASAN_SHADOW_SCALE_SHIFT= -fmacro-prefix-map=./= -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Wno-format-security -std=gnu89 -mgeneral-regs-only -DCONFIG_CC_HAS_K_CONSTRAINT=1 -Wno-psabi -mabi=lp64 -fno-asynchronous-unwind-tables -fno-unwind-tables -mbranch-protection=none -Wa,-march=armv8.5-a -DARM64_ASM_ARCH='"armv8.5-a"' -DKASAN_SHADOW_SCALE_SHIFT= -fno-delete-null-pointer-checks -Wno-frame-address -Wno-format-truncation -Wno-format-overflow -Wno-address-of-packed-member -O2 -fno-allow-store-data-races -Wframe-larger-than=2048 -fstack-protector-strong -Werror -Wno-unused-but-set-variable -Wno-unused-const-variable -fno-omit-frame-pointer -fno-optimize-sibling-calls -g -fpatchable-function-entry=2 -Wdeclaration-after-statement -Wno-pointer-sign -Wno-stringop-truncation -Wno-array-bounds -Wno-stringop-overflow -Wno-restrict -Wno-maybe-uninitialized -fno-strict-overflow -fno-stack-check -fconserve-stack -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -Wno-packed-not-aligned -mstack-protector-guard=sysreg -mstack-protector-guard-reg=sp_el0 -mstack-protector-guard-offset=1344 -DKBUILD_MODFILE='"kernel/sys"' -DKBUILD_BASENAME='"sys"' -DKBUILD_MODNAME='"sys"' -D__KBUILD_MODNAME=kmod_sys -E -o kernel/sys.i kernel/sys.c

我们运行上述的编译指令,可以得到预编译的kernel/sys.i文件,打开此文件,找到我们的宏展开的内容如下:

long __arm64_sys_kylin(const struct pt_regs *regs); static struct error_injection_entry __attribute__((__used__)) __attribute__((__section__("_error_injection_whitelist"))) _eil_addr___arm64_sys_kylin = { .addr = (unsigned long)__arm64_sys_kylin, .etype = EI_ETYPE_ERRNO, };; static long __se_sys_kylin(__typeof(__builtin_choose_expr((__builtin_types_compatible_p(typeof(( char*)0), typeof(0LL)) || __builtin_types_compatible_p(typeof(( char*)0), typeof(0ULL))), 0LL, 0L)) words); static inline __attribute__((__gnu_inline__)) __attribute__((__unused__)) __attribute__((patchable_function_entry(0, 0))) long __do_sys_kylin(char* words); long __arm64_sys_kylin(const struct pt_regs *regs) { return __se_sys_kylin(regs->regs[0]); } static long __se_sys_kylin(__typeof(__builtin_choose_expr((__builtin_types_compatible_p(typeof(( char*)0), typeof(0LL)) || __builtin_types_compatible_p(typeof(( char*)0), typeof(0ULL))), 0LL, 0L)) words) { long ret = __do_sys_kylin(( char*) words); (void)((int)(sizeof(struct { int:(-!!(!(__builtin_types_compatible_p(typeof(( char*)0), typeof(0LL)) || __builtin_types_compatible_p(typeof(( char*)0), typeof(0ULL))) && sizeof(char*) > sizeof(long))); }))); do { } while (0); return ret; } static inline __attribute__((__gnu_inline__)) __attribute__((__unused__)) __attribute__((patchable_function_entry(0, 0))) long __do_sys_kylin(char* words) { char buffer[1024]; int ret; ret = copy_from_user(buffer, words, 1024); printk("kylin: Get sys_kylin call:[%s]. ret=%d \n", buffer, ret); return 0; }

这里我简化一下,如下:

long __do_sys_kylin(char* words) { char buffer[1024]; int ret; ret = copy_from_user(buffer, words, 1024); printk("kylin: Get sys_kylin call:[%s]. ret=%d \n", buffer, ret); return 0; }

我们实现系统调用的代码如下:

# git diff kernel/sys.c diff --git a/kernel/sys.c b/kernel/sys.c index 4b0232713a90..e0cc65d5500c 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -2794,6 +2794,17 @@ static int do_sysinfo(struct sysinfo *info) return 0; } +SYSCALL_DEFINE1(kylin, char*, words) +{ + char buffer[1024]; + int ret; + + ret = copy_from_user(buffer, words, 1024); + + printk("kylin: Get sys_kylin call:[%s]. ret=%d \n", buffer, ret); + return 0; +} + SYSCALL_DEFINE1(sysinfo, struct sysinfo __user *, info) { struct sysinfo val;

四、运行

根据上面的内容,我们实现了一个系统调用__do_sys_kylin,现在我们需要在应用层测试一下,代码如下:

#include <sys/syscall.h> #include <stdio.h> #include <unistd.h> #define __NR_kylin 449 int main(int argc, char *argv[]) { int ret = 0; char* words = "Userspace say:hello kylin!"; ret = syscall(__NR_kylin, words); printf("syscall ret=%d \n", ret); }

此时我们运行代码:

# ./test_kylin_syscall syscall ret=0

dmesg可以看到信息如下:

# dmesg [ 4404.069267] kylin: Get sys_kylin call:[Userspace say:hello kylin!]. ret=0

然后我们strace看看是否下发的syscall

# strace ./test_kylin_syscall 2>&1 | grep syscall_ syscall_0x1c1(0x555ad40898, 0x7fe8349d68, 0x555ad407ac, 0, 0x501e2032a017357a, 0) = 0

至此,我们在arm64下实现了一个基本的syscall。后面基于此syscall来实现vDSO调用