[libc-commits] [libc] f2a7f83 - Introduce getenv to LLVM libc
Raman Tenneti via libc-commits
libc-commits at lists.llvm.org
Mon Feb 14 10:10:28 PST 2022
Author: Raman Tenneti
Date: 2022-02-14T10:10:13-08:00
New Revision: f2a7f835958f8bd39314acad3210875e83cad326
URL: https://github.com/llvm/llvm-project/commit/f2a7f835958f8bd39314acad3210875e83cad326
DIFF: https://github.com/llvm/llvm-project/commit/f2a7f835958f8bd39314acad3210875e83cad326.diff
LOG: Introduce getenv to LLVM libc
Add support for getenv as defined by the Open Group's "System Interface &
Header" in https://pubs.opengroup.org/onlinepubs/7908799/xsh/getenv.html
getenv requires a standard way of accessing the environment,
so a pointer to the environment is added to the startup in crt1.
Consquently, this function is not usable on top of other libcs.
Added starts_with method to StringView. getenv function uses it.
Co-authored-by: Jeff Bailey <jeffbailey at google.com>
Reviewed By: sivachandra, rtenneti
Differential Revision: https://reviews.llvm.org/D119403
Added:
libc/src/stdlib/getenv.cpp
libc/src/stdlib/getenv.h
libc/test/loader/linux/getenv_test.cpp
Modified:
libc/config/linux/app.h
libc/config/linux/x86_64/entrypoints.txt
libc/loader/linux/x86_64/start.cpp
libc/spec/posix.td
libc/src/__support/CPP/StringView.h
libc/src/stdlib/CMakeLists.txt
libc/test/loader/linux/CMakeLists.txt
libc/test/loader/linux/loader_test.h
libc/test/src/__support/CPP/stringview_test.cpp
Removed:
################################################################################
diff --git a/libc/config/linux/app.h b/libc/config/linux/app.h
index 48e42ae2c80a..35913998d596 100644
--- a/libc/config/linux/app.h
+++ b/libc/config/linux/app.h
@@ -33,8 +33,13 @@ struct AppProperties {
// The properties of an application's TLS.
TLS tls;
+
+ // Environment data.
+ uint64_t *envPtr;
};
+extern AppProperties app;
+
// Creates and initializes the TLS area for the current thread. Should not
// be called before app.tls has been initialized.
void initTLS();
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index f21d032464dc..65b027ba5f7f 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -72,6 +72,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.stdlib.atoll
libc.src.stdlib.bsearch
libc.src.stdlib.div
+ libc.src.stdlib.getenv
libc.src.stdlib.labs
libc.src.stdlib.ldiv
libc.src.stdlib.llabs
diff --git a/libc/loader/linux/x86_64/start.cpp b/libc/loader/linux/x86_64/start.cpp
index 774f01b54321..574a8f435430 100644
--- a/libc/loader/linux/x86_64/start.cpp
+++ b/libc/loader/linux/x86_64/start.cpp
@@ -29,9 +29,6 @@ static constexpr long mmapSyscallNumber = SYS_mmap;
#error "Target platform does not have SYS_mmap or SYS_mmap2 defined"
#endif
-// TODO: Declare var an extern var in config/linux/app.h so that other
-// libc functions can make use of the application wide information. For
-// example, mmap can pick up the page size from here.
AppProperties app;
// TODO: The function is x86_64 specific. Move it to config/linux/app.h
@@ -109,6 +106,7 @@ extern "C" void _start() {
// value. We step over it (the "+ 1" below) to get to the env values.
uint64_t *env_ptr = args->argv + args->argc + 1;
uint64_t *env_end_marker = env_ptr;
+ app.envPtr = env_ptr;
while (*env_end_marker)
++env_end_marker;
diff --git a/libc/spec/posix.td b/libc/spec/posix.td
index dc9a3ee773a9..d6e3d8564f79 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -265,6 +265,20 @@ def POSIX : StandardSpec<"POSIX"> {
]
>;
+ HeaderSpec StdLib = HeaderSpec<
+ "stdlib.h",
+ [], // Macros
+ [], // Types
+ [], // Enumerations
+ [
+ FunctionSpec<
+ "getenv",
+ RetValSpec<CharPtr>,
+ [ArgSpec<ConstCharPtr>]
+ >,
+ ]
+ >;
+
HeaderSpec String = HeaderSpec<
"string.h",
[
@@ -356,6 +370,7 @@ def POSIX : StandardSpec<"POSIX"> {
Errno,
FCntl,
Signal,
+ StdLib,
SysMMan,
SysStat,
UniStd,
diff --git a/libc/src/__support/CPP/StringView.h b/libc/src/__support/CPP/StringView.h
index 31efc5ac0764..8c0cfd61e097 100644
--- a/libc/src/__support/CPP/StringView.h
+++ b/libc/src/__support/CPP/StringView.h
@@ -81,6 +81,17 @@ class StringView {
return remove_prefix(PrefixLen).remove_suffix(SuffixLen);
}
+ // Check if this string starts with the given \p Prefix.
+ bool starts_with(StringView Prefix) const {
+ if (Len < Prefix.Len)
+ return false;
+ for (size_t I = 0; I < Prefix.Len; ++I) {
+ if (Data[I] != Prefix.Data[I])
+ return false;
+ }
+ return true;
+ }
+
// An equivalent method is not available in std::string_view.
bool equals(StringView Other) const {
if (Len != Other.Len)
diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index cb3aeb808949..d27d86f505b6 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -38,6 +38,17 @@ add_entrypoint_object(
libc.src.__support.str_to_integer
)
+add_entrypoint_object(
+ getenv
+ SRCS
+ getenv.cpp
+ HDRS
+ getenv.h
+ DEPENDS
+ libc.config.linux.app_h
+ libc.src.string.strncmp
+)
+
add_entrypoint_object(
strtof
SRCS
diff --git a/libc/src/stdlib/getenv.cpp b/libc/src/stdlib/getenv.cpp
new file mode 100644
index 000000000000..2492d63d6d50
--- /dev/null
+++ b/libc/src/stdlib/getenv.cpp
@@ -0,0 +1,42 @@
+//===-- Implementation of getenv ------------------------------------------===//
+//
+// 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/stdlib/getenv.h"
+#include "config/linux/app.h"
+#include "src/__support/CPP/StringView.h"
+#include "src/__support/common.h"
+
+#include <stddef.h> // For size_t.
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(char *, getenv, (const char *name)) {
+ char **env_ptr = reinterpret_cast<char **>(__llvm_libc::app.envPtr);
+
+ if (name == nullptr || env_ptr == nullptr)
+ return nullptr;
+
+ __llvm_libc::cpp::StringView env_var_name(name);
+ if (env_var_name.size() == 0)
+ return nullptr;
+ for (char **env = env_ptr; *env != nullptr; env++) {
+ __llvm_libc::cpp::StringView cur(*env);
+ if (!cur.starts_with(env_var_name))
+ continue;
+
+ if (cur[env_var_name.size()] != '=')
+ continue;
+
+ return const_cast<char *>(
+ cur.remove_prefix(env_var_name.size() + 1).data());
+ }
+
+ return nullptr;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdlib/getenv.h b/libc/src/stdlib/getenv.h
new file mode 100644
index 000000000000..a7eac3172de8
--- /dev/null
+++ b/libc/src/stdlib/getenv.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for getenv --------------------------------*-===//
+//
+// 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_STDLIB_GETENV_H
+#define LLVM_LIBC_SRC_STDLIB_GETENV_H
+
+namespace __llvm_libc {
+
+char *getenv(const char *name);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDLIB_GETENV_H
diff --git a/libc/test/loader/linux/CMakeLists.txt b/libc/test/loader/linux/CMakeLists.txt
index afe4423d9c0d..4a42fe8bf27d 100644
--- a/libc/test/loader/linux/CMakeLists.txt
+++ b/libc/test/loader/linux/CMakeLists.txt
@@ -43,6 +43,20 @@ add_loader_test(
libc.loader.linux.crt1
)
+add_loader_test(
+ getenv_test
+ SRC
+ getenv_test.cpp
+ DEPENDS
+ .loader_test
+ libc.loader.linux.crt1
+ libc.src.stdlib.getenv
+ ENV
+ FRANCE=Paris
+ GERMANY=Berlin
+)
+
+
# TODO: Disableing this test temporarily to investigate why gold fails to link
# and produce an executable for this test. Test works all fine with ld.bfd.
#add_loader_test(
diff --git a/libc/test/loader/linux/getenv_test.cpp b/libc/test/loader/linux/getenv_test.cpp
new file mode 100644
index 000000000000..0a627a7847a6
--- /dev/null
+++ b/libc/test/loader/linux/getenv_test.cpp
@@ -0,0 +1,45 @@
+//===-- Unittests for getenv ----------------------------------------------===//
+//
+// 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 "loader_test.h"
+#include "src/stdlib/getenv.h"
+
+static bool my_streq(const char *lhs, const char *rhs) {
+ if (lhs == rhs)
+ return true;
+ if (((lhs == static_cast<char *>(nullptr)) &&
+ (rhs != static_cast<char *>(nullptr))) ||
+ ((lhs != static_cast<char *>(nullptr)) &&
+ (rhs == static_cast<char *>(nullptr)))) {
+ return false;
+ }
+ const char *l, *r;
+ for (l = lhs, r = rhs; *l != '\0' && *r != '\0'; ++l, ++r)
+ if (*l != *r)
+ return false;
+
+ return *l == '\0' && *r == '\0';
+}
+
+int main(int argc, char **argv, char **envp) {
+ ASSERT_TRUE(my_streq(__llvm_libc::getenv(""), static_cast<char *>(nullptr)));
+ ASSERT_TRUE(my_streq(__llvm_libc::getenv("="), static_cast<char *>(nullptr)));
+ ASSERT_TRUE(my_streq(__llvm_libc::getenv("MISSING ENV VARIABLE"),
+ static_cast<char *>(nullptr)));
+ ASSERT_FALSE(
+ my_streq(__llvm_libc::getenv("PATH"), static_cast<char *>(nullptr)));
+ ASSERT_TRUE(my_streq(__llvm_libc::getenv("FRANCE"), "Paris"));
+ ASSERT_FALSE(my_streq(__llvm_libc::getenv("FRANCE"), "Berlin"));
+ ASSERT_TRUE(my_streq(__llvm_libc::getenv("GERMANY"), "Berlin"));
+ ASSERT_TRUE(
+ my_streq(__llvm_libc::getenv("FRANC"), static_cast<char *>(nullptr)));
+ ASSERT_TRUE(
+ my_streq(__llvm_libc::getenv("FRANCE1"), static_cast<char *>(nullptr)));
+
+ return 0;
+}
diff --git a/libc/test/loader/linux/loader_test.h b/libc/test/loader/linux/loader_test.h
index 1506da55f4e5..fe00210fc5c3 100644
--- a/libc/test/loader/linux/loader_test.h
+++ b/libc/test/loader/linux/loader_test.h
@@ -21,7 +21,17 @@
__llvm_libc::quick_exit(127); \
}
+#define __CHECK_NE(file, line, val, should_exit) \
+ if ((val)) { \
+ __llvm_libc::write_to_stderr(file ":" __AS_STRING( \
+ line) ": Expected '" #val "' to be false, but is true\n"); \
+ if (should_exit) \
+ __llvm_libc::quick_exit(127); \
+ }
+
#define EXPECT_TRUE(val) __CHECK(__FILE__, __LINE__, val, false)
#define ASSERT_TRUE(val) __CHECK(__FILE__, __LINE__, val, true)
+#define EXPECT_FALSE(val) __CHECK_NE(__FILE__, __LINE__, val, false)
+#define ASSERT_FALSE(val) __CHECK_NE(__FILE__, __LINE__, val, true)
#endif // LLVM_LIBC_TEST_LOADER_LINUX_LOADER_TEST_H
diff --git a/libc/test/src/__support/CPP/stringview_test.cpp b/libc/test/src/__support/CPP/stringview_test.cpp
index a62cde9bfb81..7835eb667d8d 100644
--- a/libc/test/src/__support/CPP/stringview_test.cpp
+++ b/libc/test/src/__support/CPP/stringview_test.cpp
@@ -45,6 +45,19 @@ TEST(LlvmLibcStringViewTest, Equals) {
ASSERT_FALSE(v.equals(__llvm_libc::cpp::StringView("abcde")));
}
+TEST(LlvmLibcStringViewTest, startsWith) {
+ __llvm_libc::cpp::StringView v("abc");
+ ASSERT_TRUE(v.starts_with(__llvm_libc::cpp::StringView("a")));
+ ASSERT_TRUE(v.starts_with(__llvm_libc::cpp::StringView("ab")));
+ ASSERT_TRUE(v.starts_with(__llvm_libc::cpp::StringView("abc")));
+ ASSERT_TRUE(v.starts_with(__llvm_libc::cpp::StringView()));
+ ASSERT_TRUE(v.starts_with(__llvm_libc::cpp::StringView("")));
+ ASSERT_FALSE(v.starts_with(__llvm_libc::cpp::StringView("123")));
+ ASSERT_FALSE(v.starts_with(__llvm_libc::cpp::StringView("abd")));
+ ASSERT_FALSE(v.starts_with(__llvm_libc::cpp::StringView("aaa")));
+ ASSERT_FALSE(v.starts_with(__llvm_libc::cpp::StringView("abcde")));
+}
+
TEST(LlvmLibcStringViewTest, RemovePrefix) {
__llvm_libc::cpp::StringView v("123456789");
More information about the libc-commits
mailing list