diff options
Diffstat (limited to 'libc')
116 files changed, 10467 insertions, 1251 deletions
diff --git a/libc/Android.mk b/libc/Android.mk index 8dd124e..590afbe 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -26,6 +26,7 @@ libc_common_src_files := \ unistd/initgroups.c \ unistd/isatty.c \ unistd/issetugid.c \ + unistd/killpg.c \ unistd/lseek64.c \ unistd/mmap.c \ unistd/nice.c \ @@ -46,7 +47,10 @@ libc_common_src_files := \ unistd/sbrk.c \ unistd/send.c \ unistd/setegid.c \ + unistd/setuid.c \ unistd/seteuid.c \ + unistd/setreuid.c \ + unistd/setresuid.c \ unistd/setpgrp.c \ unistd/sigblock.c \ unistd/siginterrupt.c \ @@ -215,14 +219,19 @@ libc_common_src_files := \ bionic/__errno.c \ bionic/__set_errno.c \ bionic/_rand48.c \ + bionic/cpuacct.c \ bionic/arc4random.c \ bionic/basename.c \ bionic/basename_r.c \ + bionic/clearenv.c \ bionic/dirname.c \ bionic/dirname_r.c \ bionic/drand48.c \ bionic/erand48.c \ + bionic/err.c \ + bionic/fdprintf.c \ bionic/fork.c \ + bionic/fts.c \ bionic/if_nametoindex.c \ bionic/if_indextoname.c \ bionic/ioctl.c \ @@ -270,13 +279,18 @@ libc_common_src_files := \ netbsd/nameser/ns_ttl.c \ netbsd/nameser/ns_netint.c \ netbsd/nameser/ns_print.c \ - netbsd/nameser/ns_samedomain.c + netbsd/nameser/ns_samedomain.c \ + regex/regcomp.c \ + regex/regerror.c \ + regex/regexec.c \ + regex/regfree.c \ # Architecture specific source files go here # ========================================================= ifeq ($(TARGET_ARCH),arm) libc_common_src_files += \ bionic/eabi.c \ + bionic/bionic_clone.c \ arch-arm/bionic/__get_pc.S \ arch-arm/bionic/__get_sp.S \ arch-arm/bionic/_exit_with_stack_teardown.S \ @@ -400,7 +414,16 @@ libc_common_cflags := \ -DNEED_PSELECT=1 \ -DINET6 \ -I$(LOCAL_PATH)/private \ - -DUSE_DL_PREFIX + -DUSE_DL_PREFIX \ + -DPOSIX_MISTAKE + +# these macro definitions are required to implement the +# 'timezone' and 'daylight' global variables, as well as +# properly update the 'tm_gmtoff' field in 'struct tm'. +# +libc_common_cflags += \ + -DTM_GMTOFF=tm_gmtoff \ + -DUSG_COMPAT=1 ifeq ($(strip $(DEBUG_BIONIC_LIBC)),true) libc_common_cflags += -DDEBUG @@ -534,17 +557,12 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ $(libc_arch_static_src_files) \ bionic/dlmalloc.c \ + bionic/malloc_debug_common.c \ bionic/libc_init_static.c -LOCAL_CFLAGS := $(libc_common_cflags) - -ifeq ($(WITH_MALLOC_CHECK_LIBC_A),true) - LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK - LOCAL_SRC_FILES += bionic/malloc_leak.c.arm -endif - +LOCAL_CFLAGS := $(libc_common_cflags) \ + -DLIBC_STATIC LOCAL_C_INCLUDES := $(libc_common_c_includes) - LOCAL_MODULE := libc LOCAL_WHOLE_STATIC_LIBRARIES := libc_common LOCAL_SYSTEM_SHARED_LIBRARIES := @@ -563,7 +581,7 @@ LOCAL_C_INCLUDES := $(libc_common_c_includes) LOCAL_SRC_FILES := \ $(libc_arch_dynamic_src_files) \ bionic/dlmalloc.c \ - bionic/malloc_leak.c.arm \ + bionic/malloc_debug_common.c \ bionic/libc_init_dynamic.c LOCAL_MODULE:= libc @@ -583,8 +601,16 @@ LOCAL_SYSTEM_SHARED_LIBRARIES := include $(BUILD_SHARED_LIBRARY) +# For all builds, except for the -user build we will enable memory +# allocation checking (including memory leaks, buffer overwrites, etc.) +# Note that all these checks are also controlled by env. settings +# that can enable, or disable specific checks. Note also that some of +# the checks are available only in emulator and are implemeted in +# libc_malloc_qemu_instrumented.so. +ifneq ($(TARGET_BUILD_VARIANT),user) + # ======================================================== -# libc_debug.so +# libc_malloc_debug_leak.so # ======================================================== include $(CLEAR_VARS) @@ -595,30 +621,49 @@ LOCAL_CFLAGS := \ LOCAL_C_INCLUDES := $(libc_common_c_includes) LOCAL_SRC_FILES := \ - $(libc_arch_dynamic_src_files) \ - bionic/dlmalloc.c \ - bionic/malloc_leak.c.arm \ - bionic/libc_init_dynamic.c + bionic/malloc_debug_leak.c -LOCAL_MODULE:= libc_debug +LOCAL_MODULE:= libc_malloc_debug_leak -# WARNING: The only library libc.so should depend on is libdl.so! If you add other libraries, -# make sure to add -Wl,--exclude-libs=libgcc.a to the LOCAL_LDFLAGS for those libraries. This -# ensures that symbols that are pulled into those new libraries from libgcc.a are not declared -# external; if that were the case, then libc would not pull those symbols from libgcc.a as it -# should, instead relying on the external symbols from the dependent libraries. That would -# create an "cloaked" dependency on libgcc.a in libc though the libraries, which is not what -# you wanted! +LOCAL_SHARED_LIBRARIES := libc +LOCAL_WHOLE_STATIC_LIBRARIES := libc_common +LOCAL_SYSTEM_SHARED_LIBRARIES := +# Don't prelink +LOCAL_PRELINK_MODULE := false +# Don't install on release build +LOCAL_MODULE_TAGS := eng debug -LOCAL_SHARED_LIBRARIES := libdl +include $(BUILD_SHARED_LIBRARY) + + +# ======================================================== +# libc_malloc_debug_qemu.so +# ======================================================== +include $(CLEAR_VARS) + +LOCAL_CFLAGS := \ + $(libc_common_cflags) \ + -DMALLOC_QEMU_INSTRUMENT + +LOCAL_C_INCLUDES := $(libc_common_c_includes) + +LOCAL_SRC_FILES := \ + bionic/malloc_debug_qemu.c + +LOCAL_MODULE:= libc_malloc_debug_qemu + +LOCAL_SHARED_LIBRARIES := libc LOCAL_WHOLE_STATIC_LIBRARIES := libc_common LOCAL_SYSTEM_SHARED_LIBRARIES := # Don't prelink LOCAL_PRELINK_MODULE := false # Don't install on release build -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := eng debug include $(BUILD_SHARED_LIBRARY) +endif #!user + + # ======================================================== include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT index eaeea2b..11407b9 100644 --- a/libc/SYSCALLS.TXT +++ b/libc/SYSCALLS.TXT @@ -34,10 +34,15 @@ void _exit_thread:exit (int) 1 pid_t __fork:fork (void) 2 pid_t _waitpid:waitpid (pid_t, int*, int, struct rusage*) -1,7 int waitid(int, pid_t, struct siginfo_t*, int,void*) 280,284 -pid_t __clone:clone(int (*fn)(void*), void *child_stack, int flags, void *arg) 120 + +# NOTE: this system call is never called directly, but we list it there +# to have __NR_clone properly defined. +# +pid_t __sys_clone:clone (int, void*, int*, void*, int*) 120 + int execve (const char*, char* const*, char* const*) 11 -int setuid:setuid32 (uid_t) 213 +int __setuid:setuid32 (uid_t) 213 uid_t getuid:getuid32 () 199 gid_t getgid:getgid32 () 200 uid_t geteuid:geteuid32 () 201 @@ -51,8 +56,8 @@ pid_t getppid() 64 pid_t setsid() 66 int setgid:setgid32(gid_t) 214 int seteuid:seteuid32(uid_t) stub -int setreuid:setreuid32(uid_t, uid_t) 203 -int setresuid:setresuid32(uid_t, uid_t, uid_t) 208 +int __setreuid:setreuid32(uid_t, uid_t) 203 +int __setresuid:setresuid32(uid_t, uid_t, uid_t) 208 int setresgid:setresgid32(gid_t, gid_t, gid_t) 210 void* __brk:brk(void*) 45 # see comments in arch-arm/bionic/kill.S to understand why we don't generate an ARM stub for kill/tkill @@ -74,6 +79,7 @@ int chroot(const char *) 61 int prctl(int option, unsigned int arg2, unsigned int arg3, unsigned int arg4, unsigned int arg5) 172 int capget(cap_user_header_t header, cap_user_data_t data) 184 int capset(cap_user_header_t header, const cap_user_data_t data) 185 +int sigaltstack(const stack_t*, stack_t*) 186 int acct(const char* filepath) 51 # file descriptors @@ -225,6 +231,10 @@ int sched_get_priority_max(int policy) 159 int sched_get_priority_min(int policy) 160 int sched_rr_get_interval(pid_t pid, struct timespec *interval) 161 +# io priorities +int ioprio_set(int which, int who, int ioprio) 314,289 +int ioprio_get(int which, int who) 315,290 + # other int uname(struct utsname *) 122 pid_t __wait4:wait4(pid_t pid, int *status, int options, struct rusage *rusage) 114 diff --git a/libc/arch-arm/bionic/__get_pc.S b/libc/arch-arm/bionic/__get_pc.S index d1377c7..4fc8929 100644 --- a/libc/arch-arm/bionic/__get_pc.S +++ b/libc/arch-arm/bionic/__get_pc.S @@ -26,6 +26,7 @@ * SUCH DAMAGE. */ .global __get_pc +.type __get_pc, %function __get_pc: mov r0, pc diff --git a/libc/arch-arm/bionic/__get_sp.S b/libc/arch-arm/bionic/__get_sp.S index 9acaf3d..0a313a3 100644 --- a/libc/arch-arm/bionic/__get_sp.S +++ b/libc/arch-arm/bionic/__get_sp.S @@ -26,6 +26,7 @@ * SUCH DAMAGE. */ .global __get_sp +.type __get_sp, %function __get_sp: mov r0, sp diff --git a/libc/arch-arm/bionic/atomics_arm.S b/libc/arch-arm/bionic/atomics_arm.S index 0cd0b92..047541f 100644 --- a/libc/arch-arm/bionic/atomics_arm.S +++ b/libc/arch-arm/bionic/atomics_arm.S @@ -28,11 +28,13 @@ #include <sys/linux-syscalls.h> .global __atomic_cmpxchg +.type __atomic_cmpxchg, %function .global __atomic_swap +.type __atomic_swap, %function .global __atomic_dec +.type __atomic_dec, %function .global __atomic_inc -.global __futex_wait -.global __futex_wake +.type __atomic_inc, %function #define FUTEX_WAIT 0 #define FUTEX_WAKE 1 @@ -153,10 +155,34 @@ __atomic_swap: bx lr /* __futex_wait(*ftx, val, *timespec) */ -/* __futex_syscall(*ftx, op, val, *timespec, *addr2, val3) */ +/* __futex_wake(*ftx, counter) */ +/* __futex_syscall3(*ftx, op, val) */ +/* __futex_syscall4(*ftx, op, val, *timespec) */ + +.global __futex_wait +.type __futex_wait, %function + +.global __futex_wake +.type __futex_wake, %function + +.global __futex_syscall3 +.type __futex_syscall3, %function + +.global __futex_syscall4 +.type __futex_syscall4, %function #if __ARM_EABI__ +__futex_syscall3: + .fnstart + stmdb sp!, {r4, r7} + .save {r4, r7} + ldr r7, =__NR_futex + swi #0 + ldmia sp!, {r4, r7} + bx lr + .fnend + __futex_wait: .fnstart stmdb sp!, {r4, r7} @@ -181,6 +207,10 @@ __futex_wake: #else +__futex_syscall3: + swi #__NR_futex + bx lr + __futex_wait: mov r3, r2 mov r2, r1 @@ -195,3 +225,6 @@ __futex_wake: bx lr #endif + +__futex_syscall4: + b __futex_syscall3 diff --git a/libc/arch-arm/bionic/clone.S b/libc/arch-arm/bionic/clone.S index 791c73d..9c25053 100644 --- a/libc/arch-arm/bionic/clone.S +++ b/libc/arch-arm/bionic/clone.S @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2008-2010 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,48 +27,102 @@ */ #include <sys/linux-syscalls.h> - .text - .type __pthread_clone, #function - .global __pthread_clone - .align 4 - + .text + .type __pthread_clone, #function + .global __pthread_clone + .align 4 + .fnstart + __pthread_clone: - @ insert the args onto the new stack - str r0, [r1, #-4] - str r3, [r1, #-8] + @ insert the args onto the new stack + str r0, [r1, #-4] + str r3, [r1, #-8] + + @ do the system call + @ get flags - @ do the system call - @ get flags - mov r0, r2 - + @ new sp is already in r1 #if __ARM_EABI__ stmfd sp!, {r4, r7} ldr r7, =__NR_clone - swi #0 + swi #0 #else - swi #__NR_clone + swi #__NR_clone #endif - movs r0, r0 + movs r0, r0 #if __ARM_EABI__ ldmnefd sp!, {r4, r7} #endif - blt __error - bxne lr + blt __error + bxne lr - @ 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 + @ 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 - ldr r0, [sp, #-4] - ldr r1, [sp, #-8] - mov r2, sp @ __thread_entry needs the TLS pointer - b __thread_entry + ldr r0, [sp, #-4] + ldr r1, [sp, #-8] + mov r2, sp @ __thread_entry needs the TLS pointer + b __thread_entry __error: - mov r0, #-1 - bx lr + mov r0, #-1 + bx lr + .fnend + + + # + # This function is defined as: + # + # pid_t __bionic_clone( int flags, void *child_stack, + # 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' + # at the end of the parameter list makes the + # implementation much simpler. + # + .type __bionic_clone, #function + .globl __bionic_clone + .align 4 + .fnstart + +__bionic_clone: + mov ip, sp + .save {r4, r5, r6, r7} + + # save registers to parent stack + stmfd sp!, {r4, r5, r6, r7} + + # load extra parameters + ldmfd ip, {r4, r5, r6} + + # store 'fn' and 'arg' to the child stack + str r5, [r1, #-4] + str r6, [r1, #-8] + + # system call + ldr r7, =__NR_clone + swi #0 + movs r0, r0 + beq 1f + + # in parent, reload saved registers + # then either exit or error + # + ldmfd sp!, {r4, r5, r6, r7} + bxne lr + b __set_syscall_errno + +1: # in the child - pick arguments + ldr r0, [sp, #-4] + ldr r1, [sp, #-8] + b __bionic_clone_entry + + .fnend diff --git a/libc/arch-arm/bionic/memcpy.S b/libc/arch-arm/bionic/memcpy.S index 024d885..ba55996 100644 --- a/libc/arch-arm/bionic/memcpy.S +++ b/libc/arch-arm/bionic/memcpy.S @@ -28,7 +28,7 @@ #include <machine/cpu-features.h> -#if __ARM_ARCH__ == 7 || defined(__ARM_NEON__) +#if defined(__ARM_NEON__) .text .fpu neon diff --git a/libc/arch-arm/syscalls.mk b/libc/arch-arm/syscalls.mk index f4cb63a..9cdd28a 100644 --- a/libc/arch-arm/syscalls.mk +++ b/libc/arch-arm/syscalls.mk @@ -4,9 +4,9 @@ syscall_src += arch-arm/syscalls/_exit.S syscall_src += arch-arm/syscalls/_exit_thread.S syscall_src += arch-arm/syscalls/__fork.S syscall_src += arch-arm/syscalls/waitid.S -syscall_src += arch-arm/syscalls/__clone.S +syscall_src += arch-arm/syscalls/__sys_clone.S syscall_src += arch-arm/syscalls/execve.S -syscall_src += arch-arm/syscalls/setuid.S +syscall_src += arch-arm/syscalls/__setuid.S syscall_src += arch-arm/syscalls/getuid.S syscall_src += arch-arm/syscalls/getgid.S syscall_src += arch-arm/syscalls/geteuid.S @@ -19,8 +19,8 @@ syscall_src += arch-arm/syscalls/getpgid.S syscall_src += arch-arm/syscalls/getppid.S syscall_src += arch-arm/syscalls/setsid.S syscall_src += arch-arm/syscalls/setgid.S -syscall_src += arch-arm/syscalls/setreuid.S -syscall_src += arch-arm/syscalls/setresuid.S +syscall_src += arch-arm/syscalls/__setreuid.S +syscall_src += arch-arm/syscalls/__setresuid.S syscall_src += arch-arm/syscalls/setresgid.S syscall_src += arch-arm/syscalls/__brk.S syscall_src += arch-arm/syscalls/__ptrace.S @@ -37,6 +37,7 @@ syscall_src += arch-arm/syscalls/chroot.S syscall_src += arch-arm/syscalls/prctl.S syscall_src += arch-arm/syscalls/capget.S syscall_src += arch-arm/syscalls/capset.S +syscall_src += arch-arm/syscalls/sigaltstack.S syscall_src += arch-arm/syscalls/acct.S syscall_src += arch-arm/syscalls/read.S syscall_src += arch-arm/syscalls/write.S @@ -150,6 +151,8 @@ syscall_src += arch-arm/syscalls/sched_getparam.S syscall_src += arch-arm/syscalls/sched_get_priority_max.S syscall_src += arch-arm/syscalls/sched_get_priority_min.S syscall_src += arch-arm/syscalls/sched_rr_get_interval.S +syscall_src += arch-arm/syscalls/ioprio_set.S +syscall_src += arch-arm/syscalls/ioprio_get.S syscall_src += arch-arm/syscalls/uname.S syscall_src += arch-arm/syscalls/__wait4.S syscall_src += arch-arm/syscalls/umask.S diff --git a/libc/arch-arm/syscalls/setresuid.S b/libc/arch-arm/syscalls/__setresuid.S index 266c1a1..7710772 100644 --- a/libc/arch-arm/syscalls/setresuid.S +++ b/libc/arch-arm/syscalls/__setresuid.S @@ -2,12 +2,12 @@ #include <sys/linux-syscalls.h> .text - .type setresuid, #function - .globl setresuid + .type __setresuid, #function + .globl __setresuid .align 4 .fnstart -setresuid: +__setresuid: .save {r4, r7} stmfd sp!, {r4, r7} ldr r7, =__NR_setresuid32 diff --git a/libc/arch-arm/syscalls/setreuid.S b/libc/arch-arm/syscalls/__setreuid.S index 0f94b47..0c68866 100644 --- a/libc/arch-arm/syscalls/setreuid.S +++ b/libc/arch-arm/syscalls/__setreuid.S @@ -2,12 +2,12 @@ #include <sys/linux-syscalls.h> .text - .type setreuid, #function - .globl setreuid + .type __setreuid, #function + .globl __setreuid .align 4 .fnstart -setreuid: +__setreuid: .save {r4, r7} stmfd sp!, {r4, r7} ldr r7, =__NR_setreuid32 diff --git a/libc/arch-arm/syscalls/setuid.S b/libc/arch-arm/syscalls/__setuid.S index 31cf446..efc6e56 100644 --- a/libc/arch-arm/syscalls/setuid.S +++ b/libc/arch-arm/syscalls/__setuid.S @@ -2,12 +2,12 @@ #include <sys/linux-syscalls.h> .text - .type setuid, #function - .globl setuid + .type __setuid, #function + .globl __setuid .align 4 .fnstart -setuid: +__setuid: .save {r4, r7} stmfd sp!, {r4, r7} ldr r7, =__NR_setuid32 diff --git a/libc/arch-arm/syscalls/__sys_clone.S b/libc/arch-arm/syscalls/__sys_clone.S new file mode 100644 index 0000000..9fe2641 --- /dev/null +++ b/libc/arch-arm/syscalls/__sys_clone.S @@ -0,0 +1,21 @@ +/* autogenerated by gensyscalls.py */ +#include <sys/linux-syscalls.h> + + .text + .type __sys_clone, #function + .globl __sys_clone + .align 4 + .fnstart + +__sys_clone: + mov ip, sp + .save {r4, r5, r6, r7} + stmfd sp!, {r4, r5, r6, r7} + ldmfd ip, {r4, r5, r6} + ldr r7, =__NR_clone + swi #0 + ldmfd sp!, {r4, r5, r6, r7} + movs r0, r0 + bxpl lr + b __set_syscall_errno + .fnend diff --git a/libc/arch-arm/syscalls/__clone.S b/libc/arch-arm/syscalls/ioprio_get.S index 650e2c0..d686e98 100644 --- a/libc/arch-arm/syscalls/__clone.S +++ b/libc/arch-arm/syscalls/ioprio_get.S @@ -2,15 +2,15 @@ #include <sys/linux-syscalls.h> .text - .type __clone, #function - .globl __clone + .type ioprio_get, #function + .globl ioprio_get .align 4 .fnstart -__clone: +ioprio_get: .save {r4, r7} stmfd sp!, {r4, r7} - ldr r7, =__NR_clone + ldr r7, =__NR_ioprio_get swi #0 ldmfd sp!, {r4, r7} movs r0, r0 diff --git a/libc/arch-arm/syscalls/ioprio_set.S b/libc/arch-arm/syscalls/ioprio_set.S new file mode 100644 index 0000000..a812557 --- /dev/null +++ b/libc/arch-arm/syscalls/ioprio_set.S @@ -0,0 +1,19 @@ +/* autogenerated by gensyscalls.py */ +#include <sys/linux-syscalls.h> + + .text + .type ioprio_set, #function + .globl ioprio_set + .align 4 + .fnstart + +ioprio_set: + .save {r4, r7} + stmfd sp!, {r4, r7} + ldr r7, =__NR_ioprio_set + swi #0 + ldmfd sp!, {r4, r7} + movs r0, r0 + bxpl lr + b __set_syscall_errno + .fnend diff --git a/libc/arch-arm/syscalls/sigaltstack.S b/libc/arch-arm/syscalls/sigaltstack.S new file mode 100644 index 0000000..3625d0b --- /dev/null +++ b/libc/arch-arm/syscalls/sigaltstack.S @@ -0,0 +1,19 @@ +/* autogenerated by gensyscalls.py */ +#include <sys/linux-syscalls.h> + + .text + .type sigaltstack, #function + .globl sigaltstack + .align 4 + .fnstart + +sigaltstack: + .save {r4, r7} + stmfd sp!, {r4, r7} + ldr r7, =__NR_sigaltstack + swi #0 + ldmfd sp!, {r4, r7} + movs r0, r0 + bxpl lr + b __set_syscall_errno + .fnend diff --git a/libc/arch-sh/bionic/atomics_sh.c b/libc/arch-sh/bionic/atomics_sh.c index 16966f7..c7815ff 100644 --- a/libc/arch-sh/bionic/atomics_sh.c +++ b/libc/arch-sh/bionic/atomics_sh.c @@ -98,3 +98,13 @@ int __futex_wake(volatile void *ftx, int count) { return futex(ftx, FUTEX_WAKE, count, NULL, NULL, 0); } + +int __futex_syscall3(volatile void *ftx, int op, int val) +{ + return futex(ftx, op, val, NULL, NULL, 0); +} + +int __futex_syscall4(volative void *ftx, int op, int val, const struct timespec *timeout) +{ + return futex(ftx, op, val, (void *)timeout, NULL, 0); +} diff --git a/libc/arch-sh/bionic/clone.S b/libc/arch-sh/bionic/clone.S index 0bbaecb..9cb19ee 100644 --- a/libc/arch-sh/bionic/clone.S +++ b/libc/arch-sh/bionic/clone.S @@ -72,3 +72,8 @@ __return: .align 2 0: .long __NR_clone 1: .long __thread_entry + +/* XXX: TODO: Add __bionic_clone here + * See bionic/bionic_clone.c and arch-arm/bionic/clone.S + * for more details... + */
\ No newline at end of file diff --git a/libc/arch-sh/syscalls.mk b/libc/arch-sh/syscalls.mk index cefb2ec..ab2f3d1 100644 --- a/libc/arch-sh/syscalls.mk +++ b/libc/arch-sh/syscalls.mk @@ -5,9 +5,9 @@ syscall_src += arch-sh/syscalls/_exit_thread.S syscall_src += arch-sh/syscalls/__fork.S syscall_src += arch-sh/syscalls/_waitpid.S syscall_src += arch-sh/syscalls/waitid.S -syscall_src += arch-sh/syscalls/__clone.S +syscall_src += arch-sh/syscalls/__sys_clone.S syscall_src += arch-sh/syscalls/execve.S -syscall_src += arch-sh/syscalls/setuid.S +syscall_src += arch-sh/syscalls/__setuid.S syscall_src += arch-sh/syscalls/getuid.S syscall_src += arch-sh/syscalls/getgid.S syscall_src += arch-sh/syscalls/geteuid.S @@ -20,8 +20,8 @@ syscall_src += arch-sh/syscalls/getpgid.S syscall_src += arch-sh/syscalls/getppid.S syscall_src += arch-sh/syscalls/setsid.S syscall_src += arch-sh/syscalls/setgid.S -syscall_src += arch-sh/syscalls/setreuid.S -syscall_src += arch-sh/syscalls/setresuid.S +syscall_src += arch-sh/syscalls/__setreuid.S +syscall_src += arch-sh/syscalls/__setresuid.S syscall_src += arch-sh/syscalls/setresgid.S syscall_src += arch-sh/syscalls/__brk.S syscall_src += arch-sh/syscalls/kill.S @@ -41,6 +41,7 @@ syscall_src += arch-sh/syscalls/chroot.S syscall_src += arch-sh/syscalls/prctl.S syscall_src += arch-sh/syscalls/capget.S syscall_src += arch-sh/syscalls/capset.S +syscall_src += arch-sh/syscalls/sigaltstack.S syscall_src += arch-sh/syscalls/acct.S syscall_src += arch-sh/syscalls/read.S syscall_src += arch-sh/syscalls/write.S @@ -139,6 +140,8 @@ syscall_src += arch-sh/syscalls/sched_getparam.S syscall_src += arch-sh/syscalls/sched_get_priority_max.S syscall_src += arch-sh/syscalls/sched_get_priority_min.S syscall_src += arch-sh/syscalls/sched_rr_get_interval.S +syscall_src += arch-sh/syscalls/ioprio_set.S +syscall_src += arch-sh/syscalls/ioprio_get.S syscall_src += arch-sh/syscalls/uname.S syscall_src += arch-sh/syscalls/__wait4.S syscall_src += arch-sh/syscalls/umask.S diff --git a/libc/arch-sh/syscalls/setresuid.S b/libc/arch-sh/syscalls/__setresuid.S index 41fe349..424100e 100644 --- a/libc/arch-sh/syscalls/setresuid.S +++ b/libc/arch-sh/syscalls/__setresuid.S @@ -2,11 +2,11 @@ #include <sys/linux-syscalls.h> .text - .type setresuid, @function - .globl setresuid + .type __setresuid, @function + .globl __setresuid .align 4 -setresuid: +__setresuid: /* invoke trap */ mov.l 0f, r3 /* trap num */ diff --git a/libc/arch-sh/syscalls/setreuid.S b/libc/arch-sh/syscalls/__setreuid.S index 025df27..6990748 100644 --- a/libc/arch-sh/syscalls/setreuid.S +++ b/libc/arch-sh/syscalls/__setreuid.S @@ -2,11 +2,11 @@ #include <sys/linux-syscalls.h> .text - .type setreuid, @function - .globl setreuid + .type __setreuid, @function + .globl __setreuid .align 4 -setreuid: +__setreuid: /* invoke trap */ mov.l 0f, r3 /* trap num */ diff --git a/libc/arch-sh/syscalls/setuid.S b/libc/arch-sh/syscalls/__setuid.S index 1fb3148..f563de7 100644 --- a/libc/arch-sh/syscalls/setuid.S +++ b/libc/arch-sh/syscalls/__setuid.S @@ -2,11 +2,11 @@ #include <sys/linux-syscalls.h> .text - .type setuid, @function - .globl setuid + .type __setuid, @function + .globl __setuid .align 4 -setuid: +__setuid: /* invoke trap */ mov.l 0f, r3 /* trap num */ diff --git a/libc/arch-sh/syscalls/__clone.S b/libc/arch-sh/syscalls/__sys_clone.S index 1df6ca2..c2e7dd2 100644 --- a/libc/arch-sh/syscalls/__clone.S +++ b/libc/arch-sh/syscalls/__sys_clone.S @@ -2,15 +2,18 @@ #include <sys/linux-syscalls.h> .text - .type __clone, @function - .globl __clone + .type __sys_clone, @function + .globl __sys_clone .align 4 -__clone: +__sys_clone: + + /* get ready for additonal arg */ + mov.l @r15, r0 /* invoke trap */ mov.l 0f, r3 /* trap num */ - trapa #(4 + 0x10) + trapa #(5 + 0x10) /* check return value */ cmp/pz r0 diff --git a/libc/arch-sh/syscalls/ioprio_get.S b/libc/arch-sh/syscalls/ioprio_get.S new file mode 100644 index 0000000..802eb91 --- /dev/null +++ b/libc/arch-sh/syscalls/ioprio_get.S @@ -0,0 +1,32 @@ +/* autogenerated by gensyscalls.py */ +#include <sys/linux-syscalls.h> + + .text + .type ioprio_get, @function + .globl ioprio_get + .align 4 + +ioprio_get: + + /* invoke trap */ + mov.l 0f, r3 /* trap num */ + trapa #(2 + 0x10) + + /* check return value */ + cmp/pz r0 + bt __NR_ioprio_get_end + + /* keep error number */ + sts.l pr, @-r15 + mov.l 1f, r1 + jsr @r1 + mov r0, r4 + lds.l @r15+, pr + +__NR_ioprio_get_end: + rts + nop + + .align 2 +0: .long __NR_ioprio_get +1: .long __set_syscall_errno diff --git a/libc/arch-sh/syscalls/ioprio_set.S b/libc/arch-sh/syscalls/ioprio_set.S new file mode 100644 index 0000000..209d756 --- /dev/null +++ b/libc/arch-sh/syscalls/ioprio_set.S @@ -0,0 +1,32 @@ +/* autogenerated by gensyscalls.py */ +#include <sys/linux-syscalls.h> + + .text + .type ioprio_set, @function + .globl ioprio_set + .align 4 + +ioprio_set: + + /* invoke trap */ + mov.l 0f, r3 /* trap num */ + trapa #(3 + 0x10) + + /* check return value */ + cmp/pz r0 + bt __NR_ioprio_set_end + + /* keep error number */ + sts.l pr, @-r15 + mov.l 1f, r1 + jsr @r1 + mov r0, r4 + lds.l @r15+, pr + +__NR_ioprio_set_end: + rts + nop + + .align 2 +0: .long __NR_ioprio_set +1: .long __set_syscall_errno diff --git a/libc/arch-sh/syscalls/sigaltstack.S b/libc/arch-sh/syscalls/sigaltstack.S new file mode 100644 index 0000000..8b03e27 --- /dev/null +++ b/libc/arch-sh/syscalls/sigaltstack.S @@ -0,0 +1,32 @@ +/* autogenerated by gensyscalls.py */ +#include <sys/linux-syscalls.h> + + .text + .type sigaltstack, @function + .globl sigaltstack + .align 4 + +sigaltstack: + + /* invoke trap */ + mov.l 0f, r3 /* trap num */ + trapa #(2 + 0x10) + + /* check return value */ + cmp/pz r0 + bt __NR_sigaltstack_end + + /* keep error number */ + sts.l pr, @-r15 + mov.l 1f, r1 + jsr @r1 + mov r0, r4 + lds.l @r15+, pr + +__NR_sigaltstack_end: + rts + nop + + .align 2 +0: .long __NR_sigaltstack +1: .long __set_syscall_errno diff --git a/libc/arch-x86/bionic/atomics_x86.S b/libc/arch-x86/bionic/atomics_x86.S index 2370f23..666e182 100644 --- a/libc/arch-x86/bionic/atomics_x86.S +++ b/libc/arch-x86/bionic/atomics_x86.S @@ -41,6 +41,38 @@ __futex_wake: popl %ebx ret +/* int __futex_syscall3(volatile void *ftx, int op, int count) */ +.text +.globl __futex_syscall3 +.type __futex_syscall3, @function +.align 4 +__futex_syscall3: + pushl %ebx + movl 8(%esp), %ebx /* ftx */ + movl 12(%esp), %ecx /* op */ + movl 16(%esp), %edx /* value */ + movl $__NR_futex, %eax + int $0x80 + popl %ebx + ret + +/* int __futex_syscall4(volatile void *ftx, int op, int val, const struct timespec *timeout) */ +.text +.globl __futex_syscall4 +.type __futex_syscall4, @function +.align 4 +__futex_syscall4: + pushl %ebx + pushl %esi + movl 12(%esp), %ebx /* ftx */ + movl 16(%esp), %ecx /* op */ + movl 20(%esp), %edx /* val */ + movl 24(%esp), %esi /* timeout */ + movl $__NR_futex, %eax + int $0x80 + popl %esi + popl %ebx + ret /* int __atomic_cmpxchg(int old, int new, volatile int* addr) */ diff --git a/libc/arch-x86/bionic/atomics_x86.c b/libc/arch-x86/bionic/atomics_x86.c deleted file mode 100644 index b7b20e6..0000000 --- a/libc/arch-x86/bionic/atomics_x86.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -#include <sys/atomics.h> - -#define FUTEX_SYSCALL 240 -#define FUTEX_WAIT 0 -#define FUTEX_WAKE 1 - -int __futex_wait(volatile void *ftx, int val) -{ - int ret; - asm volatile ( - "int $0x80;" - : "=a" (ret) - : "0" (FUTEX_SYSCALL), - "b" (ftx), - "c" (FUTEX_WAIT), - "d" (val), - "S" (0) - ); - return ret; -} - -int __futex_wake(volatile void *ftx, int count) -{ - int ret; - asm volatile ( - "int $0x80;" - : "=a" (ret) - : "0" (FUTEX_SYSCALL), - "b" (ftx), - "c" (FUTEX_WAKE), - "d" (count) - ); - return ret; -} - -int __atomic_cmpxchg(int old, int new, volatile int* addr) { - int xchg; - asm volatile ( - "lock;" - "cmpxchg %%ecx, (%%edx);" - "setne %%al;" - : "=a" (xchg) - : "a" (old), - "c" (new), - "d" (addr) - ); - return xchg; -} - -int __atomic_swap(int new, volatile int* addr) { - int old; - asm volatile ( - "lock;" - "xchg %%ecx, (%%edx);" - : "=c" (old) - : "c" (new), - "d" (addr) - ); - return old; -} - -int __atomic_dec(volatile int* addr) { - int old; - do { - old = *addr; - } while (atomic_cmpxchg(old, old-1, addr)); - return old; -} - -int __atomic_inc(volatile int* addr) { - int old; - do { - old = *addr; - } while (atomic_cmpxchg(old, old+1, addr)); - return old; -} - diff --git a/libc/arch-x86/bionic/clone.S b/libc/arch-x86/bionic/clone.S index 361808d..3b50cc3 100644 --- a/libc/arch-x86/bionic/clone.S +++ b/libc/arch-x86/bionic/clone.S @@ -48,3 +48,8 @@ __pthread_clone: popl %ecx popl %ebx ret + +/* XXX: TODO: Add __bionic_clone here + * See bionic/bionic_clone.c and arch-arm/bionic/clone.S + * for more details... + */
\ No newline at end of file diff --git a/libc/arch-x86/syscalls.mk b/libc/arch-x86/syscalls.mk index 86d2308..ab026fe 100644 --- a/libc/arch-x86/syscalls.mk +++ b/libc/arch-x86/syscalls.mk @@ -5,9 +5,9 @@ syscall_src += arch-x86/syscalls/_exit_thread.S syscall_src += arch-x86/syscalls/__fork.S syscall_src += arch-x86/syscalls/_waitpid.S syscall_src += arch-x86/syscalls/waitid.S -syscall_src += arch-x86/syscalls/__clone.S +syscall_src += arch-x86/syscalls/__sys_clone.S syscall_src += arch-x86/syscalls/execve.S -syscall_src += arch-x86/syscalls/setuid.S +syscall_src += arch-x86/syscalls/__setuid.S syscall_src += arch-x86/syscalls/getuid.S syscall_src += arch-x86/syscalls/getgid.S syscall_src += arch-x86/syscalls/geteuid.S @@ -20,8 +20,8 @@ syscall_src += arch-x86/syscalls/getpgid.S syscall_src += arch-x86/syscalls/getppid.S syscall_src += arch-x86/syscalls/setsid.S syscall_src += arch-x86/syscalls/setgid.S -syscall_src += arch-x86/syscalls/setreuid.S -syscall_src += arch-x86/syscalls/setresuid.S +syscall_src += arch-x86/syscalls/__setreuid.S +syscall_src += arch-x86/syscalls/__setresuid.S syscall_src += arch-x86/syscalls/setresgid.S syscall_src += arch-x86/syscalls/__brk.S syscall_src += arch-x86/syscalls/kill.S @@ -40,6 +40,7 @@ syscall_src += arch-x86/syscalls/chroot.S syscall_src += arch-x86/syscalls/prctl.S syscall_src += arch-x86/syscalls/capget.S syscall_src += arch-x86/syscalls/capset.S +syscall_src += arch-x86/syscalls/sigaltstack.S syscall_src += arch-x86/syscalls/acct.S syscall_src += arch-x86/syscalls/read.S syscall_src += arch-x86/syscalls/write.S @@ -153,6 +154,8 @@ syscall_src += arch-x86/syscalls/sched_getparam.S syscall_src += arch-x86/syscalls/sched_get_priority_max.S syscall_src += arch-x86/syscalls/sched_get_priority_min.S syscall_src += arch-x86/syscalls/sched_rr_get_interval.S +syscall_src += arch-x86/syscalls/ioprio_set.S +syscall_src += arch-x86/syscalls/ioprio_get.S syscall_src += arch-x86/syscalls/uname.S syscall_src += arch-x86/syscalls/__wait4.S syscall_src += arch-x86/syscalls/umask.S diff --git a/libc/arch-x86/syscalls/setresuid.S b/libc/arch-x86/syscalls/__setresuid.S index f81cb39..c492dfb 100644 --- a/libc/arch-x86/syscalls/setresuid.S +++ b/libc/arch-x86/syscalls/__setresuid.S @@ -2,11 +2,11 @@ #include <sys/linux-syscalls.h> .text - .type setresuid, @function - .globl setresuid + .type __setresuid, @function + .globl __setresuid .align 4 -setresuid: +__setresuid: pushl %ebx pushl %ecx pushl %edx diff --git a/libc/arch-x86/syscalls/setreuid.S b/libc/arch-x86/syscalls/__setreuid.S index 99e5d5b..111e999 100644 --- a/libc/arch-x86/syscalls/setreuid.S +++ b/libc/arch-x86/syscalls/__setreuid.S @@ -2,11 +2,11 @@ #include <sys/linux-syscalls.h> .text - .type setreuid, @function - .globl setreuid + .type __setreuid, @function + .globl __setreuid .align 4 -setreuid: +__setreuid: pushl %ebx pushl %ecx mov 12(%esp), %ebx diff --git a/libc/arch-x86/syscalls/setuid.S b/libc/arch-x86/syscalls/__setuid.S index de334c0..1e5f285 100644 --- a/libc/arch-x86/syscalls/setuid.S +++ b/libc/arch-x86/syscalls/__setuid.S @@ -2,11 +2,11 @@ #include <sys/linux-syscalls.h> .text - .type setuid, @function - .globl setuid + .type __setuid, @function + .globl __setuid .align 4 -setuid: +__setuid: pushl %ebx mov 8(%esp), %ebx movl $__NR_setuid32, %eax diff --git a/libc/arch-x86/syscalls/__clone.S b/libc/arch-x86/syscalls/__sys_clone.S index 5862129..172d6af 100644 --- a/libc/arch-x86/syscalls/__clone.S +++ b/libc/arch-x86/syscalls/__sys_clone.S @@ -2,19 +2,21 @@ #include <sys/linux-syscalls.h> .text - .type __clone, @function - .globl __clone + .type __sys_clone, @function + .globl __sys_clone .align 4 -__clone: +__sys_clone: pushl %ebx pushl %ecx pushl %edx pushl %esi - mov 20(%esp), %ebx - mov 24(%esp), %ecx - mov 28(%esp), %edx - mov 32(%esp), %esi + pushl %edi + mov 24(%esp), %ebx + mov 28(%esp), %ecx + mov 32(%esp), %edx + mov 36(%esp), %esi + mov 40(%esp), %edi movl $__NR_clone, %eax int $0x80 cmpl $-129, %eax @@ -25,6 +27,7 @@ __clone: addl $4, %esp orl $-1, %eax 1: + popl %edi popl %esi popl %edx popl %ecx diff --git a/libc/arch-x86/syscalls/ioprio_get.S b/libc/arch-x86/syscalls/ioprio_get.S new file mode 100644 index 0000000..3620271 --- /dev/null +++ b/libc/arch-x86/syscalls/ioprio_get.S @@ -0,0 +1,26 @@ +/* autogenerated by gensyscalls.py */ +#include <sys/linux-syscalls.h> + + .text + .type ioprio_get, @function + .globl ioprio_get + .align 4 + +ioprio_get: + pushl %ebx + pushl %ecx + mov 12(%esp), %ebx + mov 16(%esp), %ecx + movl $__NR_ioprio_get, %eax + int $0x80 + cmpl $-129, %eax + jb 1f + negl %eax + pushl %eax + call __set_errno + addl $4, %esp + orl $-1, %eax +1: + popl %ecx + popl %ebx + ret diff --git a/libc/arch-x86/syscalls/ioprio_set.S b/libc/arch-x86/syscalls/ioprio_set.S new file mode 100644 index 0000000..174d923 --- /dev/null +++ b/libc/arch-x86/syscalls/ioprio_set.S @@ -0,0 +1,29 @@ +/* autogenerated by gensyscalls.py */ +#include <sys/linux-syscalls.h> + + .text + .type ioprio_set, @function + .globl ioprio_set + .align 4 + +ioprio_set: + pushl %ebx + pushl %ecx + pushl %edx + mov 16(%esp), %ebx + mov 20(%esp), %ecx + mov 24(%esp), %edx + movl $__NR_ioprio_set, %eax + int $0x80 + cmpl $-129, %eax + jb 1f + negl %eax + pushl %eax + call __set_errno + addl $4, %esp + orl $-1, %eax +1: + popl %edx + popl %ecx + popl %ebx + ret diff --git a/libc/arch-x86/syscalls/sigaltstack.S b/libc/arch-x86/syscalls/sigaltstack.S new file mode 100644 index 0000000..d39419d --- /dev/null +++ b/libc/arch-x86/syscalls/sigaltstack.S @@ -0,0 +1,26 @@ +/* autogenerated by gensyscalls.py */ +#include <sys/linux-syscalls.h> + + .text + .type sigaltstack, @function + .globl sigaltstack + .align 4 + +sigaltstack: + pushl %ebx + pushl %ecx + mov 12(%esp), %ebx + mov 16(%esp), %ecx + movl $__NR_sigaltstack, %eax + int $0x80 + cmpl $-129, %eax + jb 1f + negl %eax + pushl %eax + call __set_errno + addl $4, %esp + orl $-1, %eax +1: + popl %ecx + popl %ebx + ret diff --git a/libc/bionic/bionic_clone.c b/libc/bionic/bionic_clone.c new file mode 100644 index 0000000..6b2fa58 --- /dev/null +++ b/libc/bionic/bionic_clone.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#define __GNU_SOURCE 1 +#include <sched.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> + +/* WARNING: AT THE MOMENT, THIS IS ONLY SUPPORTED ON ARM + */ + +extern int __bionic_clone(unsigned long clone_flags, + void* newsp, + int *parent_tidptr, + void *new_tls, + int *child_tidptr, + int (*fn)(void *), + void *arg); + +extern void _exit_thread(int retCode); + +/* this function is called from the __bionic_clone + * assembly fragment to call the thread function + * then exit. */ +extern void +__bionic_clone_entry( int (*fn)(void *), void *arg ) +{ + int ret = (*fn)(arg); + _exit_thread(ret); +} + +int +clone(int (*fn)(void *), void *child_stack, int flags, void* arg, ...) +{ + va_list args; + int *parent_tidptr = NULL; + void *new_tls = NULL; + int *child_tidptr = NULL; + int ret; + + /* extract optional parameters - they are cummulative */ + va_start(args, arg); + if (flags & (CLONE_PARENT_SETTID|CLONE_SETTLS|CLONE_CHILD_SETTID)) { + parent_tidptr = va_arg(args, int*); + } + if (flags & (CLONE_SETTLS|CLONE_CHILD_SETTID)) { + new_tls = va_arg(args, void*); + } + if (flags & CLONE_CHILD_SETTID) { + child_tidptr = va_arg(args, int*); + } + va_end(args); + + ret = __bionic_clone(flags, child_stack, parent_tidptr, new_tls, child_tidptr, fn, arg); + return ret; +} diff --git a/libc/bionic/clearenv.c b/libc/bionic/clearenv.c new file mode 100644 index 0000000..ffc58d9 --- /dev/null +++ b/libc/bionic/clearenv.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern char** environ; + +int clearenv(void) +{ + char **P = environ; + int offset; + + for (P = &environ[offset]; *P; ++P) + *P = 0; + return 0; +} diff --git a/libc/bionic/cpuacct.c b/libc/bionic/cpuacct.c new file mode 100644 index 0000000..abdbc51 --- /dev/null +++ b/libc/bionic/cpuacct.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <sys/stat.h> +//#include <sys/types.h> + +int cpuacct_add(uid_t uid) +{ + int count; + FILE *fp; + char buf[80]; + + count = snprintf(buf, sizeof(buf), "/acct/uid/%d/tasks", uid); + fp = fopen(buf, "w+"); + if (!fp) { + /* Note: sizeof("tasks") returns 6, which includes the NULL char */ + buf[count - sizeof("tasks")] = 0; + if (mkdir(buf, 0775) < 0) + return -errno; + + /* Note: sizeof("tasks") returns 6, which includes the NULL char */ + buf[count - sizeof("tasks")] = '/'; + fp = fopen(buf, "w+"); + } + if (!fp) + return -errno; + + fprintf(fp, "0"); + if (fclose(fp)) + return -errno; + + return 0; +} diff --git a/libc/bionic/dlmalloc.c b/libc/bionic/dlmalloc.c index f6f878e..19fbb75 100644 --- a/libc/bionic/dlmalloc.c +++ b/libc/bionic/dlmalloc.c @@ -390,9 +390,9 @@ MALLINFO_FIELD_TYPE default: size_t size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set REALLOC_ZERO_BYTES_FREES default: not defined - This should be set if a call to realloc with zero bytes should - be the same as a call to free. Some people think it should. Otherwise, - since this malloc returns a unique pointer for malloc(0), so does + This should be set if a call to realloc with zero bytes should + be the same as a call to free. Some people think it should. Otherwise, + since this malloc returns a unique pointer for malloc(0), so does realloc(p, 0). LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H @@ -671,7 +671,7 @@ extern "C" { /* ------------------- Declarations of public routines ------------------- */ /* Check an additional macro for the five primary functions */ -#if !defined(USE_DL_PREFIX) || !defined(MALLOC_LEAK_CHECK) +#ifndef USE_DL_PREFIX #define dlcalloc calloc #define dlfree free #define dlmalloc malloc @@ -3627,7 +3627,7 @@ static void* sys_alloc(mstate m, size_t nb) { m->seg.sflags = mmap_flag; m->magic = mparams.magic; init_bins(m); - if (is_global(m)) + if (is_global(m)) init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); else { /* Offset top by embedded malloc_state */ @@ -3778,7 +3778,7 @@ static int sys_trim(mstate m, size_t pad) { } /* Unmap any unused mmapped segments */ - if (HAVE_MMAP) + if (HAVE_MMAP) released += release_unused_segments(m); /* On failure, disable autotrim to avoid repeated failed future calls */ @@ -3986,7 +3986,7 @@ static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { while (a < alignment) a <<= 1; alignment = a; } - + if (bytes >= MAX_REQUEST - alignment) { if (m != 0) { /* Test isn't needed but avoids compiler warning */ MALLOC_FAILURE_ACTION; @@ -5446,5 +5446,5 @@ History: Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) * Based loosely on libg++-1.2X malloc. (It retains some of the overall structure of old version, but most details differ.) - + */ diff --git a/libc/bionic/dlmalloc.h b/libc/bionic/dlmalloc.h index e5f7d4a..1b642d2 100644 --- a/libc/bionic/dlmalloc.h +++ b/libc/bionic/dlmalloc.h @@ -1,14 +1,14 @@ /* Default header file for malloc-2.8.x, written by Doug Lea and released to the public domain, as explained at - http://creativecommons.org/licenses/publicdomain. - + http://creativecommons.org/licenses/publicdomain. + last update: Mon Aug 15 08:55:52 2005 Doug Lea (dl at gee) This header is for ANSI C/C++ only. You can set any of the following #defines before including: - * If USE_DL_PREFIX is defined, it is assumed that malloc.c + * If USE_DL_PREFIX is defined, it is assumed that malloc.c was also compiled with this option, so all routines have names starting with "dl". @@ -34,7 +34,7 @@ extern "C" { #if !ONLY_MSPACES /* Check an additional macro for the five primary functions */ -#if !defined(USE_DL_PREFIX) || !defined(MALLOC_LEAK_CHECK) +#if !defined(USE_DL_PREFIX) #define dlcalloc calloc #define dlfree free #define dlmalloc malloc diff --git a/libc/bionic/err.c b/libc/bionic/err.c new file mode 100644 index 0000000..535b7e1 --- /dev/null +++ b/libc/bionic/err.c @@ -0,0 +1,126 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> + +extern char *__progname; + +__noreturn void +err(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + verr(eval, fmt, ap); + va_end(ap); +} + +__noreturn void +errx(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + verrx(eval, fmt, ap); + va_end(ap); +} + +__noreturn void +verr(int eval, const char *fmt, va_list ap) +{ + int sverrno; + + sverrno = errno; + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) { + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ": "); + } + (void)fprintf(stderr, "%s\n", strerror(sverrno)); + exit(eval); +} + + +__noreturn void +verrx(int eval, const char *fmt, va_list ap) +{ + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); + exit(eval); +} + +void +warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarn(fmt, ap); + va_end(ap); +} + +void +warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); +} + +void +vwarn(const char *fmt, va_list ap) +{ + int sverrno; + + sverrno = errno; + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) { + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ": "); + } + (void)fprintf(stderr, "%s\n", strerror(sverrno)); +} + +void +vwarnx(const char *fmt, va_list ap) +{ + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); +} diff --git a/libc/bionic/fdprintf.c b/libc/bionic/fdprintf.c new file mode 100644 index 0000000..c1d05ad --- /dev/null +++ b/libc/bionic/fdprintf.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> + +int vfdprintf(int fd, const char * __restrict format, __va_list ap) +{ + char *buf=0; + int ret; + ret = vasprintf(&buf, format, ap); + if (ret < 0) + goto end; + + ret = write(fd, buf, ret); + free(buf); +end: + return ret; +} + +int fdprintf(int fd, const char * __restrict format, ...) +{ + __va_list ap; + int ret; + + va_start(ap, format); + ret = vfdprintf(fd, format, ap); + va_end(ap); + + return ret; +} diff --git a/libc/bionic/fork.c b/libc/bionic/fork.c index 1c6a4ba..e20f548 100644 --- a/libc/bionic/fork.c +++ b/libc/bionic/fork.c @@ -43,6 +43,14 @@ int fork(void) ret = __fork(); if (ret != 0) { /* not a child process */ __timer_table_start_stop(0); + } else { + /* + * Newly created process must update cpu accounting. + * Call cpuacct_add passing in our uid, which will take + * the current task id and add it to the uid group passed + * as a parameter. + */ + cpuacct_add(getuid()); } return ret; } diff --git a/libc/bionic/fts.c b/libc/bionic/fts.c new file mode 100644 index 0000000..3dcfb28 --- /dev/null +++ b/libc/bionic/fts.c @@ -0,0 +1,1041 @@ +/* $OpenBSD: fts.c,v 1.43 2009/08/27 16:19:27 millert Exp $ */ + +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define MAX(a,b) ((a)>(b)?(a):(b)) + +static FTSENT *fts_alloc(FTS *, char *, size_t); +static FTSENT *fts_build(FTS *, int); +static void fts_lfree(FTSENT *); +static void fts_load(FTS *, FTSENT *); +static size_t fts_maxarglen(char * const *); +static void fts_padjust(FTS *, FTSENT *); +static int fts_palloc(FTS *, size_t); +static FTSENT *fts_sort(FTS *, FTSENT *, int); +static u_short fts_stat(FTS *, FTSENT *, int); +static int fts_safe_changedir(FTS *, FTSENT *, int, char *); + +#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) + +#define CLR(opt) (sp->fts_options &= ~(opt)) +#define ISSET(opt) (sp->fts_options & (opt)) +#define SET(opt) (sp->fts_options |= (opt)) + +#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) + +/* fts_build flags */ +#define BCHILD 1 /* fts_children */ +#define BNAMES 2 /* fts_children, names only */ +#define BREAD 3 /* fts_read */ + +FTS * +fts_open(char * const *argv, int options, + int (*compar)(const FTSENT **, const FTSENT **)) +{ + FTS *sp; + FTSENT *p, *root; + int nitems; + FTSENT *parent, *tmp; + size_t len; + + /* Options check. */ + if (options & ~FTS_OPTIONMASK) { + errno = EINVAL; + return (NULL); + } + + /* Allocate/initialize the stream */ + if ((sp = calloc(1, sizeof(FTS))) == NULL) + return (NULL); + sp->fts_compar = compar; + sp->fts_options = options; + + /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ + if (ISSET(FTS_LOGICAL)) + SET(FTS_NOCHDIR); + + /* + * Start out with 1K of path space, and enough, in any case, + * to hold the user's paths. + */ + if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) + goto mem1; + + /* Allocate/initialize root's parent. */ + if ((parent = fts_alloc(sp, "", 0)) == NULL) + goto mem2; + parent->fts_level = FTS_ROOTPARENTLEVEL; + + /* Allocate/initialize root(s). */ + for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { + /* Don't allow zero-length paths. */ + if ((len = strlen(*argv)) == 0) { + errno = ENOENT; + goto mem3; + } + + if ((p = fts_alloc(sp, *argv, len)) == NULL) + goto mem3; + p->fts_level = FTS_ROOTLEVEL; + p->fts_parent = parent; + p->fts_accpath = p->fts_name; + p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); + + /* Command-line "." and ".." are real directories. */ + if (p->fts_info == FTS_DOT) + p->fts_info = FTS_D; + + /* + * If comparison routine supplied, traverse in sorted + * order; otherwise traverse in the order specified. + */ + if (compar) { + p->fts_link = root; + root = p; + } else { + p->fts_link = NULL; + if (root == NULL) + tmp = root = p; + else { + tmp->fts_link = p; + tmp = p; + } + } + } + if (compar && nitems > 1) + root = fts_sort(sp, root, nitems); + + /* + * Allocate a dummy pointer and make fts_read think that we've just + * finished the node before the root(s); set p->fts_info to FTS_INIT + * so that everything about the "current" node is ignored. + */ + if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) + goto mem3; + sp->fts_cur->fts_link = root; + sp->fts_cur->fts_info = FTS_INIT; + + /* + * If using chdir(2), grab a file descriptor pointing to dot to ensure + * that we can get back here; this could be avoided for some paths, + * but almost certainly not worth the effort. Slashes, symbolic links, + * and ".." are all fairly nasty problems. Note, if we can't get the + * descriptor we run anyway, just more slowly. + */ + if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0) + SET(FTS_NOCHDIR); + + if (nitems == 0) + free(parent); + + return (sp); + +mem3: fts_lfree(root); + free(parent); +mem2: free(sp->fts_path); +mem1: free(sp); + return (NULL); +} + +static void +fts_load(FTS *sp, FTSENT *p) +{ + size_t len; + char *cp; + + /* + * Load the stream structure for the next traversal. Since we don't + * actually enter the directory until after the preorder visit, set + * the fts_accpath field specially so the chdir gets done to the right + * place and the user can access the first node. From fts_open it's + * known that the path will fit. + */ + len = p->fts_pathlen = p->fts_namelen; + memmove(sp->fts_path, p->fts_name, len + 1); + if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { + len = strlen(++cp); + memmove(p->fts_name, cp, len + 1); + p->fts_namelen = len; + } + p->fts_accpath = p->fts_path = sp->fts_path; + sp->fts_dev = p->fts_dev; +} + +int +fts_close(FTS *sp) +{ + FTSENT *freep, *p; + int rfd, error = 0; + + /* + * This still works if we haven't read anything -- the dummy structure + * points to the root list, so we step through to the end of the root + * list which has a valid parent pointer. + */ + if (sp->fts_cur) { + for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { + freep = p; + p = p->fts_link ? p->fts_link : p->fts_parent; + free(freep); + } + free(p); + } + + /* Stash the original directory fd if needed. */ + rfd = ISSET(FTS_NOCHDIR) ? -1 : sp->fts_rfd; + + /* Free up child linked list, sort array, path buffer, stream ptr.*/ + if (sp->fts_child) + fts_lfree(sp->fts_child); + if (sp->fts_array) + free(sp->fts_array); + free(sp->fts_path); + free(sp); + + /* Return to original directory, checking for error. */ + if (rfd != -1) { + int saved_errno; + error = fchdir(rfd); + saved_errno = errno; + (void)close(rfd); + errno = saved_errno; + } + + return (error); +} + +/* + * Special case of "/" at the end of the path so that slashes aren't + * appended which would cause paths to be written as "....//foo". + */ +#define NAPPEND(p) \ + (p->fts_path[p->fts_pathlen - 1] == '/' \ + ? p->fts_pathlen - 1 : p->fts_pathlen) + +FTSENT * +fts_read(FTS *sp) +{ + FTSENT *p, *tmp; + int instr; + char *t; + int saved_errno; + + /* If finished or unrecoverable error, return NULL. */ + if (sp->fts_cur == NULL || ISSET(FTS_STOP)) + return (NULL); + + /* Set current node pointer. */ + p = sp->fts_cur; + + /* Save and zero out user instructions. */ + instr = p->fts_instr; + p->fts_instr = FTS_NOINSTR; + + /* Any type of file may be re-visited; re-stat and re-turn. */ + if (instr == FTS_AGAIN) { + p->fts_info = fts_stat(sp, p, 0); + return (p); + } + + /* + * Following a symlink -- SLNONE test allows application to see + * SLNONE and recover. If indirecting through a symlink, have + * keep a pointer to current location. If unable to get that + * pointer, follow fails. + */ + if (instr == FTS_FOLLOW && + (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { + p->fts_info = fts_stat(sp, p, 1); + if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { + if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) { + p->fts_errno = errno; + p->fts_info = FTS_ERR; + } else + p->fts_flags |= FTS_SYMFOLLOW; + } + return (p); + } + + /* Directory in pre-order. */ + if (p->fts_info == FTS_D) { + /* If skipped or crossed mount point, do post-order visit. */ + if (instr == FTS_SKIP || + (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) { + if (p->fts_flags & FTS_SYMFOLLOW) + (void)close(p->fts_symfd); + if (sp->fts_child) { + fts_lfree(sp->fts_child); + sp->fts_child = NULL; + } + p->fts_info = FTS_DP; + return (p); + } + + /* Rebuild if only read the names and now traversing. */ + if (sp->fts_child && ISSET(FTS_NAMEONLY)) { + CLR(FTS_NAMEONLY); + fts_lfree(sp->fts_child); + sp->fts_child = NULL; + } + + /* + * Cd to the subdirectory. + * + * If have already read and now fail to chdir, whack the list + * to make the names come out right, and set the parent errno + * so the application will eventually get an error condition. + * Set the FTS_DONTCHDIR flag so that when we logically change + * directories back to the parent we don't do a chdir. + * + * If haven't read do so. If the read fails, fts_build sets + * FTS_STOP or the fts_info field of the node. + */ + if (sp->fts_child) { + if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { + p->fts_errno = errno; + p->fts_flags |= FTS_DONTCHDIR; + for (p = sp->fts_child; p; p = p->fts_link) + p->fts_accpath = + p->fts_parent->fts_accpath; + } + } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { + if (ISSET(FTS_STOP)) + return (NULL); + return (p); + } + p = sp->fts_child; + sp->fts_child = NULL; + goto name; + } + + /* Move to the next node on this level. */ +next: tmp = p; + if ((p = p->fts_link)) { + free(tmp); + + /* + * If reached the top, return to the original directory (or + * the root of the tree), and load the paths for the next root. + */ + if (p->fts_level == FTS_ROOTLEVEL) { + if (FCHDIR(sp, sp->fts_rfd)) { + SET(FTS_STOP); + return (NULL); + } + fts_load(sp, p); + return (sp->fts_cur = p); + } + + /* + * User may have called fts_set on the node. If skipped, + * ignore. If followed, get a file descriptor so we can + * get back if necessary. + */ + if (p->fts_instr == FTS_SKIP) + goto next; + if (p->fts_instr == FTS_FOLLOW) { + p->fts_info = fts_stat(sp, p, 1); + if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { + if ((p->fts_symfd = + open(".", O_RDONLY, 0)) < 0) { + p->fts_errno = errno; + p->fts_info = FTS_ERR; + } else + p->fts_flags |= FTS_SYMFOLLOW; + } + p->fts_instr = FTS_NOINSTR; + } + +name: t = sp->fts_path + NAPPEND(p->fts_parent); + *t++ = '/'; + memmove(t, p->fts_name, p->fts_namelen + 1); + return (sp->fts_cur = p); + } + + /* Move up to the parent node. */ + p = tmp->fts_parent; + free(tmp); + + if (p->fts_level == FTS_ROOTPARENTLEVEL) { + /* + * Done; free everything up and set errno to 0 so the user + * can distinguish between error and EOF. + */ + free(p); + errno = 0; + return (sp->fts_cur = NULL); + } + + /* NUL terminate the pathname. */ + sp->fts_path[p->fts_pathlen] = '\0'; + + /* + * Return to the parent directory. If at a root node or came through + * a symlink, go back through the file descriptor. Otherwise, cd up + * one directory. + */ + if (p->fts_level == FTS_ROOTLEVEL) { + if (FCHDIR(sp, sp->fts_rfd)) { + SET(FTS_STOP); + sp->fts_cur = p; + return (NULL); + } + } else if (p->fts_flags & FTS_SYMFOLLOW) { + if (FCHDIR(sp, p->fts_symfd)) { + saved_errno = errno; + (void)close(p->fts_symfd); + errno = saved_errno; + SET(FTS_STOP); + sp->fts_cur = p; + return (NULL); + } + (void)close(p->fts_symfd); + } else if (!(p->fts_flags & FTS_DONTCHDIR) && + fts_safe_changedir(sp, p->fts_parent, -1, "..")) { + SET(FTS_STOP); + sp->fts_cur = p; + return (NULL); + } + p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; + return (sp->fts_cur = p); +} + +/* + * Fts_set takes the stream as an argument although it's not used in this + * implementation; it would be necessary if anyone wanted to add global + * semantics to fts using fts_set. An error return is allowed for similar + * reasons. + */ +/* ARGSUSED */ +int +fts_set(FTS *sp, FTSENT *p, int instr) +{ + if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && + instr != FTS_NOINSTR && instr != FTS_SKIP) { + errno = EINVAL; + return (1); + } + p->fts_instr = instr; + return (0); +} + +FTSENT * +fts_children(FTS *sp, int instr) +{ + FTSENT *p; + int fd; + + if (instr && instr != FTS_NAMEONLY) { + errno = EINVAL; + return (NULL); + } + + /* Set current node pointer. */ + p = sp->fts_cur; + + /* + * Errno set to 0 so user can distinguish empty directory from + * an error. + */ + errno = 0; + + /* Fatal errors stop here. */ + if (ISSET(FTS_STOP)) + return (NULL); + + /* Return logical hierarchy of user's arguments. */ + if (p->fts_info == FTS_INIT) + return (p->fts_link); + + /* + * If not a directory being visited in pre-order, stop here. Could + * allow FTS_DNR, assuming the user has fixed the problem, but the + * same effect is available with FTS_AGAIN. + */ + if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) + return (NULL); + + /* Free up any previous child list. */ + if (sp->fts_child) + fts_lfree(sp->fts_child); + + if (instr == FTS_NAMEONLY) { + SET(FTS_NAMEONLY); + instr = BNAMES; + } else + instr = BCHILD; + + /* + * If using chdir on a relative path and called BEFORE fts_read does + * its chdir to the root of a traversal, we can lose -- we need to + * chdir into the subdirectory, and we don't know where the current + * directory is, so we can't get back so that the upcoming chdir by + * fts_read will work. + */ + if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || + ISSET(FTS_NOCHDIR)) + return (sp->fts_child = fts_build(sp, instr)); + + if ((fd = open(".", O_RDONLY, 0)) < 0) + return (NULL); + sp->fts_child = fts_build(sp, instr); + if (fchdir(fd)) { + (void)close(fd); + return (NULL); + } + (void)close(fd); + return (sp->fts_child); +} + +/* + * This is the tricky part -- do not casually change *anything* in here. The + * idea is to build the linked list of entries that are used by fts_children + * and fts_read. There are lots of special cases. + * + * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is + * set and it's a physical walk (so that symbolic links can't be directories), + * we can do things quickly. First, if it's a 4.4BSD file system, the type + * of the file is in the directory entry. Otherwise, we assume that the number + * of subdirectories in a node is equal to the number of links to the parent. + * The former skips all stat calls. The latter skips stat calls in any leaf + * directories and for any files after the subdirectories in the directory have + * been found, cutting the stat calls by about 2/3. + */ +static FTSENT * +fts_build(FTS *sp, int type) +{ + struct dirent *dp; + FTSENT *p, *head; + FTSENT *cur, *tail; + DIR *dirp; + void *oldaddr; + size_t len, maxlen; + int nitems, cderrno, descend, level, nlinks, nostat, doadjust; + int saved_errno; + char *cp; + + /* Set current node pointer. */ + cur = sp->fts_cur; + + /* + * Open the directory for reading. If this fails, we're done. + * If being called from fts_read, set the fts_info field. + */ + if ((dirp = opendir(cur->fts_accpath)) == NULL) { + if (type == BREAD) { + cur->fts_info = FTS_DNR; + cur->fts_errno = errno; + } + return (NULL); + } + + /* + * Nlinks is the number of possible entries of type directory in the + * directory if we're cheating on stat calls, 0 if we're not doing + * any stat calls at all, -1 if we're doing stats on everything. + */ + if (type == BNAMES) + nlinks = 0; + else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) { + nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); + nostat = 1; + } else { + nlinks = -1; + nostat = 0; + } + +#ifdef notdef + (void)printf("nlinks == %d (cur: %u)\n", nlinks, cur->fts_nlink); + (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", + ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); +#endif + /* + * If we're going to need to stat anything or we want to descend + * and stay in the directory, chdir. If this fails we keep going, + * but set a flag so we don't chdir after the post-order visit. + * We won't be able to stat anything, but we can still return the + * names themselves. Note, that since fts_read won't be able to + * chdir into the directory, it will have to return different path + * names than before, i.e. "a/b" instead of "b". Since the node + * has already been visited in pre-order, have to wait until the + * post-order visit to return the error. There is a special case + * here, if there was nothing to stat then it's not an error to + * not be able to stat. This is all fairly nasty. If a program + * needed sorted entries or stat information, they had better be + * checking FTS_NS on the returned nodes. + */ + cderrno = 0; + if (nlinks || type == BREAD) { + if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) { + if (nlinks && type == BREAD) + cur->fts_errno = errno; + cur->fts_flags |= FTS_DONTCHDIR; + descend = 0; + cderrno = errno; + (void)closedir(dirp); + dirp = NULL; + } else + descend = 1; + } else + descend = 0; + + /* + * Figure out the max file name length that can be stored in the + * current path -- the inner loop allocates more path as necessary. + * We really wouldn't have to do the maxlen calculations here, we + * could do them in fts_read before returning the path, but it's a + * lot easier here since the length is part of the dirent structure. + * + * If not changing directories set a pointer so that can just append + * each new name into the path. + */ + len = NAPPEND(cur); + if (ISSET(FTS_NOCHDIR)) { + cp = sp->fts_path + len; + *cp++ = '/'; + } + len++; + maxlen = sp->fts_pathlen - len; + + /* + * fts_level is a short so we must prevent it from wrapping + * around to FTS_ROOTLEVEL and FTS_ROOTPARENTLEVEL. + */ + level = cur->fts_level; + if (level < FTS_MAXLEVEL) + level++; + + /* Read the directory, attaching each entry to the `link' pointer. */ + doadjust = 0; + for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) { + if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) + continue; + + if (!(p = fts_alloc(sp, dp->d_name, strlen(dp->d_name)))) + goto mem1; + if (strlen(dp->d_name) >= maxlen) { /* include space for NUL */ + oldaddr = sp->fts_path; + if (fts_palloc(sp, strlen(dp->d_name) +len + 1)) { + /* + * No more memory for path or structures. Save + * errno, free up the current structure and the + * structures already allocated. + */ +mem1: saved_errno = errno; + if (p) + free(p); + fts_lfree(head); + (void)closedir(dirp); + cur->fts_info = FTS_ERR; + SET(FTS_STOP); + errno = saved_errno; + return (NULL); + } + /* Did realloc() change the pointer? */ + if (oldaddr != sp->fts_path) { + doadjust = 1; + if (ISSET(FTS_NOCHDIR)) + cp = sp->fts_path + len; + } + maxlen = sp->fts_pathlen - len; + } + + p->fts_level = level; + p->fts_parent = sp->fts_cur; + p->fts_pathlen = len + strlen(dp->d_name); + if (p->fts_pathlen < len) { + /* + * If we wrap, free up the current structure and + * the structures already allocated, then error + * out with ENAMETOOLONG. + */ + free(p); + fts_lfree(head); + (void)closedir(dirp); + cur->fts_info = FTS_ERR; + SET(FTS_STOP); + errno = ENAMETOOLONG; + return (NULL); + } + + if (cderrno) { + if (nlinks) { + p->fts_info = FTS_NS; + p->fts_errno = cderrno; + } else + p->fts_info = FTS_NSOK; + p->fts_accpath = cur->fts_accpath; + } else if (nlinks == 0 +#ifdef DT_DIR + || (nostat && + dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) +#endif + ) { + p->fts_accpath = + ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; + p->fts_info = FTS_NSOK; + } else { + /* Build a file name for fts_stat to stat. */ + if (ISSET(FTS_NOCHDIR)) { + p->fts_accpath = p->fts_path; + memmove(cp, p->fts_name, p->fts_namelen + 1); + } else + p->fts_accpath = p->fts_name; + /* Stat it. */ + p->fts_info = fts_stat(sp, p, 0); + + /* Decrement link count if applicable. */ + if (nlinks > 0 && (p->fts_info == FTS_D || + p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) + --nlinks; + } + + /* We walk in directory order so "ls -f" doesn't get upset. */ + p->fts_link = NULL; + if (head == NULL) + head = tail = p; + else { + tail->fts_link = p; + tail = p; + } + ++nitems; + } + if (dirp) + (void)closedir(dirp); + + /* + * If realloc() changed the address of the path, adjust the + * addresses for the rest of the tree and the dir list. + */ + if (doadjust) + fts_padjust(sp, head); + + /* + * If not changing directories, reset the path back to original + * state. + */ + if (ISSET(FTS_NOCHDIR)) { + if (len == sp->fts_pathlen || nitems == 0) + --cp; + *cp = '\0'; + } + + /* + * If descended after called from fts_children or after called from + * fts_read and nothing found, get back. At the root level we use + * the saved fd; if one of fts_open()'s arguments is a relative path + * to an empty directory, we wind up here with no other way back. If + * can't get back, we're done. + */ + if (descend && (type == BCHILD || !nitems) && + (cur->fts_level == FTS_ROOTLEVEL ? FCHDIR(sp, sp->fts_rfd) : + fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { + cur->fts_info = FTS_ERR; + SET(FTS_STOP); + return (NULL); + } + + /* If didn't find anything, return NULL. */ + if (!nitems) { + if (type == BREAD) + cur->fts_info = FTS_DP; + return (NULL); + } + + /* Sort the entries. */ + if (sp->fts_compar && nitems > 1) + head = fts_sort(sp, head, nitems); + return (head); +} + +static u_short +fts_stat(FTS *sp, FTSENT *p, int follow) +{ + FTSENT *t; + dev_t dev; + ino_t ino; + struct stat *sbp, sb; + int saved_errno; + + /* If user needs stat info, stat buffer already allocated. */ + sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; + + /* + * If doing a logical walk, or application requested FTS_FOLLOW, do + * a stat(2). If that fails, check for a non-existent symlink. If + * fail, set the errno from the stat call. + */ + if (ISSET(FTS_LOGICAL) || follow) { + if (stat(p->fts_accpath, sbp)) { + saved_errno = errno; + if (!lstat(p->fts_accpath, sbp)) { + errno = 0; + return (FTS_SLNONE); + } + p->fts_errno = saved_errno; + goto err; + } + } else if (lstat(p->fts_accpath, sbp)) { + p->fts_errno = errno; +err: memset(sbp, 0, sizeof(struct stat)); + return (FTS_NS); + } + + if (S_ISDIR(sbp->st_mode)) { + /* + * Set the device/inode. Used to find cycles and check for + * crossing mount points. Also remember the link count, used + * in fts_build to limit the number of stat calls. It is + * understood that these fields are only referenced if fts_info + * is set to FTS_D. + */ + dev = p->fts_dev = sbp->st_dev; + ino = p->fts_ino = sbp->st_ino; + p->fts_nlink = sbp->st_nlink; + + if (ISDOT(p->fts_name)) + return (FTS_DOT); + + /* + * Cycle detection is done by brute force when the directory + * is first encountered. If the tree gets deep enough or the + * number of symbolic links to directories is high enough, + * something faster might be worthwhile. + */ + for (t = p->fts_parent; + t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) + if (ino == t->fts_ino && dev == t->fts_dev) { + p->fts_cycle = t; + return (FTS_DC); + } + return (FTS_D); + } + if (S_ISLNK(sbp->st_mode)) + return (FTS_SL); + if (S_ISREG(sbp->st_mode)) + return (FTS_F); + return (FTS_DEFAULT); +} + +static FTSENT * +fts_sort(FTS *sp, FTSENT *head, int nitems) +{ + FTSENT **ap, *p; + + /* + * Construct an array of pointers to the structures and call qsort(3). + * Reassemble the array in the order returned by qsort. If unable to + * sort for memory reasons, return the directory entries in their + * current order. Allocate enough space for the current needs plus + * 40 so don't realloc one entry at a time. + */ + if (nitems > sp->fts_nitems) { + struct _ftsent **a; + + sp->fts_nitems = nitems + 40; + if ((a = realloc(sp->fts_array, + sp->fts_nitems * sizeof(FTSENT *))) == NULL) { + if (sp->fts_array) + free(sp->fts_array); + sp->fts_array = NULL; + sp->fts_nitems = 0; + return (head); + } + sp->fts_array = a; + } + for (ap = sp->fts_array, p = head; p; p = p->fts_link) + *ap++ = p; + qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); + for (head = *(ap = sp->fts_array); --nitems; ++ap) + ap[0]->fts_link = ap[1]; + ap[0]->fts_link = NULL; + return (head); +} + +static FTSENT * +fts_alloc(FTS *sp, char *name, size_t namelen) +{ + FTSENT *p; + size_t len; + + /* + * The file name is a variable length array and no stat structure is + * necessary if the user has set the nostat bit. Allocate the FTSENT + * structure, the file name and the stat structure in one chunk, but + * be careful that the stat structure is reasonably aligned. Since the + * fts_name field is declared to be of size 1, the fts_name pointer is + * namelen + 2 before the first possible address of the stat structure. + */ + len = sizeof(FTSENT) + namelen; + if (!ISSET(FTS_NOSTAT)) + len += sizeof(struct stat) + ALIGNBYTES; + if ((p = malloc(len)) == NULL) + return (NULL); + + memset(p, 0, len); + p->fts_path = sp->fts_path; + p->fts_namelen = namelen; + p->fts_instr = FTS_NOINSTR; + if (!ISSET(FTS_NOSTAT)) + p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); + memcpy(p->fts_name, name, namelen); + + return (p); +} + +static void +fts_lfree(FTSENT *head) +{ + FTSENT *p; + + /* Free a linked list of structures. */ + while ((p = head)) { + head = head->fts_link; + free(p); + } +} + +/* + * Allow essentially unlimited paths; find, rm, ls should all work on any tree. + * Most systems will allow creation of paths much longer than MAXPATHLEN, even + * though the kernel won't resolve them. Add the size (not just what's needed) + * plus 256 bytes so don't realloc the path 2 bytes at a time. + */ +static int +fts_palloc(FTS *sp, size_t more) +{ + char *p; + + /* + * Check for possible wraparound. + */ + more += 256; + if (sp->fts_pathlen + more < sp->fts_pathlen) { + if (sp->fts_path) + free(sp->fts_path); + sp->fts_path = NULL; + errno = ENAMETOOLONG; + return (1); + } + sp->fts_pathlen += more; + p = realloc(sp->fts_path, sp->fts_pathlen); + if (p == NULL) { + if (sp->fts_path) + free(sp->fts_path); + sp->fts_path = NULL; + return (1); + } + sp->fts_path = p; + return (0); +} + +/* + * When the path is realloc'd, have to fix all of the pointers in structures + * already returned. + */ +static void +fts_padjust(FTS *sp, FTSENT *head) +{ + FTSENT *p; + char *addr = sp->fts_path; + +#define ADJUST(p) { \ + if ((p)->fts_accpath != (p)->fts_name) { \ + (p)->fts_accpath = \ + (char *)addr + ((p)->fts_accpath - (p)->fts_path); \ + } \ + (p)->fts_path = addr; \ +} + /* Adjust the current set of children. */ + for (p = sp->fts_child; p; p = p->fts_link) + ADJUST(p); + + /* Adjust the rest of the tree, including the current level. */ + for (p = head; p->fts_level >= FTS_ROOTLEVEL;) { + ADJUST(p); + p = p->fts_link ? p->fts_link : p->fts_parent; + } +} + +static size_t +fts_maxarglen(char * const *argv) +{ + size_t len, max; + + for (max = 0; *argv; ++argv) + if ((len = strlen(*argv)) > max) + max = len; + return (max + 1); +} + +/* + * Change to dir specified by fd or p->fts_accpath without getting + * tricked by someone changing the world out from underneath us. + * Assumes p->fts_dev and p->fts_ino are filled in. + */ +static int +fts_safe_changedir(FTS *sp, FTSENT *p, int fd, char *path) +{ + int ret, oerrno, newfd; + struct stat sb; + + newfd = fd; + if (ISSET(FTS_NOCHDIR)) + return (0); + if (fd < 0 && (newfd = open(path, O_RDONLY, 0)) < 0) + return (-1); + if (fstat(newfd, &sb)) { + ret = -1; + goto bail; + } + if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) { + errno = ENOENT; /* disinformation */ + ret = -1; + goto bail; + } + ret = fchdir(newfd); +bail: + oerrno = errno; + if (fd < 0) + (void)close(newfd); + errno = oerrno; + return (ret); +} diff --git a/libc/bionic/libc_init_dynamic.c b/libc/bionic/libc_init_dynamic.c index b479b27..682ebcf 100644 --- a/libc/bionic/libc_init_dynamic.c +++ b/libc/bionic/libc_init_dynamic.c @@ -52,8 +52,6 @@ #include "libc_init_common.h" #include <bionic_tls.h> -extern void malloc_debug_init(); - /* We flag the __libc_preinit function as a constructor to ensure * that its address is listed in libc.so's .init_array section. * This ensures that the function is called by the dynamic linker @@ -78,12 +76,11 @@ void __libc_prenit(void) __libc_init_common(elfdata); -#ifdef MALLOC_LEAK_CHECK - /* setup malloc leak checker, requires system properties */ + /* Setup malloc routines accordingly to the environment. + * Requires system properties + */ extern void malloc_debug_init(void); malloc_debug_init(); -#endif - } __noreturn void __libc_init(uintptr_t *elfdata, diff --git a/libc/bionic/libc_init_static.c b/libc/bionic/libc_init_static.c index e6264bb..d097b6b 100644 --- a/libc/bionic/libc_init_static.c +++ b/libc/bionic/libc_init_static.c @@ -68,12 +68,6 @@ __noreturn void __libc_init(uintptr_t *elfdata, /* Initialize the C runtime environment */ __libc_init_common(elfdata); -#ifdef MALLOC_LEAK_CHECK - /* setup malloc leak checker, requires system properties */ - extern void malloc_debug_init(void); - malloc_debug_init(); -#endif - /* Several Linux ABIs don't pass the onexit pointer, and the ones that * do never use it. Therefore, we ignore it. */ diff --git a/libc/bionic/logd_write.c b/libc/bionic/logd_write.c index 39f0258..618160f 100644 --- a/libc/bionic/logd_write.c +++ b/libc/bionic/logd_write.c @@ -66,7 +66,7 @@ static int __write_to_log_null(log_id_t log_id, struct iovec *vec); static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; -log_channel_t log_channels[LOG_ID_MAX] = { +static log_channel_t log_channels[LOG_ID_MAX] = { { __write_to_log_null, -1, NULL }, { __write_to_log_init, -1, "/dev/"LOGGER_LOG_MAIN }, { __write_to_log_init, -1, "/dev/"LOGGER_LOG_RADIO } @@ -112,6 +112,7 @@ static int __write_to_log_init(log_id_t log_id, struct iovec *vec) log_channels[log_id].logger = (fd < 0) ? __write_to_log_null : __write_to_log_kernel; + log_channels[log_id].fd = fd; log_channels[log_id].fd = fd; diff --git a/libc/bionic/malloc_debug_common.c b/libc/bionic/malloc_debug_common.c new file mode 100644 index 0000000..ec56826 --- /dev/null +++ b/libc/bionic/malloc_debug_common.c @@ -0,0 +1,488 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Contains definition of structures, global variables, and implementation of + * routines that are used by malloc leak detection code and other components in + * the system. The trick is that some components expect these data and + * routines to be defined / implemented in libc.so library, regardless + * whether or not MALLOC_LEAK_CHECK macro is defined. To make things even + * more tricky, malloc leak detection code, implemented in + * libc_malloc_debug.so also requires access to these variables and routines + * (to fill allocation entry hash table, for example). So, all relevant + * variables and routines are defined / implemented here and exported + * to all, leak detection code and other components via dynamic (libc.so), + * or static (libc.a) linking. + */ + +#include <stdlib.h> +#include <pthread.h> +#include <unistd.h> +#include "dlmalloc.h" +#include "malloc_debug_common.h" + +/* + * In a VM process, this is set to 1 after fork()ing out of zygote. + */ +int gMallocLeakZygoteChild = 0; + +pthread_mutex_t gAllocationsMutex = PTHREAD_MUTEX_INITIALIZER; +HashTable gHashTable; + +// ============================================================================= +// output functions +// ============================================================================= + +static int hash_entry_compare(const void* arg1, const void* arg2) +{ + HashEntry* e1 = *(HashEntry**)arg1; + HashEntry* e2 = *(HashEntry**)arg2; + + size_t nbAlloc1 = e1->allocations; + size_t nbAlloc2 = e2->allocations; + size_t size1 = e1->size & ~SIZE_FLAG_MASK; + size_t size2 = e2->size & ~SIZE_FLAG_MASK; + size_t alloc1 = nbAlloc1 * size1; + size_t alloc2 = nbAlloc2 * size2; + + // sort in descending order by: + // 1) total size + // 2) number of allocations + // + // This is used for sorting, not determination of equality, so we don't + // need to compare the bit flags. + int result; + if (alloc1 > alloc2) { + result = -1; + } else if (alloc1 < alloc2) { + result = 1; + } else { + if (nbAlloc1 > nbAlloc2) { + result = -1; + } else if (nbAlloc1 < nbAlloc2) { + result = 1; + } else { + result = 0; + } + } + return result; +} + +/* + * Retrieve native heap information. + * + * "*info" is set to a buffer we allocate + * "*overallSize" is set to the size of the "info" buffer + * "*infoSize" is set to the size of a single entry + * "*totalMemory" is set to the sum of all allocations we're tracking; does + * not include heap overhead + * "*backtraceSize" is set to the maximum number of entries in the back trace + */ +void get_malloc_leak_info(uint8_t** info, size_t* overallSize, + size_t* infoSize, size_t* totalMemory, size_t* backtraceSize) +{ + // don't do anything if we have invalid arguments + if (info == NULL || overallSize == NULL || infoSize == NULL || + totalMemory == NULL || backtraceSize == NULL) { + return; + } + + pthread_mutex_lock(&gAllocationsMutex); + + if (gHashTable.count == 0) { + *info = NULL; + *overallSize = 0; + *infoSize = 0; + *totalMemory = 0; + *backtraceSize = 0; + goto done; + } + + void** list = (void**)dlmalloc(sizeof(void*) * gHashTable.count); + + // get the entries into an array to be sorted + int index = 0; + int i; + for (i = 0 ; i < HASHTABLE_SIZE ; i++) { + HashEntry* entry = gHashTable.slots[i]; + while (entry != NULL) { + list[index] = entry; + *totalMemory = *totalMemory + + ((entry->size & ~SIZE_FLAG_MASK) * entry->allocations); + index++; + entry = entry->next; + } + } + + // XXX: the protocol doesn't allow variable size for the stack trace (yet) + *infoSize = (sizeof(size_t) * 2) + (sizeof(intptr_t) * BACKTRACE_SIZE); + *overallSize = *infoSize * gHashTable.count; + *backtraceSize = BACKTRACE_SIZE; + + // now get A byte array big enough for this + *info = (uint8_t*)dlmalloc(*overallSize); + + if (*info == NULL) { + *overallSize = 0; + goto out_nomem_info; + } + + qsort((void*)list, gHashTable.count, sizeof(void*), hash_entry_compare); + + uint8_t* head = *info; + const int count = gHashTable.count; + for (i = 0 ; i < count ; i++) { + HashEntry* entry = list[i]; + size_t entrySize = (sizeof(size_t) * 2) + (sizeof(intptr_t) * entry->numEntries); + if (entrySize < *infoSize) { + /* we're writing less than a full entry, clear out the rest */ + memset(head + entrySize, 0, *infoSize - entrySize); + } else { + /* make sure the amount we're copying doesn't exceed the limit */ + entrySize = *infoSize; + } + memcpy(head, &(entry->size), entrySize); + head += *infoSize; + } + +out_nomem_info: + dlfree(list); + +done: + pthread_mutex_unlock(&gAllocationsMutex); +} + +void free_malloc_leak_info(uint8_t* info) +{ + dlfree(info); +} + +struct mallinfo mallinfo() +{ + return dlmallinfo(); +} + +void* valloc(size_t bytes) { + /* assume page size of 4096 bytes */ + return memalign( getpagesize(), bytes ); +} + +/* Support for malloc debugging. + * Note that if USE_DL_PREFIX is not defined, it's assumed that memory + * allocation routines are implemented somewhere else, so all our custom + * malloc routines should not be compiled at all. + */ +#ifdef USE_DL_PREFIX + +/* Table for dispatching malloc calls, initialized with default dispatchers. */ +const MallocDebug __libc_malloc_default_dispatch __attribute__((aligned(32))) = +{ + dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign +}; + +/* Selector of dispatch table to use for dispatching malloc calls. */ +const MallocDebug* __libc_malloc_dispatch = &__libc_malloc_default_dispatch; + +void* malloc(size_t bytes) { + return __libc_malloc_dispatch->malloc(bytes); +} +void free(void* mem) { + __libc_malloc_dispatch->free(mem); +} +void* calloc(size_t n_elements, size_t elem_size) { + return __libc_malloc_dispatch->calloc(n_elements, elem_size); +} +void* realloc(void* oldMem, size_t bytes) { + return __libc_malloc_dispatch->realloc(oldMem, bytes); +} +void* memalign(size_t alignment, size_t bytes) { + return __libc_malloc_dispatch->memalign(alignment, bytes); +} + +/* We implement malloc debugging only in libc.so, so code bellow + * must be excluded if we compile this file for static libc.a + */ +#ifndef LIBC_STATIC +#include <sys/system_properties.h> +#include <dlfcn.h> +#include "logd.h" + +// ============================================================================= +// log functions +// ============================================================================= + +#define debug_log(format, ...) \ + __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", (format), ##__VA_ARGS__ ) +#define error_log(format, ...) \ + __libc_android_log_print(ANDROID_LOG_ERROR, "libc", (format), ##__VA_ARGS__ ) +#define info_log(format, ...) \ + __libc_android_log_print(ANDROID_LOG_INFO, "libc", (format), ##__VA_ARGS__ ) + +/* Table for dispatching malloc calls, depending on environment. */ +static MallocDebug gMallocUse __attribute__((aligned(32))) = { + dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign +}; + +extern char* __progname; + +/* Handle to shared library where actual memory allocation is implemented. + * This library is loaded and memory allocation calls are redirected there + * when libc.debug.malloc environment variable contains value other than + * zero: + * 1 - For memory leak detections. + * 5 - For filling allocated / freed memory with patterns defined by + * CHK_SENTINEL_VALUE, and CHK_FILL_FREE macros. + * 10 - For adding pre-, and post- allocation stubs in order to detect + * buffer overruns. + * Note that emulator's memory allocation instrumentation is not controlled by + * libc.debug.malloc value, but rather by emulator, started with -memcheck + * option. Note also, that if emulator has started with -memcheck option, + * emulator's instrumented memory allocation will take over value saved in + * libc.debug.malloc. In other words, if emulator has started with -memcheck + * option, libc.debug.malloc value is ignored. + * Actual functionality for debug levels 1-10 is implemented in + * libc_malloc_debug_leak.so, while functionality for emultor's instrumented + * allocations is implemented in libc_malloc_debug_qemu.so and can be run inside + * the emulator only. + */ +static void* libc_malloc_impl_handle = NULL; + +/* Make sure we have MALLOC_ALIGNMENT that matches the one that is + * used in dlmalloc. Emulator's memchecker needs this value to properly + * align its guarding zones. + */ +#ifndef MALLOC_ALIGNMENT +#define MALLOC_ALIGNMENT ((size_t)8U) +#endif /* MALLOC_ALIGNMENT */ + +/* Initializes memory allocation framework once per process. */ +static void malloc_init_impl(void) +{ + const char* so_name = NULL; + MallocDebugInit malloc_debug_initialize = NULL; + unsigned int qemu_running = 0; + unsigned int debug_level = 0; + unsigned int memcheck_enabled = 0; + char env[PROP_VALUE_MAX]; + char memcheck_tracing[PROP_VALUE_MAX]; + + /* Get custom malloc debug level. Note that emulator started with + * memory checking option will have priority over debug level set in + * libc.debug.malloc system property. */ + if (__system_property_get("ro.kernel.qemu", env) && atoi(env)) { + qemu_running = 1; + if (__system_property_get("ro.kernel.memcheck", memcheck_tracing)) { + if (memcheck_tracing[0] != '0') { + // Emulator has started with memory tracing enabled. Enforce it. + debug_level = 20; + memcheck_enabled = 1; + } + } + } + + /* If debug level has not been set by memcheck option in the emulator, + * lets grab it from libc.debug.malloc system property. */ + if (!debug_level && __system_property_get("libc.debug.malloc", env)) { + debug_level = atoi(env); + } + + /* Debug level 0 means that we should use dlxxx allocation + * routines (default). */ + if (!debug_level) { + return; + } + + // Lets see which .so must be loaded for the requested debug level + switch (debug_level) { + case 1: + case 5: + case 10: + so_name = "/system/lib/libc_malloc_debug_leak.so"; + break; + case 20: + // Quick check: debug level 20 can only be handled in emulator. + if (!qemu_running) { + error_log("%s: Debug level %d can only be set in emulator\n", + __progname, debug_level); + return; + } + // Make sure that memory checking has been enabled in emulator. + if (!memcheck_enabled) { + error_log("%s: Memory checking is not enabled in the emulator\n", + __progname); + return; + } + so_name = "/system/lib/libc_malloc_debug_qemu.so"; + break; + default: + error_log("%s: Debug level %d is unknown\n", + __progname, debug_level); + return; + } + + // Load .so that implements the required malloc debugging functionality. + libc_malloc_impl_handle = dlopen(so_name, RTLD_LAZY); + if (libc_malloc_impl_handle == NULL) { + error_log("%s: Missing module %s required for malloc debug level %d\n", + __progname, so_name, debug_level); + return; + } + + // Initialize malloc debugging in the loaded module. + malloc_debug_initialize = + dlsym(libc_malloc_impl_handle, "malloc_debug_initialize"); + if (malloc_debug_initialize == NULL) { + error_log("%s: Initialization routine is not found in %s\n", + __progname, so_name); + dlclose(libc_malloc_impl_handle); + return; + } + if (malloc_debug_initialize()) { + dlclose(libc_malloc_impl_handle); + return; + } + + if (debug_level == 20) { + // For memory checker we need to do extra initialization. + int (*memcheck_initialize)(int, const char*) = + dlsym(libc_malloc_impl_handle, "memcheck_initialize"); + if (memcheck_initialize == NULL) { + error_log("%s: memcheck_initialize routine is not found in %s\n", + __progname, so_name); + dlclose(libc_malloc_impl_handle); + return; + } + if (memcheck_initialize(MALLOC_ALIGNMENT, memcheck_tracing)) { + dlclose(libc_malloc_impl_handle); + return; + } + } + + // Initialize malloc dispatch table with appropriate routines. + switch (debug_level) { + case 1: + __libc_android_log_print(ANDROID_LOG_INFO, "libc", + "%s using MALLOC_DEBUG = %d (leak checker)\n", + __progname, debug_level); + gMallocUse.malloc = + dlsym(libc_malloc_impl_handle, "leak_malloc"); + gMallocUse.free = + dlsym(libc_malloc_impl_handle, "leak_free"); + gMallocUse.calloc = + dlsym(libc_malloc_impl_handle, "leak_calloc"); + gMallocUse.realloc = + dlsym(libc_malloc_impl_handle, "leak_realloc"); + gMallocUse.memalign = + dlsym(libc_malloc_impl_handle, "leak_memalign"); + break; + case 5: + __libc_android_log_print(ANDROID_LOG_INFO, "libc", + "%s using MALLOC_DEBUG = %d (fill)\n", + __progname, debug_level); + gMallocUse.malloc = + dlsym(libc_malloc_impl_handle, "fill_malloc"); + gMallocUse.free = + dlsym(libc_malloc_impl_handle, "fill_free"); + gMallocUse.calloc = dlcalloc; + gMallocUse.realloc = + dlsym(libc_malloc_impl_handle, "fill_realloc"); + gMallocUse.memalign = + dlsym(libc_malloc_impl_handle, "fill_memalign"); + break; + case 10: + __libc_android_log_print(ANDROID_LOG_INFO, "libc", + "%s using MALLOC_DEBUG = %d (sentinels, fill)\n", + __progname, debug_level); + gMallocUse.malloc = + dlsym(libc_malloc_impl_handle, "chk_malloc"); + gMallocUse.free = + dlsym(libc_malloc_impl_handle, "chk_free"); + gMallocUse.calloc = + dlsym(libc_malloc_impl_handle, "chk_calloc"); + gMallocUse.realloc = + dlsym(libc_malloc_impl_handle, "chk_realloc"); + gMallocUse.memalign = + dlsym(libc_malloc_impl_handle, "chk_memalign"); + break; + case 20: + __libc_android_log_print(ANDROID_LOG_INFO, "libc", + "%s[%u] using MALLOC_DEBUG = %d (instrumented for emulator)\n", + __progname, getpid(), debug_level); + gMallocUse.malloc = + dlsym(libc_malloc_impl_handle, "qemu_instrumented_malloc"); + gMallocUse.free = + dlsym(libc_malloc_impl_handle, "qemu_instrumented_free"); + gMallocUse.calloc = + dlsym(libc_malloc_impl_handle, "qemu_instrumented_calloc"); + gMallocUse.realloc = + dlsym(libc_malloc_impl_handle, "qemu_instrumented_realloc"); + gMallocUse.memalign = + dlsym(libc_malloc_impl_handle, "qemu_instrumented_memalign"); + break; + default: + break; + } + + // Make sure dispatch table is initialized + if ((gMallocUse.malloc == NULL) || + (gMallocUse.free == NULL) || + (gMallocUse.calloc == NULL) || + (gMallocUse.realloc == NULL) || + (gMallocUse.memalign == NULL)) { + error_log("%s: Cannot initialize malloc dispatch table for debug level" + " %d: %p, %p, %p, %p, %p\n", + __progname, debug_level, + gMallocUse.malloc, gMallocUse.free, + gMallocUse.calloc, gMallocUse.realloc, + gMallocUse.memalign); + dlclose(libc_malloc_impl_handle); + libc_malloc_impl_handle = NULL; + } else { + __libc_malloc_dispatch = &gMallocUse; + } +} + +static pthread_once_t malloc_init_once_ctl = PTHREAD_ONCE_INIT; + +#endif // !LIBC_STATIC +#endif // USE_DL_PREFIX + +/* Initializes memory allocation framework. + * This routine is called from __libc_init routines implemented + * in libc_init_static.c and libc_init_dynamic.c files. + */ +void malloc_debug_init(void) +{ + /* We need to initialize malloc iff we implement here custom + * malloc routines (i.e. USE_DL_PREFIX is defined) for libc.so */ +#if defined(USE_DL_PREFIX) && !defined(LIBC_STATIC) + if (pthread_once(&malloc_init_once_ctl, malloc_init_impl)) { + error_log("Unable to initialize malloc_debug component."); + } +#endif // USE_DL_PREFIX && !LIBC_STATIC +} diff --git a/libc/bionic/malloc_debug_common.h b/libc/bionic/malloc_debug_common.h new file mode 100644 index 0000000..87600d6 --- /dev/null +++ b/libc/bionic/malloc_debug_common.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Contains declarations of types and constants used by malloc leak + * detection code in both, libc and libc_malloc_debug libraries. + */ +#ifndef MALLOC_DEBUG_COMMON_H +#define MALLOC_DEBUG_COMMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define HASHTABLE_SIZE 1543 +#define BACKTRACE_SIZE 32 +/* flag definitions, currently sharing storage with "size" */ +#define SIZE_FLAG_ZYGOTE_CHILD (1<<31) +#define SIZE_FLAG_MASK (SIZE_FLAG_ZYGOTE_CHILD) + +#define MAX_SIZE_T (~(size_t)0) + +// ============================================================================= +// Structures +// ============================================================================= + +typedef struct HashEntry HashEntry; +struct HashEntry { + size_t slot; + HashEntry* prev; + HashEntry* next; + size_t numEntries; + // fields above "size" are NOT sent to the host + size_t size; + size_t allocations; + intptr_t backtrace[0]; +}; + +typedef struct HashTable HashTable; +struct HashTable { + size_t count; + HashEntry* slots[HASHTABLE_SIZE]; +}; + +/* Entry in malloc dispatch table. */ +typedef struct MallocDebug MallocDebug; +struct MallocDebug { + /* Address of the actual malloc routine. */ + void* (*malloc)(size_t bytes); + /* Address of the actual free routine. */ + void (*free)(void* mem); + /* Address of the actual calloc routine. */ + void* (*calloc)(size_t n_elements, size_t elem_size); + /* Address of the actual realloc routine. */ + void* (*realloc)(void* oldMem, size_t bytes); + /* Address of the actual memalign routine. */ + void* (*memalign)(size_t alignment, size_t bytes); +}; + +/* Malloc debugging initialization routine. + * This routine must be implemented in .so modules that implement malloc + * debugging. This routine is called once per process from malloc_init_impl + * routine implemented in bionic/libc/bionic/malloc_debug_common.c when malloc + * debugging gets initialized for the process. + * Return: + * 0 on success, -1 on failure. + */ +typedef int (*MallocDebugInit)(void); + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif + +#endif // MALLOC_DEBUG_COMMON_H diff --git a/libc/bionic/malloc_leak.c b/libc/bionic/malloc_debug_leak.c index b21bc6a..0a3a68d 100644 --- a/libc/bionic/malloc_leak.c +++ b/libc/bionic/malloc_debug_leak.c @@ -38,6 +38,7 @@ #include <stdarg.h> #include <fcntl.h> #include <unwind.h> +#include <dlfcn.h> #include <sys/socket.h> #include <sys/un.h> @@ -47,212 +48,37 @@ #include "dlmalloc.h" #include "logd.h" +#include "malloc_debug_common.h" -// ============================================================================= -// Utilities directly used by Dalvik -// ============================================================================= - -#define HASHTABLE_SIZE 1543 -#define BACKTRACE_SIZE 32 -/* flag definitions, currently sharing storage with "size" */ -#define SIZE_FLAG_ZYGOTE_CHILD (1<<31) -#define SIZE_FLAG_MASK (SIZE_FLAG_ZYGOTE_CHILD) - -#define MAX_SIZE_T (~(size_t)0) - -/* - * In a VM process, this is set to 1 after fork()ing out of zygote. - */ -int gMallocLeakZygoteChild = 0; - -// ============================================================================= -// Structures -// ============================================================================= - -typedef struct HashEntry HashEntry; -struct HashEntry { - size_t slot; - HashEntry* prev; - HashEntry* next; - size_t numEntries; - // fields above "size" are NOT sent to the host - size_t size; - size_t allocations; - intptr_t backtrace[0]; -}; - -typedef struct HashTable HashTable; -struct HashTable { - size_t count; - HashEntry* slots[HASHTABLE_SIZE]; -}; +// This file should be included into the build only when +// MALLOC_LEAK_CHECK, or MALLOC_QEMU_INSTRUMENT, or both +// macros are defined. +#ifndef MALLOC_LEAK_CHECK +#error MALLOC_LEAK_CHECK is not defined. +#endif // !MALLOC_LEAK_CHECK -static pthread_mutex_t gAllocationsMutex = PTHREAD_MUTEX_INITIALIZER; -static HashTable gHashTable; +// Global variables defined in malloc_debug_common.c +extern int gMallocLeakZygoteChild; +extern pthread_mutex_t gAllocationsMutex; +extern HashTable gHashTable; +extern const MallocDebug __libc_malloc_default_dispatch; +extern const MallocDebug* __libc_malloc_dispatch; // ============================================================================= // log functions // ============================================================================= #define debug_log(format, ...) \ - __libc_android_log_print(ANDROID_LOG_DEBUG, "malloc_leak", (format), ##__VA_ARGS__ ) - -// ============================================================================= -// output functions -// ============================================================================= - -static int hash_entry_compare(const void* arg1, const void* arg2) -{ - HashEntry* e1 = *(HashEntry**)arg1; - HashEntry* e2 = *(HashEntry**)arg2; - - size_t nbAlloc1 = e1->allocations; - size_t nbAlloc2 = e2->allocations; - size_t size1 = e1->size & ~SIZE_FLAG_MASK; - size_t size2 = e2->size & ~SIZE_FLAG_MASK; - size_t alloc1 = nbAlloc1 * size1; - size_t alloc2 = nbAlloc2 * size2; - - // sort in descending order by: - // 1) total size - // 2) number of allocations - // - // This is used for sorting, not determination of equality, so we don't - // need to compare the bit flags. - int result; - if (alloc1 > alloc2) { - result = -1; - } else if (alloc1 < alloc2) { - result = 1; - } else { - if (nbAlloc1 > nbAlloc2) { - result = -1; - } else if (nbAlloc1 < nbAlloc2) { - result = 1; - } else { - result = 0; - } - } - return result; -} - -/* - * Retrieve native heap information. - * - * "*info" is set to a buffer we allocate - * "*overallSize" is set to the size of the "info" buffer - * "*infoSize" is set to the size of a single entry - * "*totalMemory" is set to the sum of all allocations we're tracking; does - * not include heap overhead - * "*backtraceSize" is set to the maximum number of entries in the back trace - */ -void get_malloc_leak_info(uint8_t** info, size_t* overallSize, - size_t* infoSize, size_t* totalMemory, size_t* backtraceSize) -{ - // don't do anything if we have invalid arguments - if (info == NULL || overallSize == NULL || infoSize == NULL || - totalMemory == NULL || backtraceSize == NULL) { - return; - } - - pthread_mutex_lock(&gAllocationsMutex); - - if (gHashTable.count == 0) { - *info = NULL; - *overallSize = 0; - *infoSize = 0; - *totalMemory = 0; - *backtraceSize = 0; - goto done; - } - - void** list = (void**)dlmalloc(sizeof(void*) * gHashTable.count); - - // debug_log("*****\ngHashTable.count = %d\n", gHashTable.count); - // debug_log("list = %p\n", list); - - // get the entries into an array to be sorted - int index = 0; - int i; - for (i = 0 ; i < HASHTABLE_SIZE ; i++) { - HashEntry* entry = gHashTable.slots[i]; - while (entry != NULL) { - list[index] = entry; - *totalMemory = *totalMemory + - ((entry->size & ~SIZE_FLAG_MASK) * entry->allocations); - index++; - entry = entry->next; - } - } - - // debug_log("sorted list!\n"); - // XXX: the protocol doesn't allow variable size for the stack trace (yet) - *infoSize = (sizeof(size_t) * 2) + (sizeof(intptr_t) * BACKTRACE_SIZE); - *overallSize = *infoSize * gHashTable.count; - *backtraceSize = BACKTRACE_SIZE; - - // debug_log("infoSize = 0x%x overall = 0x%x\n", *infoSize, *overallSize); - // now get A byte array big enough for this - *info = (uint8_t*)dlmalloc(*overallSize); - - // debug_log("info = %p\n", info); - if (*info == NULL) { - *overallSize = 0; - goto out_nomem_info; - } - - // debug_log("sorting list...\n"); - qsort((void*)list, gHashTable.count, sizeof(void*), hash_entry_compare); - - uint8_t* head = *info; - const int count = gHashTable.count; - for (i = 0 ; i < count ; i++) { - HashEntry* entry = list[i]; - size_t entrySize = (sizeof(size_t) * 2) + (sizeof(intptr_t) * entry->numEntries); - if (entrySize < *infoSize) { - /* we're writing less than a full entry, clear out the rest */ - memset(head + entrySize, 0, *infoSize - entrySize); - } else { - /* make sure the amount we're copying doesn't exceed the limit */ - entrySize = *infoSize; - } - memcpy(head, &(entry->size), entrySize); - head += *infoSize; - } - -out_nomem_info: - dlfree(list); - -done: - // debug_log("+++++ done!\n"); - pthread_mutex_unlock(&gAllocationsMutex); -} - -void free_malloc_leak_info(uint8_t* info) -{ - dlfree(info); -} - -struct mallinfo mallinfo() -{ - return dlmallinfo(); -} - -void* valloc(size_t bytes) { - /* assume page size of 4096 bytes */ - return memalign( getpagesize(), bytes ); -} + __libc_android_log_print(ANDROID_LOG_DEBUG, "malloc_leak_check", (format), ##__VA_ARGS__ ) +#define error_log(format, ...) \ + __libc_android_log_print(ANDROID_LOG_ERROR, "malloc_leak_check", (format), ##__VA_ARGS__ ) +#define info_log(format, ...) \ + __libc_android_log_print(ANDROID_LOG_INFO, "malloc_leak_check", (format), ##__VA_ARGS__ ) +static int gTrapOnError = 1; -/* - * Code guarded by MALLOC_LEAK_CHECK is only needed when malloc check is - * enabled. Currently we exclude them in libc.so, and only include them in - * libc_debug.so. - */ -#ifdef MALLOC_LEAK_CHECK #define MALLOC_ALIGNMENT 8 #define GUARD 0x48151642 - #define DEBUG 0 // ============================================================================= @@ -409,13 +235,13 @@ static _Unwind_Reason_Code trace_function(__unwind_context *context, void *arg) if (state->count) { intptr_t ip = (intptr_t)_Unwind_GetIP(context); if (ip) { - state->addrs[0] = ip; + state->addrs[0] = ip; state->addrs++; state->count--; return _URC_NO_REASON; } } - /* + /* * If we run out of space to record the address or 0 has been seen, stop * unwinding the stack. */ @@ -433,70 +259,6 @@ int get_backtrace(intptr_t* addrs, size_t max_entries) } // ============================================================================= -// malloc leak function dispatcher -// ============================================================================= - -static void* leak_malloc(size_t bytes); -static void leak_free(void* mem); -static void* leak_calloc(size_t n_elements, size_t elem_size); -static void* leak_realloc(void* oldMem, size_t bytes); -static void* leak_memalign(size_t alignment, size_t bytes); - -static void* fill_malloc(size_t bytes); -static void fill_free(void* mem); -static void* fill_realloc(void* oldMem, size_t bytes); -static void* fill_memalign(size_t alignment, size_t bytes); - -static void* chk_malloc(size_t bytes); -static void chk_free(void* mem); -static void* chk_calloc(size_t n_elements, size_t elem_size); -static void* chk_realloc(void* oldMem, size_t bytes); -static void* chk_memalign(size_t alignment, size_t bytes); - -typedef struct { - void* (*malloc)(size_t bytes); - void (*free)(void* mem); - void* (*calloc)(size_t n_elements, size_t elem_size); - void* (*realloc)(void* oldMem, size_t bytes); - void* (*memalign)(size_t alignment, size_t bytes); -} MallocDebug; - -static const MallocDebug gMallocEngineTable[] __attribute__((aligned(32))) = -{ - { dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign }, - { leak_malloc, leak_free, leak_calloc, leak_realloc, leak_memalign }, - { fill_malloc, fill_free, dlcalloc, fill_realloc, fill_memalign }, - { chk_malloc, chk_free, chk_calloc, chk_realloc, chk_memalign } -}; - -enum { - INDEX_NORMAL = 0, - INDEX_LEAK_CHECK, - INDEX_MALLOC_FILL, - INDEX_MALLOC_CHECK, -}; - -static MallocDebug const * gMallocDispatch = &gMallocEngineTable[INDEX_NORMAL]; -static int gMallocDebugLevel; -static int gTrapOnError = 1; - -void* malloc(size_t bytes) { - return gMallocDispatch->malloc(bytes); -} -void free(void* mem) { - gMallocDispatch->free(mem); -} -void* calloc(size_t n_elements, size_t elem_size) { - return gMallocDispatch->calloc(n_elements, elem_size); -} -void* realloc(void* oldMem, size_t bytes) { - return gMallocDispatch->realloc(oldMem, bytes); -} -void* memalign(size_t alignment, size_t bytes) { - return gMallocDispatch->memalign(alignment, bytes); -} - -// ============================================================================= // malloc check functions // ============================================================================= @@ -534,7 +296,9 @@ static void assert_log_message(const char* format, ...) va_list args; pthread_mutex_lock(&gAllocationsMutex); - gMallocDispatch = &gMallocEngineTable[INDEX_NORMAL]; + { + const MallocDebug* current_dispatch = __libc_malloc_dispatch; + __libc_malloc_dispatch = &__libc_malloc_default_dispatch; va_start(args, format); __libc_android_log_vprint(ANDROID_LOG_ERROR, "libc", format, args); @@ -543,7 +307,8 @@ static void assert_log_message(const char* format, ...) if (gTrapOnError) { __builtin_trap(); } - gMallocDispatch = &gMallocEngineTable[INDEX_MALLOC_CHECK]; + __libc_malloc_dispatch = current_dispatch; + } pthread_mutex_unlock(&gAllocationsMutex); } @@ -576,7 +341,7 @@ static int chk_mem_check(void* mem, buf = (char*)mem - CHK_SENTINEL_HEAD_SIZE; for (i=0 ; i<CHK_SENTINEL_HEAD_SIZE ; i++) { if (buf[i] != CHK_SENTINEL_VALUE) { - assert_log_message( + assert_log_message( "*** %s CHECK: buffer %p " "corrupted %d bytes before allocation", func, mem, CHK_SENTINEL_HEAD_SIZE-i); @@ -592,7 +357,7 @@ static int chk_mem_check(void* mem, buf = (char*)mem + bytes; for (i=CHK_SENTINEL_TAIL_SIZE-1 ; i>=0 ; i--) { if (buf[i] != CHK_SENTINEL_VALUE) { - assert_log_message( + assert_log_message( "*** %s CHECK: buffer %p, size=%lu, " "corrupted %d bytes after allocation", func, buffer, bytes, i+1); @@ -746,11 +511,11 @@ void* leak_malloc(size_t bytes) intptr_t backtrace[BACKTRACE_SIZE]; size_t numEntries = get_backtrace(backtrace, BACKTRACE_SIZE); - + AllocationEntry* header = (AllocationEntry*)base; header->entry = record_backtrace(backtrace, numEntries, bytes); header->guard = GUARD; - + // now increment base to point to after our header. // this should just work since our header is 8 bytes. base = (AllocationEntry*)base + 1; @@ -768,7 +533,7 @@ void leak_free(void* mem) // check the guard to make sure it is valid AllocationEntry* header = (AllocationEntry*)mem - 1; - + if (header->guard != GUARD) { // could be a memaligned block if (((void**)mem)[-1] == MEMALIGN_GUARD) { @@ -776,7 +541,7 @@ void leak_free(void* mem) header = (AllocationEntry*)mem - 1; } } - + if (header->guard == GUARD || is_valid_entry(header->entry)) { // decrement the allocations HashEntry* entry = header->entry; @@ -845,7 +610,7 @@ void* leak_memalign(size_t alignment, size_t bytes) // need to make sure it's a power of two if (alignment & (alignment-1)) alignment = 1L << (31 - __builtin_clz(alignment)); - + // here, aligment is at least MALLOC_ALIGNMENT<<1 bytes // we will align by at least MALLOC_ALIGNMENT bytes // and at most alignment-MALLOC_ALIGNMENT bytes @@ -858,7 +623,7 @@ void* leak_memalign(size_t alignment, size_t bytes) // align the pointer ptr += ((-ptr) % alignment); - + // there is always enough space for the base pointer and the guard ((void**)ptr)[-1] = MEMALIGN_GUARD; ((void**)ptr)[-2] = base; @@ -867,60 +632,12 @@ void* leak_memalign(size_t alignment, size_t bytes) } return base; } -#endif /* MALLOC_LEAK_CHECK */ - -// called from libc_init() -extern char* __progname; -void malloc_debug_init() +/* Initializes malloc debugging framework. + * See comments on MallocDebugInit in malloc_debug_common.h + */ +int malloc_debug_initialize(void) { - unsigned int level = 0; -#ifdef MALLOC_LEAK_CHECK - // if MALLOC_LEAK_CHECK is enabled, use level=1 by default - level = 1; -#endif - char env[PROP_VALUE_MAX]; - int len = __system_property_get("libc.debug.malloc", env); - - if (len) { - level = atoi(env); -#ifndef MALLOC_LEAK_CHECK - /* Alert the user that libc_debug.so needs to be installed as libc.so - * when performing malloc checks. - */ - if (level != 0) { - __libc_android_log_print(ANDROID_LOG_INFO, "libc", - "Malloc checks need libc_debug.so pushed to the device!\n"); - - } -#endif - } - -#ifdef MALLOC_LEAK_CHECK - gMallocDebugLevel = level; - switch (level) { - default: - case 0: - gMallocDispatch = &gMallocEngineTable[INDEX_NORMAL]; - break; - case 1: - __libc_android_log_print(ANDROID_LOG_INFO, "libc", - "%s using MALLOC_DEBUG = %d (leak checker)\n", - __progname, level); - gMallocDispatch = &gMallocEngineTable[INDEX_LEAK_CHECK]; - break; - case 5: - __libc_android_log_print(ANDROID_LOG_INFO, "libc", - "%s using MALLOC_DEBUG = %d (fill)\n", - __progname, level); - gMallocDispatch = &gMallocEngineTable[INDEX_MALLOC_FILL]; - break; - case 10: - __libc_android_log_print(ANDROID_LOG_INFO, "libc", - "%s using MALLOC_DEBUG = %d (sentinels, fill)\n", - __progname, level); - gMallocDispatch = &gMallocEngineTable[INDEX_MALLOC_CHECK]; - break; - } -#endif + // We don't really have anything that requires initialization here. + return 0; } diff --git a/libc/bionic/malloc_debug_qemu.c b/libc/bionic/malloc_debug_qemu.c new file mode 100644 index 0000000..4b694e9 --- /dev/null +++ b/libc/bionic/malloc_debug_qemu.c @@ -0,0 +1,1014 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Contains implementation of memory allocation routines instrumented for + * usage in the emulator to detect memory allocation violations, such as + * memory leaks, buffer overruns, etc. + * Code, implemented here is intended to run in the emulated environment only, + * and serves simply as hooks into memory allocation routines. Main job of this + * code is to notify the emulator about memory being allocated/deallocated, + * providing information about each allocation. The idea is that emulator will + * keep list of currently allocated blocks, and, knowing boundaries of each + * block it will be able to verify that ld/st access to these blocks don't step + * over boundaries set for the user. To enforce that, each memory block + * allocated by this code is guarded with "prefix" and "suffix" areas, so + * every time emulator detects access to any of these guarding areas, it can be + * considered as access violation. + */ + +#include <stdlib.h> +#include <stddef.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <pthread.h> +#include <unistd.h> +#include <errno.h> +#include "dlmalloc.h" +#include "logd.h" +#include "malloc_debug_common.h" + +/* This file should be included into the build only when + * MALLOC_QEMU_INSTRUMENT macro is defined. */ +#ifndef MALLOC_QEMU_INSTRUMENT +#error MALLOC_QEMU_INSTRUMENT is not defined. +#endif // !MALLOC_QEMU_INSTRUMENT + +/* Controls access violation test performed to make sure that we catch AVs + * all the time they occur. See test_access_violation for more info. This macro + * is used for internal testing purposes and should always be set to zero for + * the production builds. */ +#define TEST_ACCESS_VIOLATIONS 0 + +// ============================================================================= +// Communication structures +// ============================================================================= + +/* Describes memory block allocated from the heap. This structure is passed + * along with TRACE_DEV_REG_MALLOC event. This descriptor is used to inform + * the emulator about new memory block being allocated from the heap. The entire + * structure is initialized by the guest system before event is fired up. It is + * important to remember that same structure (an exact copy, except for + * replacing pointers with target_ulong) is also declared in the emulator's + * sources (file memcheck/memcheck_common.h). So, every time a change is made to + * any of these two declaration, another one must be also updated accordingly. + */ +typedef struct MallocDesc { + /* Pointer to the memory block actually allocated from the heap. Note that + * this is not the pointer that is returned to the malloc's caller. Pointer + * returned to the caller is calculated by adding value stored in this field + * to the value stored in prefix_size field of this structure. + */ + void* ptr; + + /* Number of bytes requested by the malloc's caller. */ + uint32_t requested_bytes; + + /* Byte size of the prefix data. Actual pointer returned to the malloc's + * caller is calculated by adding value stored in this field to the value + * stored in in the ptr field of this structure. + */ + uint32_t prefix_size; + + /* Byte size of the suffix data. */ + uint32_t suffix_size; + + /* Id of the process that initialized libc instance, in which allocation + * has occurred. This field is used by the emulator to report errors in + * the course of TRACE_DEV_REG_MALLOC event handling. In case of an error, + * emulator sets this field to zero (invalid value for a process ID). + */ + uint32_t libc_pid; + + /* Id of the process in context of which allocation has occurred. + * Value in this field may differ from libc_pid value, if process that + * is doing allocation has been forked from the process that initialized + * libc instance. + */ + uint32_t allocator_pid; + + /* Number of access violations detected on this allocation. */ + uint32_t av_count; +} MallocDesc; + +/* Describes memory block info queried from emulator. This structure is passed + * along with TRACE_DEV_REG_QUERY_MALLOC event. When handling free and realloc + * calls, it is required that we have information about memory blocks that were + * actually allocated in previous calls to malloc, calloc, memalign, or realloc. + * Since we don't keep this information directly in the allocated block, but + * rather we keep it in the emulator, we need to query emulator for that + * information with TRACE_DEV_REG_QUERY_MALLOC query. The entire structure is + * initialized by the guest system before event is fired up. It is important to + * remember that same structure (an exact copy, except for replacing pointers + * with target_ulong) is also declared in the emulator's sources (file + * memcheck/memecheck_common.h). So, every time a change is made to any of these + * two declaration, another one must be also updated accordingly. + */ +typedef struct MallocDescQuery { + /* Pointer, for which information is queried. Note that this pointer doesn't + * have to be exact pointer returned to malloc's caller, but can point + * anywhere inside an allocated block, including guarding areas. Emulator + * will respond with information about allocated block that contains this + * pointer. + */ + void* ptr; + + /* Id of the process that initialized libc instance, in which this query + * is called. This field is used by the emulator to report errors in + * the course of TRACE_DEV_REG_QUERY_MALLOC event handling. In case of an + * error, emulator sets this field to zero (invalid value for a process ID). + */ + uint32_t libc_pid; + + /* Process ID in context of which query is made. */ + uint32_t query_pid; + + /* Code of the allocation routine, in context of which query has been made: + * 1 - free + * 2 - realloc + */ + uint32_t routine; + + /* Address of memory allocation descriptor for the queried pointer. + * Descriptor, addressed by this field is initialized by the emulator in + * response to the query. + */ + MallocDesc* desc; +} MallocDescQuery; + +/* Describes memory block that is being freed back to the heap. This structure + * is passed along with TRACE_DEV_REG_FREE_PTR event. The entire structure is + * initialized by the guest system before event is fired up. It is important to + * remember that same structure (an exact copy, except for replacing pointers + * with target_ulong) is also declared in the emulator's sources (file + * memcheck/memecheck_common.h). So, every time a change is made to any of these + * two declaration, another one must be also updated accordingly. + */ +typedef struct MallocFree { + /* Pointer to be freed. */ + void* ptr; + + /* Id of the process that initialized libc instance, in which this free + * is called. This field is used by the emulator to report errors in + * the course of TRACE_DEV_REG_FREE_PTR event handling. In case of an + * error, emulator sets this field to zero (invalid value for a process ID). + */ + uint32_t libc_pid; + + /* Process ID in context of which memory is being freed. */ + uint32_t free_pid; +} MallocFree; + +// ============================================================================= +// Communication events +// ============================================================================= + +/* Notifies the emulator that libc has been initialized for a process. + * Event's value parameter is PID for the process in context of which libc has + * been initialized. + */ +#define TRACE_DEV_REG_LIBC_INIT 1536 + +/* Notifies the emulator about new memory block been allocated. + * Event's value parameter points to MallocDesc instance that contains + * allocated block information. Note that 'libc_pid' field of the descriptor + * is used by emulator to report failure in handling this event. In case + * of a failure emulator will zero that field before completing this event. + */ +#define TRACE_DEV_REG_MALLOC 1537 + +/* Notifies the emulator about memory block being freed. + * Event's value parameter points to MallocFree descriptor that contains + * information about block that's being freed. Note that 'libc_pid' field + * of the descriptor is used by emulator to report failure in handling this + * event. In case of a failure emulator will zero that field before completing + * this event. + */ +#define TRACE_DEV_REG_FREE_PTR 1538 + +/* Queries the emulator about allocated memory block information. + * Event's value parameter points to MallocDescQuery descriptor that contains + * query parameters. Note that 'libc_pid' field of the descriptor is used by + * emulator to report failure in handling this event. In case of a failure + * emulator will zero that field before completing this event. + */ +#define TRACE_DEV_REG_QUERY_MALLOC 1539 + +/* Queries the emulator to print a string to its stdout. + * Event's value parameter points to a zero-terminated string to be printed. + */ +#define TRACE_DEV_REG_PRINT_USER_STR 1540 + +static void notify_qemu_string(const char* str); +static void qemu_log(int prio, const char* fmt, ...); +static void dump_malloc_descriptor(char* str, + size_t str_buf_size, + const MallocDesc* desc); + +// ============================================================================= +// Macros +// ============================================================================= + +/* Defines default size of allocation prefix. + * Note that we make prefix area quite large in order to increase chances of + * catching buffer overflow. */ +#define DEFAULT_PREFIX_SIZE (malloc_alignment * 4) + +/* Defines default size of allocation suffix. + * Note that we make suffix area quite large in order to increase chances of + * catching buffer overflow. */ +#define DEFAULT_SUFFIX_SIZE (malloc_alignment * 4) + +/* Debug tracing has been enabled by the emulator. */ +#define DEBUG_TRACING_ENABLED 0x00000001 +/* Error tracing has been enabled by the emulator. */ +#define ERROR_TRACING_ENABLED 0x00000002 +/* Info tracing has been enabled by the emulator. */ +#define INFO_TRACING_ENABLED 0x00000004 +/* All tracing flags combined. */ +#define ALL_TRACING_ENABLED (DEBUG_TRACING_ENABLED | \ + ERROR_TRACING_ENABLED | \ + INFO_TRACING_ENABLED) + +/* Prints a string to the emulator's stdout. + * In early stages of system loading, logging mesages via + * __libc_android_log_print API is not available, because ADB API has not been + * hooked up yet. So, in order to see such messages we need to print them to + * the emulator's stdout. + * Parameters passed to this macro are the same as parameters for printf + * routine. + */ +#define TR(...) \ + do { \ + char tr_str[4096]; \ + snprintf(tr_str, sizeof(tr_str), __VA_ARGS__ ); \ + tr_str[sizeof(tr_str) - 1] = '\0'; \ + notify_qemu_string(&tr_str[0]); \ + } while (0) + +// ============================================================================= +// Logging macros. Note that we simultaneously log messages to ADB and emulator. +// ============================================================================= + +/* + * Helper macros for checking if particular trace level is enabled. + */ +#define debug_LOG_ENABLED ((tracing_flags & DEBUG_TRACING_ENABLED) != 0) +#define error_LOG_ENABLED ((tracing_flags & ERROR_TRACING_ENABLED) != 0) +#define info_LOG_ENABLED ((tracing_flags & INFO_TRACING_ENABLED) != 0) +#define tracing_enabled(type) (type##_LOG_ENABLED) + +/* + * Logging helper macros. + */ +#define debug_log(format, ...) \ + do { \ + __libc_android_log_print(ANDROID_LOG_DEBUG, "memcheck", \ + (format), ##__VA_ARGS__ ); \ + if (tracing_flags & DEBUG_TRACING_ENABLED) { \ + qemu_log(ANDROID_LOG_DEBUG, (format), ##__VA_ARGS__ ); \ + } \ + } while (0) + +#define error_log(format, ...) \ + do { \ + __libc_android_log_print(ANDROID_LOG_ERROR, "memcheck", \ + (format), ##__VA_ARGS__ ); \ + if (tracing_flags & ERROR_TRACING_ENABLED) { \ + qemu_log(ANDROID_LOG_ERROR, (format), ##__VA_ARGS__ ); \ + } \ + } while (0) + +#define info_log(format, ...) \ + do { \ + __libc_android_log_print(ANDROID_LOG_INFO, "memcheck", \ + (format), ##__VA_ARGS__ ); \ + if (tracing_flags & INFO_TRACING_ENABLED) { \ + qemu_log(ANDROID_LOG_INFO, (format), ##__VA_ARGS__ ); \ + } \ + } while (0) + +/* Logs message dumping MallocDesc instance at the end of the message. + * Param: + * type - Message type: debug, error, or info + * desc - MallocDesc instance to dump. + * frmt + rest - Formats message preceding dumped descriptor. +*/ +#define log_mdesc(type, desc, frmt, ...) \ + do { \ + if (tracing_enabled(type)) { \ + char log_str[4096]; \ + size_t str_len; \ + snprintf(log_str, sizeof(log_str), frmt, ##__VA_ARGS__); \ + log_str[sizeof(log_str) - 1] = '\0'; \ + str_len = strlen(log_str); \ + dump_malloc_descriptor(log_str + str_len, \ + sizeof(log_str) - str_len, \ + (desc)); \ + type##_log(log_str); \ + } \ + } while (0) + +// ============================================================================= +// Static data +// ============================================================================= + +/* Emulator's magic page address. + * This page (mapped on /dev/qemu_trace device) is used to fire up events + * in the emulator. */ +static volatile void* qtrace = NULL; + +/* Cached PID of the process in context of which this libc instance + * has been initialized. */ +static uint32_t malloc_pid = 0; + +/* Memory allocation alignment that is used in dlmalloc. + * This variable is updated by memcheck_initialize routine. */ +static uint32_t malloc_alignment = 8; + +/* Tracing flags. These flags control which types of logging messages are + * enabled by the emulator. See XXX_TRACING_ENABLED for the values of flags + * stored in this variable. This variable is updated by memcheck_initialize + * routine. */ +static uint32_t tracing_flags = 0; + +// ============================================================================= +// Static routines +// ============================================================================= + +/* Gets pointer, returned to malloc caller for the given allocation decriptor. + * Param: + * desc - Allocation descriptor. + * Return: + * Pointer to the allocated memory returned to the malloc caller. + */ +static inline void* +mallocdesc_user_ptr(const MallocDesc* desc) +{ + return (char*)desc->ptr + desc->prefix_size; +} + +/* Gets size of memory block actually allocated from the heap for the given + * allocation decriptor. + * Param: + * desc - Allocation descriptor. + * Return: + * Size of memory block actually allocated from the heap. + */ +static inline uint32_t +mallocdesc_alloc_size(const MallocDesc* desc) +{ + return desc->prefix_size + desc->requested_bytes + desc->suffix_size; +} + +/* Gets pointer to the end of the allocated block for the given descriptor. + * Param: + * desc - Descriptor for the memory block, allocated in malloc handler. + * Return: + * Pointer to the end of (one byte past) the allocated block. + */ +static inline void* +mallocdesc_alloc_end(const MallocDesc* desc) +{ + return (char*)desc->ptr + mallocdesc_alloc_size(desc); +} + +/* Fires up an event in the emulator. + * Param: + * code - Event code (one of the TRACE_DEV_XXX). + * val - Event's value parameter. + */ +static inline void +notify_qemu(uint32_t code, uint32_t val) +{ + if (NULL != qtrace) { + *(volatile uint32_t*)((uint32_t)qtrace + ((code - 1024) << 2)) = val; + } +} + +/* Prints a zero-terminated string to the emulator's stdout (fires up + * TRACE_DEV_REG_PRINT_USER_STR event in the emulator). + * Param: + * str - Zero-terminated string to print. + */ +static void +notify_qemu_string(const char* str) +{ + if (str != NULL) { + notify_qemu(TRACE_DEV_REG_PRINT_USER_STR, (uint32_t)str); + } +} + +/* Fires up TRACE_DEV_REG_LIBC_INIT event in the emulator. + * Param: + * pid - ID of the process that initialized libc. + */ +static void +notify_qemu_libc_initialized(uint32_t pid) +{ + notify_qemu(TRACE_DEV_REG_LIBC_INIT, pid); +} + +/* Fires up TRACE_DEV_REG_MALLOC event in the emulator. + * Param: + * desc - Pointer to MallocDesc instance containing allocated block + * information. + * Return: + * Zero on success, or -1 on failure. Note that on failure libc_pid field of + * the desc parameter passed to this routine has been zeroed out by the + * emulator. + */ +static inline int +notify_qemu_malloc(volatile MallocDesc* desc) +{ + desc->libc_pid = malloc_pid; + desc->allocator_pid = getpid(); + desc->av_count = 0; + notify_qemu(TRACE_DEV_REG_MALLOC, (uint32_t)desc); + + /* Emulator reports failure by zeroing libc_pid field of the + * descriptor. */ + return desc->libc_pid != 0 ? 0 : -1; +} + +/* Fires up TRACE_DEV_REG_FREE_PTR event in the emulator. + * Param: + * ptr - Pointer to the memory block that's being freed. + * Return: + * Zero on success, or -1 on failure. + */ +static inline int +notify_qemu_free(void* ptr_to_free) +{ + volatile MallocFree free_desc; + + free_desc.ptr = ptr_to_free; + free_desc.libc_pid = malloc_pid; + free_desc.free_pid = getpid(); + notify_qemu(TRACE_DEV_REG_FREE_PTR, (uint32_t)&free_desc); + + /* Emulator reports failure by zeroing libc_pid field of the + * descriptor. */ + return free_desc.libc_pid != 0 ? 0 : -1; +} + +/* Fires up TRACE_DEV_REG_QUERY_MALLOC event in the emulator. + * Param: + * ptr - Pointer to request allocation information for. + * desc - Pointer to MallocDesc instance that will receive allocation + * information. + * routine - Code of the allocation routine, in context of which query is made: + * 1 - free + * 2 - realloc + * Return: + * Zero on success, or -1 on failure. + */ +static inline int +query_qemu_malloc_info(void* ptr, MallocDesc* desc, uint32_t routine) +{ + volatile MallocDescQuery query; + + query.ptr = ptr; + query.libc_pid = malloc_pid; + query.query_pid = getpid(); + query.routine = routine; + query.desc = desc; + notify_qemu(TRACE_DEV_REG_QUERY_MALLOC, (uint32_t)&query); + + /* Emulator reports failure by zeroing libc_pid field of the + * descriptor. */ + return query.libc_pid != 0 ? 0 : -1; +} + +/* Logs a message to emulator's stdout. + * Param: + * prio - Message priority (debug, info, or error) + * fmt + rest - Message format and parameters. + */ +static void +qemu_log(int prio, const char* fmt, ...) +{ + va_list ap; + char buf[4096]; + const char* prefix; + + /* Choose message prefix depending on the priority value. */ + switch (prio) { + case ANDROID_LOG_ERROR: + if (!tracing_enabled(error)) { + return; + } + prefix = "E"; + break; + case ANDROID_LOG_INFO: + if (!tracing_enabled(info)) { + return; + } + prefix = "I"; + break; + case ANDROID_LOG_DEBUG: + default: + if (!tracing_enabled(debug)) { + return; + } + prefix = "D"; + break; + } + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + buf[sizeof(buf) - 1] = '\0'; + + TR("%s/memcheck: %s\n", prefix, buf); +} + +/* Dumps content of memory allocation descriptor to a string. + * Param: + * str - String to dump descriptor to. + * str_buf_size - Size of string's buffer. + * desc - Descriptor to dump. + */ +static void +dump_malloc_descriptor(char* str, size_t str_buf_size, const MallocDesc* desc) +{ + if (str_buf_size) { + snprintf(str, str_buf_size, + "MDesc: %p: %X <-> %X [%u + %u + %u] by pid=%03u in libc_pid=%03u", + mallocdesc_user_ptr(desc), (uint32_t)desc->ptr, + (uint32_t)mallocdesc_alloc_end(desc), desc->prefix_size, + desc->requested_bytes, desc->suffix_size, desc->allocator_pid, + desc->libc_pid); + str[str_buf_size - 1] = '\0'; + } +} + +#if TEST_ACCESS_VIOLATIONS +/* Causes an access violation on allocation descriptor, and verifies that + * violation has been detected by memory checker in the emulator. + */ +static void +test_access_violation(const MallocDesc* desc) +{ + MallocDesc desc_chk; + char ch; + volatile char* prefix = (volatile char*)desc->ptr; + volatile char* suffix = (volatile char*)mallocdesc_user_ptr(desc) + + desc->requested_bytes; + /* We're causing AV by reading from the prefix and suffix areas of the + * allocated block. This should produce two access violations, so when we + * get allocation descriptor from QEMU, av_counter should be bigger than + * av_counter of the original descriptor by 2. */ + ch = *prefix; + ch = *suffix; + if (!query_qemu_malloc_info(mallocdesc_user_ptr(desc), &desc_chk, 2) && + desc_chk.av_count != (desc->av_count + 2)) { + log_mdesc(error, &desc_chk, + "<libc_pid=%03u, pid=%03u>: malloc: Access violation test failed:\n" + "Expected violations count %u is not equal to the actually reported %u", + malloc_pid, getpid(), desc->av_count + 2, + desc_chk.av_count); + } +} +#endif // TEST_ACCESS_VIOLATIONS + +// ============================================================================= +// API routines +// ============================================================================= + +void* qemu_instrumented_malloc(size_t bytes); +void qemu_instrumented_free(void* mem); +void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size); +void* qemu_instrumented_realloc(void* mem, size_t bytes); +void* qemu_instrumented_memalign(size_t alignment, size_t bytes); + +/* Initializes malloc debugging instrumentation for the emulator. + * This routine is called from malloc_init_impl routine implemented in + * bionic/libc/bionic/malloc_debug_common.c when malloc debugging gets + * initialized for a process. The way malloc debugging implementation is + * done, it is guaranteed that this routine will be called just once per + * process. + * Return: + * 0 on success, or -1 on failure. +*/ +int +malloc_debug_initialize(void) +{ + /* We will be using emulator's magic page to report memory allocation + * activities. In essence, what magic page does, it translates writes to + * the memory mapped spaces into writes to an I/O port that emulator + * "listens to" on the other end. Note that until we open and map that + * device, logging to emulator's stdout will not be available. */ + int fd = open("/dev/qemu_trace", O_RDWR); + if (fd < 0) { + error_log("Unable to open /dev/qemu_trace"); + return -1; + } else { + qtrace = mmap(0, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + close(fd); + + if (qtrace == MAP_FAILED) { + qtrace = NULL; + error_log("Unable to mmap /dev/qemu_trace"); + return -1; + } + } + + /* Cache pid of the process this library has been initialized for. */ + malloc_pid = getpid(); + + return 0; +} + +/* Completes malloc debugging instrumentation for the emulator. + * Note that this routine is called after successful return from + * malloc_debug_initialize, which means that connection to the emulator via + * "magic page" has been established. + * Param: + * alignment - Alignment requirement set for memiry allocations. + * memcheck_param - Emulator's -memcheck option parameters. This string + * contains abbreviation for guest events that are enabled for tracing. + * Return: + * 0 on success, or -1 on failure. +*/ +int +memcheck_initialize(int alignment, const char* memcheck_param) +{ + malloc_alignment = alignment; + + /* Parse -memcheck parameter for the guest tracing flags. */ + while (*memcheck_param != '\0') { + switch (*memcheck_param) { + case 'a': + // Enable all messages from the guest. + tracing_flags |= ALL_TRACING_ENABLED; + break; + case 'd': + // Enable debug messages from the guest. + tracing_flags |= DEBUG_TRACING_ENABLED; + break; + case 'e': + // Enable error messages from the guest. + tracing_flags |= ERROR_TRACING_ENABLED; + break; + case 'i': + // Enable info messages from the guest. + tracing_flags |= INFO_TRACING_ENABLED; + break; + default: + break; + } + if (tracing_flags == ALL_TRACING_ENABLED) { + break; + } + memcheck_param++; + } + + notify_qemu_libc_initialized(malloc_pid); + + debug_log("Instrumented for pid=%03u: malloc=%p, free=%p, calloc=%p, realloc=%p, memalign=%p", + malloc_pid, qemu_instrumented_malloc, qemu_instrumented_free, + qemu_instrumented_calloc, qemu_instrumented_realloc, + qemu_instrumented_memalign); + + return 0; +} + +/* This routine serves as entry point for 'malloc'. + * Primary responsibility of this routine is to allocate requested number of + * bytes (plus prefix, and suffix guards), and report allocation to the + * emulator. + */ +void* +qemu_instrumented_malloc(size_t bytes) +{ + MallocDesc desc; + + /* Initialize block descriptor and allocate memory. Note that dlmalloc + * returns a valid pointer on zero allocation. Lets mimic this behavior. */ + desc.prefix_size = DEFAULT_PREFIX_SIZE; + desc.requested_bytes = bytes; + desc.suffix_size = DEFAULT_SUFFIX_SIZE; + desc.ptr = dlmalloc(mallocdesc_alloc_size(&desc)); + if (desc.ptr == NULL) { + error_log("<libc_pid=%03u, pid=%03u> malloc(%u): dlmalloc(%u) failed.", + malloc_pid, getpid(), bytes, mallocdesc_alloc_size(&desc)); + return NULL; + } + + // Fire up event in the emulator. + if (notify_qemu_malloc(&desc)) { + log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: malloc: notify_malloc failed for ", + malloc_pid, getpid()); + dlfree(desc.ptr); + return NULL; + } else { +#if TEST_ACCESS_VIOLATIONS + test_access_violation(&desc); +#endif // TEST_ACCESS_VIOLATIONS + log_mdesc(info, &desc, "+++ <libc_pid=%03u, pid=%03u> malloc(%u) -> ", + malloc_pid, getpid(), bytes); + return mallocdesc_user_ptr(&desc); + } +} + +/* This routine serves as entry point for 'malloc'. + * Primary responsibility of this routine is to free requested memory, and + * report free block to the emulator. + */ +void +qemu_instrumented_free(void* mem) +{ + MallocDesc desc; + + if (mem == NULL) { + // Just let go NULL free + dlfree(mem); + return; + } + + // Query emulator for the freeing block information. + if (query_qemu_malloc_info(mem, &desc, 1)) { + error_log("<libc_pid=%03u, pid=%03u>: free(%p) query_info failed.", + malloc_pid, getpid(), mem); + return; + } + +#if TEST_ACCESS_VIOLATIONS + test_access_violation(&desc); +#endif // TEST_ACCESS_VIOLATIONS + + /* Make sure that pointer that's being freed matches what we expect + * for this memory block. Note that this violation should be already + * caught in the emulator. */ + if (mem != mallocdesc_user_ptr(&desc)) { + log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: free(%p) is invalid for ", + malloc_pid, getpid(), mem); + return; + } + + // Fire up event in the emulator and free block that was actually allocated. + if (notify_qemu_free(mem)) { + log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: free(%p) notify_free failed for ", + malloc_pid, getpid(), mem); + } else { + log_mdesc(info, &desc, "--- <libc_pid=%03u, pid=%03u> free(%p) -> ", + malloc_pid, getpid(), mem); + dlfree(desc.ptr); + } +} + +/* This routine serves as entry point for 'calloc'. + * This routine behaves similarly to qemu_instrumented_malloc. + */ +void* +qemu_instrumented_calloc(size_t n_elements, size_t elem_size) +{ + MallocDesc desc; + void* ret; + size_t total_size; + size_t total_elements; + + if (n_elements == 0 || elem_size == 0) { + // Just let go zero bytes allocation. + info_log("::: <libc_pid=%03u, pid=%03u>: Zero calloc redir to malloc", + malloc_pid, getpid()); + return qemu_instrumented_malloc(0); + } + + /* Fail on overflow - just to be safe even though this code runs only + * within the debugging C library, not the production one */ + if (n_elements && MAX_SIZE_T / n_elements < elem_size) { + return NULL; + } + + /* Calculating prefix size. The trick here is to make sure that + * first element (returned to the caller) is properly aligned. */ + if (DEFAULT_PREFIX_SIZE >= elem_size) { + /* If default alignment is bigger than element size, we will + * set our prefix size to the default alignment size. */ + desc.prefix_size = DEFAULT_PREFIX_SIZE; + /* For the suffix we will use whatever bytes remain from the prefix + * allocation size, aligned to the size of an element, plus the usual + * default suffix size. */ + desc.suffix_size = (DEFAULT_PREFIX_SIZE % elem_size) + + DEFAULT_SUFFIX_SIZE; + } else { + /* Make sure that prefix, and suffix sizes is at least elem_size, + * and first element returned to the caller is properly aligned. */ + desc.prefix_size = elem_size + DEFAULT_PREFIX_SIZE - 1; + desc.prefix_size &= ~(malloc_alignment - 1); + desc.suffix_size = DEFAULT_SUFFIX_SIZE; + } + desc.requested_bytes = n_elements * elem_size; + total_size = desc.requested_bytes + desc.prefix_size + desc.suffix_size; + total_elements = total_size / elem_size; + total_size %= elem_size; + if (total_size != 0) { + // Add extra to the suffix area. + total_elements++; + desc.suffix_size += (elem_size - total_size); + } + desc.ptr = dlcalloc(total_elements, elem_size); + if (desc.ptr == NULL) { + error_log("<libc_pid=%03u, pid=%03u> calloc: dlcalloc(%u(%u), %u) (prx=%u, sfx=%u) failed.", + malloc_pid, getpid(), n_elements, total_elements, elem_size, + desc.prefix_size, desc.suffix_size); + return NULL; + } + + if (notify_qemu_malloc(&desc)) { + log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: calloc(%u(%u), %u): notify_malloc failed for ", + malloc_pid, getpid(), n_elements, total_elements, elem_size); + dlfree(desc.ptr); + return NULL; + } else { +#if TEST_ACCESS_VIOLATIONS + test_access_violation(&desc); +#endif // TEST_ACCESS_VIOLATIONS + log_mdesc(info, &desc, "### <libc_pid=%03u, pid=%03u> calloc(%u(%u), %u) -> ", + malloc_pid, getpid(), n_elements, total_elements, elem_size); + return mallocdesc_user_ptr(&desc); + } +} + +/* This routine serves as entry point for 'realloc'. + * This routine behaves similarly to qemu_instrumented_free + + * qemu_instrumented_malloc. Note that this modifies behavior of "shrinking" an + * allocation, but overall it doesn't seem to matter, as caller of realloc + * should not expect that pointer returned after shrinking will remain the same. + */ +void* +qemu_instrumented_realloc(void* mem, size_t bytes) +{ + MallocDesc new_desc; + MallocDesc cur_desc; + size_t to_copy; + void* ret; + + if (mem == NULL) { + // Nothing to realloc. just do regular malloc. + info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %u) redir to malloc", + malloc_pid, getpid(), mem, bytes); + return qemu_instrumented_malloc(bytes); + } + + if (bytes == 0) { + // This is a "free" condition. + info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %u) redir to free and malloc", + malloc_pid, getpid(), mem, bytes); + qemu_instrumented_free(mem); + + // This is what dlrealloc does for a "free" realloc. + return NULL; + } + + // Query emulator for the reallocating block information. + if (query_qemu_malloc_info(mem, &cur_desc, 2)) { + // Note that this violation should be already caught in the emulator. + error_log("<libc_pid=%03u, pid=%03u>: realloc(%p, %u) query_info failed.", + malloc_pid, getpid(), mem, bytes); + return NULL; + } + +#if TEST_ACCESS_VIOLATIONS + test_access_violation(&cur_desc); +#endif // TEST_ACCESS_VIOLATIONS + + /* Make sure that reallocating pointer value is what we would expect + * for this memory block. Note that this violation should be already caught + * in the emulator.*/ + if (mem != mallocdesc_user_ptr(&cur_desc)) { + log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %u) is invalid for ", + malloc_pid, getpid(), mem, bytes); + return NULL; + } + + /* TODO: We're a bit inefficient here, always allocating new block from + * the heap. If this realloc shrinks current buffer, we can just do the + * shrinking "in place", adjusting suffix_size in the allocation descriptor + * for this block that is stored in the emulator. */ + + // Initialize descriptor for the new block. + new_desc.prefix_size = DEFAULT_PREFIX_SIZE; + new_desc.requested_bytes = bytes; + new_desc.suffix_size = DEFAULT_SUFFIX_SIZE; + new_desc.ptr = dlmalloc(mallocdesc_alloc_size(&new_desc)); + if (new_desc.ptr == NULL) { + log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %u): dlmalloc(%u) failed on ", + malloc_pid, getpid(), mem, bytes, + mallocdesc_alloc_size(&new_desc)); + return NULL; + } + ret = mallocdesc_user_ptr(&new_desc); + + // Copy user data from old block to the new one. + to_copy = bytes < cur_desc.requested_bytes ? bytes : + cur_desc.requested_bytes; + if (to_copy != 0) { + memcpy(ret, mallocdesc_user_ptr(&cur_desc), to_copy); + } + + // Register new block with emulator. + if(notify_qemu_malloc(&new_desc)) { + log_mdesc(error, &new_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %u) notify_malloc failed -> ", + malloc_pid, getpid(), mem, bytes); + log_mdesc(error, &cur_desc, " <- "); + dlfree(new_desc.ptr); + return NULL; + } + +#if TEST_ACCESS_VIOLATIONS + test_access_violation(&new_desc); +#endif // TEST_ACCESS_VIOLATIONS + + // Free old block. + if (notify_qemu_free(mem)) { + log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %u): notify_free failed for ", + malloc_pid, getpid(), mem, bytes); + /* Since we registered new decriptor with the emulator, we need + * to unregister it before freeing newly allocated block. */ + notify_qemu_free(mallocdesc_user_ptr(&new_desc)); + dlfree(new_desc.ptr); + return NULL; + } + dlfree(cur_desc.ptr); + + log_mdesc(info, &new_desc, "=== <libc_pid=%03u, pid=%03u>: realloc(%p, %u) -> ", + malloc_pid, getpid(), mem, bytes); + log_mdesc(info, &cur_desc, " <- "); + + return ret; +} + +/* This routine serves as entry point for 'memalign'. + * This routine behaves similarly to qemu_instrumented_malloc. + */ +void* +qemu_instrumented_memalign(size_t alignment, size_t bytes) +{ + MallocDesc desc; + + if (bytes == 0) { + // Just let go zero bytes allocation. + info_log("::: <libc_pid=%03u, pid=%03u>: memalign(%X, %u) redir to malloc", + malloc_pid, getpid(), alignment, bytes); + return qemu_instrumented_malloc(0); + } + + /* Prefix size for aligned allocation must be equal to the alignment used + * for allocation in order to ensure proper alignment of the returned + * pointer, in case that alignment requirement is greater than prefix + * size. */ + desc.prefix_size = alignment > DEFAULT_PREFIX_SIZE ? alignment : + DEFAULT_PREFIX_SIZE; + desc.requested_bytes = bytes; + desc.suffix_size = DEFAULT_SUFFIX_SIZE; + desc.ptr = dlmemalign(desc.prefix_size, mallocdesc_alloc_size(&desc)); + if (desc.ptr == NULL) { + error_log("<libc_pid=%03u, pid=%03u> memalign(%X, %u): dlmalloc(%u) failed.", + malloc_pid, getpid(), alignment, bytes, + mallocdesc_alloc_size(&desc)); + return NULL; + } + if (notify_qemu_malloc(&desc)) { + log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: memalign(%X, %u): notify_malloc failed for ", + malloc_pid, getpid(), alignment, bytes); + dlfree(desc.ptr); + return NULL; + } + +#if TEST_ACCESS_VIOLATIONS + test_access_violation(&desc); +#endif // TEST_ACCESS_VIOLATIONS + + log_mdesc(info, &desc, "@@@ <libc_pid=%03u, pid=%03u> memalign(%X, %u) -> ", + malloc_pid, getpid(), alignment, bytes); + return mallocdesc_user_ptr(&desc); +} diff --git a/libc/bionic/pthread.c b/libc/bionic/pthread.c index 7d4056d..ae44b06 100644 --- a/libc/bionic/pthread.c +++ b/libc/bionic/pthread.c @@ -43,12 +43,16 @@ #include <memory.h> #include <assert.h> #include <malloc.h> +#include <linux/futex.h> extern int __pthread_clone(int (*fn)(void*), void *child_stack, int flags, void *arg); extern void _exit_with_stack_teardown(void * stackBase, int stackSize, int retCode); extern void _exit_thread(int retCode); extern int __set_errno(int); +#define __likely(cond) __builtin_expect(!!(cond), 1) +#define __unlikely(cond) __builtin_expect(!!(cond), 0) + void _thread_created_hook(pid_t thread_id) __attribute__((noinline)); #define PTHREAD_ATTR_FLAG_DETACHED 0x00000001 @@ -711,6 +715,21 @@ int pthread_setschedparam(pthread_t thid, int policy, int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout); int __futex_wake(volatile void *ftx, int count); +int __futex_syscall3(volatile void *ftx, int op, int val); +int __futex_syscall4(volatile void *ftx, int op, int val, const struct timespec *timeout); + +#ifndef FUTEX_PRIVATE_FLAG +#define FUTEX_PRIVATE_FLAG 128 +#endif + +#ifndef FUTEX_WAIT_PRIVATE +#define FUTEX_WAIT_PRIVATE (FUTEX_WAIT|FUTEX_PRIVATE_FLAG) +#endif + +#ifndef FUTEX_WAKE_PRIVATE +#define FUTEX_WAKE_PRIVATE (FUTEX_WAKE|FUTEX_PRIVATE_FLAG) +#endif + // mutex lock states // // 0: unlocked @@ -722,7 +741,8 @@ int __futex_wake(volatile void *ftx, int count); * bits: name description * 31-16 tid owner thread's kernel id (recursive and errorcheck only) * 15-14 type mutex type - * 13-2 counter counter of recursive mutexes + * 13 shared process-shared flag + * 12-2 counter counter of recursive mutexes * 1-0 state lock state (0, 1 or 2) */ @@ -736,9 +756,17 @@ int __futex_wake(volatile void *ftx, int count); #define MUTEX_TYPE_ERRORCHECK 0x8000 #define MUTEX_COUNTER_SHIFT 2 -#define MUTEX_COUNTER_MASK 0x3ffc - +#define MUTEX_COUNTER_MASK 0x1ffc +#define MUTEX_SHARED_MASK 0x2000 +/* a mutex attribute holds the following fields + * + * bits: name description + * 0-3 type type of mutex + * 4 shared process-shared flag + */ +#define MUTEXATTR_TYPE_MASK 0x000f +#define MUTEXATTR_SHARED_MASK 0x0010 int pthread_mutexattr_init(pthread_mutexattr_t *attr) @@ -763,10 +791,14 @@ int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) { - if (attr && *attr >= PTHREAD_MUTEX_NORMAL && - *attr <= PTHREAD_MUTEX_ERRORCHECK ) { - *type = *attr; - return 0; + if (attr) { + int atype = (*attr & MUTEXATTR_TYPE_MASK); + + if (atype >= PTHREAD_MUTEX_NORMAL && + atype <= PTHREAD_MUTEX_ERRORCHECK) { + *type = atype; + return 0; + } } return EINVAL; } @@ -775,7 +807,7 @@ int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) { if (attr && type >= PTHREAD_MUTEX_NORMAL && type <= PTHREAD_MUTEX_ERRORCHECK ) { - *attr = type; + *attr = (*attr & ~MUTEXATTR_TYPE_MASK) | type; return 0; } return EINVAL; @@ -790,54 +822,70 @@ int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) switch (pshared) { case PTHREAD_PROCESS_PRIVATE: + *attr &= ~MUTEXATTR_SHARED_MASK; + return 0; + case PTHREAD_PROCESS_SHARED: /* our current implementation of pthread actually supports shared * mutexes but won't cleanup if a process dies with the mutex held. * Nevertheless, it's better than nothing. Shared mutexes are used * by surfaceflinger and audioflinger. */ + *attr |= MUTEXATTR_SHARED_MASK; return 0; } - - return ENOTSUP; + return EINVAL; } int pthread_mutexattr_getpshared(pthread_mutexattr_t *attr, int *pshared) { - if (!attr) + if (!attr || !pshared) return EINVAL; - *pshared = PTHREAD_PROCESS_PRIVATE; + *pshared = (*attr & MUTEXATTR_SHARED_MASK) ? PTHREAD_PROCESS_SHARED + : PTHREAD_PROCESS_PRIVATE; return 0; } int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) { - if ( mutex ) { - if (attr == NULL) { - mutex->value = MUTEX_TYPE_NORMAL; - return 0; - } - switch ( *attr ) { - case PTHREAD_MUTEX_NORMAL: - mutex->value = MUTEX_TYPE_NORMAL; - return 0; + int value = 0; - case PTHREAD_MUTEX_RECURSIVE: - mutex->value = MUTEX_TYPE_RECURSIVE; - return 0; + if (mutex == NULL) + return EINVAL; - case PTHREAD_MUTEX_ERRORCHECK: - mutex->value = MUTEX_TYPE_ERRORCHECK; - return 0; - } + if (__likely(attr == NULL)) { + mutex->value = MUTEX_TYPE_NORMAL; + return 0; } - return EINVAL; + + if ((*attr & MUTEXATTR_SHARED_MASK) != 0) + value |= MUTEX_SHARED_MASK; + + switch (*attr & MUTEXATTR_TYPE_MASK) { + case PTHREAD_MUTEX_NORMAL: + value |= MUTEX_TYPE_NORMAL; + break; + case PTHREAD_MUTEX_RECURSIVE: + value |= MUTEX_TYPE_RECURSIVE; + break; + case PTHREAD_MUTEX_ERRORCHECK: + value |= MUTEX_TYPE_ERRORCHECK; + break; + default: + return EINVAL; + } + + mutex->value = value; + return 0; } int pthread_mutex_destroy(pthread_mutex_t *mutex) { + if (__unlikely(mutex == NULL)) + return EINVAL; + mutex->value = 0xdead10cc; return 0; } @@ -858,13 +906,15 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex) static __inline__ void _normal_lock(pthread_mutex_t* mutex) { + /* We need to preserve the shared flag during operations */ + int shared = mutex->value & MUTEX_SHARED_MASK; /* * The common case is an unlocked mutex, so we begin by trying to * change the lock's state from 0 to 1. __atomic_cmpxchg() returns 0 * if it made the swap successfully. If the result is nonzero, this * lock is already held by another thread. */ - if (__atomic_cmpxchg(0, 1, &mutex->value ) != 0) { + if (__atomic_cmpxchg(shared|0, shared|1, &mutex->value ) != 0) { /* * We want to go to sleep until the mutex is available, which * requires promoting it to state 2. We need to swap in the new @@ -881,8 +931,10 @@ _normal_lock(pthread_mutex_t* mutex) * that the mutex is in state 2 when we go to sleep on it, which * guarantees a wake-up call. */ - while (__atomic_swap(2, &mutex->value ) != 0) - __futex_wait(&mutex->value, 2, 0); + int wait_op = shared ? FUTEX_WAIT : FUTEX_WAIT_PRIVATE; + + while (__atomic_swap(shared|2, &mutex->value ) != (shared|0)) + __futex_syscall4(&mutex->value, wait_op, shared|2, 0); } } @@ -893,12 +945,16 @@ _normal_lock(pthread_mutex_t* mutex) static __inline__ void _normal_unlock(pthread_mutex_t* mutex) { + /* We need to preserve the shared flag during operations */ + int shared = mutex->value & MUTEX_SHARED_MASK; + /* - * The mutex value will be 1 or (rarely) 2. We use an atomic decrement + * The mutex state will be 1 or (rarely) 2. We use an atomic decrement * to release the lock. __atomic_dec() returns the previous value; * if it wasn't 1 we have to do some additional work. */ - if (__atomic_dec(&mutex->value) != 1) { + if (__atomic_dec(&mutex->value) != (shared|1)) { + int wake_op = shared ? FUTEX_WAKE : FUTEX_WAKE_PRIVATE; /* * Start by releasing the lock. The decrement changed it from * "contended lock" to "uncontended lock", which means we still @@ -913,7 +969,7 @@ _normal_unlock(pthread_mutex_t* mutex) * _normal_lock(), because the __futex_wait() call there will * return immediately if the mutex value isn't 2. */ - mutex->value = 0; + mutex->value = shared; /* * Wake up one waiting thread. We don't know which thread will be @@ -936,7 +992,7 @@ _normal_unlock(pthread_mutex_t* mutex) * Either way we have correct behavior and nobody is orphaned on * the wait queue. */ - __futex_wake(&mutex->value, 1); + __futex_syscall3(&mutex->value, wake_op, 1); } } @@ -945,182 +1001,188 @@ static pthread_mutex_t __recursive_lock = PTHREAD_MUTEX_INITIALIZER; static void _recursive_lock(void) { - _normal_lock( &__recursive_lock); + _normal_lock(&__recursive_lock); } static void _recursive_unlock(void) { - _normal_unlock( &__recursive_lock ); + _normal_unlock(&__recursive_lock ); } -#define __likely(cond) __builtin_expect(!!(cond), 1) -#define __unlikely(cond) __builtin_expect(!!(cond), 0) - int pthread_mutex_lock(pthread_mutex_t *mutex) { - if (__likely(mutex != NULL)) - { - int mtype = (mutex->value & MUTEX_TYPE_MASK); + int mtype, tid, new_lock_type, shared, wait_op; - if ( __likely(mtype == MUTEX_TYPE_NORMAL) ) { - _normal_lock(mutex); - } - else - { - int tid = __get_thread()->kernel_id; + if (__unlikely(mutex == NULL)) + return EINVAL; - if ( tid == MUTEX_OWNER(mutex) ) - { - int oldv, counter; + mtype = (mutex->value & MUTEX_TYPE_MASK); + shared = (mutex->value & MUTEX_SHARED_MASK); - if (mtype == MUTEX_TYPE_ERRORCHECK) { - /* trying to re-lock a mutex we already acquired */ - return EDEADLK; - } - /* - * We own the mutex, but other threads are able to change - * the contents (e.g. promoting it to "contended"), so we - * need to hold the global lock. - */ - _recursive_lock(); - oldv = mutex->value; - counter = (oldv + (1 << MUTEX_COUNTER_SHIFT)) & MUTEX_COUNTER_MASK; - mutex->value = (oldv & ~MUTEX_COUNTER_MASK) | counter; - _recursive_unlock(); - } - else - { - /* - * If the new lock is available immediately, we grab it in - * the "uncontended" state. - */ - int new_lock_type = 1; - - for (;;) { - int oldv; - - _recursive_lock(); - oldv = mutex->value; - if (oldv == mtype) { /* uncontended released lock => 1 or 2 */ - mutex->value = ((tid << 16) | mtype | new_lock_type); - } else if ((oldv & 3) == 1) { /* locked state 1 => state 2 */ - oldv ^= 3; - mutex->value = oldv; - } - _recursive_unlock(); - - if (oldv == mtype) - break; - - /* - * The lock was held, possibly contended by others. From - * now on, if we manage to acquire the lock, we have to - * assume that others are still contending for it so that - * we'll wake them when we unlock it. - */ - new_lock_type = 2; - - __futex_wait( &mutex->value, oldv, 0 ); - } - } + /* Handle normal case first */ + if ( __likely(mtype == MUTEX_TYPE_NORMAL) ) { + _normal_lock(mutex); + return 0; + } + + /* Do we already own this recursive or error-check mutex ? */ + tid = __get_thread()->kernel_id; + if ( tid == MUTEX_OWNER(mutex) ) + { + int oldv, counter; + + if (mtype == MUTEX_TYPE_ERRORCHECK) { + /* trying to re-lock a mutex we already acquired */ + return EDEADLK; } + /* + * We own the mutex, but other threads are able to change + * the contents (e.g. promoting it to "contended"), so we + * need to hold the global lock. + */ + _recursive_lock(); + oldv = mutex->value; + counter = (oldv + (1 << MUTEX_COUNTER_SHIFT)) & MUTEX_COUNTER_MASK; + mutex->value = (oldv & ~MUTEX_COUNTER_MASK) | counter; + _recursive_unlock(); return 0; } - return EINVAL; + + /* We don't own the mutex, so try to get it. + * + * First, we try to change its state from 0 to 1, if this + * doesn't work, try to change it to state 2. + */ + new_lock_type = 1; + + /* compute futex wait opcode and restore shared flag in mtype */ + wait_op = shared ? FUTEX_WAIT : FUTEX_WAIT_PRIVATE; + mtype |= shared; + + for (;;) { + int oldv; + + _recursive_lock(); + oldv = mutex->value; + if (oldv == mtype) { /* uncontended released lock => 1 or 2 */ + mutex->value = ((tid << 16) | mtype | new_lock_type); + } else if ((oldv & 3) == 1) { /* locked state 1 => state 2 */ + oldv ^= 3; + mutex->value = oldv; + } + _recursive_unlock(); + + if (oldv == mtype) + break; + + /* + * The lock was held, possibly contended by others. From + * now on, if we manage to acquire the lock, we have to + * assume that others are still contending for it so that + * we'll wake them when we unlock it. + */ + new_lock_type = 2; + + __futex_syscall4(&mutex->value, wait_op, oldv, NULL); + } + return 0; } int pthread_mutex_unlock(pthread_mutex_t *mutex) { - if (__likely(mutex != NULL)) - { - int mtype = (mutex->value & MUTEX_TYPE_MASK); + int mtype, tid, oldv, shared; - if (__likely(mtype == MUTEX_TYPE_NORMAL)) { - _normal_unlock(mutex); - } - else - { - int tid = __get_thread()->kernel_id; + if (__unlikely(mutex == NULL)) + return EINVAL; - if ( tid == MUTEX_OWNER(mutex) ) - { - int oldv; - - _recursive_lock(); - oldv = mutex->value; - if (oldv & MUTEX_COUNTER_MASK) { - mutex->value = oldv - (1 << MUTEX_COUNTER_SHIFT); - oldv = 0; - } else { - mutex->value = mtype; - } - _recursive_unlock(); + mtype = (mutex->value & MUTEX_TYPE_MASK); + shared = (mutex->value & MUTEX_SHARED_MASK); - if ((oldv & 3) == 2) - __futex_wake( &mutex->value, 1 ); - } - else { - /* trying to unlock a lock we do not own */ - return EPERM; - } - } + /* Handle common case first */ + if (__likely(mtype == MUTEX_TYPE_NORMAL)) { + _normal_unlock(mutex); return 0; } - return EINVAL; + + /* Do we already own this recursive or error-check mutex ? */ + tid = __get_thread()->kernel_id; + if ( tid != MUTEX_OWNER(mutex) ) + return EPERM; + + /* We do, decrement counter or release the mutex if it is 0 */ + _recursive_lock(); + oldv = mutex->value; + if (oldv & MUTEX_COUNTER_MASK) { + mutex->value = oldv - (1 << MUTEX_COUNTER_SHIFT); + oldv = 0; + } else { + mutex->value = shared | mtype; + } + _recursive_unlock(); + + /* Wake one waiting thread, if any */ + if ((oldv & 3) == 2) { + int wake_op = shared ? FUTEX_WAKE : FUTEX_WAKE_PRIVATE; + __futex_syscall3(&mutex->value, wake_op, 1); + } + return 0; } int pthread_mutex_trylock(pthread_mutex_t *mutex) { - if (__likely(mutex != NULL)) + int mtype, tid, oldv, shared; + + if (__unlikely(mutex == NULL)) + return EINVAL; + + mtype = (mutex->value & MUTEX_TYPE_MASK); + shared = (mutex->value & MUTEX_SHARED_MASK); + + /* Handle common case first */ + if ( __likely(mtype == MUTEX_TYPE_NORMAL) ) { - int mtype = (mutex->value & MUTEX_TYPE_MASK); + if (__atomic_cmpxchg(shared|0, shared|1, &mutex->value) == 0) + return 0; - if ( __likely(mtype == MUTEX_TYPE_NORMAL) ) - { - if (__atomic_cmpxchg(0, 1, &mutex->value) == 0) - return 0; + return EBUSY; + } - return EBUSY; - } - else - { - int tid = __get_thread()->kernel_id; - int oldv; + /* Do we already own this recursive or error-check mutex ? */ + tid = __get_thread()->kernel_id; + if ( tid == MUTEX_OWNER(mutex) ) + { + int counter; - if ( tid == MUTEX_OWNER(mutex) ) - { - int oldv, counter; + if (mtype == MUTEX_TYPE_ERRORCHECK) { + /* already locked by ourselves */ + return EDEADLK; + } - if (mtype == MUTEX_TYPE_ERRORCHECK) { - /* already locked by ourselves */ - return EDEADLK; - } + _recursive_lock(); + oldv = mutex->value; + counter = (oldv + (1 << MUTEX_COUNTER_SHIFT)) & MUTEX_COUNTER_MASK; + mutex->value = (oldv & ~MUTEX_COUNTER_MASK) | counter; + _recursive_unlock(); + return 0; + } - _recursive_lock(); - oldv = mutex->value; - counter = (oldv + (1 << MUTEX_COUNTER_SHIFT)) & MUTEX_COUNTER_MASK; - mutex->value = (oldv & ~MUTEX_COUNTER_MASK) | counter; - _recursive_unlock(); - return 0; - } + /* Restore sharing bit in mtype */ + mtype |= shared; - /* try to lock it */ - _recursive_lock(); - oldv = mutex->value; - if (oldv == mtype) /* uncontended released lock => state 1 */ - mutex->value = ((tid << 16) | mtype | 1); - _recursive_unlock(); + /* Try to lock it, just once. */ + _recursive_lock(); + oldv = mutex->value; + if (oldv == mtype) /* uncontended released lock => state 1 */ + mutex->value = ((tid << 16) | mtype | 1); + _recursive_unlock(); - if (oldv != mtype) - return EBUSY; + if (oldv != mtype) + return EBUSY; - return 0; - } - } - return EINVAL; + return 0; } @@ -1163,100 +1225,152 @@ int pthread_mutex_lock_timeout_np(pthread_mutex_t *mutex, unsigned msecs) clockid_t clock = CLOCK_MONOTONIC; struct timespec abstime; struct timespec ts; + int mtype, tid, oldv, new_lock_type, shared, wait_op; /* compute absolute expiration time */ __timespec_to_relative_msec(&abstime, msecs, clock); - if (__likely(mutex != NULL)) - { - int mtype = (mutex->value & MUTEX_TYPE_MASK); + if (__unlikely(mutex == NULL)) + return EINVAL; - if ( __likely(mtype == MUTEX_TYPE_NORMAL) ) - { - /* fast path for unconteded lock */ - if (__atomic_cmpxchg(0, 1, &mutex->value) == 0) - return 0; + mtype = (mutex->value & MUTEX_TYPE_MASK); + shared = (mutex->value & MUTEX_SHARED_MASK); - /* loop while needed */ - while (__atomic_swap(2, &mutex->value) != 0) { - if (__timespec_to_absolute(&ts, &abstime, clock) < 0) - return EBUSY; + /* Handle common case first */ + if ( __likely(mtype == MUTEX_TYPE_NORMAL) ) + { + int wait_op = shared ? FUTEX_WAIT : FUTEX_WAIT_PRIVATE; - __futex_wait(&mutex->value, 2, &ts); - } + /* fast path for unconteded lock */ + if (__atomic_cmpxchg(shared|0, shared|1, &mutex->value) == 0) return 0; + + /* loop while needed */ + while (__atomic_swap(shared|2, &mutex->value) != (shared|0)) { + if (__timespec_to_absolute(&ts, &abstime, clock) < 0) + return EBUSY; + + __futex_syscall4(&mutex->value, wait_op, shared|2, &ts); } - else - { - int tid = __get_thread()->kernel_id; - int oldv; + return 0; + } - if ( tid == MUTEX_OWNER(mutex) ) - { - int oldv, counter; + /* Do we already own this recursive or error-check mutex ? */ + tid = __get_thread()->kernel_id; + if ( tid == MUTEX_OWNER(mutex) ) + { + int oldv, counter; - if (mtype == MUTEX_TYPE_ERRORCHECK) { - /* already locked by ourselves */ - return EDEADLK; - } + if (mtype == MUTEX_TYPE_ERRORCHECK) { + /* already locked by ourselves */ + return EDEADLK; + } - _recursive_lock(); - oldv = mutex->value; - counter = (oldv + (1 << MUTEX_COUNTER_SHIFT)) & MUTEX_COUNTER_MASK; - mutex->value = (oldv & ~MUTEX_COUNTER_MASK) | counter; - _recursive_unlock(); - return 0; - } - else - { - /* - * If the new lock is available immediately, we grab it in - * the "uncontended" state. - */ - int new_lock_type = 1; - - for (;;) { - int oldv; - struct timespec ts; - - _recursive_lock(); - oldv = mutex->value; - if (oldv == mtype) { /* uncontended released lock => 1 or 2 */ - mutex->value = ((tid << 16) | mtype | new_lock_type); - } else if ((oldv & 3) == 1) { /* locked state 1 => state 2 */ - oldv ^= 3; - mutex->value = oldv; - } - _recursive_unlock(); - - if (oldv == mtype) - break; - - /* - * The lock was held, possibly contended by others. From - * now on, if we manage to acquire the lock, we have to - * assume that others are still contending for it so that - * we'll wake them when we unlock it. - */ - new_lock_type = 2; - - if (__timespec_to_absolute(&ts, &abstime, clock) < 0) - return EBUSY; - - __futex_wait( &mutex->value, oldv, &ts ); - } - return 0; - } + _recursive_lock(); + oldv = mutex->value; + counter = (oldv + (1 << MUTEX_COUNTER_SHIFT)) & MUTEX_COUNTER_MASK; + mutex->value = (oldv & ~MUTEX_COUNTER_MASK) | counter; + _recursive_unlock(); + return 0; + } + + /* We don't own the mutex, so try to get it. + * + * First, we try to change its state from 0 to 1, if this + * doesn't work, try to change it to state 2. + */ + new_lock_type = 1; + + /* Compute wait op and restore sharing bit in mtype */ + wait_op = shared ? FUTEX_WAIT : FUTEX_WAIT_PRIVATE; + mtype |= shared; + + for (;;) { + int oldv; + struct timespec ts; + + _recursive_lock(); + oldv = mutex->value; + if (oldv == mtype) { /* uncontended released lock => 1 or 2 */ + mutex->value = ((tid << 16) | mtype | new_lock_type); + } else if ((oldv & 3) == 1) { /* locked state 1 => state 2 */ + oldv ^= 3; + mutex->value = oldv; } + _recursive_unlock(); + + if (oldv == mtype) + break; + + /* + * The lock was held, possibly contended by others. From + * now on, if we manage to acquire the lock, we have to + * assume that others are still contending for it so that + * we'll wake them when we unlock it. + */ + new_lock_type = 2; + + if (__timespec_to_absolute(&ts, &abstime, clock) < 0) + return EBUSY; + + __futex_syscall4(&mutex->value, wait_op, oldv, &ts); } - return EINVAL; + return 0; } +int pthread_condattr_init(pthread_condattr_t *attr) +{ + if (attr == NULL) + return EINVAL; + + *attr = PTHREAD_PROCESS_PRIVATE; + return 0; +} + +int pthread_condattr_getpshared(pthread_condattr_t *attr, int *pshared) +{ + if (attr == NULL || pshared == NULL) + return EINVAL; + + *pshared = *attr; + return 0; +} + +int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) +{ + if (attr == NULL) + return EINVAL; + + if (pshared != PTHREAD_PROCESS_SHARED && + pshared != PTHREAD_PROCESS_PRIVATE) + return EINVAL; + + *attr = pshared; + return 0; +} + +int pthread_condattr_destroy(pthread_condattr_t *attr) +{ + if (attr == NULL) + return EINVAL; + + *attr = 0xdeada11d; + return 0; +} + +/* We use one bit in condition variable values as the 'shared' flag + * The rest is a counter. + */ +#define COND_SHARED_MASK 0x0001 +#define COND_COUNTER_INCREMENT 0x0002 +#define COND_COUNTER_MASK (~COND_SHARED_MASK) + +#define COND_IS_SHARED(c) (((c)->value & COND_SHARED_MASK) != 0) /* XXX *technically* there is a race condition that could allow * XXX a signal to be missed. If thread A is preempted in _wait() * XXX after unlocking the mutex and before waiting, and if other - * XXX threads call signal or broadcast UINT_MAX times (exactly), + * XXX threads call signal or broadcast UINT_MAX/2 times (exactly), * XXX before thread A is scheduled again and calls futex_wait(), * XXX then the signal will be lost. */ @@ -1264,28 +1378,61 @@ int pthread_mutex_lock_timeout_np(pthread_mutex_t *mutex, unsigned msecs) int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) { + if (cond == NULL) + return EINVAL; + cond->value = 0; + + if (attr != NULL && *attr == PTHREAD_PROCESS_SHARED) + cond->value |= COND_SHARED_MASK; + return 0; } int pthread_cond_destroy(pthread_cond_t *cond) { + if (cond == NULL) + return EINVAL; + cond->value = 0xdeadc04d; return 0; } -int pthread_cond_broadcast(pthread_cond_t *cond) +/* This function is used by pthread_cond_broadcast and + * pthread_cond_signal to atomically decrement the counter + * then wake-up 'counter' threads. + */ +static int +__pthread_cond_pulse(pthread_cond_t *cond, int counter) { - __atomic_dec(&cond->value); - __futex_wake(&cond->value, INT_MAX); + long flags; + int wake_op; + + if (__unlikely(cond == NULL)) + return EINVAL; + + flags = (cond->value & ~COND_COUNTER_MASK); + for (;;) { + long oldval = cond->value; + long newval = ((oldval - COND_COUNTER_INCREMENT) & COND_COUNTER_MASK) + | flags; + if (__atomic_cmpxchg(oldval, newval, &cond->value) == 0) + break; + } + + wake_op = COND_IS_SHARED(cond) ? FUTEX_WAKE : FUTEX_WAKE_PRIVATE; + __futex_syscall3(&cond->value, wake_op, counter); return 0; } +int pthread_cond_broadcast(pthread_cond_t *cond) +{ + return __pthread_cond_pulse(cond, INT_MAX); +} + int pthread_cond_signal(pthread_cond_t *cond) { - __atomic_dec(&cond->value); - __futex_wake(&cond->value, 1); - return 0; + return __pthread_cond_pulse(cond, 1); } int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) @@ -1299,9 +1446,10 @@ int __pthread_cond_timedwait_relative(pthread_cond_t *cond, { int status; int oldvalue = cond->value; + int wait_op = COND_IS_SHARED(cond) ? FUTEX_WAIT : FUTEX_WAIT_PRIVATE; pthread_mutex_unlock(mutex); - status = __futex_wait(&cond->value, oldvalue, reltime); + status = __futex_syscall4(&cond->value, wait_op, oldvalue, reltime); pthread_mutex_lock(mutex); if (status == (-ETIMEDOUT)) return ETIMEDOUT; @@ -1687,7 +1835,17 @@ extern int __rt_sigprocmask(int, const sigset_t *, sigset_t *, size_t); int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) { - return __rt_sigprocmask(how, set, oset, _NSIG / 8); + /* pthread_sigmask must return the error code, but the syscall + * will set errno instead and return 0/-1 + */ + int ret, old_errno = errno; + + ret = __rt_sigprocmask(how, set, oset, _NSIG / 8); + if (ret < 0) + ret = errno; + + errno = old_errno; + return ret; } diff --git a/libc/bionic/semaphore.c b/libc/bionic/semaphore.c index 0c94600..84b9314 100644 --- a/libc/bionic/semaphore.c +++ b/libc/bionic/semaphore.c @@ -180,7 +180,7 @@ int sem_post(sem_t *sem) if (sem == NULL) return EINVAL; - if (__atomic_inc((volatile int*)&sem->count) == 0) + if (__atomic_inc((volatile int*)&sem->count) >= 0) __futex_wake(&sem->count, 1); return 0; @@ -196,7 +196,8 @@ int sem_trywait(sem_t *sem) if (__atomic_dec_if_positive(&sem->count) > 0) { return 0; } else { - return EAGAIN; + errno = EAGAIN; + return -1; } } diff --git a/libc/bionic/stubs.c b/libc/bionic/stubs.c index 365f21a..d495674 100644 --- a/libc/bionic/stubs.c +++ b/libc/bionic/stubs.c @@ -185,7 +185,7 @@ app_id_from_name( const char* name ) goto FAIL; id = strtoul(name+4, &end, 10); - if (id == 0 || *end != '\0') + if (*end != '\0') goto FAIL; id += AID_APP; @@ -361,6 +361,12 @@ char* ttyname(int fd) return NULL; } +int ttyname_r(int fd, char *buf, size_t buflen) +{ + fprintf(stderr, "FIX ME! implement ttyname_r() %s:%d\n", __FILE__, __LINE__); + return -ERANGE; +} + struct netent *getnetbyaddr(uint32_t net, int type) { fprintf(stderr, "FIX ME! implement %s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__); @@ -378,3 +384,20 @@ struct protoent *getprotobynumber(int proto) fprintf(stderr, "FIX ME! implement %s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__); return NULL; } + +char* getusershell(void) +{ + fprintf(stderr, "FIX ME! implement %s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__); + return NULL; +} + +void setusershell(void) +{ + fprintf(stderr, "FIX ME! implement %s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__); +} + +void endusershell(void) +{ + fprintf(stderr, "FIX ME! implement %s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__); +} + diff --git a/libc/docs/CHANGES.TXT b/libc/docs/CHANGES.TXT new file mode 100644 index 0000000..9080685 --- /dev/null +++ b/libc/docs/CHANGES.TXT @@ -0,0 +1,174 @@ +Bionic ChangeLog: +----------------- + +Differences between current and Android 2.1: + +- Add support for SH-4 CPU architecture ! + +- __atomic_swap(): use LDREX/STREX CPU instructions on ARMv6 and higher. + +- <arpa/telnet.h>: New header (declarations only, no implementation). + +- <err.h>: New header + implementation. GLibc compatibility. + +- <warn.h>: New header + implementation. GLibc compatibility. + +- <fts.h>: New header + implementation. + +- <mntent.h>: Add missing <stdio.h> include. + +- <regex.h>: New header + implementation. + +- <signal.h>: Added killpg() + +- <stdint.h>: Allow 64-bit type declarations on C99 builds. + +- <stdio.h>: Add fdprintf() and vfdprintf(). Note that GLibc provides + the confusing 'dprintf' and 'vdprintf()' functions instead. + +- <stdlib.h>: Fix ptsname_r(): the return type is int instead of char*. + The mistake comes from a GLibc man page bug (the man page listed a return + type of char*, while the implementation used int. Fixed in late 2009 only). + The Bionic implementation was incorrect. Technically, this is an ABI + breakage, but code that used this function probably never worked or + compiled properly anyway. + +- <strings.h>: Add missing <sys/types.h> include. + +- <sys/queue.h>: Added new header (no implementation - macro templates). + +- <sys/resource.h>: Add rlim_t proper definition. + +- <time64.h>: Add missing C++ header inclusion guards. + +- <unistd.h>: Add getusershell(), setusershell() and endusershell(), though + implementation are bogus. GLibc compatibility. + +- <wchar.h>: Add mbstowcs() and wcstombs() + +- add clone() implementation for ARM (x86 and SH-4 not working yet). + +- <sys/epoll.h>: <sys/system_properties.h>: Add missing C++ inclusion guards + +- fix getpwnam() and getpwgrp() to accept "app_0" as a valid user name. + +- fix sem_trywait() to return -1 and set errno to EAGAIN, instead of + returning EAGAIN directly. + +- fix sem_post() to wake up multiple threads when called rapidly in + succession. + +- DNS: partial implementation of RFC3484 (rule 1, 2, 5, 6, 8, 10 and + modified rule 9), for better address selection/sorting. + In the process, removed code that was previously used for "sortlist" + in /etc/resolv.conf. (resolv.conf is already ignored, so the latter + is a no-op for actual functionality.) + +- fix pthread_sigmask() to properly return an error code without touching + errno. Previous implementation returned -1 on error, setting errno, which + is not Posix compliant. + +- add sigaltstack() implementation for ARM. + +- <time.h>: Properly implement the 'timezone' and 'daylight' global variables + (they were not defined previously, though declared in the header). + +- <time.h>: Fix timezone management implementation to properly update + 'tm_gmtoff' field in 'struct tm' structure. + +- DNS: get rid of spurious random DNS queries when trying to resolve + an unknown domain name. Due to an initialization bug, a random DNS search + list was generated for each thread if net.dns.search is not defined. + +- <pthread.h>: Add pthread_condattr_init/destroy/setpshared/getpshared functions + to enable proper shared conditional variable initialization. + + Modify the pthread_mutex_t and pthread_cond_t implementation to use private + futexes for performance reasons. Mutexes and Condvars are no longer shareable + between processes by default anymore, unless you use PTHREAD_PROCESS_SHARED + with pthread_mutexattr_setpshared() and/or pthread_condattr_setpshared(). + +------------------------------------------------------------------------------- +Differences between Android 2.1 and 2.0.1: + +- zoneinfo: updated data tables to version 2009s + + +------------------------------------------------------------------------------- +Differences between Android 2.0.1 and 2.0: + +- abort(): ARM-specific hack to preserve the 'lr' register when abort() + is called (GCC does not preserve it by default since it thinks that + abort() never returns). This improves stack traces considerably. + + +------------------------------------------------------------------------------- +Differences between Android 2.0 and 1.6: + +- memcmp(), memcpy(): ARMv7 optimized versions. + +- pthread_mutexattr_setpshared(): implementation will not return ENOTSUP + if PTHREAD_PROCESS_SHARED is used, because our Mutex implementation can + work across multiple processes. + + *HOWEVER* it does not use "robust futexes" which means that held mutexes + *are not* automatically released by the kernel when the owner process + crashes or exits. This is only done to simplify communication between + two always-live system processes, DO NOT USE THIS IN APPLICATIONS ! + +- pthread_mutex_lock_timeout_np(): New Android-specific function to + perform a timed lock (). In case of timeout, it returns EBUSY. + +- pthread_cond_timedwait_monotonic_np(): Same as pthread_cond_timedwait() + but uses the monotonic clock(). Android-specific. + +- pthread_cond_timedwait_relative_np(): Same as pthread_cond_timedwait() + but uses a relative timeout instead. Android-specific. + +- <netinet/in.h>: Now includes <netinet/in6.h>. + +- <netinet/in6.h>: Added IPV6_JOIN_GROUP, IPV6_LEAVE_GROUP, IN6ADDR_ANY_INIT + and ipv6mr_interface definitions. + +- <time.h>: + * Add missing tzset() declaration. + * Add Android-specific strftime_tz(). + +- getaddrinfo(): + Only perform IPv6 lookup for AF_UNSPEC if we have IPv6 connectivity. + This saves one DNS query per lookup on non-IPv6 systems. + +- mktime(): Fix an infinite loop problen that appeared when switching to + GCC 4.4.0. + +- strftime(): fix incorrect handling of dates > 2038 due to 64-bit issue + in original code. + +------------------------------------------------------------------------------- +Differences between Android 1.6 and 1.5: + +- C runtime: Fix runtime initialization to be called before any static C++ + constructors. This allows these to use pthread functions properly. + +- __aeabi_atexit(): Fix implementation to properly call C++ static destructors + when the program exits (or when a shared library is unloaded). + +- <sys/stat.h>: added GLibc compatibility macros definitions: + + #define st_atimensec st_atime_nsec + #define st_mtimensec st_mtime_nsec + #define st_ctimensec st_ctime_nsec + +- getaddrinfo(): implementation will now allow numeric ports if ai_socktype is + set to ANY. This is to match the GLibc behaviour. + +- getservent(): and getservent_r() incorrectly returned the port in host-endian + order in the s_port field. It now returns it in big-endian order. + +- DNS: Allow underscore in the middle of DNS labels. While not really + standard, this extension is needed for some VPN configurations and is + supported by other operating systems. + +- DNS: Support for DNS domain search lists through the new net.dns.search + system property. The corresponding value must be a space-separated list of + domain suffixes. diff --git a/libc/include/arpa/telnet.h b/libc/include/arpa/telnet.h new file mode 100644 index 0000000..d318e08 --- /dev/null +++ b/libc/include/arpa/telnet.h @@ -0,0 +1,316 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)telnet.h 8.2 (Berkeley) 12/15/93 + */ + +#ifndef _ARPA_TELNET_H +#define _ARPA_TELNET_H 1 + +/* + * Definitions for the TELNET protocol. + */ +#define IAC 255 /* interpret as command: */ +#define DONT 254 /* you are not to use option */ +#define DO 253 /* please, you use option */ +#define WONT 252 /* I won't use option */ +#define WILL 251 /* I will use option */ +#define SB 250 /* interpret as subnegotiation */ +#define GA 249 /* you may reverse the line */ +#define EL 248 /* erase the current line */ +#define EC 247 /* erase the current character */ +#define AYT 246 /* are you there */ +#define AO 245 /* abort output--but let prog finish */ +#define IP 244 /* interrupt process--permanently */ +#define BREAK 243 /* break */ +#define DM 242 /* data mark--for connect. cleaning */ +#define NOP 241 /* nop */ +#define SE 240 /* end sub negotiation */ +#define EOR 239 /* end of record (transparent mode) */ +#define ABORT 238 /* Abort process */ +#define SUSP 237 /* Suspend process */ +#define xEOF 236 /* End of file: EOF is already used... */ + +#define SYNCH 242 /* for telfunc calls */ + +#ifdef TELCMDS +char *telcmds[] = { + "EOF", "SUSP", "ABORT", "EOR", + "SE", "NOP", "DMARK", "BRK", "IP", "AO", "AYT", "EC", + "EL", "GA", "SB", "WILL", "WONT", "DO", "DONT", "IAC", 0, +}; +#else +extern char *telcmds[]; +#endif + +#define TELCMD_FIRST xEOF +#define TELCMD_LAST IAC +#define TELCMD_OK(x) ((unsigned int)(x) <= TELCMD_LAST && \ + (unsigned int)(x) >= TELCMD_FIRST) +#define TELCMD(x) telcmds[(x)-TELCMD_FIRST] + +/* telnet options */ +#define TELOPT_BINARY 0 /* 8-bit data path */ +#define TELOPT_ECHO 1 /* echo */ +#define TELOPT_RCP 2 /* prepare to reconnect */ +#define TELOPT_SGA 3 /* suppress go ahead */ +#define TELOPT_NAMS 4 /* approximate message size */ +#define TELOPT_STATUS 5 /* give status */ +#define TELOPT_TM 6 /* timing mark */ +#define TELOPT_RCTE 7 /* remote controlled transmission and echo */ +#define TELOPT_NAOL 8 /* negotiate about output line width */ +#define TELOPT_NAOP 9 /* negotiate about output page size */ +#define TELOPT_NAOCRD 10 /* negotiate about CR disposition */ +#define TELOPT_NAOHTS 11 /* negotiate about horizontal tabstops */ +#define TELOPT_NAOHTD 12 /* negotiate about horizontal tab disposition */ +#define TELOPT_NAOFFD 13 /* negotiate about formfeed disposition */ +#define TELOPT_NAOVTS 14 /* negotiate about vertical tab stops */ +#define TELOPT_NAOVTD 15 /* negotiate about vertical tab disposition */ +#define TELOPT_NAOLFD 16 /* negotiate about output LF disposition */ +#define TELOPT_XASCII 17 /* extended ascii character set */ +#define TELOPT_LOGOUT 18 /* force logout */ +#define TELOPT_BM 19 /* byte macro */ +#define TELOPT_DET 20 /* data entry terminal */ +#define TELOPT_SUPDUP 21 /* supdup protocol */ +#define TELOPT_SUPDUPOUTPUT 22 /* supdup output */ +#define TELOPT_SNDLOC 23 /* send location */ +#define TELOPT_TTYPE 24 /* terminal type */ +#define TELOPT_EOR 25 /* end or record */ +#define TELOPT_TUID 26 /* TACACS user identification */ +#define TELOPT_OUTMRK 27 /* output marking */ +#define TELOPT_TTYLOC 28 /* terminal location number */ +#define TELOPT_3270REGIME 29 /* 3270 regime */ +#define TELOPT_X3PAD 30 /* X.3 PAD */ +#define TELOPT_NAWS 31 /* window size */ +#define TELOPT_TSPEED 32 /* terminal speed */ +#define TELOPT_LFLOW 33 /* remote flow control */ +#define TELOPT_LINEMODE 34 /* Linemode option */ +#define TELOPT_XDISPLOC 35 /* X Display Location */ +#define TELOPT_OLD_ENVIRON 36 /* Old - Environment variables */ +#define TELOPT_AUTHENTICATION 37/* Authenticate */ +#define TELOPT_ENCRYPT 38 /* Encryption option */ +#define TELOPT_NEW_ENVIRON 39 /* New - Environment variables */ +#define TELOPT_EXOPL 255 /* extended-options-list */ + + +#define NTELOPTS (1+TELOPT_NEW_ENVIRON) +#ifdef TELOPTS +const char *telopts[NTELOPTS+1] = { + "BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD", "NAME", + "STATUS", "TIMING MARK", "RCTE", "NAOL", "NAOP", + "NAOCRD", "NAOHTS", "NAOHTD", "NAOFFD", "NAOVTS", + "NAOVTD", "NAOLFD", "EXTEND ASCII", "LOGOUT", "BYTE MACRO", + "DATA ENTRY TERMINAL", "SUPDUP", "SUPDUP OUTPUT", + "SEND LOCATION", "TERMINAL TYPE", "END OF RECORD", + "TACACS UID", "OUTPUT MARKING", "TTYLOC", + "3270 REGIME", "X.3 PAD", "NAWS", "TSPEED", "LFLOW", + "LINEMODE", "XDISPLOC", "OLD-ENVIRON", "AUTHENTICATION", + "ENCRYPT", "NEW-ENVIRON", + 0, +}; +#define TELOPT_FIRST TELOPT_BINARY +#define TELOPT_LAST TELOPT_NEW_ENVIRON +#define TELOPT_OK(x) ((unsigned int)(x) <= TELOPT_LAST) +#define TELOPT(x) telopts[(x)-TELOPT_FIRST] +#endif + +/* sub-option qualifiers */ +#define TELQUAL_IS 0 /* option is... */ +#define TELQUAL_SEND 1 /* send option */ +#define TELQUAL_INFO 2 /* ENVIRON: informational version of IS */ +#define TELQUAL_REPLY 2 /* AUTHENTICATION: client version of IS */ +#define TELQUAL_NAME 3 /* AUTHENTICATION: client version of IS */ + +#define LFLOW_OFF 0 /* Disable remote flow control */ +#define LFLOW_ON 1 /* Enable remote flow control */ +#define LFLOW_RESTART_ANY 2 /* Restart output on any char */ +#define LFLOW_RESTART_XON 3 /* Restart output only on XON */ + +/* + * LINEMODE suboptions + */ + +#define LM_MODE 1 +#define LM_FORWARDMASK 2 +#define LM_SLC 3 + +#define MODE_EDIT 0x01 +#define MODE_TRAPSIG 0x02 +#define MODE_ACK 0x04 +#define MODE_SOFT_TAB 0x08 +#define MODE_LIT_ECHO 0x10 + +#define MODE_MASK 0x1f + +/* Not part of protocol, but needed to simplify things... */ +#define MODE_FLOW 0x0100 +#define MODE_ECHO 0x0200 +#define MODE_INBIN 0x0400 +#define MODE_OUTBIN 0x0800 +#define MODE_FORCE 0x1000 + +#define SLC_SYNCH 1 +#define SLC_BRK 2 +#define SLC_IP 3 +#define SLC_AO 4 +#define SLC_AYT 5 +#define SLC_EOR 6 +#define SLC_ABORT 7 +#define SLC_EOF 8 +#define SLC_SUSP 9 +#define SLC_EC 10 +#define SLC_EL 11 +#define SLC_EW 12 +#define SLC_RP 13 +#define SLC_LNEXT 14 +#define SLC_XON 15 +#define SLC_XOFF 16 +#define SLC_FORW1 17 +#define SLC_FORW2 18 + +#define NSLC 18 + +/* + * For backwards compatibility, we define SLC_NAMES to be the + * list of names if SLC_NAMES is not defined. + */ +#define SLC_NAMELIST "0", "SYNCH", "BRK", "IP", "AO", "AYT", "EOR", \ + "ABORT", "EOF", "SUSP", "EC", "EL", "EW", "RP", \ + "LNEXT", "XON", "XOFF", "FORW1", "FORW2", 0, +#ifdef SLC_NAMES +const char *slc_names[] = { + SLC_NAMELIST +}; +#else +extern char *slc_names[]; +#define SLC_NAMES SLC_NAMELIST +#endif + +#define SLC_NAME_OK(x) ((unsigned int)(x) <= NSLC) +#define SLC_NAME(x) slc_names[x] + +#define SLC_NOSUPPORT 0 +#define SLC_CANTCHANGE 1 +#define SLC_VARIABLE 2 +#define SLC_DEFAULT 3 +#define SLC_LEVELBITS 0x03 + +#define SLC_FUNC 0 +#define SLC_FLAGS 1 +#define SLC_VALUE 2 + +#define SLC_ACK 0x80 +#define SLC_FLUSHIN 0x40 +#define SLC_FLUSHOUT 0x20 + +#define OLD_ENV_VAR 1 +#define OLD_ENV_VALUE 0 +#define NEW_ENV_VAR 0 +#define NEW_ENV_VALUE 1 +#define ENV_ESC 2 +#define ENV_USERVAR 3 + +/* + * AUTHENTICATION suboptions + */ + +/* + * Who is authenticating who ... + */ +#define AUTH_WHO_CLIENT 0 /* Client authenticating server */ +#define AUTH_WHO_SERVER 1 /* Server authenticating client */ +#define AUTH_WHO_MASK 1 + +/* + * amount of authentication done + */ +#define AUTH_HOW_ONE_WAY 0 +#define AUTH_HOW_MUTUAL 2 +#define AUTH_HOW_MASK 2 + +#define AUTHTYPE_NULL 0 +#define AUTHTYPE_KERBEROS_V4 1 +#define AUTHTYPE_KERBEROS_V5 2 +#define AUTHTYPE_SPX 3 +#define AUTHTYPE_MINK 4 +#define AUTHTYPE_CNT 5 + +#define AUTHTYPE_TEST 99 + +#ifdef AUTH_NAMES +const char *authtype_names[] = { + "NULL", "KERBEROS_V4", "KERBEROS_V5", "SPX", "MINK", 0, +}; +#else +extern char *authtype_names[]; +#endif + +#define AUTHTYPE_NAME_OK(x) ((unsigned int)(x) < AUTHTYPE_CNT) +#define AUTHTYPE_NAME(x) authtype_names[x] + +/* + * ENCRYPTion suboptions + */ +#define ENCRYPT_IS 0 /* I pick encryption type ... */ +#define ENCRYPT_SUPPORT 1 /* I support encryption types ... */ +#define ENCRYPT_REPLY 2 /* Initial setup response */ +#define ENCRYPT_START 3 /* Am starting to send encrypted */ +#define ENCRYPT_END 4 /* Am ending encrypted */ +#define ENCRYPT_REQSTART 5 /* Request you start encrypting */ +#define ENCRYPT_REQEND 6 /* Request you send encrypting */ +#define ENCRYPT_ENC_KEYID 7 +#define ENCRYPT_DEC_KEYID 8 +#define ENCRYPT_CNT 9 + +#define ENCTYPE_ANY 0 +#define ENCTYPE_DES_CFB64 1 +#define ENCTYPE_DES_OFB64 2 +#define ENCTYPE_CNT 3 + +#ifdef ENCRYPT_NAMES +const char *encrypt_names[] = { + "IS", "SUPPORT", "REPLY", "START", "END", + "REQUEST-START", "REQUEST-END", "ENC-KEYID", "DEC-KEYID", + 0, +}; +const char *enctype_names[] = { + "ANY", "DES_CFB64", "DES_OFB64", 0, +}; +#else +extern const char *encrypt_names[]; +extern const char *enctype_names[]; +#endif + + +#define ENCRYPT_NAME_OK(x) ((unsigned int)(x) < ENCRYPT_CNT) +#define ENCRYPT_NAME(x) encrypt_names[x] + +#define ENCTYPE_NAME_OK(x) ((unsigned int)(x) < ENCTYPE_CNT) +#define ENCTYPE_NAME(x) enctype_names[x] + +#endif /* arpa/telnet.h */ diff --git a/libc/include/ctype.h b/libc/include/ctype.h index b5f9ff4..58b76ea 100644 --- a/libc/include/ctype.h +++ b/libc/include/ctype.h @@ -59,7 +59,11 @@ extern const short *_toupper_tab_; /* extern __inline is a GNU C extension */ #ifdef __GNUC__ +# if defined(__GNUC_STDC_INLINE__) +#define __CTYPE_INLINE extern __inline __attribute__((__gnu_inline__)) +# else #define __CTYPE_INLINE extern __inline +# endif #else #define __CTYPE_INLINE static __inline #endif diff --git a/libc/include/err.h b/libc/include/err.h index e69de29..1636efe 100644 --- a/libc/include/err.h +++ b/libc/include/err.h @@ -0,0 +1,90 @@ +/* $OpenBSD: err.h,v 1.10 2006/01/06 18:53:04 millert Exp $ */ +/* $NetBSD: err.h,v 1.11 1994/10/26 00:55:52 cgd Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)err.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _ERR_H_ +#define _ERR_H_ + +/* + * Don't use va_list in the err/warn prototypes. Va_list is typedef'd in two + * places (<machine/varargs.h> and <machine/stdarg.h>), so if we include one + * of them here we may collide with the utility's includes. It's unreasonable + * for utilities to have to include one of them to include err.h, so we get + * __va_list from <machine/_types.h> and use it. + */ +#include <sys/cdefs.h> +#include <machine/_types.h> + +__BEGIN_DECLS + +__noreturn void err(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +__noreturn void verr(int, const char *, __va_list) + __attribute__((__format__ (printf, 2, 0))); +__noreturn void errx(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +__noreturn void verrx(int, const char *, __va_list) + __attribute__((__format__ (printf, 2, 0))); +void warn(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void vwarn(const char *, __va_list) + __attribute__((__format__ (printf, 1, 0))); +void warnx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void vwarnx(const char *, __va_list) + __attribute__((__format__ (printf, 1, 0))); + +/* + * The _* versions are for use in library functions so user-defined + * versions of err*,warn* do not get used. + */ +__noreturn void _err(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +__noreturn void _verr(int, const char *, __va_list) + __attribute__((__format__ (printf, 2, 0))); +__noreturn void _errx(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +__noreturn void _verrx(int, const char *, __va_list) + __attribute__((__format__ (printf, 2, 0))); +void _warn(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void _vwarn(const char *, __va_list) + __attribute__((__format__ (printf, 1, 0))); +void _warnx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void _vwarnx(const char *, __va_list) + __attribute__((__format__ (printf, 1, 0))); + +__END_DECLS + +#endif /* !_ERR_H_ */ diff --git a/libc/include/fts.h b/libc/include/fts.h new file mode 100644 index 0000000..da26a88 --- /dev/null +++ b/libc/include/fts.h @@ -0,0 +1,125 @@ +/* $OpenBSD: fts.h,v 1.12 2009/08/27 16:19:27 millert Exp $ */ +/* $NetBSD: fts.h,v 1.5 1994/12/28 01:41:50 mycroft Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)fts.h 8.3 (Berkeley) 8/14/94 + */ + +#ifndef _FTS_H_ +#define _FTS_H_ + +typedef struct { + struct _ftsent *fts_cur; /* current node */ + struct _ftsent *fts_child; /* linked list of children */ + struct _ftsent **fts_array; /* sort array */ + dev_t fts_dev; /* starting device # */ + char *fts_path; /* path for this descent */ + int fts_rfd; /* fd for root */ + size_t fts_pathlen; /* sizeof(path) */ + int fts_nitems; /* elements in the sort array */ + int (*fts_compar)(); /* compare function */ + +#define FTS_COMFOLLOW 0x0001 /* follow command line symlinks */ +#define FTS_LOGICAL 0x0002 /* logical walk */ +#define FTS_NOCHDIR 0x0004 /* don't change directories */ +#define FTS_NOSTAT 0x0008 /* don't get stat info */ +#define FTS_PHYSICAL 0x0010 /* physical walk */ +#define FTS_SEEDOT 0x0020 /* return dot and dot-dot */ +#define FTS_XDEV 0x0040 /* don't cross devices */ +#define FTS_OPTIONMASK 0x00ff /* valid user option mask */ + +#define FTS_NAMEONLY 0x1000 /* (private) child names only */ +#define FTS_STOP 0x2000 /* (private) unrecoverable error */ + int fts_options; /* fts_open options, global flags */ +} FTS; + +typedef struct _ftsent { + struct _ftsent *fts_cycle; /* cycle node */ + struct _ftsent *fts_parent; /* parent directory */ + struct _ftsent *fts_link; /* next file in directory */ + long fts_number; /* local numeric value */ + void *fts_pointer; /* local address value */ + char *fts_accpath; /* access path */ + char *fts_path; /* root path */ + int fts_errno; /* errno for this node */ + int fts_symfd; /* fd for symlink */ + size_t fts_pathlen; /* strlen(fts_path) */ + size_t fts_namelen; /* strlen(fts_name) */ + + ino_t fts_ino; /* inode */ + dev_t fts_dev; /* device */ + nlink_t fts_nlink; /* link count */ + +#define FTS_ROOTPARENTLEVEL -1 +#define FTS_ROOTLEVEL 0 +#define FTS_MAXLEVEL 0x7fff + short fts_level; /* depth (-1 to N) */ + +#define FTS_D 1 /* preorder directory */ +#define FTS_DC 2 /* directory that causes cycles */ +#define FTS_DEFAULT 3 /* none of the above */ +#define FTS_DNR 4 /* unreadable directory */ +#define FTS_DOT 5 /* dot or dot-dot */ +#define FTS_DP 6 /* postorder directory */ +#define FTS_ERR 7 /* error; errno is set */ +#define FTS_F 8 /* regular file */ +#define FTS_INIT 9 /* initialized only */ +#define FTS_NS 10 /* stat(2) failed */ +#define FTS_NSOK 11 /* no stat(2) requested */ +#define FTS_SL 12 /* symbolic link */ +#define FTS_SLNONE 13 /* symbolic link without target */ + unsigned short fts_info; /* user flags for FTSENT structure */ + +#define FTS_DONTCHDIR 0x01 /* don't chdir .. to the parent */ +#define FTS_SYMFOLLOW 0x02 /* followed a symlink to get here */ + unsigned short fts_flags; /* private flags for FTSENT structure */ + +#define FTS_AGAIN 1 /* read node again */ +#define FTS_FOLLOW 2 /* follow symbolic link */ +#define FTS_NOINSTR 3 /* no instructions */ +#define FTS_SKIP 4 /* discard node */ + unsigned short fts_instr; /* fts_set() instructions */ + + struct stat *fts_statp; /* stat(2) information */ + char fts_name[1]; /* file name */ +} FTSENT; + +#include <sys/cdefs.h> + +__BEGIN_DECLS +FTSENT *fts_children(FTS *, int); +int fts_close(FTS *); +FTS *fts_open(char * const *, int, + int (*)(const FTSENT **, const FTSENT **)); +FTSENT *fts_read(FTS *); +int fts_set(FTS *, FTSENT *, int); +__END_DECLS + +#endif /* !_FTS_H_ */ diff --git a/libc/include/mntent.h b/libc/include/mntent.h index 468ff74..b83da1f 100644 --- a/libc/include/mntent.h +++ b/libc/include/mntent.h @@ -28,6 +28,7 @@ #ifndef _MNTENT_H_ #define _MNTENT_H_ +#include <stdio.h> #define MNTTYPE_IGNORE "ignore" diff --git a/libc/include/netdb.h b/libc/include/netdb.h index b0c3b72..c2e08ea 100644 --- a/libc/include/netdb.h +++ b/libc/include/netdb.h @@ -75,10 +75,6 @@ #define MAXHOSTNAMELEN 256 -/* BIONIC-BEGIN */ -#define h_errno (*__get_h_errno()) -extern int* __get_h_errno(void); -/* BIONIC-END */ /* * Structures returned by network data base library. All addresses are @@ -203,6 +199,10 @@ struct addrinfo { #define SCOPE_DELIMITER '%' __BEGIN_DECLS +/* BIONIC-BEGIN */ +#define h_errno (*__get_h_errno()) +int* __get_h_errno(void); +/* BIONIC-END */ void endhostent(void); void endnetent(void); void endnetgrent(void); diff --git a/libc/include/pthread.h b/libc/include/pthread.h index 6603b3f..eb2d169 100644 --- a/libc/include/pthread.h +++ b/libc/include/pthread.h @@ -165,6 +165,11 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_timedlock(pthread_mutex_t *mutex, struct timespec* ts); +int pthread_condattr_init(pthread_condattr_t *attr); +int pthread_condattr_getpshared(pthread_condattr_t *attr, int *pshared); +int pthread_condattr_setpshared(pthread_condattr_t* attr, int pshared); +int pthread_condattr_destroy(pthread_condattr_t *attr); + int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); int pthread_cond_destroy(pthread_cond_t *cond); diff --git a/libc/include/regex.h b/libc/include/regex.h new file mode 100644 index 0000000..aec38e3 --- /dev/null +++ b/libc/include/regex.h @@ -0,0 +1,105 @@ +/* $OpenBSD: regex.h,v 1.6 2003/06/02 19:34:12 millert Exp $ */ +/* $NetBSD: regex.h,v 1.4.6.1 1996/06/10 18:57:07 explorer Exp $ */ + +/*- + * Copyright (c) 1992 Henry Spencer. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer of the University of Toronto. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regex.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _REGEX_H_ +#define _REGEX_H_ + +#include <sys/cdefs.h> +#include <sys/types.h> + +/* types */ +typedef off_t regoff_t; + +typedef struct { + int re_magic; + size_t re_nsub; /* number of parenthesized subexpressions */ + const char *re_endp; /* end pointer for REG_PEND */ + struct re_guts *re_g; /* none of your business :-) */ +} regex_t; + +typedef struct { + regoff_t rm_so; /* start of match */ + regoff_t rm_eo; /* end of match */ +} regmatch_t; + +/* regcomp() flags */ +#define REG_BASIC 0000 +#define REG_EXTENDED 0001 +#define REG_ICASE 0002 +#define REG_NOSUB 0004 +#define REG_NEWLINE 0010 +#define REG_NOSPEC 0020 +#define REG_PEND 0040 +#define REG_DUMP 0200 + +/* regerror() flags */ +#define REG_NOMATCH 1 +#define REG_BADPAT 2 +#define REG_ECOLLATE 3 +#define REG_ECTYPE 4 +#define REG_EESCAPE 5 +#define REG_ESUBREG 6 +#define REG_EBRACK 7 +#define REG_EPAREN 8 +#define REG_EBRACE 9 +#define REG_BADBR 10 +#define REG_ERANGE 11 +#define REG_ESPACE 12 +#define REG_BADRPT 13 +#define REG_EMPTY 14 +#define REG_ASSERT 15 +#define REG_INVARG 16 +#define REG_ATOI 255 /* convert name to number (!) */ +#define REG_ITOA 0400 /* convert number to name (!) */ + +/* regexec() flags */ +#define REG_NOTBOL 00001 +#define REG_NOTEOL 00002 +#define REG_STARTEND 00004 +#define REG_TRACE 00400 /* tracing of execution */ +#define REG_LARGE 01000 /* force large representation */ +#define REG_BACKR 02000 /* force use of backref code */ + +__BEGIN_DECLS +int regcomp(regex_t *, const char *, int); +size_t regerror(int, const regex_t *, char *, size_t); +int regexec(const regex_t *, const char *, size_t, regmatch_t [], int); +void regfree(regex_t *); +__END_DECLS + +#endif /* !_REGEX_H_ */ diff --git a/libc/include/sched.h b/libc/include/sched.h index 6600bae..33b9ad6 100644 --- a/libc/include/sched.h +++ b/libc/include/sched.h @@ -69,8 +69,9 @@ extern int sched_rr_get_interval(pid_t pid, struct timespec *tp); #define CLONE_CHILD_SETTID 0x01000000 #define CLONE_STOPPED 0x02000000 -extern int clone(int (*fn)(void*), void *child_stack, int flags, void *arg); -extern pid_t __clone(int, void *); +#ifdef __GNU_SOURCE +extern int clone(int (*fn)(void *), void *child_stack, int flags, void* arg, ...); +#endif __END_DECLS diff --git a/libc/include/signal.h b/libc/include/signal.h index 5540847..9e5ce61 100644 --- a/libc/include/signal.h +++ b/libc/include/signal.h @@ -118,6 +118,7 @@ extern int siginterrupt(int sig, int flag); extern int raise(int); extern int kill(pid_t, int); +extern int killpg(int pgrp, int sig); __END_DECLS diff --git a/libc/include/stdio.h b/libc/include/stdio.h index 79e526b..f0e103e 100644 --- a/libc/include/stdio.h +++ b/libc/include/stdio.h @@ -112,6 +112,9 @@ struct __sbuf { * that does not match the previous one in _bf. When this happens, * _ub._base becomes non-nil (i.e., a stream has ungetc() data iff * _ub._base!=NULL) and _up and _ur save the current values of _p and _r. + * + * NOTE: if you change this structure, you also need to update the + * std() initializer in findfp.c. */ typedef struct __sFILE { unsigned char *_p; /* current position in (some) buffer */ @@ -434,4 +437,15 @@ static __inline int __sputc(int _c, FILE *_p) { #define getchar_unlocked() getc_unlocked(stdin) #define putchar_unlocked(c) putc_unlocked(c, stdout) +#ifdef _GNU_SOURCE +/* + * glibc defines dprintf(int, const char*, ...), which is poorly named + * and likely to conflict with locally defined debugging printfs + * fdprintf is a better name, and some programs that use fdprintf use a + * #define fdprintf dprintf for compatibility + */ +int fdprintf(int, const char*, ...); +int vfdprintf(int, const char*, __va_list); +#endif /* _GNU_SOURCE */ + #endif /* _STDIO_H_ */ diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h index acfe694..41e8d26 100644 --- a/libc/include/stdlib.h +++ b/libc/include/stdlib.h @@ -132,7 +132,7 @@ static __inline__ void srandom(unsigned int __s) extern int unlockpt(int); extern char* ptsname(int); -extern char* ptsname_r(int, char*, size_t); +extern int ptsname_r(int, char*, size_t); extern int getpt(void); static __inline__ int grantpt(int __fd) diff --git a/libc/include/sys/epoll.h b/libc/include/sys/epoll.h index 1478caa..decdb46 100644 --- a/libc/include/sys/epoll.h +++ b/libc/include/sys/epoll.h @@ -28,6 +28,10 @@ #ifndef _SYS_EPOLL_H_ #define _SYS_EPOLL_H_ +#include <sys/cdefs.h> + +__BEGIN_DECLS + #define EPOLLIN 0x00000001 #define EPOLLPRI 0x00000002 #define EPOLLOUT 0x00000004 @@ -62,5 +66,7 @@ int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event *events, int max, int timeout); +__END_DECLS + #endif /* _SYS_EPOLL_H_ */ diff --git a/libc/include/sys/linux-syscalls.h b/libc/include/sys/linux-syscalls.h index d904473..6e373e0 100644 --- a/libc/include/sys/linux-syscalls.h +++ b/libc/include/sys/linux-syscalls.h @@ -42,6 +42,7 @@ #define __NR_prctl (__NR_SYSCALL_BASE + 172) #define __NR_capget (__NR_SYSCALL_BASE + 184) #define __NR_capset (__NR_SYSCALL_BASE + 185) +#define __NR_sigaltstack (__NR_SYSCALL_BASE + 186) #define __NR_acct (__NR_SYSCALL_BASE + 51) #define __NR_read (__NR_SYSCALL_BASE + 3) #define __NR_write (__NR_SYSCALL_BASE + 4) @@ -170,6 +171,8 @@ #define __NR_getsockopt (__NR_SYSCALL_BASE + 295) #define __NR_sendmsg (__NR_SYSCALL_BASE + 296) #define __NR_recvmsg (__NR_SYSCALL_BASE + 297) +#define __NR_ioprio_set (__NR_SYSCALL_BASE + 314) +#define __NR_ioprio_get (__NR_SYSCALL_BASE + 315) #define __NR_epoll_create (__NR_SYSCALL_BASE + 250) #define __NR_epoll_ctl (__NR_SYSCALL_BASE + 251) #define __NR_epoll_wait (__NR_SYSCALL_BASE + 252) @@ -211,6 +214,8 @@ #define __NR_utimes (__NR_SYSCALL_BASE + 271) #define __NR_sigaction (__NR_SYSCALL_BASE + 67) #define __NR_socketcall (__NR_SYSCALL_BASE + 102) +#define __NR_ioprio_set (__NR_SYSCALL_BASE + 289) +#define __NR_ioprio_get (__NR_SYSCALL_BASE + 290) #define __NR_epoll_create (__NR_SYSCALL_BASE + 254) #define __NR_epoll_ctl (__NR_SYSCALL_BASE + 255) #define __NR_epoll_wait (__NR_SYSCALL_BASE + 256) @@ -266,6 +271,8 @@ #define __NR_socketcall (__NR_SYSCALL_BASE + 102) #define __NR_socketcall (__NR_SYSCALL_BASE + 102) #define __NR___socketcall (__NR_SYSCALL_BASE + 102) +#define __NR_ioprio_set (__NR_SYSCALL_BASE + 289) +#define __NR_ioprio_get (__NR_SYSCALL_BASE + 290) #define __NR_epoll_create (__NR_SYSCALL_BASE + 254) #define __NR_epoll_ctl (__NR_SYSCALL_BASE + 255) #define __NR_epoll_wait (__NR_SYSCALL_BASE + 256) diff --git a/libc/include/sys/linux-unistd.h b/libc/include/sys/linux-unistd.h index eb04011..b0e7822 100644 --- a/libc/include/sys/linux-unistd.h +++ b/libc/include/sys/linux-unistd.h @@ -10,9 +10,9 @@ void _exit_thread (int); pid_t __fork (void); pid_t _waitpid (pid_t, int*, int, struct rusage*); int waitid (int, pid_t, struct siginfo_t*, int,void*); -pid_t __clone (int (*fn)(void*), void *child_stack, int flags, void *arg); +pid_t __sys_clone (int, void*, int*, void*, int*); int execve (const char*, char* const*, char* const*); -int setuid (uid_t); +int __setuid (uid_t); uid_t getuid (void); gid_t getgid (void); uid_t geteuid (void); @@ -26,8 +26,8 @@ pid_t getppid (void); pid_t setsid (void); int setgid (gid_t); int seteuid (uid_t); -int setreuid (uid_t, uid_t); -int setresuid (uid_t, uid_t, uid_t); +int __setreuid (uid_t, uid_t); +int __setresuid (uid_t, uid_t, uid_t); int setresgid (gid_t, gid_t, gid_t); void* __brk (void*); int kill (pid_t, int); @@ -48,6 +48,7 @@ int chroot (const char *); int prctl (int option, unsigned int arg2, unsigned int arg3, unsigned int arg4, unsigned int arg5); int capget (cap_user_header_t header, cap_user_data_t data); int capset (cap_user_header_t header, const cap_user_data_t data); +int sigaltstack (const stack_t*, stack_t*); int acct (const char* filepath); ssize_t read (int, void*, size_t); ssize_t write (int, const void*, size_t); @@ -181,6 +182,8 @@ int sched_getparam (pid_t pid, struct sched_param *param); int sched_get_priority_max (int policy); int sched_get_priority_min (int policy); int sched_rr_get_interval (pid_t pid, struct timespec *interval); +int ioprio_set (int which, int who, int ioprio); +int ioprio_get (int which, int who); int uname (struct utsname *); pid_t __wait4 (pid_t pid, int *status, int options, struct rusage *rusage); mode_t umask (mode_t); diff --git a/libc/include/sys/queue.h b/libc/include/sys/queue.h new file mode 100644 index 0000000..b0e6b38 --- /dev/null +++ b/libc/include/sys/queue.h @@ -0,0 +1,557 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + (head)->lh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var); \ + (var) = ((var)->field.le_next)) + +/* + * List access methods. + */ +#define LIST_EMPTY(head) ((head)->lh_first == NULL) +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) do { \ + (head)->slh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while(curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) + +/* + * Singly-linked List access methods. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + (head)->stqh_first = NULL; \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (head)->stqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.stqe_next = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &(elm)->field.stqe_next; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (listelm)->field.stqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if ((head)->stqh_first == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->stqh_first; \ + while (curelm->field.stqe_next != (elm)) \ + curelm = curelm->field.stqe_next; \ + if ((curelm->field.stqe_next = \ + curelm->field.stqe_next->field.stqe_next) == NULL) \ + (head)->stqh_last = &(curelm)->field.stqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->stqh_first); \ + (var); \ + (var) = ((var)->field.stqe_next)) + +/* + * Singly-linked Tail queue access methods. + */ +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + SIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var); \ + (var) = ((var)->field.sqe_next)) + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + + +/* + * Tail queue definitions. + */ +#define _TAILQ_HEAD(name, type, qual) \ +struct name { \ + qual type *tqh_first; /* first element */ \ + qual type *qual *tqh_last; /* addr of last next element */ \ +} +#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define _TAILQ_ENTRY(type, qual) \ +struct { \ + qual type *tqe_next; /* next element */ \ + qual type *qual *tqe_prev; /* address of previous next element */\ +} +#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var); \ + (var) = ((var)->field.tqe_next)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ + (var); \ + (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) + +/* + * Tail queue access methods. + */ +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { (void *)&head, (void *)&head } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->cqh_first); \ + (var) != (const void *)(head); \ + (var) = ((var)->field.cqe_next)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = ((head)->cqh_last); \ + (var) != (const void *)(head); \ + (var) = ((var)->field.cqe_prev)) + +/* + * Circular queue access methods. + */ +#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) + +#define CIRCLEQ_LOOP_NEXT(head, elm, field) \ + (((elm)->field.cqe_next == (void *)(head)) \ + ? ((head)->cqh_first) \ + : (elm->field.cqe_next)) +#define CIRCLEQ_LOOP_PREV(head, elm, field) \ + (((elm)->field.cqe_prev == (void *)(head)) \ + ? ((head)->cqh_last) \ + : (elm->field.cqe_prev)) + +#endif /* sys/queue.h */ diff --git a/libc/include/sys/resource.h b/libc/include/sys/resource.h index a7de6f0..ef325c7 100644 --- a/libc/include/sys/resource.h +++ b/libc/include/sys/resource.h @@ -40,6 +40,8 @@ #include <linux/resource.h> #undef getrusage +typedef unsigned long rlim_t; + __BEGIN_DECLS extern int getpriority(int, int); diff --git a/libc/include/sys/system_properties.h b/libc/include/sys/system_properties.h index 2eb00cd..4fdc944 100644 --- a/libc/include/sys/system_properties.h +++ b/libc/include/sys/system_properties.h @@ -29,6 +29,10 @@ #ifndef _INCLUDE_SYS_SYSTEM_PROPERTIES_H #define _INCLUDE_SYS_SYSTEM_PROPERTIES_H +#include <sys/cdefs.h> + +__BEGIN_DECLS + typedef struct prop_info prop_info; #define PROP_NAME_MAX 32 @@ -76,4 +80,6 @@ int __system_property_read(const prop_info *pi, char *name, char *value); */ const prop_info *__system_property_find_nth(unsigned n); +__END_DECLS + #endif diff --git a/libc/include/time64.h b/libc/include/time64.h index 9da4bc7..7ec05af 100644 --- a/libc/include/time64.h +++ b/libc/include/time64.h @@ -31,9 +31,12 @@ Modified for Bionic by the Android Open Source Project #ifndef TIME64_H #define TIME64_H +#include <sys/cdefs.h> #include <time.h> #include <stdint.h> +__BEGIN_DECLS + typedef int64_t time64_t; struct tm *gmtime64_r (const time64_t *, struct tm *); @@ -51,4 +54,6 @@ time64_t timegm64 (const struct tm *); time64_t mktime64 (const struct tm *); time64_t timelocal64 (const struct tm *); +__END_DECLS + #endif /* TIME64_H */ diff --git a/libc/include/unistd.h b/libc/include/unistd.h index 1ada37e..b4f1dda 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -99,6 +99,10 @@ extern int setfsuid(uid_t); extern int issetugid(void); extern char* getlogin(void); extern int getlogin_r(char* name, size_t namesize); +extern char* getusershell(void); +extern void setusershell(void); +extern void endusershell(void); + /* Macros for access() */ @@ -145,7 +149,7 @@ extern int ftruncate(int, off_t); extern int pause(void); extern unsigned int alarm(unsigned int); extern unsigned int sleep(unsigned int); -extern void usleep(unsigned long); +extern int usleep(unsigned long); extern int gethostname(char *, size_t); extern int sethostname(const char *, size_t); @@ -163,6 +167,8 @@ extern char *optarg; extern int optind, opterr, optopt; extern int isatty(int); +extern char* ttyname(int); +extern int ttyname_r(int, char*, size_t); extern int acct(const char* filepath); @@ -185,6 +191,14 @@ extern int cacheflush(long start, long end, long flags); extern pid_t tcgetpgrp(int fd); extern int tcsetpgrp(int fd, pid_t _pid); +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) + __END_DECLS #endif /* _UNISTD_H_ */ diff --git a/libc/include/wchar.h b/libc/include/wchar.h index e2feb60..97e1b5c 100644 --- a/libc/include/wchar.h +++ b/libc/include/wchar.h @@ -100,6 +100,7 @@ extern int mbsinit(const mbstate_t *); extern size_t mbrlen(const char *, size_t, mbstate_t *); extern size_t mbrtowc(wchar_t *, const char *, size_t, mbstate_t *); extern size_t mbsrtowcs(wchar_t *, const char **, size_t, mbstate_t *); +extern size_t mbstowcs(wchar_t *, const char *, size_t); extern wint_t putwc(wchar_t, FILE *); extern wint_t putwchar(wchar_t); extern int swprintf(wchar_t *, size_t, const wchar_t *, ...); @@ -130,6 +131,7 @@ extern wchar_t *wcsstr(const wchar_t *, const wchar_t *); extern double wcstod(const wchar_t *, wchar_t **); extern wchar_t *wcstok(wchar_t *, const wchar_t *, wchar_t **); extern long int wcstol(const wchar_t *, wchar_t **, int); +extern size_t wcstombs(char *, const wchar_t *, size_t); extern unsigned long int wcstoul(const wchar_t *, wchar_t **, int); extern wchar_t *wcswcs(const wchar_t *, const wchar_t *); extern int wcswidth(const wchar_t *, size_t); diff --git a/libc/kernel/arch-arm/asm/ptrace.h b/libc/kernel/arch-arm/asm/ptrace.h index c6dfea1..b6be08c 100644 --- a/libc/kernel/arch-arm/asm/ptrace.h +++ b/libc/kernel/arch-arm/asm/ptrace.h @@ -29,6 +29,8 @@ #define PTRACE_GETCRUNCHREGS 25 #define PTRACE_SETCRUNCHREGS 26 +#define PTRACE_GETVFPREGS 27 + #define USR26_MODE 0x00000000 #define FIQ26_MODE 0x00000001 #define IRQ26_MODE 0x00000002 @@ -61,7 +63,12 @@ #ifndef __ASSEMBLY__ struct pt_regs { - long uregs[18]; + long uregs[18]; +}; + +struct user_vfp { + unsigned long long fpregs[32]; + unsigned long fpscr; }; #define ARM_cpsr uregs[16] diff --git a/libc/kernel/common/asm-generic/swab.h b/libc/kernel/common/asm-generic/swab.h new file mode 100644 index 0000000..592926d --- /dev/null +++ b/libc/kernel/common/asm-generic/swab.h @@ -0,0 +1,23 @@ +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + **************************************************************************** + ****************************************************************************/ +#ifndef _ASM_GENERIC_SWAB_H +#define _ASM_GENERIC_SWAB_H + +#include <asm/bitsperlong.h> + +#if __BITS_PER_LONG == 32 +#if defined(__GNUC__) && (!defined(__STRICT_ANSI__) || defined(__KERNEL__)) +#define __SWAB_64_THRU_32__ +#endif +#endif + +#endif diff --git a/libc/kernel/common/linux/android_pmem.h b/libc/kernel/common/linux/android_pmem.h index bdf3aba..be0b342 100644 --- a/libc/kernel/common/linux/android_pmem.h +++ b/libc/kernel/common/linux/android_pmem.h @@ -23,6 +23,7 @@ #define PMEM_CONNECT _IOW(PMEM_IOCTL_MAGIC, 6, unsigned int) #define PMEM_GET_TOTAL_SIZE _IOW(PMEM_IOCTL_MAGIC, 7, unsigned int) +#define PMEM_CACHE_FLUSH _IOW(PMEM_IOCTL_MAGIC, 8, unsigned int) struct android_pmem_platform_data { diff --git a/libc/kernel/common/linux/if_addr.h b/libc/kernel/common/linux/if_addr.h new file mode 100644 index 0000000..9c1fa15 --- /dev/null +++ b/libc/kernel/common/linux/if_addr.h @@ -0,0 +1,64 @@ +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + **************************************************************************** + ****************************************************************************/ +#ifndef __LINUX_IF_ADDR_H +#define __LINUX_IF_ADDR_H + +#include <linux/types.h> +#include <linux/netlink.h> + +struct ifaddrmsg +{ + __u8 ifa_family; + __u8 ifa_prefixlen; + __u8 ifa_flags; + __u8 ifa_scope; + __u32 ifa_index; +}; + +enum +{ + IFA_UNSPEC, + IFA_ADDRESS, + IFA_LOCAL, + IFA_LABEL, + IFA_BROADCAST, + IFA_ANYCAST, + IFA_CACHEINFO, + IFA_MULTICAST, + __IFA_MAX, +}; + +#define IFA_MAX (__IFA_MAX - 1) + +#define IFA_F_SECONDARY 0x01 +#define IFA_F_TEMPORARY IFA_F_SECONDARY + +#define IFA_F_NODAD 0x02 +#define IFA_F_OPTIMISTIC 0x04 +#define IFA_F_DADFAILED 0x08 +#define IFA_F_HOMEADDRESS 0x10 +#define IFA_F_DEPRECATED 0x20 +#define IFA_F_TENTATIVE 0x40 +#define IFA_F_PERMANENT 0x80 + +struct ifa_cacheinfo +{ + __u32 ifa_prefered; + __u32 ifa_valid; + __u32 cstamp; + __u32 tstamp; +}; + +#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) +#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) + +#endif diff --git a/libc/kernel/common/linux/if_arp.h b/libc/kernel/common/linux/if_arp.h index 1da50f5..a3df6c8 100644 --- a/libc/kernel/common/linux/if_arp.h +++ b/libc/kernel/common/linux/if_arp.h @@ -39,6 +39,7 @@ #define ARPHRD_ROSE 270 #define ARPHRD_X25 271 #define ARPHRD_HWX25 272 +#define ARPHRD_CAN 280 #define ARPHRD_PPP 512 #define ARPHRD_CISCO 513 #define ARPHRD_HDLC ARPHRD_CISCO @@ -72,6 +73,10 @@ #define ARPHRD_IEEE80211 801 #define ARPHRD_IEEE80211_PRISM 802 #define ARPHRD_IEEE80211_RADIOTAP 803 +#define ARPHRD_IEEE802154 804 + +#define ARPHRD_PHONET 820 +#define ARPHRD_PHONET_PIPE 821 #define ARPHRD_VOID 0xFFFF #define ARPHRD_NONE 0xFFFE @@ -108,11 +113,11 @@ struct arpreq_old { struct arphdr { - unsigned short ar_hrd; - unsigned short ar_pro; + __be16 ar_hrd; + __be16 ar_pro; unsigned char ar_hln; unsigned char ar_pln; - unsigned short ar_op; + __be16 ar_op; }; diff --git a/libc/kernel/common/linux/if_ether.h b/libc/kernel/common/linux/if_ether.h index ff89c3d..1ba7a99 100644 --- a/libc/kernel/common/linux/if_ether.h +++ b/libc/kernel/common/linux/if_ether.h @@ -19,6 +19,7 @@ #define ETH_ZLEN 60 #define ETH_DATA_LEN 1500 #define ETH_FRAME_LEN 1514 +#define ETH_FCS_LEN 4 #define ETH_P_LOOP 0x0060 #define ETH_P_PUP 0x0200 @@ -37,12 +38,14 @@ #define ETH_P_DIAG 0x6005 #define ETH_P_CUST 0x6006 #define ETH_P_SCA 0x6007 +#define ETH_P_TEB 0x6558 #define ETH_P_RARP 0x8035 #define ETH_P_ATALK 0x809B #define ETH_P_AARP 0x80F3 #define ETH_P_8021Q 0x8100 #define ETH_P_IPX 0x8137 #define ETH_P_IPV6 0x86DD +#define ETH_P_PAUSE 0x8808 #define ETH_P_SLOW 0x8809 #define ETH_P_WCCP 0x883E #define ETH_P_PPP_DISC 0x8863 @@ -51,8 +54,13 @@ #define ETH_P_MPLS_MC 0x8848 #define ETH_P_ATMMPOA 0x884c #define ETH_P_ATMFATE 0x8884 +#define ETH_P_PAE 0x888E #define ETH_P_AOE 0x88A2 #define ETH_P_TIPC 0x88CA +#define ETH_P_1588 0x88F7 +#define ETH_P_FCOE 0x8906 +#define ETH_P_FIP 0x8914 +#define ETH_P_EDSA 0xDADA #define ETH_P_802_3 0x0001 #define ETH_P_AX25 0x0002 @@ -63,6 +71,7 @@ #define ETH_P_WAN_PPP 0x0007 #define ETH_P_PPP_MP 0x0008 #define ETH_P_LOCALTALK 0x0009 +#define ETH_P_CAN 0x000C #define ETH_P_PPPTALK 0x0010 #define ETH_P_TR_802_2 0x0011 #define ETH_P_MOBITEX 0x0015 @@ -71,6 +80,10 @@ #define ETH_P_ECONET 0x0018 #define ETH_P_HDLC 0x0019 #define ETH_P_ARCNET 0x001A +#define ETH_P_DSA 0x001B +#define ETH_P_TRAILER 0x001C +#define ETH_P_PHONET 0x00F5 +#define ETH_P_IEEE802154 0x00F6 struct ethhdr { unsigned char h_dest[ETH_ALEN]; diff --git a/libc/kernel/common/linux/if_link.h b/libc/kernel/common/linux/if_link.h new file mode 100644 index 0000000..e9d77d4 --- /dev/null +++ b/libc/kernel/common/linux/if_link.h @@ -0,0 +1,163 @@ +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + **************************************************************************** + ****************************************************************************/ +#ifndef _LINUX_IF_LINK_H +#define _LINUX_IF_LINK_H + +#include <linux/types.h> +#include <linux/netlink.h> + +struct rtnl_link_stats +{ + __u32 rx_packets; + __u32 tx_packets; + __u32 rx_bytes; + __u32 tx_bytes; + __u32 rx_errors; + __u32 tx_errors; + __u32 rx_dropped; + __u32 tx_dropped; + __u32 multicast; + __u32 collisions; + + __u32 rx_length_errors; + __u32 rx_over_errors; + __u32 rx_crc_errors; + __u32 rx_frame_errors; + __u32 rx_fifo_errors; + __u32 rx_missed_errors; + + __u32 tx_aborted_errors; + __u32 tx_carrier_errors; + __u32 tx_fifo_errors; + __u32 tx_heartbeat_errors; + __u32 tx_window_errors; + + __u32 rx_compressed; + __u32 tx_compressed; +}; + +struct rtnl_link_ifmap +{ + __u64 mem_start; + __u64 mem_end; + __u64 base_addr; + __u16 irq; + __u8 dma; + __u8 port; +}; + +enum +{ + IFLA_UNSPEC, + IFLA_ADDRESS, + IFLA_BROADCAST, + IFLA_IFNAME, + IFLA_MTU, + IFLA_LINK, + IFLA_QDISC, + IFLA_STATS, + IFLA_COST, +#define IFLA_COST IFLA_COST + IFLA_PRIORITY, +#define IFLA_PRIORITY IFLA_PRIORITY + IFLA_MASTER, +#define IFLA_MASTER IFLA_MASTER + IFLA_WIRELESS, +#define IFLA_WIRELESS IFLA_WIRELESS + IFLA_PROTINFO, +#define IFLA_PROTINFO IFLA_PROTINFO + IFLA_TXQLEN, +#define IFLA_TXQLEN IFLA_TXQLEN + IFLA_MAP, +#define IFLA_MAP IFLA_MAP + IFLA_WEIGHT, +#define IFLA_WEIGHT IFLA_WEIGHT + IFLA_OPERSTATE, + IFLA_LINKMODE, + IFLA_LINKINFO, +#define IFLA_LINKINFO IFLA_LINKINFO + IFLA_NET_NS_PID, + IFLA_IFALIAS, + __IFLA_MAX +}; + +#define IFLA_MAX (__IFLA_MAX - 1) + +#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) +#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) + +enum +{ + IFLA_INET6_UNSPEC, + IFLA_INET6_FLAGS, + IFLA_INET6_CONF, + IFLA_INET6_STATS, + IFLA_INET6_MCAST, + IFLA_INET6_CACHEINFO, + IFLA_INET6_ICMP6STATS, + __IFLA_INET6_MAX +}; + +#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) + +struct ifla_cacheinfo +{ + __u32 max_reasm_len; + __u32 tstamp; + __u32 reachable_time; + __u32 retrans_time; +}; + +enum +{ + IFLA_INFO_UNSPEC, + IFLA_INFO_KIND, + IFLA_INFO_DATA, + IFLA_INFO_XSTATS, + __IFLA_INFO_MAX, +}; + +#define IFLA_INFO_MAX (__IFLA_INFO_MAX - 1) + +enum +{ + IFLA_VLAN_UNSPEC, + IFLA_VLAN_ID, + IFLA_VLAN_FLAGS, + IFLA_VLAN_EGRESS_QOS, + IFLA_VLAN_INGRESS_QOS, + __IFLA_VLAN_MAX, +}; + +#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1) + +struct ifla_vlan_flags { + __u32 flags; + __u32 mask; +}; + +enum +{ + IFLA_VLAN_QOS_UNSPEC, + IFLA_VLAN_QOS_MAPPING, + __IFLA_VLAN_QOS_MAX +}; + +#define IFLA_VLAN_QOS_MAX (__IFLA_VLAN_QOS_MAX - 1) + +struct ifla_vlan_qos_mapping +{ + __u32 from; + __u32 to; +}; + +#endif diff --git a/libc/kernel/common/linux/msm_kgsl.h b/libc/kernel/common/linux/msm_kgsl.h index 740ba60..f543522 100644 --- a/libc/kernel/common/linux/msm_kgsl.h +++ b/libc/kernel/common/linux/msm_kgsl.h @@ -49,6 +49,10 @@ struct kgsl_devmemstore { unsigned int sbz; volatile unsigned int eoptimestamp; unsigned int sbz2; + volatile unsigned int ts_cmp_enable; + unsigned int sbz3; + volatile unsigned int ref_wait_ts; + unsigned int sbz4; }; #define KGSL_DEVICE_MEMSTORE_OFFSET(field) offsetof(struct kgsl_devmemstore, field) @@ -65,7 +69,8 @@ enum kgsl_property_type { KGSL_PROP_DEVICE_POWER = 0x00000003, KGSL_PROP_SHMEM = 0x00000004, KGSL_PROP_SHMEM_APERTURES = 0x00000005, - KGSL_PROP_MMU_ENABLE = 0x00000006 + KGSL_PROP_MMU_ENABLE = 0x00000006, + KGSL_PROP_INTERRUPT_WAITS = 0x00000007, }; struct kgsl_shadowprop { diff --git a/libc/kernel/common/linux/msm_q6venc.h b/libc/kernel/common/linux/msm_q6venc.h index 5b63680..dbe118a 100755 --- a/libc/kernel/common/linux/msm_q6venc.h +++ b/libc/kernel/common/linux/msm_q6venc.h @@ -14,96 +14,281 @@ #include <linux/types.h> -struct venc_buf { - unsigned int src_id; +#define VENC_MAX_RECON_BUFFERS 2 + +#define VENC_FLAG_EOS 0x00000001 +#define VENC_FLAG_END_OF_FRAME 0x00000010 +#define VENC_FLAG_SYNC_FRAME 0x00000020 +#define VENC_FLAG_EXTRA_DATA 0x00000040 +#define VENC_FLAG_CODEC_CONFIG 0x00000080 + +enum venc_flush_type { + VENC_FLUSH_INPUT, + VENC_FLUSH_OUTPUT, + VENC_FLUSH_ALL +}; + +enum venc_state_type { + VENC_STATE_PAUSE = 0x1, + VENC_STATE_START = 0x2, + VENC_STATE_STOP = 0x4 +}; + +enum venc_event_type_enum { + VENC_EVENT_START_STATUS, + VENC_EVENT_STOP_STATUS, + VENC_EVENT_SUSPEND_STATUS, + VENC_EVENT_RESUME_STATUS, + VENC_EVENT_FLUSH_STATUS, + VENC_EVENT_RELEASE_INPUT, + VENC_EVENT_DELIVER_OUTPUT, + VENC_EVENT_UNKNOWN_STATUS +}; + +enum venc_status_code { + VENC_STATUS_SUCCESS, + VENC_STATUS_ERROR, + VENC_STATUS_INVALID_STATE, + VENC_STATUS_FLUSHING, + VENC_STATUS_INVALID_PARAM, + VENC_STATUS_CMD_QUEUE_FULL, + VENC_STATUS_CRITICAL, + VENC_STATUS_INSUFFICIENT_RESOURCES, + VENC_STATUS_TIMEOUT +}; + +enum venc_msg_code { + VENC_MSG_INDICATION, + VENC_MSG_INPUT_BUFFER_DONE, + VENC_MSG_OUTPUT_BUFFER_DONE, + VENC_MSG_NEED_OUTPUT_BUFFER, + VENC_MSG_FLUSH, + VENC_MSG_START, + VENC_MSG_STOP, + VENC_MSG_PAUSE, + VENC_MSG_RESUME, + VENC_MSG_STOP_READING_MSG +}; + +enum venc_error_code { + VENC_S_SUCCESS, + VENC_S_EFAIL, + VENC_S_EFATAL, + VENC_S_EBADPARAM, + VENC_S_EINVALSTATE, + VENC_S_ENOSWRES, + VENC_S_ENOHWRES, + VENC_S_EBUFFREQ, + VENC_S_EINVALCMD, + VENC_S_ETIMEOUT, + VENC_S_ENOREATMPT, + VENC_S_ENOPREREQ, + VENC_S_ECMDQFULL, + VENC_S_ENOTSUPP, + VENC_S_ENOTIMPL, + VENC_S_ENOTPMEM, + VENC_S_EFLUSHED, + VENC_S_EINSUFBUF, + VENC_S_ESAMESTATE, + VENC_S_EINVALTRANS +}; + +enum venc_mem_region_enum { + VENC_PMEM_EBI1, + VENC_PMEM_SMI +}; + +struct venc_buf_type { + unsigned int region; + unsigned int phys; + unsigned int size; + int offset; +}; + +struct venc_qp_range { + unsigned int min_qp; + unsigned int max_qp; +}; + +struct venc_frame_rate { + unsigned int frame_rate_num; + unsigned int frame_rate_den; +}; + +struct venc_slice_info { + unsigned int slice_mode; + unsigned int units_per_slice; +}; + +struct venc_extra_data { + unsigned int slice_extra_data_flag; + unsigned int slice_client_data1; + unsigned int slice_client_data2; + unsigned int slice_client_data3; + unsigned int none_extra_data_flag; + unsigned int none_client_data1; + unsigned int none_client_data2; + unsigned int none_client_data3; +}; + +struct venc_common_config { + unsigned int standard; + unsigned int input_frame_height; + unsigned int input_frame_width; + unsigned int output_frame_height; + unsigned int output_frame_width; + unsigned int rotation_angle; + unsigned int intra_period; + unsigned int rate_control; + struct venc_frame_rate frame_rate; + unsigned int bitrate; + struct venc_qp_range qp_range; + unsigned int iframe_qp; + unsigned int pframe_qp; + struct venc_slice_info slice_config; + struct venc_extra_data extra_data; +}; + +struct venc_nonio_buf_config { + struct venc_buf_type recon_buf1; + struct venc_buf_type recon_buf2; + struct venc_buf_type wb_buf; + struct venc_buf_type cmd_buf; + struct venc_buf_type vlc_buf; +}; + +struct venc_mpeg4_config { + unsigned int profile; + unsigned int level; + unsigned int time_resolution; + unsigned int ac_prediction; + unsigned int hec_interval; + unsigned int data_partition; + unsigned int short_header; + unsigned int rvlc_enable; +}; + +struct venc_h263_config { + unsigned int profile; + unsigned int level; +}; + +struct venc_h264_config { + unsigned int profile; + unsigned int level; + unsigned int max_nal; + unsigned int idr_period; +}; + +struct venc_pmem { + int src; int fd; - unsigned long offset; - unsigned long size; + unsigned int offset; + void *virt; + void *phys; + unsigned int size; }; -struct q6_init_config { - unsigned short venc_standard; - unsigned short partial_run_length_flag; - unsigned short h263_annex_ispt; - unsigned short h263_annex_jspt; - unsigned short h263_annex_tspt; - unsigned short rc_flag; - unsigned short one_mv_flag; - unsigned short acdc_pred_enable; - unsigned short rounding_bit_ctrl; - unsigned short rotation_flag; - unsigned short max_mvx; - unsigned short max_mvy; - unsigned short enc_frame_height_inmb; - unsigned short enc_frame_width_inmb; - unsigned short dvs_frame_height; - unsigned short dvs_frame_width; +struct venc_buffer { + unsigned char *ptr_buffer; + unsigned int size; + unsigned int len; + unsigned int offset; + long long time_stamp; + unsigned int flags; + unsigned int client_data; - unsigned int ref_frame_buf1_phy; - unsigned int ref_frame_buf2_phy; - unsigned int rlc_buf1_phy; - unsigned int rlc_buf2_phy; - unsigned int rlc_buf_length; }; -struct init_config { - struct venc_buf ref_frame_buf1; - struct venc_buf ref_frame_buf2; - struct venc_buf rlc_buf1; - struct venc_buf rlc_buf2; - struct q6_init_config q6_init_config; +struct venc_buffers { + struct venc_pmem recon_buf[VENC_MAX_RECON_BUFFERS]; + struct venc_pmem wb_buf; + struct venc_pmem cmd_buf; + struct venc_pmem vlc_buf; }; -struct q6_encode_param { - unsigned int luma_addr; - unsigned int chroma_addr; - unsigned int x_offset; - unsigned int y_offset; - unsigned int frame_rho_budget; - unsigned int frame_type; - unsigned int qp; +struct venc_buffer_flush { + unsigned int flush_mode; }; -struct encode_param { - struct venc_buf y_addr; - unsigned long uv_offset; - struct q6_encode_param q6_encode_param; +union venc_msg_data { + struct venc_buffer buf; + struct venc_buffer_flush flush_ret; + }; -struct intra_refresh { - unsigned int intra_refresh_enable; - unsigned int intra_mb_num; +struct venc_msg { + unsigned int status_code; + unsigned int msg_code; + union venc_msg_data msg_data; + unsigned int msg_data_size; }; -struct rc_config { - unsigned short max_frame_qp_up_delta; - unsigned short max_frame_qp_down_delta; - unsigned short min_frame_qp; - unsigned short max_frame_qp; +union venc_codec_config { + struct venc_mpeg4_config mpeg4_params; + struct venc_h263_config h263_params; + struct venc_h264_config h264_params; }; -struct q6_frame_type { - unsigned int frame_type; - unsigned int frame_len; - unsigned int frame_addr; - unsigned int map_table; +struct venc_q6_config { + struct venc_common_config config_params; + union venc_codec_config codec_params; + struct venc_nonio_buf_config buf_params; + void *callback_event; }; -struct frame_type { - struct venc_buf frame_addr; - struct q6_frame_type q6_frame_type; +struct venc_hdr_config { + struct venc_common_config config_params; + union venc_codec_config codec_params; +}; + +struct venc_init_config { + struct venc_q6_config q6_config; + struct venc_buffers q6_bufs; +}; + +struct venc_seq_config { + int size; + struct venc_pmem buf; + struct venc_q6_config q6_config; }; #define VENC_IOCTL_MAGIC 'V' -#define VENC_IOCTL_INITIALIZE _IOW(VENC_IOCTL_MAGIC, 1, struct init_config) -#define VENC_IOCTL_ENCODE _IOW(VENC_IOCTL_MAGIC, 2, struct encode_param) -#define VENC_IOCTL_INTRA_REFRESH _IOW(VENC_IOCTL_MAGIC, 3, struct intra_refresh) -#define VENC_IOCTL_RC_CONFIG _IOW(VENC_IOCTL_MAGIC, 4, struct rc_config) -#define VENC_IOCTL_ENCODE_CONFIG _IOW(VENC_IOCTL_MAGIC, 5, struct init_config) -#define VENC_IOCTL_STOP _IO(VENC_IOCTL_MAGIC, 6) -#define VENC_IOCTL_WAIT_FOR_ENCODE _IOR(VENC_IOCTL_MAGIC, 7, struct frame_type) -#define VENC_IOCTL_STOP_ENCODE _IO(VENC_IOCTL_MAGIC, 8) +#define VENC_IOCTL_CMD_READ_NEXT_MSG _IOWR(VENC_IOCTL_MAGIC, 1, struct venc_msg) -#endif +#define VENC_IOCTL_CMD_STOP_READ_MSG _IO(VENC_IOCTL_MAGIC, 2) + +#define VENC_IOCTL_SET_INPUT_BUFFER _IOW(VENC_IOCTL_MAGIC, 3, struct venc_pmem) + +#define VENC_IOCTL_SET_OUTPUT_BUFFER _IOW(VENC_IOCTL_MAGIC, 4, struct venc_pmem) + +#define VENC_IOCTL_CMD_START _IOW(VENC_IOCTL_MAGIC, 5, struct venc_init_config) +#define VENC_IOCTL_CMD_ENCODE_FRAME _IOW(VENC_IOCTL_MAGIC, 6, struct venc_buffer) + +#define VENC_IOCTL_CMD_FILL_OUTPUT_BUFFER _IOW(VENC_IOCTL_MAGIC, 7, struct venc_buffer) + +#define VENC_IOCTL_CMD_FLUSH _IOW(VENC_IOCTL_MAGIC, 8, struct venc_buffer_flush) + +#define VENC_IOCTL_CMD_PAUSE _IO(VENC_IOCTL_MAGIC, 9) + +#define VENC_IOCTL_CMD_RESUME _IO(VENC_IOCTL_MAGIC, 10) + +#define VENC_IOCTL_CMD_STOP _IO(VENC_IOCTL_MAGIC, 11) + +#define VENC_IOCTL_SET_INTRA_PERIOD _IOW(VENC_IOCTL_MAGIC, 12, int) + +#define VENC_IOCTL_CMD_REQUEST_IFRAME _IO(VENC_IOCTL_MAGIC, 13) + +#define VENC_IOCTL_GET_SEQUENCE_HDR _IOWR(VENC_IOCTL_MAGIC, 14, struct venc_seq_config) + +#define VENC_IOCTL_SET_INTRA_REFRESH _IOW(VENC_IOCTL_MAGIC, 15, int) + +#define VENC_IOCTL_SET_FRAME_RATE _IOW(VENC_IOCTL_MAGIC, 16, struct venc_frame_rate) + +#define VENC_IOCTL_SET_TARGET_BITRATE _IOW(VENC_IOCTL_MAGIC, 17, int) + +#define VENC_IOCTL_SET_QP_RANGE _IOW(VENC_IOCTL_MAGIC, 18, struct venc_qp_range) + +#endif diff --git a/libc/kernel/common/linux/neighbour.h b/libc/kernel/common/linux/neighbour.h new file mode 100644 index 0000000..2189af0 --- /dev/null +++ b/libc/kernel/common/linux/neighbour.h @@ -0,0 +1,133 @@ +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + **************************************************************************** + ****************************************************************************/ +#ifndef __LINUX_NEIGHBOUR_H +#define __LINUX_NEIGHBOUR_H + +#include <linux/types.h> +#include <linux/netlink.h> + +struct ndmsg +{ + __u8 ndm_family; + __u8 ndm_pad1; + __u16 ndm_pad2; + __s32 ndm_ifindex; + __u16 ndm_state; + __u8 ndm_flags; + __u8 ndm_type; +}; + +enum +{ + NDA_UNSPEC, + NDA_DST, + NDA_LLADDR, + NDA_CACHEINFO, + NDA_PROBES, + __NDA_MAX +}; + +#define NDA_MAX (__NDA_MAX - 1) + +#define NTF_USE 0x01 +#define NTF_PROXY 0x08 +#define NTF_ROUTER 0x80 + +#define NUD_INCOMPLETE 0x01 +#define NUD_REACHABLE 0x02 +#define NUD_STALE 0x04 +#define NUD_DELAY 0x08 +#define NUD_PROBE 0x10 +#define NUD_FAILED 0x20 + +#define NUD_NOARP 0x40 +#define NUD_PERMANENT 0x80 +#define NUD_NONE 0x00 + +struct nda_cacheinfo +{ + __u32 ndm_confirmed; + __u32 ndm_used; + __u32 ndm_updated; + __u32 ndm_refcnt; +}; + +struct ndt_stats +{ + __u64 ndts_allocs; + __u64 ndts_destroys; + __u64 ndts_hash_grows; + __u64 ndts_res_failed; + __u64 ndts_lookups; + __u64 ndts_hits; + __u64 ndts_rcv_probes_mcast; + __u64 ndts_rcv_probes_ucast; + __u64 ndts_periodic_gc_runs; + __u64 ndts_forced_gc_runs; +}; + +enum { + NDTPA_UNSPEC, + NDTPA_IFINDEX, + NDTPA_REFCNT, + NDTPA_REACHABLE_TIME, + NDTPA_BASE_REACHABLE_TIME, + NDTPA_RETRANS_TIME, + NDTPA_GC_STALETIME, + NDTPA_DELAY_PROBE_TIME, + NDTPA_QUEUE_LEN, + NDTPA_APP_PROBES, + NDTPA_UCAST_PROBES, + NDTPA_MCAST_PROBES, + NDTPA_ANYCAST_DELAY, + NDTPA_PROXY_DELAY, + NDTPA_PROXY_QLEN, + NDTPA_LOCKTIME, + __NDTPA_MAX +}; +#define NDTPA_MAX (__NDTPA_MAX - 1) + +struct ndtmsg +{ + __u8 ndtm_family; + __u8 ndtm_pad1; + __u16 ndtm_pad2; +}; + +struct ndt_config +{ + __u16 ndtc_key_len; + __u16 ndtc_entry_size; + __u32 ndtc_entries; + __u32 ndtc_last_flush; + __u32 ndtc_last_rand; + __u32 ndtc_hash_rnd; + __u32 ndtc_hash_mask; + __u32 ndtc_hash_chain_gc; + __u32 ndtc_proxy_qlen; +}; + +enum { + NDTA_UNSPEC, + NDTA_NAME, + NDTA_THRESH1, + NDTA_THRESH2, + NDTA_THRESH3, + NDTA_CONFIG, + NDTA_PARMS, + NDTA_STATS, + NDTA_GC_INTERVAL, + __NDTA_MAX +}; +#define NDTA_MAX (__NDTA_MAX - 1) + +#endif diff --git a/libc/kernel/common/linux/netlink.h b/libc/kernel/common/linux/netlink.h index b6f1c06..75e889a 100644 --- a/libc/kernel/common/linux/netlink.h +++ b/libc/kernel/common/linux/netlink.h @@ -33,8 +33,13 @@ #define NETLINK_KOBJECT_UEVENT 15 #define NETLINK_GENERIC 16 +#define NETLINK_SCSITRANSPORT 18 +#define NETLINK_ECRYPTFS 19 + #define MAX_LINKS 32 +struct net; + struct sockaddr_nl { sa_family_t nl_family; @@ -93,6 +98,8 @@ struct nlmsgerr #define NETLINK_ADD_MEMBERSHIP 1 #define NETLINK_DROP_MEMBERSHIP 2 #define NETLINK_PKTINFO 3 +#define NETLINK_BROADCAST_ERROR 4 +#define NETLINK_NO_ENOBUFS 5 struct nl_pktinfo { @@ -112,6 +119,10 @@ struct nlattr __u16 nla_type; }; +#define NLA_F_NESTED (1 << 15) +#define NLA_F_NET_BYTEORDER (1 << 14) +#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) + #define NLA_ALIGNTO 4 #define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) #define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) diff --git a/libc/kernel/common/linux/pkt_cls.h b/libc/kernel/common/linux/pkt_cls.h index 601a683..ffa29f7 100644 --- a/libc/kernel/common/linux/pkt_cls.h +++ b/libc/kernel/common/linux/pkt_cls.h @@ -12,6 +12,7 @@ #ifndef __LINUX_PKT_CLS_H #define __LINUX_PKT_CLS_H +#include <linux/types.h> #include <linux/pkt_sched.h> #define _TC_MAKE32(x) ((x)) @@ -179,8 +180,8 @@ enum struct tc_u32_key { - __u32 mask; - __u32 val; + __be32 mask; + __be32 val; int off; int offmask; }; @@ -191,12 +192,12 @@ struct tc_u32_sel unsigned char offshift; unsigned char nkeys; - __u16 offmask; + __be16 offmask; __u16 off; short offoff; short hoff; - __u32 hmask; + __be32 hmask; struct tc_u32_key keys[0]; }; @@ -273,6 +274,7 @@ enum TCA_FW_POLICE, TCA_FW_INDEV, TCA_FW_ACT, + TCA_FW_MASK, __TCA_FW_MAX }; @@ -295,6 +297,56 @@ enum enum { + FLOW_KEY_SRC, + FLOW_KEY_DST, + FLOW_KEY_PROTO, + FLOW_KEY_PROTO_SRC, + FLOW_KEY_PROTO_DST, + FLOW_KEY_IIF, + FLOW_KEY_PRIORITY, + FLOW_KEY_MARK, + FLOW_KEY_NFCT, + FLOW_KEY_NFCT_SRC, + FLOW_KEY_NFCT_DST, + FLOW_KEY_NFCT_PROTO_SRC, + FLOW_KEY_NFCT_PROTO_DST, + FLOW_KEY_RTCLASSID, + FLOW_KEY_SKUID, + FLOW_KEY_SKGID, + FLOW_KEY_VLAN_TAG, + __FLOW_KEY_MAX, +}; + +#define FLOW_KEY_MAX (__FLOW_KEY_MAX - 1) + +enum +{ + FLOW_MODE_MAP, + FLOW_MODE_HASH, +}; + +enum +{ + TCA_FLOW_UNSPEC, + TCA_FLOW_KEYS, + TCA_FLOW_MODE, + TCA_FLOW_BASECLASS, + TCA_FLOW_RSHIFT, + TCA_FLOW_ADDEND, + TCA_FLOW_MASK, + TCA_FLOW_XOR, + TCA_FLOW_DIVISOR, + TCA_FLOW_ACT, + TCA_FLOW_POLICE, + TCA_FLOW_EMATCHES, + TCA_FLOW_PERTURB, + __TCA_FLOW_MAX +}; + +#define TCA_FLOW_MAX (__TCA_FLOW_MAX - 1) + +enum +{ TCA_BASIC_UNSPEC, TCA_BASIC_CLASSID, TCA_BASIC_EMATCHES, @@ -305,6 +357,17 @@ enum #define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1) +enum +{ + TCA_CGROUP_UNSPEC, + TCA_CGROUP_ACT, + TCA_CGROUP_POLICE, + TCA_CGROUP_EMATCHES, + __TCA_CGROUP_MAX, +}; + +#define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1) + struct tcf_ematch_tree_hdr { __u16 nmatches; @@ -346,16 +409,14 @@ enum }; #define TCF_LAYER_MAX (__TCF_LAYER_MAX - 1) -enum -{ - TCF_EM_CONTAINER, - TCF_EM_CMP, - TCF_EM_NBYTE, - TCF_EM_U32, - TCF_EM_META, - TCF_EM_TEXT, - __TCF_EM_MAX -}; +#define TCF_EM_CONTAINER 0 +#define TCF_EM_CMP 1 +#define TCF_EM_NBYTE 2 +#define TCF_EM_U32 3 +#define TCF_EM_META 4 +#define TCF_EM_TEXT 5 +#define TCF_EM_VLAN 6 +#define TCF_EM_MAX 6 enum { diff --git a/libc/kernel/common/linux/pkt_sched.h b/libc/kernel/common/linux/pkt_sched.h index 1e15d83..0b2966a 100644 --- a/libc/kernel/common/linux/pkt_sched.h +++ b/libc/kernel/common/linux/pkt_sched.h @@ -12,6 +12,8 @@ #ifndef __LINUX_PKT_SCHED_H #define __LINUX_PKT_SCHED_H +#include <linux/types.h> + #define TC_PRIO_BESTEFFORT 0 #define TC_PRIO_FILLER 1 #define TC_PRIO_BULK 2 @@ -53,12 +55,34 @@ struct tc_ratespec { unsigned char cell_log; unsigned char __reserved; - unsigned short feature; - short addend; + unsigned short overhead; + short cell_align; unsigned short mpu; __u32 rate; }; +#define TC_RTAB_SIZE 1024 + +struct tc_sizespec { + unsigned char cell_log; + unsigned char size_log; + short cell_align; + int overhead; + unsigned int linklayer; + unsigned int mpu; + unsigned int mtu; + unsigned int tsize; +}; + +enum { + TCA_STAB_UNSPEC, + TCA_STAB_BASE, + TCA_STAB_DATA, + __TCA_STAB_MAX +}; + +#define TCA_STAB_MAX (__TCA_STAB_MAX - 1) + struct tc_fifo_qopt { __u32 limit; @@ -73,6 +97,11 @@ struct tc_prio_qopt __u8 priomap[TC_PRIO_MAX+1]; }; +struct tc_multiq_qopt { + __u16 bands; + __u16 max_bands; +}; + struct tc_tbf_qopt { struct tc_ratespec rate; @@ -102,6 +131,11 @@ struct tc_sfq_qopt unsigned flows; }; +struct tc_sfq_xstats +{ + __s32 allot; +}; + enum { TCA_RED_UNSPEC, @@ -402,4 +436,18 @@ struct tc_netem_corrupt #define NETEM_DIST_SCALE 8192 +enum +{ + TCA_DRR_UNSPEC, + TCA_DRR_QUANTUM, + __TCA_DRR_MAX +}; + +#define TCA_DRR_MAX (__TCA_DRR_MAX - 1) + +struct tc_drr_stats +{ + __u32 deficit; +}; + #endif diff --git a/libc/kernel/common/linux/rtnetlink.h b/libc/kernel/common/linux/rtnetlink.h index ddcffaa..e305505 100644 --- a/libc/kernel/common/linux/rtnetlink.h +++ b/libc/kernel/common/linux/rtnetlink.h @@ -12,7 +12,11 @@ #ifndef __LINUX_RTNETLINK_H #define __LINUX_RTNETLINK_H +#include <linux/types.h> #include <linux/netlink.h> +#include <linux/if_link.h> +#include <linux/if_addr.h> +#include <linux/neighbour.h> enum { RTM_BASE = 16, @@ -85,8 +89,6 @@ enum { RTM_NEWPREFIX = 52, #define RTM_NEWPREFIX RTM_NEWPREFIX - RTM_GETPREFIX = 54, -#define RTM_GETPREFIX RTM_GETPREFIX RTM_GETMULTICAST = 58, #define RTM_GETMULTICAST RTM_GETMULTICAST @@ -101,6 +103,21 @@ enum { RTM_SETNEIGHTBL, #define RTM_SETNEIGHTBL RTM_SETNEIGHTBL + RTM_NEWNDUSEROPT = 68, +#define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT + + RTM_NEWADDRLABEL = 72, +#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL + RTM_DELADDRLABEL, +#define RTM_DELADDRLABEL RTM_DELADDRLABEL + RTM_GETADDRLABEL, +#define RTM_GETADDRLABEL RTM_GETADDRLABEL + + RTM_GETDCB = 78, +#define RTM_GETDCB RTM_GETDCB + RTM_SETDCB, +#define RTM_SETDCB RTM_SETDCB + __RTM_MAX, #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) }; @@ -172,6 +189,7 @@ enum #define RTPROT_DNROUTED 13 #define RTPROT_XORP 14 #define RTPROT_NTK 15 +#define RTPROT_DHCP 16 enum rt_scope_t { @@ -192,12 +210,12 @@ enum rt_class_t { RT_TABLE_UNSPEC=0, + RT_TABLE_COMPAT=252, RT_TABLE_DEFAULT=253, RT_TABLE_MAIN=254, RT_TABLE_LOCAL=255, - __RT_TABLE_MAX + RT_TABLE_MAX=0xFFFFFFFF }; -#define RT_TABLE_MAX (__RT_TABLE_MAX - 1) enum rtattr_type_t { @@ -216,6 +234,7 @@ enum rtattr_type_t RTA_CACHEINFO, RTA_SESSION, RTA_MP_ALGO, + RTA_TABLE, __RTA_MAX }; @@ -286,6 +305,8 @@ enum #define RTAX_INITCWND RTAX_INITCWND RTAX_FEATURES, #define RTAX_FEATURES RTAX_FEATURES + RTAX_RTO_MIN, +#define RTAX_RTO_MIN RTAX_RTO_MIN __RTAX_MAX }; @@ -318,168 +339,6 @@ struct rta_session } u; }; -struct ifaddrmsg -{ - unsigned char ifa_family; - unsigned char ifa_prefixlen; - unsigned char ifa_flags; - unsigned char ifa_scope; - int ifa_index; -}; - -enum -{ - IFA_UNSPEC, - IFA_ADDRESS, - IFA_LOCAL, - IFA_LABEL, - IFA_BROADCAST, - IFA_ANYCAST, - IFA_CACHEINFO, - IFA_MULTICAST, - __IFA_MAX -}; - -#define IFA_MAX (__IFA_MAX - 1) - -#define IFA_F_SECONDARY 0x01 -#define IFA_F_TEMPORARY IFA_F_SECONDARY - -#define IFA_F_DEPRECATED 0x20 -#define IFA_F_TENTATIVE 0x40 -#define IFA_F_PERMANENT 0x80 - -struct ifa_cacheinfo -{ - __u32 ifa_prefered; - __u32 ifa_valid; - __u32 cstamp; - __u32 tstamp; -}; - -#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) -#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) - -struct ndmsg -{ - unsigned char ndm_family; - unsigned char ndm_pad1; - unsigned short ndm_pad2; - int ndm_ifindex; - __u16 ndm_state; - __u8 ndm_flags; - __u8 ndm_type; -}; - -enum -{ - NDA_UNSPEC, - NDA_DST, - NDA_LLADDR, - NDA_CACHEINFO, - NDA_PROBES, - __NDA_MAX -}; - -#define NDA_MAX (__NDA_MAX - 1) - -#define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) -#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg)) - -#define NTF_PROXY 0x08 -#define NTF_ROUTER 0x80 - -#define NUD_INCOMPLETE 0x01 -#define NUD_REACHABLE 0x02 -#define NUD_STALE 0x04 -#define NUD_DELAY 0x08 -#define NUD_PROBE 0x10 -#define NUD_FAILED 0x20 - -#define NUD_NOARP 0x40 -#define NUD_PERMANENT 0x80 -#define NUD_NONE 0x00 - -struct nda_cacheinfo -{ - __u32 ndm_confirmed; - __u32 ndm_used; - __u32 ndm_updated; - __u32 ndm_refcnt; -}; - -struct ndt_stats -{ - __u64 ndts_allocs; - __u64 ndts_destroys; - __u64 ndts_hash_grows; - __u64 ndts_res_failed; - __u64 ndts_lookups; - __u64 ndts_hits; - __u64 ndts_rcv_probes_mcast; - __u64 ndts_rcv_probes_ucast; - __u64 ndts_periodic_gc_runs; - __u64 ndts_forced_gc_runs; -}; - -enum { - NDTPA_UNSPEC, - NDTPA_IFINDEX, - NDTPA_REFCNT, - NDTPA_REACHABLE_TIME, - NDTPA_BASE_REACHABLE_TIME, - NDTPA_RETRANS_TIME, - NDTPA_GC_STALETIME, - NDTPA_DELAY_PROBE_TIME, - NDTPA_QUEUE_LEN, - NDTPA_APP_PROBES, - NDTPA_UCAST_PROBES, - NDTPA_MCAST_PROBES, - NDTPA_ANYCAST_DELAY, - NDTPA_PROXY_DELAY, - NDTPA_PROXY_QLEN, - NDTPA_LOCKTIME, - __NDTPA_MAX -}; -#define NDTPA_MAX (__NDTPA_MAX - 1) - -struct ndtmsg -{ - __u8 ndtm_family; - __u8 ndtm_pad1; - __u16 ndtm_pad2; -}; - -struct ndt_config -{ - __u16 ndtc_key_len; - __u16 ndtc_entry_size; - __u32 ndtc_entries; - __u32 ndtc_last_flush; - __u32 ndtc_last_rand; - __u32 ndtc_hash_rnd; - __u32 ndtc_hash_mask; - __u32 ndtc_hash_chain_gc; - __u32 ndtc_proxy_qlen; -}; - -enum { - NDTA_UNSPEC, - NDTA_NAME, - NDTA_THRESH1, - NDTA_THRESH2, - NDTA_THRESH3, - NDTA_CONFIG, - NDTA_PARMS, - NDTA_STATS, - NDTA_GC_INTERVAL, - __NDTA_MAX -}; -#define NDTA_MAX (__NDTA_MAX - 1) - -#define NDTA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg)))) -#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg)) - struct rtgenmsg { unsigned char rtgen_family; @@ -523,103 +382,6 @@ struct prefix_cacheinfo __u32 valid_time; }; -struct rtnl_link_stats -{ - __u32 rx_packets; - __u32 tx_packets; - __u32 rx_bytes; - __u32 tx_bytes; - __u32 rx_errors; - __u32 tx_errors; - __u32 rx_dropped; - __u32 tx_dropped; - __u32 multicast; - __u32 collisions; - - __u32 rx_length_errors; - __u32 rx_over_errors; - __u32 rx_crc_errors; - __u32 rx_frame_errors; - __u32 rx_fifo_errors; - __u32 rx_missed_errors; - - __u32 tx_aborted_errors; - __u32 tx_carrier_errors; - __u32 tx_fifo_errors; - __u32 tx_heartbeat_errors; - __u32 tx_window_errors; - - __u32 rx_compressed; - __u32 tx_compressed; -}; - -struct rtnl_link_ifmap -{ - __u64 mem_start; - __u64 mem_end; - __u64 base_addr; - __u16 irq; - __u8 dma; - __u8 port; -}; - -enum -{ - IFLA_UNSPEC, - IFLA_ADDRESS, - IFLA_BROADCAST, - IFLA_IFNAME, - IFLA_MTU, - IFLA_LINK, - IFLA_QDISC, - IFLA_STATS, - IFLA_COST, -#define IFLA_COST IFLA_COST - IFLA_PRIORITY, -#define IFLA_PRIORITY IFLA_PRIORITY - IFLA_MASTER, -#define IFLA_MASTER IFLA_MASTER - IFLA_WIRELESS, -#define IFLA_WIRELESS IFLA_WIRELESS - IFLA_PROTINFO, -#define IFLA_PROTINFO IFLA_PROTINFO - IFLA_TXQLEN, -#define IFLA_TXQLEN IFLA_TXQLEN - IFLA_MAP, -#define IFLA_MAP IFLA_MAP - IFLA_WEIGHT, -#define IFLA_WEIGHT IFLA_WEIGHT - IFLA_OPERSTATE, - IFLA_LINKMODE, - __IFLA_MAX -}; - -#define IFLA_MAX (__IFLA_MAX - 1) - -#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) -#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) - -enum -{ - IFLA_INET6_UNSPEC, - IFLA_INET6_FLAGS, - IFLA_INET6_CONF, - IFLA_INET6_STATS, - IFLA_INET6_MCAST, - IFLA_INET6_CACHEINFO, - __IFLA_INET6_MAX -}; - -#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) - -struct ifla_cacheinfo -{ - __u32 max_reasm_len; - __u32 tstamp; - __u32 reachable_time; - __u32 retrans_time; -}; - struct tcmsg { unsigned char tcm_family; @@ -641,6 +403,7 @@ enum TCA_RATE, TCA_FCNT, TCA_STATS2, + TCA_STAB, __TCA_MAX }; @@ -649,6 +412,28 @@ enum #define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg)))) #define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg)) +struct nduseroptmsg +{ + unsigned char nduseropt_family; + unsigned char nduseropt_pad1; + unsigned short nduseropt_opts_len; + int nduseropt_ifindex; + __u8 nduseropt_icmp_type; + __u8 nduseropt_icmp_code; + unsigned short nduseropt_pad2; + unsigned int nduseropt_pad3; + +}; + +enum +{ + NDUSEROPT_UNSPEC, + NDUSEROPT_SRCADDR, + __NDUSEROPT_MAX +}; + +#define NDUSEROPT_MAX (__NDUSEROPT_MAX - 1) + #define RTMGRP_LINK 1 #define RTMGRP_NOTIFY 2 #define RTMGRP_NEIGH 4 @@ -701,10 +486,19 @@ enum rtnetlink_groups { RTNLGRP_NOP2, RTNLGRP_DECnet_ROUTE, #define RTNLGRP_DECnet_ROUTE RTNLGRP_DECnet_ROUTE - RTNLGRP_NOP3, + RTNLGRP_DECnet_RULE, +#define RTNLGRP_DECnet_RULE RTNLGRP_DECnet_RULE RTNLGRP_NOP4, RTNLGRP_IPV6_PREFIX, #define RTNLGRP_IPV6_PREFIX RTNLGRP_IPV6_PREFIX + RTNLGRP_IPV6_RULE, +#define RTNLGRP_IPV6_RULE RTNLGRP_IPV6_RULE + RTNLGRP_ND_USEROPT, +#define RTNLGRP_ND_USEROPT RTNLGRP_ND_USEROPT + RTNLGRP_PHONET_IFADDR, +#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR + RTNLGRP_PHONET_ROUTE, +#define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE __RTNLGRP_MAX }; #define RTNLGRP_MAX (__RTNLGRP_MAX - 1) diff --git a/libc/kernel/common/linux/swab.h b/libc/kernel/common/linux/swab.h new file mode 100644 index 0000000..8f7d0d6 --- /dev/null +++ b/libc/kernel/common/linux/swab.h @@ -0,0 +1,80 @@ +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + **************************************************************************** + ****************************************************************************/ +#ifndef _LINUX_SWAB_H +#define _LINUX_SWAB_H + +#include <linux/types.h> +#include <linux/compiler.h> +#include <asm/swab.h> + +#define ___constant_swab16(x) ((__u16)( (((__u16)(x) & (__u16)0x00ffU) << 8) | (((__u16)(x) & (__u16)0xff00U) >> 8))) + +#define ___constant_swab32(x) ((__u32)( (((__u32)(x) & (__u32)0x000000ffUL) << 24) | (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | (((__u32)(x) & (__u32)0xff000000UL) >> 24))) + +#define ___constant_swab64(x) ((__u64)( (((__u64)(x) & (__u64)0x00000000000000ffULL) << 56) | (((__u64)(x) & (__u64)0x000000000000ff00ULL) << 40) | (((__u64)(x) & (__u64)0x0000000000ff0000ULL) << 24) | (((__u64)(x) & (__u64)0x00000000ff000000ULL) << 8) | (((__u64)(x) & (__u64)0x000000ff00000000ULL) >> 8) | (((__u64)(x) & (__u64)0x0000ff0000000000ULL) >> 24) | (((__u64)(x) & (__u64)0x00ff000000000000ULL) >> 40) | (((__u64)(x) & (__u64)0xff00000000000000ULL) >> 56))) + +#define ___constant_swahw32(x) ((__u32)( (((__u32)(x) & (__u32)0x0000ffffUL) << 16) | (((__u32)(x) & (__u32)0xffff0000UL) >> 16))) + +#define ___constant_swahb32(x) ((__u32)( (((__u32)(x) & (__u32)0x00ff00ffUL) << 8) | (((__u32)(x) & (__u32)0xff00ff00UL) >> 8))) + +#ifdef __arch_swab16 +#else +#endif +#ifdef __arch_swab32 +#else +#endif +#ifdef __arch_swab64 +#elif defined(__SWAB_64_THRU_32__) +#else +#endif +#ifdef __arch_swahw32 +#else +#endif +#ifdef __arch_swahb32 +#else +#endif +#define __swab16(x) (__builtin_constant_p((__u16)(x)) ? ___constant_swab16(x) : __fswab16(x)) +#define __swab32(x) (__builtin_constant_p((__u32)(x)) ? ___constant_swab32(x) : __fswab32(x)) +#define __swab64(x) (__builtin_constant_p((__u64)(x)) ? ___constant_swab64(x) : __fswab64(x)) +#define __swahw32(x) (__builtin_constant_p((__u32)(x)) ? ___constant_swahw32(x) : __fswahw32(x)) +#define __swahb32(x) (__builtin_constant_p((__u32)(x)) ? ___constant_swahb32(x) : __fswahb32(x)) +#ifdef __arch_swab16p +#else +#endif +#ifdef __arch_swab32p +#else +#endif +#ifdef __arch_swab64p +#else +#endif +#ifdef __arch_swahw32p +#else +#endif +#ifdef __arch_swahb32p +#else +#endif +#ifdef __arch_swab16s +#else +#endif +#ifdef __arch_swab32s +#else +#endif +#ifdef __arch_swab64s +#else +#endif +#ifdef __arch_swahw32s +#else +#endif +#ifdef __arch_swahb32s +#else +#endif +#endif diff --git a/libc/kernel/common/media/msm_camera.h b/libc/kernel/common/media/msm_camera.h index a0b4a24..da3951b 100644 --- a/libc/kernel/common/media/msm_camera.h +++ b/libc/kernel/common/media/msm_camera.h @@ -70,6 +70,8 @@ #define MSM_CAM_IOCTL_CTRL_COMMAND_2 _IOW(MSM_CAM_IOCTL_MAGIC, 24, struct msm_ctrl_cmd *) +#define MSM_CAM_IOCTL_ENABLE_OUTPUT_IND _IOW(MSM_CAM_IOCTL_MAGIC, 25, uint32_t *) + #define MAX_SENSOR_NUM 3 #define MAX_SENSOR_NAME 32 @@ -178,7 +180,7 @@ struct msm_pmem_info { uint32_t len; uint32_t y_off; uint32_t cbcr_off; - uint8_t active; + uint8_t vfe_can_write; }; struct outputCfg { diff --git a/libc/netbsd/net/getaddrinfo.c b/libc/netbsd/net/getaddrinfo.c index 1233cb8..51079ae 100644 --- a/libc/netbsd/net/getaddrinfo.c +++ b/libc/netbsd/net/getaddrinfo.c @@ -217,7 +217,6 @@ static int ip6_str2scopeid(char *, struct sockaddr_in6 *, u_int32_t *); static struct addrinfo *getanswer(const querybuf *, int, const char *, int, const struct addrinfo *); -static void aisort(struct addrinfo *s, res_state res); static int _dns_getaddrinfo(void *, void *, va_list); static void _sethtent(FILE **); static void _endhtent(FILE **); @@ -942,7 +941,7 @@ get_port(const struct addrinfo *ai, const char *servname, int matchonly) allownumeric = 1; break; case ANY: -#if 1 /* ANDROID-SPECIFIC CHANGE TO MATCH GLIBC */ +#if 1 /* ANDROID-SPECIFIC CHANGE TO MATCH GLIBC */ allownumeric = 1; #else allownumeric = 0; @@ -1259,35 +1258,366 @@ getanswer(const querybuf *answer, int anslen, const char *qname, int qtype, return NULL; } -#define SORTEDADDR(p) (((struct sockaddr_in *)(void *)(p->ai_next->ai_addr))->sin_addr.s_addr) -#define SORTMATCH(p, s) ((SORTEDADDR(p) & (s).mask) == (s).addr.s_addr) +struct addrinfo_sort_elem { + struct addrinfo *ai; + int has_src_addr; + struct sockaddr_in6 src_addr; /* Large enough to hold IPv4 or IPv6. */ + int original_order; +}; -static void -aisort(struct addrinfo *s, res_state res) +/*ARGSUSED*/ +static int +_get_scope(const struct sockaddr *addr) +{ + if (addr->sa_family == AF_INET6) { + const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; + if (IN6_IS_ADDR_MULTICAST(&addr6->sin6_addr)) { + return IPV6_ADDR_MC_SCOPE(&addr6->sin6_addr); + } else if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr) || + IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr)) { + /* + * RFC 4291 section 2.5.3 says loopback is to be treated as having + * link-local scope. + */ + return IPV6_ADDR_SCOPE_LINKLOCAL; + } else if (IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr)) { + return IPV6_ADDR_SCOPE_SITELOCAL; + } else { + return IPV6_ADDR_SCOPE_GLOBAL; + } + } else if (addr->sa_family == AF_INET) { + const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; + unsigned long int na = ntohl(addr4->sin_addr.s_addr); + + if (IN_LOOPBACK(na) || /* 127.0.0.0/8 */ + (na & 0xffff0000) == 0xa9fe0000) { /* 169.254.0.0/16 */ + return IPV6_ADDR_SCOPE_LINKLOCAL; + } else if ((na & 0xff000000) == 0x0a000000 || /* 10.0.0.0/8 */ + (na & 0xfff00000) == 0xac100000 || /* 172.16.0.0/12 */ + (na & 0xffff0000) == 0xc0a80000) { /* 192.168.0.0/16 */ + return IPV6_ADDR_SCOPE_SITELOCAL; + } else { + return IPV6_ADDR_SCOPE_GLOBAL; + } + } else { + /* + * This should never happen. + * Return a scope with low priority as a last resort. + */ + return IPV6_ADDR_SCOPE_NODELOCAL; + } +} + +/* These macros are modelled after the ones in <netinet/in6.h>. */ + +/* RFC 4380, section 2.6 */ +#define IN6_IS_ADDR_TEREDO(a) \ + ((*(const uint32_t *)(const void *)(&(a)->s6_addr[0]) == ntohl(0x20010000))) + +/* RFC 3056, section 2. */ +#define IN6_IS_ADDR_6TO4(a) \ + (((a)->s6_addr[0] == 0x20) && ((a)->s6_addr[1] == 0x02)) + +/* + * Get the label for a given IPv4/IPv6 address. + * RFC 3484, section 2.1, plus Teredo added in with label 5. + */ + +/*ARGSUSED*/ +static int +_get_label(const struct sockaddr *addr) +{ + if (addr->sa_family == AF_INET) { + return 4; + } else if (addr->sa_family == AF_INET6) { + const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; + if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr)) { + return 0; + } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr)) { + return 3; + } else if (IN6_IS_ADDR_TEREDO(&addr6->sin6_addr)) { + return 5; + } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) { + return 2; + } else { + return 1; + } + } else { + /* + * This should never happen. + * Return a semi-random label as a last resort. + */ + return 1; + } +} + +/* + * Get the precedence for a given IPv4/IPv6 address. + * RFC 3484, section 2.1, plus Teredo added in with precedence 25. + */ + +/*ARGSUSED*/ +static int +_get_precedence(const struct sockaddr *addr) { - struct addrinfo head, *t, *p; - int i; - - head.ai_next = NULL; - t = &head; - - for (i = 0; i < res->nsort; i++) { - p = s; - while (p->ai_next) { - if ((p->ai_next->ai_family != AF_INET) - || SORTMATCH(p, res->sort_list[i])) { - t->ai_next = p->ai_next; - t = t->ai_next; - p->ai_next = p->ai_next->ai_next; - } else { - p = p->ai_next; + if (addr->sa_family == AF_INET) { + return 10; + } else if (addr->sa_family == AF_INET6) { + const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; + if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr)) { + return 50; + } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr)) { + return 20; + } else if (IN6_IS_ADDR_TEREDO(&addr6->sin6_addr)) { + return 25; + } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) { + return 30; + } else { + return 40; + } + } else { + return 5; + } +} + +/* + * Find number of matching initial bits between the two addresses a1 and a2. + */ + +/*ARGSUSED*/ +static int +_common_prefix_len(const struct in6_addr *a1, const struct in6_addr *a2) +{ + const char *p1 = (const char *)a1; + const char *p2 = (const char *)a2; + unsigned i; + + for (i = 0; i < sizeof(*a1); ++i) { + int x, j; + + if (p1[i] == p2[i]) { + continue; + } + x = p1[i] ^ p2[i]; + for (j = 0; j < CHAR_BIT; ++j) { + if (x & (1 << (CHAR_BIT - 1))) { + return i * CHAR_BIT + j; } + x <<= 1; + } + } + return sizeof(*a1) * CHAR_BIT; +} + +/* + * Compare two source/destination address pairs. + * RFC 3484, section 6. + */ + +/*ARGSUSED*/ +static int +_rfc3484_compare(const void *ptr1, const void* ptr2) +{ + const struct addrinfo_sort_elem *a1 = (const struct addrinfo_sort_elem *)ptr1; + const struct addrinfo_sort_elem *a2 = (const struct addrinfo_sort_elem *)ptr2; + int scope_src1, scope_dst1, scope_match1; + int scope_src2, scope_dst2, scope_match2; + int label_src1, label_dst1, label_match1; + int label_src2, label_dst2, label_match2; + int precedence1, precedence2; + int prefixlen1, prefixlen2; + + /* Rule 1: Avoid unusable destinations. */ + if (a1->has_src_addr != a2->has_src_addr) { + return a2->has_src_addr - a1->has_src_addr; + } + + /* Rule 2: Prefer matching scope. */ + scope_src1 = _get_scope((const struct sockaddr *)&a1->src_addr); + scope_dst1 = _get_scope(a1->ai->ai_addr); + scope_match1 = (scope_src1 == scope_dst1); + + scope_src2 = _get_scope((const struct sockaddr *)&a2->src_addr); + scope_dst2 = _get_scope(a2->ai->ai_addr); + scope_match2 = (scope_src2 == scope_dst2); + + if (scope_match1 != scope_match2) { + return scope_match2 - scope_match1; + } + + /* + * Rule 3: Avoid deprecated addresses. + * TODO(sesse): We don't currently have a good way of finding this. + */ + + /* + * Rule 4: Prefer home addresses. + * TODO(sesse): We don't currently have a good way of finding this. + */ + + /* Rule 5: Prefer matching label. */ + label_src1 = _get_label((const struct sockaddr *)&a1->src_addr); + label_dst1 = _get_label(a1->ai->ai_addr); + label_match1 = (label_src1 == label_dst1); + + label_src2 = _get_label((const struct sockaddr *)&a2->src_addr); + label_dst2 = _get_label(a2->ai->ai_addr); + label_match2 = (label_src2 == label_dst2); + + if (label_match1 != label_match2) { + return label_match2 - label_match1; + } + + /* Rule 6: Prefer higher precedence. */ + precedence1 = _get_precedence(a1->ai->ai_addr); + precedence2 = _get_precedence(a2->ai->ai_addr); + if (precedence1 != precedence2) { + return precedence2 - precedence1; + } + + /* + * Rule 7: Prefer native transport. + * TODO(sesse): We don't currently have a good way of finding this. + */ + + /* Rule 8: Prefer smaller scope. */ + if (scope_dst1 != scope_dst2) { + return scope_dst1 - scope_dst2; + } + + /* + * Rule 9: Use longest matching prefix. + * We implement this for IPv6 only, as the rules in RFC 3484 don't seem + * to work very well directly applied to IPv4. (glibc uses information from + * the routing table for a custom IPv4 implementation here.) + */ + if (a1->has_src_addr && a1->ai->ai_addr->sa_family == AF_INET6 && + a2->has_src_addr && a2->ai->ai_addr->sa_family == AF_INET6) { + const struct sockaddr_in6 *a1_src = (const struct sockaddr_in6 *)&a1->src_addr; + const struct sockaddr_in6 *a1_dst = (const struct sockaddr_in6 *)a1->ai->ai_addr; + const struct sockaddr_in6 *a2_src = (const struct sockaddr_in6 *)&a2->src_addr; + const struct sockaddr_in6 *a2_dst = (const struct sockaddr_in6 *)a2->ai->ai_addr; + prefixlen1 = _common_prefix_len(&a1_src->sin6_addr, &a1_dst->sin6_addr); + prefixlen2 = _common_prefix_len(&a2_src->sin6_addr, &a2_dst->sin6_addr); + if (prefixlen1 != prefixlen2) { + return prefixlen2 - prefixlen1; + } + } + + /* + * Rule 10: Leave the order unchanged. + * We need this since qsort() is not necessarily stable. + */ + return a1->original_order - a2->original_order; +} + +/* + * Find the source address that will be used if trying to connect to the given + * address. src_addr must be large enough to hold a struct sockaddr_in6. + * + * Returns 1 if a source address was found, 0 if the address is unreachable, + * and -1 if a fatal error occurred. If 0 or 1, the contents of src_addr are + * undefined. + */ + +/*ARGSUSED*/ +static int +_find_src_addr(const struct sockaddr *addr, struct sockaddr *src_addr) +{ + int sock; + int ret; + socklen_t len; + + switch (addr->sa_family) { + case AF_INET: + len = sizeof(struct sockaddr_in); + break; + case AF_INET6: + len = sizeof(struct sockaddr_in6); + break; + default: + /* No known usable source address for non-INET families. */ + return 0; + } + + sock = socket(addr->sa_family, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) { + if (errno == EAFNOSUPPORT) { + return 0; + } else { + return -1; } } - /* add rest of list and reset s to the new list*/ - t->ai_next = s->ai_next; - s->ai_next = head.ai_next; + do { + ret = connect(sock, addr, len); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + close(sock); + return 0; + } + + if (getsockname(sock, src_addr, &len) == -1) { + close(sock); + return -1; + } + close(sock); + return 1; +} + +/* + * Sort the linked list starting at sentinel->ai_next in RFC3484 order. + * Will leave the list unchanged if an error occurs. + */ + +/*ARGSUSED*/ +static void +_rfc3484_sort(struct addrinfo *list_sentinel) +{ + struct addrinfo *cur; + int nelem = 0, i; + struct addrinfo_sort_elem *elems; + + cur = list_sentinel->ai_next; + while (cur) { + ++nelem; + cur = cur->ai_next; + } + + elems = (struct addrinfo_sort_elem *)malloc(nelem * sizeof(struct addrinfo_sort_elem)); + if (elems == NULL) { + goto error; + } + + /* + * Convert the linked list to an array that also contains the candidate + * source address for each destination address. + */ + for (i = 0, cur = list_sentinel->ai_next; i < nelem; ++i, cur = cur->ai_next) { + int has_src_addr; + assert(cur != NULL); + elems[i].ai = cur; + elems[i].original_order = i; + + has_src_addr = _find_src_addr(cur->ai_addr, (struct sockaddr *)&elems[i].src_addr); + if (has_src_addr == -1) { + goto error; + } + elems[i].has_src_addr = has_src_addr; + } + + /* Sort the addresses, and rearrange the linked list so it matches the sorted order. */ + qsort((void *)elems, nelem, sizeof(struct addrinfo_sort_elem), _rfc3484_compare); + + list_sentinel->ai_next = elems[0].ai; + for (i = 0; i < nelem - 1; ++i) { + elems[i].ai->ai_next = elems[i + 1].ai; + } + elems[nelem - 1].ai->ai_next = NULL; + +error: + free(elems); } /*ARGSUSED*/ @@ -1401,8 +1731,7 @@ _dns_getaddrinfo(void *rv, void *cb_data, va_list ap) } } - if (res->nsort) - aisort(&sentinel, res); + _rfc3484_sort(&sentinel); __res_put_state(res); diff --git a/libc/netbsd/resolv/res_cache.c b/libc/netbsd/resolv/res_cache.c index 2c912de..2621a7b 100644 --- a/libc/netbsd/resolv/res_cache.c +++ b/libc/netbsd/resolv/res_cache.c @@ -251,7 +251,7 @@ _bprint( char* p, char* end, const char* format, ... ) return p; va_start(args, format); - n = snprintf( p, avail, format, args); + n = vsnprintf( p, avail, format, args); va_end(args); /* certain C libraries return -1 in case of truncation */ diff --git a/libc/netbsd/resolv/res_init.c b/libc/netbsd/resolv/res_init.c index 751603d..81e570f 100644 --- a/libc/netbsd/resolv/res_init.c +++ b/libc/netbsd/resolv/res_init.c @@ -198,6 +198,8 @@ int load_domain_search_list(res_state statp) { if (pp > statp->dnsrch) return 1; } + statp->defdname[0] = '\0'; /* no default domain name on Android */ + statp->dnsrch[0] = NULL; return 0; } #endif @@ -381,7 +383,7 @@ __res_vinit(res_state statp, int preinit) { /* Add the domain search list */ havesearch = load_domain_search_list(statp); -#else /* IGNORE resolv.conf */ +#else /* !ANDROID_CHANGES - IGNORE resolv.conf in Android */ #define MATCH(line, name) \ (!strncmp(line, name, sizeof(name) - 1) && \ (line[sizeof(name) - 1] == ' ' || \ @@ -534,7 +536,7 @@ __res_vinit(res_state statp, int preinit) { statp->nsort = nsort; (void) fclose(fp); } -#endif /* ANDROID_CHANGES */ +#endif /* !ANDROID_CHANGES */ /* * Last chance to get a nameserver. This should not normally * be necessary diff --git a/libc/private/arpa_nameser.h b/libc/private/arpa_nameser.h index 2d2a739..438dc04 100644 --- a/libc/private/arpa_nameser.h +++ b/libc/private/arpa_nameser.h @@ -566,4 +566,12 @@ __END_DECLS #include "arpa_nameser_compat.h" #endif +#if 0 +# include <logd.h> +# define XLOG(...) \ + __libc_android_log_print(ANDROID_LOG_DEBUG,"libc",__VA_ARGS__) +#else +#define XLOG(...) do {} while (0) +#endif + #endif /* !_ARPA_NAMESER_H_ */ diff --git a/libc/regex/cclass.h b/libc/regex/cclass.h new file mode 100644 index 0000000..d105491 --- /dev/null +++ b/libc/regex/cclass.h @@ -0,0 +1,68 @@ +/* $OpenBSD: cclass.h,v 1.5 2003/06/02 20:18:36 millert Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cclass.h 8.3 (Berkeley) 3/20/94 + */ + +/* character-class table */ +static const struct cclass { + char *name; + char *chars; + char *multis; +} cclasses[] = { + { "alnum", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ +0123456789", ""} , + { "alpha", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + ""} , + { "blank", " \t", ""} , + { "cntrl", "\007\b\t\n\v\f\r\1\2\3\4\5\6\16\17\20\21\22\23\24\ +\25\26\27\30\31\32\33\34\35\36\37\177", ""} , + { "digit", "0123456789", ""} , + { "graph", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ +0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + ""} , + { "lower", "abcdefghijklmnopqrstuvwxyz", + ""} , + { "print", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ +0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ", + ""} , + { "punct", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + ""} , + { "space", "\t\n\v\f\r ", ""} , + { "upper", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + ""} , + { "xdigit", "0123456789ABCDEFabcdef", + ""} , + { NULL, 0, "" } +}; diff --git a/libc/regex/cname.h b/libc/regex/cname.h new file mode 100644 index 0000000..b674b68 --- /dev/null +++ b/libc/regex/cname.h @@ -0,0 +1,139 @@ +/* $OpenBSD: cname.h,v 1.5 2003/06/02 20:18:36 millert Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cname.h 8.3 (Berkeley) 3/20/94 + */ + +/* character-name table */ +static const struct cname { + char *name; + char code; +} cnames[] = { + { "NUL", '\0' }, + { "SOH", '\001' }, + { "STX", '\002' }, + { "ETX", '\003' }, + { "EOT", '\004' }, + { "ENQ", '\005' }, + { "ACK", '\006' }, + { "BEL", '\007' }, + { "alert", '\007' }, + { "BS", '\010' }, + { "backspace", '\b' }, + { "HT", '\011' }, + { "tab", '\t' }, + { "LF", '\012' }, + { "newline", '\n' }, + { "VT", '\013' }, + { "vertical-tab", '\v' }, + { "FF", '\014' }, + { "form-feed", '\f' }, + { "CR", '\015' }, + { "carriage-return", '\r' }, + { "SO", '\016' }, + { "SI", '\017' }, + { "DLE", '\020' }, + { "DC1", '\021' }, + { "DC2", '\022' }, + { "DC3", '\023' }, + { "DC4", '\024' }, + { "NAK", '\025' }, + { "SYN", '\026' }, + { "ETB", '\027' }, + { "CAN", '\030' }, + { "EM", '\031' }, + { "SUB", '\032' }, + { "ESC", '\033' }, + { "IS4", '\034' }, + { "FS", '\034' }, + { "IS3", '\035' }, + { "GS", '\035' }, + { "IS2", '\036' }, + { "RS", '\036' }, + { "IS1", '\037' }, + { "US", '\037' }, + { "space", ' ' }, + { "exclamation-mark", '!' }, + { "quotation-mark", '"' }, + { "number-sign", '#' }, + { "dollar-sign", '$' }, + { "percent-sign", '%' }, + { "ampersand", '&' }, + { "apostrophe", '\'' }, + { "left-parenthesis", '(' }, + { "right-parenthesis", ')' }, + { "asterisk", '*' }, + { "plus-sign", '+' }, + { "comma", ',' }, + { "hyphen", '-' }, + { "hyphen-minus", '-' }, + { "period", '.' }, + { "full-stop", '.' }, + { "slash", '/' }, + { "solidus", '/' }, + { "zero", '0' }, + { "one", '1' }, + { "two", '2' }, + { "three", '3' }, + { "four", '4' }, + { "five", '5' }, + { "six", '6' }, + { "seven", '7' }, + { "eight", '8' }, + { "nine", '9' }, + { "colon", ':' }, + { "semicolon", ';' }, + { "less-than-sign", '<' }, + { "equals-sign", '=' }, + { "greater-than-sign", '>' }, + { "question-mark", '?' }, + { "commercial-at", '@' }, + { "left-square-bracket", '[' }, + { "backslash", '\\' }, + { "reverse-solidus", '\\' }, + { "right-square-bracket", ']' }, + { "circumflex", '^' }, + { "circumflex-accent", '^' }, + { "underscore", '_' }, + { "low-line", '_' }, + { "grave-accent", '`' }, + { "left-brace", '{' }, + { "left-curly-bracket", '{' }, + { "vertical-line", '|' }, + { "right-brace", '}' }, + { "right-curly-bracket", '}' }, + { "tilde", '~' }, + { "DEL", '\177' }, + { NULL, 0 } +}; diff --git a/libc/regex/engine.c b/libc/regex/engine.c new file mode 100644 index 0000000..66be3c7 --- /dev/null +++ b/libc/regex/engine.c @@ -0,0 +1,1021 @@ +/* $OpenBSD: engine.c,v 1.15 2005/08/05 13:03:00 espie Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)engine.c 8.5 (Berkeley) 3/20/94 + */ + +/* + * The matching engine and friends. This file is #included by regexec.c + * after suitable #defines of a variety of macros used herein, so that + * different state representations can be used without duplicating masses + * of code. + */ + +#ifdef SNAMES +#define matcher smatcher +#define fast sfast +#define slow sslow +#define dissect sdissect +#define backref sbackref +#define step sstep +#define print sprint +#define at sat +#define match smat +#define nope snope +#endif +#ifdef LNAMES +#define matcher lmatcher +#define fast lfast +#define slow lslow +#define dissect ldissect +#define backref lbackref +#define step lstep +#define print lprint +#define at lat +#define match lmat +#define nope lnope +#endif + +/* another structure passed up and down to avoid zillions of parameters */ +struct match { + struct re_guts *g; + int eflags; + regmatch_t *pmatch; /* [nsub+1] (0 element unused) */ + char *offp; /* offsets work from here */ + char *beginp; /* start of string -- virtual NUL precedes */ + char *endp; /* end of string -- virtual NUL here */ + char *coldp; /* can be no match starting before here */ + char **lastpos; /* [nplus+1] */ + STATEVARS; + states st; /* current states */ + states fresh; /* states for a fresh start */ + states tmp; /* temporary */ + states empty; /* empty set of states */ +}; + +static int matcher(struct re_guts *, char *, size_t, regmatch_t[], int); +static char *dissect(struct match *, char *, char *, sopno, sopno); +static char *backref(struct match *, char *, char *, sopno, sopno, sopno, int); +static char *fast(struct match *, char *, char *, sopno, sopno); +static char *slow(struct match *, char *, char *, sopno, sopno); +static states step(struct re_guts *, sopno, sopno, states, int, states); +#define MAX_RECURSION 100 +#define BOL (OUT+1) +#define EOL (BOL+1) +#define BOLEOL (BOL+2) +#define NOTHING (BOL+3) +#define BOW (BOL+4) +#define EOW (BOL+5) +#define CODEMAX (BOL+5) /* highest code used */ +#define NONCHAR(c) ((c) > CHAR_MAX) +#define NNONCHAR (CODEMAX-CHAR_MAX) +#ifdef REDEBUG +static void print(struct match *, char *, states, int, FILE *); +#endif +#ifdef REDEBUG +static void at(struct match *, char *, char *, char *, sopno, sopno); +#endif +#ifdef REDEBUG +static char *pchar(int); +#endif + +#ifdef REDEBUG +#define SP(t, s, c) print(m, t, s, c, stdout) +#define AT(t, p1, p2, s1, s2) at(m, t, p1, p2, s1, s2) +#define NOTE(str) { if (m->eflags®_TRACE) (void)printf("=%s\n", (str)); } +static int nope = 0; +#else +#define SP(t, s, c) /* nothing */ +#define AT(t, p1, p2, s1, s2) /* nothing */ +#define NOTE(s) /* nothing */ +#endif + +/* + - matcher - the actual matching engine + */ +static int /* 0 success, REG_NOMATCH failure */ +matcher(struct re_guts *g, char *string, size_t nmatch, regmatch_t pmatch[], + int eflags) +{ + char *endp; + int i; + struct match mv; + struct match *m = &mv; + char *dp; + const sopno gf = g->firststate+1; /* +1 for OEND */ + const sopno gl = g->laststate; + char *start; + char *stop; + + /* simplify the situation where possible */ + if (g->cflags®_NOSUB) + nmatch = 0; + if (eflags®_STARTEND) { + start = string + pmatch[0].rm_so; + stop = string + pmatch[0].rm_eo; + } else { + start = string; + stop = start + strlen(start); + } + if (stop < start) + return(REG_INVARG); + + /* prescreening; this does wonders for this rather slow code */ + if (g->must != NULL) { + for (dp = start; dp < stop; dp++) + if (*dp == g->must[0] && stop - dp >= g->mlen && + memcmp(dp, g->must, (size_t)g->mlen) == 0) + break; + if (dp == stop) /* we didn't find g->must */ + return(REG_NOMATCH); + } + + /* match struct setup */ + m->g = g; + m->eflags = eflags; + m->pmatch = NULL; + m->lastpos = NULL; + m->offp = string; + m->beginp = start; + m->endp = stop; + STATESETUP(m, 4); + SETUP(m->st); + SETUP(m->fresh); + SETUP(m->tmp); + SETUP(m->empty); + CLEAR(m->empty); + + /* this loop does only one repetition except for backrefs */ + for (;;) { + endp = fast(m, start, stop, gf, gl); + if (endp == NULL) { /* a miss */ + free(m->pmatch); + free(m->lastpos); + STATETEARDOWN(m); + return(REG_NOMATCH); + } + if (nmatch == 0 && !g->backrefs) + break; /* no further info needed */ + + /* where? */ + assert(m->coldp != NULL); + for (;;) { + NOTE("finding start"); + endp = slow(m, m->coldp, stop, gf, gl); + if (endp != NULL) + break; + assert(m->coldp < m->endp); + m->coldp++; + } + if (nmatch == 1 && !g->backrefs) + break; /* no further info needed */ + + /* oh my, he wants the subexpressions... */ + if (m->pmatch == NULL) + m->pmatch = (regmatch_t *)malloc((m->g->nsub + 1) * + sizeof(regmatch_t)); + if (m->pmatch == NULL) { + STATETEARDOWN(m); + return(REG_ESPACE); + } + for (i = 1; i <= m->g->nsub; i++) + m->pmatch[i].rm_so = m->pmatch[i].rm_eo = -1; + if (!g->backrefs && !(m->eflags®_BACKR)) { + NOTE("dissecting"); + dp = dissect(m, m->coldp, endp, gf, gl); + } else { + if (g->nplus > 0 && m->lastpos == NULL) + m->lastpos = (char **)malloc((g->nplus+1) * + sizeof(char *)); + if (g->nplus > 0 && m->lastpos == NULL) { + free(m->pmatch); + STATETEARDOWN(m); + return(REG_ESPACE); + } + NOTE("backref dissect"); + dp = backref(m, m->coldp, endp, gf, gl, (sopno)0, 0); + } + if (dp != NULL) + break; + + /* uh-oh... we couldn't find a subexpression-level match */ + assert(g->backrefs); /* must be back references doing it */ + assert(g->nplus == 0 || m->lastpos != NULL); + for (;;) { + if (dp != NULL || endp <= m->coldp) + break; /* defeat */ + NOTE("backoff"); + endp = slow(m, m->coldp, endp-1, gf, gl); + if (endp == NULL) + break; /* defeat */ + /* try it on a shorter possibility */ +#ifndef NDEBUG + for (i = 1; i <= m->g->nsub; i++) { + assert(m->pmatch[i].rm_so == -1); + assert(m->pmatch[i].rm_eo == -1); + } +#endif + NOTE("backoff dissect"); + dp = backref(m, m->coldp, endp, gf, gl, (sopno)0, 0); + } + assert(dp == NULL || dp == endp); + if (dp != NULL) /* found a shorter one */ + break; + + /* despite initial appearances, there is no match here */ + NOTE("false alarm"); + if (m->coldp == stop) + break; + start = m->coldp + 1; /* recycle starting later */ + } + + /* fill in the details if requested */ + if (nmatch > 0) { + pmatch[0].rm_so = m->coldp - m->offp; + pmatch[0].rm_eo = endp - m->offp; + } + if (nmatch > 1) { + assert(m->pmatch != NULL); + for (i = 1; i < nmatch; i++) + if (i <= m->g->nsub) + pmatch[i] = m->pmatch[i]; + else { + pmatch[i].rm_so = -1; + pmatch[i].rm_eo = -1; + } + } + + if (m->pmatch != NULL) + free((char *)m->pmatch); + if (m->lastpos != NULL) + free((char *)m->lastpos); + STATETEARDOWN(m); + return(0); +} + +/* + - dissect - figure out what matched what, no back references + */ +static char * /* == stop (success) always */ +dissect(struct match *m, char *start, char *stop, sopno startst, sopno stopst) +{ + int i; + sopno ss; /* start sop of current subRE */ + sopno es; /* end sop of current subRE */ + char *sp; /* start of string matched by it */ + char *stp; /* string matched by it cannot pass here */ + char *rest; /* start of rest of string */ + char *tail; /* string unmatched by rest of RE */ + sopno ssub; /* start sop of subsubRE */ + sopno esub; /* end sop of subsubRE */ + char *ssp; /* start of string matched by subsubRE */ + char *sep; /* end of string matched by subsubRE */ + char *oldssp; /* previous ssp */ + char *dp; + + AT("diss", start, stop, startst, stopst); + sp = start; + for (ss = startst; ss < stopst; ss = es) { + /* identify end of subRE */ + es = ss; + switch (OP(m->g->strip[es])) { + case OPLUS_: + case OQUEST_: + es += OPND(m->g->strip[es]); + break; + case OCH_: + while (OP(m->g->strip[es]) != O_CH) + es += OPND(m->g->strip[es]); + break; + } + es++; + + /* figure out what it matched */ + switch (OP(m->g->strip[ss])) { + case OEND: + assert(nope); + break; + case OCHAR: + sp++; + break; + case OBOL: + case OEOL: + case OBOW: + case OEOW: + break; + case OANY: + case OANYOF: + sp++; + break; + case OBACK_: + case O_BACK: + assert(nope); + break; + /* cases where length of match is hard to find */ + case OQUEST_: + stp = stop; + for (;;) { + /* how long could this one be? */ + rest = slow(m, sp, stp, ss, es); + assert(rest != NULL); /* it did match */ + /* could the rest match the rest? */ + tail = slow(m, rest, stop, es, stopst); + if (tail == stop) + break; /* yes! */ + /* no -- try a shorter match for this one */ + stp = rest - 1; + assert(stp >= sp); /* it did work */ + } + ssub = ss + 1; + esub = es - 1; + /* did innards match? */ + if (slow(m, sp, rest, ssub, esub) != NULL) { + dp = dissect(m, sp, rest, ssub, esub); + assert(dp == rest); + } else /* no */ + assert(sp == rest); + sp = rest; + break; + case OPLUS_: + stp = stop; + for (;;) { + /* how long could this one be? */ + rest = slow(m, sp, stp, ss, es); + assert(rest != NULL); /* it did match */ + /* could the rest match the rest? */ + tail = slow(m, rest, stop, es, stopst); + if (tail == stop) + break; /* yes! */ + /* no -- try a shorter match for this one */ + stp = rest - 1; + assert(stp >= sp); /* it did work */ + } + ssub = ss + 1; + esub = es - 1; + ssp = sp; + oldssp = ssp; + for (;;) { /* find last match of innards */ + sep = slow(m, ssp, rest, ssub, esub); + if (sep == NULL || sep == ssp) + break; /* failed or matched null */ + oldssp = ssp; /* on to next try */ + ssp = sep; + } + if (sep == NULL) { + /* last successful match */ + sep = ssp; + ssp = oldssp; + } + assert(sep == rest); /* must exhaust substring */ + assert(slow(m, ssp, sep, ssub, esub) == rest); + dp = dissect(m, ssp, sep, ssub, esub); + assert(dp == sep); + sp = rest; + break; + case OCH_: + stp = stop; + for (;;) { + /* how long could this one be? */ + rest = slow(m, sp, stp, ss, es); + assert(rest != NULL); /* it did match */ + /* could the rest match the rest? */ + tail = slow(m, rest, stop, es, stopst); + if (tail == stop) + break; /* yes! */ + /* no -- try a shorter match for this one */ + stp = rest - 1; + assert(stp >= sp); /* it did work */ + } + ssub = ss + 1; + esub = ss + OPND(m->g->strip[ss]) - 1; + assert(OP(m->g->strip[esub]) == OOR1); + for (;;) { /* find first matching branch */ + if (slow(m, sp, rest, ssub, esub) == rest) + break; /* it matched all of it */ + /* that one missed, try next one */ + assert(OP(m->g->strip[esub]) == OOR1); + esub++; + assert(OP(m->g->strip[esub]) == OOR2); + ssub = esub + 1; + esub += OPND(m->g->strip[esub]); + if (OP(m->g->strip[esub]) == OOR2) + esub--; + else + assert(OP(m->g->strip[esub]) == O_CH); + } + dp = dissect(m, sp, rest, ssub, esub); + assert(dp == rest); + sp = rest; + break; + case O_PLUS: + case O_QUEST: + case OOR1: + case OOR2: + case O_CH: + assert(nope); + break; + case OLPAREN: + i = OPND(m->g->strip[ss]); + assert(0 < i && i <= m->g->nsub); + m->pmatch[i].rm_so = sp - m->offp; + break; + case ORPAREN: + i = OPND(m->g->strip[ss]); + assert(0 < i && i <= m->g->nsub); + m->pmatch[i].rm_eo = sp - m->offp; + break; + default: /* uh oh */ + assert(nope); + break; + } + } + + assert(sp == stop); + return(sp); +} + +/* + - backref - figure out what matched what, figuring in back references + */ +static char * /* == stop (success) or NULL (failure) */ +backref(struct match *m, char *start, char *stop, sopno startst, sopno stopst, + sopno lev, int rec) /* PLUS nesting level */ +{ + int i; + sopno ss; /* start sop of current subRE */ + char *sp; /* start of string matched by it */ + sopno ssub; /* start sop of subsubRE */ + sopno esub; /* end sop of subsubRE */ + char *ssp; /* start of string matched by subsubRE */ + char *dp; + size_t len; + int hard; + sop s; + regoff_t offsave; + cset *cs; + + AT("back", start, stop, startst, stopst); + sp = start; + + /* get as far as we can with easy stuff */ + hard = 0; + for (ss = startst; !hard && ss < stopst; ss++) + switch (OP(s = m->g->strip[ss])) { + case OCHAR: + if (sp == stop || *sp++ != (char)OPND(s)) + return(NULL); + break; + case OANY: + if (sp == stop) + return(NULL); + sp++; + break; + case OANYOF: + cs = &m->g->sets[OPND(s)]; + if (sp == stop || !CHIN(cs, *sp++)) + return(NULL); + break; + case OBOL: + if ( (sp == m->beginp && !(m->eflags®_NOTBOL)) || + (sp < m->endp && *(sp-1) == '\n' && + (m->g->cflags®_NEWLINE)) ) + { /* yes */ } + else + return(NULL); + break; + case OEOL: + if ( (sp == m->endp && !(m->eflags®_NOTEOL)) || + (sp < m->endp && *sp == '\n' && + (m->g->cflags®_NEWLINE)) ) + { /* yes */ } + else + return(NULL); + break; + case OBOW: + if (( (sp == m->beginp && !(m->eflags®_NOTBOL)) || + (sp < m->endp && *(sp-1) == '\n' && + (m->g->cflags®_NEWLINE)) || + (sp > m->beginp && + !ISWORD(*(sp-1))) ) && + (sp < m->endp && ISWORD(*sp)) ) + { /* yes */ } + else + return(NULL); + break; + case OEOW: + if (( (sp == m->endp && !(m->eflags®_NOTEOL)) || + (sp < m->endp && *sp == '\n' && + (m->g->cflags®_NEWLINE)) || + (sp < m->endp && !ISWORD(*sp)) ) && + (sp > m->beginp && ISWORD(*(sp-1))) ) + { /* yes */ } + else + return(NULL); + break; + case O_QUEST: + break; + case OOR1: /* matches null but needs to skip */ + ss++; + s = m->g->strip[ss]; + do { + assert(OP(s) == OOR2); + ss += OPND(s); + } while (OP(s = m->g->strip[ss]) != O_CH); + /* note that the ss++ gets us past the O_CH */ + break; + default: /* have to make a choice */ + hard = 1; + break; + } + if (!hard) { /* that was it! */ + if (sp != stop) + return(NULL); + return(sp); + } + ss--; /* adjust for the for's final increment */ + + /* the hard stuff */ + AT("hard", sp, stop, ss, stopst); + s = m->g->strip[ss]; + switch (OP(s)) { + case OBACK_: /* the vilest depths */ + i = OPND(s); + assert(0 < i && i <= m->g->nsub); + if (m->pmatch[i].rm_eo == -1) + return(NULL); + assert(m->pmatch[i].rm_so != -1); + len = m->pmatch[i].rm_eo - m->pmatch[i].rm_so; + if (len == 0 && rec++ > MAX_RECURSION) + return(NULL); + assert(stop - m->beginp >= len); + if (sp > stop - len) + return(NULL); /* not enough left to match */ + ssp = m->offp + m->pmatch[i].rm_so; + if (memcmp(sp, ssp, len) != 0) + return(NULL); + while (m->g->strip[ss] != SOP(O_BACK, i)) + ss++; + return(backref(m, sp+len, stop, ss+1, stopst, lev, rec)); + break; + case OQUEST_: /* to null or not */ + dp = backref(m, sp, stop, ss+1, stopst, lev, rec); + if (dp != NULL) + return(dp); /* not */ + return(backref(m, sp, stop, ss+OPND(s)+1, stopst, lev, rec)); + break; + case OPLUS_: + assert(m->lastpos != NULL); + assert(lev+1 <= m->g->nplus); + m->lastpos[lev+1] = sp; + return(backref(m, sp, stop, ss+1, stopst, lev+1, rec)); + break; + case O_PLUS: + if (sp == m->lastpos[lev]) /* last pass matched null */ + return(backref(m, sp, stop, ss+1, stopst, lev-1, rec)); + /* try another pass */ + m->lastpos[lev] = sp; + dp = backref(m, sp, stop, ss-OPND(s)+1, stopst, lev, rec); + if (dp == NULL) + return(backref(m, sp, stop, ss+1, stopst, lev-1, rec)); + else + return(dp); + break; + case OCH_: /* find the right one, if any */ + ssub = ss + 1; + esub = ss + OPND(s) - 1; + assert(OP(m->g->strip[esub]) == OOR1); + for (;;) { /* find first matching branch */ + dp = backref(m, sp, stop, ssub, esub, lev, rec); + if (dp != NULL) + return(dp); + /* that one missed, try next one */ + if (OP(m->g->strip[esub]) == O_CH) + return(NULL); /* there is none */ + esub++; + assert(OP(m->g->strip[esub]) == OOR2); + ssub = esub + 1; + esub += OPND(m->g->strip[esub]); + if (OP(m->g->strip[esub]) == OOR2) + esub--; + else + assert(OP(m->g->strip[esub]) == O_CH); + } + break; + case OLPAREN: /* must undo assignment if rest fails */ + i = OPND(s); + assert(0 < i && i <= m->g->nsub); + offsave = m->pmatch[i].rm_so; + m->pmatch[i].rm_so = sp - m->offp; + dp = backref(m, sp, stop, ss+1, stopst, lev, rec); + if (dp != NULL) + return(dp); + m->pmatch[i].rm_so = offsave; + return(NULL); + break; + case ORPAREN: /* must undo assignment if rest fails */ + i = OPND(s); + assert(0 < i && i <= m->g->nsub); + offsave = m->pmatch[i].rm_eo; + m->pmatch[i].rm_eo = sp - m->offp; + dp = backref(m, sp, stop, ss+1, stopst, lev, rec); + if (dp != NULL) + return(dp); + m->pmatch[i].rm_eo = offsave; + return(NULL); + break; + default: /* uh oh */ + assert(nope); + break; + } + + /* "can't happen" */ + assert(nope); + /* NOTREACHED */ + return 0; +} + +/* + - fast - step through the string at top speed + */ +static char * /* where tentative match ended, or NULL */ +fast(struct match *m, char *start, char *stop, sopno startst, sopno stopst) +{ + states st = m->st; + states fresh = m->fresh; + states tmp = m->tmp; + char *p = start; + int c = (start == m->beginp) ? OUT : *(start-1); + int lastc; /* previous c */ + int flagch; + int i; + char *coldp; /* last p after which no match was underway */ + + CLEAR(st); + SET1(st, startst); + st = step(m->g, startst, stopst, st, NOTHING, st); + ASSIGN(fresh, st); + SP("start", st, *p); + coldp = NULL; + for (;;) { + /* next character */ + lastc = c; + c = (p == m->endp) ? OUT : *p; + if (EQ(st, fresh)) + coldp = p; + + /* is there an EOL and/or BOL between lastc and c? */ + flagch = '\0'; + i = 0; + if ( (lastc == '\n' && m->g->cflags®_NEWLINE) || + (lastc == OUT && !(m->eflags®_NOTBOL)) ) { + flagch = BOL; + i = m->g->nbol; + } + if ( (c == '\n' && m->g->cflags®_NEWLINE) || + (c == OUT && !(m->eflags®_NOTEOL)) ) { + flagch = (flagch == BOL) ? BOLEOL : EOL; + i += m->g->neol; + } + if (i != 0) { + for (; i > 0; i--) + st = step(m->g, startst, stopst, st, flagch, st); + SP("boleol", st, c); + } + + /* how about a word boundary? */ + if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) && + (c != OUT && ISWORD(c)) ) { + flagch = BOW; + } + if ( (lastc != OUT && ISWORD(lastc)) && + (flagch == EOL || (c != OUT && !ISWORD(c))) ) { + flagch = EOW; + } + if (flagch == BOW || flagch == EOW) { + st = step(m->g, startst, stopst, st, flagch, st); + SP("boweow", st, c); + } + + /* are we done? */ + if (ISSET(st, stopst) || p == stop) + break; /* NOTE BREAK OUT */ + + /* no, we must deal with this character */ + ASSIGN(tmp, st); + ASSIGN(st, fresh); + assert(c != OUT); + st = step(m->g, startst, stopst, tmp, c, st); + SP("aft", st, c); + assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st)); + p++; + } + + assert(coldp != NULL); + m->coldp = coldp; + if (ISSET(st, stopst)) + return(p+1); + else + return(NULL); +} + +/* + - slow - step through the string more deliberately + */ +static char * /* where it ended */ +slow(struct match *m, char *start, char *stop, sopno startst, sopno stopst) +{ + states st = m->st; + states empty = m->empty; + states tmp = m->tmp; + char *p = start; + int c = (start == m->beginp) ? OUT : *(start-1); + int lastc; /* previous c */ + int flagch; + int i; + char *matchp; /* last p at which a match ended */ + + AT("slow", start, stop, startst, stopst); + CLEAR(st); + SET1(st, startst); + SP("sstart", st, *p); + st = step(m->g, startst, stopst, st, NOTHING, st); + matchp = NULL; + for (;;) { + /* next character */ + lastc = c; + c = (p == m->endp) ? OUT : *p; + + /* is there an EOL and/or BOL between lastc and c? */ + flagch = '\0'; + i = 0; + if ( (lastc == '\n' && m->g->cflags®_NEWLINE) || + (lastc == OUT && !(m->eflags®_NOTBOL)) ) { + flagch = BOL; + i = m->g->nbol; + } + if ( (c == '\n' && m->g->cflags®_NEWLINE) || + (c == OUT && !(m->eflags®_NOTEOL)) ) { + flagch = (flagch == BOL) ? BOLEOL : EOL; + i += m->g->neol; + } + if (i != 0) { + for (; i > 0; i--) + st = step(m->g, startst, stopst, st, flagch, st); + SP("sboleol", st, c); + } + + /* how about a word boundary? */ + if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) && + (c != OUT && ISWORD(c)) ) { + flagch = BOW; + } + if ( (lastc != OUT && ISWORD(lastc)) && + (flagch == EOL || (c != OUT && !ISWORD(c))) ) { + flagch = EOW; + } + if (flagch == BOW || flagch == EOW) { + st = step(m->g, startst, stopst, st, flagch, st); + SP("sboweow", st, c); + } + + /* are we done? */ + if (ISSET(st, stopst)) + matchp = p; + if (EQ(st, empty) || p == stop) + break; /* NOTE BREAK OUT */ + + /* no, we must deal with this character */ + ASSIGN(tmp, st); + ASSIGN(st, empty); + assert(c != OUT); + st = step(m->g, startst, stopst, tmp, c, st); + SP("saft", st, c); + assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st)); + p++; + } + + return(matchp); +} + + +/* + - step - map set of states reachable before char to set reachable after + */ +static states +step(struct re_guts *g, + sopno start, /* start state within strip */ + sopno stop, /* state after stop state within strip */ + states bef, /* states reachable before */ + int ch, /* character or NONCHAR code */ + states aft) /* states already known reachable after */ +{ + cset *cs; + sop s; + sopno pc; + onestate here; /* note, macros know this name */ + sopno look; + int i; + + for (pc = start, INIT(here, pc); pc != stop; pc++, INC(here)) { + s = g->strip[pc]; + switch (OP(s)) { + case OEND: + assert(pc == stop-1); + break; + case OCHAR: + /* only characters can match */ + assert(!NONCHAR(ch) || ch != (char)OPND(s)); + if (ch == (char)OPND(s)) + FWD(aft, bef, 1); + break; + case OBOL: + if (ch == BOL || ch == BOLEOL) + FWD(aft, bef, 1); + break; + case OEOL: + if (ch == EOL || ch == BOLEOL) + FWD(aft, bef, 1); + break; + case OBOW: + if (ch == BOW) + FWD(aft, bef, 1); + break; + case OEOW: + if (ch == EOW) + FWD(aft, bef, 1); + break; + case OANY: + if (!NONCHAR(ch)) + FWD(aft, bef, 1); + break; + case OANYOF: + cs = &g->sets[OPND(s)]; + if (!NONCHAR(ch) && CHIN(cs, ch)) + FWD(aft, bef, 1); + break; + case OBACK_: /* ignored here */ + case O_BACK: + FWD(aft, aft, 1); + break; + case OPLUS_: /* forward, this is just an empty */ + FWD(aft, aft, 1); + break; + case O_PLUS: /* both forward and back */ + FWD(aft, aft, 1); + i = ISSETBACK(aft, OPND(s)); + BACK(aft, aft, OPND(s)); + if (!i && ISSETBACK(aft, OPND(s))) { + /* oho, must reconsider loop body */ + pc -= OPND(s) + 1; + INIT(here, pc); + } + break; + case OQUEST_: /* two branches, both forward */ + FWD(aft, aft, 1); + FWD(aft, aft, OPND(s)); + break; + case O_QUEST: /* just an empty */ + FWD(aft, aft, 1); + break; + case OLPAREN: /* not significant here */ + case ORPAREN: + FWD(aft, aft, 1); + break; + case OCH_: /* mark the first two branches */ + FWD(aft, aft, 1); + assert(OP(g->strip[pc+OPND(s)]) == OOR2); + FWD(aft, aft, OPND(s)); + break; + case OOR1: /* done a branch, find the O_CH */ + if (ISSTATEIN(aft, here)) { + for (look = 1; + OP(s = g->strip[pc+look]) != O_CH; + look += OPND(s)) + assert(OP(s) == OOR2); + FWD(aft, aft, look); + } + break; + case OOR2: /* propagate OCH_'s marking */ + FWD(aft, aft, 1); + if (OP(g->strip[pc+OPND(s)]) != O_CH) { + assert(OP(g->strip[pc+OPND(s)]) == OOR2); + FWD(aft, aft, OPND(s)); + } + break; + case O_CH: /* just empty */ + FWD(aft, aft, 1); + break; + default: /* ooooops... */ + assert(nope); + break; + } + } + + return(aft); +} + +#ifdef REDEBUG +/* + - print - print a set of states + */ +static void +print(struct match *m, char *caption, states st, int ch, FILE *d) +{ + struct re_guts *g = m->g; + int i; + int first = 1; + + if (!(m->eflags®_TRACE)) + return; + + (void)fprintf(d, "%s", caption); + if (ch != '\0') + (void)fprintf(d, " %s", pchar(ch)); + for (i = 0; i < g->nstates; i++) + if (ISSET(st, i)) { + (void)fprintf(d, "%s%d", (first) ? "\t" : ", ", i); + first = 0; + } + (void)fprintf(d, "\n"); +} + +/* + - at - print current situation + */ +static void +at(struct match *m, char *title, char *start, char *stop, sopno startst, + sopno stopst) +{ + if (!(m->eflags®_TRACE)) + return; + + (void)printf("%s %s-", title, pchar(*start)); + (void)printf("%s ", pchar(*stop)); + (void)printf("%ld-%ld\n", (long)startst, (long)stopst); +} + +#ifndef PCHARDONE +#define PCHARDONE /* never again */ +/* + - pchar - make a character printable + * + * Is this identical to regchar() over in debug.c? Well, yes. But a + * duplicate here avoids having a debugging-capable regexec.o tied to + * a matching debug.o, and this is convenient. It all disappears in + * the non-debug compilation anyway, so it doesn't matter much. + */ +static char * /* -> representation */ +pchar(int ch) +{ + static char pbuf[10]; + + if (isprint(ch) || ch == ' ') + (void)snprintf(pbuf, sizeof pbuf, "%c", ch); + else + (void)snprintf(pbuf, sizeof pbuf, "\\%o", ch); + return(pbuf); +} +#endif +#endif + +#undef matcher +#undef fast +#undef slow +#undef dissect +#undef backref +#undef step +#undef print +#undef at +#undef match +#undef nope diff --git a/libc/regex/regcomp.c b/libc/regex/regcomp.c new file mode 100644 index 0000000..5b632c8 --- /dev/null +++ b/libc/regex/regcomp.c @@ -0,0 +1,1517 @@ +/* $OpenBSD: regcomp.c,v 1.19 2008/02/23 08:13:07 otto Exp $ */ +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regcomp.c 8.5 (Berkeley) 3/20/94 + */ + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> +#include <stdlib.h> +#include <regex.h> + +#include "utils.h" +#include "regex2.h" + +#include "cclass.h" +#include "cname.h" + +/* + * parse structure, passed up and down to avoid global variables and + * other clumsinesses + */ +struct parse { + char *next; /* next character in RE */ + char *end; /* end of string (-> NUL normally) */ + int error; /* has an error been seen? */ + sop *strip; /* malloced strip */ + sopno ssize; /* malloced strip size (allocated) */ + sopno slen; /* malloced strip length (used) */ + int ncsalloc; /* number of csets allocated */ + struct re_guts *g; +# define NPAREN 10 /* we need to remember () 1-9 for back refs */ + sopno pbegin[NPAREN]; /* -> ( ([0] unused) */ + sopno pend[NPAREN]; /* -> ) ([0] unused) */ +}; + +static void p_ere(struct parse *, int); +static void p_ere_exp(struct parse *); +static void p_str(struct parse *); +static void p_bre(struct parse *, int, int); +static int p_simp_re(struct parse *, int); +static int p_count(struct parse *); +static void p_bracket(struct parse *); +static void p_b_term(struct parse *, cset *); +static void p_b_cclass(struct parse *, cset *); +static void p_b_eclass(struct parse *, cset *); +static char p_b_symbol(struct parse *); +static char p_b_coll_elem(struct parse *, int); +static char othercase(int); +static void bothcases(struct parse *, int); +static void ordinary(struct parse *, int); +static void nonnewline(struct parse *); +static void repeat(struct parse *, sopno, int, int); +static int seterr(struct parse *, int); +static cset *allocset(struct parse *); +static void freeset(struct parse *, cset *); +static int freezeset(struct parse *, cset *); +static int firstch(struct parse *, cset *); +static int nch(struct parse *, cset *); +static void mcadd(struct parse *, cset *, char *); +static void mcinvert(struct parse *, cset *); +static void mccase(struct parse *, cset *); +static int isinsets(struct re_guts *, int); +static int samesets(struct re_guts *, int, int); +static void categorize(struct parse *, struct re_guts *); +static sopno dupl(struct parse *, sopno, sopno); +static void doemit(struct parse *, sop, size_t); +static void doinsert(struct parse *, sop, size_t, sopno); +static void dofwd(struct parse *, sopno, sop); +static void enlarge(struct parse *, sopno); +static void stripsnug(struct parse *, struct re_guts *); +static void findmust(struct parse *, struct re_guts *); +static sopno pluscount(struct parse *, struct re_guts *); + +static char nuls[10]; /* place to point scanner in event of error */ + +/* + * macros for use with parse structure + * BEWARE: these know that the parse structure is named `p' !!! + */ +#define PEEK() (*p->next) +#define PEEK2() (*(p->next+1)) +#define MORE() (p->next < p->end) +#define MORE2() (p->next+1 < p->end) +#define SEE(c) (MORE() && PEEK() == (c)) +#define SEETWO(a, b) (MORE() && MORE2() && PEEK() == (a) && PEEK2() == (b)) +#define EAT(c) ((SEE(c)) ? (NEXT(), 1) : 0) +#define EATTWO(a, b) ((SEETWO(a, b)) ? (NEXT2(), 1) : 0) +#define NEXT() (p->next++) +#define NEXT2() (p->next += 2) +#define NEXTn(n) (p->next += (n)) +#define GETNEXT() (*p->next++) +#define SETERROR(e) seterr(p, (e)) +#define REQUIRE(co, e) ((co) || SETERROR(e)) +#define MUSTSEE(c, e) (REQUIRE(MORE() && PEEK() == (c), e)) +#define MUSTEAT(c, e) (REQUIRE(MORE() && GETNEXT() == (c), e)) +#define MUSTNOTSEE(c, e) (REQUIRE(!MORE() || PEEK() != (c), e)) +#define EMIT(op, sopnd) doemit(p, (sop)(op), (size_t)(sopnd)) +#define INSERT(op, pos) doinsert(p, (sop)(op), HERE()-(pos)+1, pos) +#define AHEAD(pos) dofwd(p, pos, HERE()-(pos)) +#define ASTERN(sop, pos) EMIT(sop, HERE()-pos) +#define HERE() (p->slen) +#define THERE() (p->slen - 1) +#define THERETHERE() (p->slen - 2) +#define DROP(n) (p->slen -= (n)) + +#ifndef NDEBUG +static int never = 0; /* for use in asserts; shuts lint up */ +#else +#define never 0 /* some <assert.h>s have bugs too */ +#endif + +/* + - regcomp - interface for parser and compilation + */ +int /* 0 success, otherwise REG_something */ +regcomp(regex_t *preg, const char *pattern, int cflags) +{ + struct parse pa; + struct re_guts *g; + struct parse *p = &pa; + int i; + size_t len; +#ifdef REDEBUG +# define GOODFLAGS(f) (f) +#else +# define GOODFLAGS(f) ((f)&~REG_DUMP) +#endif + + cflags = GOODFLAGS(cflags); + if ((cflags®_EXTENDED) && (cflags®_NOSPEC)) + return(REG_INVARG); + + if (cflags®_PEND) { + if (preg->re_endp < pattern) + return(REG_INVARG); + len = preg->re_endp - pattern; + } else + len = strlen((char *)pattern); + + /* do the mallocs early so failure handling is easy */ + g = (struct re_guts *)malloc(sizeof(struct re_guts) + + (NC-1)*sizeof(cat_t)); + if (g == NULL) + return(REG_ESPACE); + p->ssize = len/(size_t)2*(size_t)3 + (size_t)1; /* ugh */ + p->strip = (sop *)calloc(p->ssize, sizeof(sop)); + p->slen = 0; + if (p->strip == NULL) { + free((char *)g); + return(REG_ESPACE); + } + + /* set things up */ + p->g = g; + p->next = (char *)pattern; /* convenience; we do not modify it */ + p->end = p->next + len; + p->error = 0; + p->ncsalloc = 0; + for (i = 0; i < NPAREN; i++) { + p->pbegin[i] = 0; + p->pend[i] = 0; + } + g->csetsize = NC; + g->sets = NULL; + g->setbits = NULL; + g->ncsets = 0; + g->cflags = cflags; + g->iflags = 0; + g->nbol = 0; + g->neol = 0; + g->must = NULL; + g->mlen = 0; + g->nsub = 0; + g->ncategories = 1; /* category 0 is "everything else" */ + g->categories = &g->catspace[-(CHAR_MIN)]; + (void) memset((char *)g->catspace, 0, NC*sizeof(cat_t)); + g->backrefs = 0; + + /* do it */ + EMIT(OEND, 0); + g->firststate = THERE(); + if (cflags®_EXTENDED) + p_ere(p, OUT); + else if (cflags®_NOSPEC) + p_str(p); + else + p_bre(p, OUT, OUT); + EMIT(OEND, 0); + g->laststate = THERE(); + + /* tidy up loose ends and fill things in */ + categorize(p, g); + stripsnug(p, g); + findmust(p, g); + g->nplus = pluscount(p, g); + g->magic = MAGIC2; + preg->re_nsub = g->nsub; + preg->re_g = g; + preg->re_magic = MAGIC1; +#ifndef REDEBUG + /* not debugging, so can't rely on the assert() in regexec() */ + if (g->iflags&BAD) + SETERROR(REG_ASSERT); +#endif + + /* win or lose, we're done */ + if (p->error != 0) /* lose */ + regfree(preg); + return(p->error); +} + +/* + - p_ere - ERE parser top level, concatenation and alternation + */ +static void +p_ere(struct parse *p, int stop) /* character this ERE should end at */ +{ + char c; + sopno prevback; + sopno prevfwd; + sopno conc; + int first = 1; /* is this the first alternative? */ + + for (;;) { + /* do a bunch of concatenated expressions */ + conc = HERE(); + while (MORE() && (c = PEEK()) != '|' && c != stop) + p_ere_exp(p); + REQUIRE(HERE() != conc, REG_EMPTY); /* require nonempty */ + + if (!EAT('|')) + break; /* NOTE BREAK OUT */ + + if (first) { + INSERT(OCH_, conc); /* offset is wrong */ + prevfwd = conc; + prevback = conc; + first = 0; + } + ASTERN(OOR1, prevback); + prevback = THERE(); + AHEAD(prevfwd); /* fix previous offset */ + prevfwd = HERE(); + EMIT(OOR2, 0); /* offset is very wrong */ + } + + if (!first) { /* tail-end fixups */ + AHEAD(prevfwd); + ASTERN(O_CH, prevback); + } + + assert(!MORE() || SEE(stop)); +} + +/* + - p_ere_exp - parse one subERE, an atom possibly followed by a repetition op + */ +static void +p_ere_exp(struct parse *p) +{ + char c; + sopno pos; + int count; + int count2; + sopno subno; + int wascaret = 0; + + assert(MORE()); /* caller should have ensured this */ + c = GETNEXT(); + + pos = HERE(); + switch (c) { + case '(': + REQUIRE(MORE(), REG_EPAREN); + p->g->nsub++; + subno = p->g->nsub; + if (subno < NPAREN) + p->pbegin[subno] = HERE(); + EMIT(OLPAREN, subno); + if (!SEE(')')) + p_ere(p, ')'); + if (subno < NPAREN) { + p->pend[subno] = HERE(); + assert(p->pend[subno] != 0); + } + EMIT(ORPAREN, subno); + MUSTEAT(')', REG_EPAREN); + break; +#ifndef POSIX_MISTAKE + case ')': /* happens only if no current unmatched ( */ + /* + * You may ask, why the ifndef? Because I didn't notice + * this until slightly too late for 1003.2, and none of the + * other 1003.2 regular-expression reviewers noticed it at + * all. So an unmatched ) is legal POSIX, at least until + * we can get it fixed. + */ + SETERROR(REG_EPAREN); + break; +#endif + case '^': + EMIT(OBOL, 0); + p->g->iflags |= USEBOL; + p->g->nbol++; + wascaret = 1; + break; + case '$': + EMIT(OEOL, 0); + p->g->iflags |= USEEOL; + p->g->neol++; + break; + case '|': + SETERROR(REG_EMPTY); + break; + case '*': + case '+': + case '?': + SETERROR(REG_BADRPT); + break; + case '.': + if (p->g->cflags®_NEWLINE) + nonnewline(p); + else + EMIT(OANY, 0); + break; + case '[': + p_bracket(p); + break; + case '\\': + REQUIRE(MORE(), REG_EESCAPE); + c = GETNEXT(); + ordinary(p, c); + break; + case '{': /* okay as ordinary except if digit follows */ + REQUIRE(!MORE() || !isdigit((uch)PEEK()), REG_BADRPT); + /* FALLTHROUGH */ + default: + ordinary(p, c); + break; + } + + if (!MORE()) + return; + c = PEEK(); + /* we call { a repetition if followed by a digit */ + if (!( c == '*' || c == '+' || c == '?' || + (c == '{' && MORE2() && isdigit((uch)PEEK2())) )) + return; /* no repetition, we're done */ + NEXT(); + + REQUIRE(!wascaret, REG_BADRPT); + switch (c) { + case '*': /* implemented as +? */ + /* this case does not require the (y|) trick, noKLUDGE */ + INSERT(OPLUS_, pos); + ASTERN(O_PLUS, pos); + INSERT(OQUEST_, pos); + ASTERN(O_QUEST, pos); + break; + case '+': + INSERT(OPLUS_, pos); + ASTERN(O_PLUS, pos); + break; + case '?': + /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ + INSERT(OCH_, pos); /* offset slightly wrong */ + ASTERN(OOR1, pos); /* this one's right */ + AHEAD(pos); /* fix the OCH_ */ + EMIT(OOR2, 0); /* offset very wrong... */ + AHEAD(THERE()); /* ...so fix it */ + ASTERN(O_CH, THERETHERE()); + break; + case '{': + count = p_count(p); + if (EAT(',')) { + if (isdigit((uch)PEEK())) { + count2 = p_count(p); + REQUIRE(count <= count2, REG_BADBR); + } else /* single number with comma */ + count2 = INFINITY; + } else /* just a single number */ + count2 = count; + repeat(p, pos, count, count2); + if (!EAT('}')) { /* error heuristics */ + while (MORE() && PEEK() != '}') + NEXT(); + REQUIRE(MORE(), REG_EBRACE); + SETERROR(REG_BADBR); + } + break; + } + + if (!MORE()) + return; + c = PEEK(); + if (!( c == '*' || c == '+' || c == '?' || + (c == '{' && MORE2() && isdigit((uch)PEEK2())) ) ) + return; + SETERROR(REG_BADRPT); +} + +/* + - p_str - string (no metacharacters) "parser" + */ +static void +p_str(struct parse *p) +{ + REQUIRE(MORE(), REG_EMPTY); + while (MORE()) + ordinary(p, GETNEXT()); +} + +/* + - p_bre - BRE parser top level, anchoring and concatenation + * Giving end1 as OUT essentially eliminates the end1/end2 check. + * + * This implementation is a bit of a kludge, in that a trailing $ is first + * taken as an ordinary character and then revised to be an anchor. The + * only undesirable side effect is that '$' gets included as a character + * category in such cases. This is fairly harmless; not worth fixing. + * The amount of lookahead needed to avoid this kludge is excessive. + */ +static void +p_bre(struct parse *p, + int end1, /* first terminating character */ + int end2) /* second terminating character */ +{ + sopno start = HERE(); + int first = 1; /* first subexpression? */ + int wasdollar = 0; + + if (EAT('^')) { + EMIT(OBOL, 0); + p->g->iflags |= USEBOL; + p->g->nbol++; + } + while (MORE() && !SEETWO(end1, end2)) { + wasdollar = p_simp_re(p, first); + first = 0; + } + if (wasdollar) { /* oops, that was a trailing anchor */ + DROP(1); + EMIT(OEOL, 0); + p->g->iflags |= USEEOL; + p->g->neol++; + } + + REQUIRE(HERE() != start, REG_EMPTY); /* require nonempty */ +} + +/* + - p_simp_re - parse a simple RE, an atom possibly followed by a repetition + */ +static int /* was the simple RE an unbackslashed $? */ +p_simp_re(struct parse *p, + int starordinary) /* is a leading * an ordinary character? */ +{ + int c; + int count; + int count2; + sopno pos; + int i; + sopno subno; +# define BACKSL (1<<CHAR_BIT) + + pos = HERE(); /* repetion op, if any, covers from here */ + + assert(MORE()); /* caller should have ensured this */ + c = GETNEXT(); + if (c == '\\') { + REQUIRE(MORE(), REG_EESCAPE); + c = BACKSL | GETNEXT(); + } + switch (c) { + case '.': + if (p->g->cflags®_NEWLINE) + nonnewline(p); + else + EMIT(OANY, 0); + break; + case '[': + p_bracket(p); + break; + case BACKSL|'{': + SETERROR(REG_BADRPT); + break; + case BACKSL|'(': + p->g->nsub++; + subno = p->g->nsub; + if (subno < NPAREN) + p->pbegin[subno] = HERE(); + EMIT(OLPAREN, subno); + /* the MORE here is an error heuristic */ + if (MORE() && !SEETWO('\\', ')')) + p_bre(p, '\\', ')'); + if (subno < NPAREN) { + p->pend[subno] = HERE(); + assert(p->pend[subno] != 0); + } + EMIT(ORPAREN, subno); + REQUIRE(EATTWO('\\', ')'), REG_EPAREN); + break; + case BACKSL|')': /* should not get here -- must be user */ + case BACKSL|'}': + SETERROR(REG_EPAREN); + break; + case BACKSL|'1': + case BACKSL|'2': + case BACKSL|'3': + case BACKSL|'4': + case BACKSL|'5': + case BACKSL|'6': + case BACKSL|'7': + case BACKSL|'8': + case BACKSL|'9': + i = (c&~BACKSL) - '0'; + assert(i < NPAREN); + if (p->pend[i] != 0) { + assert(i <= p->g->nsub); + EMIT(OBACK_, i); + assert(p->pbegin[i] != 0); + assert(OP(p->strip[p->pbegin[i]]) == OLPAREN); + assert(OP(p->strip[p->pend[i]]) == ORPAREN); + (void) dupl(p, p->pbegin[i]+1, p->pend[i]); + EMIT(O_BACK, i); + } else + SETERROR(REG_ESUBREG); + p->g->backrefs = 1; + break; + case '*': + REQUIRE(starordinary, REG_BADRPT); + /* FALLTHROUGH */ + default: + ordinary(p, (char)c); + break; + } + + if (EAT('*')) { /* implemented as +? */ + /* this case does not require the (y|) trick, noKLUDGE */ + INSERT(OPLUS_, pos); + ASTERN(O_PLUS, pos); + INSERT(OQUEST_, pos); + ASTERN(O_QUEST, pos); + } else if (EATTWO('\\', '{')) { + count = p_count(p); + if (EAT(',')) { + if (MORE() && isdigit((uch)PEEK())) { + count2 = p_count(p); + REQUIRE(count <= count2, REG_BADBR); + } else /* single number with comma */ + count2 = INFINITY; + } else /* just a single number */ + count2 = count; + repeat(p, pos, count, count2); + if (!EATTWO('\\', '}')) { /* error heuristics */ + while (MORE() && !SEETWO('\\', '}')) + NEXT(); + REQUIRE(MORE(), REG_EBRACE); + SETERROR(REG_BADBR); + } + } else if (c == '$') /* $ (but not \$) ends it */ + return(1); + + return(0); +} + +/* + - p_count - parse a repetition count + */ +static int /* the value */ +p_count(struct parse *p) +{ + int count = 0; + int ndigits = 0; + + while (MORE() && isdigit((uch)PEEK()) && count <= DUPMAX) { + count = count*10 + (GETNEXT() - '0'); + ndigits++; + } + + REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR); + return(count); +} + +/* + - p_bracket - parse a bracketed character list + * + * Note a significant property of this code: if the allocset() did SETERROR, + * no set operations are done. + */ +static void +p_bracket(struct parse *p) +{ + cset *cs; + int invert = 0; + + /* Dept of Truly Sickening Special-Case Kludges */ + if (p->next + 5 < p->end && strncmp(p->next, "[:<:]]", 6) == 0) { + EMIT(OBOW, 0); + NEXTn(6); + return; + } + if (p->next + 5 < p->end && strncmp(p->next, "[:>:]]", 6) == 0) { + EMIT(OEOW, 0); + NEXTn(6); + return; + } + + if ((cs = allocset(p)) == NULL) { + /* allocset did set error status in p */ + return; + } + + if (EAT('^')) + invert++; /* make note to invert set at end */ + if (EAT(']')) + CHadd(cs, ']'); + else if (EAT('-')) + CHadd(cs, '-'); + while (MORE() && PEEK() != ']' && !SEETWO('-', ']')) + p_b_term(p, cs); + if (EAT('-')) + CHadd(cs, '-'); + MUSTEAT(']', REG_EBRACK); + + if (p->error != 0) { /* don't mess things up further */ + freeset(p, cs); + return; + } + + if (p->g->cflags®_ICASE) { + int i; + int ci; + + for (i = p->g->csetsize - 1; i >= 0; i--) + if (CHIN(cs, i) && isalpha(i)) { + ci = othercase(i); + if (ci != i) + CHadd(cs, ci); + } + if (cs->multis != NULL) + mccase(p, cs); + } + if (invert) { + int i; + + for (i = p->g->csetsize - 1; i >= 0; i--) + if (CHIN(cs, i)) + CHsub(cs, i); + else + CHadd(cs, i); + if (p->g->cflags®_NEWLINE) + CHsub(cs, '\n'); + if (cs->multis != NULL) + mcinvert(p, cs); + } + + assert(cs->multis == NULL); /* xxx */ + + if (nch(p, cs) == 1) { /* optimize singleton sets */ + ordinary(p, firstch(p, cs)); + freeset(p, cs); + } else + EMIT(OANYOF, freezeset(p, cs)); +} + +/* + - p_b_term - parse one term of a bracketed character list + */ +static void +p_b_term(struct parse *p, cset *cs) +{ + char c; + char start, finish; + int i; + + /* classify what we've got */ + switch ((MORE()) ? PEEK() : '\0') { + case '[': + c = (MORE2()) ? PEEK2() : '\0'; + break; + case '-': + SETERROR(REG_ERANGE); + return; /* NOTE RETURN */ + break; + default: + c = '\0'; + break; + } + + switch (c) { + case ':': /* character class */ + NEXT2(); + REQUIRE(MORE(), REG_EBRACK); + c = PEEK(); + REQUIRE(c != '-' && c != ']', REG_ECTYPE); + p_b_cclass(p, cs); + REQUIRE(MORE(), REG_EBRACK); + REQUIRE(EATTWO(':', ']'), REG_ECTYPE); + break; + case '=': /* equivalence class */ + NEXT2(); + REQUIRE(MORE(), REG_EBRACK); + c = PEEK(); + REQUIRE(c != '-' && c != ']', REG_ECOLLATE); + p_b_eclass(p, cs); + REQUIRE(MORE(), REG_EBRACK); + REQUIRE(EATTWO('=', ']'), REG_ECOLLATE); + break; + default: /* symbol, ordinary character, or range */ +/* xxx revision needed for multichar stuff */ + start = p_b_symbol(p); + if (SEE('-') && MORE2() && PEEK2() != ']') { + /* range */ + NEXT(); + if (EAT('-')) + finish = '-'; + else + finish = p_b_symbol(p); + } else + finish = start; +/* xxx what about signed chars here... */ + REQUIRE(start <= finish, REG_ERANGE); + for (i = start; i <= finish; i++) + CHadd(cs, i); + break; + } +} + +/* + - p_b_cclass - parse a character-class name and deal with it + */ +static void +p_b_cclass(struct parse *p, cset *cs) +{ + char *sp = p->next; + struct cclass *cp; + size_t len; + char *u; + char c; + + while (MORE() && isalpha(PEEK())) + NEXT(); + len = p->next - sp; + for (cp = cclasses; cp->name != NULL; cp++) + if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0') + break; + if (cp->name == NULL) { + /* oops, didn't find it */ + SETERROR(REG_ECTYPE); + return; + } + + u = cp->chars; + while ((c = *u++) != '\0') + CHadd(cs, c); + for (u = cp->multis; *u != '\0'; u += strlen(u) + 1) + MCadd(p, cs, u); +} + +/* + - p_b_eclass - parse an equivalence-class name and deal with it + * + * This implementation is incomplete. xxx + */ +static void +p_b_eclass(struct parse *p, cset *cs) +{ + char c; + + c = p_b_coll_elem(p, '='); + CHadd(cs, c); +} + +/* + - p_b_symbol - parse a character or [..]ed multicharacter collating symbol + */ +static char /* value of symbol */ +p_b_symbol(struct parse *p) +{ + char value; + + REQUIRE(MORE(), REG_EBRACK); + if (!EATTWO('[', '.')) + return(GETNEXT()); + + /* collating symbol */ + value = p_b_coll_elem(p, '.'); + REQUIRE(EATTWO('.', ']'), REG_ECOLLATE); + return(value); +} + +/* + - p_b_coll_elem - parse a collating-element name and look it up + */ +static char /* value of collating element */ +p_b_coll_elem(struct parse *p, + int endc) /* name ended by endc,']' */ +{ + char *sp = p->next; + struct cname *cp; + int len; + + while (MORE() && !SEETWO(endc, ']')) + NEXT(); + if (!MORE()) { + SETERROR(REG_EBRACK); + return(0); + } + len = p->next - sp; + for (cp = cnames; cp->name != NULL; cp++) + if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0') + return(cp->code); /* known name */ + if (len == 1) + return(*sp); /* single character */ + SETERROR(REG_ECOLLATE); /* neither */ + return(0); +} + +/* + - othercase - return the case counterpart of an alphabetic + */ +static char /* if no counterpart, return ch */ +othercase(int ch) +{ + ch = (uch)ch; + assert(isalpha(ch)); + if (isupper(ch)) + return ((uch)tolower(ch)); + else if (islower(ch)) + return ((uch)toupper(ch)); + else /* peculiar, but could happen */ + return(ch); +} + +/* + - bothcases - emit a dualcase version of a two-case character + * + * Boy, is this implementation ever a kludge... + */ +static void +bothcases(struct parse *p, int ch) +{ + char *oldnext = p->next; + char *oldend = p->end; + char bracket[3]; + + ch = (uch)ch; + assert(othercase(ch) != ch); /* p_bracket() would recurse */ + p->next = bracket; + p->end = bracket+2; + bracket[0] = ch; + bracket[1] = ']'; + bracket[2] = '\0'; + p_bracket(p); + assert(p->next == bracket+2); + p->next = oldnext; + p->end = oldend; +} + +/* + - ordinary - emit an ordinary character + */ +static void +ordinary(struct parse *p, int ch) +{ + cat_t *cap = p->g->categories; + + if ((p->g->cflags®_ICASE) && isalpha((uch)ch) && othercase(ch) != ch) + bothcases(p, ch); + else { + EMIT(OCHAR, (uch)ch); + if (cap[ch] == 0) + cap[ch] = p->g->ncategories++; + } +} + +/* + - nonnewline - emit REG_NEWLINE version of OANY + * + * Boy, is this implementation ever a kludge... + */ +static void +nonnewline(struct parse *p) +{ + char *oldnext = p->next; + char *oldend = p->end; + char bracket[4]; + + p->next = bracket; + p->end = bracket+3; + bracket[0] = '^'; + bracket[1] = '\n'; + bracket[2] = ']'; + bracket[3] = '\0'; + p_bracket(p); + assert(p->next == bracket+3); + p->next = oldnext; + p->end = oldend; +} + +/* + - repeat - generate code for a bounded repetition, recursively if needed + */ +static void +repeat(struct parse *p, + sopno start, /* operand from here to end of strip */ + int from, /* repeated from this number */ + int to) /* to this number of times (maybe INFINITY) */ +{ + sopno finish = HERE(); +# define N 2 +# define INF 3 +# define REP(f, t) ((f)*8 + (t)) +# define MAP(n) (((n) <= 1) ? (n) : ((n) == INFINITY) ? INF : N) + sopno copy; + + if (p->error != 0) /* head off possible runaway recursion */ + return; + + assert(from <= to); + + switch (REP(MAP(from), MAP(to))) { + case REP(0, 0): /* must be user doing this */ + DROP(finish-start); /* drop the operand */ + break; + case REP(0, 1): /* as x{1,1}? */ + case REP(0, N): /* as x{1,n}? */ + case REP(0, INF): /* as x{1,}? */ + /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ + INSERT(OCH_, start); /* offset is wrong... */ + repeat(p, start+1, 1, to); + ASTERN(OOR1, start); + AHEAD(start); /* ... fix it */ + EMIT(OOR2, 0); + AHEAD(THERE()); + ASTERN(O_CH, THERETHERE()); + break; + case REP(1, 1): /* trivial case */ + /* done */ + break; + case REP(1, N): /* as x?x{1,n-1} */ + /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ + INSERT(OCH_, start); + ASTERN(OOR1, start); + AHEAD(start); + EMIT(OOR2, 0); /* offset very wrong... */ + AHEAD(THERE()); /* ...so fix it */ + ASTERN(O_CH, THERETHERE()); + copy = dupl(p, start+1, finish+1); + assert(copy == finish+4); + repeat(p, copy, 1, to-1); + break; + case REP(1, INF): /* as x+ */ + INSERT(OPLUS_, start); + ASTERN(O_PLUS, start); + break; + case REP(N, N): /* as xx{m-1,n-1} */ + copy = dupl(p, start, finish); + repeat(p, copy, from-1, to-1); + break; + case REP(N, INF): /* as xx{n-1,INF} */ + copy = dupl(p, start, finish); + repeat(p, copy, from-1, to); + break; + default: /* "can't happen" */ + SETERROR(REG_ASSERT); /* just in case */ + break; + } +} + +/* + - seterr - set an error condition + */ +static int /* useless but makes type checking happy */ +seterr(struct parse *p, int e) +{ + if (p->error == 0) /* keep earliest error condition */ + p->error = e; + p->next = nuls; /* try to bring things to a halt */ + p->end = nuls; + return(0); /* make the return value well-defined */ +} + +/* + - allocset - allocate a set of characters for [] + */ +static cset * +allocset(struct parse *p) +{ + int no = p->g->ncsets++; + size_t nc; + size_t nbytes; + cset *cs; + size_t css = (size_t)p->g->csetsize; + int i; + + if (no >= p->ncsalloc) { /* need another column of space */ + void *ptr; + + p->ncsalloc += CHAR_BIT; + nc = p->ncsalloc; + assert(nc % CHAR_BIT == 0); + nbytes = nc / CHAR_BIT * css; + + ptr = (cset *)realloc((char *)p->g->sets, nc * sizeof(cset)); + if (ptr == NULL) + goto nomem; + p->g->sets = ptr; + + ptr = (uch *)realloc((char *)p->g->setbits, nbytes); + if (ptr == NULL) + goto nomem; + p->g->setbits = ptr; + + for (i = 0; i < no; i++) + p->g->sets[i].ptr = p->g->setbits + css*(i/CHAR_BIT); + + (void) memset((char *)p->g->setbits + (nbytes - css), 0, css); + } + /* XXX should not happen */ + if (p->g->sets == NULL || p->g->setbits == NULL) + goto nomem; + + cs = &p->g->sets[no]; + cs->ptr = p->g->setbits + css*((no)/CHAR_BIT); + cs->mask = 1 << ((no) % CHAR_BIT); + cs->hash = 0; + cs->smultis = 0; + cs->multis = NULL; + + return(cs); +nomem: + free(p->g->sets); + p->g->sets = NULL; + free(p->g->setbits); + p->g->setbits = NULL; + + SETERROR(REG_ESPACE); + /* caller's responsibility not to do set ops */ + return(NULL); +} + +/* + - freeset - free a now-unused set + */ +static void +freeset(struct parse *p, cset *cs) +{ + int i; + cset *top = &p->g->sets[p->g->ncsets]; + size_t css = (size_t)p->g->csetsize; + + for (i = 0; i < css; i++) + CHsub(cs, i); + if (cs == top-1) /* recover only the easy case */ + p->g->ncsets--; +} + +/* + - freezeset - final processing on a set of characters + * + * The main task here is merging identical sets. This is usually a waste + * of time (although the hash code minimizes the overhead), but can win + * big if REG_ICASE is being used. REG_ICASE, by the way, is why the hash + * is done using addition rather than xor -- all ASCII [aA] sets xor to + * the same value! + */ +static int /* set number */ +freezeset(struct parse *p, cset *cs) +{ + uch h = cs->hash; + int i; + cset *top = &p->g->sets[p->g->ncsets]; + cset *cs2; + size_t css = (size_t)p->g->csetsize; + + /* look for an earlier one which is the same */ + for (cs2 = &p->g->sets[0]; cs2 < top; cs2++) + if (cs2->hash == h && cs2 != cs) { + /* maybe */ + for (i = 0; i < css; i++) + if (!!CHIN(cs2, i) != !!CHIN(cs, i)) + break; /* no */ + if (i == css) + break; /* yes */ + } + + if (cs2 < top) { /* found one */ + freeset(p, cs); + cs = cs2; + } + + return((int)(cs - p->g->sets)); +} + +/* + - firstch - return first character in a set (which must have at least one) + */ +static int /* character; there is no "none" value */ +firstch(struct parse *p, cset *cs) +{ + int i; + size_t css = (size_t)p->g->csetsize; + + for (i = 0; i < css; i++) + if (CHIN(cs, i)) + return((char)i); + assert(never); + return(0); /* arbitrary */ +} + +/* + - nch - number of characters in a set + */ +static int +nch(struct parse *p, cset *cs) +{ + int i; + size_t css = (size_t)p->g->csetsize; + int n = 0; + + for (i = 0; i < css; i++) + if (CHIN(cs, i)) + n++; + return(n); +} + +/* + - mcadd - add a collating element to a cset + */ +static void +mcadd( struct parse *p, cset *cs, char *cp) +{ + size_t oldend = cs->smultis; + void *np; + + cs->smultis += strlen(cp) + 1; + np = realloc(cs->multis, cs->smultis); + if (np == NULL) { + if (cs->multis) + free(cs->multis); + cs->multis = NULL; + SETERROR(REG_ESPACE); + return; + } + cs->multis = np; + + strlcpy(cs->multis + oldend - 1, cp, cs->smultis - oldend + 1); +} + +/* + - mcinvert - invert the list of collating elements in a cset + * + * This would have to know the set of possibilities. Implementation + * is deferred. + */ +/* ARGSUSED */ +static void +mcinvert(struct parse *p, cset *cs) +{ + assert(cs->multis == NULL); /* xxx */ +} + +/* + - mccase - add case counterparts of the list of collating elements in a cset + * + * This would have to know the set of possibilities. Implementation + * is deferred. + */ +/* ARGSUSED */ +static void +mccase(struct parse *p, cset *cs) +{ + assert(cs->multis == NULL); /* xxx */ +} + +/* + - isinsets - is this character in any sets? + */ +static int /* predicate */ +isinsets(struct re_guts *g, int c) +{ + uch *col; + int i; + int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT; + unsigned uc = (uch)c; + + for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize) + if (col[uc] != 0) + return(1); + return(0); +} + +/* + - samesets - are these two characters in exactly the same sets? + */ +static int /* predicate */ +samesets(struct re_guts *g, int c1, int c2) +{ + uch *col; + int i; + int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT; + unsigned uc1 = (uch)c1; + unsigned uc2 = (uch)c2; + + for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize) + if (col[uc1] != col[uc2]) + return(0); + return(1); +} + +/* + - categorize - sort out character categories + */ +static void +categorize(struct parse *p, struct re_guts *g) +{ + cat_t *cats = g->categories; + int c; + int c2; + cat_t cat; + + /* avoid making error situations worse */ + if (p->error != 0) + return; + + for (c = CHAR_MIN; c <= CHAR_MAX; c++) + if (cats[c] == 0 && isinsets(g, c)) { + cat = g->ncategories++; + cats[c] = cat; + for (c2 = c+1; c2 <= CHAR_MAX; c2++) + if (cats[c2] == 0 && samesets(g, c, c2)) + cats[c2] = cat; + } +} + +/* + - dupl - emit a duplicate of a bunch of sops + */ +static sopno /* start of duplicate */ +dupl(struct parse *p, + sopno start, /* from here */ + sopno finish) /* to this less one */ +{ + sopno ret = HERE(); + sopno len = finish - start; + + assert(finish >= start); + if (len == 0) + return(ret); + enlarge(p, p->ssize + len); /* this many unexpected additions */ + assert(p->ssize >= p->slen + len); + (void) memcpy((char *)(p->strip + p->slen), + (char *)(p->strip + start), (size_t)len*sizeof(sop)); + p->slen += len; + return(ret); +} + +/* + - doemit - emit a strip operator + * + * It might seem better to implement this as a macro with a function as + * hard-case backup, but it's just too big and messy unless there are + * some changes to the data structures. Maybe later. + */ +static void +doemit(struct parse *p, sop op, size_t opnd) +{ + /* avoid making error situations worse */ + if (p->error != 0) + return; + + /* deal with oversize operands ("can't happen", more or less) */ + assert(opnd < 1<<OPSHIFT); + + /* deal with undersized strip */ + if (p->slen >= p->ssize) + enlarge(p, (p->ssize+1) / 2 * 3); /* +50% */ + assert(p->slen < p->ssize); + + /* finally, it's all reduced to the easy case */ + p->strip[p->slen++] = SOP(op, opnd); +} + +/* + - doinsert - insert a sop into the strip + */ +static void +doinsert(struct parse *p, sop op, size_t opnd, sopno pos) +{ + sopno sn; + sop s; + int i; + + /* avoid making error situations worse */ + if (p->error != 0) + return; + + sn = HERE(); + EMIT(op, opnd); /* do checks, ensure space */ + assert(HERE() == sn+1); + s = p->strip[sn]; + + /* adjust paren pointers */ + assert(pos > 0); + for (i = 1; i < NPAREN; i++) { + if (p->pbegin[i] >= pos) { + p->pbegin[i]++; + } + if (p->pend[i] >= pos) { + p->pend[i]++; + } + } + + memmove((char *)&p->strip[pos+1], (char *)&p->strip[pos], + (HERE()-pos-1)*sizeof(sop)); + p->strip[pos] = s; +} + +/* + - dofwd - complete a forward reference + */ +static void +dofwd(struct parse *p, sopno pos, sop value) +{ + /* avoid making error situations worse */ + if (p->error != 0) + return; + + assert(value < 1<<OPSHIFT); + p->strip[pos] = OP(p->strip[pos]) | value; +} + +/* + - enlarge - enlarge the strip + */ +static void +enlarge(struct parse *p, sopno size) +{ + sop *sp; + + if (p->ssize >= size) + return; + + sp = (sop *)realloc(p->strip, size*sizeof(sop)); + if (sp == NULL) { + SETERROR(REG_ESPACE); + return; + } + p->strip = sp; + p->ssize = size; +} + +/* + - stripsnug - compact the strip + */ +static void +stripsnug(struct parse *p, struct re_guts *g) +{ + g->nstates = p->slen; + g->strip = (sop *)realloc((char *)p->strip, p->slen * sizeof(sop)); + if (g->strip == NULL) { + SETERROR(REG_ESPACE); + g->strip = p->strip; + } +} + +/* + - findmust - fill in must and mlen with longest mandatory literal string + * + * This algorithm could do fancy things like analyzing the operands of | + * for common subsequences. Someday. This code is simple and finds most + * of the interesting cases. + * + * Note that must and mlen got initialized during setup. + */ +static void +findmust(struct parse *p, struct re_guts *g) +{ + sop *scan; + sop *start; /* start initialized in the default case, after that */ + sop *newstart; /* newstart was initialized in the OCHAR case */ + sopno newlen; + sop s; + char *cp; + sopno i; + + /* avoid making error situations worse */ + if (p->error != 0) + return; + + /* find the longest OCHAR sequence in strip */ + newlen = 0; + scan = g->strip + 1; + do { + s = *scan++; + switch (OP(s)) { + case OCHAR: /* sequence member */ + if (newlen == 0) /* new sequence */ + newstart = scan - 1; + newlen++; + break; + case OPLUS_: /* things that don't break one */ + case OLPAREN: + case ORPAREN: + break; + case OQUEST_: /* things that must be skipped */ + case OCH_: + scan--; + do { + scan += OPND(s); + s = *scan; + /* assert() interferes w debug printouts */ + if (OP(s) != O_QUEST && OP(s) != O_CH && + OP(s) != OOR2) { + g->iflags |= BAD; + return; + } + } while (OP(s) != O_QUEST && OP(s) != O_CH); + /* fallthrough */ + default: /* things that break a sequence */ + if (newlen > g->mlen) { /* ends one */ + start = newstart; + g->mlen = newlen; + } + newlen = 0; + break; + } + } while (OP(s) != OEND); + + if (g->mlen == 0) /* there isn't one */ + return; + + /* turn it into a character string */ + g->must = malloc((size_t)g->mlen + 1); + if (g->must == NULL) { /* argh; just forget it */ + g->mlen = 0; + return; + } + cp = g->must; + scan = start; + for (i = g->mlen; i > 0; i--) { + while (OP(s = *scan++) != OCHAR) + continue; + assert(cp < g->must + g->mlen); + *cp++ = (char)OPND(s); + } + assert(cp == g->must + g->mlen); + *cp++ = '\0'; /* just on general principles */ +} + +/* + - pluscount - count + nesting + */ +static sopno /* nesting depth */ +pluscount(struct parse *p, struct re_guts *g) +{ + sop *scan; + sop s; + sopno plusnest = 0; + sopno maxnest = 0; + + if (p->error != 0) + return(0); /* there may not be an OEND */ + + scan = g->strip + 1; + do { + s = *scan++; + switch (OP(s)) { + case OPLUS_: + plusnest++; + break; + case O_PLUS: + if (plusnest > maxnest) + maxnest = plusnest; + plusnest--; + break; + } + } while (OP(s) != OEND); + if (plusnest != 0) + g->iflags |= BAD; + return(maxnest); +} diff --git a/libc/regex/regerror.c b/libc/regex/regerror.c new file mode 100644 index 0000000..894a939 --- /dev/null +++ b/libc/regex/regerror.c @@ -0,0 +1,130 @@ +/* $OpenBSD: regerror.c,v 1.13 2005/08/05 13:03:00 espie Exp $ */ +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regerror.c 8.4 (Berkeley) 3/20/94 + */ + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> +#include <stdlib.h> +#include <regex.h> + +#include "utils.h" + +static char *regatoi(const regex_t *, char *, int); + +static const struct rerr { + int code; + char *name; + char *explain; +} rerrs[] = { + { REG_NOMATCH, "REG_NOMATCH", "regexec() failed to match" }, + { REG_BADPAT, "REG_BADPAT", "invalid regular expression" }, + { REG_ECOLLATE, "REG_ECOLLATE", "invalid collating element" }, + { REG_ECTYPE, "REG_ECTYPE", "invalid character class" }, + { REG_EESCAPE, "REG_EESCAPE", "trailing backslash (\\)" }, + { REG_ESUBREG, "REG_ESUBREG", "invalid backreference number" }, + { REG_EBRACK, "REG_EBRACK", "brackets ([ ]) not balanced" }, + { REG_EPAREN, "REG_EPAREN", "parentheses not balanced" }, + { REG_EBRACE, "REG_EBRACE", "braces not balanced" }, + { REG_BADBR, "REG_BADBR", "invalid repetition count(s)" }, + { REG_ERANGE, "REG_ERANGE", "invalid character range" }, + { REG_ESPACE, "REG_ESPACE", "out of memory" }, + { REG_BADRPT, "REG_BADRPT", "repetition-operator operand invalid" }, + { REG_EMPTY, "REG_EMPTY", "empty (sub)expression" }, + { REG_ASSERT, "REG_ASSERT", "\"can't happen\" -- you found a bug" }, + { REG_INVARG, "REG_INVARG", "invalid argument to regex routine" }, + { 0, "", "*** unknown regexp error code ***" } +}; + +/* + - regerror - the interface to error numbers + = extern size_t regerror(int, const regex_t *, char *, size_t); + */ +/* ARGSUSED */ +size_t +regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) +{ + struct rerr *r; + size_t len; + int target = errcode &~ REG_ITOA; + char *s; + char convbuf[50]; + + if (errcode == REG_ATOI) + s = regatoi(preg, convbuf, sizeof convbuf); + else { + for (r = rerrs; r->code != 0; r++) + if (r->code == target) + break; + + if (errcode®_ITOA) { + if (r->code != 0) { + assert(strlen(r->name) < sizeof(convbuf)); + (void) strlcpy(convbuf, r->name, sizeof convbuf); + } else + (void)snprintf(convbuf, sizeof convbuf, + "REG_0x%x", target); + s = convbuf; + } else + s = r->explain; + } + + len = strlen(s) + 1; + if (errbuf_size > 0) { + strlcpy(errbuf, s, errbuf_size); + } + + return(len); +} + +/* + - regatoi - internal routine to implement REG_ATOI + */ +static char * +regatoi(const regex_t *preg, char *localbuf, int localbufsize) +{ + struct rerr *r; + + for (r = rerrs; r->code != 0; r++) + if (strcmp(r->name, preg->re_endp) == 0) + break; + if (r->code == 0) + return("0"); + + (void)snprintf(localbuf, localbufsize, "%d", r->code); + return(localbuf); +} diff --git a/libc/regex/regex2.h b/libc/regex/regex2.h new file mode 100644 index 0000000..15e15bc --- /dev/null +++ b/libc/regex/regex2.h @@ -0,0 +1,157 @@ +/* $OpenBSD: regex2.h,v 1.7 2004/11/30 17:04:23 otto Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regex2.h 8.4 (Berkeley) 3/20/94 + */ + +/* + * internals of regex_t + */ +#define MAGIC1 ((('r'^0200)<<8) | 'e') + +/* + * The internal representation is a *strip*, a sequence of + * operators ending with an endmarker. (Some terminology etc. is a + * historical relic of earlier versions which used multiple strips.) + * Certain oddities in the representation are there to permit running + * the machinery backwards; in particular, any deviation from sequential + * flow must be marked at both its source and its destination. Some + * fine points: + * + * - OPLUS_ and O_PLUS are *inside* the loop they create. + * - OQUEST_ and O_QUEST are *outside* the bypass they create. + * - OCH_ and O_CH are *outside* the multi-way branch they create, while + * OOR1 and OOR2 are respectively the end and the beginning of one of + * the branches. Note that there is an implicit OOR2 following OCH_ + * and an implicit OOR1 preceding O_CH. + * + * In state representations, an operator's bit is on to signify a state + * immediately *preceding* "execution" of that operator. + */ +typedef unsigned long sop; /* strip operator */ +typedef long sopno; +#define OPRMASK 0xf8000000LU +#define OPDMASK 0x07ffffffLU +#define OPSHIFT ((unsigned)27) +#define OP(n) ((n)&OPRMASK) +#define OPND(n) ((n)&OPDMASK) +#define SOP(op, opnd) ((op)|(opnd)) +/* operators meaning operand */ +/* (back, fwd are offsets) */ +#define OEND (1LU<<OPSHIFT) /* endmarker - */ +#define OCHAR (2LU<<OPSHIFT) /* character unsigned char */ +#define OBOL (3LU<<OPSHIFT) /* left anchor - */ +#define OEOL (4LU<<OPSHIFT) /* right anchor - */ +#define OANY (5LU<<OPSHIFT) /* . - */ +#define OANYOF (6LU<<OPSHIFT) /* [...] set number */ +#define OBACK_ (7LU<<OPSHIFT) /* begin \d paren number */ +#define O_BACK (8LU<<OPSHIFT) /* end \d paren number */ +#define OPLUS_ (9LU<<OPSHIFT) /* + prefix fwd to suffix */ +#define O_PLUS (10LU<<OPSHIFT) /* + suffix back to prefix */ +#define OQUEST_ (11LU<<OPSHIFT) /* ? prefix fwd to suffix */ +#define O_QUEST (12LU<<OPSHIFT) /* ? suffix back to prefix */ +#define OLPAREN (13LU<<OPSHIFT) /* ( fwd to ) */ +#define ORPAREN (14LU<<OPSHIFT) /* ) back to ( */ +#define OCH_ (15LU<<OPSHIFT) /* begin choice fwd to OOR2 */ +#define OOR1 (16LU<<OPSHIFT) /* | pt. 1 back to OOR1 or OCH_ */ +#define OOR2 (17LU<<OPSHIFT) /* | pt. 2 fwd to OOR2 or O_CH */ +#define O_CH (18LU<<OPSHIFT) /* end choice back to OOR1 */ +#define OBOW (19LU<<OPSHIFT) /* begin word - */ +#define OEOW (20LU<<OPSHIFT) /* end word - */ + +/* + * Structure for [] character-set representation. Character sets are + * done as bit vectors, grouped 8 to a byte vector for compactness. + * The individual set therefore has both a pointer to the byte vector + * and a mask to pick out the relevant bit of each byte. A hash code + * simplifies testing whether two sets could be identical. + * + * This will get trickier for multicharacter collating elements. As + * preliminary hooks for dealing with such things, we also carry along + * a string of multi-character elements, and decide the size of the + * vectors at run time. + */ +typedef struct { + uch *ptr; /* -> uch [csetsize] */ + uch mask; /* bit within array */ + uch hash; /* hash code */ + size_t smultis; + char *multis; /* -> char[smulti] ab\0cd\0ef\0\0 */ +} cset; +/* note that CHadd and CHsub are unsafe, and CHIN doesn't yield 0/1 */ +#define CHadd(cs, c) ((cs)->ptr[(uch)(c)] |= (cs)->mask, (cs)->hash += (c)) +#define CHsub(cs, c) ((cs)->ptr[(uch)(c)] &= ~(cs)->mask, (cs)->hash -= (c)) +#define CHIN(cs, c) ((cs)->ptr[(uch)(c)] & (cs)->mask) +#define MCadd(p, cs, cp) mcadd(p, cs, cp) /* regcomp() internal fns */ +#define MCsub(p, cs, cp) mcsub(p, cs, cp) +#define MCin(p, cs, cp) mcin(p, cs, cp) + +/* stuff for character categories */ +typedef unsigned char cat_t; + +/* + * main compiled-expression structure + */ +struct re_guts { + int magic; +# define MAGIC2 ((('R'^0200)<<8)|'E') + sop *strip; /* malloced area for strip */ + int csetsize; /* number of bits in a cset vector */ + int ncsets; /* number of csets in use */ + cset *sets; /* -> cset [ncsets] */ + uch *setbits; /* -> uch[csetsize][ncsets/CHAR_BIT] */ + int cflags; /* copy of regcomp() cflags argument */ + sopno nstates; /* = number of sops */ + sopno firststate; /* the initial OEND (normally 0) */ + sopno laststate; /* the final OEND */ + int iflags; /* internal flags */ +# define USEBOL 01 /* used ^ */ +# define USEEOL 02 /* used $ */ +# define BAD 04 /* something wrong */ + int nbol; /* number of ^ used */ + int neol; /* number of $ used */ + int ncategories; /* how many character categories */ + cat_t *categories; /* ->catspace[-CHAR_MIN] */ + char *must; /* match must contain this string */ + int mlen; /* length of must */ + size_t nsub; /* copy of re_nsub */ + int backrefs; /* does it use back references? */ + sopno nplus; /* how deep does it nest +s? */ + /* catspace must be last */ + cat_t catspace[1]; /* actually [NC] */ +}; + +/* misc utilities */ +#define OUT (CHAR_MAX+1) /* a non-character value */ +#define ISWORD(c) (isalnum(c) || (c) == '_') diff --git a/libc/regex/regexec.c b/libc/regex/regexec.c new file mode 100644 index 0000000..7b3bfc7 --- /dev/null +++ b/libc/regex/regexec.c @@ -0,0 +1,160 @@ +/* $OpenBSD: regexec.c,v 1.11 2005/08/05 13:03:00 espie Exp $ */ +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regexec.c 8.3 (Berkeley) 3/20/94 + */ + +/* + * the outer shell of regexec() + * + * This file includes engine.c *twice*, after muchos fiddling with the + * macros that code uses. This lets the same code operate on two different + * representations for state sets. + */ +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <ctype.h> +#include <regex.h> + +#include "utils.h" +#include "regex2.h" + +/* macros for manipulating states, small version */ +#define states long +#define states1 states /* for later use in regexec() decision */ +#define CLEAR(v) ((v) = 0) +#define SET0(v, n) ((v) &= ~((unsigned long)1 << (n))) +#define SET1(v, n) ((v) |= (unsigned long)1 << (n)) +#define ISSET(v, n) (((v) & ((unsigned long)1 << (n))) != 0) +#define ASSIGN(d, s) ((d) = (s)) +#define EQ(a, b) ((a) == (b)) +#define STATEVARS long dummy /* dummy version */ +#define STATESETUP(m, n) /* nothing */ +#define STATETEARDOWN(m) /* nothing */ +#define SETUP(v) ((v) = 0) +#define onestate long +#define INIT(o, n) ((o) = (unsigned long)1 << (n)) +#define INC(o) ((o) <<= 1) +#define ISSTATEIN(v, o) (((v) & (o)) != 0) +/* some abbreviations; note that some of these know variable names! */ +/* do "if I'm here, I can also be there" etc without branches */ +#define FWD(dst, src, n) ((dst) |= ((unsigned long)(src)&(here)) << (n)) +#define BACK(dst, src, n) ((dst) |= ((unsigned long)(src)&(here)) >> (n)) +#define ISSETBACK(v, n) (((v) & ((unsigned long)here >> (n))) != 0) +/* function names */ +#define SNAMES /* engine.c looks after details */ + +#include "engine.c" + +/* now undo things */ +#undef states +#undef CLEAR +#undef SET0 +#undef SET1 +#undef ISSET +#undef ASSIGN +#undef EQ +#undef STATEVARS +#undef STATESETUP +#undef STATETEARDOWN +#undef SETUP +#undef onestate +#undef INIT +#undef INC +#undef ISSTATEIN +#undef FWD +#undef BACK +#undef ISSETBACK +#undef SNAMES + +/* macros for manipulating states, large version */ +#define states char * +#define CLEAR(v) memset(v, 0, m->g->nstates) +#define SET0(v, n) ((v)[n] = 0) +#define SET1(v, n) ((v)[n] = 1) +#define ISSET(v, n) ((v)[n]) +#define ASSIGN(d, s) memcpy(d, s, m->g->nstates) +#define EQ(a, b) (memcmp(a, b, m->g->nstates) == 0) +#define STATEVARS long vn; char *space +#define STATESETUP(m, nv) { (m)->space = malloc((nv)*(m)->g->nstates); \ + if ((m)->space == NULL) return(REG_ESPACE); \ + (m)->vn = 0; } +#define STATETEARDOWN(m) { free((m)->space); } +#define SETUP(v) ((v) = &m->space[m->vn++ * m->g->nstates]) +#define onestate long +#define INIT(o, n) ((o) = (n)) +#define INC(o) ((o)++) +#define ISSTATEIN(v, o) ((v)[o]) +/* some abbreviations; note that some of these know variable names! */ +/* do "if I'm here, I can also be there" etc without branches */ +#define FWD(dst, src, n) ((dst)[here+(n)] |= (src)[here]) +#define BACK(dst, src, n) ((dst)[here-(n)] |= (src)[here]) +#define ISSETBACK(v, n) ((v)[here - (n)]) +/* function names */ +#define LNAMES /* flag */ + +#include "engine.c" + +/* + - regexec - interface for matching + * + * We put this here so we can exploit knowledge of the state representation + * when choosing which matcher to call. Also, by this point the matchers + * have been prototyped. + */ +int /* 0 success, REG_NOMATCH failure */ +regexec(const regex_t *preg, const char *string, size_t nmatch, + regmatch_t pmatch[], int eflags) +{ + struct re_guts *g = preg->re_g; +#ifdef REDEBUG +# define GOODFLAGS(f) (f) +#else +# define GOODFLAGS(f) ((f)&(REG_NOTBOL|REG_NOTEOL|REG_STARTEND)) +#endif + + if (preg->re_magic != MAGIC1 || g->magic != MAGIC2) + return(REG_BADPAT); + assert(!(g->iflags&BAD)); + if (g->iflags&BAD) /* backstop for no-debug case */ + return(REG_BADPAT); + eflags = GOODFLAGS(eflags); + + if (g->nstates <= CHAR_BIT*sizeof(states1) && !(eflags®_LARGE)) + return(smatcher(g, (char *)string, nmatch, pmatch, eflags)); + else + return(lmatcher(g, (char *)string, nmatch, pmatch, eflags)); +} diff --git a/libc/regex/regfree.c b/libc/regex/regfree.c new file mode 100644 index 0000000..a57eba3 --- /dev/null +++ b/libc/regex/regfree.c @@ -0,0 +1,71 @@ +/* $OpenBSD: regfree.c,v 1.7 2005/08/05 13:03:00 espie Exp $ */ +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regfree.c 8.3 (Berkeley) 3/20/94 + */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <regex.h> + +#include "utils.h" +#include "regex2.h" + +/* + - regfree - free everything + */ +void +regfree(regex_t *preg) +{ + struct re_guts *g; + + if (preg->re_magic != MAGIC1) /* oops */ + return; /* nice to complain, but hard */ + + g = preg->re_g; + if (g == NULL || g->magic != MAGIC2) /* oops again */ + return; + preg->re_magic = 0; /* mark it invalid */ + g->magic = 0; /* mark it invalid */ + + if (g->strip != NULL) + free((char *)g->strip); + if (g->sets != NULL) + free((char *)g->sets); + if (g->setbits != NULL) + free((char *)g->setbits); + if (g->must != NULL) + free(g->must); + free((char *)g); +} diff --git a/libc/regex/utils.h b/libc/regex/utils.h new file mode 100644 index 0000000..3e184fc --- /dev/null +++ b/libc/regex/utils.h @@ -0,0 +1,55 @@ +/* $OpenBSD: utils.h,v 1.4 2003/06/02 20:18:36 millert Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)utils.h 8.3 (Berkeley) 3/20/94 + */ + +/* utility definitions */ +#define DUPMAX 255 +#define INFINITY (DUPMAX + 1) +#define NC (CHAR_MAX - CHAR_MIN + 1) +typedef unsigned char uch; + +/* switch off assertions (if not already off) if no REDEBUG */ +#ifndef REDEBUG +#ifndef NDEBUG +#define NDEBUG /* no assertions please */ +#endif +#endif +#include <assert.h> + +/* for old systems with bcopy() but no memmove() */ +#ifdef USEBCOPY +#define memmove(d, s, c) bcopy(s, d, c) +#endif diff --git a/libc/stdlib/strtod.c b/libc/stdlib/strtod.c index d2582b1..9d599b0 100644 --- a/libc/stdlib/strtod.c +++ b/libc/stdlib/strtod.c @@ -378,6 +378,24 @@ Bigint { #endif #endif +/* Special value used to indicate an invalid Bigint value, + * e.g. when a memory allocation fails. The idea is that we + * want to avoid introducing NULL checks everytime a bigint + * computation is performed. Also the NULL value can also be + * already used to indicate "value not initialized yet" and + * returning NULL might alter the execution code path in + * case of OOM. + */ +#define BIGINT_INVALID ((Bigint *)&bigint_invalid_value) + +static const Bigint bigint_invalid_value; + + +/* Return BIGINT_INVALID on allocation failure. + * + * Most of the code here depends on the fact that this function + * never returns NULL. + */ static Bigint * Balloc #ifdef KR_headers @@ -397,11 +415,15 @@ Balloc else { x = 1 << k; rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(Long)); + if (rv == NULL) { + rv = BIGINT_INVALID; + goto EXIT; + } rv->k = k; rv->maxwds = x; } rv->sign = rv->wds = 0; - +EXIT: mutex_unlock(&freelist_mutex); return rv; @@ -415,7 +437,7 @@ Bfree (Bigint *v) #endif { - if (v) { + if (v && v != BIGINT_INVALID) { mutex_lock(&freelist_mutex); v->next = freelist[v->k]; @@ -425,8 +447,23 @@ Bfree } } -#define Bcopy(x,y) memcpy(&x->sign, &y->sign, \ - y->wds*sizeof(Long) + 2*sizeof(int)) +#define Bcopy_valid(x,y) memcpy(&(x)->sign, &(y)->sign, \ + (y)->wds*sizeof(Long) + 2*sizeof(int)) + +#define Bcopy(x,y) Bcopy_ptr(&(x),(y)) + + static void +Bcopy_ptr(Bigint **px, Bigint *y) +{ + if (*px == BIGINT_INVALID) + return; /* no space to store copy */ + if (y == BIGINT_INVALID) { + Bfree(*px); /* invalid input */ + *px = BIGINT_INVALID; + } else { + Bcopy_valid(*px,y); + } +} static Bigint * multadd @@ -443,6 +480,9 @@ multadd #endif Bigint *b1; + if (b == BIGINT_INVALID) + return b; + wds = b->wds; x = b->x; i = 0; @@ -463,7 +503,11 @@ multadd if (a) { if (wds >= b->maxwds) { b1 = Balloc(b->k+1); - Bcopy(b1, b); + if (b1 == BIGINT_INVALID) { + Bfree(b); + return b1; + } + Bcopy_valid(b1, b); Bfree(b); b = b1; } @@ -489,10 +533,15 @@ s2b for(k = 0, y = 1; x > y; y <<= 1, k++) ; #ifdef Pack_32 b = Balloc(k); + if (b == BIGINT_INVALID) + return b; b->x[0] = y9; b->wds = 1; #else b = Balloc(k+1); + if (b == BIGINT_INVALID) + return b; + b->x[0] = y9 & 0xffff; b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; #endif @@ -604,8 +653,10 @@ i2b Bigint *b; b = Balloc(1); - b->x[0] = i; - b->wds = 1; + if (b != BIGINT_INVALID) { + b->x[0] = i; + b->wds = 1; + } return b; } @@ -625,6 +676,9 @@ mult ULong z2; #endif + if (a == BIGINT_INVALID || b == BIGINT_INVALID) + return BIGINT_INVALID; + if (a->wds < b->wds) { c = a; a = b; @@ -637,6 +691,8 @@ mult if (wc > a->maxwds) k++; c = Balloc(k); + if (c == BIGINT_INVALID) + return c; for(x = c->x, xa = x + wc; x < xa; x++) *x = 0; xa = a->x; @@ -711,6 +767,9 @@ pow5mult int i; static const int p05[3] = { 5, 25, 125 }; + if (b == BIGINT_INVALID) + return b; + if ((i = k & 3) != 0) b = multadd(b, p05[i-1], 0); @@ -718,7 +777,12 @@ pow5mult return b; if (!(p5 = p5s)) { /* first time */ - p5 = p5s = i2b(625); + p5 = i2b(625); + if (p5 == BIGINT_INVALID) { + Bfree(b); + return p5; + } + p5s = p5; p5->next = 0; } for(;;) { @@ -730,7 +794,12 @@ pow5mult if (!(k = (unsigned int) k >> 1)) break; if (!(p51 = p5->next)) { - p51 = p5->next = mult(p5,p5); + p51 = mult(p5,p5); + if (p51 == BIGINT_INVALID) { + Bfree(b); + return p51; + } + p5->next = p51; p51->next = 0; } p5 = p51; @@ -750,6 +819,9 @@ lshift Bigint *b1; ULong *x, *x1, *xe, z; + if (b == BIGINT_INVALID) + return b; + #ifdef Pack_32 n = (unsigned int)k >> 5; #else @@ -760,6 +832,10 @@ lshift for(i = b->maxwds; n1 > i; i <<= 1) k1++; b1 = Balloc(k1); + if (b1 == BIGINT_INVALID) { + Bfree(b); + return b1; + } x1 = b1->x; for(i = 0; i < n; i++) *x1++ = 0; @@ -809,6 +885,13 @@ cmp ULong *xa, *xa0, *xb, *xb0; int i, j; + if (a == BIGINT_INVALID || b == BIGINT_INVALID) +#ifdef DEBUG + Bug("cmp called with a or b invalid"); +#else + return 0; /* equal - the best we can do right now */ +#endif + i = a->wds; j = b->wds; #ifdef DEBUG @@ -848,11 +931,16 @@ diff Long z; #endif + if (a == BIGINT_INVALID || b == BIGINT_INVALID) + return BIGINT_INVALID; + i = cmp(a,b); if (!i) { c = Balloc(0); - c->wds = 1; - c->x[0] = 0; + if (c != BIGINT_INVALID) { + c->wds = 1; + c->x[0] = 0; + } return c; } if (i < 0) { @@ -864,6 +952,8 @@ diff else i = 0; c = Balloc(a->k); + if (c == BIGINT_INVALID) + return c; c->sign = i; wa = a->wds; xa = a->x; @@ -972,6 +1062,9 @@ b2d #define d1 word1(d) #endif + if (a == BIGINT_INVALID) + return NAN; + xa0 = a->x; xa = xa0 + a->wds; y = *--xa; @@ -1054,6 +1147,8 @@ d2b #else b = Balloc(2); #endif + if (b == BIGINT_INVALID) + return b; x = b->x; z = d0 & Frac_mask; @@ -1169,6 +1264,9 @@ ratio _double da, db; int k, ka, kb; + if (a == BIGINT_INVALID || b == BIGINT_INVALID) + return NAN; /* for lack of better value ? */ + value(da) = b2d(a, &ka); value(db) = b2d(b, &kb); #ifdef Pack_32 @@ -1820,6 +1918,9 @@ quorem ULong si, zs; #endif + if (b == BIGINT_INVALID || S == BIGINT_INVALID) + return 0; + n = S->wds; #ifdef DEBUG /*debug*/ if (b->wds > n) @@ -2046,6 +2147,8 @@ __dtoa #endif "NaN"; result = Balloc(strlen(s)+1); + if (result == BIGINT_INVALID) + return NULL; s0 = (char *)(void *)result; strcpy(s0, s); if (rve) @@ -2063,6 +2166,8 @@ __dtoa if (!value(d)) { *decpt = 1; result = Balloc(2); + if (result == BIGINT_INVALID) + return NULL; s0 = (char *)(void *)result; strcpy(s0, "0"); if (rve) @@ -2198,6 +2303,10 @@ __dtoa // complicated way the block size need to be computed // buuurk.... result = Balloc(result_k); + if (result == BIGINT_INVALID) { + Bfree(b); + return NULL; + } s = s0 = (char *)(void *)result; if (ilim >= 0 && ilim <= Quick_max && try_quick) { @@ -2425,13 +2534,18 @@ __dtoa * and for all and pass them and a shift to quorem, so it * can do shifts and ors to compute the numerator for q. */ + if (S == BIGINT_INVALID) { + i = 0; + } else { #ifdef Pack_32 - if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) != 0) - i = 32 - i; + if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) != 0) + i = 32 - i; #else - if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf) - i = 16 - i; + if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf) + i = 16 - i; #endif + } + if (i > 4) { i -= 4; b2 += i; diff --git a/libc/stdlib/wchar.c b/libc/stdlib/wchar.c index 0bbdaa9..02947d4 100644 --- a/libc/stdlib/wchar.c +++ b/libc/stdlib/wchar.c @@ -227,6 +227,11 @@ size_t mbsrtowcs(wchar_t *dst, const char **src, size_t len, mbstate_t *ps) return len; } +size_t mbstowcs(wchar_t *dst, const char *src, size_t len) +{ + return mbsrtowcs(dst, &src, len, NULL); +} + wint_t putwc(wchar_t wc, FILE *stream) { return fputc((char)wc, stream); @@ -339,6 +344,11 @@ size_t wcsrtombs(char *dst, const wchar_t **src, size_t len, mbstate_t *ps) return len; } +size_t wcstombs(char *dst, const wchar_t *src, size_t len) +{ + return wcsrtombs(dst, &src, len, NULL); +} + size_t wcsspn(const wchar_t *ws1, const wchar_t *ws2) { return strspn( (const char*)ws1, (const char*)ws2 ); diff --git a/libc/unistd/gethostname.c b/libc/unistd/gethostname.c index 369d21e..5d3d7d9 100644 --- a/libc/unistd/gethostname.c +++ b/libc/unistd/gethostname.c @@ -41,11 +41,11 @@ int gethostname(char* buff, size_t buflen) int namelen = strlen(name.nodename); if ((int)buflen < namelen+1) { - errno = EINVAL; + errno = EINVAL; result = -1; } else { memcpy( buff, name.nodename, namelen+1 ); - } + } } return result; } diff --git a/libc/unistd/killpg.c b/libc/unistd/killpg.c new file mode 100644 index 0000000..75b1ad9 --- /dev/null +++ b/libc/unistd/killpg.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <signal.h> +#include <errno.h> + +/* + * Backwards-compatible killpg(). + */ +int +killpg(pid_t pgid, int sig) +{ + if (pgid == 1) { + errno = ESRCH; + return (-1); + } + return (kill(-pgid, sig)); +} diff --git a/libc/unistd/ptsname_r.c b/libc/unistd/ptsname_r.c index 1ed067b..2fa4c3d 100644 --- a/libc/unistd/ptsname_r.c +++ b/libc/unistd/ptsname_r.c @@ -32,7 +32,7 @@ #include <errno.h> #include <string.h> -char* ptsname_r( int fd, char* buf, size_t buflen) +int ptsname_r( int fd, char* buf, size_t buflen) { unsigned int pty_num; char buff[64]; @@ -40,17 +40,19 @@ char* ptsname_r( int fd, char* buf, size_t buflen) if (buf == NULL) { errno = EINVAL; - return NULL; + return -1; } - if ( ioctl( fd, TIOCGPTN, &pty_num ) != 0 ) - return NULL; + if ( ioctl( fd, TIOCGPTN, &pty_num ) != 0 ) { + errno = ENOTTY; + return -1; + } len = snprintf( buff, sizeof(buff), "/dev/pts/%u", pty_num ); if (len+1 > (int)buflen) { errno = ERANGE; - return NULL; + return -1; } memcpy( buf, buff, len+1 ); - return buf; + return 0; } diff --git a/libc/unistd/seteuid.c b/libc/unistd/seteuid.c index 42ee780..dd94932 100644 --- a/libc/unistd/seteuid.c +++ b/libc/unistd/seteuid.c @@ -29,5 +29,6 @@ int seteuid(uid_t euid) { - return setresuid(-1, euid,-1); + cpuacct_add(euid); + return __setresuid(-1, euid,-1); } diff --git a/libc/unistd/setresuid.c b/libc/unistd/setresuid.c new file mode 100644 index 0000000..1964881 --- /dev/null +++ b/libc/unistd/setresuid.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <unistd.h> + +int setresuid(uid_t ruid, uid_t euid, uid_t suid) +{ + cpuacct_add(euid); + return __setresuid(ruid, euid, suid); + +} diff --git a/libc/unistd/setreuid.c b/libc/unistd/setreuid.c new file mode 100644 index 0000000..04c2826 --- /dev/null +++ b/libc/unistd/setreuid.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <unistd.h> + +int setreuid(uid_t ruid, uid_t euid) +{ + cpuacct_add(euid); + return __setreuid(ruid, euid); +} diff --git a/libc/unistd/setuid.c b/libc/unistd/setuid.c new file mode 100644 index 0000000..8ab637d --- /dev/null +++ b/libc/unistd/setuid.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <unistd.h> + +int setuid(uid_t uid) +{ + cpuacct_add(uid); + return __setuid(uid); +} diff --git a/libc/unistd/usleep.c b/libc/unistd/usleep.c index 75458b1..19e8ee8 100644 --- a/libc/unistd/usleep.c +++ b/libc/unistd/usleep.c @@ -28,7 +28,7 @@ #include <time.h> #include <errno.h> -void usleep(unsigned long usec) +int usleep(unsigned long usec) { struct timespec ts; @@ -43,10 +43,13 @@ void usleep(unsigned long usec) for (;;) { - if ( nanosleep( &ts, &ts ) >= 0 ) - break; + if ( nanosleep( &ts, &ts ) == 0 ) + return 0; + // We try again if the nanosleep failure is EINTR. + // The other possible failures are EINVAL (which we should pass through), + // and ENOSYS, which doesn't happen. if ( errno != EINTR ) - break; + return -1; } } |