Linux kernel中常见的宏整理
本文为看雪论坛优秀文章
看雪论坛作者ID:PlaneJun
// object-like
//function-like
替换列表和标识符列表都是将字符串 token 化以后的列表。区别在于标识符列表使用,作为不同参数之间的分割符。每一个参数都是一个 token 化的列表。在宏中空白符只起到分割 token 的作用,空白符的多少对于预处理器是没有意义的。
宏的一些奇技淫巧:
https://gaomf.cn/2017/10/06/C_Macro/
以下是整理的一些linux kernel中的常见宏,由于不同体系架构,或者不同模块的宏定义不同,只挑选了其中容易看懂的宏作为记录,实现的功能大体一样。
Linux内核中do{...}while(0)意义:
辅助定义复杂的宏,避免引用的时候出错,如果不用{},if后面的语句只有第一条进行了判断。同时避免宏展开后“;”造成编译不通过.
避免使用goto,对程序流进行统一的控制,使用break跳出
避免空宏引起的warning
定义一个单独的函数块来实现复杂的操作
__CONCAT宏
"##"用于粘贴两个参数,"#"用于替换参数:
#define __CONCAT(a, b) a ## b
BUG_ON(condition)
条件为真,产生崩溃, 原理:未定义的异常。
相对应的有 WARN_ON:
#define BUG() assert(0)
#define BUG_ON(x) assert(!(x))
/* Does it make sense to treat warnings as errors? */
#define WARN() BUG()
#define WARN_ON(x) (BUG_ON(x), false)
BUILD_BUG_ON宏
condition为真时,sizeof(char[-1]),产生错误,编译不通过
condition为假时,sizeof(char[1]),编译通过
BUILD_BUG_ON_ZERO(e) 宏
typecheck宏
* Check at compile time that something is of a particular type.
* Always evaluates to 1 so you may use it easily in comparisons.
*/
({ type __dummy; \
typeof(x) __dummy2; \
(void)(&__dummy == &__dummy2); \
1; \
})
/*GCC的一个扩展特性,形如({ ... })这样的代码块会被视为一条语句,
* 其计算结果是{ ... }中最后一条语句的计算结果。
* 所以上述会返回1
*/
/*
* Check at compile time that 'function' is a certain type, or is a pointer
* to that type (needs to use typedef for the function type.)
*/
({ typeof(type) __tmp = function; \
(void)__tmp; \
})
min宏
(__typecheck(x, y) && __no_side_effects(x, y))
(__is_constexpr(x) && __is_constexpr(y))
typeof(x) unique_x = (x); \
typeof(y) unique_y = (y); \
__cmp(unique_x, unique_y, op); })
/*重新赋值为了防止x++这种重复+1 */
__builtin_choose_expr(__safe_cmp(x, y), \ //比较x, y的类型
__cmp(x, y, op), \ //x,y类型一样时
__cmp_once(x, y, __UNIQUE_ID(__x), __UNIQUE_ID(__y), op))
//x, y类型不同时
__is_constexpr宏
* This returns a constant expression while determining if an argument is
* a constant expression, most importantly without evaluating the argument.
* Glory to Martin Uecker <Martin.Uecker@med.uni-goettingen.de>
*/
(sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
所以会出现以下两种情况:
sizeof(int) == sizeof(*((void *)(....))) // otherwise
https://stackoverflow.com/questions/49481217/linux-kernels-is-constexpr-macro
max宏
roundup宏
{ \
const typeof(y) __y = y; \
(((x) + (__y - 1)) / __y) * __y; \
} \
)
clamp 宏
* clamp - return a value clamped to a given range with strict typechecking
* @val: current value
* @lo: lowest allowable value
* @hi: highest allowable value
*
* This macro does strict typechecking of @lo/@hi to make sure they are of the
* same type as @val. See the unnecessary pointer comparisons.
*/
#define clamp(val, lo, hi) min((typeof(val))max(val, lo), hi)
abs宏
* abs - return absolute value of an argument
* @x: the value. If it is unsigned type, it is converted to signed type first.
* char is treated as if it was signed (regardless of whether it really is)
* but the macro's return type is preserved as char.
*
* Return: an absolute value of x.
*/
__abs_choose_expr(x, long, \
__abs_choose_expr(x, int, \
__abs_choose_expr(x, short, \
__abs_choose_expr(x, char, \
__builtin_choose_expr( \
__builtin_types_compatible_p(typeof(x), char), \
(char)({ signed char __x = (x); __x<0?-__x:__x; }), \
((void)0)))))))
__builtin_types_compatible_p(typeof(x), signed type) || \
__builtin_types_compatible_p(typeof(x), unsigned type), \
({ signed type __x = (x); __x < 0 ? -__x : __x; }), other)
swap 宏
* swap - swap value of @a and @b
*/
#define swap(a, b) \
do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0)
container_of宏
/*结构体地址为0,将member地址转成size_t类型作为偏移
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \ //*__mptr保存该member变量的指针
(type *)( (char *)__mptr - offsetof(type,member) );}) //变量指针减去自身偏移得到指向结构体的指针
likely和unlikely宏
#define unlikely(x) __builtin_exp ect(!!(x), 0)
ALIGN对齐宏
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
#define __ALIGN_MASK(x, mask) __ALIGN_KERNEL_MASK((x), (mask))
__get_unaligned_le(ptr)宏
static inline u32 get_unaligned_be32(const void *p)
{
return __get_unaligned_cpu32((const u8 *)p);
}
static inline u32 __get_unaligned_cpu32(const void *p)
{
const struct __una_u32 *ptr = (const struct __una_u32 *)p;
return ptr->x;
}
struct __una_u16 { u16 x; } __packed;
struct __una_u32 { u32 x; } __packed;
struct __una_u64 { u64 x; } __packed;
类似:
__put_unaligned_le宏
void *__gu_p = (ptr); \
switch (sizeof(*(ptr))) { \
case 1: \
*(u8 *)__gu_p = (__force u8)(val); \
break; \
case 2: \
put_unaligned_le16((__force u16)(val), __gu_p); \
break; \
case 4: \
put_unaligned_le32((__force u32)(val), __gu_p); \
break; \
case 8: \
put_unaligned_le64((__force u64)(val), __gu_p); \
break; \
default: \
__bad_unaligned_access_size(); \
break; \
} \
(void)0; })
static inline void put_unaligned_be32(u32 val, void *p)
{
__put_unaligned_cpu32(val, p);
}
static inline void __put_unaligned_cpu32(u32 val, void *p)
{
struct __una_u32 *ptr = (struct __una_u32 *)p;
ptr->x = val;
}
ACCESS_ONCE 宏
使用 ACCESS_ONCE() 的两个条件是:
在无锁的情况下访问全局变量
对该变量的访问可能被编译器优化成合并成一次或者拆分成多次
ACCESS_OK宏
* access_ok: - Checks if a user space pointer is valid
* @addr: User space pointer to start of block to check
* @size: Size of block to check
*
* Context: User context only. This function may sleep if pagefaults are
* enabled.
*
* Checks if a pointer to a block of memory in user space is valid.
*
* Returns true (nonzero) if the memory block may be valid, false (zero)
* if it is definitely invalid.
*
* Note that, depending on architecture, this function probably just
* checks that the pointer is in the user space range - after calling
* this function, memory access functions may still return -EFAULT.
*/
/*__range_not_ok返回0才能验证通过
#define __range_not_ok(addr, size, limit) \
({ \
__chk_user_ptr(addr); \
__chk_range_not_ok((unsigned long __force)(addr), size, limit); \
})
/*
* Test whether a block of memory is a valid user space address.
* Returns 0 if the range is valid, nonzero otherwise.
*/
static inline bool __chk_range_not_ok(unsigned long addr, unsigned long size, unsigned long limit)
{
/*
* If we have used "sizeof()" for the size,
* we know it won't overflow the limit (but
* it might overflow the 'addr', so it's
* important to subtract the size from the
* limit, not add it to the address).
*/
if (__builtin_constant_p(size))
return unlikely(addr > limit - size);
/*__builtin_constant_p判断编译时是否为常数,如果是则返回1 */
/* Arbitrary sizes? Be careful about overflow */
addr += size;
if (unlikely(addr < size))
return true;
return unlikely(addr > limit);
}
mdelay宏
msleep是休眠函数,它不涉及忙等待.用msleep(200)的时候实际上延迟的时间,大部分时候是要多于200ms,是个不定的时间值。
(__builtin_constant_p(n) && (n)<=MAX_UDELAY_MS) ? udelay((n)*1000) : \
({unsigned long __ms=(n); while (__ms--) udelay(1000);}))
static void udelay(int loops) /*延迟微秒级 */
{
while (loops--)
io_delay(); /* Approximately 1 us */
}
static inline void io_delay(void)
{
const u16 DELAY_PORT = 0x80;
asm volatile("outb %%al,%0" : : "dN" (DELAY_PORT));
}
/*对 I/O 端口 0x80 写入任何的字节都将得到 1 us 的延时*/
系统调用宏
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
/*…:省略号代表可变的部分,用__VA_AEGS__ 代表省略的变长部分*/
#define SYSCALL_DEFINE_MAXARGS 6 /*系统调用最多可以带6个参数*/
SYSCALL_DEFINE 后面跟系统调用所带的参数个数n,第一个参数为系统调用的名字,然后接2*n个参数,每一对指明系统调用的参数类型及名字。
{
if (force_o_largefile())
flags |= O_LARGEFILE;
return do_sys_open(AT_FDCWD, filename, flags, mode);
}
展开之后是:
SYSCALL_DEFINEx(3, _open, __VA_ARGS__)
再次展开为:
__SYSCALL_DEFINEx(3, _open, __VA_ARGS__)
#define __SYSCALL_DEFINEx(x, name, ...) \
asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \
最后展开为:
asmlinkage long sys_open(__MAP(3,__SC_DECL,__VA_ARGS__))
#define __MAP0(m,...)
#define __MAP1(m,t,a) m(t,a)
#define __MAP2(m,t,a,...) m(t,a), __MAP1(m,__VA_ARGS__)
#define __MAP3(m,t,a,...) m(t,a), __MAP2(m,__VA_ARGS__)
#define __MAP4(m,t,a,...) m(t,a), __MAP3(m,__VA_ARGS__)
#define __MAP5(m,t,a,...) m(t,a), __MAP4(m,__VA_ARGS__)
#define __MAP6(m,t,a,...) m(t,a), __MAP5(m,__VA_ARGS__)
#define __MAP(n,...) __MAP##n(__VA_ARGS__)
#define __SC_DECL(t, a) t a
__MAP(3,__SC_DECL,__VA_ARGS__)
-->__MAP3(__SC_DECL,const char __user *, filename, int, flags, umode_t, mode)
-->__SC_DECL(const char __user *, filename), __MAP2(__SC_DECL,__VA_ARGS__)
-->const char __user * filename,__SC_DECL(int, flags),__MAP1(__SC_DECL,__VA_ARGS__)
-->const char __user * filename, int flags, __SC_DECL(umode_t, mode)
-->const char __user * filename, int flags, umode_t mode
最后调用asmlinkage long sys_open(const char __user *filename,int flags, umode_t mode);
内核开发者通过将系统调用的所有输入参数都先转化成long类型(64位),再强制转化到相应的类型来规避这个漏洞。
/*将参数转换成long类型*/
表示所定义的变量类型可以做强制类型转换
time_after32(a, b)宏和time_before32(b, a)宏
只比较两个32位的数:
* time_after32 - compare two 32-bit relative times
* @a: the time which may be after @b
* @b: the time which may be before @a
*
* time_after32(a, b) returns true if the time @a is after time @b.
* time_before32(b, a) returns true if the time @b is before time @a.
*
* Similar to time_after(), compare two 32-bit timestamps for relative
* times. This is useful for comparing 32-bit seconds values that can't
* be converted to 64-bit values (e.g. due to disk format or wire protocol
* issues) when it is known that the times are less than 68 years apart.
*/
#define time_after32(a, b) ((s32)((u32)(b) - (u32)(a)) < 0)
#define time_before32(b, a) time_after32(a, b)
barrier()宏
/* The "volatile" is due to gcc bugs */
#define barrier() __asm__ __volatile__("": : :"memory")
barrier();
a = b;
mfence:在mfence指令前的读写操作当必须在mfence指令后的读写操作前完成。
lfence:在lfence指令前的读操作当必须在lfence指令后的读操作前完成,不影响写操作
sfence:在sfence指令前的写操作当必须在sfence指令后的写操作前完成,不影响读操作
lock 前缀(或cpuid、xchg等指令)使得本CPU的Cache写入内存,该写入动作也会引起别的CPU invalidate其Cache。用来修饰当前指令操作的内存只能由当前CPU使用
#ifdef ASSEMBLY宏
define _AC(X,Y) X
define _AT(T,X) X
else
define __AC(X,Y) (X##Y)
define _AC(X,Y) __AC(X,Y)
define _AT(T,X) ((T)(X))
endif
define _UL(x) (_AC(x, UL))
define _ULL(x) (_AC(x, ULL))
force_o_largefile宏
(personality(current->personality) != PER_LINUX32)
PER_LINUX32 = 0x0008,
PER_MASK = 0x00ff,
/*,
* Return the base personality without flags.
*/
逻辑地址和物理地址互相转换
#define __va(x) ((void *)__phys_to_virt((unsigned long)(x)))
错误码相关的宏
static inline long __must_check IS_ERR(const void *ptr)
{
return IS_ERR_VALUE((unsigned long)ptr);
}
{
return (void *) error;
}
长整型转化为指针
static inline long __must_check PTR_ERR(const void *ptr)
{
return (long) ptr;
}
指针转化为长整型
额外有意思的宏
#define BSWAP_16(x) ((BSWAP_8(x) << 8) | BSWAP_8((x) >> 8))
#define BSWAP_32(x) ((BSWAP_16(x) << 16) | BSWAP_16((x) >> 16))
#define BSWAP_64(x) ((BSWAP_32(x) << 32) | BSWAP_32((x) >> 32))
(((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b)))
看雪ID:笔墨
https://bbs.pediy.com/user-589842.htm
推荐文章++++
进阶安全圈,不得不读的一本书