diff --git a/arch/um/Kconfig b/arch/um/Kconfig
index 4b799fad8b48..ec2802ded9d1 100644
--- a/arch/um/Kconfig
+++ b/arch/um/Kconfig
@@ -189,6 +189,16 @@ config UML_TIME_TRAVEL_SUPPORT
 
 	  It is safe to say Y, but you probably don't need this.
 
+config UML_USE_BUILT_IN_STRINGS
+	bool
+	default N
+	prompt "Use kernel memcpy, memmove, etc"
+	help
+	  UML can use the kernel built in memcpy, memmove, etc functions
+	  or use the glibc equivalents instead.
+	  The glibc equivalents are slightly faster and will result in a
+	  slightly smaller executable when linking UML dynamically.
+
 endmenu
 
 source "arch/um/drivers/Kconfig"
diff --git a/arch/um/include/asm/string.h b/arch/um/include/asm/string.h
new file mode 100644
index 000000000000..ac16890e0867
--- /dev/null
+++ b/arch/um/include/asm/string.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (C) 2020 Cambridge Greys Ltd.
+ * Copyright (C) 2020 Red Hat Inc.
+ */
+
+#ifndef __ASM_UM_STRING_H
+#define __ASM_UM_STRING_H
+
+#ifndef CONFIG_UML_USE_BUILT_IN_STRINGS
+
+#include <stddef.h>
+
+#define __HAVE_ARCH_STRCPY
+extern char *strcpy(char *dest, const char *src);
+#define __HAVE_ARCH_STRNCPY
+extern char *strncpy(char *dest, const char *src, size_t n);
+#define __HAVE_ARCH_STRCAT
+extern char *strcat(char *dest, const char *src);
+#define __HAVE_ARCH_STRNCAT
+extern char *strncat(char *dest, const char *src, size_t n);
+#define __HAVE_ARCH_STRCMP
+extern int strcmp(const char *s1, const char *s2);
+#define __HAVE_ARCH_STRNCMP
+extern int strncmp(const char *s1, const char *s2, size_t n);
+#define __HAVE_ARCH_STRCHR
+extern char *strchr(const char *s, int c);
+#define __HAVE_ARCH_STRLEN
+extern size_t strlen(const char *s);
+#define __HAVE_ARCH_MEMCPY
+extern void *memcpy(void *dest, const void *src, size_t n);
+#define __HAVE_ARCH_MEMMOVE
+extern void *memmove(void *dest, const void *src, size_t n);
+#define __HAVE_ARCH_MEMCHR
+extern void *memchr(const void *s, int c, size_t n);
+#define __HAVE_ARCH_STRNLEN
+extern size_t strnlen(const char *s, size_t maxlen);
+#define __HAVE_ARCH_STRSTR
+extern char *strstr(const char *haystack, const char *needle);
+#define __HAVE_ARCH_MEMSET
+extern void *memset(void *s, int c, size_t n);
+#define __HAVE_ARCH_STRNCASECMP
+extern int strncasecmp(const char *s1, const char *s2, size_t n);
+#define __HAVE_ARCH_STRCASECMP
+extern int strcasecmp(const char *s1, const char *s2);
+#define __HAVE_ARCH_STRCHRNUL
+extern char *strchrnul(const char *s, int c);
+#define __HAVE_ARCH_STRRCH
+extern char *strchrnul(const char *s, int c);
+#define __HAVE_ARCH_STRSPN
+extern size_t strspn(const char *s, const char *accept);
+#define __HAVE_ARCH_STRCSPN
+extern size_t strcspn(const char *s, const char *reject);
+#define __HAVE_ARCH_STRPBRK
+extern char *strpbrk(const char *s, const char *accept);
+#define __HAVE_ARCH_STRSEP
+extern char *strsep(char **stringp, const char *delim);
+#define __HAVE_ARCH_MEMCMP
+extern int memcmp(const void *s1, const void *s2, size_t n);
+#define __HAVE_ARCH_BCMP
+extern int bcmp(const void *s1, const void *s2, size_t n);
+
+#else
+
+#include <asm-generic/string.h>
+
+#endif
+
+#endif /* __ASM_UM_STRING_H */
diff --git a/arch/um/os-Linux/user_syms.c b/arch/um/os-Linux/user_syms.c
index 715594fe5719..9da980a89a9f 100644
--- a/arch/um/os-Linux/user_syms.c
+++ b/arch/um/os-Linux/user_syms.c
@@ -9,6 +9,7 @@
  * add an EXPORT for the glibc one.
  */
 
