[toc]

include/linux/build_bug.h

static_assert 静态编译警告

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* static_assert - 在构建时检查整数常量表达式
*
* static_assert() 是 C11 _Static_assert的包装器,具有
* little 宏魔术使消息成为可选的(默认为
* 测试表达式的字符串化)。
*
* 与 BUILD_BUG_ON() 相反,static_assert() 可以在 global
* 范围,但要求表达式为整数常量
* 表达式(即,__builtin_constant_p() 是不够的
* true 表示 expr)。
*
* 另请注意BUILD_BUG_ON,如果条件为
* true,而 static_assert() 如果表达式为
*假。
*/
#define static_assert(expr, ...) __static_assert(expr, ##__VA_ARGS__, #expr)
#define __static_assert(expr, msg, ...) _Static_assert(expr, msg)

BUILD_BUG_ON_MSG 中断编译提供BUG信息

1
2
3
4
5
6
7
8
/**
* BUILD_BUG_ON_MSG - 如果条件为真,则中断编译并提供 emit
*错误信息。
* @condition:编译器应该知道的条件是 false。
*
* 有关说明,请参见 BUILD_BUG_ON。
*/
#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)

BUILD_BUG_ON_INVALID 不会生成编译时的警告

https://stackoverflow.com/questions/78432546/the-macro-build-bug-on-invalide-in-linux-kernel-seems-to-be-no-use

1
2
3
/* BUILD_BUG_ON_INVALID() 允许编译器检查表达式的有效性,但避免生成任何代码,即使该表达式具有副作用。
*/
#define BUILD_BUG_ON_INVALID(e) ((void)(sizeof((__force long)(e))))

BUILD_BUG_ON

1
2
3
4
5
6
7
8
9
10
/**
* BUILD_BUG_ON - 如果条件为 true,则中断编译。
* @condition:编译器应该知道的条件是 false。
*
* 如果你有一些代码依赖于某些常量相等,或者
* 其他一些编译时评估的条件,您应该使用 BUILD_BUG_ON 来
* 检测是否有人更改它。
*/
#define BUILD_BUG_ON(condition) \
BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition)

include/linux/compiler.h 编译时完成

unreachable 不可达代码

  • unreachable 是一个宏,用于标记不可达的代码路径。它通常用于在编译器优化时提供提示,告诉编译器某些代码永远不会被执行。这可以帮助编译器进行更好的优化,并且在调试时可以帮助开发人员识别潜在的错误或不一致之处。
  • barrier_before_unreachable 是一个宏,用于在不可达代码之前插入一个屏障。这个屏障可以防止编译器对不可达代码进行优化,从而确保编译器不会删除或重排这些代码。这个宏通常用于调试和错误检查,以确保不可达代码在编译时不会被优化掉。
1
2
3
4
#define unreachable() do {		\
barrier_before_unreachable(); \
__builtin_unreachable(); \
} while (0)

likely && unlikely 优化分支预测

  • unlikely(x) 的返回值与 x 的布尔值相同
  • likely(x) 的返回值与 x 的布尔值相同

未启用CONFIG_TRACE_BRANCH_PROFILING 追踪分支分析

  • __builtin_expect 是 GCC 提供的一个内建函数,用于向编译器提供分支预测信息。!!(x) 将 x 转换为布尔值,1 表示这个条件很可能为真。通过使用这个宏,开发者可以提示编译器优化代码路径,使得这个条件为真的情况执行得更快。
1
2
#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

启用CONFIG_TRACE_BRANCH_PROFILING 追踪分支分析

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
#define __branch_check__(x, expect, is_constant) ({			\
long ______r; \
static struct ftrace_likely_data \
__aligned(4) \
__section("_ftrace_annotated_branch") \
______f = { \
.data.func = __func__, \
.data.file = __FILE__, \
.data.line = __LINE__, \
}; \
______r = __builtin_expect(!!(x), expect); \
ftrace_likely_update(&______f, ______r, \
expect, is_constant); \
______r; \
})

/*
* Using __builtin_constant_p(x) to ignore cases where the return
* value is always the same. This idea is taken from a similar patch
* written by Daniel Walker.
*/
# ifndef likely
# define likely(x) (__branch_check__(x, 1, __builtin_constant_p(x)))
# endif
# ifndef unlikely
# define unlikely(x) (__branch_check__(x, 0, __builtin_constant_p(x)))
# endif

