Merge "Fix __pthread_clone on ARM to set errno on failure."
This commit is contained in:
commit
cae7b2cfb5
7 changed files with 57 additions and 55 deletions
|
@ -29,38 +29,35 @@
|
|||
#include <machine/asm.h>
|
||||
#include <sys/linux-syscalls.h>
|
||||
|
||||
// int __pthread_clone(int (*fn)(void*), void* child_stack, int flags, void* arg);
|
||||
ENTRY(__pthread_clone)
|
||||
@ insert the args onto the new stack
|
||||
# Copy the args onto the new stack.
|
||||
stmdb r1!, {r0, r3}
|
||||
|
||||
@ do the system call
|
||||
@ get flags
|
||||
|
||||
# The sys_clone system call only takes two arguments: 'flags' and 'child_stack'.
|
||||
# 'child_stack' is already in r1, but we need to move 'flags' into position.
|
||||
mov r0, r2
|
||||
|
||||
@ new sp is already in r1
|
||||
|
||||
stmfd sp!, {r4, r7}
|
||||
|
||||
# System call.
|
||||
ldr r7, =__NR_clone
|
||||
swi #0
|
||||
|
||||
movs r0, r0
|
||||
ldmnefd sp!, {r4, r7}
|
||||
blt __error
|
||||
beq 1f
|
||||
|
||||
# In parent, reload saved registers then either exit or set errno.
|
||||
ldmfd sp!, {r4, r7}
|
||||
bxne lr
|
||||
b __set_syscall_errno
|
||||
|
||||
|
||||
@ pick the function arg and call address off the stack and jump
|
||||
@ to the C __thread_entry function which does some setup and then
|
||||
@ calls the thread's start function
|
||||
|
||||
1: # The child.
|
||||
# pick the function arg and call address off the stack and jump
|
||||
# to the C __thread_entry function which does some setup and then
|
||||
# calls the thread's start function
|
||||
pop {r0, r1}
|
||||
mov r2, sp @ __thread_entry needs the TLS pointer
|
||||
# __thread_entry needs the TLS pointer
|
||||
mov r2, sp
|
||||
b __thread_entry
|
||||
|
||||
__error:
|
||||
mov r0, #-1
|
||||
bx lr
|
||||
END(__pthread_clone)
|
||||
|
||||
|
||||
|
@ -71,8 +68,8 @@ END(__pthread_clone)
|
|||
# pid_t *pid, void *tls, pid_t *ctid,
|
||||
# int (*fn)(void *), void* arg );
|
||||
#
|
||||
# NOTE: This is not the same signature than the GLibc
|
||||
# __clone function here !! Placing 'fn' and 'arg'
|
||||
# NOTE: This is not the same signature as the glibc
|
||||
# __clone function. Placing 'fn' and 'arg'
|
||||
# at the end of the parameter list makes the
|
||||
# implementation much simpler.
|
||||
#
|
||||
|
@ -91,20 +88,18 @@ ENTRY(__bionic_clone)
|
|||
str r5, [r1, #-4]
|
||||
str r6, [r1, #-8]
|
||||
|
||||
# system call
|
||||
# System call
|
||||
ldr r7, =__NR_clone
|
||||
swi #0
|
||||
movs r0, r0
|
||||
beq 1f
|
||||
|
||||
# in parent, reload saved registers
|
||||
# then either exit or error
|
||||
#
|
||||
# In the parent, reload saved registers then either exit or set errno.
|
||||
ldmfd sp!, {r4, r5, r6, r7}
|
||||
bxne lr
|
||||
b __set_syscall_errno
|
||||
|
||||
1: # in the child - pick arguments
|
||||
1: # The child.
|
||||
ldr r0, [sp, #-4]
|
||||
ldr r1, [sp, #-8]
|
||||
b __bionic_clone_entry
|
||||
|
|
|
@ -2,10 +2,7 @@
|
|||
|
||||
.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
|
||||
.type __pthread_clone, @function
|
||||
.align 4
|
||||
|
|
|
@ -66,12 +66,12 @@ TEST(dirent, scandir) {
|
|||
|
||||
TEST(dirent, fdopendir_invalid) {
|
||||
ASSERT_TRUE(fdopendir(-1) == NULL);
|
||||
ASSERT_EQ(errno, EBADF);
|
||||
ASSERT_EQ(EBADF, errno);
|
||||
|
||||
int fd = open("/dev/null", O_RDONLY);
|
||||
ASSERT_NE(fd, -1);
|
||||
ASSERT_TRUE(fdopendir(fd) == NULL);
|
||||
ASSERT_EQ(errno, ENOTDIR);
|
||||
ASSERT_EQ(ENOTDIR, errno);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
|
@ -85,15 +85,15 @@ TEST(dirent, fdopendir) {
|
|||
|
||||
// fdopendir(3) took ownership, so closedir(3) closed our fd.
|
||||
ASSERT_EQ(close(fd), -1);
|
||||
ASSERT_EQ(errno, EBADF);
|
||||
ASSERT_EQ(EBADF, errno);
|
||||
}
|
||||
|
||||
TEST(dirent, opendir_invalid) {
|
||||
ASSERT_TRUE(opendir("/does/not/exist") == NULL);
|
||||
ASSERT_EQ(errno, ENOENT);
|
||||
ASSERT_EQ(ENOENT, errno);
|
||||
|
||||
ASSERT_TRUE(opendir("/dev/null") == NULL);
|
||||
ASSERT_EQ(errno, ENOTDIR);
|
||||
ASSERT_EQ(ENOTDIR, errno);
|
||||
}
|
||||
|
||||
TEST(dirent, opendir) {
|
||||
|
@ -107,7 +107,7 @@ TEST(dirent, opendir) {
|
|||
TEST(dirent, closedir_invalid) {
|
||||
DIR* d = NULL;
|
||||
ASSERT_EQ(closedir(d), -1);
|
||||
ASSERT_EQ(errno, EINVAL);
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
}
|
||||
|
||||
TEST(dirent, closedir) {
|
||||
|
@ -127,7 +127,7 @@ TEST(dirent, readdir) {
|
|||
}
|
||||
// Reading to the end of the directory is not an error.
|
||||
// readdir(3) returns NULL, but leaves errno as 0.
|
||||
ASSERT_EQ(errno, 0);
|
||||
ASSERT_EQ(0, errno);
|
||||
ASSERT_EQ(closedir(d), 0);
|
||||
|
||||
CheckProcSelf(name_set);
|
||||
|
@ -145,7 +145,7 @@ TEST(dirent, readdir_r) {
|
|||
}
|
||||
// Reading to the end of the directory is not an error.
|
||||
// readdir_r(3) returns NULL, but leaves errno as 0.
|
||||
ASSERT_EQ(errno, 0);
|
||||
ASSERT_EQ(0, errno);
|
||||
ASSERT_EQ(closedir(d), 0);
|
||||
|
||||
CheckProcSelf(name_set);
|
||||
|
|
|
@ -25,7 +25,7 @@ TEST(getcwd, auto_full) {
|
|||
errno = 0;
|
||||
char* cwd = getcwd(NULL, 0);
|
||||
ASSERT_TRUE(cwd != NULL);
|
||||
ASSERT_EQ(errno, 0);
|
||||
ASSERT_EQ(0, errno);
|
||||
ASSERT_GE(strlen(cwd), 1U);
|
||||
free(cwd);
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ TEST(getcwd, auto_reasonable) {
|
|||
errno = 0;
|
||||
char* cwd = getcwd(NULL, PATH_MAX);
|
||||
ASSERT_TRUE(cwd != NULL);
|
||||
ASSERT_EQ(errno, 0);
|
||||
ASSERT_EQ(0, errno);
|
||||
ASSERT_GE(strlen(cwd), 1U);
|
||||
free(cwd);
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ TEST(getcwd, auto_too_small) {
|
|||
errno = 0;
|
||||
char* cwd = getcwd(NULL, 1);
|
||||
ASSERT_TRUE(cwd == NULL);
|
||||
ASSERT_EQ(errno, ERANGE);
|
||||
ASSERT_EQ(ERANGE, errno);
|
||||
}
|
||||
|
||||
TEST(getcwd, auto_too_large) {
|
||||
|
@ -53,7 +53,7 @@ TEST(getcwd, auto_too_large) {
|
|||
errno = 0;
|
||||
char* cwd = getcwd(NULL, static_cast<size_t>(-1));
|
||||
ASSERT_TRUE(cwd == NULL);
|
||||
ASSERT_EQ(errno, ENOMEM);
|
||||
ASSERT_EQ(ENOMEM, errno);
|
||||
}
|
||||
|
||||
TEST(getcwd, manual_too_small) {
|
||||
|
@ -62,7 +62,7 @@ TEST(getcwd, manual_too_small) {
|
|||
errno = 0;
|
||||
char* cwd = getcwd(tiny_buf, sizeof(tiny_buf));
|
||||
ASSERT_TRUE(cwd == NULL);
|
||||
ASSERT_EQ(errno, ERANGE);
|
||||
ASSERT_EQ(ERANGE, errno);
|
||||
}
|
||||
|
||||
TEST(getcwd, manual_zero) {
|
||||
|
@ -71,7 +71,7 @@ TEST(getcwd, manual_zero) {
|
|||
errno = 0;
|
||||
char* cwd = getcwd(tiny_buf, 0);
|
||||
ASSERT_TRUE(cwd == NULL);
|
||||
ASSERT_EQ(errno, EINVAL);
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
}
|
||||
|
||||
TEST(getcwd, manual_path_max) {
|
||||
|
@ -79,7 +79,7 @@ TEST(getcwd, manual_path_max) {
|
|||
errno = 0;
|
||||
char* cwd = getcwd(buf, PATH_MAX);
|
||||
ASSERT_TRUE(cwd == buf);
|
||||
ASSERT_EQ(errno, 0);
|
||||
ASSERT_EQ(0, errno);
|
||||
ASSERT_GE(strlen(cwd), 1U);
|
||||
delete[] cwd;
|
||||
}
|
||||
|
|
|
@ -173,3 +173,13 @@ TEST(pthread, pthread_sigmask) {
|
|||
ASSERT_EQ(SIGUSR1, received_signal);
|
||||
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.
|
||||
errno = 0;
|
||||
ASSERT_EQ(getdelim(&word_read, &allocated_length, ' ', fp), -1);
|
||||
ASSERT_EQ(errno, 0);
|
||||
ASSERT_EQ(0, errno);
|
||||
ASSERT_TRUE(feof(fp));
|
||||
|
||||
free(word_read);
|
||||
|
@ -91,18 +91,18 @@ TEST(stdio, getdelim_invalid) {
|
|||
// The first argument can't be NULL.
|
||||
errno = 0;
|
||||
ASSERT_EQ(getdelim(NULL, &buffer_length, ' ', fp), -1);
|
||||
ASSERT_EQ(errno, EINVAL);
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
|
||||
// The second argument can't be NULL.
|
||||
errno = 0;
|
||||
ASSERT_EQ(getdelim(&buffer, NULL, ' ', fp), -1);
|
||||
ASSERT_EQ(errno, EINVAL);
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
|
||||
// The stream can't be closed.
|
||||
fclose(fp);
|
||||
errno = 0;
|
||||
ASSERT_EQ(getdelim(&buffer, &buffer_length, ' ', fp), -1);
|
||||
ASSERT_EQ(errno, EBADF);
|
||||
ASSERT_EQ(EBADF, errno);
|
||||
}
|
||||
|
||||
TEST(stdio, getline) {
|
||||
|
@ -140,7 +140,7 @@ TEST(stdio, getline) {
|
|||
// It should set the end-of-file indicator for the stream, though.
|
||||
errno = 0;
|
||||
ASSERT_EQ(getline(&line_read, &allocated_length, fp), -1);
|
||||
ASSERT_EQ(errno, 0);
|
||||
ASSERT_EQ(0, errno);
|
||||
ASSERT_TRUE(feof(fp));
|
||||
|
||||
free(line_read);
|
||||
|
@ -156,16 +156,16 @@ TEST(stdio, getline_invalid) {
|
|||
// The first argument can't be NULL.
|
||||
errno = 0;
|
||||
ASSERT_EQ(getline(NULL, &buffer_length, fp), -1);
|
||||
ASSERT_EQ(errno, EINVAL);
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
|
||||
// The second argument can't be NULL.
|
||||
errno = 0;
|
||||
ASSERT_EQ(getline(&buffer, NULL, fp), -1);
|
||||
ASSERT_EQ(errno, EINVAL);
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
|
||||
// The stream can't be closed.
|
||||
fclose(fp);
|
||||
errno = 0;
|
||||
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;
|
||||
passwd* pwd = getpwuid(uid);
|
||||
ASSERT_TRUE(pwd != NULL);
|
||||
ASSERT_EQ(errno, 0);
|
||||
ASSERT_EQ(0, errno);
|
||||
EXPECT_STREQ(username, pwd->pw_name);
|
||||
EXPECT_EQ(uid, pwd->pw_uid);
|
||||
EXPECT_EQ(uid, pwd->pw_gid);
|
||||
|
|
Loading…
Reference in a new issue