+#ifdef CONFIG_UML_USE_BUILT_IN_STRINGS
 #undef strlen
 #undef strstr
 #undef memcpy
@@ -17,6 +18,7 @@
 extern size_t strlen(const char *);
 extern void *memmove(void *, const void *, size_t);
 extern void *memset(void *, int, size_t);
+#endif
 extern int printf(const char *, ...);
 
 /* If it's not defined, the export is included in lib/string.c.*/
@@ -24,7 +26,7 @@ extern int printf(const char *, ...);
 EXPORT_SYMBOL(strstr);
 #endif
 
-#ifndef __x86_64__
+#if !defined(__x86_64__) || !defined(CONFIG_UML_USE_BUILT_IN_STRINGS)
 extern void *memcpy(void *, const void *, size_t);
 EXPORT_SYMBOL(memcpy);
 #endif
@@ -33,6 +35,29 @@ EXPORT_SYMBOL(memmove);
 EXPORT_SYMBOL(memset);
 EXPORT_SYMBOL(printf);
 
+#ifndef CONFIG_UML_USE_BUILT_IN_STRINGS
+/* These all come from glibc */
+EXPORT_SYMBOL(strcpy);
+EXPORT_SYMBOL(strncpy);
+EXPORT_SYMBOL(strcat);
+EXPORT_SYMBOL(strncat);
+EXPORT_SYMBOL(strcmp);
+EXPORT_SYMBOL(strncmp);
+EXPORT_SYMBOL(strchr);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(memchr);
+EXPORT_SYMBOL(strnlen);
+EXPORT_SYMBOL(strncasecmp);
+EXPORT_SYMBOL(strcasecmp);
+EXPORT_SYMBOL(strchrnul);
+EXPORT_SYMBOL(strcspn);
+EXPORT_SYMBOL(strpbrk);
+EXPORT_SYMBOL(strsep);
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(bcmp);
+
+#endif
+
 /* Here, instead, I can provide a fake prototype. Yes, someone cares: genksyms.
  * However, the modules will use the CRC defined *here*, no matter if it is
  * good; so the versions of these symbols will always match
diff --git a/arch/x86/um/Makefile b/arch/x86/um/Makefile
index 77f70b969d14..453ea23a9770 100644
--- a/arch/x86/um/Makefile
+++ b/arch/x86/um/Makefile
@@ -20,14 +20,16 @@ ifeq ($(CONFIG_X86_32),y)
 obj-y += checksum_32.o syscalls_32.o
 obj-$(CONFIG_ELF_CORE) += elfcore.o
 
-subarch-y = ../lib/string_32.o ../lib/atomic64_32.o ../lib/atomic64_cx8_32.o
+subarch-y = ../lib/atomic64_32.o ../lib/atomic64_cx8_32.o
 subarch-y += ../kernel/sys_ia32.o
+subarch-$(CONFIG_UML_USE_BUILT_IN_STRINGS) += ../lib/string_32.o
 
 else
 
 obj-y += syscalls_64.o vdso/
 
-subarch-y = ../lib/csum-partial_64.o ../lib/memcpy_64.o ../entry/thunk_64.o
+subarch-y = ../lib/csum-partial_64.o ../entry/thunk_64.o
+subarch-$(CONFIG_UML_USE_BUILT_IN_STRINGS) += ../lib/memcpy_64.o
 
 endif
 
