__udivdi3未定义 - 如何找到使用它的代码?

18

在32位Linux内核上编译内核模块导致

"__udivdi3" [mymodule.ko] undefined!
"__umoddi3" [mymodule.ko] undefined!

在64位系统上一切都很好。据我所知,原因是32位Linux内核不支持64位整数除法和取模。

我该如何找到执行64位操作的代码?它们很难手动查找,因为我不能轻易地检查“/”是否为32位宽度或64位宽度。如果“正常”的函数未定义,我可以使用grep来搜索它们,但这在此处不可能。有没有其他好的方法来搜索引用?某种“机器码grep”?

该模块由几千行代码组成。我真的无法手动检查每一行。

3个回答

25

首先,您可以使用do_div宏进行64位除法运算。(请注意原型为uint32_t do_div(uint64_t dividend, uint32_t divisor),而"dividend"可能会被多次评估。)

{
    unsigned long long int x = 6;
    unsigned long int y = 4;
    unsigned long int rem;

    rem = do_div(x, y);
    /* x now contains the result of x/y */
}

此外,你应该能够在你的代码中找到long long int(或uint64_t)类型的使用,或者你可以用-g标志构建你的模块,并使用objdump -S来获取源代码注释反汇编。

注意:这适用于2.6内核,我没有检查更低版本的使用情况。


10
补充一点信息,do_div() 函数定义在 <asm/div64.h> 头文件中。 - Willi Ballenthin
1
似乎相当荒谬,Linux 坚持让你使用一个丑陋的函数来进行除法,而不是直接实现 __udivdi3 等。它们很容易实现,如果你用汇编语言实现,它们将比 libgcc 的版本更高效(可能快 2-3 倍)。 - R.. GitHub STOP HELPING ICE
@WilliBallenthin即使使用<asm/div64.h>,仍然面临此问题。所以有人建议我该怎么做才能解决这个问题? - Amit Singh Tomar
1
@Jordan:-g 标志是为了 GCC(因此对象包括调试信息),应该放在 CFLAGS_MODULE 中,objdump 将在生成的目标文件上运行。 - Hasturkun
1
@Jordan:目前我无法确认,但如果我没记错的话,objdump -Sr 命令会给你提供一个带有重定向的注释汇编代码,你应该能够在那里找到对 __udivdi3 的调用。 - Hasturkun
显示剩余3条评论

5
实际上,在32位Linux内核中支持64位整数除法和取模运算;但是,您必须使用正确的宏来执行此操作(哪些宏取决于您的内核版本,因为最近创建了更好的宏)。这些宏将以最有效的方式为您编译的任何架构执行正确的操作。
查找它们被使用的最简单方法是(如@shodanex的答案中提到的)生成汇编代码;我IRC,这样做的方法类似于make directory/module.s(连同您已经必须传递给make的任何参数)。下一个最简单的方法是反汇编.o文件(例如objdump --disassemble)。这两种方法都将向您提供生成调用的函数(如果您知道如何读取汇编,则可以大致了解在函数内部进行除法的位置)。

我尝试过了,但是objdump使用的“%eiz”寄存器在gnu as中找不到。请帮忙。 - user135142
@user135142:%eiz是一个伪索引寄存器,可以在这里看到相关信息。你可以通过传递-mindex-reg参数让as识别它。 - Hasturkun

4

编译阶段之后,您应该能够获得一些文档化的汇编代码,并看到这些函数的调用位置。尝试通过混淆 CFLAGS 并添加 -S 标志来测试。 编译应该在汇编阶段停止。然后您可以在汇编文件中使用 grep 命令查找有问题的函数调用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接