summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2006-01-31 00:29:30 +0000
committerMike Frysinger <vapier@gentoo.org>2006-01-31 00:29:30 +0000
commit8504c3b4d833a77dc3384a0af36a85aeb36d9dcc (patch)
tree6fc1656635285c4780ccc77e2e78601c69bd1f88
parent8472ea4c5d55b9bde2672ca01ebac1c9f1e73b81 (diff)
John Bowler writes in Bug 385:
This patch changes all cases where the ARM assembler mov pc,rx instructions are used to ensure that the thumb/arm interwork change of process more works - in essence mov pc,rx needs to become bx rc. The ldr pc or ldm rx, {pc} instructions are not changed - this is fine on ARM >=v5 but will fail to restore thumb mode on ARM v4T, i.e. this code will not provide support for thumb on ARM v4T. One mov pc is left in resolve.S, this is fixed in a different patch - thumb-resolve.patch The changes are protected by __THUMB_INTERWORK__ - the original mov instruction will work on newer architectures and is required on arch v4 (not v4t) and earlier - those which did not support thumb - so this is safe. See gcc lib1asmfuncs for a more exact test.
-rw-r--r--ldso/ldso/arm/dl-startup.h69
-rw-r--r--ldso/ldso/arm/dl-sysdep.h18
-rw-r--r--libc/sysdeps/linux/arm/clone.S6
-rw-r--r--libc/sysdeps/linux/arm/vfork.S8
4 files changed, 100 insertions, 1 deletions
diff --git a/ldso/ldso/arm/dl-startup.h b/ldso/ldso/arm/dl-startup.h
index 8ebc0b288..fa7c9651e 100644
--- a/ldso/ldso/arm/dl-startup.h
+++ b/ldso/ldso/arm/dl-startup.h
@@ -4,6 +4,7 @@
* Copyright (C) 2000-2004 by Erik Andersen <andersen@codepoet.org>
*/
+#if defined(__arm__)
asm(
" .text\n"
" .globl _start\n"
@@ -40,7 +41,11 @@ asm(
" ldr r0, .L_FINI_PROC\n"
" ldr r0, [sl, r0]\n"
" @ jump to the user_s entry point\n"
+#if defined(__THUMB_INTERWORK__)
+ " bx r6\n"
+#else
" mov pc, r6\n"
+#endif
".L_GET_GOT:\n"
" .word _GLOBAL_OFFSET_TABLE_ - .L_GOT_GOT - 4\n"
".L_SKIP_ARGS:\n"
@@ -51,6 +56,70 @@ asm(
" .size _start,.-_start\n"
".previous\n"
);
+#else
+asm(
+ " .text\n"
+ " .arm\n"
+ " .globl _start\n"
+ " .type _start,%function\n"
+ "_start:\n"
+ " @ dumb: can't persuade the linker to make the start address\n"
+ " @ odd, so use an arm function and change to thumb (_dl_start\n"
+ " @ is thumb)\n"
+ " adr r0, __dl_thumb_start+1\n"
+ " bx r0\n"
+ "\n\n"
+ " .thumb\n"
+ " .globl __dl_thumb_start\n"
+ " .thumb_func\n"
+ " .type __dl_thumb_start,%function\n"
+ "__dl_thumb_start:\n"
+ " @ at start time, all the args are on the stack\n"
+ " mov r0, sp\n"
+ " bl _dl_start\n"
+ " @ returns user entry point in r0\n"
+ " mov r6, r0\n"
+ " @ we are PIC code, so get global offset table\n"
+ " ldr r7, .L_GET_GOT\n"
+ ".L_GOT_GOT:\n"
+ " add r7, pc\n"
+ " @ See if we were run as a command with the executable file\n"
+ " @ name as an extra leading argument.\n"
+ " ldr r4, .L_SKIP_ARGS\n"
+ " ldr r4, [r7, r4]\n"
+ " @ get the original arg count\n"
+ " ldr r1, [sp]\n"
+ " @ subtract _dl_skip_args from it\n"
+ " sub r1, r1, r4\n"
+ " @ adjust the stack pointer to skip them\n"
+ " lsl r4, r4, #2\n"
+ " add sp, r4\n"
+ " @ get the argv address\n"
+ " add r2, sp, #4\n"
+ " @ store the new argc in the new stack location\n"
+ " str r1, [sp]\n"
+ " @ compute envp\n"
+ " lsl r3, r1, #2\n"
+ " add r3, r3, r2\n"
+ " add r3, #4\n"
+ "\n\n"
+ " @ load the finalizer function\n"
+ " ldr r0, .L_FINI_PROC\n"
+ " ldr r0, [r7, r0]\n"
+ " @ jump to the user_s entry point\n"
+ " bx r6\n"
+ "\n\n"
+ ".L_GET_GOT:\n"
+ " .word _GLOBAL_OFFSET_TABLE_ - .L_GOT_GOT - 4\n"
+ ".L_SKIP_ARGS:\n"
+ " .word _dl_skip_args(GOTOFF)\n"
+ ".L_FINI_PROC:\n"
+ " .word _dl_fini(GOT)\n"
+ "\n\n"
+ " .size _start,.-_start\n"
+ ".previous\n"
+);
+#endif
/* Get a pointer to the argv array. On many platforms this can be just
diff --git a/ldso/ldso/arm/dl-sysdep.h b/ldso/ldso/arm/dl-sysdep.h
index 794c86a0c..194fd2eb7 100644
--- a/ldso/ldso/arm/dl-sysdep.h
+++ b/ldso/ldso/arm/dl-sysdep.h
@@ -85,7 +85,25 @@ elf_machine_load_address (void)
extern void __dl_start asm ("_dl_start");
Elf32_Addr got_addr = (Elf32_Addr) &__dl_start;
Elf32_Addr pcrel_addr;
+#if !defined __thumb__
asm ("adr %0, _dl_start" : "=r" (pcrel_addr));
+#else
+ int tmp;
+ /* The above adr will not work on thumb because it
+ * is negative. The only safe way is to temporarily
+ * swap to arm.
+ */
+ asm( ".align 2\n"
+ " bx pc\n"
+ " nop \n"
+ " .arm \n"
+ " adr %0, _dl_start\n"
+ " .align 2\n"
+ " orr %1, pc, #1\n"
+ " bx %1\n"
+ " .force_thumb\n"
+ : "=r" (pcrel_addr), "=&r" (tmp));
+#endif
return pcrel_addr - got_addr;
}
diff --git a/libc/sysdeps/linux/arm/clone.S b/libc/sysdeps/linux/arm/clone.S
index 6672c7d6e..98b1296c0 100644
--- a/libc/sysdeps/linux/arm/clone.S
+++ b/libc/sysdeps/linux/arm/clone.S
@@ -52,7 +52,11 @@ clone:
DO_CALL (clone)
movs a1, a1
blt __error
- movne pc, lr
+#if defined(__THUMB_INTERWORK__)
+ bxne lr
+#else
+ movne pc, lr
+#endif
@ pick the function arg and call address off the stack and execute
ldr r0, [sp, #4]
diff --git a/libc/sysdeps/linux/arm/vfork.S b/libc/sysdeps/linux/arm/vfork.S
index e13636940..486a7e8e4 100644
--- a/libc/sysdeps/linux/arm/vfork.S
+++ b/libc/sysdeps/linux/arm/vfork.S
@@ -26,7 +26,11 @@ __vfork:
#ifdef __NR_vfork
DO_CALL (vfork)
cmn r0, #4096
+#if defined(__THUMB_INTERWORK__)
+ bxcc lr
+#else
movcc pc, lr
+#endif
/* Check if vfork even exists. */
ldr r1, =-ENOSYS
@@ -39,7 +43,11 @@ __vfork:
cmn r0, #4096
/* Syscall worked. Return to child/parent */
+#if defined(__THUMB_INTERWORK__)
+ bxcc lr
+#else
movcc pc, lr
+#endif
__error:
b __syscall_error