aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2014-02-14 18:56:23 -0800
committerElliott Hughes <enh@google.com>2014-02-18 12:11:38 -0800
commit3d19a8319b9c27af8aa5cfbf495da0fe7fa62d3e (patch)
treea51591477e2f6f97ae56ae027d01ca1ff8c302ef
parentd4bc9ef83b36916d5e63c4c3e4a092381473ea52 (diff)
downloadandroid_bionic-3d19a8319b9c27af8aa5cfbf495da0fe7fa62d3e.tar.gz
android_bionic-3d19a8319b9c27af8aa5cfbf495da0fe7fa62d3e.tar.bz2
android_bionic-3d19a8319b9c27af8aa5cfbf495da0fe7fa62d3e.zip
bionic: fix __set_errno for arm64 syscalls that return a 64-bit value
bionic/libc/arch-arm64/syscalls/read.S ends with: b.hi __set_errno ret END(read) If __set_errno returns int, it will set w0 to 0xFFFFFFFF, which means x0 is 0x00000000FFFFFFFF. When interpreted as a ssize_t that is INT_MAX, not -1. Change __set_errno to return long, which will cause x0 to be set instead of w0. Change-Id: I9f9ea0f2995928d2ea240eb2ff7758ecdf0ff412
-rw-r--r--libc/bionic/__set_errno.cpp15
-rw-r--r--tests/unistd_test.cpp23
2 files changed, 36 insertions, 2 deletions
diff --git a/libc/bionic/__set_errno.cpp b/libc/bionic/__set_errno.cpp
index af6a68eb1..236aeacee 100644
--- a/libc/bionic/__set_errno.cpp
+++ b/libc/bionic/__set_errno.cpp
@@ -31,8 +31,19 @@
// This function is called from our assembler syscall stubs.
// C/C++ code should just assign 'errno' instead.
-// TODO: this should be __LIBC_HIDDEN__ but was exposed in <errno.h> in the NDK.
-extern "C" int __set_errno(int n) {
+// The return type is 'long' because we use the same routine in calls
+// that return an int as in ones that return a ssize_t. On a 32-bit
+// system these are the same size, but on a 64-bit system they're not.
+// 'long' gives us 32-bit on 32-bit systems, 64-bit on 64-bit systems.
+
+#if __LP64__
+extern "C" __LIBC_HIDDEN__ long __set_errno(int);
+#else
+// __set_errno was mistakenly exposed in <errno.h> in the 32-bit NDK.
+extern "C" long __set_errno(int);
+#endif
+
+long __set_errno(int n) {
errno = n;
return -1;
}
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index 2308ad95f..f7a5afdab 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -18,6 +18,7 @@
#include "ScopedSignalHandler.h"
#include "TemporaryFile.h"
+#include <fcntl.h>
#include <stdint.h>
#include <unistd.h>
@@ -88,3 +89,25 @@ TEST(unistd, pause) {
ASSERT_EQ(-1, pause());
ASSERT_TRUE(gPauseTestFlag);
}
+
+TEST(unistd, read) {
+ int fd = open("/proc/version", O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ char buf[5];
+ ASSERT_EQ(5, read(fd, buf, 5));
+ ASSERT_EQ(buf[0], 'L');
+ ASSERT_EQ(buf[1], 'i');
+ ASSERT_EQ(buf[2], 'n');
+ ASSERT_EQ(buf[3], 'u');
+ ASSERT_EQ(buf[4], 'x');
+ close(fd);
+}
+
+TEST(unistd, read_EBADF) {
+ // read returns ssize_t which is 64-bits on LP64, so it's worth explicitly checking that
+ // our syscall stubs correctly return a 64-bit -1.
+ char buf[1];
+ ASSERT_EQ(-1, read(-1, buf, sizeof(buf)));
+ ASSERT_EQ(EBADF, errno);
+}