[sw] Conditionally Provide Some libc Symbols

`memory.h` provides some symbols which are also provided by libc. When
these are used by DIFs, we would prefer to use the host's libc
implementations rather than our own.

This approach is used so that DIFs can depend on `memory.h`, but also be
built for host-side software.

OpenTitan does not access volatile memory using the `memory.h` functions
-- volatile memory must be accessed via the functions in `mmio.h` -- so
this should not affect the functionality of any tests.

Closes #1806.

Signed-off-by: Sam Elliott <selliott@lowrisc.org>
diff --git a/meson.build b/meson.build
index 3ecfccc..452061b 100644
--- a/meson.build
+++ b/meson.build
@@ -91,6 +91,14 @@
   c_cpp_cross_link_args,
   language: ['c', 'cpp'], native: false)
 
+# The following flags are applied only to native builds
+c_cpp_native_args = [
+  # Use a define to exclude libc redefinitions.
+  '-DHOST_BUILD',
+]
+add_project_arguments(
+  c_cpp_native_args,
+  language: ['c', 'cpp'], native: true)
 
 # Common program references.
 prog_python = import('python').find_installation('python3')
diff --git a/sw/device/lib/base/memory.c b/sw/device/lib/base/memory.c
index 165d914..6cb961f 100644
--- a/sw/device/lib/base/memory.c
+++ b/sw/device/lib/base/memory.c
@@ -7,6 +7,17 @@
 extern uint32_t read_32(const void *);
 extern void write_32(uint32_t, void *);
 
+// Some symbols below are only defined for device builds. For host builds, we
+// their implementations will be provided by the host's libc implementation.
+//
+// If you are getting missing symbol linker errors for these symbols, it's
+// likely because you have specified `-nostdlib` for a host build. Host builds
+// must be linked against the system libc.
+//
+// This approach is used so that DIFs can depend on `memory.h`, but also be
+// built for host-side software.
+
+#if !defined(HOST_BUILD)
 void *memcpy(void *restrict dest, const void *restrict src, size_t len) {
   uint8_t *dest8 = (uint8_t *)dest;
   uint8_t *src8 = (uint8_t *)src;
@@ -15,7 +26,9 @@
   }
   return dest;
 }
+#endif  // !defined(HOST_BUILD)
 
+#if !defined(HOST_BUILD)
 void *memset(void *dest, int value, size_t len) {
   uint8_t *dest8 = (uint8_t *)dest;
   uint8_t value8 = (uint8_t)value;
@@ -24,7 +37,9 @@
   }
   return dest;
 }
+#endif  // !defined(HOST_BUILD)
 
+#if !defined(HOST_BUILD)
 enum {
   kMemCmpEq = 0,
   kMemCmpLt = -42,
@@ -43,7 +58,9 @@
   }
   return kMemCmpEq;
 }
+#endif  // !defined(HOST_BUILD)
 
+#if !defined(HOST_BUILD)
 void *memchr(const void *ptr, int value, size_t len) {
   uint8_t *ptr8 = (uint8_t *)ptr;
   uint8_t value8 = (uint8_t)value;
@@ -54,6 +71,7 @@
   }
   return NULL;
 }
+#endif  // !defined(HOST_BUILD)
 
 void *memrchr(const void *ptr, int value, size_t len) {
   uint8_t *ptr8 = (uint8_t *)ptr;
diff --git a/sw/device/lib/base/memory.h b/sw/device/lib/base/memory.h
index f4df0cf..eb571af 100644
--- a/sw/device/lib/base/memory.h
+++ b/sw/device/lib/base/memory.h
@@ -84,6 +84,9 @@
  *
  * This function conforms to the semantics defined in ISO C11 S7.23.2.1.
  *
+ * This function will be provided by the platform's libc implementation for host
+ * builds.
+ *
  * @param dest the region to copy to.
  * @param src the region to copy from.
  * @param len the number of bytes to copy.
@@ -96,6 +99,9 @@
  *
  * This function conforms to the semantics defined in ISO C11 S7.23.6.1.
  *
+ * This function will be provided by the platform's libc implementation for host
+ * builds.
+ *
  * @param dest the region to write to.
  * @param value the value, converted to a byte, to write to each byte cell.
  * @param len the number of bytes to write.
@@ -109,6 +115,9 @@
  *
  * This function conforms to the semantics defined in ISO C11 S7.24.4.1.
  *
+ * This function will be provided by the platform's libc implementation for host
+ * builds.
+ *
  * @param lhs the left-hand-side of the comparison.
  * @param rhs the right-hand-side of the comparison.
  * @param len the length of both regions, in bytes.
@@ -126,6 +135,9 @@
  * Since libbase does not provide a `strlen()` function, this function can be
  * used as an approximation: `memchr(my_str, 0, SIZE_MAX) - my_str`.
  *
+ * This function will be provided by the platform's libc implementation for host
+ * builds.
+ *
  * @param ptr the region to search.
  * @param value the value, converted to a byte, to search for.
  * @param len the length of the region, in bytes.