Page MenuHomeFreeBSD

rtld-elf: Fix executable's TLS module index for direct exec
ClosedPublic

Authored by jrtc27 on May 5 2025, 11:19 PM.
Tags
None
Referenced Files
Unknown Object (File)
Mon, Jul 14, 9:35 AM
Unknown Object (File)
Sat, Jul 5, 9:25 PM
Unknown Object (File)
Sat, Jul 5, 3:59 PM
Unknown Object (File)
Thu, Jun 26, 10:01 PM
Unknown Object (File)
Sun, Jun 22, 10:39 AM
Unknown Object (File)
Thu, Jun 19, 10:52 AM
Unknown Object (File)
Sun, Jun 15, 2:28 PM
Unknown Object (File)
Jun 14 2025, 7:35 AM
Subscribers

Details

Summary

For direct exec mode we reuse map_object, but tls_max_index is
initialised to 1. As a result, the executable ends up being assigned
module 2 (and the generation is pointlessly incremented, unlike in
digest_phdr for the normal case). For most architectures this is
harmless, since TLS linker relaxation will optimise General Dynamic
accesses to Initial Exec or Local Exec for executables, but on RISC-V
this relaxation does not exist, yet the linker will initialise the
tls_index in the GOT with module 1, and at run time the call to
__tls_get_addr will fail with:

ld-elf.so.1: Can't find module with TLS index 1

Fix this by making map_object use 1 for obj->tlsindex when it's loading
the main executable, and don't bother to increment tls_dtv_generation
either, matching digest_phdr (though that one is harmless).

(Note this also applies to MIPS on stable/13)

Fixes: 0fc65b0ab82c ("Make ld-elf.so.1 directly executable.")
MFC after: 1 week

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

libexec/rtld-elf/map_object.c
63

I don't love this design, it's not something rtld normally does (normally it'd do obj == &obj_main, but obj_main is still NULL here as map_object itself allocates the Obj_Entry), but it's simple and works. Happy to change it if you have other ideas.

Or instead, re-set the tls_max_index to 0 for direct exec mode before calling map_object()?

libexec/rtld-elf/map_object.c
63

If obj_main is made global, then ismain == (obj_main == NULL). I do not see anything awful with the ismain argument.

In D50186#1144828, @kib wrote:

Or instead, re-set the tls_max_index to 0 for direct exec mode before calling map_object()?

Then this would also require a fixup after the map_object() call, for the case where the main obj does not have TLS init segment.

In D50186#1144833, @kib wrote:
In D50186#1144828, @kib wrote:

Or instead, re-set the tls_max_index to 0 for direct exec mode before calling map_object()?

Then this would also require a fixup after the map_object() call, for the case where the main obj does not have TLS init segment.

I considered that but it felt uglier.

This revision is now accepted and ready to land.May 6 2025, 5:27 PM