open() is a weird function; treat it with care.

diff --git a/include/klibc/compiler.h b/include/klibc/compiler.h
index 49872b8..6c01474 100644
--- a/include/klibc/compiler.h
+++ b/include/klibc/compiler.h
@@ -8,12 +8,16 @@
 #define _KLIBC_COMPILER_H
 
 /* Specific calling conventions */
+/* __cdecl is used when we want varadic and non-varadic functions to have
+   the same binary calling convention. */
 #ifdef __i386__
 # ifdef __GNUC__
 #  define __cdecl __attribute__((cdecl,regparm(0)))
 # else
   /* Most other C compilers have __cdecl as a keyword */
 # endif
+#else
+# define __cdecl		/* Meaningless on non-i386 */
 #endif
 
 /* How to declare a function that *must* be inlined */
diff --git a/include/unistd.h b/include/unistd.h
index 0b61d1a..13ff03b 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -77,8 +77,8 @@
 
 __extern ssize_t read(int, void *, size_t);
 __extern ssize_t write(int, const void *, size_t);
-#ifndef __IN_SYS_COMMON
-__extern int open(const char *, int, ...);
+#ifndef __IN_OPEN_C
+__extern __cdecl int open(const char *, int, ...);
 #endif
 __extern int close(int);
 __extern off_t lseek(int, off_t, int);
diff --git a/klibc/Makefile b/klibc/Makefile
index 2ddd753..d0689f6 100644
--- a/klibc/Makefile
+++ b/klibc/Makefile
@@ -18,6 +18,7 @@
 	  globals.o exitc.o atexit.o onexit.o \
 	  execl.o execle.o execv.o execvpe.o execvp.o execlp.o execlpe.o \
 	  fork.o wait.o wait3.o waitpid.o system.o setpgrp.o getpgrp.o \
+	  open.o \
 	  printf.o vprintf.o fprintf.o vfprintf.o perror.o \
 	  fopen.o fread.o fread2.o fgetc.o fgets.o \
 	  fwrite.o fwrite2.o fputc.o fputs.o puts.o \
diff --git a/klibc/SYSCALLS b/klibc/SYSCALLS
index 809a364..f206ddf 100644
--- a/klibc/SYSCALLS
+++ b/klibc/SYSCALLS
@@ -91,7 +91,6 @@
 #
 ssize_t read(int, void *, size_t)
 ssize_t write(int, const void *, size_t)
-int open(const char *, int, mode_t)
 int close(int)
 off_t lseek(int, off_t, int)
 int dup(int)
diff --git a/klibc/include/klibc/compiler.h b/klibc/include/klibc/compiler.h
index 49872b8..6c01474 100644
--- a/klibc/include/klibc/compiler.h
+++ b/klibc/include/klibc/compiler.h
@@ -8,12 +8,16 @@
 #define _KLIBC_COMPILER_H
 
 /* Specific calling conventions */
+/* __cdecl is used when we want varadic and non-varadic functions to have
+   the same binary calling convention. */
 #ifdef __i386__
 # ifdef __GNUC__
 #  define __cdecl __attribute__((cdecl,regparm(0)))
 # else
   /* Most other C compilers have __cdecl as a keyword */
 # endif
+#else
+# define __cdecl		/* Meaningless on non-i386 */
 #endif
 
 /* How to declare a function that *must* be inlined */
diff --git a/klibc/include/unistd.h b/klibc/include/unistd.h
index 0b61d1a..13ff03b 100644
--- a/klibc/include/unistd.h
+++ b/klibc/include/unistd.h
@@ -77,8 +77,8 @@
 
 __extern ssize_t read(int, void *, size_t);
 __extern ssize_t write(int, const void *, size_t);
-#ifndef __IN_SYS_COMMON
-__extern int open(const char *, int, ...);
+#ifndef __IN_OPEN_C
+__extern __cdecl int open(const char *, int, ...);
 #endif
 __extern int close(int);
 __extern off_t lseek(int, off_t, int);
diff --git a/klibc/open.c b/klibc/open.c
new file mode 100644
index 0000000..0d7db10
--- /dev/null
+++ b/klibc/open.c
@@ -0,0 +1,17 @@
+/*
+ * open.c
+ *
+ * The open syscall is weird, because it's defined as a varadic
+ * function, but implementing it as such generally sucks for
+ * performance.  Thus we generate it as a 3-argument function,
+ * but with explicit __cdecl assuming the __cdecl convention is
+ * independent of being varadic.
+ */
+
+#define __IN_OPEN_C
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+
+__cdecl _syscall3(int,open,const char *,file,int,flags,mode_t,mode)