如何使用 LD_PRELOAD 包装 ioctl(int d, unsigned long request, ...)?

How to wrap ioctl(int d, unsigned long request, ...) using LD_PRELOAD?

这是我使用 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() 是一个可变参数函数,但实际上并非如此。

参见 this quote from the book Linux Device Drivers

The prototype stands out in the list of Unix system calls because of the dots, which usually mark the function as having a variable number of arguments. In a real system, however, a system call can't actually have a variable number of arguments. System calls must have a well-defined prototype, because user programs can access them only through hardware "gates." Therefore, the dots in the prototype represent not a variable number of arguments but a single optional argument, traditionally identified as char *argp. The dots are simply there to prevent type checking during compilation.

所以你可以这样写你的假设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 中的签名相矛盾这一事实是无关紧要的:链接器不检查类型,你的包装库的假 ioctl 将使用与没有 LD_PRELOAD

的真实库完全相同的参数调用