如何编写宏实现根据参数调用原始系统调用或libc封装函数?
实现SYSCALL_VARIANT宏的可行方案
完全可行,核心是靠C预处理器的**token粘贴(##)**功能,把__NR_前缀和系统调用名拼接成合法的系统调用号符号。下面直接上可运行的实现方案和细节说明:
核心实现(编译时分支选择,性能最优)
#include <sys/syscall.h> // 必须包含,__NR_* 系统调用号定义在这里 #include <unistd.h> // 包含syscall()函数声明 // 辅助宏:处理原始系统调用的调用逻辑 #define _SYSCALL_VARIANT_RAW(scall_name, ...) \ syscall(__NR_##scall_name, __VA_ARGS__) // 辅助宏:处理libc封装函数的调用逻辑 #define _SYSCALL_VARIANT_LIBC(scall_name, ...) \ scall_name(__VA_ARGS__) // 主宏:根据variant参数选择调用分支 #define SYSCALL_VARIANT(variant, scall_name, ...) \ _SYSCALL_VARIANT_##variant(scall_name, __VA_ARGS__)
使用示例
#include <stdlib.h> int main() { void *addr = malloc(4096); // 调用原始系统调用__NR_pkey_mprotect long raw_result = SYSCALL_VARIANT(RAW, pkey_mprotect, addr, 4096, PROT_READ, 0); // 调用libc封装的pkey_mprotect函数 int libc_result = SYSCALL_VARIANT(LIBC, pkey_mprotect, addr, 4096, PROT_READ, 0); free(addr); return 0; }
关键细节说明
##是预处理器的token粘贴操作符,能把左右两个标识符拼接成一个新的合法符号,比如__NR_##pkey_mprotect会被预处理器替换成__NR_pkey_mprotect- 主宏里的
variant参数必须是RAW或LIBC,要和辅助宏的后缀完全匹配,否则会触发编译错误(这是好事,能提前发现参数错误) - 可变参数
__VA_ARGS__用来兼容不同参数数量的系统调用,不管是write(3个参数)还是pkey_mprotect(4个参数)都能适配 - 一定要包含
<sys/syscall.h>,否则__NR_*符号会找不到
可选:运行时判断的实现
如果需要在运行时动态选择调用方式,也可以用条件分支实现(性能略低于编译时分支,但灵活性更高):
#include <sys/syscall.h> #include <unistd.h> #define SYSCALL_VARIANT(variant, scall_name, ...) \ ((variant == 0) ? \ syscall(__NR_##scall_name, __VA_ARGS__) : \ (long)scall_name(__VA_ARGS__))
这里用三元运算符做运行时判断,注意要统一返回值类型(比如把libc函数的返回值转成long,和syscall()的返回值类型对齐)
内容的提问来源于stack exchange,提问作者pevik
相关产品推荐
相关产品推荐

