问题描述
我有一个 64 位的 Enterprise SuSE 11我有一个应用程序,它打开 HIDRAW 设备并在其上运行 ioctl 函数以从该设备获取原始信息,如下所示:
struct hidraw_devinfo devinfo;int fd = open("/dev/hidraw0", 0);int ret = ioctl(fd, HIDIOCGRAWINFO, &devinfo);...
如果我在 64 位模式下编译这个程序,没有错误也没有问题,当我执行应用程序时,ioctl 函数正常工作.
g++ main.cpp
如果我在 32 位模式下编译这个程序,也没有错误也没有问题.但是当我执行应用程序时,ioctl 函数返回 EINVAL 错误(errno = 22 , Invalid Argument)
g++ -m32 main.cpp
有什么问题吗?
注意:
struct hidraw_devinfo{__u32 总线类型;__s16 供应商;__s16 产品;}
Linux ioctl
定义和兼容性层是我刚刚猛烈抨击的一个引人入胜的话题.
通常 ioctl
定义使用一系列宏 _IOW/_IOR 等,这些宏将您的参数类型名称作为参考,以及一个幻数和序数值,这些宏被修改为您的 ioctl参数值(例如 HIDIOCGRAWINFO
).类型名称用于将 sizeof(arg_type)
编码到定义中.这意味着 user 空间中使用的类型决定了 ioctl
宏生成的 value - 即 HIDIOCGRAWINFO 可能因包含条件而异.>
这是 32 位和 64 位不同的第一点,sizeof
可能会有所不同,具体取决于包装、模糊数据大小(例如 long)的使用,但尤其是(并且不可避免)如果您使用指针参数.因此,在这种情况下,想要支持 32 位客户端的 64 位内核模块需要定义兼容性参数类型以匹配 32 位等效参数类型的布局,从而匹配 32 位兼容 ioctl.这些 32 位等效定义使用名为 compat
的内核工具/层.
在您的情况下,sizeof()
是相同的,因此这不是您要采取的路径 - 但了解可能发生的全部情况很重要.
此外,内核配置可以定义 CONFIG_COMPAT
来更改系统调用包装器(尤其是围绕用户/内核接口 wrt ioctl
的代码)以减轻负担支持 32 位和 64 位.其中一部分包括一个名为 ioctl_compat
的兼容性 ioctl
回调.
我所看到的是 CONFIG_COMPAT
定义了 32 位程序将生成将 ioctl
传递给 ioctl_compat
回调的代码,即使它可以生成与 64 位相同的 ioctl
值(例如在您的情况下).因此,驱动程序编写者需要确保 ioctl_compat
处理两者特殊的(不同的)32 位兼容 ioctl
类型和正常的64-位 - 或未更改的 32 位"类型.
因此,在仅 32 位和仅 64 位系统(没有 CONFIG_COMPAT)上设计和测试的内核模块可能适用于 32 位和 64 位程序,但不适用于同时支持两者的程序.
所以查看 HID 我看到这是在 2.6.38 中添加的:
http://lxr.linux.no/#linux+v2.6.38/drivers/hid/hidraw.c#L347
I have a 64 bit Enterprice SuSE 11I 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;
}
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 ioctl
s 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
这篇关于在 64 位操作系统上以 32 位模式和 64 位模式编译关于 ioctl 函数的执行有何不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!