Build errors for timespec_add_ns (undefined reference to `__aeabi_uldivmod')

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]


Following up on this thread (which I couldn't find using Google, 

I'm building with arm-linux-gnueabi-gcc (Debian 4.3.0-1) 4.3.1 
20080309 (prerelease), and getting this:

kernel/built-in.o: In function `getnstimeofday':
utsname_sysctl.c:(.text+0x24184): undefined reference to `__aeabi_uldivmod'

I understand fully what the problem is, but I don't understand why I 
don't see more discussion about it on the list.  Are Adrian and I the 
only ones using 4.3?  :)

Anyway, here's what I've been doing to track down the problem.  When I 
build timespec_add_ns() with -O0, I don't get any calls to 
__aeabi_uldivmod.  Anything else seems to trigger the call.  Here's what 
the -O1 output looks like (don't trust this objdump --source too much, 
since the input code has been optimized):

void timespec_add_ns(struct timespec *a, u64 ns)
    0:   e1a0c00d        mov     ip, sp
    4:   e92ddbf0        push    {r4, r5, r6, r7, r8, r9, fp, ip, lr, pc}
    8:   e24cb004        sub     fp, ip, #4      ; 0x4
    c:   e1a05000        mov     r5, r0
         ns += a->tv_nsec;
         while(unlikely(ns >= NSEC_PER_SEC)) {
                 ns -= NSEC_PER_SEC;
   10:   e5900004        ldr     r0, [r0, #4]
   14:   e1a01fc0        asr     r1, r0, #31
   18:   e0928000        adds    r8, r2, r0
   1c:   e0a39001        adc     r9, r3, r1
   20:   e3590000        cmp     r9, #0  ; 0x0
   24:   1a000003        bne     38 <timespec_add_ns+0x38>
   28:   1a000016        bne     88 <timespec_add_ns+0x88>
   2c:   e59f305c        ldr     r3, [pc, #92]   ; 90 <timespec_add_ns+0x90>
   30:   e1580003        cmp     r8, r3
   34:   9a000013        bls     88 <timespec_add_ns+0x88>
   38:   e59f2054        ldr     r2, [pc, #84]   ; 94 <timespec_add_ns+0x94>
   3c:   e3e03000        mvn     r3, #0  ; 0x0
   40:   e0986002        adds    r6, r8, r2
   44:   e0a97003        adc     r7, r9, r3
         a->tv_nsec = ns;
   48:   e5954000        ldr     r4, [r5]
   4c:   e2844001        add     r4, r4, #1      ; 0x1
   50:   e1a00006        mov     r0, r6
   54:   e1a01007        mov     r1, r7
   58:   e59f2038        ldr     r2, [pc, #56]   ; 98 <timespec_add_ns+0x98>
   5c:   e3a03000        mov     r3, #0  ; 0x0
   60:   ebfffffe        bl      0 <__aeabi_uldivmod>
   64:   e1a08002        mov     r8, r2
   68:   e1a09003        mov     r9, r3
   6c:   e1a00006        mov     r0, r6
   70:   e1a01007        mov     r1, r7
   74:   e59f201c        ldr     r2, [pc, #28]   ; 98 <timespec_add_ns+0x98>
   78:   e3a03000        mov     r3, #0  ; 0x0
   7c:   ebfffffe        bl      0 <__aeabi_uldivmod>
   80:   e0844000        add     r4, r4, r0
   84:   e5854000        str     r4, [r5]

   88:   e5858004        str     r8, [r5, #4]

   8c:   e89dabf0        ldm     sp, {r4, r5, r6, r7, r8, r9, fp, sp, pc}
   90:   3b9ac9ff        .word   0x3b9ac9ff
   94:   c4653600        .word   0xc4653600
   98:   3b9aca00        .word   0x3b9aca00

Long story short, gcc is calling uldivmod to do the addition, more or 
less.  I tracked down the code in gcc-4.3, and it's basically this:

ARM_FUNC_START aeabi_uldivmod
         sub sp, sp, #8
#if defined(__thumb2__)
         mov ip, sp
         push {ip, lr}
         do_push {sp, lr}
         bl SYM(__gnu_uldivmod_helper) __PLT__
         ldr lr, [sp, #4]
         add sp, sp, #8
         do_pop {r2, r3}

And, not that it matters at this point since we're not linking libgcc:

unsigned long long
__gnu_uldivmod_helper (unsigned long long a,
                        unsigned long long b,
                        unsigned long long *remainder)
   unsigned long long quotient;

   quotient = __udivdi3 (a, b);
   *remainder = a - b * quotient;
   return quotient;

(I'm not sure I'm putting the pieces together properly here, since the 
timespec_add_ns function is explicitly passing #0 in r3, which should be 
the pointer for the remainder, right?  Odd.)

So why is gcc calling uldivmod to do simple addition in timespec_add_ns? 
  Because the result is getting stuffed into a u32, so according to C it 
has to truncate.  There are a handful of options here, gcc is choosing 
to implement the truncation by modulo.  I'll just stop there.  :)

Not that I'm proposing this (ok, maybe I am), but this little quickie 
addresses the problem brute-force:

diff --git a/include/linux/time.h b/include/linux/time.h
index b04136d..59682d0 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -169,8 +169,9 @@ extern struct timeval ns_to_timeval(const s64 nsec);
   * @a:         pointer to timespec to be incremented
   * @ns:                unsigned nanoseconds value to be added
-static inline void timespec_add_ns(struct timespec *a, u64 ns)
+static inline void timespec_add_ns(struct timespec *a, u64 nsl)
+       u32 ns = nsl;
         ns += a->tv_nsec;
         while(unlikely(ns >= NSEC_PER_SEC)) {
                 ns -= NSEC_PER_SEC;

The problem here is that I don't know what the range for the "ns" 
parameter might be, so I don't know if u32 is too small.  I haven't 
tested this patch, other than to verify that it deals with the missing 

Bill Gatliff

List admin:

[Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Fedora ARM]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [PDAs]     [Linux]     [Linux Book List]     [Linux MIPS]     [Yosemite Campsites]     [Photos]

Add to Google Google PageRank Checking tool