From 3d19a8319b9c27af8aa5cfbf495da0fe7fa62d3e Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 14 Feb 2014 18:56:23 -0800 Subject: [PATCH] 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 --- libc/bionic/__set_errno.cpp | 15 +++++++++++++-- tests/unistd_test.cpp | 23 +++++++++++++++++++++++ 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 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 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 #include #include @@ -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); +}