Merge "Use a class for system properties filenames." into main
diff --git a/docs/status.md b/docs/status.md
index ad7e1c5..3c5d1ba 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -64,7 +64,7 @@
     multi-threaded C).
   * `mbsrtowcs_l` and `wcsrtombs_l` aliases for `mbsrtowcs` and `wcsrtombs`.
   * New system call wrappers: `__riscv_flush_icache` (`<sys/cachectl.h>`),
-    `__riscv_hwprobe` (`<sys/hwprobe.h>`).
+    `__riscv_hwprobe` (`<sys/hwprobe.h>`), `epoll_pwait2`/`epoll_pwait2_64` (`<sys/epoll.h>`).
 
 New libc behavior in V (API level 35):
   * Added `LD_SHOW_AUXV` to the dynamic linker to dump the ELF auxiliary
diff --git a/libc/Android.bp b/libc/Android.bp
index fe263fd..99455c1 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -2282,6 +2282,8 @@
         "bionic", // crtbegin.c includes bionic/libc_init_common.h
     ],
 
+    cflags: [ "-DCRTBEGIN_STATIC", ],
+
     srcs: ["arch-common/bionic/crtbegin.c"],
     objs: [
         "crtbrand",
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index e8dde7c..0db5d79 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -322,6 +322,7 @@
 int __epoll_create1:epoll_create1(int)  all
 int epoll_ctl(int, int op, int, struct epoll_event*)  all
 int __epoll_pwait:epoll_pwait(int, struct epoll_event*, int, int, const sigset64_t*, size_t)  all
+int __epoll_pwait2:epoll_pwait2(int, struct epoll_event*, int, const timespec64*, const sigset64_t*, size_t)  all
 
 int __eventfd:eventfd2(unsigned int, int)  all
 
diff --git a/libc/arch-common/bionic/crtbegin.c b/libc/arch-common/bionic/crtbegin.c
index b87db64..127896a 100644
--- a/libc/arch-common/bionic/crtbegin.c
+++ b/libc/arch-common/bionic/crtbegin.c
@@ -30,17 +30,53 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#define SECTION(name) __attribute__((__section__(name)))
-SECTION(".preinit_array") init_func_t* __PREINIT_ARRAY__ = (init_func_t*)-1;
-SECTION(".init_array.0") init_func_t* __INIT_ARRAY__ = (init_func_t*)-1;
-SECTION(".fini_array.0") fini_func_t* __FINI_ARRAY__ = (fini_func_t*)-1;
-#undef SECTION
+extern init_func_t* __preinit_array_start[];
+extern init_func_t* __preinit_array_end[];
+extern init_func_t* __init_array_start[];
+extern init_func_t* __init_array_end[];
+extern fini_func_t* __fini_array_start[];
+extern fini_func_t* __fini_array_end[];
+
+#if !defined(CRTBEGIN_STATIC)
+/* This function will be called during normal program termination
+ * to run the destructors that are listed in the .fini_array section
+ * of the executable, if any.
+ *
+ * 'fini_array' points to a list of function addresses.
+ */
+static void call_fini_array() {
+  fini_func_t** array = __fini_array_start;
+  size_t count = __fini_array_end - __fini_array_start;
+  // Call fini functions in reverse order.
+  while (count-- > 0) {
+    fini_func_t* function = array[count];
+    (*function)();
+  }
+}
+
+// libc.so needs fini_array with sentinels. So create a fake fini_array with sentinels.
+// It contains a function to call functions in real fini_array.
+static fini_func_t* fini_array_with_sentinels[] = {
+    (fini_func_t*)-1,
+    &call_fini_array,
+    (fini_func_t*)0,
+};
+#endif  // !defined(CRTBEGIN_STATIC)
 
 __used static void _start_main(void* raw_args) {
-  structors_array_t array;
-  array.preinit_array = &__PREINIT_ARRAY__;
-  array.init_array = &__INIT_ARRAY__;
-  array.fini_array = &__FINI_ARRAY__;
+  structors_array_t array = {};
+#if defined(CRTBEGIN_STATIC)
+  array.preinit_array = __preinit_array_start;
+  array.preinit_array_count = __preinit_array_end - __preinit_array_start;
+  array.init_array = __init_array_start;
+  array.init_array_count = __init_array_end - __init_array_start;
+  array.fini_array = __fini_array_start;
+  array.fini_array_count = __fini_array_end - __fini_array_start;
+#else
+  if (__fini_array_end - __fini_array_start > 0) {
+    array.fini_array = fini_array_with_sentinels;
+  }
+#endif  // !defined(CRTBEGIN_STATIC)
 
   __libc_init(raw_args, NULL, &main, &array);
 }
diff --git a/libc/arch-common/bionic/crtend.S b/libc/arch-common/bionic/crtend.S
index 49c729f..74b3aa9 100644
--- a/libc/arch-common/bionic/crtend.S
+++ b/libc/arch-common/bionic/crtend.S
@@ -34,18 +34,6 @@
 __bionic_asm_custom_note_gnu_section()
 #endif
 
-	.section .preinit_array, "aw"
-	ASM_ALIGN_TO_PTR_SIZE
-	ASM_PTR_SIZE(0)
-
-	.section .init_array, "aw"
-	ASM_ALIGN_TO_PTR_SIZE
-	ASM_PTR_SIZE(0)
-
-	.section .fini_array, "aw"
-	ASM_ALIGN_TO_PTR_SIZE
-	ASM_PTR_SIZE(0)
-
 	.section .note.GNU-stack, "", %progbits
 
 #if !defined(__arm__)
diff --git a/libc/bionic/libc_init_common.h b/libc/bionic/libc_init_common.h
index 6b39d6d..126f002 100644
--- a/libc/bionic/libc_init_common.h
+++ b/libc/bionic/libc_init_common.h
@@ -38,6 +38,10 @@
   init_func_t** preinit_array;
   init_func_t** init_array;
   fini_func_t** fini_array;
+  // Below fields are only available in static executables.
+  size_t preinit_array_count;
+  size_t init_array_count;
+  size_t fini_array_count;
 } structors_array_t;
 
 __BEGIN_DECLS
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index a3c66d4..1591785 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -69,10 +69,21 @@
 extern "C" int __cxa_atexit(void (*)(void *), void *, void *);
 extern "C" const char* __gnu_basename(const char* path);
 
