diff --git a/src/unistd/getcwd.c b/src/unistd/getcwd.c
index 103fbbb5..306dbc4f 100644
--- a/src/unistd/getcwd.c
+++ b/src/unistd/getcwd.c
@@ -3,17 +3,10 @@
 #include <limits.h>
 #include <string.h>
 #include "syscall.h"
+#include "libc.h"
 
-char *getcwd(char *buf, size_t size)
+static char *do_getcwd(char *buf, size_t size)
 {
-	char tmp[PATH_MAX];
-	if (!buf) {
-		buf = tmp;
-		size = PATH_MAX;
-	} else if (!size) {
-		errno = EINVAL;
-		return 0;
-	}
 	long ret = syscall(SYS_getcwd, buf, size);
 	if (ret < 0)
 		return 0;
@@ -21,5 +14,37 @@ char *getcwd(char *buf, size_t size)
 		errno = ENOENT;
 		return 0;
 	}
-	return buf == tmp ? strdup(buf) : buf;
+	return buf;
+}
+
+static char *getcwd_glibc(size_t size)
+{
+	char tmp[PATH_MAX];
+	if (!do_getcwd(tmp, sizeof tmp))
+		return 0;
+	size_t len = strlen(tmp) + 1;
+	if (!size)
+		size = len;
+	else if (size < len) {
+		errno = ERANGE;
+		return 0;
+	}
+	char *buf = malloc(size);
+	if (!buf) {
+		errno = ENOMEM;
+		return 0;
+	}
+	memcpy(buf, tmp, len);
+	return buf;
+}
+
+char *getcwd(char *buf, size_t size)
+{
+	if (!buf)
+		return getcwd_glibc(size);
+	if (!size) {
+		errno = EINVAL;
+		return 0;
+	}
+	return do_getcwd(buf, size);
 }