undefined reference to `__udivdi3'

【问题分析】

ARM 是精简指令集, 对求余和除法操作基本上不支持,所以应该尽量避免上述操作。 libgcc库包含这些操作,这种错误是没有包含GCC的支持库libgcc.a,缺少库文件。

【解决办法】

1.在连接时指定连接GCC库,-lgcc

-lgcc 代表链接器将连接GCC的支持库libgcc.a。这里需要注意的是:-lm 代表链接器将连接GCC的标准数学库库libm.a,-lc 代表链接器将连接GCC的标准C库libc.a,如果这三者都需要,则连接排列顺序为-lm -lc -lgcc。

2.自己实现响应的库函数

如果不想基于库函数,可以自己参考linux内核源码linux/arch/arm/lib/lib1funcs.S实现这两个库函数, lib1funcs.S实现除法、求模操作,具体的源码如下:

* linux/arch/arm/lib/lib1funcs.S: Optimized ARM division routines * Author: Nicolas Pitre <nico@cam.org> * - contributed to gcc-3.4 on Sep 30, 2003 * - adapted for the Linux kernel on Oct 2, 2003 This file is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. In addition to the permissions in the GNU General Public License, the Free Software Foundation gives you unlimited permission to link the compiled version of this file into combinations with other programs, and to distribute those combinations without any restriction coming from the use of this file. (The General Public License restrictions do apply in other respects; for example, they cover modification of the file, and distribution when not linked into a combine executable.) This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <linux/linkage.h> #include <asm/assembler.h> #define ALIGN .align 4,0x90 #define __LINUX_ARM_ARCH__ 1 #define ENTRY(name) \ .globl name; \ ALIGN; \ name: .macro ARM_DIV_BODY dividend, divisor, result, curbit #if __LINUX_ARM_ARCH__ >= 5 clz \curbit, \divisor clz \result, \dividend sub \result, \curbit, \result mov \curbit, #1 mov \divisor, \divisor, lsl \result mov \curbit, \curbit, lsl \result mov \result, #0 #else @ Initially shift the divisor left 3 bits if possible, @ set curbit accordingly. This allows for curbit to be located @ at the left end of each 4 bit nibbles in the division loop @ to save one loop in most cases. tst \divisor, #0xe0000000 moveq \divisor, \divisor, lsl #3 moveq \curbit, #8 movne \curbit, #1 @ Unless the divisor is very big, shift it up in multiples of @ four bits, since this is the amount of unwinding in the main @ division loop. Continue shifting until the divisor is @ larger than the dividend. 1: cmp \divisor, #0x10000000 cmplo \divisor, \dividend movlo \divisor, \divisor, lsl #4 movlo \curbit, \curbit, lsl #4 blo 1b @ For very big divisors, we must shift it a bit at a time, or @ we will be in danger of overflowing. 1: cmp \divisor, #0x80000000 cmplo \divisor, \dividend movlo \divisor, \divisor, lsl #1 movlo \curbit, \curbit, lsl #1 blo 1b mov \result, #0 #endif @ Division loop 1: cmp \dividend, \divisor subhs \dividend, \dividend, \divisor orrhs \result, \result, \curbit cmp \dividend, \divisor, lsr #1 subhs \dividend, \dividend, \divisor, lsr #1 orrhs \result, \result, \curbit, lsr #1 cmp \dividend, \divisor, lsr #2 subhs \dividend, \dividend, \divisor, lsr #2 orrhs \result, \result, \curbit, lsr #2 cmp \dividend, \divisor, lsr #3 subhs \dividend, \dividend, \divisor, lsr #3 orrhs \result, \result, \curbit, lsr #3 cmp \dividend, #0 @ Early termination? movnes \curbit, \curbit, lsr #4 @ No, any more bits to do? movne \divisor, \divisor, lsr #4 bne 1b .endm .macro ARM_DIV2_ORDER divisor, order #if __LINUX_ARM_ARCH__ >= 5 clz \order, \divisor rsb \order, \order, #31 #else cmp \divisor, #(1 << 16) movhs \divisor, \divisor, lsr #16 movhs \order, #16 movlo \order, #0 cmp \divisor, #(1 << 8) movhs \divisor, \divisor, lsr #8 addhs \order, \order, #8 cmp \divisor, #(1 << 4) movhs \divisor, \divisor, lsr #4 addhs \order, \order, #4 cmp \divisor, #(1 << 2) addhi \order, \order, #3 addls \order, \order, \divisor, lsr #1 #endif .endm .macro ARM_MOD_BODY dividend, divisor, order, spare #if __LINUX_ARM_ARCH__ >= 5 clz \order, \divisor clz \spare, \dividend sub \order, \order, \spare mov \divisor, \divisor, lsl \order #else mov \order, #0 @ Unless the divisor is very big, shift it up in multiples of @ four bits, since this is the amount of unwinding in the main @ division loop. Continue shifting until the divisor is @ larger than the dividend. 1: cmp \divisor, #0x10000000 cmplo \divisor, \dividend movlo \divisor, \divisor, lsl #4 addlo \order, \order, #4 blo 1b @ For very big divisors, we must shift it a bit at a time, or @ we will be in danger of overflowing. 1: cmp \divisor, #0x80000000 cmplo \divisor, \dividend movlo \divisor, \divisor, lsl #1 addlo \order, \order, #1 blo 1b #endif @ Perform all needed substractions to keep only the reminder. @ Do comparisons in batch of 4 first. subs \order, \order, #3 @ yes, 3 is intended here blt 2f 1: cmp \dividend, \divisor subhs \dividend, \dividend, \divisor cmp \dividend, \divisor, lsr #1 subhs \dividend, \dividend, \divisor, lsr #1 cmp \dividend, \divisor, lsr #2 subhs \dividend, \dividend, \divisor, lsr #2 cmp \dividend, \divisor, lsr #3 subhs \dividend, \dividend, \divisor, lsr #3 cmp \dividend, #1 mov \divisor, \divisor, lsr #4 subges \order, \order, #4 bge 1b tst \order, #3 teqne \dividend, #0 beq 5f @ Either 1, 2 or 3 comparison/substractions are left. 2: cmn \order, #2 blt 4f beq 3f cmp \dividend, \divisor subhs \dividend, \dividend, \divisor mov \divisor, \divisor, lsr #1 3: cmp \dividend, \divisor subhs \dividend, \dividend, \divisor mov \divisor, \divisor, lsr #1 4: cmp \dividend, \divisor subhs \dividend, \dividend, \divisor .endm ENTRY(__udivsi3) subs r2, r1, #1 moveq pc, lr bcc Ldiv0 cmp r0, r1 bls 11f tst r1, r2 beq 12f ARM_DIV_BODY r0, r1, r2, r3 mov r0, r2 mov pc, lr 11: moveq r0, #1 movne r0, #0 mov pc, lr 12: ARM_DIV2_ORDER r1, r2 mov r0, r0, lsr r2 mov pc, lr ENTRY(__umodsi3) subs r2, r1, #1 @ compare divisor with 1 bcc Ldiv0 cmpne r0, r1 @ compare dividend with divisor moveq r0, #0 tsthi r1, r2 @ see if divisor is power of 2 andeq r0, r0, r2 movls pc, lr ARM_MOD_BODY r0, r1, r2, r3 mov pc, lr ENTRY(__divsi3) cmp r1, #0 eor ip, r0, r1 @ save the sign of the result. beq Ldiv0 rsbmi r1, r1, #0 @ loops below use unsigned. subs r2, r1, #1 @ division by 1 or -1 ? beq 10f movs r3, r0 rsbmi r3, r0, #0 @ positive dividend value cmp r3, r1 bls 11f tst r1, r2 @ divisor is power of 2 ? beq 12f ARM_DIV_BODY r3, r1, r0, r2 cmp ip, #0 rsbmi r0, r0, #0 mov pc, lr 10: teq ip, r0 @ same sign ? rsbmi r0, r0, #0 mov pc, lr 11: movlo r0, #0 moveq r0, ip, asr #31 orreq r0, r0, #1 mov pc, lr 12: ARM_DIV2_ORDER r1, r2 cmp ip, #0 mov r0, r3, lsr r2 rsbmi r0, r0, #0 mov pc, lr ENTRY(__modsi3) cmp r1, #0 beq Ldiv0 rsbmi r1, r1, #0 @ loops below use unsigned. movs ip, r0 @ preserve sign of dividend rsbmi r0, r0, #0 @ if negative make positive subs r2, r1, #1 @ compare divisor with 1 cmpne r0, r1 @ compare dividend with divisor moveq r0, #0 tsthi r1, r2 @ see if divisor is power of 2 andeq r0, r0, r2 bls 10f ARM_MOD_BODY r0, r1, r2, r3 10: cmp ip, #0 rsbmi r0, r0, #0 mov pc, lr Ldiv0: str lr, [sp, #-4]! /* bl __div0 */ mov r0, #0 @ About as wrong as it could be. ldr pc, [sp], #4

类似问题-- ARM中的64位除法问题】

32位嵌入式系统中(目前多数系统都是,比如ARM的片子),对于普通的a除以b(b为32位):
(1)当a为32位,Linux 内核中,常用uint32_t 类型,可以直接写为 a/b
(2)但是,对于a是64位,uint64_t的时候,就要用到专门的除操作相关的函数,linux内核里面一般为do_div(n, base),注意,此处do_div得到的结果是余数,而真正的a/b的结果,是用a来保存的。do_div(n,base)的具体定义,和当前体系结构有关,对于arm平台,在arch/arm/include/asm/div64.h和linux/arch/arm/lib/div64.S中,其实现很复杂,感兴趣的自己去代码里看。因此,如果你当前写代码,a/b,如果a是uint64_t类型,那么一定要利用do_div(a,b),而得到结果a,而不能简单的用a/b,否则编译可以正常编译,但是最后链接最后出错,会提示上面的那个错误:undefined reference to "__udivdi3"。

知道原因,就好办了。办法就是, 你代码里面找到对应的用到除法的地方,即类似于a/b的地方,其中被除数a为64位,Linux中一般用uint64_t,将a/b用do_div(a,b)得到的a去代替(注意,不是直接用do_div() 因为do_div(a,b)得到的是余数。)即可。

Linux内核早已经帮我们实现了对应的64位的unsingned和signed两个函数:

static inline u64 div_u64(u64 dividend, u32 divisor);
static inline s64 div_s64(s64 dividend, s32 divisor);
我们可以直接拿过来用了,注意用此函数时,要包含对应头文件:
#include <linux/math64.h>
如果需要在进行64位除数的时候,同时得到余数remainder,可以直接用#include <linux/math64.h>中的:
static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder);
static inline s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder);
undefined reference to `__modsi3'和`__udivdi3'问题的分析与解决办法嵌入式开发交流群280352802,欢迎加入!【编译器版本】 arm-linux-gcc 3.4.1【问题描述】 在做嵌入式底层开发时(基于ARM编译无OS的程序),编写整数转字符串函数,用到了求余操作%和除数操作,部分代码如下:...while(num){ denom = num
欢迎使用Markdown编辑器写博客本Markdown编辑器使用StackEdit修改而来,用它写博客,将会带来全新的体验哦: Markdown和扩展Markdown简洁的语法 代码块高亮 图片链接和图片上传 LaTex数学公式 UML序列图和流程图 离线写博客 导入导出Markdown文件 丰富的快捷键 加粗 Ctrl + B 斜体 Ctrl + I 引用 Ctrl
报错如下: 通过参考博客:http://blog.chinaunix.net/uid-20717979-id-3351360.html 后发现是数据类型 问题 ,但是作者提供的思路没有 解决 ,于是去lib/printfmt.c文件中做类型强转换后运行成功 printnum(putch, putdat, num / base, base, width - 1, padc); printnum(putch, putdat, (uint32_t)num / (uint32_t)base, bas
[ 17.940000] qca_ol: Unknown symbol __udivdi3 (err 0) insmod: can ' t insert ' /lib/modules/qca_ol.ko ' : unknown symbol in module, or unknown parameter         这个错误导致qca_ol.ko无法
(转载注:这篇文章对于我有很大的帮助,只看了一半就把目前手上的内核线程的 问题 解决 了,而这个 问题 之前并没有找到合适的 解决 方法) 原文地址:http://yijunzhu.diandian.com/?tag=%E5%86%85%E6%A0%B8 【 问题 】 编译Linux下面的代码,经常会遇到这种错误: undefined reference to `__udivdi 3 ' 解决 过程】 ...
[1]系统移植的一个错误 ....but target u-boot has EABI version 5 [2]u-boot-1.1.6移植时出现一个编译器版本 问题 [3]uboot编译突然出现has EABI version 5, but target u-boot has EABI version 0错误 [4]Source object has EABI version
按照毕业班课程之自己编写bootloader,因为用到了求余运算编译完成链接时出现错误提示: undefined reference to ‘__umodsi3’,揣测这是因为编译器版本太低不支持求余运算,需要其他库支持导致的,使用4.3.2版本的arm-linux-gcc后 问题 得以 解决 。 安装如何arm-linux-gcc参考博文:arm-linux-gcc的安装配置
MIT6828操作系统实践记录(一) 最近经常感受到被大佬碾压,想想自己写了几年代码但对操作系统的理解似乎仍然停留在课本上…OTZ,特开此篇来进行实践、总结。感谢大佬们,大佬们的碾压就是我前进的动力。 本系列的目的是通过QEMU模拟计算机硬件,然后在此基础上进行操作系统的实践学习,课程地址:https://pdos.csail.mit.edu/6.828/2017/schedule.html 本文将逐步记录实践操作,并复习相关的知识。 vmware workstation16 player +