summaryrefslogtreecommitdiff
path: root/libc/sysdeps/linux/arm
diff options
context:
space:
mode:
authorBernhard Reutner-Fischer <rep.dot.nop@gmail.com>2008-03-26 13:40:36 +0000
committerBernhard Reutner-Fischer <rep.dot.nop@gmail.com>2008-03-26 13:40:36 +0000
commitefce79f09ae6daa77cd322df0d532beec3f445f5 (patch)
treeae936850c5671b8bea0abf0d33bf2196f7abc796 /libc/sysdeps/linux/arm
parent17e961d9c708ab202760ce830f8efe73e91bb129 (diff)
Paul Brook writes:
The attached patch adds support for compiling arm uClibc as pure Thumb code. This is needed because some recent ARM codes do not implement traditional ARM mode. Specifically: * Cortex-M1 - An extremely minimal FPGA based core that only implements Thumb-1 (aka ARMv6-M). * Cortex-M3 - A Thumb-2 only ARMv7-M core. Most of uClibc already builds in Thumb mode, all that is left are a handful of assembly bits. Tested on arm-uclinuxeabi.
Diffstat (limited to 'libc/sysdeps/linux/arm')
-rw-r--r--libc/sysdeps/linux/arm/__longjmp.S33
-rw-r--r--libc/sysdeps/linux/arm/bits/arm_asm.h28
-rw-r--r--libc/sysdeps/linux/arm/bsd-_setjmp.S28
-rw-r--r--libc/sysdeps/linux/arm/bsd-setjmp.S26
-rw-r--r--libc/sysdeps/linux/arm/clone.S53
-rw-r--r--libc/sysdeps/linux/arm/crt1.S69
-rw-r--r--libc/sysdeps/linux/arm/crti.S1
-rw-r--r--libc/sysdeps/linux/arm/crtn.S1
-rw-r--r--libc/sysdeps/linux/arm/mmap64.S45
-rw-r--r--libc/sysdeps/linux/arm/setjmp.S27
-rw-r--r--libc/sysdeps/linux/arm/sigrestorer.S7
-rw-r--r--libc/sysdeps/linux/arm/syscall-eabi.S26
-rw-r--r--libc/sysdeps/linux/arm/vfork.S40
13 files changed, 380 insertions, 4 deletions
diff --git a/libc/sysdeps/linux/arm/__longjmp.S b/libc/sysdeps/linux/arm/__longjmp.S
index 4261797f8..5faf4ece9 100644
--- a/libc/sysdeps/linux/arm/__longjmp.S
+++ b/libc/sysdeps/linux/arm/__longjmp.S
@@ -18,6 +18,7 @@
02111-1307 USA. */
#include <features.h>
+#include <bits/arm_asm.h>
#define _SETJMP_H
#define _ASM
#include <bits/setjmp.h>
@@ -26,13 +27,44 @@
.global __longjmp
.type __longjmp,%function
.align 2
+#if defined(THUMB1_ONLY)
+.thumb_func
+__longjmp:
+ mov r2, r0
+ movs r0, r1
+ /* can't let setjmp() return zero! */
+ bne 1f
+ mov r0, #1
+1:
+ mov r1, r2
+ /* Restore registers, shuffling them through low regs. */
+ add r2, #(4 * 4)
+ ldmia r2!, {r4, r5, r6, r7}
+ mov r8, r4
+ mov r9, r5
+ mov sl, r6
+ mov fp, r7
+ ldmia r2!, {r4, r5}
+ mov sp, r4
+ mov lr, r5
+ ldmia r1!, {r4, r5, r6, r7}
+ bx lr
+#else
__longjmp:
mov ip, r0 /* save jmp_buf pointer */
movs r0, r1 /* get the return value in place */
+ IT(t, eq)
moveq r0, #1 /* can't let setjmp() return zero! */
+#if defined(__thumb2__)
+ /* Thumb-2 does not allow loading sp with ldm. */
+ ldmia ip!, {v1-v6, sl, fp}
+ ldr sp, [ip], #4
+ ldr lr, [ip], #4
+#else
ldmia ip!, {v1-v6, sl, fp, sp, lr}
+#endif
#if defined __UCLIBC_HAS_FLOATS__ && ! defined __UCLIBC_HAS_SOFT_FLOAT__
#ifdef __VFP_FP__
@@ -76,6 +108,7 @@ __longjmp:
#else
mov pc, lr
#endif
+#endif
.size __longjmp,.-__longjmp
libc_hidden_def(__longjmp)
diff --git a/libc/sysdeps/linux/arm/bits/arm_asm.h b/libc/sysdeps/linux/arm/bits/arm_asm.h
new file mode 100644
index 000000000..1d87df6eb
--- /dev/null
+++ b/libc/sysdeps/linux/arm/bits/arm_asm.h
@@ -0,0 +1,28 @@
+/* Various definitons used the the ARM uClibc assembly code. */
+#ifndef _ARM_ASM_H
+#define _ARM_ASM_H
+
+#ifdef __thumb2__
+.thumb
+.syntax unified
+#define IT(t, cond) i##t cond
+#else
+/* XXX: This can be removed if/when we require an assembler that supports
+ unified assembly syntax. */
+#define IT(t, cond)
+/* Code to return from a thumb function stub. */
+#ifdef __ARM_ARCH_4T__
+#define POP_RET pop {r2, pc}
+#else
+#define POP_RET pop {r2, r3}; bx r3
+#endif
+#endif
+
+#if defined(__ARM_ARCH_6M__)
+/* Force arm mode to flush out errors on M profile cores. */
+#undef IT
+#define THUMB1_ONLY 1
+#endif
+
+#endif /* _ARM_ASM_H */
+
diff --git a/libc/sysdeps/linux/arm/bsd-_setjmp.S b/libc/sysdeps/linux/arm/bsd-_setjmp.S
index f70073266..a05570df7 100644
--- a/libc/sysdeps/linux/arm/bsd-_setjmp.S
+++ b/libc/sysdeps/linux/arm/bsd-_setjmp.S
@@ -17,13 +17,38 @@
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
-/* This just does a tail-call to `__sigsetjmp (ARG, 1)'.
+#include <bits/arm_asm.h>
+
+/* This just does a tail-call to `__sigsetjmp (ARG, 0)'.
We cannot do it in C because it must be a tail-call, so frame-unwinding
in setjmp doesn't clobber the state restored by longjmp. */
.global _setjmp
.type _setjmp,%function
.align 2
+#if defined(THUMB1_ONLY)
+.thumb_func
+_setjmp:
+ mov r1, #0
+#ifdef __PIC__
+ ldr r3, .L_GOT
+ adr r2, .L_GOT
+ add r3, r2, r3
+
+ ldr r2, .L_GOT+4 /* __sigsetjmp */
+ ldr r2, [r2, r3]
+ bx r2
+
+ .align 2
+.L_GOT:
+ .word _GLOBAL_OFFSET_TABLE_-.L_GOT
+ .word __sigsetjmp(GOT)
+#else
+ ldr r2, =__sigsetjmp
+ bx r2
+.pool
+#endif
+#else
_setjmp:
mov r1, #0
#ifdef __PIC__
@@ -31,5 +56,6 @@ _setjmp:
#else
b __sigsetjmp
#endif
+#endif
.size _setjmp,.-_setjmp
diff --git a/libc/sysdeps/linux/arm/bsd-setjmp.S b/libc/sysdeps/linux/arm/bsd-setjmp.S
index 6253c6675..d7ca72ad5 100644
--- a/libc/sysdeps/linux/arm/bsd-setjmp.S
+++ b/libc/sysdeps/linux/arm/bsd-setjmp.S
@@ -17,6 +17,8 @@
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
+#include <bits/arm_asm.h>
+
/* This just does a tail-call to `__sigsetjmp (ARG, 1)'.
We cannot do it in C because it must be a tail-call, so frame-unwinding
in setjmp doesn't clobber the state restored by longjmp. */
@@ -24,6 +26,29 @@
.global setjmp
.type setjmp,%function
.align 2
+#if defined(THUMB1_ONLY)
+.thumb_func
+setjmp:
+ mov r1, #1
+#ifdef __PIC__
+ ldr r3, .L_GOT
+ adr r2, .L_GOT
+ add r3, r2, r3
+
+ ldr r2, .L_GOT+4 /* __sigsetjmp */
+ ldr r2, [r2, r3]
+ bx r2
+
+ .align 2
+.L_GOT:
+ .word _GLOBAL_OFFSET_TABLE_-.L_GOT
+ .word __sigsetjmp(GOT)
+#else
+ ldr r2, =__sigsetjmp
+ bx r2
+.pool
+#endif
+#else
setjmp:
mov r1, #1
#ifdef __PIC__
@@ -31,5 +56,6 @@ setjmp:
#else
b __sigsetjmp
#endif
+#endif
.size setjmp,.-setjmp
diff --git a/libc/sysdeps/linux/arm/clone.S b/libc/sysdeps/linux/arm/clone.S
index a5a847d1e..d9483735d 100644
--- a/libc/sysdeps/linux/arm/clone.S
+++ b/libc/sysdeps/linux/arm/clone.S
@@ -24,17 +24,66 @@
#include <features.h>
#include <bits/errno.h>
#include <sys/syscall.h>
+#include <bits/arm_asm.h>
-#ifdef __NR_clone
+#if defined(__NR_clone)
/* int clone(int (*fn)(void *arg), void *child_stack, int flags, void *arg); */
.text
.global clone
.type clone,%function
.align 2
+#if defined(THUMB1_ONLY)
+.thumb_func
clone:
@ sanity check args
cmp r0, #0
+ beq __einval
+ cmp r1, #0
+ beq __einval
+
+ @ insert the args onto the new stack
+ sub r1, r1, #8
+ str r3, [r1, #4]
+ @ save the function pointer as the 0th element
+ str r0, [r1]
+
+ @ do the system call
+ @ get flags
+ mov r0, r2
+ @ new sp is already in r1
+ DO_CALL (clone)
+ movs a1, a1
+ blt __error
+ beq 1f
+ bx lr
+1:
+
+ @ pick the function arg and call address off the stack and execute
+ ldr r0, [sp, #4]
+ ldr r1, [sp]
+ bl 2f @ blx r1
+
+ @ and we are done, passing the return value through r0
+ bl HIDDEN_JUMPTARGET(_exit)
+ @ Should never return
+ b .
+
+2:
+ bx r1
+
+__einval:
+ ldr r0, =-EINVAL
+__error:
+ push {r3, lr}
+ bl __syscall_error
+ POP_RET
+.pool
+#else
+clone:
+ @ sanity check args
+ cmp r0, #0
+ IT(te, ne)
cmpne r1, #0
moveq r0, #-EINVAL
beq __error
@@ -52,6 +101,7 @@ clone:
DO_CALL (clone)
movs a1, a1
blt __error
+ IT(t, ne)
#if defined(__USE_BX__)
bxne lr
#else
@@ -68,6 +118,7 @@ clone:
__error:
b __syscall_error
+#endif
.size clone,.-clone
diff --git a/libc/sysdeps/linux/arm/crt1.S b/libc/sysdeps/linux/arm/crt1.S
index 8d4d230a7..082348e39 100644
--- a/libc/sysdeps/linux/arm/crt1.S
+++ b/libc/sysdeps/linux/arm/crt1.S
@@ -94,6 +94,7 @@ ARM register quick reference:
*/
#include <features.h>
+#include <bits/arm_asm.h>
.text
.globl _start
@@ -105,6 +106,73 @@ ARM register quick reference:
.weak _fini
#endif
+#if defined(THUMB1_ONLY)
+.thumb_func
+_start:
+ /* Clear the frame pointer since this is the outermost frame. */
+ mov r3, #0
+ mov fp, r3
+
+#ifdef __ARCH_USE_MMU__
+ /* Pop argc off the stack and save a pointer to argv */
+ pop {a2}
+ mov a3, sp
+#else
+ /*
+ * uClinux/arm stacks look a little different from normal
+ * MMU-full Linux/arm stacks (for no good reason)
+ */
+ /* pull argc and argv off the stack. We are going to push 3
+ * arguments, so pop one here to maintain doubleword alignment. */
+ pop {a2}
+ ldr a3, [sp]
+#endif
+
+ /* Push stack limit and rtld_fini */
+ push {a1, a3}
+
+#ifdef __PIC__
+ ldr r4, .L_GOT
+.L_GOT_OFF:
+ adr r5, .L_GOT
+ add r4, r5, r4
+
+ ldr r5, .L_GOT+4 /* _fini */
+ ldr a1, [r4, r5]
+ push {a1} /* Push _fini */
+
+ ldr r5, .L_GOT+8 /* _init */
+ ldr a4, [r4, r5]
+
+ ldr r5, .L_GOT+12 /* main */
+ ldr a1, [r4, r5]
+
+#else
+ /* Fetch address of fini */
+ ldr r4, =_fini
+ /* Push fini */
+ push {r4}
+
+ /* Set up the other arguments in registers */
+ ldr a1, =main
+ ldr a4, =_init
+#endif
+ /* __uClibc_main (main, argc, argv, init, fini, rtld_fini, stack_end) */
+ /* Let the libc call main and exit with its return code. */
+ bl __uClibc_main
+
+ /* should never get here....*/
+ bl abort
+.pool
+
+#ifdef __PIC__
+.L_GOT:
+ .word _GLOBAL_OFFSET_TABLE_-.L_GOT
+ .word _fini(GOT)
+ .word _init(GOT)
+ .word main(GOT)
+#endif
+#else /* !THUMB1_ONLY */
_start:
/* Clear the frame pointer and link register since this is the outermost frame. */
mov fp, #0
@@ -175,6 +243,7 @@ _start:
.word _init(GOT)
.word main(GOT)
#endif
+#endif
/* Define a symbol for the first piece of initialized data. */
.data
diff --git a/libc/sysdeps/linux/arm/crti.S b/libc/sysdeps/linux/arm/crti.S
index 4835b8331..e335b7140 100644
--- a/libc/sysdeps/linux/arm/crti.S
+++ b/libc/sysdeps/linux/arm/crti.S
@@ -1,5 +1,6 @@
.file "initfini.c"
+#include <bits/arm_asm.h>
.section .init
.global _init
.type _init, %function
diff --git a/libc/sysdeps/linux/arm/crtn.S b/libc/sysdeps/linux/arm/crtn.S
index 7a1ca1ab1..de01b38dc 100644
--- a/libc/sysdeps/linux/arm/crtn.S
+++ b/libc/sysdeps/linux/arm/crtn.S
@@ -1,5 +1,6 @@
.file "initfini.c"
+#include <bits/arm_asm.h>
.section .init
.global _init
.type _init, %function
diff --git a/libc/sysdeps/linux/arm/mmap64.S b/libc/sysdeps/linux/arm/mmap64.S
index ba8cb2fca..73d6b51ce 100644
--- a/libc/sysdeps/linux/arm/mmap64.S
+++ b/libc/sysdeps/linux/arm/mmap64.S
@@ -20,6 +20,7 @@
#define _ERRNO_H
#include <bits/errno.h>
#include <sys/syscall.h>
+#include <bits/arm_asm.h>
#if defined __UCLIBC_HAS_LFS__ && defined __NR_mmap2
@@ -28,9 +29,46 @@
.global mmap64
.type mmap64,%function
.align 2
-mmap64:
#ifdef __ARM_EABI__
+#if defined(THUMB1_ONLY)
+.thumb_func
+mmap64:
+#ifdef __ARMEB__
+/* Offsets are after pushing 3 words. */
+# define LOW_OFFSET 12 + 8 + 4
+# define HIGH_OFFSET 12 + 8 + 0
+#else
+# define LOW_OFFSET 12 + 8 + 0
+# define HIGH_OFFSET 12 + 8 + 4
+#endif
+ push {r4, r5, r6}
+ ldr r6, [sp, $LOW_OFFSET]
+ ldr r5, [sp, $HIGH_OFFSET]
+ lsl r4, r6, #20 @ check that offset is page-aligned
+ bne .Linval
+ lsr r4, r5, #12 @ check for overflow
+ bne .Linval
+ @ compose page offset
+ lsr r6, r6, #12
+ lsl r5, r5, #20
+ orr r5, r5, r6
+ ldr r4, [sp, #8] @ load fd
+ DO_CALL (mmap2)
+ ldr r1, =0xfffff000
+ cmp r0, r1
+ bcs .Lerror
+ bx lr
+.Linval:
+ ldr r0, =-EINVAL
+ pop {r4, r5, r6}
+.Lerror:
+ push {r3, lr}
+ bl __syscall_error
+ POP_RET
+.pool
+#else /* !THUMB1_ONLY */
+mmap64:
#ifdef __ARMEB__
# define LOW_OFFSET 8 + 4
/* The initial + 4 is for the stack postdecrement. */
@@ -45,6 +83,7 @@ mmap64:
str r4, [sp, #-4]!
movs r4, ip, lsl $20 @ check that offset is page-aligned
mov ip, ip, lsr $12
+ IT(t, eq)
moveqs r4, r5, lsr $12 @ check for overflow
bne .Linval
ldr r4, [sp, $8] @ load fd
@@ -52,6 +91,7 @@ mmap64:
DO_CALL (mmap2)
cmn r0, $4096
ldmfd sp!, {r4, r5}
+ IT(t, cc)
#if defined(__USE_BX__)
bxcc lr
#else
@@ -62,7 +102,8 @@ mmap64:
mov r0, $-EINVAL
ldmfd sp!, {r4, r5}
b __syscall_error
-#else
+#endif
+#else /* !__ARM_EABI__ */
stmfd sp!, {r4, r5, lr}
ldr r5, [sp, $16]
ldr r4, [sp, $12]
diff --git a/libc/sysdeps/linux/arm/setjmp.S b/libc/sysdeps/linux/arm/setjmp.S
index 8d15b8324..2df7d551a 100644
--- a/libc/sysdeps/linux/arm/setjmp.S
+++ b/libc/sysdeps/linux/arm/setjmp.S
@@ -18,15 +18,41 @@
02111-1307 USA. */
#include <features.h>
+#include <bits/arm_asm.h>
.global __sigsetjmp
.type __sigsetjmp,%function
.align 2
+#if defined(THUMB1_ONLY)
+.thumb_func
__sigsetjmp:
+ push {r3, r4, r5, r6, r7, lr}
mov ip, r0
+ stmia r0!, {r4, r5, r6, r7}
+ mov r2, r8
+ mov r3, r9
+ mov r4, sl
+ mov r5, fp
+ add r6, sp, #(6 * 4)
+ mov r7, lr
+ stmia r0!, {r2, r3, r4, r5, r6, r7}
+ mov r0, ip
+ bl __sigjmp_save
+ pop {r3, r4, r5, r6, r7, pc}
+
+#else
+__sigsetjmp:
+ /* Save registers */
+ mov ip, r0
+#if defined(__thumb2__)
+ stmia ip!, {v1-v6, sl, fp}
+ movs r2, sp
+ stmia ip!, {r2, lr}
+#else
/* Save registers */
stmia ip!, {v1-v6, sl, fp, sp, lr}
+#endif
#if defined __UCLIBC_HAS_FLOATS__ && ! defined __UCLIBC_HAS_SOFT_FLOAT__
# ifdef __VFP_FP__
/* Store the VFP registers. */
@@ -70,5 +96,6 @@ __sigsetjmp:
#else
B __sigjmp_save
#endif
+#endif
.size __sigsetjmp,.-__sigsetjmp
diff --git a/libc/sysdeps/linux/arm/sigrestorer.S b/libc/sysdeps/linux/arm/sigrestorer.S
index 194228a38..79728fd40 100644
--- a/libc/sysdeps/linux/arm/sigrestorer.S
+++ b/libc/sysdeps/linux/arm/sigrestorer.S
@@ -16,6 +16,7 @@
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
+#include <bits/arm_asm.h>
#include <sys/syscall.h>
#include <linux/version.h>
@@ -38,6 +39,9 @@
.type __default_sa_restorer,%function
.align 2
#ifdef __ARM_EABI__
+#ifdef __thumb__
+.thumb_func
+#endif
.fnstart
.save {r0-r15}
#if LINUX_VERSION_CODE >= 0x020612
@@ -62,6 +66,9 @@ __default_sa_restorer:
.type __default_rt_sa_restorer,%function
.align 2
#ifdef __ARM_EABI__
+#ifdef __thumb__
+.thumb_func
+#endif
.fnstart
.save {r0-r15}
#if LINUX_VERSION_CODE >= 0x020612
diff --git a/libc/sysdeps/linux/arm/syscall-eabi.S b/libc/sysdeps/linux/arm/syscall-eabi.S
index efc30690c..b9318821b 100644
--- a/libc/sysdeps/linux/arm/syscall-eabi.S
+++ b/libc/sysdeps/linux/arm/syscall-eabi.S
@@ -17,6 +17,7 @@
02111-1307 USA. */
#include <sys/syscall.h>
+#include <bits/arm_asm.h>
/* In the EABI syscall interface, we don't need a special syscall to
implement syscall(). It won't work reliably with 64-bit arguments
@@ -26,6 +27,29 @@
.global syscall
.type syscall,%function
.align 4
+#if defined(THUMB1_ONLY)
+.thumb_func
+syscall:
+ push {r4, r5, r6, r7}
+ mov ip, r0
+ mov r0, r1
+ mov r1, r2
+ mov r2, r3
+ add r7, sp, #(4 * 4)
+ ldmia r7!, {r3, r4, r5, r6}
+ mov r7, ip
+ swi 0x0
+ pop {r4, r5, r6, r7}
+ ldr r1, =0xfffff000
+ cmp r0, r1
+ bcs 1f
+ bx lr
+1:
+ push {r3, lr}
+ bl __syscall_error
+ POP_RET
+.pool
+#else
syscall:
mov ip, sp
stmfd sp!, {r4, r5, r6, r7}
@@ -37,11 +61,13 @@ syscall:
swi 0x0
ldmfd sp!, {r4, r5, r6, r7}
cmn r0, #4096
+ IT(t, cc)
#if defined(__USE_BX__)
bxcc lr
#else
movcc pc, lr
#endif
b __syscall_error
+#endif
.size syscall,.-syscall
diff --git a/libc/sysdeps/linux/arm/vfork.S b/libc/sysdeps/linux/arm/vfork.S
index e9f63d46e..42595b026 100644
--- a/libc/sysdeps/linux/arm/vfork.S
+++ b/libc/sysdeps/linux/arm/vfork.S
@@ -6,6 +6,7 @@
*/
#include <features.h>
+#include <bits/arm_asm.h>
#define _ERRNO_H
#include <bits/errno.h>
@@ -18,11 +19,47 @@
.type __vfork,%function
.align 4
+#if defined(__thumb__) && !defined(__thumb2__)
+.thumb_func
+__vfork:
+#ifdef __NR_vfork
+ DO_CALL (vfork)
+ ldr r1, =0xfffff000
+ cmp r0, r1
+ bcs 1f
+ bx lr
+1:
+
+ /* Check if vfork even exists. */
+ ldr r1, =-ENOSYS
+ cmp r0, r1
+ bne __error
+
+ /* If we don't have vfork, use fork. */
+ DO_CALL (fork)
+ ldr r1, =0xfffff000
+ cmp r0, r1
+
+ /* Syscall worked. Return to child/parent */
+ bcs 1f
+ bx lr
+1:
+
+__error:
+ push {r3, lr}
+ bl __syscall_error
+ POP_RET
+.pool
+
+#endif
+
+#else
__vfork:
#ifdef __NR_vfork
DO_CALL (vfork)
cmn r0, #4096
+ IT(t, cc)
#if defined(__USE_BX__)
bxcc lr
#else
@@ -40,6 +77,7 @@ __vfork:
cmn r0, #4096
/* Syscall worked. Return to child/parent */
+ IT(t, cc)
#if defined(__USE_BX__)
bxcc lr
#else
@@ -48,8 +86,10 @@ __vfork:
__error:
b __syscall_error
+#endif
.size __vfork,.-__vfork
+
weak_alias(__vfork,vfork)
libc_hidden_weak(vfork)
#endif