From nobody@FreeBSD.org  Sun Sep 14 19:56:42 2008
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id B4E3E1065679
	for <freebsd-gnats-submit@FreeBSD.org>; Sun, 14 Sep 2008 19:56:42 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21])
	by mx1.freebsd.org (Postfix) with ESMTP id A090D8FC19
	for <freebsd-gnats-submit@FreeBSD.org>; Sun, 14 Sep 2008 19:56:42 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (localhost [127.0.0.1])
	by www.freebsd.org (8.14.2/8.14.2) with ESMTP id m8EJugOg068064
	for <freebsd-gnats-submit@FreeBSD.org>; Sun, 14 Sep 2008 19:56:42 GMT
	(envelope-from nobody@www.freebsd.org)
Received: (from nobody@localhost)
	by www.freebsd.org (8.14.2/8.14.1/Submit) id m8EJug2W068063;
	Sun, 14 Sep 2008 19:56:42 GMT
	(envelope-from nobody)
Message-Id: <200809141956.m8EJug2W068063@www.freebsd.org>
Date: Sun, 14 Sep 2008 19:56:42 GMT
From: Christoph Mallon <christoph.mallon@gmx.de>
To: freebsd-gnats-submit@FreeBSD.org
Subject: Inline assembler in x86 _start() in crt1.c only works by chance
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         127387
>Category:       i386
>Synopsis:       [i386] [patch] Inline assembler in x86 _start() in crt1.c only works by chance
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    kib
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Sep 14 20:00:10 UTC 2008
>Closed-Date:    Mon Mar 15 11:31:52 UTC 2010
>Last-Modified:  Mon Mar 15 11:31:52 UTC 2010
>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 = &ap;
-	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:
Responsible-Changed-From-To: freebsd-i386->kib 
Responsible-Changed-By: rwatson 
Responsible-Changed-When: Fri Oct 31 15:07:23 UTC 2008 
Responsible-Changed-Why:  
Assign to Kostik, who has expressed interest. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=127387 
State-Changed-From-To: open->closed 
State-Changed-By: kib 
State-Changed-When: Mon Mar 15 11:31:21 UTC 2010 
State-Changed-Why:  
Different patch was committed to head and stable/8. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=127387 
>Unformatted:
