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:
parent
1fea0f258a
commit
5e3fc43dde
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user