6

Here's the template I use for wrapping a function using 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);
}

I realized though that ioctl seems to have the following signature:

   int ioctl(int d, unsigned long request, ...);

How could I wrap it in a similar way, given the ... that's in the signature?

d33tah
  • 10,999
  • 13
  • 68
  • 158

2 Answers2

9

Although John Bollinger is correct in that according to its prototype in ioctl.h, ioctl() is a variadic function, in practice, it is not.

See e.g 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.

So you can write your susbtitute ioctl() as follows:

int ioctl(int d, unsigned long request, char *argp)
{
  /* follow the same recipe as for your example gettimeofday() */
  return ioctl_real(d, request, argp);
}

If you just want to build a wrapper library for use with LD_PRELOAD, the fact that your ioctl signature contradicts the one in sys/ioctl.h is irrelevant: linkers don't check types, and your wrapper library's fake ioctl will be called with exactly the same arguments as the real one would without LD_PRELOAD

Hans Lub
  • 5,513
  • 1
  • 23
  • 43
  • 1
    I think it is a bit brave to tell that `char*p` would work for all ABIs... On ARM registers are used up to their availability, for example... – Valeri Atamaniouk Feb 13 '15 at 23:58
  • 1
    @Valeri Atamaniouk: the use of `char *` is just tradition. What matters is that it always is exactly one machine word that will be interpreted as an `int`, or say a `struct termios *` by the kernel code that handles the `ioctl`, dependent on the value of `request` (see e.g.`man ioctl_list`) This word can be passed in a register, or on the stack, independent of the type of `argp` – Hans Lub Feb 14 '15 at 00:28
  • 1
    Yes, you are right. One extra long type argument if present. – Valeri Atamaniouk Feb 14 '15 at 00:39
1

There is no portable way for one variadic function to forward its variadic arguments to another variadic function. In part this is because the number and size of the actual arguments must be known at compile time for the compiler to construct the variadic call, but they are not statically known (not even in parametric fashion) inside the called function.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157