Merge "Alias lockf and lock64 on LP64." into main
diff --git a/docs/status.md b/docs/status.md
index 0436c40..0810499 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -56,7 +56,7 @@
Current libc symbols: https://android.googlesource.com/platform/bionic/+/main/libc/libc.map.txt
New libc functions in API level 36:
- * `qsort_r` (new POSIX addition).
+ * `qsort_r`, `sig2str`/`str2sig` (POSIX Issue 8 additions).
New libc functions in V (API level 35):
* New `android_crash_detail_register`, `android_crash_detail_unregister`,
diff --git a/libc/Android.bp b/libc/Android.bp
index 42ffd37..9265b4d 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -571,7 +571,6 @@
"upstream-openbsd/lib/libc/stdio/fgetwc.c",
"upstream-openbsd/lib/libc/stdio/fgetws.c",
"upstream-openbsd/lib/libc/stdio/flags.c",
- "upstream-openbsd/lib/libc/stdio/fpurge.c",
"upstream-openbsd/lib/libc/stdio/fputwc.c",
"upstream-openbsd/lib/libc/stdio/fputws.c",
"upstream-openbsd/lib/libc/stdio/fvwrite.c",
diff --git a/libc/bionic/strsignal.cpp b/libc/bionic/strsignal.cpp
index f18b6d0..29c22e2 100644
--- a/libc/bionic/strsignal.cpp
+++ b/libc/bionic/strsignal.cpp
@@ -27,29 +27,29 @@
*/
#include <signal.h>
+#include <stdlib.h>
#include <string.h>
#include "bionic/pthread_internal.h"
+// Maps regular signals like SIGSEGV to strings like "Segmentation fault".
+// Signal 0 and all the real-time signals are just nullptr, but that's the ABI.
const char* const sys_siglist[NSIG] = {
#define __BIONIC_SIGDEF(signal_number, signal_description) [signal_number] = signal_description,
#include "private/bionic_sigdefs.h"
};
+// Maps regular signals like SIGSEGV to strings like "SEGV".
+// Signal 0 and all the real-time signals are just nullptr, but that's the ABI.
const char* const sys_signame[NSIG] = {
#define __BIONIC_SIGDEF(signal_number, unused) [signal_number] = &(#signal_number)[3],
#include "private/bionic_sigdefs.h"
};
extern "C" __LIBC_HIDDEN__ const char* __strsignal(int signal_number, char* buf, size_t buf_len) {
- const char* signal_name = nullptr;
- if (signal_number >= 0 && signal_number < NSIG) {
- signal_name = sys_siglist[signal_number];
+ if (signal_number >= SIGHUP && signal_number < SIGSYS) {
+ return sys_siglist[signal_number];
}
- if (signal_name != nullptr) {
- return signal_name;
- }
-
const char* prefix = "Unknown";
if (signal_number >= SIGRTMIN && signal_number <= SIGRTMAX) {
prefix = "Real-time";
@@ -66,3 +66,72 @@
bionic_tls& tls = __get_bionic_tls();
return const_cast<char*>(__strsignal(signal_number, tls.strsignal_buf, sizeof(tls.strsignal_buf)));
}
+
+int sig2str(int sig, char* str) {
+ if (sig >= SIGHUP && sig <= SIGSYS) {
+ strcpy(str, sys_signame[sig]);
+ return 0;
+ }
+ if (sig == SIGRTMIN) {
+ strcpy(str, "RTMIN");
+ return 0;
+ }
+ if (sig == SIGRTMAX) {
+ strcpy(str, "RTMAX");
+ return 0;
+ }
+ if (sig > SIGRTMIN && sig < SIGRTMAX) {
+ if (sig - SIGRTMIN <= SIGRTMAX - sig) {
+ sprintf(str, "RTMIN+%d", sig - SIGRTMIN);
+ } else {
+ sprintf(str, "RTMAX-%d", SIGRTMAX - sig);
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int str2sig(const char* str, int* sig) {
+ // A name in our list, like "SEGV"?
+ for (size_t i = SIGHUP; i <= SIGSYS; ++i) {
+ if (!strcmp(str, sys_signame[i])) {
+ *sig = i;
+ return 0;
+ }
+ }
+
+ // The two named special cases?
+ if (!strcmp(str, "RTMIN")) {
+ *sig = SIGRTMIN;
+ return 0;
+ }
+ if (!strcmp(str, "RTMAX")) {
+ *sig = SIGRTMAX;
+ return 0;
+ }
+
+ // Must be either an integer corresponding to a regular signal such as "9",
+ // or a string of the form "RTMIN+%d" or "RTMAX-%d".
+ int base = 0;
+ if (!strncmp(str, "RTMIN+", 6)) {
+ base = SIGRTMIN;
+ str += 5;
+ } else if (!strncmp(str, "RTMAX-", 6)) {
+ base = SIGRTMAX;
+ str += 5;
+ }
+ char* end = nullptr;
+ errno = 0;
+ int offset = strtol(str, &end, 10);
+ if (errno || *end) return -1;
+
+ // Reject out of range integers (like "666"),
+ // and out of range real-time signals (like "RTMIN+666" or "RTMAX-666").
+ int result = base + offset;
+ bool regular = (base == 0 && result >= SIGHUP && result <= SIGSYS);
+ bool realtime = (result >= SIGRTMIN && result <= SIGRTMAX);
+ if (!regular && !realtime) return -1;
+
+ *sig = result;
+ return 0;
+}
diff --git a/libc/include/elf.h b/libc/include/elf.h
index 374d5bb..24454d7 100644
--- a/libc/include/elf.h
+++ b/libc/include/elf.h
@@ -272,14 +272,12 @@
/* riscv64 psabi. */
-/* FreeBSD is missing these, found in
+/*
* https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#relocations
- * so I've sent https://github.com/freebsd/freebsd-src/pull/1141 upstream.
+ * Missing from FreeBSD and the Linux uapi headers.
+ * TODO: upstreamed to FreeBSD as https://github.com/freebsd/freebsd-src/pull/1141.
*/
#define R_RISCV_TLSDESC 12
-#define R_RISCV_PLT32 59
-#define R_RISCV_SET_ULEB128 60
-#define R_RISCV_SUB_ULEB128 61
#define R_RISCV_TLSDESC_HI20 62
#define R_RISCV_TLSDESC_LOAD_LO12 63
#define R_RISCV_TLSDESC_ADD_LO12 64
diff --git a/libc/include/signal.h b/libc/include/signal.h
index 9d47bcc..a827c2f 100644
--- a/libc/include/signal.h
+++ b/libc/include/signal.h
@@ -122,6 +122,34 @@
int sigwaitinfo(const sigset_t* _Nonnull __set, siginfo_t* _Nullable __info) __INTRODUCED_IN(23);
int sigwaitinfo64(const sigset64_t* _Nonnull __set, siginfo_t* _Nullable __info) __INTRODUCED_IN(28);
+/**
+ * Buffer size suitable for any call to sig2str().
+ */
+#define SIG2STR_MAX 32
+
+/**
+ * [sig2str(3)](http://man7.org/linux/man-pages/man3/sig2str.3.html)
+ * converts the integer corresponding to SIGSEGV (say) into a string
+ * like "SEGV" (not including the "SIG" used in the constants).
+ * SIG2STR_MAX is a safe size to use for the buffer.
+ *
+ * Returns 0 on success, and returns -1 _without_ setting errno otherwise.
+ *
+ * Available since API level 36.
+ */
+int sig2str(int __signal, char* _Nonnull __buf) __INTRODUCED_IN(36);
+
+/**
+ * [str2sig(3)](http://man7.org/linux/man-pages/man3/str2sig.3.html)
+ * converts a string like "SEGV" (not including the "SIG" used in the constants)
+ * into the integer corresponding to SIGSEGV.
+ *
+ * Returns 0 on success, and returns -1 _without_ setting errno otherwise.
+ *
+ * Available since API level 36.
+ */
+int str2sig(const char* _Nonnull __name, int* _Nonnull __signal) __INTRODUCED_IN(36);
+
__END_DECLS
#endif
diff --git a/libc/include/stdio_ext.h b/libc/include/stdio_ext.h
index 8b106a6..53589ec 100644
--- a/libc/include/stdio_ext.h
+++ b/libc/include/stdio_ext.h
@@ -89,10 +89,8 @@
/**
* [__fpurge(3)](http://man7.org/linux/man-pages/man3/__fpurge.3.html) discards the contents of
* the stream's buffer.
- *
- * Available since API level 23.
*/
-void __fpurge(FILE* _Nonnull __fp) __INTRODUCED_IN(23);
+void __fpurge(FILE* _Nonnull __fp) __RENAME(fpurge);
/**
* [__fpending(3)](http://man7.org/linux/man-pages/man3/__fpending.3.html) returns the number of
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 35e67c8..ffd5d1e 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -953,6 +953,7 @@
setvbuf;
setxattr;
shutdown;
+ sig2str; # introduced=36
sigaction;
sigaddset;
sigaltstack;
@@ -995,6 +996,7 @@
stdout; # var introduced=23
stpcpy;
stpncpy;
+ str2sig; # introduced=36
strcasecmp;
strcasecmp_l; # introduced=23
strcasestr;
diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp
index f18cd81..37b9665 100644
--- a/libc/stdio/stdio.cpp
+++ b/libc/stdio/stdio.cpp
@@ -1079,6 +1079,26 @@
return __sflush(fp);
}
+int fpurge(FILE* fp) {
+ CHECK_FP(fp);
+
+ ScopedFileLock sfl(fp);
+
+ if (fp->_flags == 0) {
+ // Already freed!
+ errno = EBADF;
+ return EOF;
+ }
+
+ if (HASUB(fp)) FREEUB(fp);
+ WCIO_FREE(fp);
+ fp->_p = fp->_bf._base;
+ fp->_r = 0;
+ fp->_w = fp->_flags & (__SLBF | __SNBF) ? 0 : fp->_bf._size;
+ return 0;
+}
+__strong_alias(__fpurge, fpurge);
+
size_t fread(void* buf, size_t size, size_t count, FILE* fp) {
CHECK_FP(fp);
ScopedFileLock sfl(fp);
diff --git a/libc/stdio/stdio_ext.cpp b/libc/stdio/stdio_ext.cpp
index 99a8af7..3eb2f33 100644
--- a/libc/stdio/stdio_ext.cpp
+++ b/libc/stdio/stdio_ext.cpp
@@ -59,10 +59,6 @@
return (fp->_flags & __SLBF) != 0;
}
-void __fpurge(FILE* fp) {
- fpurge(fp);
-}
-
size_t __fpending(FILE* fp) {
return fp->_p - fp->_bf._base;
}
diff --git a/libc/upstream-openbsd/lib/libc/stdio/fpurge.c b/libc/upstream-openbsd/lib/libc/stdio/fpurge.c
deleted file mode 100644
index 8dd8a91..0000000
--- a/libc/upstream-openbsd/lib/libc/stdio/fpurge.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/* $OpenBSD: fpurge.c,v 1.10 2015/08/31 02:53:57 guenther Exp $ */
-/*-
- * Copyright (c) 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Chris Torek.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "local.h"
-
-/*
- * fpurge: like fflush, but without writing anything: leave the
- * given FILE's buffer empty.
- */
-int
-fpurge(FILE *fp)
-{
- FLOCKFILE(fp);
- if (!fp->_flags) {
- FUNLOCKFILE(fp);
- errno = EBADF;
- return(EOF);
- }
-
- if (HASUB(fp))
- FREEUB(fp);
- WCIO_FREE(fp);
- fp->_p = fp->_bf._base;
- fp->_r = 0;
- fp->_w = fp->_flags & (__SLBF|__SNBF) ? 0 : fp->_bf._size;
- FUNLOCKFILE(fp);
- return (0);
-}
-DEF_WEAK(fpurge);
diff --git a/tests/headers/posix/signal_h.c b/tests/headers/posix/signal_h.c
index c2e544e..82751f4 100644
--- a/tests/headers/posix/signal_h.c
+++ b/tests/headers/posix/signal_h.c
@@ -63,6 +63,10 @@
MACRO(SIGEV_SIGNAL);
MACRO(SIGEV_THREAD);
+#if !defined(__GLIBC__) // Our glibc is too old.
+ MACRO(SIG2STR_MAX);
+#endif
+
TYPE(union sigval);
STRUCT_MEMBER(union sigval, int, sival_int);
STRUCT_MEMBER(union sigval, void*, sival_ptr);
@@ -205,6 +209,9 @@
FUNCTION(pthread_kill, int (*f)(pthread_t, int));
FUNCTION(pthread_sigmask, int (*f)(int, const sigset_t*, sigset_t*));
FUNCTION(raise, int (*f)(int));
+#if !defined(__GLIBC__) // Our glibc is too old.
+ FUNCTION(sig2str, int (*f)(int, char*));
+#endif
FUNCTION(sigaction, int (*f)(int, const struct sigaction*, struct sigaction*));
FUNCTION(sigaddset, int (*f)(sigset_t*, int));
FUNCTION(sigaltstack, int (*f)(const stack_t*, stack_t*));
@@ -226,4 +233,7 @@
FUNCTION(sigtimedwait, int (*f)(const sigset_t*, siginfo_t*, const struct timespec*));
FUNCTION(sigwait, int (*f)(const sigset_t*, int*));
FUNCTION(sigwaitinfo, int (*f)(const sigset_t*, siginfo_t*));
+#if !defined(__GLIBC__) // Our glibc is too old.
+ FUNCTION(str2sig, int (*f)(const char*, int*));
+#endif
}
diff --git a/tests/signal_test.cpp b/tests/signal_test.cpp
index de126da..c1719dc 100644
--- a/tests/signal_test.cpp
+++ b/tests/signal_test.cpp
@@ -982,3 +982,94 @@
ASSERT_EQ(-1, killpg(-1, SIGKILL));
ASSERT_ERRNO(EINVAL);
}
+
+TEST(signal, sig2str) {
+#if defined(__BIONIC__)
+ char str[SIG2STR_MAX];
+
+ // A regular signal.
+ ASSERT_EQ(0, sig2str(SIGHUP, str));
+ ASSERT_STREQ("HUP", str);
+
+ // A real-time signal.
+ ASSERT_EQ(0, sig2str(SIGRTMIN + 4, str));
+ ASSERT_STREQ("RTMIN+4", str);
+ ASSERT_EQ(0, sig2str(SIGRTMAX - 4, str));
+ ASSERT_STREQ("RTMAX-4", str);
+ // Special cases.
+ ASSERT_EQ(0, sig2str(SIGRTMAX, str));
+ ASSERT_STREQ("RTMAX", str);
+ ASSERT_EQ(0, sig2str(SIGRTMIN, str));
+ ASSERT_STREQ("RTMIN", str);
+ // One of the signals the C library keeps to itself.
+ ASSERT_EQ(-1, sig2str(32, str)); // __SIGRTMIN
+
+ // Errors.
+ ASSERT_EQ(-1, sig2str(-1, str)); // Too small.
+ ASSERT_EQ(-1, sig2str(0, str)); // Still too small.
+ ASSERT_EQ(-1, sig2str(1234, str)); // Too large.
+#else
+ GTEST_SKIP() << "our old glibc doesn't have sig2str";
+#endif
+}
+
+TEST(signal, str2sig) {
+#if defined(__BIONIC__)
+ int sig;
+
+ // A regular signal, by number.
+ sig = -1;
+ ASSERT_EQ(0, str2sig("9", &sig));
+ ASSERT_EQ(SIGKILL, sig);
+
+ // A regular signal, by name.
+ sig = -1;
+ ASSERT_EQ(0, str2sig("HUP", &sig));
+ ASSERT_EQ(SIGHUP, sig);
+
+ // A real-time signal, by number.
+ sig = -1;
+ ASSERT_EQ(0, str2sig("64", &sig));
+ ASSERT_EQ(SIGRTMAX, sig);
+
+ // A real-time signal, by name and offset.
+ sig = -1;
+ ASSERT_EQ(0, str2sig("RTMAX-4", &sig));
+ ASSERT_EQ(SIGRTMAX - 4, sig);
+ sig = -1;
+ ASSERT_EQ(0, str2sig("RTMIN+4", &sig));
+ ASSERT_EQ(SIGRTMIN + 4, sig);
+ // Unspecified by POSIX, but we try to be reasonable.
+ sig = -1;
+ ASSERT_EQ(0, str2sig("RTMAX-0", &sig));
+ ASSERT_EQ(SIGRTMAX, sig);
+ sig = -1;
+ ASSERT_EQ(0, str2sig("RTMIN+0", &sig));
+ ASSERT_EQ(SIGRTMIN, sig);
+ // One of the signals the C library keeps to itself, numerically.
+ ASSERT_EQ(-1, str2sig("32", &sig)); // __SIGRTMIN
+
+ // Special cases.
+ sig = -1;
+ ASSERT_EQ(0, str2sig("RTMAX", &sig));
+ ASSERT_EQ(SIGRTMAX, sig);
+ sig = -1;
+ ASSERT_EQ(0, str2sig("RTMIN", &sig));
+ ASSERT_EQ(SIGRTMIN, sig);
+
+ // Errors.
+ ASSERT_EQ(-1, str2sig("SIGHUP", &sig)); // No "SIG" prefix allowed.
+ ASSERT_EQ(-1, str2sig("-1", &sig)); // Too small.
+ ASSERT_EQ(-1, str2sig("0", &sig)); // Still too small.
+ ASSERT_EQ(-1, str2sig("1234", &sig)); // Too large.
+ ASSERT_EQ(-1, str2sig("RTMAX-666", &sig)); // Offset too small.
+ ASSERT_EQ(-1, str2sig("RTMIN+666", &sig)); // Offset too large.
+ ASSERT_EQ(-1, str2sig("RTMAX-+1", &sig)); // Silly.
+ ASSERT_EQ(-1, str2sig("RTMIN+-1", &sig)); // Silly.
+ ASSERT_EQ(-1, str2sig("HUPs", &sig)); // Trailing junk.
+ ASSERT_EQ(-1, str2sig("2b", &sig)); // Trailing junk.
+ ASSERT_EQ(-1, str2sig("RTMIN+2b", &sig)); // Trailing junk.
+#else
+ GTEST_SKIP() << "our old glibc doesn't have str2sig";
+#endif
+}