diff --git a/elf/dl-object.c b/elf/dl-object.c
index d2cdf135cc..717fb253bb 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -202,38 +202,30 @@ _dl_new_object (char *realname, const char *libname, int type,
 	}
       else
 	{
-	  size_t len = realname_len;
-	  char *result = NULL;
-
-	  /* Get the current directory name.  */
-	  origin = NULL;
-	  do
+	  /* The rtld __getcwd implementation does not handle paths larger
+	     than PATH_MAX (which would be invalid to be used on subsequent
+	     open calls).  */
+	  origin = __getcwd (NULL, 0);
+	  if (origin == NULL)
 	    {
-	      char *new_origin;
-
-	      len += 128;
-	      new_origin = (char *) realloc (origin, len);
-	      if (new_origin == NULL)
-		/* We exit the loop.  Note that result == NULL.  */
-		break;
-	      origin = new_origin;
+	      origin = (char *) -1;
+	      goto out;
 	    }
-	  while ((result = __getcwd (origin, len - realname_len)) == NULL
-		 && errno == ERANGE);
+	  size_t len = strlen (origin);
+	  char *result = realloc (origin, len + realname_len);
 
 	  if (result == NULL)
 	    {
-	      /* We were not able to determine the current directory.
-		 Note that free(origin) is OK if origin == NULL.  */
 	      free (origin);
 	      origin = (char *) -1;
 	      goto out;
 	    }
+	  origin = result;
+	  cp = origin + len;
 
 	  /* Find the end of the path and see whether we have to add a
 	     slash.  We could use rawmemchr but this need not be
 	     fast.  */
-	  cp = (strchr) (origin, '\0');
 	  if (cp[-1] != '/')
 	    *cp++ = '/';
 	}
diff --git a/sysdeps/unix/sysv/linux/getcwd.c b/sysdeps/unix/sysv/linux/getcwd.c
index c5886f5283..5ef0b092ec 100644
--- a/sysdeps/unix/sysv/linux/getcwd.c
+++ b/sysdeps/unix/sysv/linux/getcwd.c
@@ -17,115 +17,65 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
-#include <assert.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
-#include <sys/param.h>
 
 #include <sysdep.h>
 #include <sys/syscall.h>
 
-
-/* If we compile the file for use in ld.so we don't need the feature
-   that getcwd() allocates the buffers itself.  */
-#if IS_IN (rtld)
-# define NO_ALLOCATION	1
-#endif
-
-
-/* The "proc" filesystem provides an easy method to retrieve the value.
-   For each process, the corresponding directory contains a symbolic link
-   named `cwd'.  Reading the content of this link immediate gives us the
-   information.  But we have to take care for systems which do not have
-   the proc filesystem mounted.  Use the POSIX implementation in this case.  */
-
-/* Get the code for the generic version.  */
+#if !IS_IN (rtld)
+/* The generic implementation is used for valid paths larger than
+   PATH_MAX (where the syscall returns a positive value with errno
+   set to ENAMETOOLONG).  */
 #define GETCWD_RETURN_TYPE	static char *
 #include <sysdeps/posix/getcwd.c>
+#endif
 
 char *
 __getcwd (char *buf, size_t size)
 {
-  char *path;
-  char *result;
+  char *r = NULL;
+  int len;
 
-#ifndef NO_ALLOCATION
-  size_t alloc_size = size;
-  if (size == 0)
+  if (buf != NULL)
     {
-      if (buf != NULL)
+      if (size == 0)
 	{
 	  __set_errno (EINVAL);
 	  return NULL;
-	}
 
-      alloc_size = MAX (PATH_MAX, __getpagesize ());
-    }
-
-  if (buf == NULL)
-    {
-      path = malloc (alloc_size);
-      if (path == NULL)
-	return NULL;
+	}
+      len = INLINE_SYSCALL_CALL (getcwd, buf, size);
+      if (len > 0)
+	r = buf;
     }
   else
-#else
-# define alloc_size size
-#endif
-    path = buf;
-
-  int retval;
-
-  retval = INLINE_SYSCALL (getcwd, 2, path, alloc_size);
-  if (retval > 0 && path[0] == '/')
-    {
-#ifndef NO_ALLOCATION
-      if (buf == NULL && size == 0)
-	/* Ensure that the buffer is only as large as necessary.  */
-	buf = realloc (path, (size_t) retval);
-
-      if (buf == NULL)
-	/* Either buf was NULL all along, or `realloc' failed but
-	   we still have the original string.  */
-	buf = path;
-#endif
-
-      return buf;
-    }
-
-  /* The system call either cannot handle paths longer than a page
-     or can succeed without returning an absolute path.  Just use the
-     generic implementation right away.  */
-  if (retval >= 0 || errno == ENAMETOOLONG)
     {
-#ifndef NO_ALLOCATION
-      if (buf == NULL && size == 0)
+      char tmp[PATH_MAX];
+      size_t tmps = size == 0 || size > PATH_MAX
+		    ? PATH_MAX : size;
+      len = INLINE_SYSCALL_CALL (getcwd, tmp, tmps);
+      if (len > 0)
 	{
-	  free (path);
-	  path = NULL;
-	}
-#endif
-
-      result = __getcwd_generic (path, size);
+	  /* Allocates a buffer with at least SIZE bytes, even it is larger
+	     than required buffer.  */
+	  r = malloc (size > len ? size : len);
+	  if (r == NULL)
+	    return NULL;
 
-#ifndef NO_ALLOCATION
-      if (result == NULL && buf == NULL && size != 0)
-	free (path);
-#endif
-
-      return result;
+	  memcpy (r, tmp, len);
+	}
     }
 
-  /* It should never happen that the `getcwd' syscall failed because
-     the buffer is too small if we allocated the buffer ourselves
-     large enough.  */
-  assert (errno != ERANGE || buf != NULL || size != 0);
+  if (len > 0 && r[0] == '/')
+    return r;
 
-#ifndef NO_ALLOCATION
-  if (buf == NULL)
-    free (path);
+#if !IS_IN (rtld)
+  if (len >=0 || errno == ENAMETOOLONG)
+    return __getcwd_generic (buf, size);
 #endif
 
   return NULL;
