Просмотр исходного кода

pagesize: restore PAGE_SHIFT fallback for binfmt_flat (no auxv)

binfmt_flat builds the initial process stack from only argc/argv/envp;
it does not place an auxiliary vector. Commit 487af1498 ("drop
__pagesize, make _dl_pagesize the single source of truth") assumed
AT_PAGESZ is always supplied by the kernel and changed the _dl_pagesize
fallback from PAGE_SIZE to 0. On noMMU/FLAT targets that leaves
_dl_pagesize == 0, so every mmap-based malloc rounds its length to 0,
mmap() fails with EINVAL and the first allocation aborts -- busybox dies
with "init: out of memory" before it can run anything.

Restore the fallback that arc/nds32 bits/uClibc_page.h have always
documented ("_dl_pagesize = auxvt[AT_PAGESZ].a_un.a_val ? : PAGE_SIZE"),
expressed as (1UL << PAGE_SHIFT) so it keeps working now that PAGE_SIZE
is no longer defined in <bits/uClibc_page.h>:

  - Seed _dl_pagesize with (1UL << PAGE_SHIFT) at its definition in the
    two translation units that own the symbol: the dynamic linker
    (ldso.c, used by ld.so) and the static-libc support (dl-support.c).
  - Override it from AT_PAGESZ only when that entry is actually present
    (a_type == AT_PAGESZ) and non-zero, instead of unconditionally
    resetting it to 0.

libdl.c (!SHARED) previously also defined _dl_pagesize. That was
harmless while dl-support.c's definition was tentative (the two merged),
but giving both a non-zero initializer makes them two strong .data
definitions, which collide as "multiple definition of `_dl_pagesize'"
when a static link pulls both objects out of libc.a (e.g. NPTL builds
that drag in libdl). Drop the redundant libdl.c definition: dl-support.c
is always co-linked from libc.a and provides the symbol.

For FLAT, skip the aux-vector scan in __uClibc_main() altogether: there
is nothing valid past the envp NULL, so _dl_aux_init() is not called and
_dl_auxv_start stays NULL. Guard getauxval()'s slow-path loop against
that NULL so a lookup of an uncached (>= AUX_MAX_AT_ID) tag returns
ENOENT instead of dereferencing NULL.

Verified on riscv32 noMMU/FLAT (gcc 15.2.0, linux 6.18.33): busybox
boots as init and the testsuite runs (was "init: out of memory"
before). Dynamic + NPTL links are unaffected -- x86_64 NPTL static
busybox links cleanly and ld-uClibc loads the testsuite.

Signed-off-by: Ramin Moussavi <ramin.moussavi@yacoub.de>
Ramin Moussavi 1 месяц назад
Родитель
Сommit
7730f0d05a
5 измененных файлов с 16 добавлено и 10 удалено
  1. 4 3
      ldso/ldso/ldso.c
  2. 1 1
      ldso/libdl/libdl.c
  3. 2 1
      libc/misc/auxvt/getauxval.c
  4. 4 3
      libc/misc/elf/dl-support.c
  5. 5 2
      libc/misc/internals/__uClibc_main.c

+ 4 - 3
ldso/ldso/ldso.c

@@ -52,7 +52,7 @@ char *_dl_library_path         = NULL;	/* Where we look for libraries */
 char *_dl_preload              = NULL;	/* Things to be loaded before the libs */
 char *_dl_preload              = NULL;	/* Things to be loaded before the libs */
 #endif
 #endif
 int _dl_errno                  = 0;	/* We can't use the real errno in ldso */
 int _dl_errno                  = 0;	/* We can't use the real errno in ldso */
-size_t _dl_pagesize            = 0;	/* Store the page size for use later */
+size_t _dl_pagesize            = (1UL << PAGE_SHIFT);	/* fallback; overridden from AT_PAGESZ if present */
 struct r_debug *_dl_debug_addr = NULL;	/* Used to communicate with the gdb debugger */
 struct r_debug *_dl_debug_addr = NULL;	/* Used to communicate with the gdb debugger */
 void *(*_dl_malloc_function) (size_t size) = NULL;
 void *(*_dl_malloc_function) (size_t size) = NULL;
 void (*_dl_free_function) (void *p) = NULL;
 void (*_dl_free_function) (void *p) = NULL;
@@ -460,8 +460,9 @@ void *_dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr,
 
 
 	_dl_memset(app_tpnt, 0, sizeof(*app_tpnt));
 	_dl_memset(app_tpnt, 0, sizeof(*app_tpnt));
 
 
-	/* Store the page size for later use */
-	_dl_pagesize = (_dl_auxvt[AT_PAGESZ].a_un.a_val) ? (size_t) _dl_auxvt[AT_PAGESZ].a_un.a_val : 0;
+	/* Override the fallback only if AT_PAGESZ is present and non-zero */
+	if (_dl_auxvt[AT_PAGESZ].a_type == AT_PAGESZ && _dl_auxvt[AT_PAGESZ].a_un.a_val)
+		_dl_pagesize = (size_t) _dl_auxvt[AT_PAGESZ].a_un.a_val;
 	/* Make it so _dl_malloc can use the page of memory we have already
 	/* Make it so _dl_malloc can use the page of memory we have already
 	 * allocated.  We shouldn't need to grab any more memory.  This must
 	 * allocated.  We shouldn't need to grab any more memory.  This must
 	 * be first since things like _dl_dprintf() use _dl_malloc()...
 	 * be first since things like _dl_dprintf() use _dl_malloc()...

+ 1 - 1
ldso/libdl/libdl.c

@@ -109,7 +109,7 @@ void (*_dl_free_function) (void *p);
 char *_dl_library_path         = NULL;         /* Where we look for libraries */
 char *_dl_library_path         = NULL;         /* Where we look for libraries */
 #endif
 #endif
 int _dl_errno                  = 0;         /* We can't use the real errno in ldso */
 int _dl_errno                  = 0;         /* We can't use the real errno in ldso */
-size_t _dl_pagesize            = 0; /* Store the page size for use later */
+/* _dl_pagesize is provided by dl-support.c (always co-linked from libc.a) */
 /* This global variable is also to communicate with debuggers such as gdb. */
 /* This global variable is also to communicate with debuggers such as gdb. */
 struct r_debug *_dl_debug_addr = NULL;
 struct r_debug *_dl_debug_addr = NULL;
 
 

+ 2 - 1
libc/misc/auxvt/getauxval.c

@@ -33,7 +33,8 @@ unsigned long int __getauxval (unsigned long int __type)
 	}
 	}
 
 
 	// Otherwise we have to iterate the auxiliary vector.
 	// Otherwise we have to iterate the auxiliary vector.