barrier 内存屏障

  • barrier 是一个宏,用于在编译器优化时插入一个内存屏障。内存屏障是一种指令,用于防止编译器和处理器对内存操作进行重排序,从而确保内存操作的顺序性。这在多线程和并发编程中非常重要,因为它可以确保不同线程之间的内存访问是可预测的。
1
# define barrier() __asm__ __volatile__("": : :"memory")
  • : : : "memory" 是汇编指令的操作数部分。"memory" 告诉编译器,这段汇编代码会影响内存的状态,因此编译器在这段代码之前和之后不能对内存访问进行重新排序。

is_signed_type is_unsigned_type 有符号类型 无符号类型

1
2
3
4
5
6
/*
* 'type' 是有符号类型还是无符号类型。支持标量类型,
* bool 以及指针类型。
*/
#define is_signed_type(type) (((type)(-1)) < (__force type)1)
#define is_unsigned_type(type) (!is_signed_type(type))

statically_true 编译时判断为常量

1
2
3
4
5
6
/*
* “Is this condition know at compile-work?” 的有用简写
*
* 请注意,该条件可能涉及非常量值,但编译器可能对这些值的详细信息有足够的了解,从而确定该条件是否为静态 true。
*/
#define statically_true(x) (__builtin_constant_p(x) && (x))

include/linux/compiler_types.h

__cold 表示该函数很少被调用

  • 这是 GCC 编译器的一个属性,表示该函数很少被调用。编译器可以利用这个信息进行优化,例如将这些函数放置在不常用的代码段中,减少对常用代码段的干扰,从而提高缓存命中率和整体性能。
  • CONFIG_CC_HAS_SANE_FUNCTION_ALIGNMENT:
    • 这个宏用于检查编译器是否支持合理的函数对齐。函数对齐是指将函数的起始地址对齐到特定的字节边界,以提高访问效率和性能。
  • CONFIG_FUNCTION_ALIGNMENT:
    • 这个宏用于设置函数的对齐方式。函数对齐可以提高函数调用的效率,尤其是在某些架构上,函数调用需要特定的对齐方式才能正常工作。
1
2
3
4
5
#if defined(CONFIG_CC_HAS_SANE_FUNCTION_ALIGNMENT) || (CONFIG_FUNCTION_ALIGNMENT == 0)
#define __cold __attribute__((__cold__))
#else
#define __cold
#endif

__latent_entropy 随机性

  • 这个属性通常用于内核开发中,特别是在 Linux 内核中。它用于标记那些在系统启动时需要一些随机性(entropy)的函数或变量。通过使用这个属性,编译器会确保在系统启动时,这些函数或变量能够获得一些随机数据,从而增强系统的安全性和不可预测性
1
2
3
#if defined(LATENT_ENTROPY_PLUGIN) && !defined(__CHECKER__)
#define __latent_entropy __attribute__((latent_entropy))
#endif

__always_inline 强制内联

  • __attribute__((__always_inline__)) 是 GCC 编译器特定的扩展属性,强制编译器始终内联标记的函数,即使在优化级别较低的情况下也会内联。这对于性能关键的代码段非常有用,因为它确保了函数调用的开销被最小化。
1
#define __always_inline                 inline __attribute__((__always_inline__))

__alloc_size __realloc_size 分配大小

  • __alloc_size__realloc_size 是 GCC 编译器的属性,用于标记函数的参数,以指示这些参数表示分配的内存大小。这些属性可以帮助编译器进行更好的优化和静态分析,确保在内存分配时使用正确的大小。

__force 强制转换

  • __force 是 GCC 编译器的一个属性,用于强制类型转换。它通常用于指示编译器在进行类型转换时要强制执行,而不考虑潜在的类型不匹配或警告。这在某些情况下是有用的,例如在处理低级别的硬件编程或内核开发时。
1
#define __force	__attribute__((force))

