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)