【问题标题】:What's different between compiling in 32bit mode and 64bit mode on 64bit os about execution of ioctl function?关于 ioctl 函数的执行,在 64 位操作系统上以 32 位模式编译和 64 位模式编译有什么不同?
【发布时间】:2012-02-13 09:37:21
【问题描述】:

我有一个 64 位的 Enterprice 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 bustype;
     __s16 vendor;
     __s16 product;
}

【问题讨论】:

    标签: linux gcc g++ 32bit-64bit ioctl


    【解决方案1】:

    Linux ioctl 的定义和兼容性层是一个有趣的话题,我刚刚猛烈抨击。

    通常ioctl 定义使用一系列宏 _IOW/_IOR 等,这些宏将您的参数类型名称作为参考,以及一个幻数和序数值,这些数值和序数值被用来为您提供您的 ioctl 参数值(例如 @ 987654325@)。 type-name 用于将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 位程序将生成将 ioctls 传递给 ioctl_compat 回调的代码,即使它可以生成相同的 @ 987654338@ 值与 64 位一样(例如,在您的情况下)。所以驱动程序编写者需要确保ioctl_compat 处理both 特殊(不同)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

    【解决方案2】:

    问题可能是您的程序传递给ioctl 函数的devinfo 结构不匹配。

    我猜你是在 64 位系统上工作的。因此,您的内核以 64 位运行,而您正在与之通信的内核模块(使用 ioctl)也是 64 位。

    当你编译你的64位用户程序时,内核模块和用户程序中devinfo的定义是一样的。

    当您以 32 位编译用户程序时,内核模块中的 devinfo 定义与用户程序中的定义不同。实际上,在 32 位中,某些类型的大小会发生变化:主要是 long 和指针。因此,您的程序创建了一个特定大小的结构,内核模块以不同的方式解释它接收到的数据。内核模块可能不理解您赋予它的值,因为它不会在您放置它的位置查找它。

    解决办法是注意devinfo结构体的定义,使其在编译32位和64位时具有相同的二进制表示。

    【讨论】:

    • 我无法理解您的回答。 __u32 在 32 位和 64 位中都是 4 个字节。并且 __s16 在 32 位和 64 位中都是 2 个字节。并且 sizeof(hidraw_devinfo) 为 8 字节长(考虑 OS 打包)。
    • 另一种解释是内核模块不希望 32 位用户程序调用它。 (必须有一些特殊的东西才能接受它们。)但如果是这样的话,我对-EINVAL 错误感到有些惊讶。
    猜你喜欢
    • 1970-01-01
    • 2018-09-20
    • 2011-11-11
    • 1970-01-01
    • 1970-01-01
    • 2013-03-20
    • 1970-01-01
    • 2015-04-04
    • 1970-01-01
    相关资源
    最近更新 更多