compiletime_assert 中断编译提供BUG信息

  1. 启用__OPTIMIZE__时,将prefix与suffix连接成一个函数,并且这个函数是__compiletime_error类型的,这个函数会在编译时检查条件是否成立。
  2. 如果条件不成立,则调用这个函数,从而中断编译并提供错误信息。
  3. __COUNTER__是一个预定义宏,它在每次使用时都会递增,用于生成唯一的后缀,确保每次调用compiletime_assert时都生成不同的函数名。所以prefix##suffix将会生成一个唯一的函数名。
    • 例如
    1
    2
    3
    __noreturn extern void prefix ## suffix(void)		\
    __compiletime_error(msg); \
    __noreturn extern void __compiletime_assert_1(void) __compiletime_error("int size is not 4 bytes");
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
#ifdef __OPTIMIZE__
# define __compiletime_assert(condition, msg, prefix, suffix) \
do { \
/* \
* 需要__noreturn来为编译器提供足够的 \
* 避免某些可能未初始化的信息 \
* 警告(无论构建是否失败)。 \
*/ \
__noreturn extern void prefix ## suffix(void) \
__compiletime_error(msg); \
if (!(condition)) \
prefix ## suffix(); \
} while (0)
#else
# define __compiletime_assert(condition, msg, prefix, suffix) do { } while (0)
#endif

#define _compiletime_assert(condition, msg, prefix, suffix) \
__compiletime_assert(condition, msg, prefix, suffix)

/**
* compiletime_assert - 如果 Condition 为 false,则中断构建并发出 msg
* @condition:要检查的编译时常量条件
* @msg:如果 condition 为 false,则发出一条消息
*
* 按照 POSIX 断言的传统,如果
* 提供的条件为 *false*,如果
* 编译器支持这样做。
*/
#define compiletime_assert(condition, msg) \
_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)

BTF_TYPE_TAG

  • BTF_TYPE_TAG 是一个宏,用于在编译时为类型添加 BTF(BPF Type Format)标签。BTF 是一种用于描述内核数据结构的格式,主要用于 BPF(Berkeley Packet Filter)程序的类型信息。
  • 通过添加 btf_type_tag 属性,可以为类型提供额外的元数据,帮助调试和分析工具更好地理解和处理这些类型。
1
2
3
4
5
6
#if defined(CONFIG_DEBUG_INFO_BTF) && defined(CONFIG_PAHOLE_HAS_BTF_TAG) && \
__has_attribute(btf_type_tag) && !defined(__BINDGEN__)
# define BTF_TYPE_TAG(value) __attribute__((btf_type_tag(#value)))
#else
# define BTF_TYPE_TAG(value) /* nothing */
#endif

__native_word 本机字节长度

1
2
3
4
/* 此类型是本机字大小吗 -- 对原子作有用 */
#define __native_word(t) \
(sizeof(t) == sizeof(char) || sizeof(t) == sizeof(short) || \
sizeof(t) == sizeof(int) || sizeof(t) == sizeof(long))

__unqual_scalar_typeof 用于获取变量的非限定标量类型

  • _Generic: _Generic 是 C11 标准中的一种泛型选择机制,它根据表达式的类型选择一个对应的表达式。在这里,它用于选择适当的类型转换表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
* __unqual_scalar_typeof(x) - 声明一个非限定标量类型,留下
* 非标量类型保持不变。
*/
/*
* 首选 C11 _Generic以获得更好的编译时间和更简单的代码。注意: 'char'
* 与 'signed char' 类型不兼容,我们定义一个单独的 case。
*/
#define __scalar_type_to_expr_cases(type) \
unsigned type: (unsigned type)0, \
signed type: (signed type)0

#define __unqual_scalar_typeof(x) typeof( \
_Generic((x), \
char: (char)0, \
__scalar_type_to_expr_cases(char), \
__scalar_type_to_expr_cases(short), \
__scalar_type_to_expr_cases(int), \
__scalar_type_to_expr_cases(long), \
__scalar_type_to_expr_cases(long long), \
default: (x)))

noinstr 禁止内联插桩的段

  1. noinstr 是一个内核特定的函数属性,用于标记那些不允许插入任何额外代码(如调试、性能分析或跟踪代码)的函数。这些函数通常是与硬件或中断处理密切相关的关键代码,任何额外的插桩代码都可能导致不可预测的行为或性能问题。
1
2
3
4
5
6
7
/* Section for code which can't be instrumented at all */
#define __noinstr_section(section) \
noinline notrace __attribute((__section__(section))) \
__no_kcsan __no_sanitize_address __no_profile __no_sanitize_coverage \
__no_sanitize_memory __signed_wrap

