undefined reference to ' modsi3'和 udivdi3'问题的分析与解决办法

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

undefined reference to `__modsi3'和`__udivdi3'问题的分析与解决办法

嵌入式开发交流群280352802,欢迎加入!

【编译器版本】

 arm-linux-gcc 3.4.1

【问题描述】

 在做嵌入式底层开发时(基于ARM编译无OS的程序),编写整数转字符串函数,用到了求余操作%和除数操作,部分代码如下:

...while(num){ denom = num % radix; num /= radix; *ptr++ = denom + '0';   if((num < radix) && num) {  *ptr++ = num + '0';  *ptr = '\0';  break; }}...

部分Makefile如下:

....c.o: $(CC) -static -nostartfiles -nostdlib -fno-builtin $(CPPFLAGS) $(CFLAGS) -c $<...

结果报错: 

undefined reference to `__umodsi3'

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 *//* Copyright 1995, 1996, 1998, 1999, 2000, 2003 Free Software Foundation, Inc.This file is free software; you can redistribute it and/or modify itunder the terms of the GNU General Public License as published by theFree Software Foundation; either version 2, or (at your option) anylater version.In addition to the permissions in the GNU General Public License, theFree Software Foundation gives you unlimited permission to link thecompiled version of this file into combinations with other programs,and to distribute those combinations without any restriction comingfrom the use of this file.  (The General Public License restrictionsdo apply in other respects; for example, they cover modification ofthe file, and distribution when not linked into a combineexecutable.)This file is distributed in the hope that it will be useful, butWITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNUGeneral Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; see the file COPYING.  If not, write tothe 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 loop1: 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 2f1: 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 #13: cmp \dividend, \divisor subhs \dividend, \dividend, \divisor mov \divisor,  \divisor,  lsr #14: cmp \dividend, \divisor subhs \dividend, \dividend, \divisor5:.endmENTRY(__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, lr11: moveq r0, #1 movne r0, #0 mov pc, lr12: ARM_DIV2_ORDER r1, r2 mov r0, r0, lsr r2 mov pc, lrENTRY(__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, lrENTRY(__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, lr10: teq ip, r0    @ same sign ? rsbmi r0, r0, #0 mov pc, lr11: movlo r0, #0 moveq r0, ip, asr #31 orreq r0, r0, #1 mov pc, lr12: ARM_DIV2_ORDER r1, r2 cmp ip, #0 mov r0, r3, lsr r2 rsbmi r0, r0, #0 mov pc, lrENTRY(__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, r310: cmp ip, #0 rsbmi r0, r0, #0 mov pc, lrLdiv0: 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);

           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow
这里写图片描述
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值