|
|
|
Build errors for timespec_add_ns (undefined reference to `__aeabi_uldivmod') | |
| [Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] | |
Guys:
Following up on this thread (which I couldn't find using Google,
interestingly):
http://lists.arm.linux.org.uk/lurker/message/20080227.081641.1580db5d.en.html
I'm building 2.6.24.4 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
a->tv_sec++;
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}
#else
do_push {sp, lr}
#endif
bl SYM(__gnu_uldivmod_helper) __PLT__
ldr lr, [sp, #4]
add sp, sp, #8
do_pop {r2, r3}
RET
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
uldivmod.
b.g.
--
Bill Gatliff
bgat@xxxxxxxxxxxxxxx
-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php
[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]
![]() |
|