Merge "Refactor pthread_key.cpp to be lock-free."
diff --git a/benchmarks/Benchmark.cpp b/benchmarks/Benchmark.cpp
index eea304f..5ca1d47 100644
--- a/benchmarks/Benchmark.cpp
+++ b/benchmarks/Benchmark.cpp
@@ -39,40 +39,45 @@
return static_cast<uint64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
}
+bool Benchmark::header_printed_;
+
std::vector<Benchmark*>& Benchmark::List() {
static std::vector<Benchmark*> list;
return list;
}
int Benchmark::MaxNameColumnWidth() {
- int max = 20;
+ size_t max = 20;
for (auto& benchmark : List()) {
max = std::max(max, benchmark->NameColumnWidth());
}
- return max;
+ return static_cast<int>(max);
}
-bool Benchmark::RunAll(std::vector<regex_t*>& regs) {
- bool ran_benchmark = false;
+size_t Benchmark::RunAll(std::vector<regex_t*>& regs) {
+ size_t benchmarks_run = 0;
+ header_printed_ = false;
for (auto& benchmark : List()) {
- if (benchmark->ShouldRun(regs)) {
- if (!ran_benchmark) {
- printf("%-*s %10s %10s\n", MaxNameColumnWidth(), "", "iterations", "ns/op");
- ran_benchmark = true;
- }
- benchmark->RunAll();
- }
+ benchmarks_run += benchmark->RunAllArgs(regs);
}
- return ran_benchmark;
+ return benchmarks_run;
}
-bool Benchmark::ShouldRun(std::vector<regex_t*>& regs) {
+void Benchmark::PrintHeader() {
+ if (!header_printed_) {
+ printf("%-*s %10s %10s\n", MaxNameColumnWidth(), "", "iterations", "ns/op");
+ header_printed_ = true;
+ }
+}
+
+template <typename T>
+bool BenchmarkT<T>::ShouldRun(std::vector<regex_t*>& regs, T arg) {
if (regs.empty()) {
return true;
}
for (const auto& re : regs) {
- if (regexec(re, Name().c_str(), 0, NULL, 0) != REG_NOMATCH) {
+ if (regexec(re, GetNameStr(arg).c_str(), 0, NULL, 0) != REG_NOMATCH) {
return true;
}
}
diff --git a/benchmarks/benchmark/Benchmark.h b/benchmarks/benchmark/Benchmark.h
index 16ae5fa..ae5c1a2 100644
--- a/benchmarks/benchmark/Benchmark.h
+++ b/benchmarks/benchmark/Benchmark.h
@@ -34,27 +34,29 @@
virtual std::string Name() = 0;
- virtual void RunAll() = 0;
-
- bool ShouldRun(std::vector<regex_t*>&);
+ virtual size_t RunAllArgs(std::vector<regex_t*>&) = 0;
void SetBenchmarkBytesProcessed(uint64_t bytes) { bytes_processed_ += bytes; }
void StopBenchmarkTiming();
void StartBenchmarkTiming();
// Run all of the benchmarks that have registered.
- static bool RunAll(std::vector<regex_t*>&);
+ static size_t RunAll(std::vector<regex_t*>&);
static std::vector<Benchmark*>& List();
static int MaxNameColumnWidth();
protected:
- virtual int NameColumnWidth() = 0;
+ virtual size_t NameColumnWidth() = 0;
uint64_t bytes_processed_;
uint64_t total_time_ns_;
uint64_t start_time_ns_;
+
+ static bool header_printed_;
+
+ static void PrintHeader();
};
template <typename T>
@@ -64,6 +66,7 @@
virtual ~BenchmarkT() {}
protected:
+ bool ShouldRun(std::vector<regex_t*>&, T arg);
void RunWithArg(T arg);
virtual void RunIterations(int, T) = 0;
virtual std::string GetNameStr(T) = 0;
@@ -75,8 +78,14 @@
virtual ~BenchmarkWithoutArg() {}
protected:
- virtual void RunAll() override {
- RunWithArg(nullptr);
+ virtual size_t RunAllArgs(std::vector<regex_t*>& regs) override {
+ size_t benchmarks_run = 0;
+ if (BenchmarkT<void*>::ShouldRun(regs, nullptr)) {
+ PrintHeader();
+ RunWithArg(nullptr);
+ benchmarks_run++;
+ }
+ return benchmarks_run;
}
virtual void RunIterations(int iters, void*) override {
@@ -85,8 +94,8 @@
virtual void Run(int) = 0;
- virtual int NameColumnWidth() override {
- return (int)Name().size();
+ virtual size_t NameColumnWidth() override {
+ return Name().size();
}
virtual std::string GetNameStr(void *) override;
@@ -104,20 +113,26 @@
}
protected:
- virtual int NameColumnWidth() override {
- int max = 0;
- for (const auto arg : args_) {
- max = std::max(max, (int)GetNameStr(arg).size());
+ virtual size_t NameColumnWidth() override {
+ size_t max = 0;
+ for (const auto& arg : args_) {
+ max = std::max(max, GetNameStr(arg).size());
}
return max;
}
std::string GetNameStr(T arg) override;
- virtual void RunAll() override {
- for (T arg : args_) {
- BenchmarkT<T>::RunWithArg(arg);
+ virtual size_t RunAllArgs(std::vector<regex_t*>& regs) override {
+ size_t benchmarks_run = 0;
+ for (T& arg : args_) {
+ if (BenchmarkT<T>::ShouldRun(regs, arg)) {
+ Benchmark::PrintHeader();
+ BenchmarkT<T>::RunWithArg(arg);
+ benchmarks_run++;
+ }
}
+ return benchmarks_run;
}
virtual void RunIterations(int iters, T arg) override {
diff --git a/benchmarks/main.cpp b/benchmarks/main.cpp
index b6984fc..5bce479 100644
--- a/benchmarks/main.cpp
+++ b/benchmarks/main.cpp
@@ -48,7 +48,7 @@
regs.push_back(re);
}
- if (!::testing::Benchmark::RunAll(regs)) {
+ if (::testing::Benchmark::RunAll(regs) == 0) {
fprintf(stderr, "No matching benchmarks!\n");
fprintf(stderr, "Available benchmarks:\n");
for (const auto& benchmark : ::testing::Benchmark::List()) {
diff --git a/libc/Android.mk b/libc/Android.mk
index 9dd3864..cb1d8c0 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -116,6 +116,7 @@
bionic/error.cpp \
bionic/eventfd_read.cpp \
bionic/eventfd_write.cpp \
+ bionic/faccessat.cpp \
bionic/fchmod.cpp \
bionic/fchmodat.cpp \
bionic/ffs.cpp \
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index aae7de7..150dd14 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -130,7 +130,7 @@
int __getdents64:getdents64(unsigned int, struct dirent*, unsigned int) arm,arm64,mips,mips64,x86,x86_64
int __openat:openat(int, const char*, int, mode_t) all
-int faccessat(int, const char*, int, int) all
+int ___faccessat:faccessat(int, const char*, int) all
int ___fchmodat:fchmodat(int, const char*, mode_t) all
int fchownat(int, const char*, uid_t, gid_t, int) all
int fstatat64|fstatat:fstatat64(int, const char*, struct stat*, int) arm,mips,x86
diff --git a/libc/arch-arm/syscalls/faccessat.S b/libc/arch-arm/syscalls/___faccessat.S
similarity index 81%
rename from libc/arch-arm/syscalls/faccessat.S
rename to libc/arch-arm/syscalls/___faccessat.S
index a1df5c0..1d09cf7 100644
--- a/libc/arch-arm/syscalls/faccessat.S
+++ b/libc/arch-arm/syscalls/___faccessat.S
@@ -2,7 +2,7 @@
#include <private/bionic_asm.h>
-ENTRY(faccessat)
+ENTRY(___faccessat)
mov ip, r7
ldr r7, =__NR_faccessat
swi #0
@@ -11,4 +11,5 @@
bxls lr
neg r0, r0
b __set_errno_internal
-END(faccessat)
+END(___faccessat)
+.hidden ___faccessat
diff --git a/libc/arch-arm64/syscalls/faccessat.S b/libc/arch-arm64/syscalls/___faccessat.S
similarity index 79%
rename from libc/arch-arm64/syscalls/faccessat.S
rename to libc/arch-arm64/syscalls/___faccessat.S
index 4c96cfa..6a41b69 100644
--- a/libc/arch-arm64/syscalls/faccessat.S
+++ b/libc/arch-arm64/syscalls/___faccessat.S
@@ -2,7 +2,7 @@
#include <private/bionic_asm.h>
-ENTRY(faccessat)
+ENTRY(___faccessat)
mov x8, __NR_faccessat
svc #0
@@ -11,4 +11,5 @@
b.hi __set_errno_internal
ret
-END(faccessat)
+END(___faccessat)
+.hidden ___faccessat
diff --git a/libc/arch-mips/syscalls/faccessat.S b/libc/arch-mips/syscalls/___faccessat.S
similarity index 82%
rename from libc/arch-mips/syscalls/faccessat.S
rename to libc/arch-mips/syscalls/___faccessat.S
index e616106..4e11bae 100644
--- a/libc/arch-mips/syscalls/faccessat.S
+++ b/libc/arch-mips/syscalls/___faccessat.S
@@ -2,7 +2,7 @@
#include <private/bionic_asm.h>
-ENTRY(faccessat)
+ENTRY(___faccessat)
.set noreorder
.cpload t9
li v0, __NR_faccessat
@@ -16,4 +16,5 @@
j t9
nop
.set reorder
-END(faccessat)
+END(___faccessat)
+.hidden ___faccessat
diff --git a/libc/arch-mips64/bionic/stat.cpp b/libc/arch-mips64/bionic/stat.cpp
index df63906..2767fbd 100644
--- a/libc/arch-mips64/bionic/stat.cpp
+++ b/libc/arch-mips64/bionic/stat.cpp
@@ -29,7 +29,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/syscall.h>
-#include <asm/unistd.h>
+#include <unistd.h>
struct kernel_stat {
unsigned int st_dev;
diff --git a/libc/arch-mips64/syscalls/faccessat.S b/libc/arch-mips64/syscalls/___faccessat.S
similarity index 85%
rename from libc/arch-mips64/syscalls/faccessat.S
rename to libc/arch-mips64/syscalls/___faccessat.S
index 18bb800..240625f 100644
--- a/libc/arch-mips64/syscalls/faccessat.S
+++ b/libc/arch-mips64/syscalls/___faccessat.S
@@ -2,7 +2,7 @@
#include <private/bionic_asm.h>
-ENTRY(faccessat)
+ENTRY(___faccessat)
.set push
.set noreorder
li v0, __NR_faccessat
@@ -22,4 +22,5 @@
j t9
move ra, t0
.set pop
-END(faccessat)
+END(___faccessat)
+.hidden ___faccessat
diff --git a/libc/arch-x86/syscalls/faccessat.S b/libc/arch-x86/syscalls/___faccessat.S
similarity index 70%
rename from libc/arch-x86/syscalls/faccessat.S
rename to libc/arch-x86/syscalls/___faccessat.S
index 9d52231..361a6ea 100644
--- a/libc/arch-x86/syscalls/faccessat.S
+++ b/libc/arch-x86/syscalls/___faccessat.S
@@ -2,7 +2,7 @@
#include <private/bionic_asm.h>
-ENTRY(faccessat)
+ENTRY(___faccessat)
pushl %ebx
.cfi_def_cfa_offset 8
.cfi_rel_offset ebx, 0
@@ -12,13 +12,9 @@
pushl %edx
.cfi_adjust_cfa_offset 4
.cfi_rel_offset edx, 0
- pushl %esi
- .cfi_adjust_cfa_offset 4
- .cfi_rel_offset esi, 0
- mov 20(%esp), %ebx
- mov 24(%esp), %ecx
- mov 28(%esp), %edx
- mov 32(%esp), %esi
+ mov 16(%esp), %ebx
+ mov 20(%esp), %ecx
+ mov 24(%esp), %edx
movl $__NR_faccessat, %eax
int $0x80
cmpl $-MAX_ERRNO, %eax
@@ -28,9 +24,9 @@
call __set_errno_internal
addl $4, %esp
1:
- popl %esi
popl %edx
popl %ecx
popl %ebx
ret
-END(faccessat)
+END(___faccessat)
+.hidden ___faccessat
diff --git a/libc/arch-x86_64/syscalls/faccessat.S b/libc/arch-x86_64/syscalls/___faccessat.S
similarity index 81%
rename from libc/arch-x86_64/syscalls/faccessat.S
rename to libc/arch-x86_64/syscalls/___faccessat.S
index 05a6e78..e8fd3f5 100644
--- a/libc/arch-x86_64/syscalls/faccessat.S
+++ b/libc/arch-x86_64/syscalls/___faccessat.S
@@ -2,8 +2,7 @@
#include <private/bionic_asm.h>
-ENTRY(faccessat)
- movq %rcx, %r10
+ENTRY(___faccessat)
movl $__NR_faccessat, %eax
syscall
cmpq $-MAX_ERRNO, %rax
@@ -13,4 +12,5 @@
call __set_errno_internal
1:
ret
-END(faccessat)
+END(___faccessat)
+.hidden ___faccessat
diff --git a/libc/bionic/dup2.cpp b/libc/bionic/dup2.cpp
index 0b8632b..98c5646 100644
--- a/libc/bionic/dup2.cpp
+++ b/libc/bionic/dup2.cpp
@@ -26,8 +26,19 @@
* SUCH DAMAGE.
*/
+#include <fcntl.h>
#include <unistd.h>
int dup2(int old_fd, int new_fd) {
+ // If old_fd is equal to new_fd and a valid file descriptor, dup2 returns
+ // old_fd without closing it. This is not true of dup3, so we have to
+ // handle this case ourselves.
+ if (old_fd == new_fd) {
+ if (fcntl(old_fd, F_GETFD) == -1) {
+ return -1;
+ }
+ return old_fd;
+ }
+
return dup3(old_fd, new_fd, 0);
}
diff --git a/libc/bionic/faccessat.cpp b/libc/bionic/faccessat.cpp
new file mode 100644
index 0000000..5f375e0
--- /dev/null
+++ b/libc/bionic/faccessat.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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 <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+extern "C" int ___faccessat(int, const char*, int);
+
+int faccessat(int dirfd, const char* pathname, int mode, int flags) {
+ // "The mode specifies the accessibility check(s) to be performed,
+ // and is either the value F_OK, or a mask consisting of the
+ // bitwise OR of one or more of R_OK, W_OK, and X_OK."
+ if ((mode != F_OK) && ((mode & ~(R_OK | W_OK | X_OK)) != 0) &&
+ ((mode & (R_OK | W_OK | X_OK)) == 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (flags != 0) {
+ // We deliberately don't support AT_SYMLINK_NOFOLLOW, a glibc
+ // only feature which is error prone and dangerous.
+ //
+ // AT_EACCESS isn't supported either. Android doesn't have setuid
+ // programs, and never runs code with euid!=uid. It could be
+ // implemented in an expensive way, following the model at
+ // https://gitlab.com/bminor/musl/commit/0a05eace163cee9b08571d2ff9d90f5e82d9c228
+ // but not worth it.
+ errno = EINVAL;
+ return -1;
+ }
+
+ return ___faccessat(dirfd, pathname, mode);
+}
diff --git a/libc/bionic/ndk_cruft.cpp b/libc/bionic/ndk_cruft.cpp
index 77412ac..109c523 100644
--- a/libc/bionic/ndk_cruft.cpp
+++ b/libc/bionic/ndk_cruft.cpp
@@ -26,8 +26,7 @@
* SUCH DAMAGE.
*/
-// This file perpetuates the mistakes of the past, but only for 32-bit targets.
-#if !defined(__LP64__)
+// This file perpetuates the mistakes of the past.
#include <ctype.h>
#include <dirent.h>
@@ -46,6 +45,11 @@
#include <unistd.h>
#include <wchar.h>
+#include "private/libc_logging.h"
+
+// The part is only for 32-bit targets.
+#if !defined(__LP64__)
+
// These were accidentally declared in <unistd.h> because we stupidly used to inline
// getpagesize() and __getpageshift(). Needed for backwards compatibility with old NDK apps.
extern "C" {
@@ -342,4 +346,18 @@
return malloc(size);
}
-#endif
+#endif // !defined(__LP64__)
+
+// This is never implemented in bionic, only needed for ABI compatibility with the NDK.
+extern "C" char* getusershell() {
+ return NULL;
+}
+
+// This is never implemented in bionic, only needed for ABI compatibility with the NDK.
+extern "C" void setusershell() { }
+
+// This is never implemented in bionic, only needed for ABI compatibility with the NDK.
+extern "C" void endusershell() { }
+
+// This is never implemented in bionic, only needed for ABI compatibility with the NDK.
+extern "C" void endpwent() { }
diff --git a/libc/bionic/stubs.cpp b/libc/bionic/stubs.cpp
index 1264fd7..f9a31b9 100644
--- a/libc/bionic/stubs.cpp
+++ b/libc/bionic/stubs.cpp
@@ -446,31 +446,6 @@
return NULL;
}
-static void unimplemented_stub(const char* function) {
- const char* fmt = "%s(3) is not implemented on Android\n";
- __libc_format_log(ANDROID_LOG_WARN, "libc", fmt, function);
- fprintf(stderr, fmt, function);
-}
-
-#define UNIMPLEMENTED unimplemented_stub(__PRETTY_FUNCTION__)
-
-void endpwent() {
- UNIMPLEMENTED;
-}
-
-char* getusershell() {
- UNIMPLEMENTED;
- return NULL;
-}
-
-void setusershell() {
- UNIMPLEMENTED;
-}
-
-void endusershell() {
- UNIMPLEMENTED;
-}
-
// Portable code should use sysconf(_SC_PAGE_SIZE) directly instead.
int getpagesize() {
// We dont use sysconf(3) here because that drags in stdio, which makes static binaries fat.
diff --git a/libc/bionic/system_properties.cpp b/libc/bionic/system_properties.cpp
index 170e7ac..aae99b1 100644
--- a/libc/bionic/system_properties.cpp
+++ b/libc/bionic/system_properties.cpp
@@ -51,7 +51,6 @@
#include <sys/_system_properties.h>
#include <sys/system_properties.h>
-#include "private/bionic_atomic_inline.h"
#include "private/bionic_futex.h"
#include "private/bionic_macros.h"
@@ -80,22 +79,26 @@
uint8_t namelen;
uint8_t reserved[3];
- // TODO: The following fields should be declared as atomic_uint32_t.
- // They should be assigned to with release semantics, instead of using
- // explicit fences. Unfortunately, the read accesses are generally
- // followed by more dependent read accesses, and the dependence
- // is assumed to enforce memory ordering. Which it does on supported
- // hardware. This technically should use memory_order_consume, if
- // that worked as intended.
+ // The property trie is updated only by the init process (single threaded) which provides
+ // property service. And it can be read by multiple threads at the same time.
+ // As the property trie is not protected by locks, we use atomic_uint_least32_t types for the
+ // left, right, children "pointers" in the trie node. To make sure readers who see the
+ // change of "pointers" can also notice the change of prop_bt structure contents pointed by
+ // the "pointers", we always use release-consume ordering pair when accessing these "pointers".
+
+ // prop "points" to prop_info structure if there is a propery associated with the trie node.
+ // Its situation is similar to the left, right, children "pointers". So we use
+ // atomic_uint_least32_t and release-consume ordering to protect it as well.
+
// We should also avoid rereading these fields redundantly, since not
// all processor implementations ensure that multiple loads from the
// same field are carried out in the right order.
- volatile uint32_t prop;
+ atomic_uint_least32_t prop;
- volatile uint32_t left;
- volatile uint32_t right;
+ atomic_uint_least32_t left;
+ atomic_uint_least32_t right;
- volatile uint32_t children;
+ atomic_uint_least32_t children;
char name[0];
@@ -103,8 +106,6 @@
this->namelen = name_length;
memcpy(this->name, name, name_length);
this->name[name_length] = '\0';
- ANDROID_MEMBAR_FULL(); // TODO: Instead use a release store
- // for subsequent pointer assignment.
}
private:
@@ -143,8 +144,6 @@
atomic_init(&this->serial, valuelen << 24);
memcpy(this->value, value, valuelen);
this->value[valuelen] = '\0';
- ANDROID_MEMBAR_FULL(); // TODO: Instead use a release store
- // for subsequent point assignment.
}
private:
DISALLOW_COPY_AND_ASSIGN(prop_info);
@@ -291,10 +290,10 @@
return map_result;
}
-static void *allocate_obj(const size_t size, uint32_t *const off)
+static void *allocate_obj(const size_t size, uint_least32_t *const off)
{
prop_area *pa = __system_property_area__;
- const size_t aligned = BIONIC_ALIGN(size, sizeof(uint32_t));
+ const size_t aligned = BIONIC_ALIGN(size, sizeof(uint_least32_t));
if (pa->bytes_used + aligned > pa_data_size) {
return NULL;
}
@@ -304,12 +303,12 @@
return pa->data + *off;
}
-static prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint32_t *const off)
+static prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint_least32_t *const off)
{
- uint32_t new_offset;
- void *const offset = allocate_obj(sizeof(prop_bt) + namelen + 1, &new_offset);
- if (offset) {
- prop_bt* bt = new(offset) prop_bt(name, namelen);
+ uint_least32_t new_offset;
+ void *const p = allocate_obj(sizeof(prop_bt) + namelen + 1, &new_offset);
+ if (p != NULL) {
+ prop_bt* bt = new(p) prop_bt(name, namelen);
*off = new_offset;
return bt;
}
@@ -318,20 +317,20 @@
}
static prop_info *new_prop_info(const char *name, uint8_t namelen,
- const char *value, uint8_t valuelen, uint32_t *const off)
+ const char *value, uint8_t valuelen, uint_least32_t *const off)
{
- uint32_t off_tmp;
- void* const offset = allocate_obj(sizeof(prop_info) + namelen + 1, &off_tmp);
- if (offset) {
- prop_info* info = new(offset) prop_info(name, namelen, value, valuelen);
- *off = off_tmp;
+ uint_least32_t new_offset;
+ void* const p = allocate_obj(sizeof(prop_info) + namelen + 1, &new_offset);
+ if (p != NULL) {
+ prop_info* info = new(p) prop_info(name, namelen, value, valuelen);
+ *off = new_offset;
return info;
}
return NULL;
}
-static void *to_prop_obj(const uint32_t off)
+static void *to_prop_obj(uint_least32_t off)
{
if (off > pa_data_size)
return NULL;
@@ -341,7 +340,17 @@
return (__system_property_area__->data + off);
}
-static prop_bt *root_node()
+static inline prop_bt *to_prop_bt(atomic_uint_least32_t* off_p) {
+ uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume);
+ return reinterpret_cast<prop_bt*>(to_prop_obj(off));
+}
+
+static inline prop_info *to_prop_info(atomic_uint_least32_t* off_p) {
+ uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume);
+ return reinterpret_cast<prop_info*>(to_prop_obj(off));
+}
+
+static inline prop_bt *root_node()
{
return reinterpret_cast<prop_bt*>(to_prop_obj(0));
}
@@ -373,36 +382,34 @@
}
if (ret < 0) {
- if (current->left) {
- current = reinterpret_cast<prop_bt*>(to_prop_obj(current->left));
+ uint_least32_t left_offset = atomic_load_explicit(¤t->left, memory_order_relaxed);
+ if (left_offset != 0) {
+ current = to_prop_bt(¤t->left);
} else {
if (!alloc_if_needed) {
return NULL;
}
- // Note that there isn't a race condition here. "clients" never
- // reach this code-path since It's only the (single threaded) server
- // that allocates new nodes. Though "bt->left" is volatile, it can't
- // have changed since the last value was last read.
- uint32_t new_offset = 0;
+ uint_least32_t new_offset;
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
if (new_bt) {
- current->left = new_offset;
+ atomic_store_explicit(¤t->left, new_offset, memory_order_release);
}
return new_bt;
}
} else {
- if (current->right) {
- current = reinterpret_cast<prop_bt*>(to_prop_obj(current->right));
+ uint_least32_t right_offset = atomic_load_explicit(¤t->right, memory_order_relaxed);
+ if (right_offset != 0) {
+ current = to_prop_bt(¤t->right);
} else {
if (!alloc_if_needed) {
return NULL;
}
- uint32_t new_offset;
+ uint_least32_t new_offset;
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
if (new_bt) {
- current->right = new_offset;
+ atomic_store_explicit(¤t->right, new_offset, memory_order_release);
}
return new_bt;
}
@@ -429,13 +436,14 @@
}
prop_bt* root = NULL;
- if (current->children) {
- root = reinterpret_cast<prop_bt*>(to_prop_obj(current->children));
+ uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed);
+ if (children_offset != 0) {
+ root = to_prop_bt(¤t->children);
} else if (alloc_if_needed) {
- uint32_t new_bt_offset;
- root = new_prop_bt(remaining_name, substr_size, &new_bt_offset);
+ uint_least32_t new_offset;
+ root = new_prop_bt(remaining_name, substr_size, &new_offset);
if (root) {
- current->children = new_bt_offset;
+ atomic_store_explicit(¤t->children, new_offset, memory_order_release);
}
}
@@ -454,13 +462,14 @@
remaining_name = sep + 1;
}
- if (current->prop) {
- return reinterpret_cast<prop_info*>(to_prop_obj(current->prop));
+ uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed);
+ if (prop_offset != 0) {
+ return to_prop_info(¤t->prop);
} else if (alloc_if_needed) {
- uint32_t new_info_offset;
- prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_info_offset);
+ uint_least32_t new_offset;
+ prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_offset);
if (new_info) {
- current->prop = new_info_offset;
+ atomic_store_explicit(¤t->prop, new_offset, memory_order_release);
}
return new_info;
@@ -534,31 +543,34 @@
cookie->count++;
}
-static int foreach_property(const uint32_t off,
+static int foreach_property(prop_bt *const trie,
void (*propfn)(const prop_info *pi, void *cookie), void *cookie)
{
- prop_bt *trie = reinterpret_cast<prop_bt*>(to_prop_obj(off));
if (!trie)
return -1;
- if (trie->left) {
- const int err = foreach_property(trie->left, propfn, cookie);
+ uint_least32_t left_offset = atomic_load_explicit(&trie->left, memory_order_relaxed);
+ if (left_offset != 0) {
+ const int err = foreach_property(to_prop_bt(&trie->left), propfn, cookie);
if (err < 0)
return -1;
}
- if (trie->prop) {
- prop_info *info = reinterpret_cast<prop_info*>(to_prop_obj(trie->prop));
+ uint_least32_t prop_offset = atomic_load_explicit(&trie->prop, memory_order_relaxed);
+ if (prop_offset != 0) {
+ prop_info *info = to_prop_info(&trie->prop);
if (!info)
return -1;
propfn(info, cookie);
}
- if (trie->children) {
- const int err = foreach_property(trie->children, propfn, cookie);
+ uint_least32_t children_offset = atomic_load_explicit(&trie->children, memory_order_relaxed);
+ if (children_offset != 0) {
+ const int err = foreach_property(to_prop_bt(&trie->children), propfn, cookie);
if (err < 0)
return -1;
}
- if (trie->right) {
- const int err = foreach_property(trie->right, propfn, cookie);
+ uint_least32_t right_offset = atomic_load_explicit(&trie->right, memory_order_relaxed);
+ if (right_offset != 0) {
+ const int err = foreach_property(to_prop_bt(&trie->right), propfn, cookie);
if (err < 0)
return -1;
}
@@ -766,5 +778,5 @@
return __system_property_foreach_compat(propfn, cookie);
}
- return foreach_property(0, propfn, cookie);
+ return foreach_property(root_node(), propfn, cookie);
}
diff --git a/libc/include/grp.h b/libc/include/grp.h
index 7b3e32b..df7a613 100644
--- a/libc/include/grp.h
+++ b/libc/include/grp.h
@@ -54,9 +54,9 @@
struct group *getgrgid(gid_t);
struct group *getgrnam(const char *);
#if __POSIX_VISIBLE >= 200112 || __XPG_VISIBLE
-struct group *getgrent(void) __errorattr("getgrent is meaningless on Android");
-__errordecl(setgrent, "setgrent is meaningless on Android");
-__errordecl(endgrent, "endgrent is meaningless on Android");
+struct group *getgrent(void) __attribute__((deprecated("getgrent is meaningless on Android")));
+void setgrent(void) __attribute__((deprecated("setgrent is meaningless on Android")));
+void endgrent(void) __attribute__((deprecated("endgrent is meaningless on Android")));
int getgrgid_r(gid_t, struct group *, char *,
size_t, struct group **);
int getgrnam_r(const char *, struct group *, char *,
diff --git a/libc/include/paths.h b/libc/include/paths.h
index 1eba536..33c2eee 100644
--- a/libc/include/paths.h
+++ b/libc/include/paths.h
@@ -39,7 +39,6 @@
#define _PATH_CONSOLE "/dev/console"
#define _PATH_DEVNULL "/dev/null"
#define _PATH_KLOG "/proc/kmsg"
-#define _PATH_MEM "/dev/mem"
#define _PATH_MOUNTED "/proc/mounts"
#define _PATH_TTY "/dev/tty"
diff --git a/libc/include/pwd.h b/libc/include/pwd.h
index 6d483c0..6012b96 100644
--- a/libc/include/pwd.h
+++ b/libc/include/pwd.h
@@ -119,10 +119,6 @@
int getpwnam_r(const char*, struct passwd*, char*, size_t, struct passwd**);
int getpwuid_r(uid_t, struct passwd*, char*, size_t, struct passwd**);
-void endpwent(void);
-struct passwd* getpwent(void);
-int setpwent(void);
-
__END_DECLS
#endif
diff --git a/libc/include/stdio.h b/libc/include/stdio.h
index 17ef096..f5ed652 100644
--- a/libc/include/stdio.h
+++ b/libc/include/stdio.h
@@ -264,16 +264,16 @@
#ifndef __AUDIT__
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112L
-char* gets(char*) __attribute__((deprecated("gets is very unsafe; consider using fgets")));
+char* gets(char*) __attribute__((deprecated("gets is unsafe, use fgets instead")));
#endif
int sprintf(char* __restrict, const char* __restrict, ...)
__printflike(2, 3) __warnattr("sprintf is often misused; please use snprintf");
-char* tmpnam(char*) __warnattr("tmpnam possibly used unsafely; consider using mkstemp");
int vsprintf(char* __restrict, const char* __restrict, __va_list)
__printflike(2, 0) __warnattr("vsprintf is often misused; please use vsnprintf");
+char* tmpnam(char*) __attribute__((deprecated("tmpnam is unsafe, use mkstemp or tmpfile instead")));
#if __XPG_VISIBLE
char* tempnam(const char*, const char*)
- __warnattr("tempnam possibly used unsafely; consider using mkstemp");
+ __attribute__((deprecated("tempnam is unsafe, use mkstemp or tmpfile instead")));
#endif
#endif
diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h
index cbd7aeb..84bf56d 100644
--- a/libc/include/stdlib.h
+++ b/libc/include/stdlib.h
@@ -58,7 +58,7 @@
extern int clearenv(void);
extern char* mkdtemp(char*);
-extern char* mktemp(char*) __warnattr("mktemp possibly used unsafely; consider using mkstemp");
+extern char* mktemp(char*) __attribute__((deprecated("mktemp is unsafe, use mkstemp or tmpfile instead")));
extern int mkostemp64(char*, int);
extern int mkostemp(char*, int);
diff --git a/libc/include/sys/resource.h b/libc/include/sys/resource.h
index a91fa53..3f8dd45 100644
--- a/libc/include/sys/resource.h
+++ b/libc/include/sys/resource.h
@@ -36,6 +36,10 @@
__BEGIN_DECLS
+/* The kernel header doesn't have these, but POSIX does. */
+#define RLIM_SAVED_CUR RLIM_INFINITY
+#define RLIM_SAVED_MAX RLIM_INFINITY
+
typedef unsigned long rlim_t;
extern int getrlimit(int, struct rlimit*);
diff --git a/libc/include/unistd.h b/libc/include/unistd.h
index a6d791c..92d3abe 100644
--- a/libc/include/unistd.h
+++ b/libc/include/unistd.h
@@ -116,10 +116,6 @@
extern int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
extern int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);
extern char* getlogin(void);
-extern char* getusershell(void);
-extern void setusershell(void);
-extern void endusershell(void);
-
extern long fpathconf(int, int);
extern long pathconf(const char*, int);
diff --git a/libc/private/bionic_atomic_arm.h b/libc/private/bionic_atomic_arm.h
deleted file mode 100644
index 0cb832f..0000000
--- a/libc/private/bionic_atomic_arm.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef BIONIC_ATOMIC_ARM_H
-#define BIONIC_ATOMIC_ARM_H
-
-__ATOMIC_INLINE__ void __bionic_memory_barrier() {
- __asm__ __volatile__ ( "dmb ish" : : : "memory" );
-}
-
-/* Compare-and-swap, without any explicit barriers. Note that this function
- * returns 0 on success, and 1 on failure. The opposite convention is typically
- * used on other platforms.
- */
-__ATOMIC_INLINE__ int __bionic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr) {
- int32_t prev, status;
- do {
- __asm__ __volatile__ (
- "ldrex %0, [%3]\n"
- "mov %1, #0\n"
- "teq %0, %4\n"
-#ifdef __thumb2__
- "it eq\n"
-#endif
- "strexeq %1, %5, [%3]"
- : "=&r" (prev), "=&r" (status), "+m"(*ptr)
- : "r" (ptr), "Ir" (old_value), "r" (new_value)
- : "cc");
- } while (__builtin_expect(status != 0, 0));
- return prev != old_value;
-}
-
-/* Swap, without any explicit barriers. */
-__ATOMIC_INLINE__ int32_t __bionic_swap(int32_t new_value, volatile int32_t* ptr) {
- int32_t prev, status;
- do {
- __asm__ __volatile__ (
- "ldrex %0, [%3]\n"
- "strex %1, %4, [%3]"
- : "=&r" (prev), "=&r" (status), "+m" (*ptr)
- : "r" (ptr), "r" (new_value)
- : "cc");
- } while (__builtin_expect(status != 0, 0));
- return prev;
-}
-
-/* Atomic decrement, without explicit barriers. */
-__ATOMIC_INLINE__ int32_t __bionic_atomic_dec(volatile int32_t* ptr) {
- int32_t prev, tmp, status;
- do {
- __asm__ __volatile__ (
- "ldrex %0, [%4]\n"
- "sub %1, %0, #1\n"
- "strex %2, %1, [%4]"
- : "=&r" (prev), "=&r" (tmp), "=&r" (status), "+m"(*ptr)
- : "r" (ptr)
- : "cc");
- } while (__builtin_expect(status != 0, 0));
- return prev;
-}
-
-#endif /* SYS_ATOMICS_ARM_H */
diff --git a/libc/private/bionic_atomic_arm64.h b/libc/private/bionic_atomic_arm64.h
deleted file mode 100644
index c3a34e1..0000000
--- a/libc/private/bionic_atomic_arm64.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef BIONIC_ATOMIC_AARCH64_H
-#define BIONIC_ATOMIC_AARCH64_H
-
-/* For ARMv8, we can use the 'dmb' instruction directly */
-__ATOMIC_INLINE__ void __bionic_memory_barrier() {
- __asm__ __volatile__ ( "dmb ish" : : : "memory" );
-}
-
-/* Compare-and-swap, without any explicit barriers. Note that this function
- * returns 0 on success, and 1 on failure. The opposite convention is typically
- * used on other platforms.
- */
-__ATOMIC_INLINE__ int __bionic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr) {
- int32_t tmp, oldval;
- __asm__ __volatile__ (
- "// atomic_cmpxchg\n"
- "1: ldxr %w1, [%3]\n"
- " cmp %w1, %w4\n"
- " b.ne 2f\n"
- " stxr %w0, %w5, [%3]\n"
- " cbnz %w0, 1b\n"
- "2:"
- : "=&r" (tmp), "=&r" (oldval), "+o"(*ptr)
- : "r" (ptr), "Ir" (old_value), "r" (new_value)
- : "cc", "memory");
- return oldval != old_value;
-}
-
-/* Swap, without any explicit barriers. */
-__ATOMIC_INLINE__ int32_t __bionic_swap(int32_t new_value, volatile int32_t* ptr) {
- int32_t prev, status;
- __asm__ __volatile__ (
- "// atomic_swap\n"
- "1: ldxr %w0, [%3]\n"
- " stxr %w1, %w4, [%3]\n"
- " cbnz %w1, 1b\n"
- : "=&r" (prev), "=&r" (status), "+o" (*ptr)
- : "r" (ptr), "r" (new_value)
- : "cc", "memory");
- return prev;
-}
-
-/* Atomic decrement, without explicit barriers. */
-__ATOMIC_INLINE__ int32_t __bionic_atomic_dec(volatile int32_t* ptr) {
- int32_t prev, tmp, status;
- __asm__ __volatile__ (
- "1: ldxr %w0, [%4]\n"
- " sub %w1, %w0, #1\n"
- " stxr %w2, %w1, [%4]\n"
- " cbnz %w2, 1b"
- : "=&r" (prev), "=&r" (tmp), "=&r" (status), "+m"(*ptr)
- : "r" (ptr)
- : "cc", "memory");
- return prev;
-}
-
-#endif /* BIONIC_ATOMICS_AARCH64_H */
diff --git a/libc/private/bionic_atomic_gcc_builtin.h b/libc/private/bionic_atomic_gcc_builtin.h
deleted file mode 100644
index 70eb861..0000000
--- a/libc/private/bionic_atomic_gcc_builtin.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef BIONIC_ATOMIC_GCC_BUILTIN_H
-#define BIONIC_ATOMIC_GCC_BUILTIN_H
-
-/*
- * This header file is used by default if we don't have optimized atomic
- * routines for a given platform. See bionic_atomic_arm.h and
- * bionic_atomic_x86.h for examples.
- *
- * Note that the GCC builtins include barriers that aren't present in
- * the architecture-specific assembler versions.
- */
-
-__ATOMIC_INLINE__ void __bionic_memory_barrier() {
- __sync_synchronize();
-}
-
-__ATOMIC_INLINE__ int __bionic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr) {
- /* We must return 0 on success. */
- return __sync_val_compare_and_swap(ptr, old_value, new_value) != old_value;
-}
-
-__ATOMIC_INLINE__ int32_t __bionic_swap(int32_t new_value, volatile int32_t* ptr) {
- int32_t old_value;
- do {
- old_value = *ptr;
- } while (__sync_val_compare_and_swap(ptr, old_value, new_value) != old_value);
- return old_value;
-}
-
-__ATOMIC_INLINE__ int32_t __bionic_atomic_dec(volatile int32_t* ptr) {
- /* We must return the old value. */
- return __sync_fetch_and_add(ptr, -1);
-}
-
-#endif /* BIONIC_ATOMIC_GCC_BUILTIN_H */
diff --git a/libc/private/bionic_atomic_inline.h b/libc/private/bionic_atomic_inline.h
deleted file mode 100644
index f8032c3..0000000
--- a/libc/private/bionic_atomic_inline.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef BIONIC_ATOMIC_INLINE_H
-#define BIONIC_ATOMIC_INLINE_H
-
-/*
- * Inline declarations and macros for some special-purpose atomic
- * operations. These are intended for rare circumstances where a
- * memory barrier needs to be issued inline rather than as a function
- * call.
- *
- * Macros defined in this header:
- *
- * void ANDROID_MEMBAR_FULL()
- * Full memory barrier. Provides a compiler reordering barrier, and
- * on SMP systems emits an appropriate instruction.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Define __ATOMIC_INLINE__ to control the inlining of all atomics
- * functions declared here. For a slight performance boost, we want
- * all of them to be always_inline
- */
-#define __ATOMIC_INLINE__ static __inline__ __attribute__((always_inline))
-
-#if defined(__arm__)
-# include "bionic_atomic_arm.h"
-#elif defined(__aarch64__)
-# include "bionic_atomic_arm64.h"
-#elif defined(__i386__)
-# include "bionic_atomic_x86.h"
-#elif defined(__mips__)
-# include "bionic_atomic_mips.h"
-#else
-# include "bionic_atomic_gcc_builtin.h"
-#endif
-
-#define ANDROID_MEMBAR_FULL __bionic_memory_barrier
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // BIONIC_ATOMIC_INLINE_H
diff --git a/libc/private/bionic_atomic_mips.h b/libc/private/bionic_atomic_mips.h
deleted file mode 100644
index 83f75fe..0000000
--- a/libc/private/bionic_atomic_mips.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef BIONIC_ATOMIC_MIPS_H
-#define BIONIC_ATOMIC_MIPS_H
-
-/* Define a full memory barrier, this is only needed if we build the
- * platform for a multi-core device.
- */
-
-__ATOMIC_INLINE__ void __bionic_memory_barrier() {
- __asm__ __volatile__ ( "sync" : : : "memory" );
-}
-
-/* Compare-and-swap, without any explicit barriers. Note that this function
- * returns 0 on success, and 1 on failure. The opposite convention is typically
- * used on other platforms.
- */
-__ATOMIC_INLINE__ int __bionic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr) {
- int32_t prev, status;
- __asm__ __volatile__ ("1: move %[status], %[new_value] \n"
- " ll %[prev], 0(%[ptr]) \n"
- " bne %[old_value], %[prev], 2f \n"
- " sc %[status], 0(%[ptr]) \n"
- " beqz %[status], 1b \n"
- "2: \n"
- : [prev]"=&r"(prev), [status]"=&r"(status), "+m"(*ptr)
- : [new_value]"r"(new_value), [old_value]"r"(old_value), [ptr]"r"(ptr)
- : "memory");
- return prev != old_value;
-}
-
-/* Swap, without any explicit barriers. */
-__ATOMIC_INLINE__ int32_t __bionic_swap(int32_t new_value, volatile int32_t* ptr) {
- int32_t prev, status;
- __asm__ __volatile__ ("1: move %[status], %[new_value] \n"
- " ll %[prev], 0(%[ptr]) \n"
- " sc %[status], 0(%[ptr]) \n"
- " beqz %[status], 1b \n"
- : [prev]"=&r"(prev), [status]"=&r"(status), "+m"(*ptr)
- : [ptr]"r"(ptr), [new_value]"r"(new_value)
- : "memory");
- return prev;
-}
-
-/* Atomic decrement, without explicit barriers. */
-__ATOMIC_INLINE__ int32_t __bionic_atomic_dec(volatile int32_t* ptr) {
- int32_t prev, status;
- __asm__ __volatile__ ("1: ll %[prev], 0(%[ptr]) \n"
- " addiu %[status], %[prev], -1 \n"
- " sc %[status], 0(%[ptr]) \n"
- " beqz %[status], 1b \n"
- : [prev]"=&r" (prev), [status]"=&r"(status), "+m" (*ptr)
- : [ptr]"r"(ptr)
- : "memory");
- return prev;
-}
-
-#endif /* BIONIC_ATOMIC_MIPS_H */
diff --git a/libc/private/bionic_atomic_x86.h b/libc/private/bionic_atomic_x86.h
deleted file mode 100644
index e63df93..0000000
--- a/libc/private/bionic_atomic_x86.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef BIONIC_ATOMIC_X86_H
-#define BIONIC_ATOMIC_X86_H
-
-/* Define a full memory barrier, this is only needed if we build the
- * platform for a multi-core device.
- */
-__ATOMIC_INLINE__ void __bionic_memory_barrier() {
- __asm__ __volatile__ ( "mfence" : : : "memory" );
-}
-
-/* Compare-and-swap, without any explicit barriers. Note that this function
- * returns 0 on success, and 1 on failure. The opposite convention is typically
- * used on other platforms.
- */
-__ATOMIC_INLINE__ int __bionic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr) {
- int32_t prev;
- __asm__ __volatile__ ("lock; cmpxchgl %1, %2"
- : "=a" (prev)
- : "q" (new_value), "m" (*ptr), "0" (old_value)
- : "memory");
- return prev != old_value;
-}
-
-/* Swap, without any explicit barriers. */
-__ATOMIC_INLINE__ int32_t __bionic_swap(int32_t new_value, volatile int32_t *ptr) {
- __asm__ __volatile__ ("xchgl %1, %0"
- : "=r" (new_value)
- : "m" (*ptr), "0" (new_value)
- : "memory");
- return new_value;
-}
-
-/* Atomic decrement, without explicit barriers. */
-__ATOMIC_INLINE__ int32_t __bionic_atomic_dec(volatile int32_t* ptr) {
- int increment = -1;
- __asm__ __volatile__ ("lock; xaddl %0, %1"
- : "+r" (increment), "+m" (*ptr)
- : : "memory");
- /* increment now holds the old value of *ptr */
- return increment;
-}
-
-#endif /* BIONIC_ATOMIC_X86_H */
diff --git a/libc/stdio/fread.c b/libc/stdio/fread.c
index bac8dad..f3f0127 100644
--- a/libc/stdio/fread.c
+++ b/libc/stdio/fread.c
@@ -102,6 +102,12 @@
* avoid copying it through the buffer?
*/
if (total > (size_t) fp->_bf._size) {
+ /*
+ * Make sure that fseek doesn't think it can
+ * reuse the buffer since we are going to read
+ * directly from the file descriptor.
+ */
+ fp->_flags |= __SMOD;
break;
}
diff --git a/libm/Android.mk b/libm/Android.mk
index dc6c704..ebc3c9f 100644
--- a/libm/Android.mk
+++ b/libm/Android.mk
@@ -130,7 +130,6 @@
upstream-freebsd/lib/msun/src/s_fdim.c \
upstream-freebsd/lib/msun/src/s_finite.c \
upstream-freebsd/lib/msun/src/s_finitef.c \
- upstream-freebsd/lib/msun/src/s_floor.c \
upstream-freebsd/lib/msun/src/s_floorf.c \
upstream-freebsd/lib/msun/src/s_fma.c \
upstream-freebsd/lib/msun/src/s_fmaf.c \
@@ -264,20 +263,39 @@
LOCAL_SRC_FILES_arm += \
arm/fenv.c \
+# s_floor.S requires neon instructions.
+ifdef TARGET_2ND_ARCH
+arch_variant := $(TARGET_2ND_ARCH_VARIANT)
+else
+arch_variant := $(TARGET_ARCH_VARIANT)
+endif
+
+# Use the C version on armv7-a since it doesn't support neon instructions.
+ifeq ($(arch_variant),armv7-a)
+LOCAL_SRC_FILES_arm += upstream-freebsd/lib/msun/src/s_floor.c
+else
+LOCAL_SRC_FILES_arm += arm/s_floor.S
+endif
+
LOCAL_SRC_FILES_arm64 += \
arm64/fenv.c \
+ upstream-freebsd/lib/msun/src/s_floor.c \
LOCAL_SRC_FILES_mips += \
mips/fenv.c \
+ upstream-freebsd/lib/msun/src/s_floor.c \
LOCAL_SRC_FILES_mips64 += \
mips/fenv.c \
+ upstream-freebsd/lib/msun/src/s_floor.c \
LOCAL_SRC_FILES_x86 += \
i387/fenv.c \
+ upstream-freebsd/lib/msun/src/s_floor.c \
LOCAL_SRC_FILES_x86_64 += \
amd64/fenv.c \
+ upstream-freebsd/lib/msun/src/s_floor.c \
LOCAL_C_INCLUDES_x86 += $(LOCAL_PATH)/i387
@@ -297,6 +315,9 @@
-Wno-unknown-pragmas \
-fvisibility=hidden \
+LOCAL_ASFLAGS := \
+ -Ibionic/libc \
+
# Workaround the GCC "(long)fn -> lfn" optimization bug which will result in
# self recursions for lrint, lrintf, and lrintl.
# BUG: 14225968
diff --git a/libm/arm/s_floor.S b/libm/arm/s_floor.S
new file mode 100644
index 0000000..4405358
--- /dev/null
+++ b/libm/arm/s_floor.S
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2013-2014, NVIDIA Corporation. All rights reserved.
+ * Johnny Qiu <joqiu@nvidia.com>
+ * Shu Zhang <chazhang@nvidia.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 <float.h>
+#include <private/bionic_asm.h>
+
+ENTRY(floor) /* x in r0, r1 */
+
+ and r3, r1, #0x80000000 /* sign(x) */
+ bic r1, r1, #0x80000000 /* x = abs(x) */
+
+ /* extract exp of x */
+ lsr r2, r1, #20
+ sub r2, r2, #0x3fc
+ subs r2, r2, #0x3 /* r2 <- exp */
+
+ /* |x| < 1.0? */
+ blt .Lx_lt_one
+
+ /* x < 0? */
+ cmp r3, #0
+ bne .Lclr_frac_neg
+
+ /* |x| <= 2^20? */
+ cmp r2, #20
+ ble .Lclr_frac_r1
+
+ /* |x| < 2^52? */
+ cmp r2, #52
+ blt .Lclr_frac_r0
+
+ /* return x */
+ bx lr
+
+.Lclr_frac_r1:
+ rsb r2, r2, #20
+ lsr r1, r1, r2
+ lsl r1, r1, r2
+ mov r0, #0
+ bx lr
+
+.Lclr_frac_r0:
+ rsb r2, r2, #52
+ lsr r0, r0, r2
+ lsl r0, r0, r2
+ bx lr
+
+.Lclr_frac_neg:
+ /* |x| <= 2^20? */
+ cmp r2, #20
+ ble .Lclr_frac_r1_neg
+
+ /* |x| < 2^52? */
+ cmp r2, #52
+ blt .Lclr_frac_r0_neg
+
+ /* return x */
+ orr r1, r1, #0x80000000
+ bx lr
+
+.Lclr_frac_r1_neg:
+ rsb r2, r2, #20
+ mov r3, #1
+ lsl r3, r3, r2
+ sub r3, r3, #1
+ and r3, r1, r3
+ orr r3, r3, r0
+ lsr r1, r1, r2
+ lsl r1, r1, r2
+ mov r0, #0
+ b .Lreturn_x_neg
+
+.Lclr_frac_r0_neg:
+ rsb r2, r2, #52
+ mov r3, #1
+ lsl r3, r3, r2
+ sub r3, r3, #1
+ and r3, r0, r3
+ lsr r0, r0, r2
+ lsl r0, r0, r2
+ b .Lreturn_x_neg
+
+.Lx_lt_one:
+ /* x == +-0? */
+ cmp r0, #0
+ cmpeq r1, #0
+ orreq r1, r1, r3
+ bxeq lr
+
+ /* (x > 0) ? 0 : -1 */
+ mov r1, #0x00100000
+ mov r0, #0
+ cmp r3, #0
+ movne r1, #0xc0000000
+ sub r1, r1, #0x00100000
+ bx lr
+
+.Lreturn_x_neg:
+ cmp r3, #0
+ orr r1, r1, #0x80000000
+ bxeq lr
+
+ vmov d16, r0, r1
+ vmov.f64 d18, #1.0
+ vsub.f64 d16, d16, d18
+ vmov r0, r1, d16
+ bx lr
+
+END(floor)
+
+#if LDBL_MANT_DIG == 53
+ .weak floorl
+ .equ floorl,floor
+#endif
diff --git a/tests/Android.mk b/tests/Android.mk
index bd4695f..b3eea06 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -211,7 +211,7 @@
include $(LOCAL_PATH)/Android.build.mk
# -----------------------------------------------------------------------------
-# Library of bionic customized gtest main function.
+# Library of bionic customized gtest main function, with simplified output format.
# -----------------------------------------------------------------------------
libBionicGtestMain_src_files := gtest_main.cpp
@@ -228,6 +228,24 @@
include $(LOCAL_PATH)/Android.build.mk
# -----------------------------------------------------------------------------
+# Library of bionic customized gtest main function, with normal gtest output format,
+# which is needed by bionic cts test.
+# -----------------------------------------------------------------------------
+libBionicCtsGtestMain_src_files := gtest_main.cpp
+
+libBionicCtsGtestMain_cflags := $(test_cflags)
+
+libBionicCtsGtestMain_cppflags := $(test_cppflags) -DUSING_GTEST_OUTPUT_FORMAT
+
+module := libBionicCtsGtestMain
+module_tag := optional
+build_type := target
+build_target := STATIC_TEST_LIBRARY
+include $(LOCAL_PATH)/Android.build.mk
+build_type := host
+include $(LOCAL_PATH)/Android.build.mk
+
+# -----------------------------------------------------------------------------
# Tests for the device using bionic's .so. Run with:
# adb shell /data/nativetest/bionic-unit-tests/bionic-unit-tests32
# adb shell /data/nativetest/bionic-unit-tests/bionic-unit-tests64
diff --git a/tests/gtest_main.cpp b/tests/gtest_main.cpp
index 86d6466..b1953fc 100644
--- a/tests/gtest_main.cpp
+++ b/tests/gtest_main.cpp
@@ -124,15 +124,15 @@
int64_t GetTestTime() const { return elapsed_time_ns_; }
- void AppendFailureMessage(const std::string& s) { failure_message_ += s; }
+ void AppendTestOutput(const std::string& s) { output_ += s; }
- const std::string& GetFailureMessage() const { return failure_message_; }
+ const std::string& GetTestOutput() const { return output_; }
private:
const std::string name_;
TestResult result_;
int64_t elapsed_time_ns_;
- std::string failure_message_;
+ std::string output_;
};
class TestCase {
@@ -196,10 +196,6 @@
std::vector<Test> test_list_;
};
-// This is the file descriptor used by the child process to write failure message.
-// The parent process will collect the information and dump to stdout / xml file.
-static int child_output_fd;
-
class TestResultPrinter : public testing::EmptyTestEventListener {
public:
TestResultPrinter() : pinfo_(NULL) {}
@@ -219,25 +215,9 @@
return;
// Print failure message from the assertion (e.g. expected this and got that).
- char buf[1024];
- snprintf(buf, sizeof(buf), "%s:(%d) Failure in test %s.%s\n%s\n", result.file_name(),
- result.line_number(),
- pinfo_->test_case_name(),
- pinfo_->name(),
- result.message());
-
- int towrite = strlen(buf);
- char* p = buf;
- while (towrite > 0) {
- ssize_t bytes_written = TEMP_FAILURE_RETRY(write(child_output_fd, p, towrite));
- if (bytes_written == -1) {
- fprintf(stderr, "failed to write child_output_fd: %s\n", strerror(errno));
- exit(1);
- } else {
- towrite -= bytes_written;
- p += bytes_written;
- }
- }
+ printf("%s:(%d) Failure in test %s.%s\n%s\n", result.file_name(), result.line_number(),
+ pinfo_->test_case_name(), pinfo_->name(), result.message());
+ fflush(stdout);
}
static int64_t NanoTime() {
@@ -315,6 +295,32 @@
fflush(stdout);
}
+// bionic cts test needs gtest output format.
+#if defined(USING_GTEST_OUTPUT_FORMAT)
+
+static void OnTestEndPrint(const TestCase& testcase, size_t test_id) {
+ ColoredPrintf(COLOR_GREEN, "[ RUN ] ");
+ printf("%s\n", testcase.GetTestName(test_id).c_str());
+
+ const std::string& test_output = testcase.GetTest(test_id).GetTestOutput();
+ printf("%s", test_output.c_str());
+
+ TestResult result = testcase.GetTestResult(test_id);
+ if (result == TEST_SUCCESS) {
+ ColoredPrintf(COLOR_GREEN, "[ OK ] ");
+ } else {
+ ColoredPrintf(COLOR_RED, "[ FAILED ] ");
+ }
+ printf("%s", testcase.GetTestName(test_id).c_str());
+ if (testing::GTEST_FLAG(print_time)) {
+ printf(" (%" PRId64 " ms)", testcase.GetTestTime(test_id) / 1000000);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+#else // !defined(USING_GTEST_OUTPUT_FORMAT)
+
static void OnTestEndPrint(const TestCase& testcase, size_t test_id) {
TestResult result = testcase.GetTestResult(test_id);
if (result == TEST_SUCCESS) {
@@ -327,16 +333,17 @@
printf("%s", testcase.GetTestName(test_id).c_str());
if (testing::GTEST_FLAG(print_time)) {
- printf(" (%" PRId64 " ms)\n", testcase.GetTestTime(test_id) / 1000000);
- } else {
- printf("\n");
+ printf(" (%" PRId64 " ms)", testcase.GetTestTime(test_id) / 1000000);
}
+ printf("\n");
- const std::string& failure_message = testcase.GetTest(test_id).GetFailureMessage();
- printf("%s", failure_message.c_str());
+ const std::string& test_output = testcase.GetTest(test_id).GetTestOutput();
+ printf("%s", test_output.c_str());
fflush(stdout);
}
+#endif // !defined(USING_GTEST_OUTPUT_FORMAT)
+
static void OnTestIterationEndPrint(const std::vector<TestCase>& testcase_list, size_t /*iteration*/,
int64_t elapsed_time_ns) {
@@ -481,8 +488,8 @@
fputs(" />\n", fp);
} else {
fputs(">\n", fp);
- const std::string& failure_message = testcase.GetTest(j).GetFailureMessage();
- fprintf(fp, " <failure message=\"%s\" type=\"\">\n", failure_message.c_str());
+ const std::string& test_output = testcase.GetTest(j).GetTestOutput();
+ fprintf(fp, " <failure message=\"%s\" type=\"\">\n", test_output.c_str());
fputs(" </failure>\n", fp);
fputs(" </testcase>\n", fp);
}
@@ -538,7 +545,10 @@
} else if (pid == 0) {
// In child process, run a single test.
close(pipefd[0]);
- child_output_fd = pipefd[1];
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ dup2(pipefd[1], STDOUT_FILENO);
+ dup2(pipefd[1], STDERR_FILENO);
if (sigprocmask(SIG_SETMASK, &sigmask, NULL) == -1) {
perror("sigprocmask SIG_SETMASK");
@@ -692,7 +702,7 @@
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(child_proc.child_read_fd, buf, sizeof(buf) - 1));
if (bytes_read > 0) {
buf[bytes_read] = '\0';
- testcase.GetTest(test_id).AppendFailureMessage(buf);
+ testcase.GetTest(test_id).AppendTestOutput(buf);
} else if (bytes_read == 0) {
break; // Read end.
} else {
@@ -713,7 +723,7 @@
char buf[1024];
snprintf(buf, sizeof(buf), "%s killed because of timeout at %" PRId64 " ms.\n",
testcase.GetTestName(test_id).c_str(), testcase.GetTestTime(test_id) / 1000000);
- testcase.GetTest(test_id).AppendFailureMessage(buf);
+ testcase.GetTest(test_id).AppendTestOutput(buf);
} else if (WIFSIGNALED(child_proc.exit_status)) {
// Record signal terminated test as failed.
@@ -721,7 +731,7 @@
char buf[1024];
snprintf(buf, sizeof(buf), "%s terminated by signal: %s.\n",
testcase.GetTestName(test_id).c_str(), strsignal(WTERMSIG(child_proc.exit_status)));
- testcase.GetTest(test_id).AppendFailureMessage(buf);
+ testcase.GetTest(test_id).AppendTestOutput(buf);
} else {
testcase.SetTestResult(test_id, WEXITSTATUS(child_proc.exit_status) == 0 ?
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 890e86e..2ecfc60 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -965,3 +965,41 @@
TEST(stdio, fwrite_after_fread_fast_path) {
test_fwrite_after_fread(64*1024);
}
+
+// http://b/19172514
+TEST(stdio, fread_after_fseek) {
+ TemporaryFile tf;
+
+ FILE* fp = fopen(tf.filename, "w+");
+ ASSERT_TRUE(fp != nullptr);
+
+ char file_data[12288];
+ for (size_t i = 0; i < 12288; i++) {
+ file_data[i] = i;
+ }
+ ASSERT_EQ(12288U, fwrite(file_data, 1, 12288, fp));
+ fclose(fp);
+
+ fp = fopen(tf.filename, "r");
+ ASSERT_TRUE(fp != nullptr);
+
+ char buffer[8192];
+ size_t cur_location = 0;
+ // Small read to populate internal buffer.
+ ASSERT_EQ(100U, fread(buffer, 1, 100, fp));
+ ASSERT_EQ(memcmp(file_data, buffer, 100), 0);
+
+ cur_location = static_cast<size_t>(ftell(fp));
+ // Large read to force reading into the user supplied buffer and bypassing
+ // the internal buffer.
+ ASSERT_EQ(8192U, fread(buffer, 1, 8192, fp));
+ ASSERT_EQ(memcmp(file_data+cur_location, buffer, 8192), 0);
+
+ // Small backwards seek to verify fseek does not reuse the internal buffer.
+ ASSERT_EQ(0, fseek(fp, -22, SEEK_CUR));
+ cur_location = static_cast<size_t>(ftell(fp));
+ ASSERT_EQ(22U, fread(buffer, 1, 22, fp));
+ ASSERT_EQ(memcmp(file_data+cur_location, buffer, 22), 0);
+
+ fclose(fp);
+}
diff --git a/tests/sys_stat_test.cpp b/tests/sys_stat_test.cpp
index 7bbb7c6..28c7c52 100644
--- a/tests/sys_stat_test.cpp
+++ b/tests/sys_stat_test.cpp
@@ -219,3 +219,43 @@
ASSERT_EQ(ENOTSUP, errno);
unlink(linkname);
}
+
+TEST(sys_stat, faccessat_EINVAL) {
+ ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", F_OK, ~AT_SYMLINK_NOFOLLOW));
+ ASSERT_EQ(EINVAL, errno);
+#if defined(__BIONIC__)
+ ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", ~(R_OK | W_OK | X_OK), 0));
+ ASSERT_EQ(EINVAL, errno);
+#else
+ ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", ~(R_OK | W_OK | X_OK), AT_SYMLINK_NOFOLLOW));
+ ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", ~(R_OK | W_OK | X_OK), 0));
+ ASSERT_EQ(EINVAL, errno);
+#endif
+}
+
+TEST(sys_stat, faccessat_AT_SYMLINK_NOFOLLOW_EINVAL) {
+#if defined(__BIONIC__)
+ // Android doesn't support AT_SYMLINK_NOFOLLOW
+ ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", F_OK, AT_SYMLINK_NOFOLLOW));
+ ASSERT_EQ(EINVAL, errno);
+#else
+ ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", F_OK, AT_SYMLINK_NOFOLLOW));
+#endif
+}
+
+TEST(sys_stat, faccessat_dev_null) {
+ ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", F_OK, 0));
+ ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", R_OK, 0));
+ ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", W_OK, 0));
+ ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", R_OK|W_OK, 0));
+}
+
+TEST(sys_stat, faccessat_nonexistant) {
+ ASSERT_EQ(-1, faccessat(AT_FDCWD, "/blah", F_OK, AT_SYMLINK_NOFOLLOW));
+#if defined(__BIONIC__)
+ // Android doesn't support AT_SYMLINK_NOFOLLOW
+ ASSERT_EQ(EINVAL, errno);
+#else
+ ASSERT_EQ(ENOENT, errno);
+#endif
+}
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index f5c0524..f54a461 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -801,3 +801,22 @@
VERIFY_SYSCONF_NOT_SUPPORT(_SC_XOPEN_UUCP);
#endif // defined(__BIONIC__)
}
+
+TEST(unistd, dup2_same) {
+ // POSIX says of dup2:
+ // If fildes2 is already a valid open file descriptor ...
+ // [and] fildes is equal to fildes2 ... dup2() shall return
+ // fildes2 without closing it.
+ // This isn't true of dup3(2), so we need to manually implement that.
+
+ // Equal and valid.
+ int fd = open("/proc/version", O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+ ASSERT_EQ(fd, dup2(fd, fd));
+ ASSERT_EQ(0, close(fd)); // Check that dup2 didn't close fd.
+
+ // Equal, but invalid.
+ errno = 0;
+ ASSERT_EQ(-1, dup2(fd, fd));
+ ASSERT_EQ(EBADF, errno);
+}