这里是我使用LD_PRELOAD封装函数的模板:
int gettimeofday(struct timeval *tv, struct timezone *tz) { static int (*gettimeofday_real)(struct timeval *tv, struct timezone *tz)=NULL; if (!gettimeofday_real) gettimeofday_real=dlsym(RTLD_NEXT,"gettimeofday"); return gettimeofday_real(tv, tz); }
我意识到,虽然ioctl似乎有以下签名:
int ioctl(int d, unsigned long request, ...);
我怎么能用类似的方式把它包裹起来?
尽管John Bollinger根据其在ioctl.h
原型是正确的,但ioctl()
是一个可变参数函数,实际上并不是这样。
请参阅本书“ Linux设备驱动程序”中的 引用
原型在Unix系统调用列表中突出,因为它们通常将函数标记为具有可变数目的参数。 然而,在一个真实的系统中,一个系统调用实际上不能具有可变数目的参数。 系统调用必须有一个定义良好的原型,因为用户程序只能通过硬件“门”访问它们。 因此,原型中的点不是可变数量的参数,而是单个可选参数,传统上被标识为char * argp。 这些点只是为了防止编译期间的类型检查。
所以你可以写下你的suso ioctl()
,如下所示:
int ioctl(int d, unsigned long request, char *argp) { /* follow the same recipe as for your example gettimeofday() */ return ioctl_real(d, request, argp); }
如果你只是想建立一个与LD_PRELOAD
一起使用的包装库,那么你的ioctl
签名与sys/ioctl.h
签名相矛盾的sys/ioctl.h
是不相干的:连接器不检查类型,你的包装库的假ioctl
将被调用与没有LD_PRELOAD
的实际相同的参数
对于一个可变参数函数来说,没有可移植的方法来将其可变参数转发给另一个可变参数函数。 部分原因是编译时必须知道实际参数的数量和大小,以便编译器构建可变参数调用,但在被调用函数内部它们不是静态的(即使参数化方式)。