Jelajahi Sumber

newlib: implement posix_memalign, sysconf, realpath

Closes https://github.com/espressif/esp-idf/issues/6119
Closes https://github.com/espressif/esp-idf/issues/7798
Ivan Grokhotkov 4 tahun lalu
induk
melakukan
f158a0d538

+ 3 - 1
components/newlib/CMakeLists.txt

@@ -17,7 +17,9 @@ set(srcs
     "syscalls.c"
     "termios.c"
     "stdatomic.c"
-    "time.c")
+    "time.c"
+    "sysconf.c"
+    "realpath.c")
 set(include_dirs platform_include)
 
 if(CONFIG_SPIRAM_CACHE_WORKAROUND)

+ 23 - 13
components/newlib/heap.c

@@ -1,20 +1,13 @@
-// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
 
 #include <string.h>
 #include <stdlib.h>
 #include <sys/reent.h>
+#include <errno.h>
 #include <malloc.h>
 #include "esp_heap_caps.h"
 
@@ -81,6 +74,23 @@ void* memalign(size_t alignment, size_t n)
     return heap_caps_aligned_alloc(alignment, n, MALLOC_CAP_DEFAULT);
 }
 
+int posix_memalign(void **out_ptr, size_t alignment, size_t size)
+{
+    if (size == 0) {
+        /* returning NULL for zero size is allowed, don't treat this as an error */
+        *out_ptr = NULL;
+        return 0;
+    }
+    void *result = heap_caps_aligned_alloc(alignment, size, MALLOC_CAP_DEFAULT);
+    if (result != NULL) {
+        /* Modify output pointer only on success */
+        *out_ptr = result;
+        return 0;
+    }
+    /* Note: error returned, not set via errno! */
+    return ENOMEM;
+}
+
 /* No-op function, used to force linking this file,
    instead of the heap implementation from newlib.
  */

+ 125 - 0
components/newlib/realpath.c

@@ -0,0 +1,125 @@
+/*
+ * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/param.h>
+
+/* realpath logic:
+ * 1. prepend CWD (/)
+ * 2. iterate over components (search until next '/' or end of line)
+ *    - empty, skip the component
+ *    - if it is '.', skip the component
+ *    - if it is '..'
+ *      - and out_level == 0, ??? ('/..')
+ *      - otherwise, reverse-search for '/', set out_pos to that - 1, decrement out_level
+ *    - otherwise, add the component to output, increment out_level
+ */
+
+char * realpath(const char *file_name, char *resolved_name)
+{
+    char * out_path = resolved_name;
+    if (out_path == NULL) {
+        /* allowed as an extension, allocate memory for the output path */
+        out_path = malloc(PATH_MAX);
+        if (out_path == NULL) {
+            errno = ENOMEM;
+            return NULL;
+        }
+    }
+
+    /* canonical path starts with / */
+    strlcpy(out_path, "/", PATH_MAX);
+
+    /* pointers moving over the input and output path buffers */
+    const char* in_ptr = file_name;
+    char* out_ptr = out_path + 1;
+    /* number of path components in the output buffer */
+    size_t out_depth = 0;
+
+
+    while (*in_ptr) {
+        /* "path component" is the part between two '/' path separators.
+         * locate the next path component in the input path:
+         */
+        const char* end_of_path_component = strchrnul(in_ptr, '/');
+        size_t path_component_len = end_of_path_component - in_ptr;
+
+        if (path_component_len == 0 ||
+            (path_component_len == 1 && in_ptr[0] == '.')) {
+            /* empty path component or '.' - nothing to do */
+        } else if (path_component_len == 2 && in_ptr[0] == '.' && in_ptr[1] == '.') {
+            /* '..' - remove one path component from the output */
+            if (out_depth == 0) {
+                /* nothing to remove */
+            } else if (out_depth == 1) {
+                /* there is only one path component in output;
+                 * remove it, but keep the leading separator
+                 */
+                out_ptr = out_path + 1;
+                *out_ptr = '\0';
+                out_depth = 0;
+            } else {
+                /* remove last path component and the separator preceding it */
+                char * prev_sep = strrchr(out_path, '/');
+                assert(prev_sep > out_path);  /* this shouldn't be the leading separator */
+                out_ptr = prev_sep;
+                *out_ptr = '\0';
+                --out_depth;
+            }
+        } else {
+            /* copy path component to output; +1 is for the separator  */
+            if (out_ptr - out_path + 1 + path_component_len > PATH_MAX - 1) {
+                /* output buffer insufficient */
+                errno = E2BIG;
+                goto fail;
+            } else {
+                /* add separator if necessary */
+                if (out_depth > 0) {
+                    *out_ptr = '/';
+                    ++out_ptr;
+                }
+                memcpy(out_ptr, in_ptr, path_component_len);
+                out_ptr += path_component_len;
+                *out_ptr = '\0';
+                ++out_depth;
+            }
+        }
+        /* move input pointer to separator right after this path component */
+        in_ptr += path_component_len;
+        if (*in_ptr != '\0') {
+            /* move past it unless already at the end of the input string */
+            ++in_ptr;
+        }
+    }
+    return out_path;
+
+fail:
+    if (resolved_name == NULL) {
+        /* out_path was allocated, free it */
+        free(out_path);
+    }
+    return NULL;
+}
+
+char * getcwd(char *buf, size_t size)
+{
+    if (buf == NULL) {
+        return strdup("/");
+    }
+    strlcpy(buf, "/", size);
+    return buf;
+}
+
+int chdir(const char *path)
+{
+    (void) path;
+    errno = ENOSYS;
+    return -1;
+}

