From efce79f09ae6daa77cd322df0d532beec3f445f5 Mon Sep 17 00:00:00 2001 From: Bernhard Reutner-Fischer Date: Wed, 26 Mar 2008 13:40:36 +0000 Subject: 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. --- libc/sysdeps/linux/arm/__longjmp.S | 33 +++++++++++++++++ libc/sysdeps/linux/arm/bits/arm_asm.h | 28 ++++++++++++++ libc/sysdeps/linux/arm/bsd-_setjmp.S | 28 +++++++++++++- libc/sysdeps/linux/arm/bsd-setjmp.S | 26 +++++++++++++ libc/sysdeps/linux/arm/clone.S | 53 ++++++++++++++++++++++++++- libc/sysdeps/linux/arm/crt1.S | 69 +++++++++++++++++++++++++++++++++++ libc/sysdeps/linux/arm/crti.S | 1 + libc/sysdeps/linux/arm/crtn.S | 1 + libc/sysdeps/linux/arm/mmap64.S | 45 ++++++++++++++++++++++- libc/sysdeps/linux/arm/setjmp.S | 27 ++++++++++++++ libc/sysdeps/linux/arm/sigrestorer.S | 7 ++++ libc/sysdeps/linux/arm/syscall-eabi.S | 26 +++++++++++++ libc/sysdeps/linux/arm/vfork.S | 40 ++++++++++++++++++++ 13 files changed, 380 insertions(+), 4 deletions(-) create mode 100644 libc/sysdeps/linux/arm/bits/arm_asm.h (limited to 'libc/sysdeps/linux') 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 +#include #define _SETJMP_H #define _ASM #include @@ -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 + +/* 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 + /* 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 #include #include +#include -#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 +#include .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 .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 .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 #include +#include #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 +#include .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 #include #include @@ -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 +#include /* 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 +#include #define _ERRNO_H #include @@ -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 -- cgit v1.2.3