From: Jinoh Kang jinoh.kang.kr@gmail.com
Collect scattered variables holding stack addresses (e.g. pargc, argv, envp, auxv) in one place.
This facilitates modifying stack values (e.g. removing argv[0], switching stacks due to address conflict with reserved regions) without leaving pointer variables stale.
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- loader/preloader.c | 309 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 236 insertions(+), 73 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c index 585be50624f..7b69046238a 100644 --- a/loader/preloader.c +++ b/loader/preloader.c @@ -164,6 +164,25 @@ struct wld_auxv } a_un; };
+/* Aggregates information about initial program stack and variables + * (e.g. argv and envp) that reside in it. + */ +struct stackarg_info +{ + void *stack; + int argc; + char **argv; + char **envp; + struct wld_auxv *auxv; + struct wld_auxv *auxv_last; +}; + +/* Currently only contains the main stackarg_info. */ +struct preloader_state +{ + struct stackarg_info s; +}; + /* * The __bb_init_func is an empty function only called when file is * compiled with gcc flags "-fprofile-arcs -ftest-coverage". This @@ -674,6 +693,32 @@ static inline void *wld_memset( void *dest, int val, size_t len ) return dest; }
+static size_t wld_strlen( const char *str ) +{ + const char *ptr = str; + while (*ptr) ptr++; + return ptr - str; +} + +static inline void *wld_memmove( void *dest, const void *src, size_t len ) +{ + unsigned char *destp = dest; + const unsigned char *srcp = src; + + if ((unsigned long)dest - (unsigned long)src < len) + { + destp += len; + srcp += len; + while (len--) *--destp = *--srcp; + } + else + { + while (len--) *destp++ = *srcp++; + } + + return dest; +} + /* * wld_printf - just the basics * @@ -794,72 +839,201 @@ static void dump_auxiliary( struct wld_auxv *av ) } #endif
-/* - * set_auxiliary_values +/* find_auxv_last_entry * - * Set the new auxiliary values + * Get the last auxv entry (AT_NULL). */ -static void set_auxiliary_values( struct wld_auxv *av, const struct wld_auxv *new_av, - const struct wld_auxv *delete_av, void **stack ) +struct wld_auxv *find_auxv_last_entry( const struct wld_auxv *auxv ) { - int i, j, av_count = 0, new_count = 0, delete_count = 0; - char *src, *dst; + const struct wld_auxv *ptr;
- /* count how many aux values we have already */ - while (av[av_count].a_type != AT_NULL) av_count++; + for (ptr = auxv; ptr->a_type != AT_NULL; ptr++) + ;
- /* delete unwanted values */ - for (j = 0; delete_av[j].a_type != AT_NULL; j++) + return (struct wld_auxv *)ptr; +} + +/* find_auxv_entry + * + * Find the first auxv entry that has the given type, or NULL if no such entry + * is found. + */ +static struct wld_auxv *find_auxv_entry( const struct wld_auxv *auxv, + const struct wld_auxv *auxv_last, + ElfW(Addr) type ) +{ + const struct wld_auxv *ptr; + + for (ptr = auxv; ptr != auxv_last; ptr++) { - for (i = 0; i < av_count; i++) if (av[i].a_type == delete_av[j].a_type) - { - av[i].a_type = av[av_count-1].a_type; - av[i].a_un.a_val = av[av_count-1].a_un.a_val; - av[--av_count].a_type = AT_NULL; - delete_count++; - break; - } + if (ptr->a_type == type) return (struct wld_auxv *)ptr; }
- /* count how many values we have in new_av that aren't in av */ - for (j = 0; new_av[j].a_type != AT_NULL; j++) + return NULL; +} + +/* + * parse_stackargs + * + * parse out the initial stack for argv, envp, and etc., and store the + * information into the given stackarg_info structure. + */ +static void parse_stackargs( struct stackarg_info *outinfo, void *stack ) +{ + int argc; + char **argv, **envp, **env_end; + struct wld_auxv *auxv; + + argc = *(int *)stack; + argv = (char **)stack + 1; + envp = argv + (unsigned int)argc + 1; + + env_end = envp; + while (*env_end++) + ; + auxv = (struct wld_auxv *)env_end; + + outinfo->stack = stack; + outinfo->argc = argc; + outinfo->argv = argv; + outinfo->envp = envp; + outinfo->auxv = auxv; + outinfo->auxv_last = find_auxv_last_entry( auxv ); +} + +/* + * stackargs_getenv + * + * Retrieve the value of an environment variable from stackarg_info. + */ +static char *stackargs_getenv( const struct stackarg_info *info, const char *name ) +{ + char **envp = info->envp; + size_t namelen = wld_strlen( name ); + + while (*envp) { - for (i = 0; i < av_count; i++) if (av[i].a_type == new_av[j].a_type) break; - if (i == av_count) new_count++; + if (wld_strncmp( *envp, name, namelen ) == 0 && + (*envp)[namelen] == '=') return *envp + namelen + 1; + envp++; } + return NULL; +} + +/* + * stackargs_shift_args + * + * Remove the specific number of arguments from the start of argv. + */ +static void stackargs_shift_args( struct stackarg_info *info, int num_args ) +{ + info->stack = (char **)info->stack + num_args; + info->argc -= num_args; + info->argv = (char **)info->stack + 1; + + wld_memset( info->stack, 0, sizeof(char *) ); + /* Don't coalesce zeroing and setting argc -- we *might* support big endian in the future */ + *(int *)info->stack = info->argc; +} + +/* + * stackargs_switch_stack + * + * Fix up variables in oldinfo to the given stack base, and return + * the new information to newinfo (does not modify oldinfo). + */ +static void stackargs_switch_stack( struct stackarg_info *newinfo, struct stackarg_info *oldinfo, void *newstack ) +{ + unsigned long delta = (unsigned long)newstack - (unsigned long)oldinfo->stack; + + /* NOTE it is legal that newinfo == oldinfo */ + newinfo->stack = newstack; + newinfo->argc = oldinfo->argc; + newinfo->argv = (void *)((unsigned long)oldinfo->argv + delta); + newinfo->envp = (void *)((unsigned long)oldinfo->envp + delta); + newinfo->auxv = (void *)((unsigned long)oldinfo->auxv + delta); + newinfo->auxv_last = (void *)((unsigned long)oldinfo->auxv_last + delta); +} + +/* delete_auxv_entries + * + * Delete specified types from the given auxiliary vector. + * Returns the address of the last entry of the vector. + */ +static struct wld_auxv *delete_auxv_entries( struct wld_auxv *auxv, + struct wld_auxv *auxv_last, + const struct wld_auxv *delete_av ) +{ + struct wld_auxv *dstp = auxv, *srcp; + struct wld_auxv *delete_av_end = find_auxv_last_entry( delete_av );
- src = (char *)*stack; - dst = src - (new_count - delete_count) * sizeof(*av); - dst = (char *)((unsigned long)dst & ~15); - if (dst < src) /* need to make room for the extra values */ + for (srcp = auxv; srcp != auxv_last; srcp++) { - int len = (char *)(av + av_count + 1) - src; - for (i = 0; i < len; i++) dst[i] = src[i]; + if (!find_auxv_entry( delete_av, delete_av_end, srcp->a_type )) + { + if (dstp != srcp) + { + dstp->a_type = srcp->a_type; + dstp->a_un.a_val = srcp->a_un.a_val; + } + dstp++; + } } - else if (dst > src) /* get rid of unused values */ + + dstp->a_type = AT_NULL; + dstp->a_un.a_val = 0; + return dstp; +} + +/* + * set_auxiliary_values + * + * Set the new auxiliary values + */ +static void set_auxiliary_values( struct preloader_state *state, + const struct wld_auxv *new_av, + const struct wld_auxv *delete_av ) +{ + size_t i, new_count = 0, delete_count; + unsigned long dst; + struct wld_auxv *avp, *new_auxv_last; + + /* delete unwanted values */ + new_auxv_last = delete_auxv_entries( state->s.auxv, state->s.auxv_last, delete_av ); + delete_count = state->s.auxv_last - new_auxv_last; + state->s.auxv_last = new_auxv_last; + + /* count how many values we have in new_av that aren't in av */ + for (i = 0; new_av[i].a_type != AT_NULL; i++) { - int len = (char *)(av + av_count + 1) - src; - for (i = len - 1; i >= 0; i--) dst[i] = src[i]; + if (find_auxv_entry( state->s.auxv, state->s.auxv_last, new_av[i].a_type )) new_count++; } - *stack = dst; - av = (struct wld_auxv *)((char *)av + (dst - src)); + + dst = ((unsigned long)state->s.stack - + (new_count - delete_count) * sizeof(struct wld_auxv)) & ~15; + wld_memmove( (void *)dst, state->s.stack, + (unsigned long)(state->s.auxv_last + 1) - + (unsigned long)state->s.stack ); + stackargs_switch_stack( &state->s, &state->s, (void *)dst );
/* now set the values */ - for (j = 0; new_av[j].a_type != AT_NULL; j++) + for (i = 0; new_av[i].a_type != AT_NULL; i++) { - for (i = 0; i < av_count; i++) if (av[i].a_type == new_av[j].a_type) break; - if (i < av_count) av[i].a_un.a_val = new_av[j].a_un.a_val; + avp = find_auxv_entry( state->s.auxv, state->s.auxv_last, new_av[i].a_type ); + if (avp) avp->a_un.a_val = new_av[i].a_un.a_val; else { - av[av_count].a_type = new_av[j].a_type; - av[av_count].a_un.a_val = new_av[j].a_un.a_val; - av_count++; + state->s.auxv_last->a_type = new_av[i].a_type; + state->s.auxv_last->a_un.a_val = new_av[i].a_un.a_val; + state->s.auxv_last++; } } + state->s.auxv_last->a_type = AT_NULL; + state->s.auxv_last->a_un.a_val = 0;
#ifdef DUMP_AUX_INFO wld_printf("New auxiliary info:\n"); - dump_auxiliary( av ); + dump_auxiliary( state->s.auxv ); #endif }
@@ -1369,47 +1543,36 @@ static void set_process_name( int argc, char *argv[] ) */ void* wld_start( void **stack ) { - long i, *pargc; - char **argv, **p; - char *interp, *reserve = NULL; - struct wld_auxv new_av[8], delete_av[3], *av; + long i; + char *interp, *reserve; + struct wld_auxv new_av[8], delete_av[3]; struct wld_link_map main_binary_map, ld_so_map; struct wine_preload_info **wine_main_preload_info; + struct preloader_state state = { 0 };
- pargc = *stack; - argv = (char **)pargc + 1; - if (*pargc < 2) fatal_error( "Usage: %s wine_binary [args]\n", argv[0] ); - - /* skip over the parameters */ - p = argv + *pargc + 1; + parse_stackargs( &state.s, *stack );
- /* skip over the environment */ - while (*p) - { - static const char res[] = "WINEPRELOADRESERVE="; - if (!wld_strncmp( *p, res, sizeof(res)-1 )) reserve = *p + sizeof(res) - 1; - p++; - } + if (state.s.argc < 2) fatal_error( "Usage: %s wine_binary [args]\n", state.s.argv[0] );
- av = (struct wld_auxv *)(p+1); - page_size = get_auxiliary( av, AT_PAGESZ, 4096 ); + page_size = get_auxiliary( state.s.auxv, AT_PAGESZ, 4096 ); page_mask = page_size - 1;
preloader_start = (char *)_start - ((unsigned long)_start & page_mask); preloader_end = (char *)((unsigned long)(_end + page_mask) & ~page_mask);
#ifdef DUMP_AUX_INFO - wld_printf( "stack = %p\n", *stack ); - for( i = 0; i < *pargc; i++ ) wld_printf("argv[%lx] = %s\n", i, argv[i]); - dump_auxiliary( av ); + wld_printf( "stack = %p\n", state.s.stack ); + for( i = 0; i < state.s.argc; i++ ) wld_printf("argv[%lx] = %s\n", i, state.s.argv[i]); + dump_auxiliary( state.s.auxv ); #endif
/* reserve memory that Wine needs */ + reserve = stackargs_getenv( &state.s, "WINEPRELOADRESERVE" ); if (reserve) preload_reserve( reserve ); for (i = 0; preload_info[i].size; i++) { - if ((char *)av >= (char *)preload_info[i].addr && - (char *)pargc <= (char *)preload_info[i].addr + preload_info[i].size) + if ((char *)state.s.auxv >= (char *)preload_info[i].addr && + (char *)state.s.stack <= (char *)preload_info[i].addr + preload_info[i].size) { remove_preload_range( i ); i--; @@ -1436,7 +1599,7 @@ void* wld_start( void **stack ) wld_mprotect( (char *)0x80000000 - page_size, page_size, PROT_EXEC | PROT_READ );
/* load the main binary */ - map_so_lib( argv[1], &main_binary_map ); + map_so_lib( state.s.argv[1], &main_binary_map );
/* load the ELF interpreter */ interp = (char *)main_binary_map.l_addr + main_binary_map.l_interp; @@ -1453,14 +1616,14 @@ void* wld_start( void **stack ) SET_NEW_AV( 2, AT_PHNUM, main_binary_map.l_phnum ); SET_NEW_AV( 3, AT_PAGESZ, page_size ); SET_NEW_AV( 4, AT_BASE, ld_so_map.l_addr ); - SET_NEW_AV( 5, AT_FLAGS, get_auxiliary( av, AT_FLAGS, 0 ) ); + SET_NEW_AV( 5, AT_FLAGS, get_auxiliary( state.s.auxv, AT_FLAGS, 0 ) ); SET_NEW_AV( 6, AT_ENTRY, main_binary_map.l_entry ); SET_NEW_AV( 7, AT_NULL, 0 ); #undef SET_NEW_AV
i = 0; /* delete sysinfo values if addresses conflict */ - if (is_in_preload_range( av, AT_SYSINFO ) || is_in_preload_range( av, AT_SYSINFO_EHDR )) + if (is_in_preload_range( state.s.auxv, AT_SYSINFO ) || is_in_preload_range( state.s.auxv, AT_SYSINFO_EHDR )) { delete_av[i++].a_type = AT_SYSINFO; delete_av[i++].a_type = AT_SYSINFO_EHDR; @@ -1468,14 +1631,13 @@ void* wld_start( void **stack ) delete_av[i].a_type = AT_NULL;
/* get rid of first argument */ - set_process_name( *pargc, argv ); - pargc[1] = pargc[0] - 1; - *stack = pargc + 1; + set_process_name( state.s.argc, state.s.argv ); + stackargs_shift_args( &state.s, 1 );
- set_auxiliary_values( av, new_av, delete_av, stack ); + set_auxiliary_values( &state, new_av, delete_av );
#ifdef DUMP_AUX_INFO - wld_printf("new stack = %p\n", *stack); + wld_printf("new stack = %p\n", state.s.stack); wld_printf("jumping to %p\n", (void *)ld_so_map.l_entry); #endif #ifdef DUMP_MAPS @@ -1490,6 +1652,7 @@ void* wld_start( void **stack ) } #endif
+ *stack = state.s.stack; return (void *)ld_so_map.l_entry; }