+ 24 - 0
components/newlib/sysconf.c

@@ -0,0 +1,24 @@
+/*
+ * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include "sdkconfig.h"
+#include "freertos/FreeRTOS.h"
+
+#define CPU_NUM portNUM_PROCESSORS
+
+long sysconf(int arg)
+{
+    switch (arg) {
+    case _SC_NPROCESSORS_CONF:
+    case _SC_NPROCESSORS_ONLN:
+        return CPU_NUM;
+    default:
+        errno = EINVAL;
+        return -1;
+    }
+}

+ 1 - 0
components/newlib/test_apps/main/CMakeLists.txt

@@ -1,5 +1,6 @@
 idf_component_register(SRCS
                         "test_newlib_main.c"
                         "test_stdatomic.c"
+                        "test_misc.c"
                        REQUIRES test_utils
                        PRIV_REQUIRES unity)

+ 95 - 0
components/newlib/test_apps/main/test_misc.c

@@ -0,0 +1,95 @@
+/*
+ * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include <assert.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include "unity.h"
+#include "unity_fixture.h"
+
+// unity_fixture_malloc_overrides.h defines 'free' as 'unity_free',
+// which can only handle pointers allocated with 'unity_malloc'.
+// This test allocates memory via 'posix_memalign' so calling 'free'
+// for these pointers causes the heap guards check to fail.
+#undef free
+
+TEST_GROUP(misc);
+
+TEST_SETUP(misc)
+{
+}
+
+TEST_TEAR_DOWN(misc)
+{
+}
+
+TEST(misc, posix_memalign)
+{
+    void* outptr;
+    int ret;
+
+    ret = posix_memalign(&outptr, 4, 0);
+    TEST_ASSERT_EQUAL_PTR(NULL, outptr);
+    TEST_ASSERT_EQUAL_INT(ret, 0);
+
+    void* magic = (void*) 0xEFEFEFEF;
+    outptr = magic;
+    ret = posix_memalign(&outptr, 0x10000000, 64);  // too big alignment - should fail on all targets
+    TEST_ASSERT_EQUAL_INT(ret, ENOMEM);
+    TEST_ASSERT_EQUAL_PTR(magic, outptr);  // was not modified
+
+    outptr = magic;
+    ret = posix_memalign(&outptr, 16, 0x10000000);  // too big size - should fail on all targets
+    TEST_ASSERT_EQUAL_INT(ret, ENOMEM);
+    TEST_ASSERT_EQUAL_PTR(magic, outptr);  // was not modified
+
+    outptr = magic;
+    ret = posix_memalign(&outptr, 16, 64);
+    TEST_ASSERT_TRUE(outptr != magic);
+    TEST_ASSERT_NOT_NULL(outptr);
+    TEST_ASSERT_EQUAL_INT(ret, 0);
+    free(outptr);
+}
+
+TEST(misc, sysconf)
+{
+    TEST_ASSERT_NOT_EQUAL(-1, sysconf(_SC_NPROCESSORS_ONLN));
+}
+
+TEST(misc, realpath)
+{
+    char out[PATH_MAX];
+
+    TEST_ASSERT_EQUAL_STRING("/", realpath("/", out));
+    TEST_ASSERT_EQUAL_STRING("/", realpath("//", out));
+    TEST_ASSERT_EQUAL_STRING("/", realpath(".", out));
+    TEST_ASSERT_EQUAL_STRING("/", realpath("./", out));
+    TEST_ASSERT_EQUAL_STRING("/", realpath("/.", out));
+    TEST_ASSERT_EQUAL_STRING("/", realpath("./.", out));
+    TEST_ASSERT_EQUAL_STRING("/", realpath("..", out));
+    TEST_ASSERT_EQUAL_STRING("/", realpath("../..", out));
+    TEST_ASSERT_EQUAL_STRING("/a", realpath("/a/", out));
+    TEST_ASSERT_EQUAL_STRING("/", realpath("/a/..", out));
+    TEST_ASSERT_EQUAL_STRING("/", realpath("/a/../..", out));
+    TEST_ASSERT_EQUAL_STRING("/c", realpath("/a/../b/../c", out));
+    TEST_ASSERT_EQUAL_STRING("/abc/def", realpath("/abc/./def/ghi/..", out));
+    char* out_new = realpath("/abc/./def/ghi/..", NULL);
+    TEST_ASSERT_NOT_NULL(out_new);
+    TEST_ASSERT_EQUAL_STRING("/abc/def", out_new);
+    free(out_new);
+}
+
+TEST_GROUP_RUNNER(misc)
+{
+    RUN_TEST_CASE(misc, posix_memalign)
+    RUN_TEST_CASE(misc, sysconf)
+    RUN_TEST_CASE(misc, realpath)
+}

+ 1 - 0
components/newlib/test_apps/main/test_newlib_main.c

@@ -4,6 +4,7 @@
 static void run_all_tests(void)
 {
     RUN_TEST_GROUP(stdatomic);
+    RUN_TEST_GROUP(misc);
 }
 
 void app_main(void)

+ 0 - 1
tools/ci/check_copyright_ignore.txt

@@ -1582,7 +1582,6 @@ components/mqtt/host_test/mocks/include/sys/queue.h
 components/mqtt/test/test_mqtt.c
 components/newlib/abort.c
 components/newlib/assert.c
-components/newlib/heap.c
 components/newlib/platform_include/assert.h
 components/newlib/platform_include/errno.h
 components/newlib/platform_include/esp_newlib.h