首页 > 代码库 > asm-offset.h 生成
asm-offset.h 生成
内核文件 arch/x86/kernel/syscall_64.c 文件中包含了一个头文件 arch/x86/include/asm/asm-offset.h ,这个文件在内核构建之初是不存在的,是在构建过程中生成的。下面我们来看看它是如何生成的。
1. include/linux/kbuild.h
#define DEFINE(sym, val) \ asm volatile("\n->" #sym " %0 " #val : : "i" (val))
这是一个内联汇编宏,不过实际上它不会生成合法的内联汇编代码,它只是利用了内联汇编中嵌入立即数的功能。
2. arch/x86/include/asm/unistd.h
这个文件使用了宏控制,在 x86_64 平台下包含了 arch/x86/include/asm/unistd_64.h ,部分内容如下,
...#ifndef __SYSCALL#define __SYSCALL(a, b)#endif...#define __NR_read 0__SYSCALL(__NR_read, sys_read)#define __NR_write 1__SYSCALL(__NR_write, sys_write)#define __NR_open 2__SYSCALL(__NR_open, sys_open)#define __NR_close 3__SYSCALL(__NR_close, sys_close)...
3. arch/x86/kernel/asm-offsets_64.c
...#include <linux/kbuild.h>...#define __NO_STUBS 1#undef __SYSCALL#undef _ASM_X86_UNISTD_64_H#define __SYSCALL(nr, sym) [nr] = 1,static char syscalls[] = {#include <asm/unistd.h>};...DEFINE(__NR_syscall_max, sizeof(syscalls) - 1);
syscalls 这个数组的大小刚好就等于所有系统调用项总的值,注意它类型是 char 型数组,而且也请注意 gnu c 数组初始化时的扩展语法。
4. Kbuild
这个文件在根目录下,实际上是一个 Makefile,它的部分内容如下
###### 2) Generate asm-offsets.h#offsets-file := include/asm/asm-offsets.halways += $(offsets-file)targets += $(offsets-file)targets += arch/$(SRCARCH)/kernel/asm-offsets.s# Default sed regexp - multiline due to syntax constraintsdefine sed-y "/^->/{s:->#\(.*\):/* \1 */:; s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; s:->::; p;}"endefquiet_cmd_offsets = GEN $@define cmd_offsets (set -e; \ echo "#ifndef __ASM_OFFSETS_H__"; \ echo "#define __ASM_OFFSETS_H__"; \ echo "/*"; \ echo " * DO NOT MODIFY."; \ echo " *"; \ echo " * This file was generated by Kbuild"; \ echo " *"; \ echo " */"; \ echo ""; \ sed -ne $(sed-y) $<; \ echo ""; \ echo "#endif" ) > $@endef# We use internal kbuild rules to avoid the "is up to date" message from makearch/$(SRCARCH)/kernel/asm-offsets.s: arch/$(SRCARCH)/kernel/asm-offsets.c $(obj)/$(bounds-file) FORCE $(Q)mkdir -p $(dir $@) $(call if_changed_dep,cc_s_c)$(obj)/$(offsets-file): arch/$(SRCARCH)/kernel/asm-offsets.s Kbuild $(call cmd,offsets)
也就是先用 .c 文件生成 .s 文件,然后再用 sed 命令对其中特定的行进行替换,进而重定向到目标文件中,也就是 asm-offset.h。
5. 模拟
有了基于上面过程的分析,我们可以自己对这个过程进行构建。
(1)kbuild.h
#ifndef _KBUILD_H_#define _KBUILD_H_#define DEFINE(sym, val) \ asm volatile("\n->" #sym " %0 " #val : : "i" (val))#endif
(2)unistd.h
#ifndef _UNISTD_H_#define _UNISTD_H_#ifndef __SYSCALL#define __SYSCALL(a, b)#endif#define __NR_read 0__SYSCALL(__NR_read, sys_read)#define __NR_write 1__SYSCALL(__NR_write, sys_write)#define __NR_open 2__SYSCALL(__NR_open, sys_open)#define __NR_close 3__SYSCALL(__NR_close, sys_close)#endif
(3)asm-offsets.c
#include "kbuild.h"#define __NO_STUBS 1#undef __SYSCALL#undef _UNISTD_H_#define __SYSCALL(nr, sym) [nr] = 1,static char syscalls[] = {#include "unistd.h"};int main(void){ DEFINE(__NR_syscall_max, sizeof(syscalls) - 1); return 0;}
注意没有 main 函数会报错的。
(4)Makefile
offsets-file := asm-offsets.hdefine sed-y "/^->/{s:->#\(.*\):/* \1 */:; s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; s:->::; p;}"endefdefine cmd_offsets (set -e; \ echo "#ifndef __ASM_OFFSETS_H__"; \ echo "#define __ASM_OFFSETS_H__"; \ echo "/*"; \ echo " * DO NOT MODIFY."; \ echo " *"; \ echo " * This file was generated by Kbuild"; \ echo " *"; \ echo " */"; \ echo ""; \ sed -ne $(sed-y) $<; \ echo ""; \ echo "#endif" ) > $@endefasm-offsets.s: asm-offsets.c gcc -S $<$(offsets-file): asm-offsets.s @$(cmd_offsets)
只要执行命令
make asm-offsets.h
就可以一生成下面的文件,
#ifndef __ASM_OFFSETS_H__#define __ASM_OFFSETS_H__/* * DO NOT MODIFY. * * This file was generated by Kbuild * */#define __NR_syscall_max 3 /* sizeof(syscalls) - 1 */#endif
所以在构建之前生成这个头文件,就可以完成对 __NR_syscall_max 的自动赋值,进入如果要增加系统调用选项,只需要在 unistd.h 中添加相应的系统调用号就可以了。
asm-offset.h 生成