diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -110,6 +110,24 @@ list(APPEND TARGET_ENTRYPOINT_NAME_LIST ${entrypoint_name}) endforeach() +# External entrypoints are only included in TARGET_ENTRYPOINT_NAME_LIST, +# not TARGET_LLVMLIBC_ENTRYPOINTS. +# TARGET_ENTRYPOINT_NAME_LIST just defines which function prototypes are +# included in the generated headers, while TARGET_LLVMLIBC_ENTRYPOINTS is used +# for determining which functions will be built. +if(TARGET_LIBC_EXTERNAL_ENTRYPOINTS) + foreach(entrypoint IN LISTS TARGET_LIBC_EXTERNAL_ENTRYPOINTS) + string(FIND ${entrypoint} "." last_dot_loc REVERSE) + if(${last_dot_loc} EQUAL -1) + message(FATAL "Invalid entrypoint target name ${entrypoint}; Expected a '.' " + "(dot) in the name.") + endif() + math(EXPR name_loc "${last_dot_loc} + 1") + string(SUBSTRING ${entrypoint} ${name_loc} -1 entrypoint_name) + list(APPEND TARGET_ENTRYPOINT_NAME_LIST ${entrypoint_name}) + endforeach() +endif() + if(LLVM_LIBC_FULL_BUILD) # We need to set up hdrgen first since other targets depend on it. add_subdirectory(utils/LibcTableGenUtil) diff --git a/libc/cmake/modules/LLVMLibCObjectRules.cmake b/libc/cmake/modules/LLVMLibCObjectRules.cmake --- a/libc/cmake/modules/LLVMLibCObjectRules.cmake +++ b/libc/cmake/modules/LLVMLibCObjectRules.cmake @@ -258,6 +258,29 @@ endfunction(add_entrypoint_object) +# A rule for external entrypoint targets. +# Usage: +# add_entrypoint_external( +# +# ) +function(add_entrypoint_external target_name) + + get_fq_target_name(${target_name} fq_target_name) + set(entrypoint_name ${target_name}) + + add_custom_target(${fq_target_name}) + set_target_properties( + ${fq_target_name} + PROPERTIES + "ENTRYPOINT_NAME" ${entrypoint_name} + "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE} + "OBJECT_FILE" "" + "OBJECT_FILE_RAW" "" + "DEPS" "" + ) + +endfunction(add_entrypoint_external) + # Rule build a redirector object file. function(add_redirector_object target_name) cmake_parse_arguments( diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -36,6 +36,7 @@ libc.src.string.strcmp libc.src.string.strcpy libc.src.string.strcspn + libc.src.string.strdup libc.src.string.strlen libc.src.string.strncat libc.src.string.strncmp @@ -228,6 +229,14 @@ ) endif() +set(TARGET_LIBC_EXTERNAL_ENTRYPOINTS + # stdlib.h external entrypoints + libc.src.stdlib.malloc + libc.src.stdlib.calloc + libc.src.stdlib.realloc + libc.src.stdlib.free +) + set(TARGET_LLVMLIBC_ENTRYPOINTS ${TARGET_LIBC_ENTRYPOINTS} ${TARGET_LIBM_ENTRYPOINTS} diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -279,6 +279,11 @@ RetValSpec, [ArgSpec, ArgSpec] >, + FunctionSpec< + "strdup", + RetValSpec, + [ArgSpec] + >, FunctionSpec< "strpbrk", RetValSpec, @@ -513,6 +518,11 @@ FunctionSpec<"strtoul", RetValSpec, [ArgSpec, ArgSpec, ArgSpec]>, FunctionSpec<"strtoull", RetValSpec, [ArgSpec, ArgSpec, ArgSpec]>, + FunctionSpec<"malloc", RetValSpec, [ArgSpec]>, + FunctionSpec<"calloc", RetValSpec, [ArgSpec, ArgSpec]>, + FunctionSpec<"realloc", RetValSpec, [ArgSpec, ArgSpec]>, + FunctionSpec<"free", RetValSpec, [ArgSpec]>, + FunctionSpec<"_Exit", RetValSpec, [ArgSpec]>, ] >; diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt --- a/libc/src/stdlib/CMakeLists.txt +++ b/libc/src/stdlib/CMakeLists.txt @@ -181,6 +181,19 @@ libc.include.stdlib ) +add_entrypoint_external( + malloc +) +add_entrypoint_external( + calloc +) +add_entrypoint_external( + realloc +) +add_entrypoint_external( + free +) + if(NOT LLVM_LIBC_FULL_BUILD) return() endif() diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt --- a/libc/src/string/CMakeLists.txt +++ b/libc/src/string/CMakeLists.txt @@ -104,6 +104,18 @@ .string_utils ) +add_entrypoint_object( + strdup + SRCS + strdup.cpp + HDRS + strdup.h + DEPENDS + .memcpy + .string_utils + libc.include.stdlib +) + add_entrypoint_object( strlen SRCS diff --git a/libc/src/string/strdup.h b/libc/src/string/strdup.h new file mode 100644 --- /dev/null +++ b/libc/src/string/strdup.h @@ -0,0 +1,20 @@ +//===-- Implementation header for strdup ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_STRING_STRDUP_H +#define LLVM_LIBC_SRC_STRING_STRDUP_H + +#include + +namespace __llvm_libc { + +char *strdup(const char *src); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STRDUP_H diff --git a/libc/src/string/strdup.cpp b/libc/src/string/strdup.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/strdup.cpp @@ -0,0 +1,32 @@ +//===-- Implementation of strdup ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/string/strdup.h" +#include "src/string/memcpy.h" +#include "src/string/string_utils.h" + +#include "src/__support/common.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(char *, strdup, (const char *src)) { + if (src == nullptr) { + return nullptr; + } + size_t len = internal::string_length(src) + 1; + char *dest = reinterpret_cast(::malloc(len)); // NOLINT + if (dest == nullptr) { + return nullptr; + } + char *result = reinterpret_cast(__llvm_libc::memcpy(dest, src, len)); + return result; +} + +} // namespace __llvm_libc diff --git a/libc/test/src/string/CMakeLists.txt b/libc/test/src/string/CMakeLists.txt --- a/libc/test/src/string/CMakeLists.txt +++ b/libc/test/src/string/CMakeLists.txt @@ -92,6 +92,17 @@ libc.src.string.strcspn ) +add_libc_unittest( + strdup_test + SUITE + libc_string_unittests + SRCS + strdup_test.cpp + DEPENDS + libc.include.stdlib + libc.src.string.strdup +) + add_libc_unittest( strlen_test SUITE diff --git a/libc/test/src/string/strdup_test.cpp b/libc/test/src/string/strdup_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/string/strdup_test.cpp @@ -0,0 +1,39 @@ +//===-- Unittests for strdup ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/string/strdup.h" +#include "utils/UnitTest/Test.h" +#include + +TEST(LlvmLibcStrDupTest, EmptyString) { + const char *empty = ""; + + char *result = __llvm_libc::strdup(empty); + ASSERT_NE(result, static_cast(nullptr)); + ASSERT_NE(empty, const_cast(result)); + ASSERT_STREQ(empty, result); + ::free(result); +} + +TEST(LlvmLibcStrDupTest, AnyString) { + const char *abc = "abc"; + + char *result = __llvm_libc::strdup(abc); + + ASSERT_NE(result, static_cast(nullptr)); + ASSERT_NE(abc, const_cast(result)); + ASSERT_STREQ(abc, result); + ::free(result); +} + +TEST(LlvmLibcStrDupTest, NullPtr) { + + char *result = __llvm_libc::strdup(nullptr); + + ASSERT_EQ(result, static_cast(nullptr)); +}