From smpatel@xi.dorm.umd.edu  Tue Mar 26 19:27:51 1996
Received: from xi.dorm.umd.edu (xi.dorm.umd.edu [129.2.152.45])
          by freefall.freebsd.org (8.7.3/8.7.3) with ESMTP id TAA10164
          for <FreeBSD-gnats-submit@freebsd.org>; Tue, 26 Mar 1996 19:27:49 -0800 (PST)
Received: (from smpatel@localhost) by xi.dorm.umd.edu (8.7.4/8.6.12) id WAA00525; Tue, 26 Mar 1996 22:27:47 -0500 (EST)
Message-Id: <199603270327.WAA00525@xi.dorm.umd.edu>
Date: Tue, 26 Mar 1996 22:27:47 -0500 (EST)
From: Sujal Patel <smpatel@xi.dorm.umd.edu>
Reply-To: smpatel@xi.dorm.umd.edu
To: FreeBSD-gnats-submit@freebsd.org
Subject: Differentiation of FreeBSD & Linux ELF binaries [patch]
X-Send-Pr-Version: 3.2

>Number:         1102
>Category:       kern
>Synopsis:       Differentiation of FreeBSD & Linux ELF binaries [patch]
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    smpatel
>State:          closed
>Quarter:
>Keywords:
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Tue Mar 26 19:30:03 PST 1996
>Closed-Date:    Sat Oct 19 01:11:17 PDT 1996
>Last-Modified:  Sat Oct 19 01:15:03 PDT 1996
>Originator:     Sujal Patel
>Release:        FreeBSD 2.2-CURRENT i386
>Organization:
Sujal
>Environment:

-current

>Description:

Since the ELF format provides no way to tell the difference between FreeBSD, 
SysV, and Linux ELF binaries, we need to devise a way to set the correct
compatibility sysvec for ELF binaries.  Dynamic bins work fine, but static
ELF binaries are always assumed to be FreeBSD (therefore Linux ELF binaries do
not work in -current).

I've heard / thought of 3 possible fixes:

1.  Use a wrapper program that will pass in the sysvec to use when running a
binary.  I.E.  you'd need to invoke a static ELF binary with something like
"compat -linux linux.helloworld.elf.static".  This is pretty messy IMO.

2.  Use one of the unused bytes in the ELF e_ident to store an "OS Type".
The problem with this is that you'd need to alter a Linux ELF binary to add
the "OS Type".  While this wouldn't break anything, it's very messy.

3.  Follow the "interp" sections hints (current behavior) unless the
environment variable COMPAT_SYSVEC exists.  If the variable exists, it
overrides the sysvec in the "interp" section.  This means that we can now
invoke Linux ELF binaries by:

(export COMPAT_SYSVEC=linux; linux.helloworld.elf.static)


I think solution #3 would be the cleanest way to do this...  The patch below
will implement this solution.  It works great here.

>How-To-Repeat:
>Fix:

This implements solution #3 from above.  Valid values for the COMPAT_SYSVEC
environment variable are currently "freebsd" and "linux".  Don't forget to
recompile the linux LKM since this edits a header file.


diff -ur ./i386/linux/linux_sysvec.c /usr/src/sys/i386/linux/linux_sysvec.c
--- ./i386/linux/linux_sysvec.c	Tue Mar 26 21:58:57 1996
+++ /usr/src/sys/i386/linux/linux_sysvec.c	Tue Mar 26 21:02:37 1996
@@ -405,6 +405,7 @@
 Elf32_Interp_info linux_interp = {
 					&elf_linux_sysvec,
 					"/lib/ld-linux.so.1",
+					"linux",
 					"/compat/linux"
 				 };
 
