From c5b297a86f3d65081bc690e81ab53db47b18d31c Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 3 Nov 2021 09:26:45 +0100 Subject: [PATCH] newlib: implement posix_memalign, sysconf, realpath Closes https://github.com/espressif/esp-idf/issues/6119 Closes https://github.com/espressif/esp-idf/issues/7798 --- components/newlib/CMakeLists.txt | 4 +- components/newlib/heap.c | 36 +++-- components/newlib/realpath.c | 125 ++++++++++++++++++ components/newlib/sysconf.c | 27 ++++ .../newlib/test_apps/main/CMakeLists.txt | 1 + components/newlib/test_apps/main/test_misc.c | 95 +++++++++++++ .../newlib/test_apps/main/test_newlib_main.c | 1 + tools/ci/check_copyright_ignore.txt | 1 - 8 files changed, 275 insertions(+), 15 deletions(-) create mode 100644 components/newlib/realpath.c create mode 100644 components/newlib/sysconf.c create mode 100644 components/newlib/test_apps/main/test_misc.c diff --git a/components/newlib/CMakeLists.txt b/components/newlib/CMakeLists.txt index 5e837b6cd8f7..ab732217ba79 100644 --- a/components/newlib/CMakeLists.txt +++ b/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) diff --git a/components/newlib/heap.c b/components/newlib/heap.c index d226a39197e6..fca44906a2d1 100644 --- a/components/newlib/heap.c +++ b/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 #include #include +#include #include #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. */ diff --git a/components/newlib/realpath.c b/components/newlib/realpath.c new file mode 100644 index 000000000000..e2f06619d9f6 --- /dev/null +++ b/components/newlib/realpath.c @@ -0,0 +1,125 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +/* 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; +} diff --git a/components/newlib/sysconf.c b/components/newlib/sysconf.c new file mode 100644 index 000000000000..96dd70203b75 --- /dev/null +++ b/components/newlib/sysconf.c @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "sdkconfig.h" + +#ifdef CONFIG_FREERTOS_UNICORE +#define CPU_NUM 1 +#else +#define CPU_NUM CONFIG_SOC_CPU_CORES_NUM +#endif + +long sysconf(int arg) +{ + switch (arg) { + case _SC_NPROCESSORS_CONF: + case _SC_NPROCESSORS_ONLN: + return CPU_NUM; + default: + errno = EINVAL; + return -1; + } +} diff --git a/components/newlib/test_apps/main/CMakeLists.txt b/components/newlib/test_apps/main/CMakeLists.txt index 3eb45e8ff9e9..76686cf6228a 100644 --- a/components/newlib/test_apps/main/CMakeLists.txt +++ b/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) diff --git a/components/newlib/test_apps/main/test_misc.c b/components/newlib/test_apps/main/test_misc.c new file mode 100644 index 000000000000..0d0374c153e1 --- /dev/null +++ b/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 +#include +#include +#include +#include +#include +#include +#include +#include +#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) +} diff --git a/components/newlib/test_apps/main/test_newlib_main.c b/components/newlib/test_apps/main/test_newlib_main.c index 1197d8f82d70..189fe762107a 100644 --- a/components/newlib/test_apps/main/test_newlib_main.c +++ b/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) diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index b9c1b800d08d..4308f57e343b 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -1421,7 +1421,6 @@ components/mqtt/host_test/mocks/include/machine/endian.h components/mqtt/host_test/mocks/include/sys/queue.h 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