Skip to content

Commit f2a7f83

Browse files
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 <[email protected]> Reviewed By: sivachandra, rtenneti Differential Revision: https://reviews.llvm.org/D119403
1 parent 07b9a44 commit f2a7f83

File tree

12 files changed

+186
-3
lines changed

12 files changed

+186
-3
lines changed

libc/config/linux/app.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,13 @@ struct AppProperties {
3333

3434
// The properties of an application's TLS.
3535
TLS tls;
36+
37+
// Environment data.
38+
uint64_t *envPtr;
3639
};
3740

41+
extern AppProperties app;
42+
3843
// Creates and initializes the TLS area for the current thread. Should not
3944
// be called before app.tls has been initialized.
4045
void initTLS();

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ set(TARGET_LIBC_ENTRYPOINTS
7272
libc.src.stdlib.atoll
7373
libc.src.stdlib.bsearch
7474
libc.src.stdlib.div
75+
libc.src.stdlib.getenv
7576
libc.src.stdlib.labs
7677
libc.src.stdlib.ldiv
7778
libc.src.stdlib.llabs

libc/loader/linux/x86_64/start.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ static constexpr long mmapSyscallNumber = SYS_mmap;
2929
#error "Target platform does not have SYS_mmap or SYS_mmap2 defined"
3030
#endif
3131

32-
// TODO: Declare var an extern var in config/linux/app.h so that other
33-
// libc functions can make use of the application wide information. For
34-
// example, mmap can pick up the page size from here.
3532
AppProperties app;
3633

3734
// TODO: The function is x86_64 specific. Move it to config/linux/app.h
@@ -109,6 +106,7 @@ extern "C" void _start() {
109106
// value. We step over it (the "+ 1" below) to get to the env values.
110107
uint64_t *env_ptr = args->argv + args->argc + 1;
111108
uint64_t *env_end_marker = env_ptr;
109+
app.envPtr = env_ptr;
112110
while (*env_end_marker)
113111
++env_end_marker;
114112

libc/spec/posix.td

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,20 @@ def POSIX : StandardSpec<"POSIX"> {
265265
]
266266
>;
267267

268+
HeaderSpec StdLib = HeaderSpec<
269+
"stdlib.h",
270+
[], // Macros
271+
[], // Types
272+
[], // Enumerations
273+
[
274+
FunctionSpec<
275+
"getenv",
276+
RetValSpec<CharPtr>,
277+
[ArgSpec<ConstCharPtr>]
278+
>,
279+
]
280+
>;
281+
268282
HeaderSpec String = HeaderSpec<
269283
"string.h",
270284
[
@@ -356,6 +370,7 @@ def POSIX : StandardSpec<"POSIX"> {
356370
Errno,
357371
FCntl,
358372
Signal,
373+
StdLib,
359374
SysMMan,
360375
SysStat,
361376
UniStd,

libc/src/__support/CPP/StringView.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ class StringView {
8181
return remove_prefix(PrefixLen).remove_suffix(SuffixLen);
8282
}
8383

84+
// Check if this string starts with the given \p Prefix.
85+
bool starts_with(StringView Prefix) const {
86+
if (Len < Prefix.Len)
87+
return false;
88+
for (size_t I = 0; I < Prefix.Len; ++I) {
89+
if (Data[I] != Prefix.Data[I])
90+
return false;
91+
}
92+
return true;
93+
}
94+
8495
// An equivalent method is not available in std::string_view.
8596
bool equals(StringView Other) const {
8697
if (Len != Other.Len)

libc/src/stdlib/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ add_entrypoint_object(
3838
libc.src.__support.str_to_integer
3939
)
4040

41+
add_entrypoint_object(
42+
getenv
43+
SRCS
44+
getenv.cpp
45+
HDRS
46+
getenv.h
47+
DEPENDS
48+
libc.config.linux.app_h
49+
libc.src.string.strncmp
50+
)
51+
4152
add_entrypoint_object(
4253
strtof
4354
SRCS

libc/src/stdlib/getenv.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//===-- Implementation of getenv ------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/stdlib/getenv.h"
10+
#include "config/linux/app.h"
11+
#include "src/__support/CPP/StringView.h"
12+
#include "src/__support/common.h"
13+
14+
#include <stddef.h> // For size_t.
15+
16+
namespace __llvm_libc {
17+
18+
LLVM_LIBC_FUNCTION(char *, getenv, (const char *name)) {
19+
char **env_ptr = reinterpret_cast<char **>(__llvm_libc::app.envPtr);
20+
21+
if (name == nullptr || env_ptr == nullptr)
22+
return nullptr;
23+
24+
__llvm_libc::cpp::StringView env_var_name(name);
25+
if (env_var_name.size() == 0)
26+
return nullptr;
27+
for (char **env = env_ptr; *env != nullptr; env++) {
28+
__llvm_libc::cpp::StringView cur(*env);
29+
if (!cur.starts_with(env_var_name))
30+
continue;
31+
32+
if (cur[env_var_name.size()] != '=')
33+
continue;
34+
35+
return const_cast<char *>(
36+
cur.remove_prefix(env_var_name.size() + 1).data());
37+
}
38+
39+
return nullptr;
40+
}
41+
42+
} // namespace __llvm_libc

libc/src/stdlib/getenv.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//===-- Implementation header for getenv --------------------------------*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STDLIB_GETENV_H
10+
#define LLVM_LIBC_SRC_STDLIB_GETENV_H
11+
12+
namespace __llvm_libc {
13+
14+
char *getenv(const char *name);
15+
16+
} // namespace __llvm_libc
17+
18+
#endif // LLVM_LIBC_SRC_STDLIB_GETENV_H

libc/test/loader/linux/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ add_loader_test(
4343
libc.loader.linux.crt1
4444
)
4545

46+
add_loader_test(
47+
getenv_test
48+
SRC
49+
getenv_test.cpp
50+
DEPENDS
51+
.loader_test
52+
libc.loader.linux.crt1
53+
libc.src.stdlib.getenv
54+
ENV
55+
FRANCE=Paris
56+
GERMANY=Berlin
57+
)
58+
59+
4660
# TODO: Disableing this test temporarily to investigate why gold fails to link
4761
# and produce an executable for this test. Test works all fine with ld.bfd.
4862
#add_loader_test(
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//===-- Unittests for getenv ----------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "loader_test.h"
10+
#include "src/stdlib/getenv.h"
11+
12+
static bool my_streq(const char *lhs, const char *rhs) {
13+
if (lhs == rhs)
14+
return true;
15+
if (((lhs == static_cast<char *>(nullptr)) &&
16+
(rhs != static_cast<char *>(nullptr))) ||
17+
((lhs != static_cast<char *>(nullptr)) &&
18+
(rhs == static_cast<char *>(nullptr)))) {
19+
return false;
20+
}
21+
const char *l, *r;
22+
for (l = lhs, r = rhs; *l != '\0' && *r != '\0'; ++l, ++r)
23+
if (*l != *r)
24+
return false;
25+
26+
return *l == '\0' && *r == '\0';
27+
}
28+
29+
int main(int argc, char **argv, char **envp) {
30+
ASSERT_TRUE(my_streq(__llvm_libc::getenv(""), static_cast<char *>(nullptr)));
31+
ASSERT_TRUE(my_streq(__llvm_libc::getenv("="), static_cast<char *>(nullptr)));
32+
ASSERT_TRUE(my_streq(__llvm_libc::getenv("MISSING ENV VARIABLE"),
33+
static_cast<char *>(nullptr)));
34+
ASSERT_FALSE(
35+
my_streq(__llvm_libc::getenv("PATH"), static_cast<char *>(nullptr)));
36+
ASSERT_TRUE(my_streq(__llvm_libc::getenv("FRANCE"), "Paris"));
37+
ASSERT_FALSE(my_streq(__llvm_libc::getenv("FRANCE"), "Berlin"));
38+
ASSERT_TRUE(my_streq(__llvm_libc::getenv("GERMANY"), "Berlin"));
39+
ASSERT_TRUE(
40+
my_streq(__llvm_libc::getenv("FRANC"), static_cast<char *>(nullptr)));
41+
ASSERT_TRUE(
42+
my_streq(__llvm_libc::getenv("FRANCE1"), static_cast<char *>(nullptr)));
43+
44+
return 0;
45+
}

libc/test/loader/linux/loader_test.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,17 @@
2121
__llvm_libc::quick_exit(127); \
2222
}
2323

24+
#define __CHECK_NE(file, line, val, should_exit) \
25+
if ((val)) { \
26+
__llvm_libc::write_to_stderr(file ":" __AS_STRING( \
27+
line) ": Expected '" #val "' to be false, but is true\n"); \
28+
if (should_exit) \
29+
__llvm_libc::quick_exit(127); \
30+
}
31+
2432
#define EXPECT_TRUE(val) __CHECK(__FILE__, __LINE__, val, false)
2533
#define ASSERT_TRUE(val) __CHECK(__FILE__, __LINE__, val, true)
34+
#define EXPECT_FALSE(val) __CHECK_NE(__FILE__, __LINE__, val, false)
35+
#define ASSERT_FALSE(val) __CHECK_NE(__FILE__, __LINE__, val, true)
2636

2737
#endif // LLVM_LIBC_TEST_LOADER_LINUX_LOADER_TEST_H

libc/test/src/__support/CPP/stringview_test.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@ TEST(LlvmLibcStringViewTest, Equals) {
4545
ASSERT_FALSE(v.equals(__llvm_libc::cpp::StringView("abcde")));
4646
}
4747

48+
TEST(LlvmLibcStringViewTest, startsWith) {
49+
__llvm_libc::cpp::StringView v("abc");
50+
ASSERT_TRUE(v.starts_with(__llvm_libc::cpp::StringView("a")));
51+
ASSERT_TRUE(v.starts_with(__llvm_libc::cpp::StringView("ab")));
52+
ASSERT_TRUE(v.starts_with(__llvm_libc::cpp::StringView("abc")));
53+
ASSERT_TRUE(v.starts_with(__llvm_libc::cpp::StringView()));
54+
ASSERT_TRUE(v.starts_with(__llvm_libc::cpp::StringView("")));
55+
ASSERT_FALSE(v.starts_with(__llvm_libc::cpp::StringView("123")));
56+
ASSERT_FALSE(v.starts_with(__llvm_libc::cpp::StringView("abd")));
57+
ASSERT_FALSE(v.starts_with(__llvm_libc::cpp::StringView("aaa")));
58+
ASSERT_FALSE(v.starts_with(__llvm_libc::cpp::StringView("abcde")));
59+
}
60+
4861
TEST(LlvmLibcStringViewTest, RemovePrefix) {
4962
__llvm_libc::cpp::StringView v("123456789");
5063

0 commit comments

Comments
 (0)