-static void call_array(init_func_t** list, int argc, char* argv[], char* envp[]) {
-  // First element is -1, list is null-terminated
-  while (*++list) {
-    (*list)(argc, argv, envp);
+static void call_array(init_func_t** list, size_t count, int argc, char* argv[], char* envp[]) {
+  while (count-- > 0) {
+    init_func_t* function = *list++;
+    (*function)(argc, argv, envp);
+  }
+}
+
+static void call_fini_array(void* arg) {
+  structors_array_t* structors = reinterpret_cast<structors_array_t*>(arg);
+  fini_func_t** array = structors->fini_array;
+  size_t count = structors->fini_array_count;
+  // Now call each destructor in reverse order.
+  while (count-- > 0) {
+    fini_func_t* function = array[count];
+    (*function)();
   }
 }
 
@@ -413,14 +424,15 @@
   // Several Linux ABIs don't pass the onexit pointer, and the ones that
   // do never use it.  Therefore, we ignore it.
 
-  call_array(structors->preinit_array, args.argc, args.argv, args.envp);
-  call_array(structors->init_array, args.argc, args.argv, args.envp);
+  call_array(structors->preinit_array, structors->preinit_array_count, args.argc, args.argv,
+             args.envp);
+  call_array(structors->init_array, structors->init_array_count, args.argc, args.argv, args.envp);
 
   // The executable may have its own destructors listed in its .fini_array
   // so we need to ensure that these are called when the program exits
   // normally.
-  if (structors->fini_array != nullptr) {
-    __cxa_atexit(__libc_fini,structors->fini_array,nullptr);
+  if (structors->fini_array_count > 0) {
+    __cxa_atexit(call_fini_array, const_cast<structors_array_t*>(structors), nullptr);
   }
 
   __libc_init_mte_late();
diff --git a/libc/bionic/sys_epoll.cpp b/libc/bionic/sys_epoll.cpp
index cffa173..be97818 100644
--- a/libc/bionic/sys_epoll.cpp
+++ b/libc/bionic/sys_epoll.cpp
@@ -34,6 +34,8 @@
 
 extern "C" int __epoll_create1(int flags);
 extern "C" int __epoll_pwait(int, epoll_event*, int, int, const sigset64_t*, size_t);
+extern "C" int __epoll_pwait2(int, epoll_event*, int, const __kernel_timespec*, const sigset64_t*,
+                              size_t);
 
 int epoll_create(int size) {
   if (size <= 0) {
@@ -56,6 +58,31 @@
   return __epoll_pwait(fd, events, max_events, timeout, ss, sizeof(*ss));
 }
 
+int epoll_pwait2(int fd, epoll_event* events, int max_events, const timespec* timeout,
+                 const sigset_t* ss) {
+  SigSetConverter set{ss};
+  return epoll_pwait2_64(fd, events, max_events, timeout, set.ptr);
+}
+
+int epoll_pwait2_64(int fd, epoll_event* events, int max_events, const timespec* timeout,
+                    const sigset64_t* ss) {
+  // epoll_pwait2() is our first syscall that assumes a 64-bit time_t even for
+  // 32-bit processes, so for ILP32 we need to convert.
+  // TODO: factor this out into a TimeSpecConverter as/when we get more syscalls like this.
+#if __LP64__
+  const __kernel_timespec* kts_ptr = reinterpret_cast<const __kernel_timespec*>(timeout);
+#else
+  __kernel_timespec kts;
+  const __kernel_timespec* kts_ptr = nullptr;
+  if (timeout) {
+    kts.tv_sec = timeout->tv_sec;
+    kts.tv_nsec = timeout->tv_nsec;
+    kts_ptr = &kts;
+  }
+#endif
+  return __epoll_pwait2(fd, events, max_events, kts_ptr, ss, sizeof(*ss));
+}
+
 int epoll_wait(int fd, struct epoll_event* events, int max_events, int timeout) {
   return epoll_pwait64(fd, events, max_events, timeout, nullptr);
 }
diff --git a/libc/include/sys/epoll.h b/libc/include/sys/epoll.h
index 2091b90..2bad655 100644
--- a/libc/include/sys/epoll.h
+++ b/libc/include/sys/epoll.h
@@ -26,8 +26,12 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _SYS_EPOLL_H_
-#define _SYS_EPOLL_H_
+#pragma once
+
+/**
+ * @file sys/epoll.h
+ * @brief I/O event file descriptors.
+ */
 
 #include <sys/cdefs.h>
 #include <sys/types.h>
@@ -37,14 +41,67 @@
 
 __BEGIN_DECLS
 
+/**
+ * [epoll_create(2)](http://man7.org/linux/man-pages/man2/epoll_create.2.html)
+ * creates a new [epoll](http://man7.org/linux/man-pages/man7/epoll.7.html)
+ * file descriptor.
+ *
+ * Returns a new file descriptor on success and returns -1 and sets `errno` on
+ * failure.
+ */
 int epoll_create(int __size);
+
+/**
+ * [epoll_create1(2)](http://man7.org/linux/man-pages/man2/epoll_create1.2.html)
+ * creates a new [epoll](http://man7.org/linux/man-pages/man7/epoll.7.html)
+ * file descriptor with the given flags.
+ *
+ * Returns a new file descriptor on success and returns -1 and sets `errno` on
+ * failure.
+ */
 int epoll_create1(int __flags);
 
+/**
+ * [epoll_ctl(2)](http://man7.org/linux/man-pages/man2/epoll_ctl.2.html)
+ * adds/modifies/removes file descriptors from the given epoll file descriptor.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
 int epoll_ctl(int __epoll_fd, int __op, int __fd, struct epoll_event* __BIONIC_COMPLICATED_NULLNESS __event);
+
+/**
+ * [epoll_wait(2)](http://man7.org/linux/man-pages/man2/epoll_wait.2.html)
+ * waits for an event on the given epoll file descriptor.
+ *
+ * Returns the number of ready file descriptors on success, 0 on timeout,
+ * or -1 and sets `errno` on failure.
+ */
 int epoll_wait(int __epoll_fd, struct epoll_event* _Nonnull __events, int __event_count, int __timeout_ms);
+
+/**
+ * Like epoll_wait() but atomically applying the given signal mask.
+ */
 int epoll_pwait(int __epoll_fd, struct epoll_event* _Nonnull __events, int __event_count, int __timeout_ms, const sigset_t* _Nullable __mask);
+
+/**
+ * Like epoll_pwait() but using a 64-bit signal mask even on 32-bit systems.
+ *
+ * Available since API level 28.
+ */
 int epoll_pwait64(int __epoll_fd, struct epoll_event* _Nonnull __events, int __event_count, int __timeout_ms, const sigset64_t* _Nullable __mask) __INTRODUCED_IN(28);
 
-__END_DECLS
+/**
+ * Like epoll_pwait() but with a `struct timespec` timeout, for nanosecond resolution.
+ *
+ * Available since API level 35.
+ */
+int epoll_pwait2(int __epoll_fd, struct epoll_event* _Nonnull __events, int __event_count, const struct timespec* _Nullable __timeout, const sigset_t* _Nullable __mask) __INTRODUCED_IN(35);
 
-#endif
+/**
+ * Like epoll_pwait2() but using a 64-bit signal mask even on 32-bit systems.
+ *
+ * Available since API level 35.
+ */
+int epoll_pwait2_64(int __epoll_fd, struct epoll_event* _Nonnull __events, int __event_count, const struct timespec* _Nullable __timeout, const sigset64_t* _Nullable __mask) __INTRODUCED_IN(35);
+
+__END_DECLS
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index b3ef185..e6ea3c2 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1586,6 +1586,8 @@
 
 LIBC_V { # introduced=VanillaIceCream
   global:
+    epoll_pwait2;
+    epoll_pwait2_64;
     localtime_rz;
     mbsrtowcs_l;
     mktime_z;
diff --git a/tests/sys_epoll_test.cpp b/tests/sys_epoll_test.cpp
index fb2a48f..8dee93f 100644
--- a/tests/sys_epoll_test.cpp
+++ b/tests/sys_epoll_test.cpp
@@ -24,29 +24,101 @@
 
 #include "utils.h"
 
-TEST(sys_epoll, smoke) {
+TEST(sys_epoll, epoll_wait) {
   int epoll_fd = epoll_create(1);
-  ASSERT_NE(-1, epoll_fd) << strerror(errno);
-  epoll_event events[1];
+  ASSERT_NE(-1, epoll_fd);
 
   // Regular epoll_wait.
+  epoll_event events[1] = {};
   ASSERT_EQ(0, epoll_wait(epoll_fd, events, 1, 1));
+}
+
+TEST(sys_epoll, epoll_pwait_no_sigset) {
+  int epoll_fd = epoll_create(1);
+  ASSERT_NE(-1, epoll_fd);
 
   // epoll_pwait without a sigset (which is equivalent to epoll_wait).
+  epoll_event events[1] = {};
   ASSERT_EQ(0, epoll_pwait(epoll_fd, events, 1, 1, nullptr));
+}
 
+TEST(sys_epoll, epoll_pwait64_no_sigset) {
 #if defined(__BIONIC__)
+  int epoll_fd = epoll_create(1);
+  ASSERT_NE(-1, epoll_fd);
+
   // epoll_pwait64 without a sigset (which is equivalent to epoll_wait).
+  epoll_event events[1] = {};
   ASSERT_EQ(0, epoll_pwait64(epoll_fd, events, 1, 1, nullptr));
+#else
+  GTEST_SKIP() << "epoll_pwait64 is bionic-only";
 #endif
+}
+
+TEST(sys_epoll, epoll_pwait2_no_sigset) {
+#if defined(__BIONIC__)
+  int epoll_fd = epoll_create(1);
+  ASSERT_NE(-1, epoll_fd);
+
+  // epoll_pwait2 without a sigset (which is equivalent to epoll_wait).
+  epoll_event events[1] = {};
+  timespec ts = {.tv_nsec = 500};
+  int rc = epoll_pwait2(epoll_fd, events, 1, &ts, nullptr);
+  if (rc == -1 && errno == ENOSYS) GTEST_SKIP() << "no epoll_pwait2 in this kernel";
+  ASSERT_EQ(0, rc) << strerror(errno);
+#else
+  GTEST_SKIP() << "epoll_pwait2 is only in glibc 2.35+";
+#endif
+}
+
+TEST(sys_epoll, epoll_pwait_with_sigset) {
+  int epoll_fd = epoll_create(1);
+  ASSERT_NE(-1, epoll_fd);
 
   // epoll_pwait with a sigset.
+  epoll_event events[1] = {};
   sigset_t ss;
   sigemptyset(&ss);
   sigaddset(&ss, SIGPIPE);
   ASSERT_EQ(0, epoll_pwait(epoll_fd, events, 1, 1, &ss));
 }
 
+TEST(sys_epoll, epoll_pwait2_with_sigset) {
+  int epoll_fd = epoll_create(1);
+  ASSERT_NE(-1, epoll_fd);
+
+#if defined(__BIONIC__)
+  epoll_event events[1] = {};
+  timespec ts = {.tv_nsec = 500};
+  sigset_t ss2;
+  sigemptyset(&ss2);
+  sigaddset(&ss2, SIGPIPE);
+  int rc = epoll_pwait2(epoll_fd, events, 1, &ts, &ss2);
+  if (rc == -1 && errno == ENOSYS) GTEST_SKIP() << "no epoll_pwait2 in this kernel";
+  ASSERT_EQ(0, rc) << strerror(errno);
+#else
+  GTEST_SKIP() << "epoll_pwait2 is only in glibc 2.35+";
+#endif
+}
+
+TEST(sys_epoll, epoll_pwait2_64_with_sigset) {
+  int epoll_fd = epoll_create(1);
+  ASSERT_NE(-1, epoll_fd);
+
+#if defined(__BIONIC__)
+  epoll_event events[1] = {};
+  timespec ts = {.tv_nsec = 500};
+  sigset64_t ss2;
+  sigemptyset64(&ss2);
+  sigaddset64(&ss2, SIGPIPE);
+  int rc = epoll_pwait2_64(epoll_fd, events, 1, &ts, &ss2);
+  if (rc == -1 && errno == ENOSYS) GTEST_SKIP() << "no epoll_pwait2 in this kernel";
+  ASSERT_EQ(0, rc) << strerror(errno);
+#else
+  GTEST_SKIP() << "epoll_pwait2_64 is bionic-only";
+#endif
+}
+
 TEST(sys_epoll, epoll_create_invalid_size) {
   errno = 0;
   ASSERT_EQ(-1, epoll_create(0));