This patch adds the implementation of the custom operator new functions.
The implementation of the internal strdup has been updated to use
operator new for allocation.
We will make it a policy and document that all allocations have to go
through the libc's own operator new. A future change will also add
operator delete replacements and make it a policy that deallocations in
libc internal code have to go through those replacements.
Reviewed By: lntue
Differential Revision: https://reviews.llvm.org/D139584
--- /dev/null
+//===-- Libc specific custom operator new and delete ------------*- 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_SUPPORT_CPP_NEW_H
+#define LLVM_LIBC_SRC_SUPPORT_CPP_NEW_H
+
+#include <stddef.h> // For size_t
+#include <stdlib.h> // For malloc, free etc.
+
+// Defining members in the std namespace is not preferred. But, we do it here
+// so that we can use it to define the operator new which takes std::align_val_t
+// argument.
+namespace std {
+
+enum class align_val_t : size_t {};
+
+} // namespace std
+
+namespace __llvm_libc {
+
+class AllocChecker {
+ bool success = false;
+ AllocChecker &operator=(bool status) {
+ success = status;
+ return *this;
+ }
+
+public:
+ AllocChecker() = default;
+ operator bool() const { return success; }
+
+ static void *alloc(size_t s, AllocChecker &ac) {
+ void *mem = ::malloc(s);
+ ac = (mem != nullptr);
+ return mem;
+ }
+
+ static void *aligned_alloc(size_t s, std::align_val_t align,
+ AllocChecker &ac) {
+ void *mem = ::aligned_alloc(static_cast<size_t>(align), s);
+ ac = (mem != nullptr);
+ return mem;
+ }
+};
+
+} // namespace __llvm_libc
+
+inline void *operator new(size_t size, __llvm_libc::AllocChecker &ac) noexcept {
+ return __llvm_libc::AllocChecker::alloc(size, ac);
+}
+
+inline void *operator new(size_t size, std::align_val_t align,
+ __llvm_libc::AllocChecker &ac) noexcept {
+ return __llvm_libc::AllocChecker::aligned_alloc(size, align, ac);
+}
+
+inline void *operator new[](size_t size,
+ __llvm_libc::AllocChecker &ac) noexcept {
+ return __llvm_libc::AllocChecker::alloc(size, ac);
+}
+
+inline void *operator new[](size_t size, std::align_val_t align,
+ __llvm_libc::AllocChecker &ac) noexcept {
+ return __llvm_libc::AllocChecker::aligned_alloc(size, align, ac);
+}
+
+#endif // LLVM_LIBC_SRC_SUPPORT_CPP_NEW_H
HDRS
allocating_string_utils.h
DEPENDS
- libc.include.stdlib
.memory_utils.memcpy_implementation
+ libc.include.stdlib
+ libc.src.__support.CPP.optional
)
add_entrypoint_object(
DEPENDS
.memory_utils.memcpy_implementation
.string_utils
+ libc.include.errno
libc.include.stdlib
+ libc.src.errno.errno
)
add_entrypoint_object(
#ifndef LIBC_SRC_STRING_ALLOCATING_STRING_UTILS_H
#define LIBC_SRC_STRING_ALLOCATING_STRING_UTILS_H
-#include "src/__support/CPP/bitset.h"
-#include "src/__support/common.h"
-#include "src/string/memory_utils/bzero_implementations.h"
-#include "src/string/memory_utils/memcpy_implementations.h"
+#include "src/__support/CPP/new.h"
+#include "src/__support/CPP/optional.h"
+#include "src/string/memory_utils/memcpy_implementations.h" // For string_length
#include "src/string/string_utils.h"
+
#include <stddef.h> // For size_t
-#include <stdlib.h> // For malloc
namespace __llvm_libc {
namespace internal {
-inline char *strdup(const char *src) {
+cpp::optional<char *> strdup(const char *src) {
if (src == nullptr)
- return nullptr;
+ return cpp::nullopt;
size_t len = string_length(src) + 1;
- char *newstr = reinterpret_cast<char *>(::malloc(len));
- if (newstr == nullptr)
- return nullptr;
+ AllocChecker ac;
+ char *newstr = new (ac) char[len];
+ if (!ac)
+ return cpp::nullopt;
inline_memcpy(newstr, src, len);
return newstr;
}
#include "src/__support/common.h"
+#include <errno.h>
#include <stdlib.h>
namespace __llvm_libc {
LLVM_LIBC_FUNCTION(char *, strdup, (const char *src)) {
- return internal::strdup(src);
+ auto dup = internal::strdup(src);
+ if (dup)
+ return *dup;
+ if (src != nullptr)
+ errno = ENOMEM;
+ return nullptr;
}
} // namespace __llvm_libc
char pathbuf[PATH_MAX];
if (!getcwd_syscall(pathbuf, PATH_MAX))
return nullptr;
- char *cwd = internal::strdup(pathbuf);
- if (cwd == nullptr) {
+ auto cwd = internal::strdup(pathbuf);
+ if (!cwd) {
errno = ENOMEM;
return nullptr;
}
- return cwd;
+ return *cwd;
} else if (size == 0) {
errno = EINVAL;
return nullptr;
SRCS
strdup_test.cpp
DEPENDS
+ libc.include.errno
libc.include.stdlib
libc.src.string.strdup
+ libc.src.errno.errno
)
add_libc_unittest(
#include "src/string/strdup.h"
#include "utils/UnitTest/Test.h"
+
+#include <errno.h>
#include <stdlib.h>
TEST(LlvmLibcStrDupTest, EmptyString) {
const char *empty = "";
+ errno = 0;
char *result = __llvm_libc::strdup(empty);
+ ASSERT_EQ(errno, 0);
+
ASSERT_NE(result, static_cast<char *>(nullptr));
ASSERT_NE(empty, const_cast<const char *>(result));
ASSERT_STREQ(empty, result);
TEST(LlvmLibcStrDupTest, AnyString) {
const char *abc = "abc";
+ errno = 0;
char *result = __llvm_libc::strdup(abc);
+ ASSERT_EQ(errno, 0);
ASSERT_NE(result, static_cast<char *>(nullptr));
ASSERT_NE(abc, const_cast<const char *>(result));
}
TEST(LlvmLibcStrDupTest, NullPtr) {
-
+ errno = 0;
char *result = __llvm_libc::strdup(nullptr);
+ ASSERT_EQ(errno, 0);
ASSERT_EQ(result, static_cast<char *>(nullptr));
}