[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