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.h

always  += $(offsets-file)
targets += $(offsets-file)
targets += arch/$(SRCARCH)/kernel/asm-offsets.s

# Default sed regexp - multiline due to syntax constraints
define sed-y
    "/^->/{s:->#\(.*\):/* \1 */:;     s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:;     s:->::; p;}"
endef

quiet_cmd_offsets = GEN     [email protected]
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" ) > [email protected]
endef

# We use internal kbuild rules to avoid the "is up to date" message from make
arch/$(SRCARCH)/kernel/asm-offsets.s: arch/$(SRCARCH)/kernel/asm-offsets.c                                       $(obj)/$(bounds-file) FORCE
    $(Q)mkdir -p $(dir [email protected])
    $(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.h

define sed-y
    "/^->/{s:->#\(.*\):/* \1 */:;     s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:;     s:->::; p;}"
endef

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" ) > [email protected]
endef

asm-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 中添加相应的系统调用号就可以了。

时间: 2024-07-28 18:24:16

asm-offset.h 生成的相关文章

KVM源代码解读:linux-3.17.4\arch\x86\include\asm\kvm_host.h

/* * Kernel-based Virtual Machine driver for Linux * * This header defines architecture specific interfaces, x86 version * * This work is licensed under the terms of the GNU GPL, version 2.  See * the COPYING file in the top-level directory. * */ #if

《linux 内核全然剖析》 include/asm/io.h

include/asm/io.h #define outb(value,port) __asm__ ("outb %%al,%%dx"::"a" (value),"d" (port)) //宏定义outb用汇编实现了在端口地址port处写入值value //使用的寄存器是al,一个byte长度,而端口port使用的是2byte长度地址来标记的寄存器,注意这里寄存器的使用 #define inb(port) ({ unsigned char _v;

《linux 内核完全剖析》 include/asm/io.h

include/asm/io.h #define outb(value,port) __asm__ ("outb %%al,%%dx"::"a" (value),"d" (port)) //宏定义outb用汇编实现了在端口地址port处写入值value //使用的寄存器是al,一个byte长度,而端口port使用的是2byte长度地址来标记的寄存器,注意这里寄存器的使用 #define inb(port) ({ unsigned char _v;

include/asm/dma.h

/* $Id: dma.h,v 1.7 1992/12/14 00:29:34 root Exp root $ * linux/include/asm/dma.h: Defines for using and allocating dma channels. * Written by Hennus Bergman, 1992. * High DMA channel support & info by Hannu Savolainen * and John Boyd, Nov. 1992. */

CMake构建NDK项目提示asm/types.h找不到

用CMake构建NDK项目时,会传入toolchain的cmake脚本文件android.toolchain.cmake给CMake.这个文件中会做若干设定,其中就包括include路径. 我遇到的情况是,自己手动修改CMAKE_C_FLAGS和CMAKE_CXX_FLAGS时,覆盖了它们原有的(android.toolchain.cmake修改后的)值,导致asm/types.h找不到. 我的错误设定: set(CMAKE_C_FLAGS "${MY_CMAKE_C_FLAGS}")

Linux设备文件自动生成

第一种是使用mknod手工创建:# mknod <devfilename> <devtype> <major> <minor> 第二种是自动创建设备节点:利用udev(mdev)来实现设备文件的自动创建,首先应保证支持udev(mdev),由busybox配置. 具体udev相关知识这里不详细阐述,可以移步Linux 文件系统与设备文件系统 -- udev 设备文件系统,这里主要讲使用方法. 在驱动用加入对udev 的支持主要做的就是:在驱动初始化的代码里调

KVM源代码解读:linux-3.17.4\include\uapi\linux\kvm.h

#ifndef __LINUX_KVM_H #define __LINUX_KVM_H /* * Userspace interface for /dev/kvm - kernel based virtual machine * * Note: you must update KVM_API_VERSION if you change this interface. */ #include <linux/types.h> #include <linux/compiler.h> #i

KVM源代码解读:linux-3.17.4\include\linux\kvm_host.h

#ifndef __KVM_HOST_H #define __KVM_HOST_H /* * This work is licensed under the terms of the GNU GPL, version 2.  See * the COPYING file in the top-level directory. */ #include <linux/types.h> #include <linux/hardirq.h> #include <linux/list.

[转] 使用CodeViz生成C/C++函数调用关系图

运行环境:虚拟机下的Ubuntu 11.04 结合Graphviz工具,使用CodeViz可以生成直观和漂亮的C/C++程序函数之间的调用关系图. 1.安装graphviz 在安装CodeViz之前,必须先安装它所依赖的工具dot,否则将无法完成./configure操作并提示以下错误信息: checking for dot...not found FATAL: The program dot was not in your path. This is probably available fo