libsanitizer: drop __pagesize, make _dl_pagesize the single source of truth
uClibc-ng's getpagesize() returned __pagesize, a libc-local variable
that is only initialised by libc.so.0's DT_INIT (__uClibc_init), which
runs *after* the DT_INIT of every earlier-loaded shared object. In
particular it runs after the constructors of libasan/libbacktrace, whose
symbolizer calls getpagesize() to mmap() its first work page. With
__pagesize still 0 the mmap fails with EINVAL, ASan loses symbolization
for every stack frame, and many ASan testsuite cases (e.g. pr69276.C)
fail spuriously.
The dynamic linker, however, already populates _dl_pagesize from
AT_PAGESZ before any DT_INIT runs, and exports it as a dynamic symbol.
glibc takes advantage of this and implements getpagesize() as simply
`return GLRO(dl_pagesize);' (sysdeps/unix/sysv/linux/getpagesize.c).
Do the same here.
While we are at it, get rid of __pagesize entirely. Keeping it would
mean maintaining two parallel page-size variables with subtly different
lifetimes -- the long-standing source of this bug and the cause of a
second, more nasty failure mode:
The original re-entry guard in __uClibc_init was
if (__pagesize) return;
__pagesize = PAGE_SIZE;
so any compile-time initialiser other than 0 on __pagesize turns
the whole libc init into a no-op (_locale_init, _stdio_init, ...
are skipped, MB_CUR_MAX stays 0, setlocale/regexec crash). We hit
exactly this trap during an earlier revision of this patch and
spent a long time chasing it -- replacing the sentinel with a
dedicated `static int __uClibc_initialized' makes the guard
obvious and removes that footgun forever.
The few in-tree readers of __pagesize (libc/inet/ifaddrs.c,
libc/misc/internals/parse_config.c, getpagesize.c itself) are
converted to _dl_pagesize. Internal users of the compile-time
PAGE_SIZE constant in the ldso (ldso/ldso/ldso.c, ldso/libdl/libdl.c,
libc/misc/elf/dl-support.c, ldso/ldso/arm/elfinterp.c) likewise read
_dl_pagesize, leaving the AT_PAGESZ value from the auxiliary vector
as the single source of truth. PAGE_SIZE and PAGE_MASK macros are
removed from <bits/uClibc_page.h>; PAGE_SHIFT is kept (still used as
a compile-time constant by libc/stdlib/malloc-standard/malloc.h's
PROTECT_PTR pointer-mangling macros, and by xtensa -- see below).
Xtensa is special: INIT_GOT and PERFORM_BOOTSTRAP_GOT in
ldso/ldso/xtensa/dl-sysdep.h and dl-startup.h need page-granular
mprotect() because the xtensa GOT lives interleaved with code
(literal pools) and must be temporarily flipped to RWX. These
macros run before the GOT itself is patched, so _dl_pagesize is not
yet reachable. They are rewritten to use (1UL << PAGE_SHIFT)
inline, which keeps PAGE_SIZE out of the xtensa code paths entirely
while still using a compile-time constant where one is required.
The static-link case is covered: ldso/libdl/libdl.c is compiled into
libc.a, _dl_pagesize is therefore present in static binaries, and
_dl_aux_init() runs from __uClibc_main() before any user constructors,
so getpagesize() returns the correct value from the very first call.
The sys/user.h NBPG/UPAGES cleanup that becomes necessary once
PAGE_SIZE is gone from <bits/uClibc_page.h> is split out into a
follow-up patch ("sys/user.h: remove obsolete BFD trad-core
NBPG/UPAGES defines").
Verified on arm-bbs-linux-uclibcgnueabihf:
* mb_probe: MB_CUR_MAX == 1 (was 0 with the broken sentinel)
* regex_repro (REG_STARTEND with embedded NUL): match found
* pagesize_static, mb_probe_static, regex_repro_static: identical
behaviour shared vs. static
* gcc.dg/asan/pr69276.C: ASan stack frames are symbolised
Xtensa is not exercised on the build host; the change is textually
mechanical: PAGE_SIZE -> (1UL << PAGE_SHIFT).
Signed-off-by: Ramin Moussavi <ramin.moussavi@yacoub.de>