diff --git a/libc/Android.mk b/libc/Android.mk index f950c7e1b..330802ae5 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -66,6 +66,7 @@ libc_common_src_files := \ unistd/usleep.c \ unistd/wait.c \ stdio/asprintf.c \ + stdio/clrerr.c \ stdio/fclose.c \ stdio/fdopen.c \ stdio/feof.c \ @@ -179,6 +180,7 @@ libc_common_src_files := \ string/strcat.c \ string/strchr.c \ string/strcmp.c \ + string/strcoll.c \ string/strcpy.c \ string/strcspn.c \ string/strdup.c \ @@ -198,6 +200,7 @@ libc_common_src_files := \ string/strstr.c \ string/strtok.c \ string/strtotimeval.c \ + string/strxfrm.c \ inet/bindresvport.c \ inet/inet_addr.c \ inet/inet_aton.c \ @@ -234,6 +237,7 @@ libc_common_src_files := \ bionic/ssp.c \ bionic/stubs.c \ bionic/system_properties.c \ + bionic/time64.c \ bionic/thread_atexit.c \ bionic/utime.c \ bionic/utmp.c \ diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT index 0445fc93b..1ef06d261 100644 --- a/libc/SYSCALLS.TXT +++ b/libc/SYSCALLS.TXT @@ -216,22 +216,6 @@ 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 -# system-V inter-process communication -# TODO: implement x86 stubs for these functions when needed (when ?) -# -int semctl(int semid, int semnum, int cmd, ...) 300,-1 -int semget(key_t key, int nsems, int semflg) 299,-1 -int semop(int semid, struct sembuf* sops, size_t nsops) 298,-1 -void* shmat(int shmid, const void* shmaddr, int shmflg) 305,-1 -int shmctl(int shmid, int cmd, struct shmid_ds* buf) 308,-1 -int shmdt(const void* shmaddr) 306,-1 -int shmget(key_t key, size_t size, int shmflg) 307,-1 -int msgctl(int msqid, int cmd, struct msqid_ds *buf) 304,-1 -int msgget(key_t key, int msgflg) 303,-1 -int msgrcv(int msqid, void* msgp, size_t msgsz, long int msgtyp, int msgflg) 302,-1 -int msgsnd(int msqid, const void* msgp, size_t msgsz, int msgflg) 301,-1 - - # 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/syscalls.mk b/libc/arch-arm/syscalls.mk index a140a59c5..706cb0c9b 100644 --- a/libc/arch-arm/syscalls.mk +++ b/libc/arch-arm/syscalls.mk @@ -150,17 +150,6 @@ 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/semctl.S -syscall_src += arch-arm/syscalls/semget.S -syscall_src += arch-arm/syscalls/semop.S -syscall_src += arch-arm/syscalls/shmat.S -syscall_src += arch-arm/syscalls/shmctl.S -syscall_src += arch-arm/syscalls/shmdt.S -syscall_src += arch-arm/syscalls/shmget.S -syscall_src += arch-arm/syscalls/msgctl.S -syscall_src += arch-arm/syscalls/msgget.S -syscall_src += arch-arm/syscalls/msgrcv.S -syscall_src += arch-arm/syscalls/msgsnd.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/msgctl.S b/libc/arch-arm/syscalls/msgctl.S deleted file mode 100644 index 5c0bbf596..000000000 --- a/libc/arch-arm/syscalls/msgctl.S +++ /dev/null @@ -1,19 +0,0 @@ -/* autogenerated by gensyscalls.py */ -#include - - .text - .type msgctl, #function - .globl msgctl - .align 4 - .fnstart - -msgctl: - .save {r4, r7} - stmfd sp!, {r4, r7} - ldr r7, =__NR_msgctl - swi #0 - ldmfd sp!, {r4, r7} - movs r0, r0 - bxpl lr - b __set_syscall_errno - .fnend diff --git a/libc/arch-arm/syscalls/msgget.S b/libc/arch-arm/syscalls/msgget.S deleted file mode 100644 index ac1644002..000000000 --- a/libc/arch-arm/syscalls/msgget.S +++ /dev/null @@ -1,19 +0,0 @@ -/* autogenerated by gensyscalls.py */ -#include - - .text - .type msgget, #function - .globl msgget - .align 4 - .fnstart - -msgget: - .save {r4, r7} - stmfd sp!, {r4, r7} - ldr r7, =__NR_msgget - swi #0 - ldmfd sp!, {r4, r7} - movs r0, r0 - bxpl lr - b __set_syscall_errno - .fnend diff --git a/libc/arch-arm/syscalls/msgrcv.S b/libc/arch-arm/syscalls/msgrcv.S deleted file mode 100644 index 8db4125c1..000000000 --- a/libc/arch-arm/syscalls/msgrcv.S +++ /dev/null @@ -1,21 +0,0 @@ -/* autogenerated by gensyscalls.py */ -#include - - .text - .type msgrcv, #function - .globl msgrcv - .align 4 - .fnstart - -msgrcv: - mov ip, sp - .save {r4, r5, r6, r7} - stmfd sp!, {r4, r5, r6, r7} - ldmfd ip, {r4, r5, r6} - ldr r7, =__NR_msgrcv - 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/msgsnd.S b/libc/arch-arm/syscalls/msgsnd.S deleted file mode 100644 index 02c92ec94..000000000 --- a/libc/arch-arm/syscalls/msgsnd.S +++ /dev/null @@ -1,19 +0,0 @@ -/* autogenerated by gensyscalls.py */ -#include - - .text - .type msgsnd, #function - .globl msgsnd - .align 4 - .fnstart - -msgsnd: - .save {r4, r7} - stmfd sp!, {r4, r7} - ldr r7, =__NR_msgsnd - swi #0 - ldmfd sp!, {r4, r7} - movs r0, r0 - bxpl lr - b __set_syscall_errno - .fnend diff --git a/libc/arch-arm/syscalls/semctl.S b/libc/arch-arm/syscalls/semctl.S deleted file mode 100644 index a50e4dc3e..000000000 --- a/libc/arch-arm/syscalls/semctl.S +++ /dev/null @@ -1,19 +0,0 @@ -/* autogenerated by gensyscalls.py */ -#include - - .text - .type semctl, #function - .globl semctl - .align 4 - .fnstart - -semctl: - .save {r4, r7} - stmfd sp!, {r4, r7} - ldr r7, =__NR_semctl - swi #0 - ldmfd sp!, {r4, r7} - movs r0, r0 - bxpl lr - b __set_syscall_errno - .fnend diff --git a/libc/arch-arm/syscalls/semget.S b/libc/arch-arm/syscalls/semget.S deleted file mode 100644 index aaabd379d..000000000 --- a/libc/arch-arm/syscalls/semget.S +++ /dev/null @@ -1,19 +0,0 @@ -/* autogenerated by gensyscalls.py */ -#include - - .text - .type semget, #function - .globl semget - .align 4 - .fnstart - -semget: - .save {r4, r7} - stmfd sp!, {r4, r7} - ldr r7, =__NR_semget - swi #0 - ldmfd sp!, {r4, r7} - movs r0, r0 - bxpl lr - b __set_syscall_errno - .fnend diff --git a/libc/arch-arm/syscalls/semop.S b/libc/arch-arm/syscalls/semop.S deleted file mode 100644 index 20ef68bba..000000000 --- a/libc/arch-arm/syscalls/semop.S +++ /dev/null @@ -1,19 +0,0 @@ -/* autogenerated by gensyscalls.py */ -#include - - .text - .type semop, #function - .globl semop - .align 4 - .fnstart - -semop: - .save {r4, r7} - stmfd sp!, {r4, r7} - ldr r7, =__NR_semop - swi #0 - ldmfd sp!, {r4, r7} - movs r0, r0 - bxpl lr - b __set_syscall_errno - .fnend diff --git a/libc/arch-arm/syscalls/shmat.S b/libc/arch-arm/syscalls/shmat.S deleted file mode 100644 index 8b63bf02e..000000000 --- a/libc/arch-arm/syscalls/shmat.S +++ /dev/null @@ -1,19 +0,0 @@ -/* autogenerated by gensyscalls.py */ -#include - - .text - .type shmat, #function - .globl shmat - .align 4 - .fnstart - -shmat: - .save {r4, r7} - stmfd sp!, {r4, r7} - ldr r7, =__NR_shmat - swi #0 - ldmfd sp!, {r4, r7} - movs r0, r0 - bxpl lr - b __set_syscall_errno - .fnend diff --git a/libc/arch-arm/syscalls/shmctl.S b/libc/arch-arm/syscalls/shmctl.S deleted file mode 100644 index a29caa654..000000000 --- a/libc/arch-arm/syscalls/shmctl.S +++ /dev/null @@ -1,19 +0,0 @@ -/* autogenerated by gensyscalls.py */ -#include - - .text - .type shmctl, #function - .globl shmctl - .align 4 - .fnstart - -shmctl: - .save {r4, r7} - stmfd sp!, {r4, r7} - ldr r7, =__NR_shmctl - swi #0 - ldmfd sp!, {r4, r7} - movs r0, r0 - bxpl lr - b __set_syscall_errno - .fnend diff --git a/libc/arch-arm/syscalls/shmdt.S b/libc/arch-arm/syscalls/shmdt.S deleted file mode 100644 index bee707e4f..000000000 --- a/libc/arch-arm/syscalls/shmdt.S +++ /dev/null @@ -1,19 +0,0 @@ -/* autogenerated by gensyscalls.py */ -#include - - .text - .type shmdt, #function - .globl shmdt - .align 4 - .fnstart - -shmdt: - .save {r4, r7} - stmfd sp!, {r4, r7} - ldr r7, =__NR_shmdt - swi #0 - ldmfd sp!, {r4, r7} - movs r0, r0 - bxpl lr - b __set_syscall_errno - .fnend diff --git a/libc/arch-arm/syscalls/shmget.S b/libc/arch-arm/syscalls/shmget.S deleted file mode 100644 index be307049e..000000000 --- a/libc/arch-arm/syscalls/shmget.S +++ /dev/null @@ -1,19 +0,0 @@ -/* autogenerated by gensyscalls.py */ -#include - - .text - .type shmget, #function - .globl shmget - .align 4 - .fnstart - -shmget: - .save {r4, r7} - stmfd sp!, {r4, r7} - ldr r7, =__NR_shmget - swi #0 - ldmfd sp!, {r4, r7} - movs r0, r0 - bxpl lr - b __set_syscall_errno - .fnend diff --git a/libc/bionic/eabi.c b/libc/bionic/eabi.c index c491f0504..f212d0556 100644 --- a/libc/bionic/eabi.c +++ b/libc/bionic/eabi.c @@ -30,7 +30,10 @@ void* __dso_handle = 0; -int __aeabi_atexit (void *object, void (*destructor) (void *), void *dso_handle) +/* Make this a weak symbol to avoid a multiple definition error when linking + * with libstdc++-v3. */ +int __attribute__((weak)) +__aeabi_atexit (void *object, void (*destructor) (void *), void *dso_handle) { //return __cxa_atexit(destructor, object, dso_handle); return 0; diff --git a/libc/bionic/logd_write.c b/libc/bionic/logd_write.c index 6de646d50..7c3608bf7 100644 --- a/libc/bionic/logd_write.c +++ b/libc/bionic/logd_write.c @@ -38,7 +38,7 @@ #include #include -#include +#include #include "logd.h" #include diff --git a/libc/bionic/malloc_leak.c b/libc/bionic/malloc_leak.c index 5ddc91329..a0aa2ae64 100644 --- a/libc/bionic/malloc_leak.c +++ b/libc/bionic/malloc_leak.c @@ -58,6 +58,8 @@ #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. */ @@ -608,8 +610,16 @@ void chk_free(void* mem) void* chk_calloc(size_t n_elements, size_t elem_size) { - size_t size = n_elements * elem_size; - void* ptr = chk_malloc(size); + size_t size; + void* ptr; + + /* 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; + } + size = n_elements * elem_size; + ptr = chk_malloc(size); if (ptr != NULL) { memset(ptr, 0, size); } @@ -763,8 +773,16 @@ void leak_free(void* mem) void* leak_calloc(size_t n_elements, size_t elem_size) { - size_t size = n_elements * elem_size; - void* ptr = leak_malloc(size); + size_t size; + void* ptr; + + /* 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; + } + size = n_elements * elem_size; + ptr = leak_malloc(size); if (ptr != NULL) { memset(ptr, 0, size); } diff --git a/libc/bionic/pthread-timers.c b/libc/bionic/pthread-timers.c index b8f748801..7b9c99e1c 100644 --- a/libc/bionic/pthread-timers.c +++ b/libc/bionic/pthread-timers.c @@ -469,7 +469,7 @@ timer_settime( timer_t id, } if ( __likely(!TIMER_ID_IS_WRAPPED(id)) ) { - return __timer_gettime( id, ospec ); + return __timer_settime( id, flags, spec, ospec ); } else { thr_timer_t* timer = thr_timer_from_id(id); struct timespec expires, now; @@ -485,16 +485,19 @@ timer_settime( timer_t id, timer_gettime_internal(timer, ospec ); } - /* compute next expiration time */ + /* compute next expiration time. note that if the + * new it_interval is 0, we should disarm the timer + */ expires = spec->it_value; - clock_gettime( timer->clock, &now ); - if (!(flags & TIMER_ABSTIME)) { - timespec_add(&expires, &now); - } else { - if (timespec_cmp(&expires, &now) < 0) - expires = now; + if (!timespec_is_zero(&expires)) { + clock_gettime( timer->clock, &now ); + if (!(flags & TIMER_ABSTIME)) { + timespec_add(&expires, &now); + } else { + if (timespec_cmp(&expires, &now) < 0) + expires = now; + } } - timer->expires = expires; timer->period = spec->it_interval; thr_timer_unlock( timer ); @@ -560,11 +563,11 @@ timer_thread_start( void* _arg ) if (timespec_cmp( &expires, &now ) > 0) { /* cool, there was no overrun, so compute the - * relative timeout as 'now - expires', then wait + * relative timeout as 'expires - now', then wait */ int ret; - struct timespec diff = now; - timespec_sub( &diff, &expires ); + struct timespec diff = expires; + timespec_sub( &diff, &now ); ret = __pthread_cond_timedwait_relative( &timer->cond, &timer->mutex, &diff); diff --git a/libc/bionic/pthread.c b/libc/bionic/pthread.c index 6114f40eb..ec3c459e0 100644 --- a/libc/bionic/pthread.c +++ b/libc/bionic/pthread.c @@ -488,6 +488,21 @@ int pthread_getattr_np(pthread_t thid, pthread_attr_t * attr) return 0; } +int pthread_attr_setscope(pthread_attr_t *attr, int scope) +{ + if (scope == PTHREAD_SCOPE_SYSTEM) + return 0; + if (scope == PTHREAD_SCOPE_PROCESS) + return ENOTSUP; + + return EINVAL; +} + +int pthread_attr_getscope(pthread_attr_t const *attr) +{ + return PTHREAD_SCOPE_SYSTEM; +} + /* CAVEAT: our implementation of pthread_cleanup_push/pop doesn't support C++ exceptions * and thread cancelation diff --git a/libc/bionic/stubs.c b/libc/bionic/stubs.c index 1f76bba15..365f21a2a 100644 --- a/libc/bionic/stubs.c +++ b/libc/bionic/stubs.c @@ -35,6 +35,7 @@ #include #include #include +#include /** Thread-specific state for the stubs functions **/ @@ -95,8 +96,9 @@ __stubs_state(void) return s; } -static struct passwd *android_iinfo_to_passwd( - struct passwd *pw, struct android_id_info *iinfo) +static struct passwd* +android_iinfo_to_passwd( struct passwd *pw, + struct android_id_info *iinfo ) { pw->pw_name = (char*)iinfo->name; pw->pw_uid = iinfo->aid; @@ -106,8 +108,9 @@ static struct passwd *android_iinfo_to_passwd( return pw; } -static struct group *android_iinfo_to_group( - struct group *gr, struct android_id_info *iinfo) +static struct group* +android_iinfo_to_group( struct group *gr, + struct android_id_info *iinfo ) { gr->gr_name = (char*) iinfo->name; gr->gr_gid = iinfo->aid; @@ -116,8 +119,8 @@ static struct group *android_iinfo_to_group( return gr; } -static struct passwd *android_id_to_passwd( - struct passwd *pw, unsigned id) +static struct passwd * +android_id_to_passwd( struct passwd *pw, unsigned id) { struct android_id_info *iinfo = android_ids; unsigned n; @@ -126,11 +129,11 @@ static struct passwd *android_id_to_passwd( return android_iinfo_to_passwd(pw, iinfo + n); } } - return 0; + return NULL; } -static struct passwd *android_name_to_passwd( - struct passwd *pw, const char *name) +static struct passwd* +android_name_to_passwd(struct passwd *pw, const char *name) { struct android_id_info *iinfo = android_ids; unsigned n; @@ -139,11 +142,11 @@ static struct passwd *android_name_to_passwd( return android_iinfo_to_passwd(pw, iinfo + n); } } - return 0; + return NULL; } -static struct group *android_id_to_group( - struct group *gr, unsigned id) +static struct group* +android_id_to_group( struct group *gr, unsigned id ) { struct android_id_info *iinfo = android_ids; unsigned n; @@ -152,11 +155,11 @@ static struct group *android_id_to_group( return android_iinfo_to_group(gr, iinfo + n); } } - return 0; + return NULL; } -static struct group *android_name_to_group( - struct group *gr, const char *name) +static struct group* +android_name_to_group( struct group *gr, const char *name ) { struct android_id_info *iinfo = android_ids; unsigned n; @@ -165,10 +168,92 @@ static struct group *android_name_to_group( return android_iinfo_to_group(gr, iinfo + n); } } + return NULL; +} + +/* translate a user/group name like app_1234 into the + * corresponding user/group id (AID_APP + 1234) + * returns 0 and sets errno to ENOENT in case of error + */ +static unsigned +app_id_from_name( const char* name ) +{ + unsigned long id; + char* end; + + if (memcmp(name, "app_", 4) != 0 || !isdigit(name[4])) + goto FAIL; + + id = strtoul(name+4, &end, 10); + if (id == 0 || *end != '\0') + goto FAIL; + + id += AID_APP; + + /* check for overflow and that the value can be + * stored in our 32-bit uid_t/gid_t */ + if (id < AID_APP || (unsigned)id != id) + goto FAIL; + + return (unsigned)id; + +FAIL: + errno = ENOENT; return 0; } -struct passwd* getpwuid(uid_t uid) +/* translate a uid into the corresponding app_ + * passwd structure (sets errno to ENOENT on failure) + */ +static struct passwd* +app_id_to_passwd(uid_t uid, stubs_state_t* state) +{ + struct passwd* pw = &state->passwd; + + if (uid < AID_APP) { + errno = ENOENT; + return NULL; + } + + snprintf( state->app_name_buffer, sizeof state->app_name_buffer, + "app_%u", uid - AID_APP ); + + pw->pw_name = state->app_name_buffer; + pw->pw_dir = "/data"; + pw->pw_shell = "/system/bin/sh"; + pw->pw_uid = uid; + pw->pw_gid = uid; + + return pw; +} + +/* translate a gid into the corresponding app_ + * group structure (sets errno to ENOENT on failure) + */ +static struct group* +app_id_to_group(gid_t gid, stubs_state_t* state) +{ + struct group* gr = &state->group; + + if (gid < AID_APP) { + errno = ENOENT; + return NULL; + } + + snprintf(state->group_name_buffer, sizeof state->group_name_buffer, + "app_%u", gid - AID_APP); + + gr->gr_name = state->group_name_buffer; + gr->gr_gid = gid; + gr->gr_mem[0] = gr->gr_name; + gr->gr_mem[1] = NULL; + + return gr; +} + + +struct passwd* +getpwuid(uid_t uid) { stubs_state_t* state = __stubs_state(); struct passwd* pw; @@ -181,35 +266,27 @@ struct passwd* getpwuid(uid_t uid) if ( android_id_to_passwd(pw, uid) != NULL ) return pw; - if (uid < AID_APP) { - errno = ENOENT; - return NULL; - } - - snprintf( state->app_name_buffer, sizeof state->app_name_buffer, - "app_%d", uid - AID_APP ); - - pw->pw_name = state->app_name_buffer; - pw->pw_dir = "/data"; - pw->pw_shell = "/system/bin/sh"; - pw->pw_uid = uid; - pw->pw_gid = uid; - - return pw; + return app_id_to_passwd(uid, state); } -struct passwd* getpwnam(const char *login) +struct passwd* +getpwnam(const char *login) { stubs_state_t* state = __stubs_state(); if (state == NULL) return NULL; - return android_name_to_passwd(&state->passwd, login); + if (android_name_to_passwd(&state->passwd, login) != NULL) + return &state->passwd; + + return app_id_to_passwd( app_id_from_name(login), state ); } -int getgrouplist (const char *user, gid_t group, - gid_t *groups, int *ngroups) { +int +getgrouplist (const char *user, gid_t group, + gid_t *groups, int *ngroups) +{ if (*ngroups < 1) { *ngroups = 1; return -1; @@ -218,18 +295,20 @@ int getgrouplist (const char *user, gid_t group, return (*ngroups = 1); } -char* getlogin(void) +char* +getlogin(void) { struct passwd *pw = getpwuid(getuid()); if(pw) { return pw->pw_name; } else { - return 0; + return NULL; } } -struct group* getgrgid(gid_t gid) +struct group* +getgrgid(gid_t gid) { stubs_state_t* state = __stubs_state(); struct group* gr; @@ -241,34 +320,25 @@ struct group* getgrgid(gid_t gid) if (gr != NULL) return gr; - if (gid < AID_APP) { - errno = ENOENT; - return NULL; - } - - snprintf(state->group_name_buffer, sizeof state->group_name_buffer, - "app_%d", gid - AID_APP); - - gr = &state->group; - - gr->gr_name = state->group_name_buffer; - gr->gr_gid = gid; - gr->gr_mem[0] = gr->gr_name; - gr->gr_mem[1] = NULL; - - return gr; + return app_id_to_group(gid, state); } -struct group* getgrnam(const char *name) +struct group* +getgrnam(const char *name) { stubs_state_t* state = __stubs_state(); + unsigned id; if (state == NULL) return NULL; - return android_name_to_group(&state->group, name); + if (android_name_to_group(&state->group, name) != 0) + return &state->group; + + return app_id_to_group( app_id_from_name(name), state ); } + struct netent* getnetbyname(const char *name) { fprintf(stderr, "FIX ME! implement getgrnam() %s:%d\n", __FILE__, __LINE__); @@ -308,5 +378,3 @@ struct protoent *getprotobynumber(int proto) fprintf(stderr, "FIX ME! implement %s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__); return NULL; } - - diff --git a/libc/bionic/time64.c b/libc/bionic/time64.c new file mode 100644 index 000000000..1e1f88189 --- /dev/null +++ b/libc/bionic/time64.c @@ -0,0 +1,793 @@ +/* + +Copyright (c) 2007-2008 Michael G Schwern + +This software originally derived from Paul Sheer's pivotal_gmtime_r.c. + +The MIT License: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +/* See http://code.google.com/p/y2038 for this code's origin */ + +/* + +Programmers who have available to them 64-bit time values as a 'long +long' type can use localtime64_r() and gmtime64_r() which correctly +converts the time even on 32-bit systems. Whether you have 64-bit time +values will depend on the operating system. + +localtime64_r() is a 64-bit equivalent of localtime_r(). + +gmtime64_r() is a 64-bit equivalent of gmtime_r(). + +*/ + +#include +#include +#include +#include +#include +#include +#include "time64.h" + +/* BIONIC_BEGIN */ +/* the following are here to avoid exposing time64_config.h and + * other types in our public time64.h header + */ +#include "time64_config.h" + +/* Not everyone has gm/localtime_r(), provide a replacement */ +#ifdef HAS_LOCALTIME_R +# define LOCALTIME_R(clock, result) localtime_r(clock, result) +#else +# define LOCALTIME_R(clock, result) fake_localtime_r(clock, result) +#endif +#ifdef HAS_GMTIME_R +# define GMTIME_R(clock, result) gmtime_r(clock, result) +#else +# define GMTIME_R(clock, result) fake_gmtime_r(clock, result) +#endif + +typedef int64_t Int64; +typedef time64_t Time64_T; +typedef int64_t Year; +#define TM tm +/* BIONIC_END */ + +/* Spec says except for stftime() and the _r() functions, these + all return static memory. Stabbings! */ +static struct TM Static_Return_Date; +static char Static_Return_String[35]; + +static const int days_in_month[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, +}; + +static const int julian_days_by_month[2][12] = { + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}, +}; + +static char const wday_name[7][3] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +static char const mon_name[12][3] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +static const int length_of_year[2] = { 365, 366 }; + +/* Some numbers relating to the gregorian cycle */ +static const Year years_in_gregorian_cycle = 400; +#define days_in_gregorian_cycle ((365 * 400) + 100 - 4 + 1) +static const Time64_T seconds_in_gregorian_cycle = days_in_gregorian_cycle * 60LL * 60LL * 24LL; + +/* Year range we can trust the time funcitons with */ +#define MAX_SAFE_YEAR 2037 +#define MIN_SAFE_YEAR 1971 + +/* 28 year Julian calendar cycle */ +#define SOLAR_CYCLE_LENGTH 28 + +/* Year cycle from MAX_SAFE_YEAR down. */ +static const int safe_years_high[SOLAR_CYCLE_LENGTH] = { + 2016, 2017, 2018, 2019, + 2020, 2021, 2022, 2023, + 2024, 2025, 2026, 2027, + 2028, 2029, 2030, 2031, + 2032, 2033, 2034, 2035, + 2036, 2037, 2010, 2011, + 2012, 2013, 2014, 2015 +}; + +/* Year cycle from MIN_SAFE_YEAR up */ +static const int safe_years_low[SOLAR_CYCLE_LENGTH] = { + 1996, 1997, 1998, 1971, + 1972, 1973, 1974, 1975, + 1976, 1977, 1978, 1979, + 1980, 1981, 1982, 1983, + 1984, 1985, 1986, 1987, + 1988, 1989, 1990, 1991, + 1992, 1993, 1994, 1995, +}; + +/* This isn't used, but it's handy to look at */ +static const int dow_year_start[SOLAR_CYCLE_LENGTH] = { + 5, 0, 1, 2, /* 0 2016 - 2019 */ + 3, 5, 6, 0, /* 4 */ + 1, 3, 4, 5, /* 8 1996 - 1998, 1971*/ + 6, 1, 2, 3, /* 12 1972 - 1975 */ + 4, 6, 0, 1, /* 16 */ + 2, 4, 5, 6, /* 20 2036, 2037, 2010, 2011 */ + 0, 2, 3, 4 /* 24 2012, 2013, 2014, 2015 */ +}; + +/* Let's assume people are going to be looking for dates in the future. + Let's provide some cheats so you can skip ahead. + This has a 4x speed boost when near 2008. +*/ +/* Number of days since epoch on Jan 1st, 2008 GMT */ +#define CHEAT_DAYS (1199145600 / 24 / 60 / 60) +#define CHEAT_YEARS 108 + +#define IS_LEAP(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0) +#define WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a)) + +#ifdef USE_SYSTEM_LOCALTIME +# define SHOULD_USE_SYSTEM_LOCALTIME(a) ( \ + (a) <= SYSTEM_LOCALTIME_MAX && \ + (a) >= SYSTEM_LOCALTIME_MIN \ +) +#else +# define SHOULD_USE_SYSTEM_LOCALTIME(a) (0) +#endif + +#ifdef USE_SYSTEM_GMTIME +# define SHOULD_USE_SYSTEM_GMTIME(a) ( \ + (a) <= SYSTEM_GMTIME_MAX && \ + (a) >= SYSTEM_GMTIME_MIN \ +) +#else +# define SHOULD_USE_SYSTEM_GMTIME(a) (0) +#endif + +/* Multi varadic macros are a C99 thing, alas */ +#ifdef TIME_64_DEBUG +# define TRACE(format) (fprintf(stderr, format)) +# define TRACE1(format, var1) (fprintf(stderr, format, var1)) +# define TRACE2(format, var1, var2) (fprintf(stderr, format, var1, var2)) +# define TRACE3(format, var1, var2, var3) (fprintf(stderr, format, var1, var2, var3)) +#else +# define TRACE(format) ((void)0) +# define TRACE1(format, var1) ((void)0) +# define TRACE2(format, var1, var2) ((void)0) +# define TRACE3(format, var1, var2, var3) ((void)0) +#endif + + +static int is_exception_century(Year year) +{ + int is_exception = ((year % 100 == 0) && !(year % 400 == 0)); + TRACE1("# is_exception_century: %s\n", is_exception ? "yes" : "no"); + + return(is_exception); +} + + +/* timegm() is not in the C or POSIX spec, but it is such a useful + extension I would be remiss in leaving it out. Also I need it + for localtime64() +*/ +Time64_T timegm64(const struct TM *date) { + Time64_T days = 0; + Time64_T seconds = 0; + Year year; + Year orig_year = (Year)date->tm_year; + int cycles = 0; + + if( orig_year > 100 ) { + cycles = (orig_year - 100) / 400; + orig_year -= cycles * 400; + days += (Time64_T)cycles * days_in_gregorian_cycle; + } + else if( orig_year < -300 ) { + cycles = (orig_year - 100) / 400; + orig_year -= cycles * 400; + days += (Time64_T)cycles * days_in_gregorian_cycle; + } + TRACE3("# timegm/ cycles: %d, days: %lld, orig_year: %lld\n", cycles, days, orig_year); + + if( orig_year > 70 ) { + year = 70; + while( year < orig_year ) { + days += length_of_year[IS_LEAP(year)]; + year++; + } + } + else if ( orig_year < 70 ) { + year = 69; + do { + days -= length_of_year[IS_LEAP(year)]; + year--; + } while( year >= orig_year ); + } + + + days += julian_days_by_month[IS_LEAP(orig_year)][date->tm_mon]; + days += date->tm_mday - 1; + + seconds = days * 60 * 60 * 24; + + seconds += date->tm_hour * 60 * 60; + seconds += date->tm_min * 60; + seconds += date->tm_sec; + + return(seconds); +} + + +static int check_tm(struct TM *tm) +{ + /* Don't forget leap seconds */ + assert(tm->tm_sec >= 0); + assert(tm->tm_sec <= 61); + + assert(tm->tm_min >= 0); + assert(tm->tm_min <= 59); + + assert(tm->tm_hour >= 0); + assert(tm->tm_hour <= 23); + + assert(tm->tm_mday >= 1); + assert(tm->tm_mday <= days_in_month[IS_LEAP(tm->tm_year)][tm->tm_mon]); + + assert(tm->tm_mon >= 0); + assert(tm->tm_mon <= 11); + + assert(tm->tm_wday >= 0); + assert(tm->tm_wday <= 6); + + assert(tm->tm_yday >= 0); + assert(tm->tm_yday <= length_of_year[IS_LEAP(tm->tm_year)]); + +#ifdef HAS_TM_TM_GMTOFF + assert(tm->tm_gmtoff >= -24 * 60 * 60); + assert(tm->tm_gmtoff <= 24 * 60 * 60); +#endif + + return 1; +} + + +/* The exceptional centuries without leap years cause the cycle to + shift by 16 +*/ +static Year cycle_offset(Year year) +{ + const Year start_year = 2000; + Year year_diff = year - start_year; + Year exceptions; + + if( year > start_year ) + year_diff--; + + exceptions = year_diff / 100; + exceptions -= year_diff / 400; + + TRACE3("# year: %lld, exceptions: %lld, year_diff: %lld\n", + year, exceptions, year_diff); + + return exceptions * 16; +} + +/* For a given year after 2038, pick the latest possible matching + year in the 28 year calendar cycle. + + A matching year... + 1) Starts on the same day of the week. + 2) Has the same leap year status. + + This is so the calendars match up. + + Also the previous year must match. When doing Jan 1st you might + wind up on Dec 31st the previous year when doing a -UTC time zone. + + Finally, the next year must have the same start day of week. This + is for Dec 31st with a +UTC time zone. + It doesn't need the same leap year status since we only care about + January 1st. +*/ +static int safe_year(const Year year) +{ + int safe_year = 0; + Year year_cycle; + + if( year >= MIN_SAFE_YEAR && year <= MAX_SAFE_YEAR ) { + return (int)year; + } + + year_cycle = year + cycle_offset(year); + + /* safe_years_low is off from safe_years_high by 8 years */ + if( year < MIN_SAFE_YEAR ) + year_cycle -= 8; + + /* Change non-leap xx00 years to an equivalent */ + if( is_exception_century(year) ) + year_cycle += 11; + + /* Also xx01 years, since the previous year will be wrong */ + if( is_exception_century(year - 1) ) + year_cycle += 17; + + year_cycle %= SOLAR_CYCLE_LENGTH; + if( year_cycle < 0 ) + year_cycle = SOLAR_CYCLE_LENGTH + year_cycle; + + assert( year_cycle >= 0 ); + assert( year_cycle < SOLAR_CYCLE_LENGTH ); + if( year < MIN_SAFE_YEAR ) + safe_year = safe_years_low[year_cycle]; + else if( year > MAX_SAFE_YEAR ) + safe_year = safe_years_high[year_cycle]; + else + assert(0); + + TRACE3("# year: %lld, year_cycle: %lld, safe_year: %d\n", + year, year_cycle, safe_year); + + assert(safe_year <= MAX_SAFE_YEAR && safe_year >= MIN_SAFE_YEAR); + + return safe_year; +} + + +void copy_tm_to_TM(const struct tm *src, struct TM *dest) { + if( src == NULL ) { + memset(dest, 0, sizeof(*dest)); + } + else { +# ifdef USE_TM64 + dest->tm_sec = src->tm_sec; + dest->tm_min = src->tm_min; + dest->tm_hour = src->tm_hour; + dest->tm_mday = src->tm_mday; + dest->tm_mon = src->tm_mon; + dest->tm_year = (Year)src->tm_year; + dest->tm_wday = src->tm_wday; + dest->tm_yday = src->tm_yday; + dest->tm_isdst = src->tm_isdst; + +# ifdef HAS_TM_TM_GMTOFF + dest->tm_gmtoff = src->tm_gmtoff; +# endif + +# ifdef HAS_TM_TM_ZONE + dest->tm_zone = src->tm_zone; +# endif + +# else + /* They're the same type */ + memcpy(dest, src, sizeof(*dest)); +# endif + } +} + + +void copy_TM_to_tm(const struct TM *src, struct tm *dest) { + if( src == NULL ) { + memset(dest, 0, sizeof(*dest)); + } + else { +# ifdef USE_TM64 + dest->tm_sec = src->tm_sec; + dest->tm_min = src->tm_min; + dest->tm_hour = src->tm_hour; + dest->tm_mday = src->tm_mday; + dest->tm_mon = src->tm_mon; + dest->tm_year = (int)src->tm_year; + dest->tm_wday = src->tm_wday; + dest->tm_yday = src->tm_yday; + dest->tm_isdst = src->tm_isdst; + +# ifdef HAS_TM_TM_GMTOFF + dest->tm_gmtoff = src->tm_gmtoff; +# endif + +# ifdef HAS_TM_TM_ZONE + dest->tm_zone = src->tm_zone; +# endif + +# else + /* They're the same type */ + memcpy(dest, src, sizeof(*dest)); +# endif + } +} + + +/* Simulate localtime_r() to the best of our ability */ +struct tm * fake_localtime_r(const time_t *clock, struct tm *result) { + const struct tm *static_result = localtime(clock); + + assert(result != NULL); + + if( static_result == NULL ) { + memset(result, 0, sizeof(*result)); + return NULL; + } + else { + memcpy(result, static_result, sizeof(*result)); + return result; + } +} + + + +/* Simulate gmtime_r() to the best of our ability */ +struct tm * fake_gmtime_r(const time_t *clock, struct tm *result) { + const struct tm *static_result = gmtime(clock); + + assert(result != NULL); + + if( static_result == NULL ) { + memset(result, 0, sizeof(*result)); + return NULL; + } + else { + memcpy(result, static_result, sizeof(*result)); + return result; + } +} + + +static Time64_T seconds_between_years(Year left_year, Year right_year) { + int increment = (left_year > right_year) ? 1 : -1; + Time64_T seconds = 0; + int cycles; + + if( left_year > 2400 ) { + cycles = (left_year - 2400) / 400; + left_year -= cycles * 400; + seconds += cycles * seconds_in_gregorian_cycle; + } + else if( left_year < 1600 ) { + cycles = (left_year - 1600) / 400; + left_year += cycles * 400; + seconds += cycles * seconds_in_gregorian_cycle; + } + + while( left_year != right_year ) { + seconds += length_of_year[IS_LEAP(right_year - 1900)] * 60 * 60 * 24; + right_year += increment; + } + + return seconds * increment; +} + + +Time64_T mktime64(const struct TM *input_date) { + struct tm safe_date; + struct TM date; + Time64_T time; + Year year = input_date->tm_year + 1900; + + if( MIN_SAFE_YEAR <= year && year <= MAX_SAFE_YEAR ) { + copy_TM_to_tm(input_date, &safe_date); + return (Time64_T)mktime(&safe_date); + } + + /* Have to make the year safe in date else it won't fit in safe_date */ + date = *input_date; + date.tm_year = safe_year(year) - 1900; + copy_TM_to_tm(&date, &safe_date); + + time = (Time64_T)mktime(&safe_date); + + time += seconds_between_years(year, (Year)(safe_date.tm_year + 1900)); + + return time; +} + + +/* Because I think mktime() is a crappy name */ +Time64_T timelocal64(const struct TM *date) { + return mktime64(date); +} + + +struct TM *gmtime64_r (const Time64_T *in_time, struct TM *p) +{ + int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday; + Time64_T v_tm_tday; + int leap; + Time64_T m; + Time64_T time = *in_time; + Year year = 70; + int cycles = 0; + + assert(p != NULL); + + /* Use the system gmtime() if time_t is small enough */ + if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) { + time_t safe_time = *in_time; + struct tm safe_date; + GMTIME_R(&safe_time, &safe_date); + + copy_tm_to_TM(&safe_date, p); + assert(check_tm(p)); + + return p; + } + +#ifdef HAS_TM_TM_GMTOFF + p->tm_gmtoff = 0; +#endif + p->tm_isdst = 0; + +#ifdef HAS_TM_TM_ZONE + p->tm_zone = "UTC"; +#endif + + v_tm_sec = (int)(time % 60); + time /= 60; + v_tm_min = (int)(time % 60); + time /= 60; + v_tm_hour = (int)(time % 24); + time /= 24; + v_tm_tday = time; + + WRAP (v_tm_sec, v_tm_min, 60); + WRAP (v_tm_min, v_tm_hour, 60); + WRAP (v_tm_hour, v_tm_tday, 24); + + v_tm_wday = (int)((v_tm_tday + 4) % 7); + if (v_tm_wday < 0) + v_tm_wday += 7; + m = v_tm_tday; + + if (m >= CHEAT_DAYS) { + year = CHEAT_YEARS; + m -= CHEAT_DAYS; + } + + if (m >= 0) { + /* Gregorian cycles, this is huge optimization for distant times */ + cycles = (int)(m / (Time64_T) days_in_gregorian_cycle); + if( cycles ) { + m -= (cycles * (Time64_T) days_in_gregorian_cycle); + year += (cycles * years_in_gregorian_cycle); + } + + /* Years */ + leap = IS_LEAP (year); + while (m >= (Time64_T) length_of_year[leap]) { + m -= (Time64_T) length_of_year[leap]; + year++; + leap = IS_LEAP (year); + } + + /* Months */ + v_tm_mon = 0; + while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) { + m -= (Time64_T) days_in_month[leap][v_tm_mon]; + v_tm_mon++; + } + } else { + year--; + + /* Gregorian cycles */ + cycles = (int)((m / (Time64_T) days_in_gregorian_cycle) + 1); + if( cycles ) { + m -= (cycles * (Time64_T) days_in_gregorian_cycle); + year += (cycles * years_in_gregorian_cycle); + } + + /* Years */ + leap = IS_LEAP (year); + while (m < (Time64_T) -length_of_year[leap]) { + m += (Time64_T) length_of_year[leap]; + year--; + leap = IS_LEAP (year); + } + + /* Months */ + v_tm_mon = 11; + while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) { + m += (Time64_T) days_in_month[leap][v_tm_mon]; + v_tm_mon--; + } + m += (Time64_T) days_in_month[leap][v_tm_mon]; + } + + p->tm_year = year; + if( p->tm_year != year ) { +#ifdef EOVERFLOW + errno = EOVERFLOW; +#endif + return NULL; + } + + /* At this point m is less than a year so casting to an int is safe */ + p->tm_mday = (int) m + 1; + p->tm_yday = julian_days_by_month[leap][v_tm_mon] + (int)m; + p->tm_sec = v_tm_sec; + p->tm_min = v_tm_min; + p->tm_hour = v_tm_hour; + p->tm_mon = v_tm_mon; + p->tm_wday = v_tm_wday; + + assert(check_tm(p)); + + return p; +} + + +struct TM *localtime64_r (const Time64_T *time, struct TM *local_tm) +{ + time_t safe_time; + struct tm safe_date; + struct TM gm_tm; + Year orig_year; + int month_diff; + + assert(local_tm != NULL); + + /* Use the system localtime() if time_t is small enough */ + if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) { + safe_time = *time; + + TRACE1("Using system localtime for %lld\n", *time); + + LOCALTIME_R(&safe_time, &safe_date); + + copy_tm_to_TM(&safe_date, local_tm); + assert(check_tm(local_tm)); + + return local_tm; + } + + if( gmtime64_r(time, &gm_tm) == NULL ) { + TRACE1("gmtime64_r returned null for %lld\n", *time); + return NULL; + } + + orig_year = gm_tm.tm_year; + + if (gm_tm.tm_year > (2037 - 1900) || + gm_tm.tm_year < (1970 - 1900) + ) + { + TRACE1("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year); + gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year + 1900)) - 1900; + } + + safe_time = timegm64(&gm_tm); + if( LOCALTIME_R(&safe_time, &safe_date) == NULL ) { + TRACE1("localtime_r(%d) returned NULL\n", (int)safe_time); + return NULL; + } + + copy_tm_to_TM(&safe_date, local_tm); + + local_tm->tm_year = orig_year; + if( local_tm->tm_year != orig_year ) { + TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n", + (Year)local_tm->tm_year, (Year)orig_year); + +#ifdef EOVERFLOW + errno = EOVERFLOW; +#endif + return NULL; + } + + + month_diff = local_tm->tm_mon - gm_tm.tm_mon; + + /* When localtime is Dec 31st previous year and + gmtime is Jan 1st next year. + */ + if( month_diff == 11 ) { + local_tm->tm_year--; + } + + /* When localtime is Jan 1st, next year and + gmtime is Dec 31st, previous year. + */ + if( month_diff == -11 ) { + local_tm->tm_year++; + } + + /* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st + in a non-leap xx00. There is one point in the cycle + we can't account for which the safe xx00 year is a leap + year. So we need to correct for Dec 31st comming out as + the 366th day of the year. + */ + if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 ) + local_tm->tm_yday--; + + assert(check_tm(local_tm)); + + return local_tm; +} + + +int valid_tm_wday( const struct TM* date ) { + if( 0 <= date->tm_wday && date->tm_wday <= 6 ) + return 1; + else + return 0; +} + +int valid_tm_mon( const struct TM* date ) { + if( 0 <= date->tm_mon && date->tm_mon <= 11 ) + return 1; + else + return 0; +} + + +char *asctime64_r( const struct TM* date, char *result ) { + /* I figure everything else can be displayed, even hour 25, but if + these are out of range we walk off the name arrays */ + if( !valid_tm_wday(date) || !valid_tm_mon(date) ) + return NULL; + + sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", + wday_name[date->tm_wday], + mon_name[date->tm_mon], + date->tm_mday, date->tm_hour, + date->tm_min, date->tm_sec, + 1900 + date->tm_year); + + return result; +} + + +char *ctime64_r( const Time64_T* time, char* result ) { + struct TM date; + + localtime64_r( time, &date ); + return asctime64_r( &date, result ); +} + + +/* Non-thread safe versions of the above */ +struct TM *localtime64(const Time64_T *time) { + return localtime64_r(time, &Static_Return_Date); +} + +struct TM *gmtime64(const Time64_T *time) { + return gmtime64_r(time, &Static_Return_Date); +} + +char *asctime64( const struct TM* date ) { + return asctime64_r( date, Static_Return_String ); +} + +char *ctime64( const Time64_T* time ) { + return asctime64(localtime64(time)); +} diff --git a/libc/bionic/time64_config.h b/libc/bionic/time64_config.h new file mode 100644 index 000000000..53bcecf6b --- /dev/null +++ b/libc/bionic/time64_config.h @@ -0,0 +1,75 @@ +/* Debugging + TIME_64_DEBUG + Define if you want debugging messages +*/ +/* #define TIME_64_DEBUG */ + + +/* INT_64_T + A 64 bit integer type to use to store time and others. + Must be defined. +*/ +#define INT_64_T long long + + +/* USE_TM64 + Should we use a 64 bit safe replacement for tm? This will + let you go past year 2 billion but the struct will be incompatible + with tm. Conversion functions will be provided. +*/ +/* #define USE_TM64 */ + + +/* Availability of system functions. + + HAS_GMTIME_R + Define if your system has gmtime_r() + + HAS_LOCALTIME_R + Define if your system has localtime_r() + + HAS_TIMEGM + Define if your system has timegm(), a GNU extension. +*/ +#define HAS_GMTIME_R +#define HAS_LOCALTIME_R +/*#define HAS_TIMEGM */ + + +/* Details of non-standard tm struct elements. + + HAS_TM_TM_GMTOFF + True if your tm struct has a "tm_gmtoff" element. + A BSD extension. + + HAS_TM_TM_ZONE + True if your tm struct has a "tm_zone" element. + A BSD extension. +*/ +#define HAS_TM_TM_GMTOFF +#define HAS_TM_TM_ZONE + + +/* USE_SYSTEM_LOCALTIME + USE_SYSTEM_GMTIME + Should we use the system functions if the time is inside their range? + Your system localtime() is probably more accurate, but our gmtime() is + fast and safe. +*/ +#define USE_SYSTEM_LOCALTIME +/* #define USE_SYSTEM_GMTIME */ + + +/* SYSTEM_LOCALTIME_MAX + SYSTEM_LOCALTIME_MIN + SYSTEM_GMTIME_MAX + SYSTEM_GMTIME_MIN + Maximum and minimum values your system's gmtime() and localtime() + can handle. We will use your system functions if the time falls + inside these ranges. +*/ +#define SYSTEM_LOCALTIME_MAX 2147483647 +#define SYSTEM_LOCALTIME_MIN -2147483647 +#define SYSTEM_GMTIME_MAX 2147483647 +#define SYSTEM_GMTIME_MIN -2147483647 + diff --git a/libc/docs/OVERVIEW.TXT b/libc/docs/OVERVIEW.TXT new file mode 100644 index 000000000..4c153b152 --- /dev/null +++ b/libc/docs/OVERVIEW.TXT @@ -0,0 +1,388 @@ +Bionic C Library Overview: +========================== + +Introduction: + +Core Philosophy: + + The core idea behind Bionic's design is: KEEP IT REALLY SIMPLE. + + This implies that the C library should only provide lightweight wrappers + around kernel facilities and not try to be too smart to deal with edge cases. + + The name "Bionic" comes from the fact that it is part-BSD and part-Linux: + its source code consists in a mix of BSD C library pieces with custom + Linux-specific bits used to deal with threads, processes, signals and a few + others things. + + All original BSD pieces carry the BSD copyright disclaimer. Bionic-specific + bits carry the Android Open Source Project copyright disclaimer. And + everything is released under the BSD license. + +Architectures: + + Bionic currently supports the ARM and x86 instruction sets. In theory, it + should be possible to support more, but this may require a little work (e.g. + adding system call IDs to SYSCALLS.TXT, described below, or modifying the + dynamic linker). + + The ARM-specific code is under arch-arm/ and the x86-specific one is under + arch-x86/ + + Note that the x86 version is only meant to run on an x86 Android device. We + make absolutely no claim that you could build and use Bionic on a stock x86 + Linux distribution (though that would be cool, so patches are welcomed :-)) + +Syscall stubs: + + Each system call function is implemented by a tiny assembler source fragment + (called a "syscall stub"), which is generated automatically by + tools/gensyscalls.py which reads the SYSCALLS.TXT file for input. + + SYSCALLS.TXT contains the list of all syscall stubs to generate, along with + the corresponding syscall numeric identifier (which may differ between ARM + and x86), and its signature + + If you modify this file, you may want to use tools/checksyscalls.py which + checks its content against official Linux kernel header files, and will + report errors when invalid syscall ids are used. + + Sometimes, the C library function is really a wrapper that calls the + corresponding syscall with another name. For example, the exit() function + is provided by the C library and calls the _exit() syscall stub. + + See SYSCALLS.TXT for documentation and details. + + +time_t: + + time_t is 32-bit as defined by the kernel on 32-bit CPUs. A 64-bit version + would be preferrable to avoid the Y2038 bug, but the kernel maintainers + consider that this is not needed at the moment. + + Instead, Bionic provides a header that defines a time64_t type, + and related functions like mktime64(), localtime64(), etc... + + +Timezone management: + + The name of the current timezone is taken from the TZ environment variable, + if defined. Otherwise, the system property named 'persist.sys.timezone' is + checked instead. + + The zoneinfo timezone database and index files are located under directory + /system/usr/share/zoneinfo, instead of the more Posix-compliant path of + /usr/share/zoneinfo + + +off_t: + + For similar reasons, off_t is 32-bit. We define loff_t as the 64-bit variant + due to BSD inheritance, but off64_t should be available as a typedef to ease + porting of current Linux-specific code. + + +Linux kernel headers: + + Bionic comes with its own set of "clean" Linux kernel headers to allow + user-space code to use kernel-specific declarations (e.g. IOCTLs, structure + declarations, constants, etc...). They are located in: + + ./kernel/common, + ./kernel/arch-arm + ./kernel/arch-x86 + + These headers have been generated by a tool (kernel/tools/update-all.py) to + only include the public definitions from the original Linux kernel headers. + + If you want to know why and how this is done, read kernel/README.TXT to get + all the (gory) details. + + +PThread implementation: + + Bionic's C library comes with its own pthread implementation bundled in. + This is different from other historical C libraries which: + + - place it in an external library (-lpthread) + - play linker tricks with weak symbols at dynamic link time + + The support for real-time features (a.k.a. -lrt) is also bundled in the + C library. + + The implementation is based on futexes and strives to provide *very* short + code paths for common operations. Notable features are the following: + + - pthread_mutex_t, pthread_cond_t are only 4 bytes each. + + - Normal, recursive and error-check mutexes are supported, and the code + path is heavily optimized for the normal case, which is used most of + the time. + + - Process-shared mutexes and condition variables are not supported. + Their implementation requires far more complexity and was absolutely + not needed for Android (which uses other inter-process synchronization + capabilities). + + Note that they could be added in the future without breaking the ABI + by specifying more sophisticated code paths (which may make the common + paths slightly slower though). + + - There is currently no support for read/write locks, priority-ceiling in + mutexes and other more advanced features. Again, the main idea being + that this was not needed for Android at all but could be added in the + future. + +pthread_cancel(): + + pthread_cancel() will *not* be supported in Bionic, because doing this would + involve making the C library significantly bigger for very little benefit. + + Consider that: + + - A proper implementation must insert pthread cancellation checks in a lot + of different places of the C library. And conformance is very difficult + to test properly. + + - A proper implementation must also clean up resources, like releasing + memory, or unlocking mutexes, properly if the cancellation happens in a + complex function (e.g. inside gethostbyname() or fprintf() + complex + formatting rules). This tends to slow down the path of many functions. + + - pthread cancellation cannot stop all threads: e.g. it can't do anything + against an infinite loop + + - pthread cancellation itself has short-comings and isn't very portable + (see http://advogato.org/person/slamb/diary.html?start=49 for example). + + All of this is contrary to the Bionic design goals. If your code depends on + thread cancellation, please consider alternatives. + + Note however that Bionic does implement pthread_cleanup_push() and + pthread_cleanup_pop(), which can be used to handle cleanups that happen when + a thread voluntarily exits through pthread_exit() or returning from its + main function. + + +pthread_once(): + + Do not call fork() within a callback provided to pthread_once(). Doing this + may result in a deadlock in the child process the next time it calls + pthread_once(). + + Also, you can't throw a C++ Exception from the callback (see C++ Exception + Support below). + + The current implementation of pthread_once() lacks the necessary support of + multi-core-safe double-checked-locking (read and write barriers). + + +Thread-specific data + + The thread-specific storage only provides for a bit less than 64 + pthread_key_t objects to each process. The implementation provides 64 real + slots but also uses about 5 of them (exact number may depend on + implementation) for its own use (e.g. two slots are pre-allocated by the C + library to speed-up the Android OpenGL sub-system). + + Note that Posix mandates a minimum of 128 slots, but we do not claim to be + Posix-compliant. + + Except for the main thread, the TLS area is stored at the top of the stack. + See comments in bionic/libc/bionic/pthread.c for details. + + At the moment, thread-local storage defined through the __thread compiler + keyword is not supported by the Bionic C library and dynamic linker. + + +Multi-core support + + At the moment, Bionic does not provide or use read/write memory barriers. + This means that using it on certain multi-core systems might not be + supported, depending on its exact CPU architecture. + + +Android-specific features: + + Bionic provides a small number of Android-specific features to its clients: + + - access to system properties: + + Android provides a simple shared value/key space to all processes on the + system. It stores a liberal number of 'properties', each of them being a + simple size-limited string that can be associated to a size-limited + string value. + + The header can be used to read system + properties and also defines the maximum size of keys and values. + + - Android-specific user/group management: + + There is no /etc/passwd or /etc/groups in Android. By design, it is + meant to be used by a single handset user. On the other hand, Android + uses the Linux user/group management features extensively to secure + process permissions, like access to various filesystem directories. + + In the Android scheme, each installed application gets its own + uid_t/gid_t starting from 10000; lower numerical ids are reserved for + system daemons. + + getpwnam() recognizes some hard-coded subsystems names (e.g. "radio") + and will translate them to their low-user-id values. It also recognizes + "app_1234" as the synthetic name of the application that was installed + with uid 10000 + 1234, which is 11234. getgrnam() works similarly + + getgrouplist() will always return a single group for any user name, + which is the one passed as an input parameter. + + getgrgid() will similarly only return a structure that contains a + single-element members list, corresponding to the user with the same + numerical value than the group. + + See bionic/libc/bionic/stubs.c for more details. + + - getservent() + + There is no /etc/services on Android. Instead the C library embeds a + constant list of services in its executable, which is parsed on demand + by the various functions that depend on it. See + bionic/libc/netbsd/net/getservent.c and + bionic/libc/netbsd/net/services.h + + The list of services defined internally might change liberally in the + future. This feature is mostly historically and is very rarely used. + + The getservent() returns thread-local data. getservbyport() and + getservbyname() are also implemented in a similar fashion. + + - getprotoent() + + There is no /etc/protocol on Android. Bionic does not currently + implement getprotoent() and related functions. If added, it will + likely be done in a way similar to getservent() + +DNS resolver: + + Bionic uses a NetBSD-derived resolver library which has been modified in + the following ways: + + - don't implement the name-server-switch feature (a.k.a. ) + + - read /system/etc/resolv.conf instead of /etc/resolv.conf + + - read the list of servers from system properties. the code looks for + 'net.dns1', 'net.dns2', etc.. Each property should contain the IP + address of a DNS server. + + these properties are set/modified by other parts of the Android system + (e.g. the dhcpd daemon). + + the implementation also supports per-process DNS server list, using the + properties 'net.dns1.', 'net.dns2.', etc... Where stands + for the numerical ID of the current process. + + - when performing a query, use a properly randomized Query ID (instead of + a incremented one), for increased security. + + - when performing a query, bind the local client socket to a random port + for increased security. + + - get rid of *many* unfortunate thread-safety issues in the original code + + Bionic does *not* expose implementation details of its DNS resolver; the + content of is intentionally blank. The resolver + implementation might change completely in the future. + + +PThread Real-Time Timers: + + timer_create(), timer_gettime(), timer_settime() and timer_getoverrun() are + supported. + + Bionic also now supports SIGEV_THREAD real-time timers (see timer_create()). + The implementation simply uses a single thread per timer, unlike GLibc which + uses complex heuristics to try to use the less threads possible when several + timers with compatible properties are used. + + This means that if your code uses a lot of SIGEV_THREAD timers, your program + may consume a lot of memory. However, if your program needs many of these + timers, it'd better handle timeout events directly instead. + + Other timers (e.g. SIGEV_SIGNAL) are handled by the kernel and use much less + system resources. + + +Binary Compatibility: + + Bionic is *not* in any way binary-compatible with the GNU C Library, ucLibc + or any known Linux C library. This means several things: + + - You cannot expect to build something against the GNU C Library headers and + have it dynamically link properly to Bionic later. + + - You should *really* use the Android toolchain to build your program against + Bionic. The toolchain deals with many important details that are crucial + to get something working properly. + + Failure to do so will usually result in the inability to run or link your + program, or even runtime crashes. Several random web pages on the Internet + describe how you can succesfully write a "hello-world" program with the + ARM GNU toolchain. These examples usually work by chance, if anything else, + and you should not follow these instructions unless you want to waste a lot + of your time in the process. + + Note however that you *can* generate a binary that is built against the + GNU C Library headers and then statically linked to it. The corresponding + executable should be able to run (if it doesn't use dlopen()/dlsym()) + + +Dynamic Linker: + + Bionic comes with its own dynamic linker (just like ld.so on Linux really + comes from GLibc). This linker does not support all the relocations + generated by other GCC ARM toolchains. + + +C++ Exceptions Support: + + At the moment, Bionic doesn't support C++ exceptions, what this really means + is the following: + + - If pthread_once() is called with a C++ callback that throws an exception, + then the C library will keep the corresponding pthread_once_t mutex + locked. Any further call to pthread_once() will result in a deadlock. + + A proper implementation should be able to register a C++ exception + cleanup handler before the callback to properly unlock the + pthread_once_t. Unfortunately this requires tricky assembly code that + is highly dependent on the compiler. + + This feature is not planned to be supported anytime soon. + + - The same problem may arise if you throw an exception within a callback + called from the C library. Fortunately, these cases are very rare in the + real-world, but any callback you provide to the C library should *not* + throw an exception. + + - Bionic lacks a few support functions to have exception support work + properly. + +System V IPCs: + + Bionic intentionally does not provide support for System-V IPCs mechanisms, + like the ones provided by semget(), shmget(), msgget(). The reason for this + is to avoid denial-of-service. For a detailed rationale about this, please + read the file docs/SYSV-IPCS.TXT. + +Include Paths: + + The Android build system should automatically provide the necessary include + paths required to build against the C library headers. However, if you want + to do that yourself, you will need to add: + + libc/arch-$ARCH/include + libc/include + libc/kernel/common + libc/kernel/arch-$ARCH + + to your C include path. diff --git a/libc/docs/SYSV-IPC.TXT b/libc/docs/SYSV-IPC.TXT new file mode 100644 index 000000000..5a3eef0d7 --- /dev/null +++ b/libc/docs/SYSV-IPC.TXT @@ -0,0 +1,103 @@ +Android does not support System V IPCs, i.e. the facilities provided by the +following standard Posix headers: + + /* SysV semaphores */ + /* SysV shared memory segments */ + /* SysV message queues */ + /* General IPC definitions */ + +The reason for this is due to the fact that, by design, they lead to global +kernel resource leakage. + +For example, there is no way to automatically release a SysV semaphore +allocated in the kernel when: + +- a buggy or malicious process exits +- a non-buggy and non-malicious process crashes or is explicitely killed. + +Killing processes automatically to make room for new ones is an +important part of Android's application lifecycle implementation. This means +that, even assuming only non-buggy and non-malicious code, it is very likely +that over time, the kernel global tables used to implement SysV IPCs will fill +up. + +At that point, strange failures are likely to occur and prevent programs that +use them to run properly until the next reboot of the system. + +And we can't ignore potential malicious applications. As a proof of concept +here is a simple exploit that you can run on a standard Linux box today: + +--------------- cut here ------------------------ +#include +#include +#include +#include +#include +#include + +#define NUM_SEMAPHORES 32 +#define MAX_FAILS 10 + +int main(void) +{ + int counter = 0; + int fails = 0; + + if (counter == IPC_PRIVATE) + counter++; + + printf( "%d (NUM_SEMAPHORES=%d)\n", counter, NUM_SEMAPHORES); + + for (;;) { + int ret = fork(); + int status; + + if (ret < 0) { + perror("fork:"); + break; + } + if (ret == 0) { + /* in the child */ + ret = semget( (key_t)counter, NUM_SEMAPHORES, IPC_CREAT ); + if (ret < 0) { + return errno; + } + return 0; + } + else { + /* in the parent */ + ret = wait(&status); + if (ret < 0) { + perror("waitpid:"); + break; + } + if (status != 0) { + status = WEXITSTATUS(status); + fprintf(stderr, "child %d FAIL at counter=%d: %d\n", ret, + counter, status); + if (++fails >= MAX_FAILS) + break; + } + } + + counter++; + if ((counter % 1000) == 0) { + printf("%d\n", counter); + } + if (counter == IPC_PRIVATE) + counter++; + } + return 0; +} +--------------- cut here ------------------------ + +If you run it on a typical Linux distribution today, you'll discover that it +will quickly fill up the kernel's table of unique key_t values, and that +strange things will happen in some parts of the system, but not all. + +(You can use the "ipcs -u" command to get a summary describing the kernel + tables and their allocations) + +For example, in our experience, anything program launched after that that +calls strerror() will simply crash. The USB sub-system starts spoutting weird +errors to the system console, etc... diff --git a/libc/include/pthread.h b/libc/include/pthread.h index 9c4009996..e3afdaede 100644 --- a/libc/include/pthread.h +++ b/libc/include/pthread.h @@ -97,6 +97,9 @@ typedef volatile int pthread_once_t; #define PTHREAD_PROCESS_PRIVATE 0 #define PTHREAD_PROCESS_SHARED 1 +#define PTHREAD_SCOPE_SYSTEM 0 +#define PTHREAD_SCOPE_PROCESS 1 + /* * Prototypes */ @@ -128,6 +131,9 @@ int pthread_attr_getstack(pthread_attr_t const * attr, void ** stackaddr, size_t int pthread_attr_setguardsize(pthread_attr_t * attr, size_t guard_size); int pthread_attr_getguardsize(pthread_attr_t const * attr, size_t * guard_size); +int pthread_attr_setscope(pthread_attr_t *attr, int scope); +int pthread_attr_getscope(pthread_attr_t const *attr); + int pthread_getattr_np(pthread_t thid, pthread_attr_t * attr); int pthread_create(pthread_t *thread, pthread_attr_t const * attr, diff --git a/libc/include/string.h b/libc/include/string.h index 435923b0b..613dcd78a 100644 --- a/libc/include/string.h +++ b/libc/include/string.h @@ -82,9 +82,8 @@ extern size_t strspn(const char *, const char *); extern char* strsignal(int sig); -/* Just declared to make libstdc++-v3 happy. */ -extern int strcoll (const char *, const char *); -extern size_t strxfrm (char *, const char *, size_t); +extern int strcoll(const char *, const char *); +extern size_t strxfrm(char *, const char *, size_t); __END_DECLS diff --git a/libc/include/sys/linux-syscalls.h b/libc/include/sys/linux-syscalls.h index bed00ce60..7772f1ec2 100644 --- a/libc/include/sys/linux-syscalls.h +++ b/libc/include/sys/linux-syscalls.h @@ -170,17 +170,6 @@ #define __NR_getsockopt (__NR_SYSCALL_BASE + 295) #define __NR_sendmsg (__NR_SYSCALL_BASE + 296) #define __NR_recvmsg (__NR_SYSCALL_BASE + 297) -#define __NR_semctl (__NR_SYSCALL_BASE + 300) -#define __NR_semget (__NR_SYSCALL_BASE + 299) -#define __NR_semop (__NR_SYSCALL_BASE + 298) -#define __NR_shmat (__NR_SYSCALL_BASE + 305) -#define __NR_shmctl (__NR_SYSCALL_BASE + 308) -#define __NR_shmdt (__NR_SYSCALL_BASE + 306) -#define __NR_shmget (__NR_SYSCALL_BASE + 307) -#define __NR_msgctl (__NR_SYSCALL_BASE + 304) -#define __NR_msgget (__NR_SYSCALL_BASE + 303) -#define __NR_msgrcv (__NR_SYSCALL_BASE + 302) -#define __NR_msgsnd (__NR_SYSCALL_BASE + 301) #define __NR_epoll_create (__NR_SYSCALL_BASE + 250) #define __NR_epoll_ctl (__NR_SYSCALL_BASE + 251) #define __NR_epoll_wait (__NR_SYSCALL_BASE + 252) diff --git a/libc/include/sys/linux-unistd.h b/libc/include/sys/linux-unistd.h index 8539c7b77..789271eef 100644 --- a/libc/include/sys/linux-unistd.h +++ b/libc/include/sys/linux-unistd.h @@ -179,17 +179,6 @@ 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 semctl (int semid, int semnum, int cmd, ...); -int semget (key_t key, int nsems, int semflg); -int semop (int semid, struct sembuf* sops, size_t nsops); -void* shmat (int shmid, const void* shmaddr, int shmflg); -int shmctl (int shmid, int cmd, struct shmid_ds* buf); -int shmdt (const void* shmaddr); -int shmget (key_t key, size_t size, int shmflg); -int msgctl (int msqid, int cmd, struct msqid_ds *buf); -int msgget (key_t key, int msgflg); -int msgrcv (int msqid, void* msgp, size_t msgsz, long int msgtyp, int msgflg); -int msgsnd (int msqid, const void* msgp, size_t msgsz, int msgflg); 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/stat.h b/libc/include/sys/stat.h index 5e6363ff4..23ab5aeec 100644 --- a/libc/include/sys/stat.h +++ b/libc/include/sys/stat.h @@ -71,6 +71,10 @@ struct stat { unsigned long long st_ino; }; +extern int chmod(const char *, mode_t); +extern int fchmod(int, mode_t); +extern int mkdir(const char *, mode_t); + extern int stat(const char *, struct stat *); extern int fstat(int, struct stat *); extern int lstat(const char *, struct stat *); diff --git a/libc/include/sys/time.h b/libc/include/sys/time.h index 4dee4dabc..1f010d45e 100644 --- a/libc/include/sys/time.h +++ b/libc/include/sys/time.h @@ -56,7 +56,7 @@ extern int utimes(const char *, const struct timeval *); #define timeradd(a, b, res) \ do { \ (res)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ - (res)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + (res)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ if ((res)->tv_usec >= 1000000) { \ (res)->tv_usec -= 1000000; \ (res)->tv_sec += 1; \ diff --git a/libc/include/sys/types.h b/libc/include/sys/types.h index 92b452c4c..b071ee912 100644 --- a/libc/include/sys/types.h +++ b/libc/include/sys/types.h @@ -65,6 +65,7 @@ typedef __kernel_nlink_t nlink_t; #define _OFF_T_DEFINED_ typedef __kernel_off_t off_t; typedef __kernel_loff_t loff_t; +typedef loff_t off64_t; /* GLibc-specific */ typedef __kernel_pid_t pid_t; diff --git a/libc/include/time64.h b/libc/include/time64.h new file mode 100644 index 000000000..9da4bc702 --- /dev/null +++ b/libc/include/time64.h @@ -0,0 +1,54 @@ +/* + +Copyright (c) 2007-2008 Michael G Schwern + +This software originally derived from Paul Sheer's pivotal_gmtime_r.c. + +The MIT License: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Origin: http://code.google.com/p/y2038 +Modified for Bionic by the Android Open Source Project + +*/ +#ifndef TIME64_H +#define TIME64_H + +#include +#include + +typedef int64_t time64_t; + +struct tm *gmtime64_r (const time64_t *, struct tm *); +struct tm *localtime64_r (const time64_t *, struct tm *); +struct tm *gmtime64 (const time64_t *); +struct tm *localtime64 (const time64_t *); + +char *asctime64 (const struct tm *); +char *asctime64_r (const struct tm *, char *); + +char *ctime64 (const time64_t*); +char *ctime64_r (const time64_t*, char*); + +time64_t timegm64 (const struct tm *); +time64_t mktime64 (const struct tm *); +time64_t timelocal64 (const struct tm *); + +#endif /* TIME64_H */ diff --git a/libc/include/unistd.h b/libc/include/unistd.h index b6d70cc43..1ada37e51 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -34,6 +34,7 @@ #include #include #include +#include __BEGIN_DECLS @@ -111,9 +112,6 @@ extern int link(const char *, const char *); extern int unlink(const char *); extern int chdir(const char *); extern int fchdir(int); -extern int chmod(const char *, mode_t); -extern int fchmod(int, mode_t); -extern int mkdir(const char *, mode_t); extern int rmdir(const char *); extern int pipe(int *); extern int chroot(const char *); diff --git a/libc/kernel/common/linux/msm_audio.h b/libc/kernel/common/linux/msm_audio.h index 58dc6f3f9..9ac58aaea 100644 --- a/libc/kernel/common/linux/msm_audio.h +++ b/libc/kernel/common/linux/msm_audio.h @@ -29,6 +29,10 @@ #define AUDIO_SET_EQ _IOW(AUDIO_IOCTL_MAGIC, 8, unsigned) #define AUDIO_SET_RX_IIR _IOW(AUDIO_IOCTL_MAGIC, 9, unsigned) #define AUDIO_SET_VOLUME _IOW(AUDIO_IOCTL_MAGIC, 10, unsigned) +#define AUDIO_ENABLE_AUDPRE _IOW(AUDIO_IOCTL_MAGIC, 11, unsigned) +#define AUDIO_SET_AGC _IOW(AUDIO_IOCTL_MAGIC, 12, unsigned) +#define AUDIO_SET_NS _IOW(AUDIO_IOCTL_MAGIC, 13, unsigned) +#define AUDIO_SET_TX_IIR _IOW(AUDIO_IOCTL_MAGIC, 14, unsigned) struct msm_audio_config { uint32_t buffer_size; diff --git a/libc/netbsd/gethnamaddr.c b/libc/netbsd/gethnamaddr.c index e6f919ec2..1c219b2a2 100644 --- a/libc/netbsd/gethnamaddr.c +++ b/libc/netbsd/gethnamaddr.c @@ -626,37 +626,12 @@ gethostbyname_internal(const char *name, int af, res_state res) break; } -#ifdef ANDROID_CHANGES - cache = __get_res_cache(); - if (cache != NULL) { - hp = _resolv_cache_lookup( cache, name, af ); - if (hp == _RESOLV_HOSTENT_NONE) { - h_errno = HOST_NOT_FOUND; - return NULL; - } - if (hp != NULL) { - h_errno = NETDB_SUCCESS; - return hp; - } - } -#endif - hp = NULL; h_errno = NETDB_INTERNAL; if (nsdispatch(&hp, dtab, NSDB_HOSTS, "gethostbyname", default_dns_files, name, strlen(name), af) != NS_SUCCESS) { -#ifdef ANDROID_CHANGES - /* cache negative DNS entry */ - if (h_errno == HOST_NOT_FOUND && cache != NULL) - _resolv_cache_add( cache, name, af, _RESOLV_HOSTENT_NONE ); -#endif return NULL; } -#ifdef ANDROID_CHANGES - if (cache != NULL) { - _resolv_cache_add( cache, name, af, hp ); - } -#endif h_errno = NETDB_SUCCESS; return hp; } diff --git a/libc/netbsd/resolv/res_cache.c b/libc/netbsd/resolv/res_cache.c index 210dfdd72..2c912de41 100644 --- a/libc/netbsd/resolv/res_cache.c +++ b/libc/netbsd/resolv/res_cache.c @@ -28,30 +28,104 @@ #include "resolv_cache.h" #include +#include #include #include "pthread.h" -/* this code implements a small DNS resolver cache for all gethostbyname* functions +/* This code implements a small and *simple* DNS resolver cache. * - * the cache is shared between all threads of the current process, but the result of - * a succesful lookup is always copied to a thread-local variable to honor the persistence - * rules of the gethostbyname*() APIs. + * It is only used to cache DNS answers for a maximum of CONFIG_SECONDS seconds + * in order to reduce DNS traffic. It is not supposed to be a full DNS cache, + * since we plan to implement that in the future in a dedicated process running + * on the system. + * + * Note that its design is kept simple very intentionally, i.e.: + * + * - it takes raw DNS query packet data as input, and returns raw DNS + * answer packet data as output + * + * (this means that two similar queries that encode the DNS name + * differently will be treated distinctly). + * + * - the TTLs of answer RRs are ignored. our DNS resolver library does not use + * them anyway, but it means that records with a TTL smaller than + * CONFIG_SECONDS will be kept in the cache anyway. + * + * this is bad, but we absolutely want to avoid parsing the answer packets + * (and should be solved by the later full DNS cache process). + * + * - the implementation is just a (query-data) => (answer-data) hash table + * with a trivial least-recently-used expiration policy. + * + * Doing this keeps the code simple and avoids to deal with a lot of things + * that a full DNS cache is expected to do. + * + * The API is also very simple: + * + * - the client calls _resolv_cache_get() to obtain a handle to the cache. + * this will initialize the cache on first usage. the result can be NULL + * if the cache is disabled. + * + * - the client calls _resolv_cache_lookup() before performing a query + * + * if the function returns RESOLV_CACHE_FOUND, a copy of the answer data + * has been copied into the client-provided answer buffer. + * + * if the function returns RESOLV_CACHE_NOTFOUND, the client should perform + * a request normally, *then* call _resolv_cache_add() to add the received + * answer to the cache. + * + * if the function returns RESOLV_CACHE_UNSUPPORTED, the client should + * perform a request normally, and *not* call _resolv_cache_add() + * + * note that RESOLV_CACHE_UNSUPPORTED is also returned if the answer buffer + * is too short to accomodate the cached result. + * + * - when network settings change, the cache must be flushed since the list + * of DNS servers probably changed. this is done by calling + * _resolv_cache_reset() + * + * the parameter to this function must be an ever-increasing generation + * number corresponding to the current network settings state. + * + * This is done because several threads could detect the same network + * settings change (but at different times) and will all end up calling the + * same function. Comparing with the last used generation number ensures + * that the cache is only flushed once per network change. */ -/* the name of an environment variable that will be checked the first time this code is called - * if its value is "0", then the resolver cache is disabled. +/* the name of an environment variable that will be checked the first time + * this code is called if its value is "0", then the resolver cache is + * disabled. */ #define CONFIG_ENV "BIONIC_DNSCACHE" -/* entries older than CONFIG_SECONDS seconds are always discarded. otherwise we could keep - * stale addresses in the cache and be oblivious to DNS server changes +/* entries older than CONFIG_SECONDS seconds are always discarded. */ #define CONFIG_SECONDS (60*10) /* 10 minutes */ -/* maximum number of entries kept in the cache. be frugal, this is the C library - * this MUST BE A POWER OF 2 +/* maximum number of entries kept in the cache. This value has been + * determined by browsing through various sites and counting the number + * of corresponding requests. Keep in mind that our framework is currently + * performing two requests per name lookup (one for IPv4, the other for IPv6) + * + * www.google.com 4 + * www.ysearch.com 6 + * www.amazon.com 8 + * www.nytimes.com 22 + * www.espn.com 28 + * www.msn.com 28 + * www.lemonde.fr 35 + * + * (determined in 2009-2-17 from Paris, France, results may vary depending + * on location) + * + * most high-level websites use lots of media/ad servers with different names + * but these are generally reused when browsing through the site. + * + * As such, a valud of 64 should be relatively conformtable at the moment. */ -#define CONFIG_MAX_ENTRIES 128 +#define CONFIG_MAX_ENTRIES 64 /****************************************************************************/ /****************************************************************************/ @@ -61,42 +135,204 @@ /****************************************************************************/ /****************************************************************************/ -#define DEBUG 0 +/* set to 1 to debug cache operations */ +#define DEBUG 0 + +/* set to 1 to debug query data */ +#define DEBUG_DATA 0 #if DEBUG -#include -#include -#include +# include +# define XLOG(...) \ + __libc_android_log_print(ANDROID_LOG_DEBUG,"libc",__VA_ARGS__) + #include -static void -xlog( const char* fmt, ... ) +#include + +/** BOUNDED BUFFER FORMATTING + **/ + +/* technical note: + * + * the following debugging routines are used to append data to a bounded + * buffer they take two parameters that are: + * + * - p : a pointer to the current cursor position in the buffer + * this value is initially set to the buffer's address. + * + * - end : the address of the buffer's limit, i.e. of the first byte + * after the buffer. this address should never be touched. + * + * IMPORTANT: it is assumed that end > buffer_address, i.e. + * that the buffer is at least one byte. + * + * the _bprint_() functions return the new value of 'p' after the data + * has been appended, and also ensure the following: + * + * - the returned value will never be strictly greater than 'end' + * + * - a return value equal to 'end' means that truncation occured + * (in which case, end[-1] will be set to 0) + * + * - after returning from a _bprint_() function, the content of the buffer + * is always 0-terminated, even in the event of truncation. + * + * these conventions allow you to call _bprint_ functions multiple times and + * only check for truncation at the end of the sequence, as in: + * + * char buff[1000], *p = buff, *end = p + sizeof(buff); + * + * p = _bprint_c(p, end, '"'); + * p = _bprint_s(p, end, my_string); + * p = _bprint_c(p, end, '"'); + * + * if (p >= end) { + * // buffer was too small + * } + * + * printf( "%s", buff ); + */ + +/* add a char to a bounded buffer */ +static char* +_bprint_c( char* p, char* end, int c ) { - static int fd = -2; - int ret; - - if (fd == -2) { - do { - fd = open( "/data/dns.log", O_CREAT | O_APPEND | O_WRONLY, 0666 ); - } while (fd < 0 && errno == EINTR); - } - - if (fd >= 0) { - char temp[128]; - va_list args; - va_start(args, fmt); - vsnprintf( temp, sizeof(temp), fmt, args); - va_end(args); - - do { - ret = write( fd, temp, strlen(temp) ); - } while (ret == -1 && errno == EINTR); + if (p < end) { + if (p+1 == end) + *p++ = 0; + else { + *p++ = (char) c; + *p = 0; + } } + return p; } -#define XLOG(...) xlog(__VA_ARGS__) -#else -#define XLOG(...) ((void)0) -#endif +/* add a sequence of bytes to a bounded buffer */ +static char* +_bprint_b( char* p, char* end, const char* buf, int len ) +{ + int avail = end - p; + + if (avail <= 0 || len <= 0) + return p; + + if (avail > len) + avail = len; + + memcpy( p, buf, avail ); + p += avail; + + if (p < end) + p[0] = 0; + else + end[-1] = 0; + + return p; +} + +/* add a string to a bounded buffer */ +static char* +_bprint_s( char* p, char* end, const char* str ) +{ + return _bprint_b(p, end, str, strlen(str)); +} + +/* add a formatted string to a bounded buffer */ +static char* +_bprint( char* p, char* end, const char* format, ... ) +{ + int avail, n; + va_list args; + + avail = end - p; + + if (avail <= 0) + return p; + + va_start(args, format); + n = snprintf( p, avail, format, args); + va_end(args); + + /* certain C libraries return -1 in case of truncation */ + if (n < 0 || n > avail) + n = avail; + + p += n; + /* certain C libraries do not zero-terminate in case of truncation */ + if (p == end) + p[-1] = 0; + + return p; +} + +/* add a hex value to a bounded buffer, up to 8 digits */ +static char* +_bprint_hex( char* p, char* end, unsigned value, int numDigits ) +{ + char text[sizeof(unsigned)*2]; + int nn = 0; + + while (numDigits-- > 0) { + text[nn++] = "0123456789abcdef"[(value >> (numDigits*4)) & 15]; + } + return _bprint_b(p, end, text, nn); +} + +/* add the hexadecimal dump of some memory area to a bounded buffer */ +static char* +_bprint_hexdump( char* p, char* end, const uint8_t* data, int datalen ) +{ + int lineSize = 16; + + while (datalen > 0) { + int avail = datalen; + int nn; + + if (avail > lineSize) + avail = lineSize; + + for (nn = 0; nn < avail; nn++) { + if (nn > 0) + p = _bprint_c(p, end, ' '); + p = _bprint_hex(p, end, data[nn], 2); + } + for ( ; nn < lineSize; nn++ ) { + p = _bprint_s(p, end, " "); + } + p = _bprint_s(p, end, " "); + + for (nn = 0; nn < avail; nn++) { + int c = data[nn]; + + if (c < 32 || c > 127) + c = '.'; + + p = _bprint_c(p, end, c); + } + p = _bprint_c(p, end, '\n'); + + data += avail; + datalen -= avail; + } + return p; +} + +/* dump the content of a query of packet to the log */ +static void +XLOG_BYTES( const void* base, int len ) +{ + char buff[1024]; + char* p = buff, *end = p + sizeof(buff); + + p = _bprint_hexdump(p, end, base, len); + XLOG("%s",buff); +} + +#else /* !DEBUG */ +# define XLOG(...) ((void)0) +# define XLOG_BYTES(a,b) ((void)0) +#endif static time_t _time_now( void ) @@ -107,96 +343,625 @@ _time_now( void ) return tv.tv_sec; } -/****************************************************************************/ -/****************************************************************************/ -/***** *****/ -/***** *****/ -/***** *****/ -/****************************************************************************/ -/****************************************************************************/ - -/* used to define the content of _RESOLV_HOSTENT_NONE +/* reminder: the general format of a DNS packet is the following: + * + * HEADER (12 bytes) + * QUESTION (variable) + * ANSWER (variable) + * AUTHORITY (variable) + * ADDITIONNAL (variable) + * + * the HEADER is made of: + * + * ID : 16 : 16-bit unique query identification field + * + * QR : 1 : set to 0 for queries, and 1 for responses + * Opcode : 4 : set to 0 for queries + * AA : 1 : set to 0 for queries + * TC : 1 : truncation flag, will be set to 0 in queries + * RD : 1 : recursion desired + * + * RA : 1 : recursion available (0 in queries) + * Z : 3 : three reserved zero bits + * RCODE : 4 : response code (always 0=NOERROR in queries) + * + * QDCount: 16 : question count + * ANCount: 16 : Answer count (0 in queries) + * NSCount: 16: Authority Record count (0 in queries) + * ARCount: 16: Additionnal Record count (0 in queries) + * + * the QUESTION is made of QDCount Question Record (QRs) + * the ANSWER is made of ANCount RRs + * the AUTHORITY is made of NSCount RRs + * the ADDITIONNAL is made of ARCount RRs + * + * Each Question Record (QR) is made of: + * + * QNAME : variable : Query DNS NAME + * TYPE : 16 : type of query (A=1, PTR=12, MX=15, AAAA=28, ALL=255) + * CLASS : 16 : class of query (IN=1) + * + * Each Resource Record (RR) is made of: + * + * NAME : variable : DNS NAME + * TYPE : 16 : type of query (A=1, PTR=12, MX=15, AAAA=28, ALL=255) + * CLASS : 16 : class of query (IN=1) + * TTL : 32 : seconds to cache this RR (0=none) + * RDLENGTH: 16 : size of RDDATA in bytes + * RDDATA : variable : RR data (depends on TYPE) + * + * Each QNAME contains a domain name encoded as a sequence of 'labels' + * terminated by a zero. Each label has the following format: + * + * LEN : 8 : lenght of label (MUST be < 64) + * NAME : 8*LEN : label length (must exclude dots) + * + * A value of 0 in the encoding is interpreted as the 'root' domain and + * terminates the encoding. So 'www.android.com' will be encoded as: + * + * <3>www<7>android<3>com<0> + * + * Where represents the byte with value 'n' + * + * Each NAME reflects the QNAME of the question, but has a slightly more + * complex encoding in order to provide message compression. This is achieved + * by using a 2-byte pointer, with format: + * + * TYPE : 2 : 0b11 to indicate a pointer, 0b01 and 0b10 are reserved + * OFFSET : 14 : offset to another part of the DNS packet + * + * The offset is relative to the start of the DNS packet and must point + * A pointer terminates the encoding. + * + * The NAME can be encoded in one of the following formats: + * + * - a sequence of simple labels terminated by 0 (like QNAMEs) + * - a single pointer + * - a sequence of simple labels terminated by a pointer + * + * A pointer shall always point to either a pointer of a sequence of + * labels (which can themselves be terminated by either a 0 or a pointer) + * + * The expanded length of a given domain name should not exceed 255 bytes. + * + * NOTE: we don't parse the answer packets, so don't need to deal with NAME + * records, only QNAMEs. */ -const struct hostent _resolv_hostent_none_cst = { - NULL, - NULL, - AF_INET, - 4, - NULL -}; -struct hostent* -_resolv_hostent_copy( struct hostent* hp ) +#define DNS_HEADER_SIZE 12 + +#define DNS_TYPE_A "\00\01" /* big-endian decimal 1 */ +#define DNS_TYPE_PTR "\00\014" /* big-endian decimal 12 */ +#define DNS_TYPE_MX "\00\017" /* big-endian decimal 15 */ +#define DNS_TYPE_AAAA "\00\034" /* big-endian decimal 28 */ +#define DNS_TYPE_ALL "\00\0377" /* big-endian decimal 255 */ + +#define DNS_CLASS_IN "\00\01" /* big-endian decimal 1 */ + +typedef struct { + const uint8_t* base; + const uint8_t* end; + const uint8_t* cursor; +} DnsPacket; + +static void +_dnsPacket_init( DnsPacket* packet, const uint8_t* buff, int bufflen ) { - struct hostent* dst; - int nn, len; - char* p; - int num_aliases = 0, num_addresses = 0; + packet->base = buff; + packet->end = buff + bufflen; + packet->cursor = buff; +} - if (hp == NULL) - return NULL; +static void +_dnsPacket_rewind( DnsPacket* packet ) +{ + packet->cursor = packet->base; +} - if (hp == _RESOLV_HOSTENT_NONE) - return hp; +static void +_dnsPacket_skip( DnsPacket* packet, int count ) +{ + const uint8_t* p = packet->cursor + count; - len = sizeof(*hp); - len += strlen(hp->h_name) + 1; + if (p > packet->end) + p = packet->end; - if (hp->h_aliases != NULL) { - for (nn = 0; hp->h_aliases[nn] != NULL; nn++) - len += sizeof(char*) + strlen(hp->h_aliases[nn]) + 1; - num_aliases = nn; + packet->cursor = p; +} + +static int +_dnsPacket_readInt16( DnsPacket* packet ) +{ + const uint8_t* p = packet->cursor; + + if (p+2 > packet->end) + return -1; + + packet->cursor = p+2; + return (p[0]<< 8) | p[1]; +} + +/** QUERY CHECKING + **/ + +/* check bytes in a dns packet. returns 1 on success, 0 on failure. + * the cursor is only advanced in the case of success + */ +static int +_dnsPacket_checkBytes( DnsPacket* packet, int numBytes, const void* bytes ) +{ + const uint8_t* p = packet->cursor; + + if (p + numBytes > packet->end) + return 0; + + if (memcmp(p, bytes, numBytes) != 0) + return 0; + + packet->cursor = p + numBytes; + return 1; +} + +/* parse and skip a given QNAME stored in a query packet, + * from the current cursor position. returns 1 on success, + * or 0 for malformed data. + */ +static int +_dnsPacket_checkQName( DnsPacket* packet ) +{ + const uint8_t* p = packet->cursor; + const uint8_t* end = packet->end; + + for (;;) { + int c; + + if (p >= end) + break; + + c = *p++; + + if (c == 0) { + packet->cursor = p; + return 1; + } + + /* we don't expect label compression in QNAMEs */ + if (c >= 64) + break; + + p += c; + /* we rely on the bound check at the start + * of the loop here */ } - len += sizeof(char*); + /* malformed data */ + XLOG("malformed QNAME"); + return 0; +} - for (nn = 0; hp->h_addr_list[nn] != NULL; nn++) { - len += sizeof(char*) + hp->h_length; +/* parse and skip a given QR stored in a packet. + * returns 1 on success, and 0 on failure + */ +static int +_dnsPacket_checkQR( DnsPacket* packet ) +{ + int len; + + if (!_dnsPacket_checkQName(packet)) + return 0; + + /* TYPE must be one of the things we support */ + if (!_dnsPacket_checkBytes(packet, 2, DNS_TYPE_A) && + !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_PTR) && + !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_MX) && + !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_AAAA) && + !_dnsPacket_checkBytes(packet, 2, DNS_TYPE_ALL)) + { + XLOG("unsupported TYPE"); + return 0; } - num_addresses = nn; - len += sizeof(char*); - - dst = malloc( len ); - if (dst == NULL) - return NULL; - - dst->h_aliases = (char**)(dst + 1); - dst->h_addr_list = dst->h_aliases + num_aliases + 1; - dst->h_length = hp->h_length; - dst->h_addrtype = hp->h_addrtype; - - p = (char*)(dst->h_addr_list + num_addresses + 1); - - /* write the addresses first, to help with alignment issues */ - for (nn = 0; nn < num_addresses; nn++) { - dst->h_addr_list[nn] = p; - len = hp->h_length; - memcpy( p, hp->h_addr_list[nn], len ); - p += len; + /* CLASS must be IN */ + if (!_dnsPacket_checkBytes(packet, 2, DNS_CLASS_IN)) { + XLOG("unsupported CLASS"); + return 0; } - dst->h_addr_list[nn] = NULL; - for (nn = 0; nn < num_aliases; nn++) { - dst->h_aliases[nn] = p; - len = strlen(hp->h_aliases[nn]) + 1; - memcpy( p, hp->h_aliases[nn], len ); - p += len; + return 1; +} + +/* check the header of a DNS Query packet, return 1 if it is one + * type of query we can cache, or 0 otherwise + */ +static int +_dnsPacket_checkQuery( DnsPacket* packet ) +{ + const uint8_t* p = packet->base; + int qdCount, anCount, dnCount, arCount; + + if (p + DNS_HEADER_SIZE > packet->end) { + XLOG("query packet too small"); + return 0; } - dst->h_aliases[nn] = NULL; - dst->h_name = p; - len = strlen(hp->h_name) + 1; - memcpy(p, hp->h_name, len); - p += len; + /* QR must be set to 0, opcode must be 0 and AA must be 0 */ + /* RA, Z, and RCODE must be 0 */ + if ((p[2] & 0xFC) != 0 || p[3] != 0) { + XLOG("query packet flags unsupported"); + return 0; + } - return dst; + /* Note that we ignore the TC and RD bits here for the + * following reasons: + * + * - there is no point for a query packet sent to a server + * to have the TC bit set, but the implementation might + * set the bit in the query buffer for its own needs + * between a _resolv_cache_lookup and a + * _resolv_cache_add. We should not freak out if this + * is the case. + * + * - we consider that the result from a RD=0 or a RD=1 + * query might be different, hence that the RD bit + * should be used to differentiate cached result. + * + * this implies that RD is checked when hashing or + * comparing query packets, but not TC + */ + + /* ANCOUNT, DNCOUNT and ARCOUNT must be 0 */ + qdCount = (p[4] << 8) | p[5]; + anCount = (p[6] << 8) | p[7]; + dnCount = (p[8] << 8) | p[9]; + arCount = (p[10]<< 8) | p[11]; + + if (anCount != 0 || dnCount != 0 || arCount != 0) { + XLOG("query packet contains non-query records"); + return 0; + } + + if (qdCount == 0) { + XLOG("query packet doesn't contain query record"); + return 0; + } + + /* Check QDCOUNT QRs */ + packet->cursor = p + DNS_HEADER_SIZE; + + for (;qdCount > 0; qdCount--) + if (!_dnsPacket_checkQR(packet)) + return 0; + + return 1; +} + +/** QUERY DEBUGGING + **/ +#if DEBUG +static char* +_dnsPacket_bprintQName(DnsPacket* packet, char* bp, char* bend) +{ + const uint8_t* p = packet->cursor; + const uint8_t* end = packet->end; + int first = 1; + + for (;;) { + int c; + + if (p >= end) + break; + + c = *p++; + + if (c == 0) { + packet->cursor = p; + return bp; + } + + /* we don't expect label compression in QNAMEs */ + if (c >= 64) + break; + + if (first) + first = 0; + else + bp = _bprint_c(bp, bend, '.'); + + bp = _bprint_b(bp, bend, (const char*)p, c); + + p += c; + /* we rely on the bound check at the start + * of the loop here */ + } + /* malformed data */ + bp = _bprint_s(bp, bend, ""); + return bp; +} + +static char* +_dnsPacket_bprintQR(DnsPacket* packet, char* p, char* end) +{ +#define QQ(x) { DNS_TYPE_##x, #x } + static const struct { + const char* typeBytes; + const char* typeString; + } qTypes[] = + { + QQ(A), QQ(PTR), QQ(MX), QQ(AAAA), QQ(ALL), + { NULL, NULL } + }; + int nn; + const char* typeString = NULL; + + /* dump QNAME */ + p = _dnsPacket_bprintQName(packet, p, end); + + /* dump TYPE */ + p = _bprint_s(p, end, " ("); + + for (nn = 0; qTypes[nn].typeBytes != NULL; nn++) { + if (_dnsPacket_checkBytes(packet, 2, qTypes[nn].typeBytes)) { + typeString = qTypes[nn].typeString; + break; + } + } + + if (typeString != NULL) + p = _bprint_s(p, end, typeString); + else { + int typeCode = _dnsPacket_readInt16(packet); + p = _bprint(p, end, "UNKNOWN-%d", typeCode); + } + + p = _bprint_c(p, end, ')'); + + /* skip CLASS */ + _dnsPacket_skip(packet, 2); + return p; +} + +/* this function assumes the packet has already been checked */ +static char* +_dnsPacket_bprintQuery( DnsPacket* packet, char* p, char* end ) +{ + int qdCount; + + if (packet->base[2] & 0x1) { + p = _bprint_s(p, end, "RECURSIVE "); + } + + _dnsPacket_skip(packet, 4); + qdCount = _dnsPacket_readInt16(packet); + _dnsPacket_skip(packet, 6); + + for ( ; qdCount > 0; qdCount-- ) { + p = _dnsPacket_bprintQR(packet, p, end); + } + return p; +} +#endif + + +/** QUERY HASHING SUPPORT + ** + ** THE FOLLOWING CODE ASSUMES THAT THE INPUT PACKET HAS ALREADY + ** BEEN SUCCESFULLY CHECKED. + **/ + +/* use 32-bit FNV hash function */ +#define FNV_MULT 16777619U +#define FNV_BASIS 2166136261U + +static unsigned +_dnsPacket_hashBytes( DnsPacket* packet, int numBytes, unsigned hash ) +{ + const uint8_t* p = packet->cursor; + const uint8_t* end = packet->end; + + while (numBytes > 0 && p < end) { + hash = hash*FNV_MULT ^ *p++; + } + packet->cursor = p; + return hash; } -void -_resolv_hostent_free( struct hostent* hp ) +static unsigned +_dnsPacket_hashQName( DnsPacket* packet, unsigned hash ) { - if (hp && hp != _RESOLV_HOSTENT_NONE) - free(hp); + const uint8_t* p = packet->cursor; + const uint8_t* end = packet->end; + + for (;;) { + int c; + + if (p >= end) { /* should not happen */ + XLOG("%s: INTERNAL_ERROR: read-overflow !!\n", __FUNCTION__); + break; + } + + c = *p++; + + if (c == 0) + break; + + if (c >= 64) { + XLOG("%s: INTERNAL_ERROR: malformed domain !!\n", __FUNCTION__); + break; + } + if (p + c >= end) { + XLOG("%s: INTERNAL_ERROR: simple label read-overflow !!\n", + __FUNCTION__); + break; + } + while (c > 0) { + hash = hash*FNV_MULT ^ *p++; + c -= 1; + } + } + packet->cursor = p; + return hash; +} + +static unsigned +_dnsPacket_hashQR( DnsPacket* packet, unsigned hash ) +{ + int len; + + hash = _dnsPacket_hashQName(packet, hash); + hash = _dnsPacket_hashBytes(packet, 4, hash); /* TYPE and CLASS */ + return hash; +} + +static unsigned +_dnsPacket_hashQuery( DnsPacket* packet ) +{ + unsigned hash = FNV_BASIS; + int count; + _dnsPacket_rewind(packet); + + /* we ignore the TC bit for reasons explained in + * _dnsPacket_checkQuery(). + * + * however we hash the RD bit to differentiate + * between answers for recursive and non-recursive + * queries. + */ + hash = hash*FNV_MULT ^ (packet->base[2] & 1); + + /* assume: other flags are 0 */ + _dnsPacket_skip(packet, 4); + + /* read QDCOUNT */ + count = _dnsPacket_readInt16(packet); + + /* assume: ANcount, NScount, ARcount are 0 */ + _dnsPacket_skip(packet, 6); + + /* hash QDCOUNT QRs */ + for ( ; count > 0; count-- ) + hash = _dnsPacket_hashQR(packet, hash); + + return hash; +} + + +/** QUERY COMPARISON + ** + ** THE FOLLOWING CODE ASSUMES THAT THE INPUT PACKETS HAVE ALREADY + ** BEEN SUCCESFULLY CHECKED. + **/ + +static int +_dnsPacket_isEqualDomainName( DnsPacket* pack1, DnsPacket* pack2 ) +{ + const uint8_t* p1 = pack1->cursor; + const uint8_t* end1 = pack1->end; + const uint8_t* p2 = pack2->cursor; + const uint8_t* end2 = pack2->end; + + for (;;) { + int c1, c2; + + if (p1 >= end1 || p2 >= end2) { + XLOG("%s: INTERNAL_ERROR: read-overflow !!\n", __FUNCTION__); + break; + } + c1 = *p1++; + c2 = *p2++; + if (c1 != c2) + break; + + if (c1 == 0) { + pack1->cursor = p1; + pack2->cursor = p2; + return 1; + } + if (c1 >= 64) { + XLOG("%s: INTERNAL_ERROR: malformed domain !!\n", __FUNCTION__); + break; + } + if ((p1+c1 > end1) || (p2+c1 > end2)) { + XLOG("%s: INTERNAL_ERROR: simple label read-overflow !!\n", + __FUNCTION__); + break; + } + if (memcmp(p1, p2, c1) != 0) + break; + p1 += c1; + p2 += c1; + /* we rely on the bound checks at the start of the loop */ + } + /* not the same, or one is malformed */ + XLOG("different DN"); + return 0; +} + +static int +_dnsPacket_isEqualBytes( DnsPacket* pack1, DnsPacket* pack2, int numBytes ) +{ + const uint8_t* p1 = pack1->cursor; + const uint8_t* p2 = pack2->cursor; + + if ( p1 + numBytes > pack1->end || p2 + numBytes > pack2->end ) + return 0; + + if ( memcmp(p1, p2, numBytes) != 0 ) + return 0; + + pack1->cursor += numBytes; + pack2->cursor += numBytes; + return 1; +} + +static int +_dnsPacket_isEqualQR( DnsPacket* pack1, DnsPacket* pack2 ) +{ + /* compare domain name encoding + TYPE + CLASS */ + if ( !_dnsPacket_isEqualDomainName(pack1, pack2) || + !_dnsPacket_isEqualBytes(pack1, pack2, 2+2) ) + return 0; + + return 1; +} + +static int +_dnsPacket_isEqualQuery( DnsPacket* pack1, DnsPacket* pack2 ) +{ + int count1, count2; + + /* compare the headers, ignore most fields */ + _dnsPacket_rewind(pack1); + _dnsPacket_rewind(pack2); + + /* compare RD, ignore TC, see comment in _dnsPacket_checkQuery */ + if ((pack1->base[2] & 1) != (pack2->base[2] & 1)) { + XLOG("different RD"); + return 0; + } + + /* assume: other flags are all 0 */ + _dnsPacket_skip(pack1, 4); + _dnsPacket_skip(pack2, 4); + + /* compare QDCOUNT */ + count1 = _dnsPacket_readInt16(pack1); + count2 = _dnsPacket_readInt16(pack2); + if (count1 != count2 || count1 < 0) { + XLOG("different QDCOUNT"); + return 0; + } + + /* assume: ANcount, NScount and ARcount are all 0 */ + _dnsPacket_skip(pack1, 6); + _dnsPacket_skip(pack2, 6); + + /* compare the QDCOUNT QRs */ + for ( ; count1 > 0; count1-- ) { + if (!_dnsPacket_isEqualQR(pack1, pack2)) { + XLOG("different QR"); + return 0; + } + } + return 1; } /****************************************************************************/ @@ -207,15 +972,23 @@ _resolv_hostent_free( struct hostent* hp ) /****************************************************************************/ /****************************************************************************/ +/* cache entry. for simplicity, 'hash' and 'hlink' are inlined in this + * structure though they are conceptually part of the hash table. + * + * similarly, mru_next and mru_prev are part of the global MRU list + */ typedef struct Entry { - unsigned int hash; - const char* name; - short af; - short index; + unsigned int hash; /* hash value */ + struct Entry* hlink; /* next in collision chain */ struct Entry* mru_prev; struct Entry* mru_next; - time_t when; - struct hostent* hp; + + const uint8_t* query; + int querylen; + const uint8_t* answer; + int answerlen; + time_t when; /* time_t when entry was added to table */ + int id; /* for debugging purpose */ } Entry; @@ -224,30 +997,10 @@ entry_free( Entry* e ) { /* everything is allocated in a single memory block */ if (e) { - _resolv_hostent_free(e->hp); free(e); } } -static void -entry_init_key( Entry* e, const char* name, int af ) -{ - unsigned h = 0; - const char* p = name; - - /* compute hash */ - p = name; - while (*p) { - h = h*33 + *p++; - } - h += af*17; - - e->hash = h; - e->name = name; - e->af = (short) af; -} - - static __inline__ void entry_mru_remove( Entry* e ) { @@ -267,46 +1020,75 @@ entry_mru_add( Entry* e, Entry* list ) first->mru_prev = e; } +/* compute the hash of a given entry, this is a hash of most + * data in the query (key) */ +static unsigned +entry_hash( const Entry* e ) +{ + DnsPacket pack[1]; + _dnsPacket_init(pack, e->query, e->querylen); + return _dnsPacket_hashQuery(pack); +} + +/* initialize an Entry as a search key, this also checks the input query packet + * returns 1 on success, or 0 in case of unsupported/malformed data */ +static int +entry_init_key( Entry* e, const void* query, int querylen ) +{ + DnsPacket pack[1]; + + memset(e, 0, sizeof(*e)); + + e->query = query; + e->querylen = querylen; + e->hash = entry_hash(e); + + _dnsPacket_init(pack, query, querylen); + + return _dnsPacket_checkQuery(pack); +} + +/* allocate a new entry as a cache node */ static Entry* -entry_alloc( const char* name, int af, int index, struct hostent* hp ) +entry_alloc( const Entry* init, const void* answer, int answerlen ) { Entry* e; - int num_aliases = 0; - int num_addresses = 0; - char** aliases; - char** addresses; + int size; - /* compute the length of the memory block that will contain everything */ - int len = sizeof(*e) + strlen(name)+1; - - e = malloc(len); + size = sizeof(*e) + init->querylen + answerlen; + e = calloc(size, 1); if (e == NULL) return e; - entry_init_key(e, name, af); + e->hash = init->hash; + e->query = (const uint8_t*)(e+1); + e->querylen = init->querylen; - e->mru_next = e->mru_prev = e; - e->index = (short) index; - e->when = _time_now(); - e->hp = _resolv_hostent_copy(hp); + memcpy( (char*)e->query, init->query, e->querylen ); - if (e->hp == NULL) { - free(e); - return NULL; - } + e->answer = e->query + e->querylen; + e->answerlen = answerlen; + + memcpy( (char*)e->answer, answer, e->answerlen ); + + e->when = _time_now(); - e->name = (char*)(e+1); - len = strlen(name)+1; - memcpy( (char*)e->name, name, len ); return e; } - -static __inline__ int +static int entry_equals( const Entry* e1, const Entry* e2 ) { - return e1->hash == e2->hash && e1->af == e2->af && !strcmp( e1->name, e2->name ); + DnsPacket pack1[1], pack2[1]; + + if (e1->querylen != e2->querylen) { + return 0; + } + _dnsPacket_init(pack1, e1->query, e1->querylen); + _dnsPacket_init(pack2, e2->query, e2->querylen); + + return _dnsPacket_isEqualQuery(pack1, pack2); } /****************************************************************************/ @@ -317,30 +1099,49 @@ entry_equals( const Entry* e1, const Entry* e2 ) /****************************************************************************/ /****************************************************************************/ +/* We use a simple hash table with external collision lists + * for simplicity, the hash-table fields 'hash' and 'hlink' are + * inlined in the Entry structure. + */ #define MAX_HASH_ENTRIES (2*CONFIG_MAX_ENTRIES) typedef struct resolv_cache { - int num_entries; - Entry mru_list; - pthread_mutex_t lock; - int disabled; - Entry* entries[ MAX_HASH_ENTRIES ]; /* hash-table of pointers to entries */ + int num_entries; + Entry mru_list; + pthread_mutex_t lock; + unsigned generation; + int last_id; + Entry* entries[ MAX_HASH_ENTRIES ]; } Cache; -void -_resolv_cache_destroy( struct resolv_cache* cache ) -{ - if (cache != NULL) { - int nn; - for (nn = 0; nn < MAX_HASH_ENTRIES; nn++) { - entry_free(cache->entries[nn]); - } - pthread_mutex_destroy(&cache->lock); - free(cache); - } -} +#define HTABLE_VALID(x) ((x) != NULL && (x) != HTABLE_DELETED) +static void +_cache_flush_locked( Cache* cache ) +{ + int nn; + time_t now = _time_now(); + + for (nn = 0; nn < MAX_HASH_ENTRIES; nn++) + { + Entry** pnode = &cache->entries[nn]; + + while (*pnode != NULL) { + Entry* node = *pnode; + *pnode = node->hlink; + entry_free(node); + } + } + + cache->mru_list.mru_next = cache->mru_list.mru_prev = &cache->mru_list; + cache->num_entries = 0; + cache->last_id = 0; + + XLOG("*************************\n" + "*** DNS CACHE FLUSHED ***\n" + "*************************"); +} struct resolv_cache* _resolv_cache_create( void ) @@ -349,116 +1150,203 @@ _resolv_cache_create( void ) cache = calloc(sizeof(*cache), 1); if (cache) { - const char* env = getenv(CONFIG_ENV); - - if (env && atoi(env) == 0) - cache->disabled = 1; - + cache->generation = ~0U; pthread_mutex_init( &cache->lock, NULL ); cache->mru_list.mru_prev = cache->mru_list.mru_next = &cache->mru_list; - XLOG("%s: cache=%p %s\n", __FUNCTION__, cache, cache->disabled ? "disabled" : "enabled" ); + XLOG("%s: cache created\n", __FUNCTION__); } return cache; } -static int -_resolv_cache_find_index( Cache* cache, - const char* name, - int af ) +#if DEBUG +static void +_dump_query( const uint8_t* query, int querylen ) { - Entry key; - int nn, step, tries; + char temp[256], *p=temp, *end=p+sizeof(temp); + DnsPacket pack[1]; - entry_init_key( &key, name, af ); - - tries = MAX_HASH_ENTRIES; - nn = key.hash % MAX_HASH_ENTRIES; - step = 5; - - while (tries > 0) { - Entry* key2 = cache->entries[nn]; - - if (key2 == NULL) { - return -(nn + 1); - } - - if (entry_equals( &key, key2 ) ) { - return nn; - } - - nn = (nn + step) % MAX_HASH_ENTRIES; - tries -= 1; - } - return -(MAX_HASH_ENTRIES+1); + _dnsPacket_init(pack, query, querylen); + p = _dnsPacket_bprintQuery(pack, p, end); + XLOG("QUERY: %s", temp); } - static void -_resolv_cache_remove( struct resolv_cache* cache, - Entry* e ) +_cache_dump_mru( Cache* cache ) { - XLOG("%s: name='%s' af=%d\n", __FUNCTION__, e->name, e->af); - cache->entries[ e->index ] = NULL; /* remove from hash table */ - entry_mru_remove( e ); - entry_free( e ); + char temp[512], *p=temp, *end=p+sizeof(temp); + Entry* e; + + p = _bprint(temp, end, "MRU LIST (%2d): ", cache->num_entries); + for (e = cache->mru_list.mru_next; e != &cache->mru_list; e = e->mru_next) + p = _bprint(p, end, " %d", e->id); + + XLOG("%s", temp); +} +#endif + +#if DEBUG +# define XLOG_QUERY(q,len) _dump_query((q), (len)) +#else +# define XLOG_QUERY(q,len) ((void)0) +#endif + +/* This function tries to find a key within the hash table + * In case of success, it will return a *pointer* to the hashed key. + * In case of failure, it will return a *pointer* to NULL + * + * So, the caller must check '*result' to check for success/failure. + * + * The main idea is that the result can later be used directly in + * calls to _resolv_cache_add or _resolv_cache_remove as the 'lookup' + * parameter. This makes the code simpler and avoids re-searching + * for the key position in the htable. + * + * The result of a lookup_p is only valid until you alter the hash + * table. + */ +static Entry** +_cache_lookup_p( Cache* cache, + Entry* key ) +{ + int index = key->hash % MAX_HASH_ENTRIES; + Entry** pnode = &cache->entries[ key->hash % MAX_HASH_ENTRIES ]; + + while (*pnode != NULL) { + Entry* node = *pnode; + + if (node == NULL) + break; + + if (node->hash == key->hash && entry_equals(node, key)) + break; + + pnode = &node->hlink; + } + return pnode; +} + +/* Add a new entry to the hash table. 'lookup' must be the + * result of an immediate previous failed _lookup_p() call + * (i.e. with *lookup == NULL), and 'e' is the pointer to the + * newly created entry + */ +static void +_cache_add_p( Cache* cache, + Entry** lookup, + Entry* e ) +{ + *lookup = e; + e->id = ++cache->last_id; + entry_mru_add(e, &cache->mru_list); + cache->num_entries += 1; + + XLOG("%s: entry %d added (count=%d)", __FUNCTION__, + e->id, cache->num_entries); +} + +/* Remove an existing entry from the hash table, + * 'lookup' must be the result of an immediate previous + * and succesful _lookup_p() call. + */ +static void +_cache_remove_p( Cache* cache, + Entry** lookup ) +{ + Entry* e = *lookup; + + XLOG("%s: entry %d removed (count=%d)", __FUNCTION__, + e->id, cache->num_entries-1); + + entry_mru_remove(e); + *lookup = e->hlink; + entry_free(e); cache->num_entries -= 1; } - -struct hostent* -_resolv_cache_lookup( struct resolv_cache* cache, - const char* name, - int af ) +/* Remove the oldest entry from the hash table. + */ +static void +_cache_remove_oldest( Cache* cache ) { - int index; - struct hostent* result = NULL; + Entry* oldest = cache->mru_list.mru_prev; + Entry** lookup = _cache_lookup_p(cache, oldest); - if (cache->disabled) - return NULL; + if (*lookup == NULL) { /* should not happen */ + XLOG("%s: OLDEST NOT IN HTABLE ?", __FUNCTION__); + return; + } + _cache_remove_p(cache, lookup); +} + +ResolvCacheStatus +_resolv_cache_lookup( struct resolv_cache* cache, + const void* query, + int querylen, + void* answer, + int answersize, + int *answerlen ) +{ + DnsPacket pack[1]; + Entry key[1]; + int index; + Entry** lookup; + Entry* e; + time_t now; + + ResolvCacheStatus result = RESOLV_CACHE_NOTFOUND; + + XLOG("%s: lookup", __FUNCTION__); + XLOG_QUERY(query, querylen); + + /* we don't cache malformed queries */ + if (!entry_init_key(key, query, querylen)) { + XLOG("%s: unsupported query", __FUNCTION__); + return RESOLV_CACHE_UNSUPPORTED; + } + /* lookup cache */ pthread_mutex_lock( &cache->lock ); - XLOG( "%s: cache=%p name='%s' af=%d ", __FUNCTION__, cache, name, af ); - index = _resolv_cache_find_index( cache, name, af ); - if (index >= 0) { - Entry* e = cache->entries[index]; - time_t now = _time_now(); - struct hostent** pht; + /* see the description of _lookup_p to understand this. + * the function always return a non-NULL pointer. + */ + lookup = _cache_lookup_p(cache, key); + e = *lookup; - /* ignore stale entries, they will be discarded in _resolv_cache_add */ - if ( (unsigned)(now - e->when) >= CONFIG_SECONDS ) { - XLOG( " OLD\n" ); - goto Exit; - } - - /* bump up this entry to the top of the MRU list */ - if (e != cache->mru_list.mru_next) { - entry_mru_remove( e ); - entry_mru_add( e, &cache->mru_list ); - } - - /* now copy the result into a thread-local variable */ - pht = __get_res_cache_hostent_p(); - if (pht == NULL) { - XLOG( " NOTLS\n" ); /* shouldn't happen */ - goto Exit; - } - - if (pht[0]) { - _resolv_hostent_free( pht[0] ); /* clear previous value */ - pht[0] = NULL; - } - result = _resolv_hostent_copy( e->hp ); - if (result == NULL) { - XLOG( " NOMEM\n" ); /* bummer */ - goto Exit; - } - XLOG( " OK\n" ); - pht[0] = result; + if (e == NULL) { + XLOG( "NOT IN CACHE"); goto Exit; } - XLOG( " KO\n" ); + + now = _time_now(); + + /* remove stale entries here */ + if ( (unsigned)(now - e->when) >= CONFIG_SECONDS ) { + XLOG( " NOT IN CACHE (STALE ENTRY %p DISCARDED)", *lookup ); + _cache_remove_p(cache, lookup); + goto Exit; + } + + *answerlen = e->answerlen; + if (e->answerlen > answersize) { + /* NOTE: we return UNSUPPORTED if the answer buffer is too short */ + result = RESOLV_CACHE_UNSUPPORTED; + XLOG(" ANSWER TOO LONG"); + goto Exit; + } + + memcpy( answer, e->answer, e->answerlen ); + + /* bump up this entry to the top of the MRU list */ + if (e != cache->mru_list.mru_next) { + entry_mru_remove( e ); + entry_mru_add( e, &cache->mru_list ); + } + + XLOG( "FOUND IN CACHE entry=%p", e ); + result = RESOLV_CACHE_FOUND; + Exit: pthread_mutex_unlock( &cache->lock ); return result; @@ -467,42 +1355,59 @@ Exit: void _resolv_cache_add( struct resolv_cache* cache, - const char* name, - int af, - struct hostent* hp ) + const void* query, + int querylen, + const void* answer, + int answerlen ) { + Entry key[1]; Entry* e; - int index; + Entry** lookup; - if (cache->disabled) + /* don't assume that the query has already been cached + */ + if (!entry_init_key( key, query, querylen )) { + XLOG( "%s: passed invalid query ?", __FUNCTION__); return; + } pthread_mutex_lock( &cache->lock ); - XLOG( "%s: cache=%p name='%s' af=%d\n", __FUNCTION__, cache, name, af); + XLOG( "%s: query:", __FUNCTION__ ); + XLOG_QUERY(query,querylen); +#if DEBUG_DATA + XLOG( "answer:"); + XLOG_BYTES(answer,answerlen); +#endif + + lookup = _cache_lookup_p(cache, key); + e = *lookup; + + if (e != NULL) { /* should not happen */ + XLOG("%s: ALREADY IN CACHE (%p) ? IGNORING ADD", + __FUNCTION__, e); + goto Exit; + } - /* get rid of the oldest entry if needed */ if (cache->num_entries >= CONFIG_MAX_ENTRIES) { - Entry* oldest = cache->mru_list.mru_prev; - _resolv_cache_remove( cache, oldest ); + _cache_remove_oldest(cache); + /* need to lookup again */ + lookup = _cache_lookup_p(cache, key); + e = *lookup; + if (e != NULL) { + XLOG("%s: ALREADY IN CACHE (%p) ? IGNORING ADD", + __FUNCTION__, e); + goto Exit; + } } - index = _resolv_cache_find_index( cache, name, af ); - if (index >= 0) { - /* discard stale entry */ - _resolv_cache_remove( cache, cache->entries[index] ); - } else { - index = -(index+1); - if (index >= MAX_HASH_ENTRIES) - goto Exit; /* should not happen */ - } - - e = entry_alloc( name, af, index, hp ); + e = entry_alloc( key, answer, answerlen ); if (e != NULL) { - entry_mru_add( e, &cache->mru_list ); - cache->entries[index] = e; - cache->num_entries += 1; + _cache_add_p(cache, lookup, e); } +#if DEBUG + _cache_dump_mru(cache); +#endif Exit: pthread_mutex_unlock( &cache->lock ); } @@ -521,6 +1426,13 @@ static pthread_once_t _res_cache_once; static void _res_cache_init( void ) { + const char* env = getenv(CONFIG_ENV); + + if (env && atoi(env) == 0) { + /* the cache is disabled */ + return; + } + _res_cache = _resolv_cache_create(); } @@ -531,3 +1443,19 @@ __get_res_cache( void ) pthread_once( &_res_cache_once, _res_cache_init ); return _res_cache; } + +void +_resolv_cache_reset( unsigned generation ) +{ + XLOG("%s: generation=%d", __FUNCTION__, generation); + + if (_res_cache == NULL) + return; + + pthread_mutex_lock( &_res_cache->lock ); + if (_res_cache->generation != generation) { + _cache_flush_locked(_res_cache); + _res_cache->generation = generation; + } + pthread_mutex_unlock( &_res_cache->lock ); +} diff --git a/libc/netbsd/resolv/res_send.c b/libc/netbsd/resolv/res_send.c index 24b740a2c..3aca760fe 100644 --- a/libc/netbsd/resolv/res_send.c +++ b/libc/netbsd/resolv/res_send.c @@ -81,6 +81,9 @@ __RCSID("$NetBSD: res_send.c,v 1.9 2006/01/24 17:41:25 christos Exp $"); #endif #endif /* LIBC_SCCS and not lint */ +/* set to 1 to use our small/simple/limited DNS cache */ +#define USE_RESOLV_CACHE 1 + /* * Send query to name server and wait for reply. */ @@ -111,6 +114,10 @@ __RCSID("$NetBSD: res_send.c,v 1.9 2006/01/24 17:41:25 christos Exp $"); #include +#if USE_RESOLV_CACHE +# include +#endif + #ifndef DE_CONST #define DE_CONST(c,v) v = ((c) ? \ strchr((const void *)(c), *(const char *)(const void *)(c)) : NULL) @@ -344,12 +351,17 @@ res_queriesmatch(const u_char *buf1, const u_char *eom1, return (1); } + int res_nsend(res_state statp, const u_char *buf, int buflen, u_char *ans, int anssiz) { int gotsomewhere, terrno, try, v_circuit, resplen, ns, n; char abuf[NI_MAXHOST]; +#if USE_RESOLV_CACHE + struct resolv_cache* cache; + ResolvCacheStatus cache_status = RESOLV_CACHE_UNSUPPORTED; +#endif if (statp->nscount == 0) { errno = ESRCH; @@ -365,6 +377,20 @@ res_nsend(res_state statp, gotsomewhere = 0; terrno = ETIMEDOUT; +#if USE_RESOLV_CACHE + cache = __get_res_cache(); + if (cache != NULL) { + int anslen = 0; + cache_status = _resolv_cache_lookup( + cache, buf, buflen, + ans, anssiz, &anslen); + + if (cache_status == RESOLV_CACHE_FOUND) { + return anslen; + } + } +#endif + /* * If the ns_addr_list in the resolver context has changed, then * invalidate our cached copy and the associated timing data. @@ -534,6 +560,12 @@ res_nsend(res_state statp, (stdout, "%s", ""), ans, (resplen > anssiz) ? anssiz : resplen); +#if USE_RESOLV_CACHE + if (cache_status == RESOLV_CACHE_NOTFOUND) { + _resolv_cache_add(cache, buf, buflen, + ans, resplen); + } +#endif /* * If we have temporarily opened a virtual circuit, * or if we haven't been asked to keep a socket open, diff --git a/libc/netbsd/resolv/res_state.c b/libc/netbsd/resolv/res_state.c index 8f2851ab0..3a2301d2e 100644 --- a/libc/netbsd/resolv/res_state.c +++ b/libc/netbsd/resolv/res_state.c @@ -46,7 +46,6 @@ typedef struct { struct __res_state _nres[1]; unsigned _serial; struct prop_info* _pi; - struct hostent* _hostent; struct res_static _rstatic[1]; } _res_thread; @@ -66,9 +65,9 @@ _res_thread_alloc(void) if ( res_ninit( rt->_nres ) < 0 ) { free(rt); rt = NULL; + } else { + memset(rt->_rstatic, 0, sizeof rt->_rstatic); } - rt->_hostent = NULL; - memset(rt->_rstatic, 0, sizeof rt->_rstatic); } return rt; } @@ -93,7 +92,6 @@ _res_thread_free( void* _rt ) _res_thread* rt = _rt; _res_static_done(rt->_rstatic); - _resolv_hostent_free(rt->_hostent); res_ndestroy(rt->_nres); free(rt); } @@ -132,6 +130,7 @@ _res_thread_get(void) rt = NULL; pthread_setspecific( _res_key, rt ); } + _resolv_cache_reset(rt->_serial); return rt; } @@ -177,14 +176,6 @@ __res_put_state(res_state res) res=res; } -struct hostent** -__get_res_cache_hostent_p(void) -{ - _res_thread* rt = _res_thread_get(); - - return rt ? &rt->_hostent : NULL; -} - res_static __res_get_static(void) { diff --git a/libc/bionic/logd.h b/libc/private/logd.h similarity index 100% rename from libc/bionic/logd.h rename to libc/private/logd.h diff --git a/libc/private/resolv_cache.h b/libc/private/resolv_cache.h index 8c255836f..cd876fb28 100644 --- a/libc/private/resolv_cache.h +++ b/libc/private/resolv_cache.h @@ -28,31 +28,39 @@ #ifndef _RESOLV_CACHE_H_ #define _RESOLV_CACHE_H_ -#include - -const struct hostent _resolv_hostent_none; -#define _RESOLV_HOSTENT_NONE ((struct hostent*)&_resolv_hostent_none) - struct resolv_cache; /* forward */ + +/* get cache instance, can be NULL if cache is disabled + * (e.g. through an environment variable) */ extern struct resolv_cache* __get_res_cache(void); -extern struct hostent** __get_res_cache_hostent_p(void); -extern struct resolv_cache* _resolv_cache_get( void ); +/* this gets called everytime we detect some changes in the DNS configuration + * and will flush the cache */ +extern void _resolv_cache_reset( unsigned generation ); -extern struct resolv_cache* _resolv_cache_create( void ); +typedef enum { + RESOLV_CACHE_UNSUPPORTED, /* the cache can't handle that kind of queries */ + /* or the answer buffer is too small */ + RESOLV_CACHE_NOTFOUND, /* the cache doesn't know about this query */ + RESOLV_CACHE_FOUND /* the cache found the answer */ +} ResolvCacheStatus; -extern void _resolv_cache_destroy( struct resolv_cache* cache ); +extern ResolvCacheStatus +_resolv_cache_lookup( struct resolv_cache* cache, + const void* query, + int querylen, + void* answer, + int answersize, + int *answerlen ); -extern struct hostent* _resolv_cache_lookup( struct resolv_cache* cache, - const char* name, - int af ); - -extern void _resolv_cache_add( struct resolv_cache* cache, - const char* name, - int af, - struct hostent* hp ); - -extern struct hostent* _resolv_hostent_copy( struct hostent* hp ); -extern void _resolv_hostent_free( struct hostent* hp ); +/* add a (query,answer) to the cache, only call if _resolv_cache_lookup + * did return RESOLV_CACHE_NOTFOUND + */ +extern void +_resolv_cache_add( struct resolv_cache* cache, + const void* query, + int querylen, + const void* answer, + int answerlen ); #endif /* _RESOLV_CACHE_H_ */ diff --git a/libc/include/sys/msg.h b/libc/string/strcoll.c old mode 100644 new mode 100755 similarity index 74% rename from libc/include/sys/msg.h rename to libc/string/strcoll.c index dd1b527da..365cad53a --- a/libc/include/sys/msg.h +++ b/libc/string/strcoll.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2009 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,19 +25,16 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef _SYS_MSG_H -#define _SYS_MSG_H +#include -#include -#include - -__BEGIN_DECLS - -extern int msgctl(int msqid, int cmd, struct msqid_ds *buf); -extern int msgget(key_t key, int msgflg); -extern int msgrcv(int msqid, void* msgp, size_t msgsz, long int msgtyp, int msgflg); -extern int msgsnd(int msqid, const void* msgp, size_t msgsz, int msgflg); - -__END_DECLS - -#endif /* _SYS_MSG_H */ +/* + * Compare strings using the current locale. Since Bionic really does not + * support locales, we assume we always use the C locale and call strcmp. + * + * This function is provided to make libstdc++-v3 usable. + */ +int +strcoll(const char *s1, const char *s2) +{ + return strcmp (s1, s2); +} diff --git a/libc/string/strxfrm.c b/libc/string/strxfrm.c new file mode 100755 index 000000000..f1843b5b1 --- /dev/null +++ b/libc/string/strxfrm.c @@ -0,0 +1,47 @@ +/* + * 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. + */ +#include + +/* + * Transform string s2 to string s1 using the current locale so that + * strcmp of transformed strings yields the same result as strcoll. + * Since Bionic really does not support locales, we assume we always use + * the C locale. + * + * This function is provided to make libstdc++-v3 usable. + */ +size_t +strxfrm(char *s1, const char *s2, size_t n) +{ + size_t len = strlen(s2) + 1; + + if (len < n) + n = len; + memcpy(s1, s2, n); + return len; +} diff --git a/libc/unistd/abort.c b/libc/unistd/abort.c index a02bebac3..d4e40e199 100644 --- a/libc/unistd/abort.c +++ b/libc/unistd/abort.c @@ -34,6 +34,11 @@ #include "thread_private.h" #include "atexit.h" +/* temporary, for bug hunting */ +#include "logd.h" +#define debug_log(format, ...) \ + __libc_android_log_print(ANDROID_LOG_DEBUG, "libc-abort", (format), ##__VA_ARGS__ ) + void abort(void) { @@ -48,6 +53,9 @@ abort(void) * any errors -- X311J doesn't allow abort to return anyway. */ sigdelset(&mask, SIGABRT); + /* temporary, so deliberate seg fault can be caught by debuggerd */ + sigdelset(&mask, SIGSEGV); + /* -- */ (void)sigprocmask(SIG_SETMASK, &mask, (sigset_t *)NULL); /* @@ -64,6 +72,13 @@ abort(void) } } + /* temporary, for bug hunting */ + debug_log("abort() called in pid %d\n", getpid()); + /* seg fault seems to produce better debuggerd results than SIGABRT */ + *((char*)0xdeadbaad) = 39; + debug_log("somehow we're not dead?\n"); + /* -- */ + (void)kill(getpid(), SIGABRT); /* diff --git a/libdl/dltest.c b/libdl/dltest.c index e84d5a324..14cd854da 100755 --- a/libdl/dltest.c +++ b/libdl/dltest.c @@ -1,119 +1,147 @@ -#include -#include -#include -#include -#include -#include - -extern char *optarg; -extern int optind, opterr, optopt; - -static struct option long_options[] = { - {"library", required_argument, 0, 'l'}, - {"symbol", required_argument, 0, 's'}, - {"help", no_argument, 0, 'h'}, - {0, 0, 0, 0}, -}; - -/* This array must parallel long_options[] */ -static const char *descriptions[] = { - "specify a library path to look up symbol", - "specify symbol to look up", - "print this help screen", -}; - -void print_help(const char *name) { - fprintf(stdout, - "invokation:\n" - "\t%s [-l ] -s \n" - "\t%s -h\n\n", name, name); - fprintf(stdout, "options:\n"); - struct option *opt = long_options; - const char **desc = descriptions; - while (opt->name) { - fprintf(stdout, "\t-%c/--%s%s: %s\n", - opt->val, - opt->name, - (opt->has_arg ? " (argument)" : ""), - *desc); - opt++; - desc++; - } -} - -int get_options(int argc, char **argv, char **lib, char **sym) -{ - int c; - - *lib = 0; - *sym = 0; - - while (1) { - /* getopt_long stores the option index here. */ - int option_index = 0; - - c = getopt_long (argc, argv, - "l:s:h", - long_options, - &option_index); - /* Detect the end of the options. */ - if (c == -1) break; - - switch (c) { - case 'l': - *lib = strdup(optarg); - break; - case 's': - *sym = strdup(optarg); - break; - case 'h': print_help(argv[0]); exit(EXIT_FAILURE); break; - case '?': - /* getopt_long already printed an error message. */ - break; - default: - fprintf(stderr, "Unknown option"); - exit(EXIT_FAILURE); - } - } - - return optind; -} - -int main(int argc, char **argv) -{ - char *libname, *symname, *prog = *argv; - - get_options(argc, argv, &libname, &symname); - - if (symname == NULL) { - fprintf(stderr, "You must specify a symbol!\n"); - print_help(prog); - exit(EXIT_FAILURE); - } - - { - const char *dlerr; - void *handle, *symbol; - - printf("opening library [%s]\n", libname); - dlerr = dlerror(); - handle = libname ? dlopen(libname, RTLD_NOW) : RTLD_DEFAULT; - dlerr = dlerror(); - if (dlerr != NULL) fprintf(stderr, "dlopen() error: %s\n", dlerr); - - printf("opening symbol [%s]\n", symname); - symbol = dlsym(handle, symname); - dlerr = dlerror(); - if (dlerr != NULL) fprintf(stderr, "dlsym() error: %s\n", dlerr); - - printf("closing library [%s]\n", libname); - dlclose(handle); - dlerr = dlerror(); - if (dlerr != NULL) fprintf(stderr, "dlclose() error: %s\n", dlerr); - else printf("successfully opened symbol\n"); - } - - if (libname != NULL) free(libname); - if (symname != NULL) free(symname); - return 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 +#include +#include +#include +#include +#include + +extern char *optarg; +extern int optind, opterr, optopt; + +static struct option long_options[] = { + {"library", required_argument, 0, 'l'}, + {"symbol", required_argument, 0, 's'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0}, +}; + +/* This array must parallel long_options[] */ +static const char *descriptions[] = { + "specify a library path to look up symbol", + "specify symbol to look up", + "print this help screen", +}; + +void print_help(const char *name) { + fprintf(stdout, + "invokation:\n" + "\t%s [-l ] -s \n" + "\t%s -h\n\n", name, name); + fprintf(stdout, "options:\n"); + struct option *opt = long_options; + const char **desc = descriptions; + while (opt->name) { + fprintf(stdout, "\t-%c/--%s%s: %s\n", + opt->val, + opt->name, + (opt->has_arg ? " (argument)" : ""), + *desc); + opt++; + desc++; + } +} + +int get_options(int argc, char **argv, char **lib, char **sym) +{ + int c; + + *lib = 0; + *sym = 0; + + while (1) { + /* getopt_long stores the option index here. */ + int option_index = 0; + + c = getopt_long (argc, argv, + "l:s:h", + long_options, + &option_index); + /* Detect the end of the options. */ + if (c == -1) break; + + switch (c) { + case 'l': + *lib = strdup(optarg); + break; + case 's': + *sym = strdup(optarg); + break; + case 'h': print_help(argv[0]); exit(EXIT_FAILURE); break; + case '?': + /* getopt_long already printed an error message. */ + break; + default: + fprintf(stderr, "Unknown option"); + exit(EXIT_FAILURE); + } + } + + return optind; +} + +int main(int argc, char **argv) +{ + char *libname, *symname, *prog = *argv; + + get_options(argc, argv, &libname, &symname); + + if (symname == NULL) { + fprintf(stderr, "You must specify a symbol!\n"); + print_help(prog); + exit(EXIT_FAILURE); + } + + { + const char *dlerr; + void *handle, *symbol; + + printf("opening library [%s]\n", libname); + dlerr = dlerror(); + handle = libname ? dlopen(libname, RTLD_NOW) : RTLD_DEFAULT; + dlerr = dlerror(); + if (dlerr != NULL) fprintf(stderr, "dlopen() error: %s\n", dlerr); + + printf("opening symbol [%s]\n", symname); + symbol = dlsym(handle, symname); + dlerr = dlerror(); + if (dlerr != NULL) fprintf(stderr, "dlsym() error: %s\n", dlerr); + + printf("closing library [%s]\n", libname); + dlclose(handle); + dlerr = dlerror(); + if (dlerr != NULL) fprintf(stderr, "dlclose() error: %s\n", dlerr); + else printf("successfully opened symbol\n"); + } + + if (libname != NULL) free(libname); + if (symname != NULL) free(symname); + return 0; +} diff --git a/linker/Android.mk b/linker/Android.mk index 98eceda14..48141bef5 100644 --- a/linker/Android.mk +++ b/linker/Android.mk @@ -6,7 +6,8 @@ LOCAL_SRC_FILES:= \ linker.c \ rt.c \ dlfcn.c \ - debugger.c + debugger.c \ + ba.c LINKER_TEXT_BASE := 0xB0000100 @@ -16,7 +17,9 @@ LINKER_AREA_SIZE := 0x01000000 LOCAL_LDFLAGS := -Wl,-Ttext,$(LINKER_TEXT_BASE) -LOCAL_CFLAGS += -DPRELINK -DLINKER_TEXT_BASE=$(LINKER_TEXT_BASE) -DLINKER_AREA_SIZE=$(LINKER_AREA_SIZE) +LOCAL_CFLAGS += -DPRELINK +LOCAL_CFLAGS += -DLINKER_TEXT_BASE=$(LINKER_TEXT_BASE) +LOCAL_CFLAGS += -DLINKER_AREA_SIZE=$(LINKER_AREA_SIZE) # we need to access the Bionic private header # in the linker diff --git a/linker/arch/arm/begin.S b/linker/arch/arm/begin.S index f9b3bace4..e2599027a 100644 --- a/linker/arch/arm/begin.S +++ b/linker/arch/arm/begin.S @@ -1,3 +1,31 @@ +/* + * 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. + */ + .text .align 4 .type _start,#function diff --git a/linker/arch/x86/begin.S b/linker/arch/x86/begin.S index 8f468e6b6..d8a39ca32 100644 --- a/linker/arch/x86/begin.S +++ b/linker/arch/x86/begin.S @@ -1,3 +1,31 @@ +/* + * 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. + */ + .text .align 4 .type _start, @function @@ -21,3 +49,4 @@ _start: __CTOR_LIST__: .long -1 + diff --git a/linker/ba.c b/linker/ba.c new file mode 100644 index 000000000..bea6f849d --- /dev/null +++ b/linker/ba.c @@ -0,0 +1,180 @@ +/* + * 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 "linker.h" +#include "linker_debug.h" +#include "ba.h" + +struct ba_bits { + unsigned allocated:1; /* 1 if allocated, 0 if free */ + unsigned order:7; /* size of the region in ba space */ +}; + +struct ba_info { + /* start address of the ba space */ + unsigned long base; + /* total size of the ba space */ + unsigned long size; + /* number of entries in the ba space */ + int num_entries; + /* the bitmap for the region indicating which entries are allocated + * and which are free */ + struct ba_bits *bitmap; +}; + +#undef min +#define min(a,b) ((a)<(b)?(a):(b)) + +#define BA_MIN_ALLOC LIBINC +#define BA_MAX_ORDER 128 +#define BA_START LIBBASE +#define BA_SIZE (LIBLAST - LIBBASE) + +#define BA_IS_FREE(index) (!(ba.bitmap[index].allocated)) +#define BA_ORDER(index) ba.bitmap[index].order +#define BA_BUDDY_INDEX(index) ((index) ^ (1 << BA_ORDER(index))) +#define BA_NEXT_INDEX(index) ((index) + (1 << BA_ORDER(index))) +#define BA_OFFSET(index) ((index) * BA_MIN_ALLOC) +#define BA_START_ADDR(index) (BA_OFFSET(index) + ba.base) +#define BA_LEN(index) ((1 << BA_ORDER(index)) * BA_MIN_ALLOC) + +static struct ba_bits ba_bitmap[BA_SIZE / BA_MIN_ALLOC]; + +static struct ba_info ba = { + .base = BA_START, + .size = BA_SIZE, + .bitmap = ba_bitmap, + .num_entries = sizeof(ba_bitmap)/sizeof(ba_bitmap[0]), +}; + +void ba_init(void) +{ + int i, index = 0; + for (i = sizeof(ba.num_entries) * 8 - 1; i >= 0; i--) { + if (ba.num_entries & 1<> i == 0) + break; + return i; +} + +int ba_allocate(unsigned long len) +{ + int curr = 0; + int end = ba.num_entries; + int best_fit = -1; + unsigned long order = ba_order(len); + + if (order > BA_MAX_ORDER) + return -1; + + /* look through the bitmap: + * if you find a free slot of the correct order use it + * otherwise, use the best fit (smallest with size > order) slot + */ + while (curr < end) { + if (BA_IS_FREE(curr)) { + if (BA_ORDER(curr) == (unsigned char)order) { + /* set the not free bit and clear others */ + best_fit = curr; + break; + } + if (BA_ORDER(curr) > (unsigned char)order && + (best_fit < 0 || + BA_ORDER(curr) < BA_ORDER(best_fit))) + best_fit = curr; + } + curr = BA_NEXT_INDEX(curr); + } + + /* if best_fit < 0, there are no suitable slots, + * return an error + */ + if (best_fit < 0) + return -1; + + /* now partition the best fit: + * split the slot into 2 buddies of order - 1 + * repeat until the slot is of the correct order + */ + while (BA_ORDER(best_fit) > (unsigned char)order) { + int buddy; + BA_ORDER(best_fit) -= 1; + buddy = BA_BUDDY_INDEX(best_fit); + BA_ORDER(buddy) = BA_ORDER(best_fit); + } + ba.bitmap[best_fit].allocated = 1; + return best_fit; +} + +unsigned long ba_start_addr(int index) +{ + return BA_START_ADDR(index); +} + +unsigned long ba_len(int index) +{ + return BA_LEN(index); +} diff --git a/libc/include/sys/shm.h b/linker/ba.h similarity index 78% rename from libc/include/sys/shm.h rename to linker/ba.h index 495d33e4e..78f462610 100644 --- a/libc/include/sys/shm.h +++ b/linker/ba.h @@ -25,19 +25,14 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#ifndef _SYS_SHM_H -#define _SYS_SHM_H -#include -#include +#ifndef __LINKER_BA_H +#define __LINKER_BA_H -__BEGIN_DECLS +extern void ba_init(void); +extern int ba_allocate(unsigned long len); +extern int ba_free(int index); +extern unsigned long ba_start_addr(int index); +extern unsigned long ba_len(int index); -extern void* shmat(int shmid, const void* shmaddr, int shmflg); -extern int shmctl(int shmid, int cmd, struct shmid_ds* buf); -extern int shmdt(const void* shmaddr); -extern int shmget(key_t key, size_t size, int shmflg); - -__END_DECLS - -#endif /* _SYS_SHM_H */ +#endif diff --git a/linker/debugger.c b/linker/debugger.c index 542cf8dab..5bb065c6c 100644 --- a/linker/debugger.c +++ b/linker/debugger.c @@ -1,3 +1,31 @@ +/* + * 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 #include #include @@ -21,7 +49,7 @@ void debugger_signal_handler(int n) signal(SIGUSR1, SIG_IGN); tid = gettid(); - s = socket_local_client("android:debuggerd", + s = socket_local_client("android:debuggerd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); if(s >= 0) { @@ -49,4 +77,5 @@ void debugger_init() signal(SIGFPE, debugger_signal_handler); signal(SIGSEGV, debugger_signal_handler); signal(SIGSTKFLT, debugger_signal_handler); + signal(SIGPIPE, debugger_signal_handler); } diff --git a/linker/linker.c b/linker/linker.c index 8f15f6277..91435de19 100644 --- a/linker/linker.c +++ b/linker/linker.c @@ -1,3 +1,31 @@ +/* + * 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 #include @@ -21,6 +49,8 @@ #include "linker.h" #include "linker_debug.h" +#include "ba.h" + #define SO_MAX 64 /* >>> IMPORTANT NOTE - READ ME BEFORE MODIFYING <<< @@ -82,7 +112,8 @@ extern void __attribute__((noinline)) rtld_db_dlactivity(void); extern void sched_yield(void); -static struct r_debug _r_debug = {1, NULL, &rtld_db_dlactivity, RT_CONSISTENT, 0}; +static struct r_debug _r_debug = {1, NULL, &rtld_db_dlactivity, + RT_CONSISTENT, 0}; static struct link_map *r_debug_tail = 0; //static pthread_mutex_t _r_debug_lock = PTHREAD_MUTEX_INITIALIZER; @@ -179,6 +210,7 @@ static soinfo *alloc_info(const char *name) memset(si, 0, sizeof(soinfo)); strcpy((char*) si->name, name); sonext->next = si; + si->ba_index = -1; /* by default, prelinked */ si->next = NULL; si->refcount = 0; sonext = si; @@ -450,8 +482,6 @@ static int open_library(const char *name) return -1; } -static unsigned libbase = LIBBASE; - /* temporary space for holding the first page of the shared lib * which contains the elf header (with the pht). */ static unsigned char __header[PAGE_SIZE]; @@ -597,64 +627,67 @@ get_lib_extents(int fd, const char *name, void *__hdr, unsigned *total_sz) * segments into the correct locations within this memory range. * * Args: - * req_base: The requested base of the allocation. If 0, a sane one will be + * si->base: The requested base of the allocation. If 0, a sane one will be * chosen in the range LIBBASE <= base < LIBLAST. - * sz: The size of the allocation. + * si->size: The size of the allocation. * * Returns: - * NULL on failure, and non-NULL pointer to memory region on success. + * -1 on failure, and 0 on success. On success, si->base will contain + * the virtual address at which the library will be mapped. */ -static void * -alloc_mem_region(const char *name, unsigned req_base, unsigned sz) + +static int reserve_mem_region(soinfo *si) { - void *base; + void *base = mmap((void *)si->base, si->size, PROT_READ | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (base == MAP_FAILED) { + ERROR("%5d can NOT map (%sprelinked) library '%s' at 0x%08x " + "as requested, will try general pool: %d (%s)\n", + pid, (si->base ? "" : "non-"), si->name, si->base, + errno, strerror(errno)); + return -1; + } else if (base != (void *)si->base) { + ERROR("OOPS: %5d %sprelinked library '%s' mapped at 0x%08x, " + "not at 0x%08x\n", pid, (si->base ? "" : "non-"), + si->name, (unsigned)base, si->base); + munmap(base, si->size); + return -1; + } + return 0; +} - if (req_base) { - /* we should probably map it as PROT_NONE, but the init code needs - * to read the phdr, so mark everything as readable. */ - base = mmap((void *)req_base, sz, PROT_READ | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (base == MAP_FAILED) { - WARN("%5d can NOT map (prelinked) library '%s' at 0x%08x " - "as requested, will try general pool: %d (%s)\n", - pid, name, req_base, errno, strerror(errno)); - } else if (base != (void *)req_base) { - ERROR("OOPS: %5d prelinked library '%s' mapped at 0x%08x, " - "not at 0x%08x\n", pid, name, (unsigned)base, req_base); - munmap(base, sz); - return NULL; - } - - /* Here we know that we got a valid allocation. Hooray! */ - return base; +static int +alloc_mem_region(soinfo *si) +{ + if (si->base) { + /* Attempt to mmap a prelinked library. */ + si->ba_index = -1; + return reserve_mem_region(si); } - /* We either did not request a specific base address to map at - * (i.e. not-prelinked) OR we could not map at the requested address. - * Try to find a memory range in our "reserved" area that can be mapped. - */ - while(libbase < LIBLAST) { - base = mmap((void*) libbase, sz, PROT_READ | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - if(((unsigned)base) == libbase) { - /* success -- got the address we wanted */ - return base; + /* This is not a prelinked library, so we attempt to allocate space + for it from the buddy allocator, which manages the area between + LIBBASE and LIBLAST. + */ + si->ba_index = ba_allocate(si->size); + if(si->ba_index >= 0) { + si->base = ba_start_addr(si->ba_index); + PRINT("%5d mapping library '%s' at %08x (index %d) " \ + "through buddy allocator.\n", + pid, si->name, si->base, si->ba_index); + if (reserve_mem_region(si) < 0) { + ba_free(si->ba_index); + si->ba_index = -1; + si->base = 0; + goto err; } - - /* If we got a different address than requested (rather than - * just a failure), we need to unmap the mismapped library - * before trying again - */ - if(base != MAP_FAILED) - munmap(base, sz); - - libbase += LIBINC; + return 0; } +err: ERROR("OOPS: %5d cannot map library '%s'. no vspace available.\n", - pid, name); - return NULL; + pid, si->name); + return -1; } #define MAYBE_MAP_FLAG(x,from,to) (((x) & (from)) ? (to) : 0) @@ -884,8 +917,7 @@ load_library(const char *name) int cnt; unsigned ext_sz; unsigned req_base; - void *base; - soinfo *si; + soinfo *si = NULL; Elf32_Ehdr *hdr; if(fd == -1) @@ -911,14 +943,6 @@ load_library(const char *name) TRACE("[ %5d - '%s' (%s) wants base=0x%08x sz=0x%08x ]\n", pid, name, (req_base ? "prelinked" : "not pre-linked"), req_base, ext_sz); - /* Carve out a chunk of memory where we will map in the individual - * segments */ - base = alloc_mem_region(name, req_base, ext_sz); - if (base == NULL) - goto fail; - TRACE("[ %5d allocated memory for %s @ %p (0x%08x) ]\n", - pid, name, base, (unsigned) ext_sz); - /* Now configure the soinfo struct where we'll store all of our data * for the ELF object. If the loading fails, we waste the entry, but * same thing would happen if we failed during linking. Configuring the @@ -928,19 +952,31 @@ load_library(const char *name) if (si == NULL) goto fail; - si->base = (unsigned)base; + /* Carve out a chunk of memory where we will map in the individual + * segments */ + si->base = req_base; si->size = ext_sz; si->flags = 0; si->entry = 0; si->dynamic = (unsigned *)-1; + if (alloc_mem_region(si) < 0) + goto fail; + + TRACE("[ %5d allocated memory for %s @ %p (0x%08x) ]\n", + pid, name, (void *)si->base, (unsigned) ext_sz); /* Now actually load the library's segments into right places in memory */ - if (load_segments(fd, &__header[0], si) < 0) + if (load_segments(fd, &__header[0], si) < 0) { + if (si->ba_index >= 0) { + ba_free(si->ba_index); + si->ba_index = -1; + } goto fail; + } /* this might not be right. Technically, we don't even need this info * once we go through 'load_segments'. */ - hdr = (Elf32_Ehdr *)base; + hdr = (Elf32_Ehdr *)si->base; si->phdr = (Elf32_Phdr *)((unsigned char *)si->base + hdr->e_phoff); si->phnum = hdr->e_phnum; /**/ @@ -949,6 +985,7 @@ load_library(const char *name) return si; fail: + if (si) free_info(si); close(fd); return NULL; } @@ -957,8 +994,6 @@ static soinfo * init_library(soinfo *si) { unsigned wr_offset = 0xffffffff; - unsigned libbase_before = 0; - unsigned libbase_after = 0; /* At this point we know that whatever is loaded @ base is a valid ELF * shared library whose segments are properly mapped in. */ @@ -968,24 +1003,10 @@ init_library(soinfo *si) if (si->base < LIBBASE || si->base >= LIBLAST) si->flags |= FLAG_PRELINKED; - /* Adjust libbase for the size of this library, rounded up to - ** LIBINC alignment. Make note of the previous and current - ** value of libbase to allow us to roll back in the event of - ** a link failure. - */ - if (!(si->flags & FLAG_PRELINKED)) { - libbase_before = libbase; - libbase += (si->size + (LIBINC - 1)) & (~(LIBINC - 1)); - libbase_after = libbase; - } - if(link_image(si, wr_offset)) { /* We failed to link. However, we can only restore libbase ** if no additional libraries have moved it since we updated it. */ - if(!(si->flags & FLAG_PRELINKED) && (libbase == libbase_after)) { - libbase = libbase_before; - } munmap((void *)si->base, si->size); return NULL; } @@ -1039,6 +1060,12 @@ unsigned unload_library(soinfo *si) } munmap((char *)si->base, si->size); + if (si->ba_index >= 0) { + PRINT("%5d releasing library '%s' address space at %08x "\ + "through buddy allocator.\n", + pid, si->name, si->base); + ba_free(si->ba_index); + } free_info(si); si->refcount = 0; } @@ -1105,13 +1132,25 @@ static int reloc_library(soinfo *si, Elf32_Rel *rel, unsigned count) switch(type){ #if defined(ANDROID_ARM_LINKER) case R_ARM_JUMP_SLOT: + COUNT_RELOC(RELOC_ABSOLUTE); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "%5d RELO JMP_SLOT %08x <- %08x %s\n", pid, + reloc, sym_addr, sym_name); + *((unsigned*)reloc) = sym_addr; + break; case R_ARM_GLOB_DAT: + COUNT_RELOC(RELOC_ABSOLUTE); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "%5d RELO GLOB_DAT %08x <- %08x %s\n", pid, + reloc, sym_addr, sym_name); + *((unsigned*)reloc) = sym_addr; + break; case R_ARM_ABS32: COUNT_RELOC(RELOC_ABSOLUTE); MARK(rel->r_offset); TRACE_TYPE(RELO, "%5d RELO ABS %08x <- %08x %s\n", pid, reloc, sym_addr, sym_name); - *((unsigned*)reloc) = sym_addr; + *((unsigned*)reloc) += sym_addr; break; #elif defined(ANDROID_X86_LINKER) case R_386_JUMP_SLOT: @@ -1563,13 +1602,13 @@ static int link_image(soinfo *si, unsigned wr_offset) } #endif - /* If this is a SETUID programme, dup /dev/null to openned stdin, + /* If this is a SET?ID program, dup /dev/null to opened stdin, stdout and stderr to close a security hole described in: ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-02:23.stdio.asc */ - if (getuid() != geteuid()) + if (getuid() != geteuid() || getgid() != getegid()) nullify_closed_stdio (); call_constructors(si); notify_gdb_of_load(si); @@ -1668,6 +1707,8 @@ unsigned __linker_init(unsigned **elfdata) vecs += 2; } + ba_init(); + si->base = 0; si->dynamic = (unsigned *)-1; si->wrprotect_start = 0xffffffff; diff --git a/linker/linker.h b/linker/linker.h index ab2c38566..d80c7614e 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -1,3 +1,31 @@ +/* + * 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. + */ + #ifndef _LINKER_H_ #define _LINKER_H_ @@ -67,6 +95,8 @@ struct soinfo unsigned entry; unsigned base; unsigned size; + // buddy-allocator index, negative for prelinked libraries + int ba_index; unsigned *dynamic; diff --git a/linker/linker_debug.h b/linker/linker_debug.h index fa2a1a1b1..3cc1343af 100644 --- a/linker/linker_debug.h +++ b/linker/linker_debug.h @@ -1,3 +1,31 @@ +/* + * 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. + */ + #ifndef _LINKER_DEBUG_H_ #define _LINKER_DEBUG_H_ @@ -7,8 +35,8 @@ * this on when submitting back to repository */ #define LINKER_DEBUG 0 #define TRACE_DEBUG 0 -#define DO_TRACE_LOOKUP 1 -#define DO_TRACE_RELO 1 +#define DO_TRACE_LOOKUP 0 +#define DO_TRACE_RELO 0 #define TIMING 0 #define STATS 0 #define COUNT_PAGES 0 diff --git a/linker/rt.c b/linker/rt.c index d0802ceab..30d5a4877 100644 --- a/linker/rt.c +++ b/linker/rt.c @@ -1,3 +1,31 @@ +/* + * 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. + */ + /* * This function is an empty stub where GDB locates a breakpoint to get notified * about linker activity.