liballocs icon indicating copy to clipboard operation
liballocs copied to clipboard

Document and track depended-on parts of libc interface, at least during startup

Open stephenrkell opened this issue 2 years ago • 4 comments

A recent issue I encountered on upgrading to glibc 2.36 is that unlike in earlier glibcs, environ was not yet initialized when liballocs startup code was running, so the logic for getting the auxv was broken. A quick fix was to use a different method for getting the auxv. But more generally we want to isolate ourselves from libc details like this. Can we itemise which parts of libc we use, and mitigate wherever we depend on any implementation-specific detail of them? Initialization order is a classic case here. (Potential for reentrancy is another of course, and some of that has been mitigated already.)

One mitigation in this example would be to define environ ourselves (overriding glibc's definition) and ensure that it gets initialized to something sensible really early on, e.g. to the envp vector that is on the initial stack. That might confuse glibc, however, if it assumes it owns the environ global. (Or in this case we could just snarf the auxv in allocsld, if we deem that the One True Way to launch any process using liballocs.)

If we can complete our startup before any other library's initializer is run -- this means before we call anything in libc! -- we would be doing something good.

stephenrkell avatar Nov 23 '23 06:11 stephenrkell

Possibly an extreme version of this change is for liballocs simply not to depend on (in a DT_NEEDED sense) libc at all. That is hard to achieve... e.g. it means not depending on libpthread or libdl. Still it would make conceptual sense.

stephenrkell avatar Nov 23 '23 07:11 stephenrkell

It might appear we could bootstrap this as follows: use fake_dlsym to get hold of dlsym, dlopen et al. However, this assumes they are present! And the only way to force that is to link with libdl, which links with libc.

The only non-GLIBC_PRIVATE symbols in glibc's ld.so on my machine are:

000000000002bfdc g    DO .rodata        0000000000000004  GLIBC_2.35  __rseq_flags
0000000000031a48 g    DO .data.rel.ro   0000000000000008  GLIBC_2.2.5 __libc_stack_end
0000000000033118 g    DO .bss   0000000000000028  GLIBC_2.2.5 _r_debug
0000000000014430 g    DF .text  000000000000003d  GLIBC_2.3   __tls_get_addr
00000000000309c8 g    DO .data.rel.ro   0000000000000008  GLIBC_2.35  __rseq_offset
0000000000025f30 g    DF .text  0000000000000001 (GLIBC_2.34) __rtld_version_placeholder
00000000000309d0 g    DO .data.rel.ro   0000000000000004  GLIBC_2.35  __rseq_size
000000000000d7f0 g    DF .text  0000000000000241  GLIBC_2.2.5 _dl_mcount

... which is pretty slim pickings.

stephenrkell avatar Nov 23 '23 07:11 stephenrkell

Still we could refrain from using symbols in libc, only using those in libdl and only directly depending on libdl (but transitively pulling in libc through that). That would be a fairly clean way to be.

stephenrkell avatar Nov 23 '23 07:11 stephenrkell

If we do not even DT_NEEDED-depend on libdl.so but have UND references to dlopen et al, can we still link and load? If so, that may be another way to go. The references could even be weak, so that allocsld can (somehow) take care of sourcing libdl.so if it's needed. That is a bit hacky of course, as it's special-casing the linking of certain symbols and a certain library. If allocsld.so were to participate in linking (rather than being entirely hidden, as at present) this could alll be made less hacky, i.e. by exposing its own dlopen et al and somehow ensuring it goes at the head of the link map (making any of its defined symbols non-preemptible! hmm...).

stephenrkell avatar Dec 01 '23 00:12 stephenrkell