[PATCH v3 03/13] loader: Refactor argv/envp/auxv management.

Jinoh Kang jinoh.kang.kr at gmail.com
Tue Jan 25 09:24:45 CST 2022


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 at gmail.com>
---

Notes:
    v1 -> v2:
    - Zero argc slot before writing to it
    - s/stackargs_eat_args/stackargs_shift_args/
    - s/shift_stackargs/stackargs_switch_stack/
    - s/offset/delta/
    - slightly change auxv append logic to match the original closer

 loader/preloader.c | 243 ++++++++++++++++++++++++++++++++-------------
 1 file changed, 173 insertions(+), 70 deletions(-)

diff --git a/loader/preloader.c b/loader/preloader.c
index 9675dc3f8eb..446e2f0e239 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -164,6 +164,21 @@ struct wld_auxv
     } a_un;
 };
 
+struct stackarg_info
+{
+    void *stack;
+    int argc;
+    char **argv;
+    char **envp;
+    struct wld_auxv *auxv;
+    struct wld_auxv *auxv_end;
+};
+
+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 +689,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 +835,145 @@ static void dump_auxiliary( struct wld_auxv *av )
 }
 #endif
 
+static void parse_stackargs( struct stackarg_info *outinfo, void *stack )
+{
+    int argc;
+    char **argv, **envp, **env_end;
+    struct wld_auxv *auxv, *auxv_end;
+
+    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;
+
+    auxv_end = auxv;
+    while ((auxv_end++)->a_type != AT_NULL)
+        ;
+
+    outinfo->stack = stack;
+    outinfo->argc = argc;
+    outinfo->argv = argv;
+    outinfo->envp = envp;
+    outinfo->auxv = auxv;
+    outinfo->auxv_end = auxv_end;
+}
+
+static char *stackargs_getenv( const struct stackarg_info *info, const char *name )
+{
+    char **envp = info->envp;
+    size_t namelen = wld_strlen( name );
+
+    while (*envp)
+    {
+        if (wld_strncmp( *envp, name, namelen ) == 0 &&
+            (*envp)[namelen] == '=') return *envp + namelen + 1;
+        envp++;
+    }
+    return NULL;
+}
+
+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;
+}
+
+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_end = (void *)((unsigned long)oldinfo->auxv_end + delta);
+}
+
 /*
  * set_auxiliary_values
  *
  * Set the new auxiliary values
  */
-static void set_auxiliary_values( struct wld_auxv *av, const struct wld_auxv *new_av,
-                                  const struct wld_auxv *delete_av, void **stack )
+static void set_auxiliary_values( struct preloader_state *state,
+                                  const struct wld_auxv *new_av,
+                                  const struct wld_auxv *delete_av )
 {
-    int i, j, av_count = 0, new_count = 0, delete_count = 0;
-    char *src, *dst;
-
-    /* count how many aux values we have already */
-    while (av[av_count].a_type != AT_NULL) av_count++;
+    size_t i, new_count = 0, delete_count = 0;
+    unsigned long dst;
+    struct wld_auxv *avpd, *avps, *avp;
+    int is_deleted;
 
     /* delete unwanted values */
-    for (j = 0; delete_av[j].a_type != AT_NULL; j++)
+    for (avps = avpd = state->s.auxv; avps + 1 != state->s.auxv_end; avps++)
     {
-        for (i = 0; i < av_count; i++) if (av[i].a_type == delete_av[j].a_type)
+        is_deleted = 0;
+        for (i = 0; delete_av[i].a_type != AT_NULL; i++)
+        {
+            if (avps->a_type == new_av[i].a_type)
+            {
+                is_deleted = 1;
+                break;
+            }
+        }
+        if (is_deleted)
         {
-            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;
+            continue;
         }
+        if (avpd != avps)
+        {
+            avpd->a_type = avps->a_type;
+            avpd->a_un.a_val = avps->a_un.a_val;
+        }
+        avpd++;
     }
+    avpd->a_type = AT_NULL;
+    avpd->a_un.a_val = 0;
+    state->s.auxv_end = avpd + 1;
 
     /* 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++)
+    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) new_count++;
+        for (avp = state->s.auxv; avp + 1 != state->s.auxv_end; avp++) if (avp->a_type == new_av[i].a_type) break;
+        if (avp + 1 == state->s.auxv_end) new_count++;
     }
 
-    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 */
-    {
-        int len = (char *)(av + av_count + 1) - src;
-        for (i = 0; i < len; i++) dst[i] = src[i];
-    }
-    else if (dst > src)  /* get rid of unused values */
-    {
-        int len = (char *)(av + av_count + 1) - src;
-        for (i = len - 1; i >= 0; i--) dst[i] = src[i];
-    }
-    *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_end -
+                 (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;
+        for (avp = state->s.auxv; avp + 1 != state->s.auxv_end; avp++) if (avp->a_type == new_av[i].a_type) break;
+        if (avp + 1 != state->s.auxv_end) 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++;
+            avp->a_type     = new_av[i].a_type;
+            avp->a_un.a_val = new_av[i].a_un.a_val;
+            state->s.auxv_end++;
         }
     }
+    state->s.auxv_end[-1].a_type = AT_NULL;
+    state->s.auxv_end[-1].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 +1483,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] );
+    parse_stackargs( &state.s, *stack );
 
-    /* skip over the parameters */
-    p = argv + *pargc + 1;
+    if (state.s.argc < 2) fatal_error( "Usage: %s wine_binary [args]\n", state.s.argv[0] );
 
-    /* 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++;
-    }
-
-    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 +1539,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 +1556,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 +1571,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 +1592,7 @@ void* wld_start( void **stack )
     }
 #endif
 
+    *stack = state.s.stack;
     return (void *)ld_so_map.l_entry;
 }
 
-- 
2.31.1




More information about the wine-devel mailing list