Support running unit tests from another directory

When running a test suite, try to change to the directory containing the
executable. This allows running a test suite from any directory, and still
allow it to access its .datax file as well as data files (generally in
tests/data_files) used by individual test cases.

Only implemented on Unix-like systems and on Windows.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
diff --git a/tests/suites/host_test.function b/tests/suites/host_test.function
index 06f391f..3a3cb34 100644
--- a/tests/suites/host_test.function
+++ b/tests/suites/host_test.function
@@ -432,6 +432,39 @@
     fflush(outcome_file);
 }
 
+#if defined(__unix__) ||                                \
+    (defined(__APPLE__) && defined(__MACH__)) ||        \
+    defined(_WIN32)
+#define MBEDTLS_HAVE_CHDIR
+#endif
+
+#if defined(MBEDTLS_HAVE_CHDIR)
+/** Try chdir to the directory containing argv0.
+ *
+ * Failures are silent.
+ */
+static void try_chdir(const char *argv0)
+{
+    const char *slash = strrchr(argv0, '/');
+    if (slash == NULL) {
+        return;
+    }
+    size_t path_size = slash - argv0 + 1;
+    char *path = mbedtls_calloc(1, path_size);
+    if (path == NULL) {
+        return;
+    }
+    memcpy(path, argv0, path_size - 1);
+    path[path_size - 1] = 0;
+#if defined(_WIN32)
+    (void) _chdir(path);
+#else
+    (void) chdir(path);
+#endif
+    mbedtls_free(path);
+}
+#endif /* MBEDTLS_HAVE_CHDIR */
+
 /**
  * \brief       Desktop implementation of execute_tests().
  *              Parses command line and executes tests from
diff --git a/tests/suites/main_test.function b/tests/suites/main_test.function
index 6c8d98e..eb74e8f 100644
--- a/tests/suites/main_test.function
+++ b/tests/suites/main_test.function
@@ -237,6 +237,21 @@
 #endif
 #endif
 
+#ifdef MBEDTLS_HAVE_CHDIR
+    /* Try changing to the directory containing the executable, if
+     * using the default data file. This allows running the executable
+     * from another directory (e.g. the project root) and still access
+     * the .datax file as well as data files used by test cases
+     * (typically from tests/data_files).
+     *
+     * Note that we do this before the platform setup (which may access
+     * files such as a random seed). We also do this before accessing
+     * test-specific files such as the outcome file, which is arguably
+     * not desirable and should be fixed later.
+     */
+    try_chdir(argv[0]);
+#endif /* MBEDTLS_HAVE_CHDIR */
+
     int ret = mbedtls_test_platform_setup();
     if (ret != 0) {
         mbedtls_fprintf(stderr,