-	for (ElfW(auxv_t) *entry = _dl_auxv_start; entry->a_type != AT_NULL; entry++)
+	// _dl_auxv_start is NULL when there is no aux vector (e.g. FLAT).
+	for (ElfW(auxv_t) *entry = _dl_auxv_start; entry && entry->a_type != AT_NULL; entry++)
 		if (entry->a_type == __type)
 		if (entry->a_type == __type)
 			return entry->a_un.a_val;
 			return entry->a_un.a_val;
 
 

+ 4 - 3
libc/misc/elf/dl-support.c

@@ -30,7 +30,7 @@ void (*_dl_init_static_tls) (struct link_map *) = &_dl_nothread_init_static_tls;
 
 
 ElfW(Phdr) *_dl_phdr;
 ElfW(Phdr) *_dl_phdr;
 size_t _dl_phnum;
 size_t _dl_phnum;
-size_t _dl_pagesize;
+size_t _dl_pagesize = (1UL << PAGE_SHIFT);	/* fallback; overridden from AT_PAGESZ if present */
 
 
 ElfW(auxv_t) _dl_auxvt[AUX_MAX_AT_ID];
 ElfW(auxv_t) _dl_auxvt[AUX_MAX_AT_ID];
 ElfW(auxv_t) *_dl_auxv_start;
 ElfW(auxv_t) *_dl_auxv_start;
@@ -52,8 +52,9 @@ void internal_function _dl_aux_init (ElfW(auxv_t) *av)
    /* Get the number of program headers from the aux vect */
    /* Get the number of program headers from the aux vect */
    _dl_phnum = (size_t) _dl_auxvt[AT_PHNUM].a_un.a_val;
    _dl_phnum = (size_t) _dl_auxvt[AT_PHNUM].a_un.a_val;
 
 
-   /* Get the pagesize from the aux vect */
-   _dl_pagesize = (_dl_auxvt[AT_PAGESZ].a_un.a_val) ? (size_t) _dl_auxvt[AT_PAGESZ].a_un.a_val : 0;
+   /* Override the fallback only if AT_PAGESZ is present and non-zero */
+   if (_dl_auxvt[AT_PAGESZ].a_type == AT_PAGESZ && _dl_auxvt[AT_PAGESZ].a_un.a_val)
+      _dl_pagesize = (size_t) _dl_auxvt[AT_PAGESZ].a_un.a_val;
 }
 }
 
 
 #if defined(USE_TLS) && USE_TLS
 #if defined(USE_TLS) && USE_TLS

+ 5 - 2
libc/misc/internals/__uClibc_main.c

@@ -369,7 +369,7 @@ void __uClibc_main(int (*main)(int, char **, char **), int argc,
 		    char **argv, void (*app_init)(void), void (*app_fini)(void),
 		    char **argv, void (*app_init)(void), void (*app_fini)(void),
 		    void (*rtld_fini)(void), void *stack_end attribute_unused)
 		    void (*rtld_fini)(void), void *stack_end attribute_unused)
 {
 {
-#ifndef SHARED
+#if !defined(SHARED) && !defined(__UCLIBC_FORMAT_FLAT__)
     unsigned long *aux_dat;
     unsigned long *aux_dat;
 #endif
 #endif
 
 
@@ -395,7 +395,9 @@ void __uClibc_main(int (*main)(int, char **, char **), int argc,
     }
     }
 
 
 #ifndef SHARED
 #ifndef SHARED
-    /* Pull stuff from the ELF header when possible */
+# ifndef __UCLIBC_FORMAT_FLAT__
+    /* Pull stuff from the ELF header when possible. binfmt_flat passes no
+       aux vector, so skip this for FLAT. */
     aux_dat = (unsigned long*)__environ;
     aux_dat = (unsigned long*)__environ;
     while (*aux_dat) {
     while (*aux_dat) {
 	aux_dat++;
 	aux_dat++;
@@ -404,6 +406,7 @@ void __uClibc_main(int (*main)(int, char **, char **), int argc,
     /* Get the program headers (_dl_phdr) from the aux vector
     /* Get the program headers (_dl_phdr) from the aux vector
        It will be used into __libc_setup_tls. */
        It will be used into __libc_setup_tls. */
     _dl_aux_init ((ElfW(auxv_t) *)aux_dat);
     _dl_aux_init ((ElfW(auxv_t) *)aux_dat);
+# endif
 #endif
 #endif
 
 
     /* We need to initialize uClibc.  If we are dynamically linked this
     /* We need to initialize uClibc.  If we are dynamically linked this