Browse Source

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.

Mike Frysinger 18 years ago
parent
commit
8504c3b4d8

+ 69 - 0
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

+ 18 - 0
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;
 }
 

+ 5 - 1
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]

+ 8 - 0
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