diff -ur ./kern/imgact_elf.c /usr/src/sys/kern/imgact_elf.c
--- ./kern/imgact_elf.c	Tue Mar 26 22:00:55 1996
+++ /usr/src/sys/kern/imgact_elf.c	Tue Mar 26 21:54:35 1996
@@ -92,6 +92,7 @@
 static Elf32_Interp_info freebsd_interp = {
 						&elf_freebsd_sysvec,
 						"/usr/libexec/ld-elf.so.1",
+						"freebsd",
 						""
 					  };
 static Elf32_Interp_info *interp_list[MAX_INTERP] = {
@@ -475,6 +476,8 @@
 	u_long addr, entry = 0, proghdr = 0;
 	int error, i, header_size = 0, interp_len = 0;
 	char *interp = NULL;
+	char path[MAXPATHLEN];
+	char *sp;
 
 	/*
 	 * Do we have a valid ELF header ?
@@ -608,31 +611,67 @@
 
 	addr = 2*MAXDSIZ; /* May depend on OS type XXX */
 
-	if (interp) {
-		char path[MAXPATHLEN];
+	/*
+	 * See if the user wants to override the sysvec with the sysvec
+	 * in the COMPAT_SYSVEC environment variable
+	 */
+	sp = imgp->stringbase;
+
+	for (i = 0; i < imgp->argc; i++) {
+		while (*sp++); /* Constructed by kernel, should be valid */
+	}
+
+	for (i = 0; i < imgp->envc; i++) {
+		while (*sp++); /* Constructed by kernel, should be valid */
+
+		if (bcmp (sp, "COMPAT_SYSVEC=", 13) == 0) {
+			sp += 14; /* sp points to the osname */
+			break;
+		}
+	}
+
+	if (*sp == NULL)
+		sp = NULL;
+	else
+		UPRINTF ("sysvec %s requested by user\n", sp);
+
+	if (interp || sp) {
 		/* 
 		 * So which kind of ELF binary do we have at hand
 		 * FreeBSD, SVR4 or Linux ??
 		 */
 		for (i=0; i<MAX_INTERP; i++) {
-			if (interp_list[i] != NULL) {
-				if (!strcmp(interp, interp_list[i]->path)) {
-					imgp->proc->p_sysent = 
-						interp_list[i]->sysvec;
-					strcpy(path, interp_list[i]->emul_path);
-					strcat(path, interp_list[i]->path);
-					UPRINTF("interpreter=<%s> %s\n",
-						interp_list[i]->path,
-						interp_list[i]->emul_path);
-					break;
-				}
-			}
+			if (interp_list[i] == NULL)
+				continue;
+
+			/* User's sysvec always overrides the interp section */
+			if (sp && (strcmp(sp, interp_list[i]->osname)))
+				continue;
+
+			if (interp && (strcmp(interp, interp_list[i]->path)))
+				continue;
+
+			imgp->proc->p_sysent = interp_list[i]->sysvec;
+			strcpy(path, interp_list[i]->emul_path);
+			strcat(path, interp_list[i]->path);
+			UPRINTF("interpreter=<%s> %s\n", interp_list[i]->path,
+				interp_list[i]->emul_path);
+			break;
 		}
 		if (i == MAX_INTERP) {
-			uprintf("ELF interpreter %s not known\n", interp);
+			if (sp == 0) {
+				uprintf("ELF interpreter %s not known\n",
+					interp);
+			} else {
+				uprintf("COMPAT_SYSVEC %s not known\n",
+					sp);
+			}
 			error = ENOEXEC;
 			goto fail;
 		}
+	}
+
+	if (interp) {
 		if (error = elf_load_file(imgp->proc,
 					  path,
 				          &addr, 	/* XXX */
diff -ur ./sys/imgact_elf.h /usr/src/sys/sys/imgact_elf.h
--- ./sys/imgact_elf.h	Tue Mar 26 21:59:04 1996
+++ /usr/src/sys/sys/imgact_elf.h	Tue Mar 26 21:01:30 1996
@@ -202,6 +202,7 @@
 typedef struct {
         struct sysentvec *sysvec;
 	char *path;
+	char *osname;		/* OS name for user specified override */
 	char *emul_path;
 } Elf32_Interp_info;
 
>Release-Note:
>Audit-Trail:

From: Sujal Patel <smpatel@wam.umd.edu>
To: Sujal Patel <smpatel@xi.dorm.umd.edu>
Cc: FreeBSD-gnats-submit@freebsd.org
Subject: Re: kern/1102: Differentiation of FreeBSD & Linux ELF binaries [patch]
Date: Wed, 27 Mar 1996 12:01:42 -0500 (EST)

 On Tue, 26 Mar 1996, Sujal Patel wrote:
 
 > >Synopsis:       Differentiation of FreeBSD & Linux ELF binaries [patch]
 >
 > 3.  Follow the "interp" sections hints (current behavior) unless the
 > environment variable COMPAT_SYSVEC exists.  If the variable exists, it
 > overrides the sysvec in the "interp" section.  This means that we can now
 > invoke Linux ELF binaries by:
 >
 > (export COMPAT_SYSVEC=linux; linux.helloworld.elf.static)
 
 Maybe I was a bit too tired when I wrote this patch, but I don't really
 think it's a good idea to let the kernel parse the environment :)
 
 How about instead of that, add an extra argument to the execve() syscall
 (to hold the emulation type requested), and then have libc pass in the
 emulation type requested (by reading the COMPAT_SYSVEC environment
 variable).  Also, the libc exec() and friends can strip out the
 COMPAT_SYSVEC environment variable, so it is not inherited by the
 processes children (which is probably the desired behavior?).
 
 Comments?
 
 
 Sujal
 
Responsible-Changed-From-To: freebsd-bugs->smpatel 
Responsible-Changed-By: smpatel 
Responsible-Changed-When: Sun Apr 14 09:20:49 PDT 1996 
Responsible-Changed-Why:  
This is my PR, and I'm working on a better approach for fixing this. 
State-Changed-From-To: open->closed 
State-Changed-By: smpatel 
State-Changed-When: Sat Oct 19 01:11:17 PDT 1996 
State-Changed-Why:  
Fixed by sos in v1.1 of brandelf.c 
>Unformatted:
