Search code examples
linuxgccg++32bit-64bitioctl

What's different between compiling in 32bit mode and 64bit mode on 64bit os about execution of ioctl function?


I have a 64 bit Enterprice SuSE 11 I have an application which open a HIDRAW device and operate an ioctl function on it to get raw info from this device like below:

struct hidraw_devinfo devinfo;
int fd = open("/dev/hidraw0", 0);
int ret = ioctl(fd, HIDIOCGRAWINFO, &devinfo);
...

If I compile this program in 64 bit mode there is no error and no problem and when I execute the application the ioctl function works properly.

g++ main.cpp

If I complie this program in 32 bit mode there is also no error and no problem. but when I execute the application the ioctl function return EINVAL error(errno = 22 , Invalid Argument)

g++ -m32 main.cpp

what's the problem?

Note:

struct hidraw_devinfo 
{
     __u32 bustype;
     __s16 vendor;
     __s16 product;
}

Solution

  • Linux ioctl definitions and compatibility layers are a fascinating topic I've just bashed my head against.

    Typically ioctl definitions use a family of macros _IOW/_IOR et al that take your argument type-name as a reference, along with a magic number and ordinal value that are munged to give you your ioctl argument value (eg HIDIOCGRAWINFO). The type-name is used to encode sizeof(arg_type) into the definition. This means that the type used in user space determines the value generated by the ioctl macro - ie HIDIOCGRAWINFO may vary based on include conditions.

    Here is the first point where 32- and 64-bit differ, the sizeof may differ, depending on packing, use of vague data-sizes (eg long), but especially (and unavoidably) if you use a pointer argument. So in this case a 64-bit kernel module what wants to support 32-bit clients needs do define a compatibility argument-type to match the layout of the 32-bit equivalent of the argument type and thus a 32-bit compatible ioctl. These 32-bit equivalent definitions make use of a kernel facility/layer called compat.

    In your case the sizeof() is the same so that is not the path you are taking - but its important to understand the whole of what could be happening.

    In addition a kernel configuration may define CONFIG_COMPAT which changes the sys-call wrappers (especially the code surrounding the user/kernel interface wrt ioctl) to ease the burden of supporting 32- and 64-bit. Part of this includes a compatibility ioctl callback called ioctl_compat.

    What I've seen is with CONFIG_COMPAT defined that 32-bit programs will generate code that delivers ioctls to the ioctl_compat callback, even if it could generate the same ioctl value as 64-bit does (eg in your case). So the driver writer needs to make sure that ioctl_compat handles both the special (different) 32-bit compatible ioctl TYPEs and the normal "64-bit - or unchanged 32-bit" types.

    So a kernel-module designed and tested on 32-bit only and 64-bit only systems (without CONFIG_COMPAT) may work for 32- and 64-bit programs, but not for one which supports both.

    So looking in HID I see this was added in 2.6.38:

    http://lxr.linux.no/#linux+v2.6.38/drivers/hid/hidraw.c#L347