川畅互联咨询 网站建设,百度搜索关键词排名查询,临清建设网站,免费自己创建个人网站0x00 宏的基本知识// object-like
#define 宏名 替换列表 换行符
//function-like
#define 宏名 ([标识符列表]) 替换列表 换行符替换列表和标识符列表都是将字符串 token 化以后的列表。区别在于标识符列表使用,作为不同参数之间的分割符。每一个参数都是一个 token 化的列表。… 0x00 宏的基本知识// object-like
#define 宏名 替换列表 换行符
//function-like
#define 宏名 ([标识符列表]) 替换列表 换行符
替换列表和标识符列表都是将字符串 token 化以后的列表。区别在于标识符列表使用,作为不同参数之间的分割符。每一个参数都是一个 token 化的列表。在宏中空白符只起到分割 token 的作用空白符的多少对于预处理器是没有意义的。 宏的一些奇技淫巧https://gaomf.cn/2017/10/06/C_Macro/ 以下是整理的一些linux kernel中的常见宏由于不同体系架构或者不同模块的宏定义不同只挑选了其中容易看懂的宏作为记录实现的功能大体一样。 Linux内核中do{...}while(0)意义辅助定义复杂的宏避免引用的时候出错如果不用{}if后面的语句只有第一条进行了判断。同时避免宏展开后“;”造成编译不通过.避免使用goto对程序流进行统一的控制使用break跳出避免空宏引起的warning定义一个单独的函数块来实现复杂的操作0x01 常见宏整理__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宏#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
!!(e) 对 e 的结果进行两次求非。如果e为0则结果为0如果 e 不为 0则结果为1。所以上述表达式的结果有两种condition为真时sizeof(char[-1])产生错误编译不通过condition为假时sizeof(char[1])编译通过BUILD_BUG_ON_ZERO(e) 宏#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
检查表达式e是否为0为0编译通过且返回0如果不为0则编译不通过。struct { int : –!!(0); } - struct { int : 0; }
如果e为0则该结构体拥有一个int型的数据域并且规定它所占的位的个数为0。struct { int : –!!(1); } - struct { int : –1; }
如果e非0结构体的int型数据域的位域将变为一个负数产生语法的错误。 typeof获得x的变量类型根据传入参数类型的不同产生不同的行为实现“编译时多态”。实际typeof是在预编译时处理最后实际转化为数据类型被编译器处理。所以其中的表达式在运行时是不会被执行的比如typeof(fun())fun()函数是不会被执行的typeof只是在编译时分析得到了fun()的返回值而已。typeof还有一些局限性其中的变量是不能包含存储类说明符的如static、extern这类都是不行的。typecheck宏宏typecheck用于检查x是否为type类型如果不是会抛出warning: comparison of distinct pointer types lacks a casttypecheck_fn用于检查函数function是否为type类型不一致跑出warning: initialization from incompatible pointer type。/** Check at compile time that something is of a particular type.* Always evaluates to 1 so you may use it easily in comparisons.*/
#define typecheck(type,x) \
({ 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.)*/
#define typecheck_fn(type,function) \
({ typeof(type) __tmp function; \(void)__tmp; \
})
min宏通过type进行隐式转换安全通过编译否则会跑出warning#define min(x, y) __careful_cmp(x, y, )
#define __cmp(x, y, op) ((x) op (y) ? (x) : (y))
#define __safe_cmp(x, y) \(__typecheck(x, y) __no_side_effects(x, y))
#define __no_side_effects(x, y) \(__is_constexpr(x) __is_constexpr(y))#define __cmp_once(x, y, unique_x, unique_y, op) ({ \typeof(x) unique_x (x); \typeof(y) unique_y (y); \__cmp(unique_x, unique_y, op); })
/*重新赋值为了防止x这种重复1 */
#define __careful_cmp(x, y, op) \__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类型不同时
__UNIQUE_ID保证变量唯一。__is_constexpr宏判断x是否为整数常量表达式/** 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.Ueckermed.uni-goettingen.de*/
#define __is_constexpr(x) \(sizeof(int) sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
如果x是常量表达式则(void )((long)(x) 0l)是一个空指针常量就会使用第三个操作数即((int *)8)的类型。如果不是常量表达式则会使用第二个操作数void类型。所以会出现以下两种情况sizeof(int) sizeof(*((int *) (NULL))) // if x was an integer constant expression
sizeof(int) sizeof(*((void *)(....))) // otherwise
因为sizeof(void) 1所以如果x是整数常量表达式则宏的结果为1否则为0。https://stackoverflow.com/questions/49481217/linux-kernels-is-constexpr-macroint __builtin_types_compatible_p(type_a, type_b);
描述此函数为GNU扩展用来判断两个类型是否相同如果type_a与 type_b相同的话就会返回1否则的话返回0。int __builtin_choose_expr(exp, e1, e2);
max宏同min 宏。roundup宏返回一个能够整除y并且大于x最接近x的值向上取整可用于地址的内存对齐#define roundup(x, y) ( \
{ \const typeof(y) __y y; \(((x) (__y - 1)) / __y) * __y; \
} \
)
clamp 宏判断val是否在lo和hi的范围内如果小于lo返回lo如果大于hi则返回hi如果在lo和hi之间就返回val/*** 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 macros return type is preserved as char.** Return: an absolute value of x.*/
#define abs(x) __abs_choose_expr(x, long long, \__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); __x0?-__x:__x; }), \((void)0)))))))#define __abs_choose_expr(x, type, other) __builtin_choose_expr( \__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 宏利用typeof获取要交换变量的类型/** swap - swap value of a and b*/
#define swap(a, b) \do { typeof(a) __tmp (a); (a) (b); (b) __tmp; } while (0)
container_of宏根据一个结构体变量中的成员变量来获取整个结构体变量的指针。#define offsetof(TYPE, MEMBER) ((size_t) ((TYPE *)0)-MEMBER)
/*结构体地址为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 likely(x) __builtin_exp ect(!!(x), 1)
#define unlikely(x) __builtin_exp ect(!!(x), 0)
GCC的内建方法会判断 EXP C 是否成立成立则将if分支中的执行语句紧跟放在汇编跳转指令之后否则将else分支中的执行语句紧跟汇编跳转指令之后。这样cache在预取数据时就可以将分支后的执行语句放在cache中提高cache的命中率。 http://www.169it.com/article/17243108930910839727.htmlALIGN对齐宏对齐是采用上对齐的方式例如0x123以16对齐结果是0x130因为对齐常在分配内存时使用所以分配的要比需要的大。#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
#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)宏获取未对齐的数据主要是识别数据大小#define __get_unaligned_le(ptr) ((__force typeof(*(ptr)))({ \__builtin_choose_expr(sizeof(*(ptr)) 1, *(ptr), \__builtin_choose_expr(sizeof(*(ptr)) 2, get_unaligned_le16((ptr)), \__builtin_choose_expr(sizeof(*(ptr)) 4, get_unaligned_le32((ptr)), \__builtin_choose_expr(sizeof(*(ptr)) 8, get_unaligned_le64((ptr)), \__bad_unaligned_access_size())))); \}))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;
编译器默认会对结构体采用字节对齐的方式__packed关键字可以取消字节对齐采用1字节对齐。类似#pragma pack (1)
__put_unaligned_le宏写入未对齐的数据。#define __put_unaligned_le(val, ptr) ({ \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 宏访问目标地址一次先取得x的地址然后把这个地址转换成一个指向这个地址类型的指针然后再取得这个指针所指向的内容达到了访问一次的目的。volatile表示不进行优化强制访问一次。在一些并发的场景中对变量进行优化有可能导致错误需要时刻得到变量的最新值所以用volatile强制访问一次进行更新。使用 ACCESS_ONCE() 的两个条件是在无锁的情况下访问全局变量对该变量的访问可能被编译器优化成合并成一次或者拆分成多次#define ACCESS_ONCE(x) (*(volatile typeof(x) *)(x))
https://blog.csdn.net/ganggexiongqi/article/details/24603363ACCESS_OK宏CVE-2017-5123waitid系统调用,检查指针是不是属于用户空间的x86架构下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.*/
#define access_ok(addr, size) \
({ \WARN_ON_IN_IRQ(); \likely(!__range_not_ok(addr, size, user_addr_max())); \
})
/*__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 wont overflow the limit (but* it might overflow the addr, so its* 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宏忙等待函数在延迟过程中无法运行其他任务会占用CPU时间延迟时间是准确的。msleep是休眠函数它不涉及忙等待用msleep200的时候实际上延迟的时间大部分时候是要多于200ms是个不定的时间值。#define MAX_UDELAY_MS 5
#define mdelay(n) (\ /*延迟毫秒级*/(__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 的延时*/
系统调用宏linux 内核中最常见的宏使用之一系统调用#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#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个参数*/
以open系统调用为例SYSCALL_DEFINE 后面跟系统调用所带的参数个数n第一个参数为系统调用的名字然后接2*n个参数每一对指明系统调用的参数类型及名字。SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{if (force_o_largefile())flags | O_LARGEFILE;return do_sys_open(AT_FDCWD, filename, flags, mode);
}
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, 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);
为什么要将系统调用定义成宏CVE-2009-0029CVE-2010-3301Linux 2.6.28及以前版本的内核中将系统调用中32位参数传入64位的寄存器时无法作符号扩展可能导致系统崩溃或提权漏洞。内核开发者通过将系统调用的所有输入参数都先转化成long类型64位再强制转化到相应的类型来规避这个漏洞。asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \
{ \long ret __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\__MAP(x,__SC_TEST,__VA_ARGS__); \__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \return ret; \
} \#define __TYPE_AS(t, v) __same_type((__force t)0, v) /*判断t和v是否是同一个类型*/
#define __TYPE_IS_L(t) (__TYPE_AS(t, 0L)) /*判断t是否是long 类型,是返回1*/
#define __TYPE_IS_UL(t) (__TYPE_AS(t, 0UL)) /*判断t是否是unsigned long 类型,是返回1*/
#define __TYPE_IS_LL(t) (__TYPE_AS(t, 0LL) || __TYPE_AS(t, 0ULL))/*是long类型就返回1*/
#define __SC_LONG(t, a) __typeof(__builtin_choose_expr(__TYPE_IS_LL(t), 0LL, 0L)) a
/*将参数转换成long类型*/
#define __SC_CAST(t, a) (__force t) a /*转成成原来的类型*/
# define __force __attribute__((force))
表示所定义的变量类型可以做强制类型转换
time_after32(a, b)宏和time_before32(b, a)宏time_after32(a, b)宏返回true时说明time a比b大在后面。 time_before32(b, a)宏返回true时说明time 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 cant* 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()宏内存屏障该语句不产生任何代码但是执行后刷新寄存器对变量的分配。/* Optimization barrier */
/* The volatile is due to gcc bugs */
#define barrier() __asm__ __volatile__(: : :memory)
执行该语句后cpu中的寄存器和cache中已缓存的数据将作废重新读取内存中的数据。这就阻止了cpu将寄存器和cache中的数据用于去优化指令而避免去访问内存。例如int a 5, b 6;
barrier();
a b;
第三行中GCC不会用存放b的寄存器给a赋值而是invalidate b 的cache line重新读取内存中的b值给a赋值。 另外的内存屏障宏定义mfence在mfence指令前的读写操作当必须在mfence指令后的读写操作前完成。lfence在lfence指令前的读操作当必须在lfence指令后的读操作前完成不影响写操作sfence在sfence指令前的写操作当必须在sfence指令后的写操作前完成不影响读操作lock 前缀或cpuid、xchg等指令使得本CPU的Cache写入内存该写入动作也会引起别的CPU invalidate其Cache。用来修饰当前指令操作的内存只能由当前CPU使用内存对于缓存更新策略要区分Write-Through和Write-Back两种策略。前者更新内容直接写内存并不同时更新Cache但要置Cache失效后者先更新Cache随后异步更新内存。通常X86 CPU更新内存都使用Write-Back策略。#ifdef ASSEMBLY宏一些常量宏同时在汇编和C中使用然而我们不能像注释C的常量宏那样加一个“UL”或其他后缀。所以我们需要使用以下的宏解决这个问题。 例如调用#define DEMO_MACRO _AT(1 UL)在C中会被解释为 #define DEMO_MACRO 1UL 而在汇编中什么都不做就是#define DEMO_MACRO 1#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宏判断是否支持大文件。#define force_o_largefile() \(personality(current-personality) ! PER_LINUX32)PER_LINUX32 0x0008,
PER_MASK 0x00ff,/** Return the base personality without flags.*/
#define personality(pers) (pers PER_MASK)
逻辑地址和物理地址互相转换#define __pa(x) __virt_to_phys((unsigned long)(x))
#define __va(x) ((void *)__phys_to_virt((unsigned long)(x)))
错误码相关的宏linux 内核的一些错误码以它们的负数来作为函数返回值简单地使用大于等于-4095的虚拟地址来分别表示相应的错误码。在32位系统上-4095转换成unsigned long类型的值为0xFFFFF001也就是说地址区间[0xFFFFF001, 0xFFFFFFFF]被分别用来表示错误码从-4095到-1。 判断一个函数返回的指针到底是有效地址还是错误码#define MAX_ERRNO 4095#define IS_ERR_VALUE(x) unlikely((x) (unsigned long)-MAX_ERRNO)static inline long __must_check IS_ERR(const void *ptr)
{return IS_ERR_VALUE((unsigned long)ptr);
}
错误码与相应地址的互换static inline void * __must_check ERR_PTR(long error)
{return (void *) error;
}
长整型转化为指针static inline long __must_check PTR_ERR(const void *ptr)
{return (long) ptr;
}
指针转化为长整型
额外有意思的宏递归宏颠倒字节#define BSWAP_8(x) ((x) 0xff)
#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))
交换宏不需要额外定义变量#define swap(a, b) \
(((a) ^ (b)), ((b) ^ (a)), ((a) ^ (b)))
- End -扫码或长按关注回复「 篮球的大肚子」进入技术群聊