Export freestanding symbols as unmangled.

Mangling these is painful for the compiler and we've missed at least one
place where it needs to happen.  We don't mangle the atomics, so it's a
good idea to also not mangle the freestanding ones.

As a transitional measure, export the functions as both their mangled
and unmangled forms.

The next step is to make the mangled form the compatibility definition
and use `asm` in the header to export these as non-mangled by default
for direct calls, then finally to remove `compat.S`.

Once this is merged, the compiler is free to start emitting `mem*` as
libcalls without name mangling.

Fixes #368
diff --git a/sdk/include/string.h b/sdk/include/string.h
index 965a02b..2dd75f1 100644
--- a/sdk/include/string.h
+++ b/sdk/include/string.h
@@ -6,21 +6,27 @@
 #include <stddef.h>
 #include <stdint.h>
 
-int __cheri_libcall   memcmp(const void *str1, const void *str2, size_t count);
-void *__cheri_libcall memcpy(void *dest, const void *src, size_t n);
-void *__cheri_libcall memset(void *, int, size_t);
-void *__cheri_libcall memmove(void *dest, const void *src, size_t n);
-void *__cheri_libcall memchr(const void *, int, size_t);
-void *__cheri_libcall memrchr(const void *, int, size_t);
-size_t __cheri_libcall      strlen(const char *str);
-int __cheri_libcall         strncmp(const char *s1, const char *s2, size_t n);
-char *__cheri_libcall       strncpy(char *dest, const char *src, size_t n);
-int __cheri_libcall         strcmp(const char *s1, const char *s2);
-char *__cheri_libcall       strnstr(const char *haystack,
-                                    const char *needle,
-                                    size_t      haystackLength);
-char *__cheri_libcall       strchr(const char *s, int c);
-size_t __cheri_libcall      strlcpy(char *dest, const char *src, size_t n);
+int __cheri_libcall    memcmp(const void *str1,
+                              const void *str2,
+                              size_t      count) __asm__("memcmp");
+void *__cheri_libcall  memcpy(void       *dest,
+                              const void *src,
+                              size_t      n) __asm__("memcpy");
+void *__cheri_libcall  memset(void *, int, size_t) __asm__("memset");
+void *__cheri_libcall  memmove(void       *dest,
+                               const void *src,
+                               size_t      n) __asm__("memmove");
+void *__cheri_libcall  memchr(const void *, int, size_t);
+void *__cheri_libcall  memrchr(const void *, int, size_t);
+size_t __cheri_libcall strlen(const char *str);
+int __cheri_libcall    strncmp(const char *s1, const char *s2, size_t n);
+char *__cheri_libcall  strncpy(char *dest, const char *src, size_t n);
+int __cheri_libcall    strcmp(const char *s1, const char *s2);
+char *__cheri_libcall  strnstr(const char *haystack,
+                               const char *needle,
+                               size_t      haystackLength);
+char *__cheri_libcall  strchr(const char *s, int c);
+size_t __cheri_libcall strlcpy(char *dest, const char *src, size_t n);
 
 /**
  * Explicit bzero is a memset variant that the compiler is not permitted to
diff --git a/sdk/lib/freestanding/compat.S b/sdk/lib/freestanding/compat.S
new file mode 100644
index 0000000..b6b90c7
--- /dev/null
+++ b/sdk/lib/freestanding/compat.S
@@ -0,0 +1,31 @@
+// Copyright CHERIoT Contributors.
+// SPDX-License-Identifier: MIT
+
+/**
+ * This file contains compatibility aliases for exporting the freestanding
+ * library functions as both mangled and unmangled symbols.
+ */
+
+/**
+ * Given a function named `function_name`, export it as a library function
+ * named `export_function_name`.
+ */
+.macro EXPORT_COMPATIBILITY_ALIAS export_function_name, function_name, flags
+	.section .compartment_exports,"aR",@progbits
+	.type    __library_export_libcalls_\export_function_name\(),@object
+	.global  __library_export_libcalls_\export_function_name\()
+    .p2align 2
+  __library_export_libcalls_\export_function_name\():
+	.half \function_name - __compartment_pcc_start
+	// Stack usage: Ignored for library exports
+	.byte 0
+	// Flags, only interrupt state is used for library exports, 0 is inherited
+	.byte \flags
+	.size __library_export_libcalls_\export_function_name, 40
+	.previous
+.endm
+
+EXPORT_COMPATIBILITY_ALIAS _Z6memcmpPKvS0_j, memcmp, 3
+EXPORT_COMPATIBILITY_ALIAS _Z6memcpyPvPKvj, memcpy, 3
+EXPORT_COMPATIBILITY_ALIAS _Z6memsetPvij, memset, 2
+EXPORT_COMPATIBILITY_ALIAS _Z7memmovePvPKvj, memmove, 3
diff --git a/sdk/lib/freestanding/memcmp.c b/sdk/lib/freestanding/memcmp.c
index 8f3eef9..9a3a518 100644
--- a/sdk/lib/freestanding/memcmp.c
+++ b/sdk/lib/freestanding/memcmp.c
@@ -34,11 +34,12 @@
 
 #include <cdefs.h>
 #include <stddef.h>
