Fix __pthread_clone on ARM to set errno on failure.

MIPS and x86 appear to have been correct already.

(Also fix unit tests that ASSERT_EQ with errno so that the
arguments are in the retarded junit order.)

Bug: 3461078
Change-Id: I2418ea98927b56e15b4ba9cfec97f5e7094c6291
This commit is contained in:
Elliott Hughes 2013-02-11 16:36:48 -08:00
parent 1fea0f258a
commit 5e3fc43dde
7 changed files with 57 additions and 55 deletions

View File

@ -29,38 +29,35 @@
#include <machine/asm.h> #include <machine/asm.h>
#include <sys/linux-syscalls.h> #include <sys/linux-syscalls.h>
// int __pthread_clone(int (*fn)(void*), void* child_stack, int flags, void* arg);
ENTRY(__pthread_clone) ENTRY(__pthread_clone)
@ insert the args onto the new stack # Copy the args onto the new stack.
stmdb r1!, {r0, r3} stmdb r1!, {r0, r3}
@ do the system call # The sys_clone system call only takes two arguments: 'flags' and 'child_stack'.
@ get flags # 'child_stack' is already in r1, but we need to move 'flags' into position.
mov r0, r2 mov r0, r2
@ new sp is already in r1
stmfd sp!, {r4, r7} stmfd sp!, {r4, r7}
# System call.
ldr r7, =__NR_clone ldr r7, =__NR_clone
swi #0 swi #0
movs r0, r0 movs r0, r0
ldmnefd sp!, {r4, r7} beq 1f
blt __error
# In parent, reload saved registers then either exit or set errno.
ldmfd sp!, {r4, r7}
bxne lr bxne lr
b __set_syscall_errno
1: # The child.
@ pick the function arg and call address off the stack and jump # pick the function arg and call address off the stack and jump
@ to the C __thread_entry function which does some setup and then # to the C __thread_entry function which does some setup and then
@ calls the thread's start function # calls the thread's start function
pop {r0, r1} pop {r0, r1}
mov r2, sp @ __thread_entry needs the TLS pointer # __thread_entry needs the TLS pointer
mov r2, sp
b __thread_entry b __thread_entry
__error:
mov r0, #-1
bx lr
END(__pthread_clone) END(__pthread_clone)
@ -71,8 +68,8 @@ END(__pthread_clone)
# pid_t *pid, void *tls, pid_t *ctid, # pid_t *pid, void *tls, pid_t *ctid,
# int (*fn)(void *), void* arg ); # int (*fn)(void *), void* arg );
# #
# NOTE: This is not the same signature than the GLibc # NOTE: This is not the same signature as the glibc
# __clone function here !! Placing 'fn' and 'arg' # __clone function. Placing 'fn' and 'arg'
# at the end of the parameter list makes the # at the end of the parameter list makes the
# implementation much simpler. # implementation much simpler.
# #
@ -91,20 +88,18 @@ ENTRY(__bionic_clone)
str r5, [r1, #-4] str r5, [r1, #-4]
str r6, [r1, #-8] str r6, [r1, #-8]
# system call # System call
ldr r7, =__NR_clone ldr r7, =__NR_clone
swi #0 swi #0
movs r0, r0 movs r0, r0
beq 1f beq 1f
# in parent, reload saved registers # In the parent, reload saved registers then either exit or set errno.
# then either exit or error
#
ldmfd sp!, {r4, r5, r6, r7} ldmfd sp!, {r4, r5, r6, r7}
bxne lr bxne lr
b __set_syscall_errno b __set_syscall_errno
1: # in the child - pick arguments 1: # The child.
ldr r0, [sp, #-4] ldr r0, [sp, #-4]
ldr r1, [sp, #-8] ldr r1, [sp, #-8]
b __bionic_clone_entry b __bionic_clone_entry

View File

@ -2,10 +2,7 @@
.text .text
/* // int __pthread_clone(int (*fn)(void*), void* tls, int flags, void* arg);
* int __pthread_clone(int (*fn)(void*), void *tls, int flags,
* void *arg);
*/
.globl __pthread_clone .globl __pthread_clone
.type __pthread_clone, @function .type __pthread_clone, @function
.align 4 .align 4

View File

@ -66,12 +66,12 @@ TEST(dirent, scandir) {
TEST(dirent, fdopendir_invalid) { TEST(dirent, fdopendir_invalid) {
ASSERT_TRUE(fdopendir(-1) == NULL); ASSERT_TRUE(fdopendir(-1) == NULL);
ASSERT_EQ(errno, EBADF); ASSERT_EQ(EBADF, errno);
int fd = open("/dev/null", O_RDONLY); int fd = open("/dev/null", O_RDONLY);
ASSERT_NE(fd, -1); ASSERT_NE(fd, -1);
ASSERT_TRUE(fdopendir(fd) == NULL); ASSERT_TRUE(fdopendir(fd) == NULL);
ASSERT_EQ(errno, ENOTDIR); ASSERT_EQ(ENOTDIR, errno);
close(fd); close(fd);
} }
@ -85,15 +85,15 @@ TEST(dirent, fdopendir) {
// fdopendir(3) took ownership, so closedir(3) closed our fd. // fdopendir(3) took ownership, so closedir(3) closed our fd.
ASSERT_EQ(close(fd), -1); ASSERT_EQ(close(fd), -1);
ASSERT_EQ(errno, EBADF); ASSERT_EQ(EBADF, errno);
} }
TEST(dirent, opendir_invalid) { TEST(dirent, opendir_invalid) {
ASSERT_TRUE(opendir("/does/not/exist") == NULL); ASSERT_TRUE(opendir("/does/not/exist") == NULL);
ASSERT_EQ(errno, ENOENT); ASSERT_EQ(ENOENT, errno);
ASSERT_TRUE(opendir("/dev/null") == NULL); ASSERT_TRUE(opendir("/dev/null") == NULL);
ASSERT_EQ(errno, ENOTDIR); ASSERT_EQ(ENOTDIR, errno);
} }
TEST(dirent, opendir) { TEST(dirent, opendir) {
@ -107,7 +107,7 @@ TEST(dirent, opendir) {
TEST(dirent, closedir_invalid) { TEST(dirent, closedir_invalid) {
DIR* d = NULL; DIR* d = NULL;
ASSERT_EQ(closedir(d), -1); ASSERT_EQ(closedir(d), -1);
ASSERT_EQ(errno, EINVAL); ASSERT_EQ(EINVAL, errno);
} }
TEST(dirent, closedir) { TEST(dirent, closedir) {
@ -127,7 +127,7 @@ TEST(dirent, readdir) {
} }
// Reading to the end of the directory is not an error. // Reading to the end of the directory is not an error.
// readdir(3) returns NULL, but leaves errno as 0. // readdir(3) returns NULL, but leaves errno as 0.
ASSERT_EQ(errno, 0); ASSERT_EQ(0, errno);
ASSERT_EQ(closedir(d), 0); ASSERT_EQ(closedir(d), 0);
CheckProcSelf(name_set); CheckProcSelf(name_set);
@ -145,7 +145,7 @@ TEST(dirent, readdir_r) {
} }
// Reading to the end of the directory is not an error. // Reading to the end of the directory is not an error.
// readdir_r(3) returns NULL, but leaves errno as 0. // readdir_r(3) returns NULL, but leaves errno as 0.
ASSERT_EQ(errno, 0); ASSERT_EQ(0, errno);
ASSERT_EQ(closedir(d), 0); ASSERT_EQ(closedir(d), 0);
CheckProcSelf(name_set); CheckProcSelf(name_set);

View File

@ -25,7 +25,7 @@ TEST(getcwd, auto_full) {
errno = 0; errno = 0;
char* cwd = getcwd(NULL, 0); char* cwd = getcwd(NULL, 0);
ASSERT_TRUE(cwd != NULL); ASSERT_TRUE(cwd != NULL);
ASSERT_EQ(errno, 0); ASSERT_EQ(0, errno);
ASSERT_GE(strlen(cwd), 1U); ASSERT_GE(strlen(cwd), 1U);
free(cwd); free(cwd);
} }
@ -35,7 +35,7 @@ TEST(getcwd, auto_reasonable) {
errno = 0; errno = 0;
char* cwd = getcwd(NULL, PATH_MAX); char* cwd = getcwd(NULL, PATH_MAX);
ASSERT_TRUE(cwd != NULL); ASSERT_TRUE(cwd != NULL);
ASSERT_EQ(errno, 0); ASSERT_EQ(0, errno);
ASSERT_GE(strlen(cwd), 1U); ASSERT_GE(strlen(cwd), 1U);
free(cwd); free(cwd);
} }
@ -45,7 +45,7 @@ TEST(getcwd, auto_too_small) {
errno = 0; errno = 0;
char* cwd = getcwd(NULL, 1); char* cwd = getcwd(NULL, 1);
ASSERT_TRUE(cwd == NULL); ASSERT_TRUE(cwd == NULL);
ASSERT_EQ(errno, ERANGE); ASSERT_EQ(ERANGE, errno);
} }
TEST(getcwd, auto_too_large) { TEST(getcwd, auto_too_large) {
@ -53,7 +53,7 @@ TEST(getcwd, auto_too_large) {
errno = 0; errno = 0;
char* cwd = getcwd(NULL, static_cast<size_t>(-1)); char* cwd = getcwd(NULL, static_cast<size_t>(-1));
ASSERT_TRUE(cwd == NULL); ASSERT_TRUE(cwd == NULL);
ASSERT_EQ(errno, ENOMEM); ASSERT_EQ(ENOMEM, errno);
} }
TEST(getcwd, manual_too_small) { TEST(getcwd, manual_too_small) {
@ -62,7 +62,7 @@ TEST(getcwd, manual_too_small) {
errno = 0; errno = 0;
char* cwd = getcwd(tiny_buf, sizeof(tiny_buf)); char* cwd = getcwd(tiny_buf, sizeof(tiny_buf));
ASSERT_TRUE(cwd == NULL); ASSERT_TRUE(cwd == NULL);
ASSERT_EQ(errno, ERANGE); ASSERT_EQ(ERANGE, errno);
} }
TEST(getcwd, manual_zero) { TEST(getcwd, manual_zero) {
@ -71,7 +71,7 @@ TEST(getcwd, manual_zero) {
errno = 0; errno = 0;
char* cwd = getcwd(tiny_buf, 0); char* cwd = getcwd(tiny_buf, 0);
ASSERT_TRUE(cwd == NULL); ASSERT_TRUE(cwd == NULL);
ASSERT_EQ(errno, EINVAL); ASSERT_EQ(EINVAL, errno);
} }
TEST(getcwd, manual_path_max) { TEST(getcwd, manual_path_max) {
@ -79,7 +79,7 @@ TEST(getcwd, manual_path_max) {
errno = 0; errno = 0;
char* cwd = getcwd(buf, PATH_MAX); char* cwd = getcwd(buf, PATH_MAX);
ASSERT_TRUE(cwd == buf); ASSERT_TRUE(cwd == buf);
ASSERT_EQ(errno, 0); ASSERT_EQ(0, errno);
ASSERT_GE(strlen(cwd), 1U); ASSERT_GE(strlen(cwd), 1U);
delete[] cwd; delete[] cwd;
} }

View File

@ -173,3 +173,13 @@ TEST(pthread, pthread_sigmask) {
ASSERT_EQ(SIGUSR1, received_signal); ASSERT_EQ(SIGUSR1, received_signal);
ASSERT_EQ(0, reinterpret_cast<int>(join_result)); ASSERT_EQ(0, reinterpret_cast<int>(join_result));
} }
#if !defined(__GLIBC__)
extern "C" int __pthread_clone(int (*fn)(void*), void* child_stack, int flags, void* arg);
TEST(pthread, __pthread_clone) {
uintptr_t fake_child_stack[16];
errno = 0;
ASSERT_EQ(-1, __pthread_clone(NULL, &fake_child_stack[0], CLONE_THREAD, NULL));
ASSERT_EQ(EINVAL, errno);
}
#endif

View File

@ -75,7 +75,7 @@ TEST(stdio, getdelim) {
// It should set the end-of-file indicator for the stream, though. // It should set the end-of-file indicator for the stream, though.
errno = 0; errno = 0;
ASSERT_EQ(getdelim(&word_read, &allocated_length, ' ', fp), -1); ASSERT_EQ(getdelim(&word_read, &allocated_length, ' ', fp), -1);
ASSERT_EQ(errno, 0); ASSERT_EQ(0, errno);
ASSERT_TRUE(feof(fp)); ASSERT_TRUE(feof(fp));
free(word_read); free(word_read);
@ -91,18 +91,18 @@ TEST(stdio, getdelim_invalid) {
// The first argument can't be NULL. // The first argument can't be NULL.
errno = 0; errno = 0;
ASSERT_EQ(getdelim(NULL, &buffer_length, ' ', fp), -1); ASSERT_EQ(getdelim(NULL, &buffer_length, ' ', fp), -1);
ASSERT_EQ(errno, EINVAL); ASSERT_EQ(EINVAL, errno);
// The second argument can't be NULL. // The second argument can't be NULL.
errno = 0; errno = 0;
ASSERT_EQ(getdelim(&buffer, NULL, ' ', fp), -1); ASSERT_EQ(getdelim(&buffer, NULL, ' ', fp), -1);
ASSERT_EQ(errno, EINVAL); ASSERT_EQ(EINVAL, errno);
// The stream can't be closed. // The stream can't be closed.
fclose(fp); fclose(fp);
errno = 0; errno = 0;
ASSERT_EQ(getdelim(&buffer, &buffer_length, ' ', fp), -1); ASSERT_EQ(getdelim(&buffer, &buffer_length, ' ', fp), -1);
ASSERT_EQ(errno, EBADF); ASSERT_EQ(EBADF, errno);
} }
TEST(stdio, getline) { TEST(stdio, getline) {
@ -140,7 +140,7 @@ TEST(stdio, getline) {
// It should set the end-of-file indicator for the stream, though. // It should set the end-of-file indicator for the stream, though.
errno = 0; errno = 0;
ASSERT_EQ(getline(&line_read, &allocated_length, fp), -1); ASSERT_EQ(getline(&line_read, &allocated_length, fp), -1);
ASSERT_EQ(errno, 0); ASSERT_EQ(0, errno);
ASSERT_TRUE(feof(fp)); ASSERT_TRUE(feof(fp));
free(line_read); free(line_read);
@ -156,16 +156,16 @@ TEST(stdio, getline_invalid) {
// The first argument can't be NULL. // The first argument can't be NULL.
errno = 0; errno = 0;
ASSERT_EQ(getline(NULL, &buffer_length, fp), -1); ASSERT_EQ(getline(NULL, &buffer_length, fp), -1);
ASSERT_EQ(errno, EINVAL); ASSERT_EQ(EINVAL, errno);
// The second argument can't be NULL. // The second argument can't be NULL.
errno = 0; errno = 0;
ASSERT_EQ(getline(&buffer, NULL, fp), -1); ASSERT_EQ(getline(&buffer, NULL, fp), -1);
ASSERT_EQ(errno, EINVAL); ASSERT_EQ(EINVAL, errno);
// The stream can't be closed. // The stream can't be closed.
fclose(fp); fclose(fp);
errno = 0; errno = 0;
ASSERT_EQ(getline(&buffer, &buffer_length, fp), -1); ASSERT_EQ(getline(&buffer, &buffer_length, fp), -1);
ASSERT_EQ(errno, EBADF); ASSERT_EQ(EBADF, errno);
} }

View File

@ -38,7 +38,7 @@ static void check_getpwnam(const char* username, uid_t uid, uid_type_t uid_type)
errno = 0; errno = 0;
passwd* pwd = getpwuid(uid); passwd* pwd = getpwuid(uid);
ASSERT_TRUE(pwd != NULL); ASSERT_TRUE(pwd != NULL);
ASSERT_EQ(errno, 0); ASSERT_EQ(0, errno);
EXPECT_STREQ(username, pwd->pw_name); EXPECT_STREQ(username, pwd->pw_name);
EXPECT_EQ(uid, pwd->pw_uid); EXPECT_EQ(uid, pwd->pw_uid);
EXPECT_EQ(uid, pwd->pw_gid); EXPECT_EQ(uid, pwd->pw_gid);