#define noinstr __noinstr_section(".noinstr.text")

__same_type 类型是否相等

1
2
/* Are two types/vars the same type (ignoring qualifiers)? */
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))

include/linux/kbuild.h

  • 定义了.c生成.s的相关宏
1
2
3
4
5
6
7
8
9
10
11
#define DEFINE(sym, val) \
asm volatile("\n.ascii \"->" #sym " %0 " #val "\"" : : "i" (val))

#define BLANK() asm volatile("\n.ascii \"->\"" : : )

#define OFFSET(sym, str, mem) \
DEFINE(sym, offsetof(struct str, mem))

#define COMMENT(x) \
asm volatile("\n.ascii \"->#" x "\"")

include/linux/kconfig.h

  1. #define __ARG_PLACEHOLDER_1 0,定义了一个宏__ARG_PLACEHOLDER_1,它的值是0,。这个宏用于在后续的宏中作为占位符。
  2. 如果宏没有定义,则这里的__ARG_PLACEHOLDER_1将不会被替换,而是直接使用0,作为参数。
  3. #define __take_second_arg(__ignored, val, ...) val定义了一个宏__take_second_arg,它接受多个参数,但只返回第二个参数val。这个宏用于在后续的宏中提取第二个参数。
  4. 所以宏有定义,则返回第二个参数1,没有定义,则返回第3个参数0
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#define __ARG_PLACEHOLDER_1 0,
#define __take_second_arg(__ignored, val, ...) val

/*
* The use of "&&" / "||" is limited in certain expressions.
* The following enable to calculate "and" / "or" with macro expansion only.
*/
#define __and(x, y) ___and(x, y)
#define ___and(x, y) ____and(__ARG_PLACEHOLDER_##x, y)
#define ____and(arg1_or_junk, y) __take_second_arg(arg1_or_junk y, 0)

#define __or(x, y) ___or(x, y)
#define ___or(x, y) ____or(__ARG_PLACEHOLDER_##x, y)
#define ____or(arg1_or_junk, y) __take_second_arg(arg1_or_junk 1, y)

/*
* Helper macros to use CONFIG_ options in C/CPP expressions. Note that
* these only work with boolean and tristate options.
*/

/*
* Getting something that works in C and CPP for an arg that may or may
* not be defined is tricky. Here, if we have "#define CONFIG_BOOGER 1"
* we match on the placeholder define, insert the "0," for arg1 and generate
* the triplet (0, 1, 0). Then the last step cherry picks the 2nd arg (a one).
* When CONFIG_BOOGER is not defined, we generate a (... 1, 0) pair, and when
* the last step cherry picks the 2nd arg, we get a zero.
*/
#define __is_defined(x) ___is_defined(x)
#define ___is_defined(val) ____is_defined(__ARG_PLACEHOLDER_##val)
#define ____is_defined(arg1_or_junk) __take_second_arg(arg1_or_junk 1, 0)

/*
* IS_BUILTIN(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y', 0
* otherwise. For boolean options, this is equivalent to
* IS_ENABLED(CONFIG_FOO).
*/
#define IS_BUILTIN(option) __is_defined(option)

/*
* IS_MODULE(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'm', 0
* otherwise. CONFIG_FOO=m results in "#define CONFIG_FOO_MODULE 1" in
* autoconf.h.
*/
#define IS_MODULE(option) __is_defined(option##_MODULE)

/*
* IS_REACHABLE(CONFIG_FOO) evaluates to 1 if the currently compiled
* code can call a function defined in code compiled based on CONFIG_FOO.
* This is similar to IS_ENABLED(), but returns false when invoked from
* built-in code when CONFIG_FOO is set to 'm'.
*/
#define IS_REACHABLE(option) __or(IS_BUILTIN(option), \
__and(IS_MODULE(option), __is_defined(MODULE)))

/*
* IS_ENABLED(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y' or 'm',
* 0 otherwise. Note that CONFIG_FOO=y results in "#define CONFIG_FOO 1" in
* autoconf.h, while CONFIG_FOO=m results in "#define CONFIG_FOO_MODULE 1".
*/
#define IS_ENABLED(option) __or(IS_BUILTIN(option), IS_MODULE(option))