+#include <string.h>
 
 /*
  * Compare memory regions.
  */
-int __cheri_libcall
+int
 memcmp(const void *s1, const void *s2, size_t n)
 {
 	if (n != 0) {
diff --git a/sdk/lib/freestanding/memcpy.c b/sdk/lib/freestanding/memcpy.c
index 5b7c791..80e880a 100644
--- a/sdk/lib/freestanding/memcpy.c
+++ b/sdk/lib/freestanding/memcpy.c
@@ -45,6 +45,7 @@
 
 #include <cdefs.h>
 #include <stddef.h>
+#include <string.h>
 
 typedef void *word;
 
@@ -54,7 +55,7 @@
 
 #define wmask (wsize - 1)
 
-void *__cheri_libcall memcpy(void *dst0, const void *src0, size_t length)
+void *memcpy(void *dst0, const void *src0, size_t length)
 {
 	char *      dst = dst0;
 	const char *src = src0;
@@ -119,7 +120,7 @@
 	return (dst0);
 }
 
-void *__cheri_libcall memmove(void *dst0, const void *src0, size_t length)
+void *memmove(void *dst0, const void *src0, size_t length)
 {
 	return memcpy(dst0, src0, length);
 }
diff --git a/sdk/lib/freestanding/memset.c b/sdk/lib/freestanding/memset.c
index c9e4c0a..8945d5c 100644
--- a/sdk/lib/freestanding/memset.c
+++ b/sdk/lib/freestanding/memset.c
@@ -34,11 +34,12 @@
 
 #include <cdefs.h>
 #include <stddef.h>
+#include <string.h>
 
 #define wsize sizeof(unsigned int)
 #define wmask (wsize - 1)
 
-void *__cheri_libcall memset(void *dst0, int c0, size_t length)
+void *memset(void *dst0, int c0, size_t length)
 {
 	size_t         t;
 	unsigned int   c;
diff --git a/sdk/lib/freestanding/xmake.lua b/sdk/lib/freestanding/xmake.lua
index 62f10fb..6daf6c5 100644
--- a/sdk/lib/freestanding/xmake.lua
+++ b/sdk/lib/freestanding/xmake.lua
@@ -1,2 +1,2 @@
 library("freestanding")
-  add_files("memcmp.c", "memcpy.c", "memset.c")
+  add_files("memcmp.c", "memcpy.c", "memset.c", "compat.S")
diff --git a/tests/misc-test.cc b/tests/misc-test.cc
index dc09d77..2de724a 100644
--- a/tests/misc-test.cc
+++ b/tests/misc-test.cc
@@ -10,211 +10,235 @@
 
 using namespace CHERI;
 
-/**
- * Test timeouts.
- *
- * This test checks the following:
- *
- * - A timeout of zero would not block.
- * - `elapse` saturates values, i.e., a `remaining` value of zero will still be
- *   zero after a call to `elapse`, and an `elapsed` value of `UINT32_MAX`
- *   would still be `UINT32_MAX` after a call to `elapse`.
- * - An unlimited timeout is really unlimited, i.e., a call to `elapse` does
- *   not modify its `remaining` value, which blocks.
- */
-void check_timeouts()
+namespace
 {
-	debug_log("Test timeouts.");
 
-	// Create a zero timeout.
-	Timeout t{0};
-	// Ensure that a zero timeout does not block.
-	TEST(!t.may_block(), "A zero timeout should not block.");
-
-	// Create a zero timer with maximum elapsed time.
-	t = Timeout{UINT32_MAX /* elapsed */, 0 /* remaining */};
-	// Ensure that a call to `elapse` saturates both `elapsed` and
-	// `remaining`.
-	t.elapse(42);
-	TEST(t.remaining == 0,
-	     "`elapse` does not saturate the `remaining` value of a zero timer.");
-	TEST(t.elapsed == UINT32_MAX,
-	     "`elapse` does not saturate the `elapsed` value of a zero timer.");
-
-	// Create an unlimited timeout.
-	t = Timeout{UnlimitedTimeout /* remaining */};
-	// Ensure that a call to `elapse` does not modify the `remaining` value
-	// of the unlimited timeout.
-	t.elapse(42);
-	TEST(t.remaining == UnlimitedTimeout,
-	     "`elapse` alters the remaining value of an unlimited timeout.");
-	// Ensure that an unlimited timeout blocks.
-	TEST(t.may_block(), "An unlimited timeout should block.");
-}
-
-/**
- * Test memchr.
- *
- * This test checks the following:
- *
- * - memchr finds the first occurrence of the character when it is present
- *   (test for different values, particularly the first and the last one).
- * - memchr returns NULL when the string does not contain the character (test
- *   for non-NULL terminated string).
- * - memchr does not stop at \0 characters.
- * - memchr returns NULL for 0-size pointers.
- */
-void check_memchr()
-{
-	debug_log("Test memchr.");
-
-	char string[] = {'C', 'H', 'E', 'R', 'R', 'I', 'E', 'S'};
-
-	TEST(memchr(string, 'C', sizeof(string)) == &string[0],
-	     "memchr must return the first occurence of the character.");
-	TEST(memchr(string, 'R', sizeof(string)) == &string[3],
-	     "memchr must return the first occurence of the character.");
-	TEST(memchr(string, 'S', sizeof(string)) == &string[7],
-	     "memchr must return the first occurence of the character.");
-	TEST(memchr(string, 'X', sizeof(string)) == NULL,
-	     "memchr must return NULL when a character is not present.");
-
-	char stringWithNull[] = {'Y', 'E', 'S', '\0', 'N', 'O', '\0'};
-
-	TEST(memchr(stringWithNull, 'N', sizeof(stringWithNull)) ==
-	       &stringWithNull[4],
-	     "memchr must not stop at NULL characters.");
-
-	TEST(memchr(stringWithNull, 'N', 0) == NULL,
-	     "memchr must return NULL for zero-size pointers.");
-}
-
-/**
- * Test memrchr.
- *
- * This test checks the following:
- *
- * - memrchr finds the first occurrence of the character when it is present
- *   (test for different values, particularly the first and the last one).
- * - memrchr returns NULL when the string does not contain the character (test
- *   for non-NULL terminated string).
- * - memrchr does not stop at \0 characters.
- * - memrchr returns NULL for 0-size pointers.
- */
-void check_memrchr()
-{
-	debug_log("Test memrchr.");
-
-	char string[] = {'C', 'H', 'E', 'R', 'R', 'I', 'O', 'T'};
-
-	TEST(memchr(string, 'C', sizeof(string)) == &string[0],
-	     "memrchr must return the first occurence of the character.");
-	TEST(memrchr(string, 'R', sizeof(string)) == &string[4],
-	     "memrchr must return the first occurence of the character.");
-	TEST(memrchr(string, 'T', sizeof(string)) == &string[7],
-	     "memrchr must return the first occurence of the character.");
-	TEST(memrchr(string, 'X', sizeof(string)) == NULL,
-	     "memrchr must return NULL when a character is not present.");
-
-	char stringWithNull[] = {'F', 'U', '\0', 'B', 'A', 'R', '\0'};
-
-	TEST(memrchr(stringWithNull, 'F', sizeof(stringWithNull)) ==
-	       &stringWithNull[0],
-	     "memrchr must not stop at NULL characters.");
-
-	TEST(memrchr(stringWithNull, 'Y', 0) == NULL,
-	     "memrchr must return NULL for zero-size pointers.");
-}
-
-/**
- * Test pointer utilities.
- *
- * Not comprehensive, would benefit from being expanded at some point.
- */
-void check_pointer_utilities()
-{
-	debug_log("Test pointer utilities.");
-
-	int                              integer        = 42;
-	int                             *integerPointer = &integer;
-	ds::pointer::proxy::Pointer<int> pointer{integerPointer};
-
-	TEST((pointer == integerPointer) && (*pointer == 42),
-	     "The pointer proxy does not return the value of its proxy.");
-
-	int                              anotherInteger        = -100;
-	int                             *anotherIntegerPointer = &anotherInteger;
-	ds::pointer::proxy::Pointer<int> anotherPointer{anotherIntegerPointer};
-
-	pointer = anotherPointer;
-
-	TEST((pointer == anotherIntegerPointer) && (*pointer == -100),
-	     "The pointer proxy `=` operator does not correctly set the pointer.");
-}
-
-void check_shared_object(const char      *name,
-                         Capability<void> object,
-                         size_t           size,
-                         PermissionSet    permissions)
-{
-	debug_log("Checking shared object {}.", object);
-	TEST(object.length() == size,
-	     "Object {} is {} bytes, expected {}",
-	     name,
-	     object.length(),
-	     size);
-	TEST(object.permissions() == permissions,
-	     "Object {} has permissions {}, expected {}",
-	     name,
-	     PermissionSet{object.permissions()},
-	     permissions);
-}
-
-// This test is somewhat intimately familiar with parameters of CHERIoT's
-// capability encoding and so might need revision if that changes.
-void check_capability_set_inexact_at_most()
-{
-	void *p = malloc(3128);
-
-	debug_log("Test Capability::BoundsProxy::set_inexact_at_most with {}", p);
-
-	// Too many bits for mantissa, regardless of base alignment
+	/**
+	 * Test timeouts.
+	 *
+	 * This test checks the following:
+	 *
+	 * - A timeout of zero would not block.
+	 * - `elapse` saturates values, i.e., a `remaining` value of zero will still
+	 * be zero after a call to `elapse`, and an `elapsed` value of `UINT32_MAX`
+	 *   would still be `UINT32_MAX` after a call to `elapse`.
+	 * - An unlimited timeout is really unlimited, i.e., a call to `elapse` does
+	 *   not modify its `remaining` value, which blocks.
+	 */
+	void check_timeouts()
 	{
-		Capability<void> q      = {p};
-		size_t           reqlen = 2047;
-		q.bounds().set_inexact_at_most(reqlen);
-		debug_log("Requesting 2047 gives {}: {}", q.length(), q);
-		TEST(q.is_valid(), "set_inexact_at_most untagged");
-		TEST(q.length() < 2047, "set_inexact_at_most failed to truncate");
-		TEST(q.base() == q.address(), "set_inexact_at_most nonzero offset");
+		debug_log("Test timeouts.");
+
+		// Create a zero timeout.
+		Timeout t{0};
+		// Ensure that a zero timeout does not block.
+		TEST(!t.may_block(), "A zero timeout should not block.");
+
+		// Create a zero timer with maximum elapsed time.
+		t = Timeout{UINT32_MAX /* elapsed */, 0 /* remaining */};
+		// Ensure that a call to `elapse` saturates both `elapsed` and
+		// `remaining`.
+		t.elapse(42);
+		TEST(
+		  t.remaining == 0,
+		  "`elapse` does not saturate the `remaining` value of a zero timer.");
+		TEST(t.elapsed == UINT32_MAX,
+		     "`elapse` does not saturate the `elapsed` value of a zero timer.");
+
+		// Create an unlimited timeout.
+		t = Timeout{UnlimitedTimeout /* remaining */};
+		// Ensure that a call to `elapse` does not modify the `remaining` value
+		// of the unlimited timeout.
+		t.elapse(42);
+		TEST(t.remaining == UnlimitedTimeout,
+		     "`elapse` alters the remaining value of an unlimited timeout.");
+		// Ensure that an unlimited timeout blocks.
+		TEST(t.may_block(), "An unlimited timeout should block.");
 	}
 
-	// Fits in mantissa, but not reachable from misaligned base
+	/**
+	 * Test memchr.
+	 *
+	 * This test checks the following:
+	 *
+	 * - memchr finds the first occurrence of the character when it is present
+	 *   (test for different values, particularly the first and the last one).
+	 * - memchr returns NULL when the string does not contain the character
+	 * (test for non-NULL terminated string).
+	 * - memchr does not stop at \0 characters.
+	 * - memchr returns NULL for 0-size pointers.
+	 */
+	void check_memchr()
 	{
-		Capability<void> q = {p};
-		q.address() += 2;
-		size_t reqlen = 1024;
-		q.bounds().set_inexact_at_most(reqlen);
-		debug_log("Requesting 1024 at align 2 gives {}: {}", q.length(), q);
-		TEST(q.is_valid(), "set_inexact_at_most untagged");
-		TEST(q.length() < 1024, "set_inexact_at_most failed to truncate");
-		TEST(q.base() == q.address(), "set_inexact_at_most nonzero offset");
+		debug_log("Test memchr.");
+
+		char string[] = {'C', 'H', 'E', 'R', 'R', 'I', 'E', 'S'};
+
+		TEST(memchr(string, 'C', sizeof(string)) == &string[0],
+		     "memchr must return the first occurence of the character.");
+		TEST(memchr(string, 'R', sizeof(string)) == &string[3],
+		     "memchr must return the first occurence of the character.");
+		TEST(memchr(string, 'S', sizeof(string)) == &string[7],
+		     "memchr must return the first occurence of the character.");
+		TEST(memchr(string, 'X', sizeof(string)) == NULL,
+		     "memchr must return NULL when a character is not present.");
+
+		char stringWithNull[] = {'Y', 'E', 'S', '\0', 'N', 'O', '\0'};
+
+		TEST(memchr(stringWithNull, 'N', sizeof(stringWithNull)) ==
+		       &stringWithNull[4],
+		     "memchr must not stop at NULL characters.");
+
+		TEST(memchr(stringWithNull, 'N', 0) == NULL,
+		     "memchr must return NULL for zero-size pointers.");
 	}
 
-	// Fits in mantissa and reachable from misaligned base
+	/**
+	 * Test memrchr.
+	 *
+	 * This test checks the following:
+	 *
+	 * - memrchr finds the first occurrence of the character when it is present
+	 *   (test for different values, particularly the first and the last one).
+	 * - memrchr returns NULL when the string does not contain the character
+	 * (test for non-NULL terminated string).
+	 * - memrchr does not stop at \0 characters.
+	 * - memrchr returns NULL for 0-size pointers.
+	 */
+	void check_memrchr()
 	{
-		Capability<void> q = {p};
-		q.address() += 1;
-		size_t reqlen = 511;
-		q.bounds().set_inexact_at_most(reqlen);
-		debug_log("Requesting 511 at align 1 gives {}: {}", q.length(), q);
-		TEST(q.is_valid(), "set_inexact_at_most untagged");
-		TEST(q.length() == 511, "set_inexact_at_most truncated unnecessarily");
-		TEST(q.base() == q.address(), "set_inexact_at_most nonzero offset");
+		debug_log("Test memrchr.");
+
+		char string[] = {'C', 'H', 'E', 'R', 'R', 'I', 'O', 'T'};
+
+		TEST(memchr(string, 'C', sizeof(string)) == &string[0],
+		     "memrchr must return the first occurence of the character.");
+		TEST(memrchr(string, 'R', sizeof(string)) == &string[4],
+		     "memrchr must return the first occurence of the character.");
+		TEST(memrchr(string, 'T', sizeof(string)) == &string[7],
+		     "memrchr must return the first occurence of the character.");
+		TEST(memrchr(string, 'X', sizeof(string)) == NULL,
+		     "memrchr must return NULL when a character is not present.");
+
+		char stringWithNull[] = {'F', 'U', '\0', 'B', 'A', 'R', '\0'};
+
+		TEST(memrchr(stringWithNull, 'F', sizeof(stringWithNull)) ==
+		       &stringWithNull[0],
+		     "memrchr must not stop at NULL characters.");
+
+		TEST(memrchr(stringWithNull, 'Y', 0) == NULL,
+		     "memrchr must return NULL for zero-size pointers.");
 	}
 
-	free(p);
-}
+	/**
+	 * Test pointer utilities.
+	 *
+	 * Not comprehensive, would benefit from being expanded at some point.
+	 */
+	void check_pointer_utilities()
+	{
+		debug_log("Test pointer utilities.");
+
+		int                              integer        = 42;
+		int                             *integerPointer = &integer;
+		ds::pointer::proxy::Pointer<int> pointer{integerPointer};
+
+		TEST((pointer == integerPointer) && (*pointer == 42),
+		     "The pointer proxy does not return the value of its proxy.");
+
+		int  anotherInteger        = -100;
+		int *anotherIntegerPointer = &anotherInteger;
+		ds::pointer::proxy::Pointer<int> anotherPointer{anotherIntegerPointer};
+
+		pointer = anotherPointer;
+
+		TEST(
+		  (pointer == anotherIntegerPointer) && (*pointer == -100),
+		  "The pointer proxy `=` operator does not correctly set the pointer.");
+	}
+
+	void check_shared_object(const char      *name,
+	                         Capability<void> object,
+	                         size_t           size,
+	                         PermissionSet    permissions)
+	{
+		debug_log("Checking shared object {}.", object);
+		TEST(object.length() == size,
+		     "Object {} is {} bytes, expected {}",
+		     name,
+		     object.length(),
+		     size);
+		TEST(object.permissions() == permissions,
+		     "Object {} has permissions {}, expected {}",
+		     name,
+		     PermissionSet{object.permissions()},
+		     permissions);
+	}
+
+	// This test is somewhat intimately familiar with parameters of CHERIoT's
+	// capability encoding and so might need revision if that changes.
+	void check_capability_set_inexact_at_most()
+	{
+		void *p = malloc(3128);
+
+		debug_log("Test Capability::BoundsProxy::set_inexact_at_most with {}",
+		          p);
+
+		// Too many bits for mantissa, regardless of base alignment
+		{
+			Capability<void> q      = {p};
+			size_t           reqlen = 2047;
+			q.bounds().set_inexact_at_most(reqlen);
+			debug_log("Requesting 2047 gives {}: {}", q.length(), q);
+			TEST(q.is_valid(), "set_inexact_at_most untagged");
+			TEST(q.length() < 2047, "set_inexact_at_most failed to truncate");
+			TEST(q.base() == q.address(), "set_inexact_at_most nonzero offset");
+		}
+
+		// Fits in mantissa, but not reachable from misaligned base
+		{
+			Capability<void> q = {p};
+			q.address() += 2;
+			size_t reqlen = 1024;
+			q.bounds().set_inexact_at_most(reqlen);
+			debug_log("Requesting 1024 at align 2 gives {}: {}", q.length(), q);
+			TEST(q.is_valid(), "set_inexact_at_most untagged");
+			TEST(q.length() < 1024, "set_inexact_at_most failed to truncate");
+			TEST(q.base() == q.address(), "set_inexact_at_most nonzero offset");
+		}
+
+		// Fits in mantissa and reachable from misaligned base
+		{
+			Capability<void> q = {p};
+			q.address() += 1;
+			size_t reqlen = 511;
+			q.bounds().set_inexact_at_most(reqlen);
+			debug_log("Requesting 511 at align 1 gives {}: {}", q.length(), q);
+			TEST(q.is_valid(), "set_inexact_at_most untagged");
+			TEST(q.length() == 511,
+			     "set_inexact_at_most truncated unnecessarily");
+			TEST(q.base() == q.address(), "set_inexact_at_most nonzero offset");
+		}
+
+		free(p);
+	}
+
+	/**
+	 * This is a regression test for #368.  There are many different ways for
+	 * the compiler to generate a memcmp call and this manages to trigger one of
+	 * the ones that wasn't being mangled the same way as others.  The run-time
+	 * behaviour of this test is irrelevant, we should get a linker failure if
+	 * the freestanding library and the compiler disagree on function names.
+	 */
+	void check_odd_memcmp()
+	{
+		std::string first  = "first";
+		std::string second = "second";
+		TEST((first == second) == false,
+		     "This test should never fail but exists to make sure that a "
+		     "comparison result is used");
+	}
+} // namespace
 
 int test_misc()
 {
@@ -247,5 +271,6 @@
 	                      void, test_word, true, false, false, false),
 	                    4,
 	                    {Permission::Global, Permission::Load});
+	check_odd_memcmp();
 	return 0;
 }