[toc]

Makefile宏

LINUX_ARM_ARCH

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//arch/arm/Makefile
# Note that GCC does not numerically define an architecture version
# macro, but instead defines a whole series of macros which makes
# testing for a specific architecture or later rather impossible.
cpp-$(CONFIG_CPU_32v7M) :=-D__LINUX_ARM_ARCH__=7
cpp-$(CONFIG_CPU_32v7) :=-D__LINUX_ARM_ARCH__=7
cpp-$(CONFIG_CPU_32v6) :=-D__LINUX_ARM_ARCH__=6
# Only override the compiler option if ARMv6. The ARMv6K extensions are
# always available in ARMv7
ifeq ($(CONFIG_CPU_32v6),y)
cpp-$(CONFIG_CPU_32v6K) :=-D__LINUX_ARM_ARCH__=6
endif
cpp-$(CONFIG_CPU_32v5) :=-D__LINUX_ARM_ARCH__=5
cpp-$(CONFIG_CPU_32v4T) :=-D__LINUX_ARM_ARCH__=4
cpp-$(CONFIG_CPU_32v4) :=-D__LINUX_ARM_ARCH__=4
cpp-$(CONFIG_CPU_32v3) :=-D__LINUX_ARM_ARCH__=3

include/linux/typecheck.h 定义了内核与用户空间共享的基础类型,如 __u8, __s32

typecheck 类型检查

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
* 在编译时检查某些内容是否属于特定类型。
* 始终计算为 1,因此您可以轻松地在比较中使用它。
*/
#define typecheck(type,x) \
({ type __dummy; \
typeof(x) __dummy2; \
/* 通过比较 __DUMMY 和 __DUMMY2 的地址,检查它们的类型是否相同。
如果 TYPE 和 TYPEOF(X) 不同,编译器会报错,因为无法比较不同类型的指针。
*/
(void)(&__dummy == &__dummy2); \
1; \
})

include/uapi/linux/types.h 定义了内核与用户空间共享的基础类型,如 __u8, __s32

__bitwise 位域标记

  • attribute((bitwise)) 是一个编译器特定的扩展属性,通常用于标记某些类型或变量,以指示它们应该被视为位域或位操作相关的类型。这种属性可以帮助编译器进行更严格的类型检查,防止误用位操作符或混淆不同的位域类型。
1
2
3
4
5
#ifdef __CHECKER__
#define __bitwise __attribute__((bitwise))
#else
#define __bitwise
#endif

include/linux/export.h 提供 EXPORT_SYMBOL() 宏,用于将内核符号导出给可加载模块使用

__EXPORT_SYMBOL_REF 导出符号引用

1
2
3
4
5
6
7
8
9
#ifdef CONFIG_64BIT
#define __EXPORT_SYMBOL_REF(sym) \
.balign 8 ASM_NL \
.quad sym
#else
#define __EXPORT_SYMBOL_REF(sym) \
.balign 4 ASM_NL \
.long sym //将符号sym的地址作为一个32位整数插入到汇编代码中
#endif

___EXPORT_SYMBOL 导出符号底层实现

  • ___EXPORT_SYMBOL宏通过将符号、许可证信息和命名空间信息嵌入到特定的汇编段中,确保这些信息在最终生成的二进制文件中可用。这在模块化编程和内核开发中非常有用,允许不同模块之间共享符号和元数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
#define ___EXPORT_SYMBOL(sym, license, ns)		\
//将接下来的内容放入名为.export_symbol的汇编段中。"a"表示该段具有分配属性(allocatable),即在最终的可执行文件中分配空间。ASM_NL是一个换行符宏,用于确保生成的汇编代码格式正确。
.section ".export_symbol","a" ASM_NL \
// 定义了一个新的标签,名称为__export_symbol_后跟符号名sym
__export_symbol_##sym: ASM_NL \
//将许可证信息作为以空字符结尾的字符串(ASCII zero-terminated string)嵌入到汇编代码中。license参数包含许可证信息。
.asciz license ASM_NL \
//这行代码将命名空间信息作为以空字符结尾的字符串嵌入到汇编代码中。ns参数包含命名空间信息。
.asciz ns ASM_NL \
// 生成对符号sym的引用
__EXPORT_SYMBOL_REF(sym) ASM_NL \
// .previous:这行代码将当前段切换回之前的段。它确保后续的汇编代码不会继续写入.export_symbol段。
.previous

__EXPORT_SYMBOL 导出符号具体实现

  1. __DISABLE_EXPORTS 宏用于完全禁用元件导出,以便 C 代码可以在其他执行上下文中重用,例如 UEFI 存根或解压缩器。
  2. __GENKSYMS__ 宏用于生成符号导出信息,以便在内核模块之间共享符号。在scripts/Makefile.build 进行定义使用
  3. __ASSEMBLY__ 宏用于在汇编代码中使用导出符号。它通常用于在汇编代码中引用 C 语言中的符号。
  4. CONFIG_GENDWARFKSYMS 宏用于确保编译器为所有导出符号生成调试信息,包括在不同的翻译单元中定义的符号。它通过添加一个指向符号的指针来实现,这个指针在最终链接时被丢弃。
  5. 默认情况下,__EXPORT_SYMBOL 宏用于导出符号。它会生成一个汇编指令,将符号导出到指定的命名空间中。这个宏通常用于内核模块之间共享符号,以便在不同的模块中使用相同的符号。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#if defined(__DISABLE_EXPORTS)
