i386/127387: Inline assembler in x86 _start() in crt1.c only works
by chance
Christoph Mallon
christoph.mallon at gmx.de
Sun Sep 14 20:00:11 UTC 2008
>Number: 127387
>Category: i386
>Synopsis: Inline assembler in x86 _start() in crt1.c only works by chance
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: freebsd-i386
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Sun Sep 14 20:00:10 UTC 2008
>Closed-Date:
>Last-Modified:
>Originator: Christoph Mallon
>Release: n/a
>Organization:
>Environment:
n/a
>Description:
Disassembling _start() of a common binary shows this[0]:
08048450 <_start>:
8048450: 55 push %ebp
8048451: 89 e5 mov %esp,%ebp
8048453: 57 push %edi
8048454: 56 push %esi
8048455: 53 push %ebx
8048456: 83 ec 0c sub $0xc,%esp
8048459: 83 e4 f0 and $0xfffffff0,%esp
804845c: 8b 5d 04 mov 0x4(%ebp),%ebx
804845f: 89 d7 mov %edx,%edi
[...]
The last instruction shown is generated via inline assembler in src/lib/csu/i386-elf/crt1.c. It copies the rtld "cleanup" function pointer from %edx into a different register[1]. Just before the only declared parameter ist loaded from stack into %ebx. In the source file this instruction is below the inline assembler to load "cleanup", but of course the compiler is free to shuffle things around. Now the problematic part: The compiler chose %ebx as destination register. It could have chosen %edx, because nobody told him that there is a precious value in there. In fact there is no safe way to ensure that %edx is not destroyed before it is read in this function. I present a replacement, which does the ugly handling[2] in a small global asm block.
[0] objdump -d $SOME_UNSTRIPPED_BINARY | $PAGER or just objdump -d /usr/lib/crt1.o | $PAGER
[1] The used inline assembler statement is unnecessarily complicated, but that's another story. Simpler is asm("" : "=d" (cleanup));.
[2] Other parts like fetching argc from the stack and aligning the stack via inline assembler are far beyond legal C, too.
>How-To-Repeat:
>Fix:
See attachment.
Patch attached with submission follows:
Index: crt1.c
===================================================================
--- crt1.c (Revision 183012)
+++ crt1.c (Arbeitskopie)
@@ -38,8 +38,6 @@
extern int _DYNAMIC;
#pragma weak _DYNAMIC
-typedef void (*fptr)(void);
-
extern void _fini(void);
extern void _init(void);
extern int main(int, char **, char **);
@@ -55,35 +53,16 @@
char **environ;
const char *__progname = "";
-static __inline fptr
-get_rtld_cleanup(void)
-{
- fptr retval;
-
-#ifdef __GNUC__
- __asm__("movl %%edx,%0" : "=rm"(retval));
-#else
- retval = (fptr)0; /* XXXX Fix this for other compilers */
-#endif
- return(retval);
-}
-
/* The entry function. */
-void
-_start(char *ap, ...)
+static void
+#ifdef __GNUC__
+__attribute__((regparm(3), noreturn, used))
+#endif
+internal_start(int argc, void (*cleanup)(void), char *argv[])
{
- fptr cleanup;
- int argc;
- char **argv;
char **env;
const char *s;
-#ifdef __GNUC__
- __asm__("and $0xfffffff0,%esp");
-#endif
- cleanup = get_rtld_cleanup();
- argv = ≈
- argc = *(long *)(void *)(argv - 1);
env = argv + argc + 1;
environ = env;
if (argc > 0 && argv[0] != NULL) {
@@ -110,4 +89,22 @@
exit( main(argc, argv, env) );
}
-__asm__(".ident\t\"$FreeBSD$\"");
+#ifdef __GNUC__
+__asm__(
+ ".text \n\t"
+ ".p2align 4,,15 \n"
+ ".globl _start \n\t"
+ ".type _start, @function\n"
+ "_start: \n\t"
+ "pushl %ebp \n\t"
+ "movl %esp, %ebp\n\t"
+ "andl $0xFFFFFFF0, %esp\n\t" // align stack
+ "movl 4(%ebp), %eax\n\t" // argc
+ /* edx already contains rtld cleanup */
+ "leal 8(%ebp), %ecx\n\t" // argv
+ "call internal_start \n\t"
+ ".size _start, . - _start"
+);
+#endif
+
+__asm__(".ident\t\"$FreeBSD: src/lib/csu/i386-elf/crt1.c,v 1.15 2005/10/07 22:13:17 bde Exp $\"");
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-i386
mailing list