/*
* 允许完全禁用元件导出,以便 C 代码可以
* 在其他执行上下文中重用,例如 UEFI 存根或
* 解压缩器。
*/
#define __EXPORT_SYMBOL(sym, license, ns)

#elif defined(__GENKSYMS__)

#define __EXPORT_SYMBOL(sym, license, ns) __GENKSYMS_EXPORT_SYMBOL(sym)

#elif defined(__ASSEMBLY__) //汇编中使用

#define __EXPORT_SYMBOL(sym, license, ns) \
___EXPORT_SYMBOL(sym, license, ns)

#else

#ifdef CONFIG_GENDWARFKSYMS
/*
* 使用 CONFIG_GENDWARFKSYMS,确保编译器发出调试
* 所有导出的符号的信息,包括
* 不同的 TU,通过添加 __gendwarfksyms_ptr_<symbol> 指针
* 在最终链接期间被丢弃。
*/
#define __GENDWARFKSYMS_EXPORT(sym) \
static typeof(sym) *__gendwarfksyms_ptr_##sym __used \
__section(".discard.gendwarfksyms") = &sym;
#else
#define __GENDWARFKSYMS_EXPORT(sym)
#endif

#define __EXPORT_SYMBOL(sym, license, ns) \
extern typeof(sym) sym; \
//__ADDRESSABLE(sym):这个宏通常用于确保符号sym是可寻址的,即使它没有在当前文件中被直接引用。
__ADDRESSABLE(sym) \
//生成DWARF调试符号相关,确保符号sym在调试信息中被正确导出。
__GENDWARFKSYMS_EXPORT(sym) \
//使用内联汇编,将___EXPORT_SYMBOL(sym, license, ns)转换为字符串,并将其嵌入到汇编代码中
asm(__stringify(___EXPORT_SYMBOL(sym, license, ns)))

#endif

EXPORT_SYMBOL 导出符号

  • 这个宏用于将符号 sym 导出,使其可以被其他内核模块使用。
1
2
3
4
#define EXPORT_SYMBOL(sym)		_EXPORT_SYMBOL(sym, "")
#define EXPORT_SYMBOL_GPL(sym) _EXPORT_SYMBOL(sym, "GPL") //导出的符号只能被GPL兼容的模块使用
#define EXPORT_SYMBOL_NS(sym, ns) __EXPORT_SYMBOL(sym, "", ns)//将符号 sym 导出到指定的命名空间 ns 中
#define EXPORT_SYMBOL_NS_GPL(sym, ns) __EXPORT_SYMBOL(sym, "GPL", ns)

include/linux/init.h 提供 __init、__exit、module_init 等宏,用于标记初始化和退出函数

__init __exitdata等 内核可以将此视为该函数仅在初始化阶段使用的提示,并在初始化阶段之后释放已使用的内存资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
* 这些宏用于将某些函数或初始化的数据(不适用于未初始化的数据)标记为 'initialization' 函数。内核可以将此视为该函数仅在初始化阶段使用的提示,并在初始化阶段之后释放已使用的内存资源
*
*用法:
* 功能:
*
* 您应该在函数名称前添加 __init,例如:
*
* static void __init initme(int x, int y)
* {
* extern int z;z = x * y;
* }
*
* 如果函数在某处有原型,您还可以添加
* __init原型的右大括号和分号之间:
*
* extern int initialize_foobar_device(intintint) __init;
*
* 对于初始化的数据:
* 您应该在变量名称之间插入 __initdata 或 __initconst
* 和等号后跟值,例如:
*
* static int init_variable __initdata = 0;
* static const char linux_logo[] __initconst = { 0x320x36, ... };
*
* 不要忘记初始化不在文件范围(即在函数内)的数据,
* 因为 gcc 否则会将数据放入 BSS 部分,而不是 init 中
*部分。
*/

/* /* 这些适合所有人(尽管并非所有 arch 实际上都会将其划分为模块) */ */
#define __init __section(".init.text") __cold __latent_entropy __noinitretpoline
#define __initdata __section(".init.data")
#define __initconst __section(".init.rodata")
#define __exitdata __section(".exit.data")
#define __exit_call __used __section(".exitcall.exit")

include/linux/thread_info.h 定义 thread_info 结构体,连接进程描述符和内核栈

current_thread_info 获取当前线程信息

  • current_thread_info 是一个宏,用于获取当前线程的信息。它通常用于访问与当前线程相关的数据结构,例如线程的状态、优先级等。
1
2
3
4
5
6
7
8
#ifdef CONFIG_THREAD_INFO_IN_TASK
/*
* 对于 CONFIG_THREAD_INFO_IN_TASK 内核,我们需要 <asm/current.h> 来定义 current,
* 但对于 !CONFIG_THREAD_INFO_IN_TASK 内核,包括 <asm/current.h> 可能会导致某些平台上的循环依赖。
*/
#include <asm/current.h>
#define current_thread_info() ((struct thread_info *)current)
#endif

tif_need_resched 返回是否需要调度

  • 测试&current_thread_info()->flags的TIF_NEED_RESCHED位是否为1
1
2
3
4
5
6
7
8
9
10
static __always_inline bool tif_test_bit(int bit)
{
return test_bit(bit,
(unsigned long *)(&current_thread_info()->flags));
}
static __always_inline bool tif_need_resched(void)
{
return tif_test_bit(TIF_NEED_RESCHED);
}