@@ -1351,7 +1351,7 @@ void doDestruct(SharedObject *object) {
13511351// --------------------------------------------------------
13521352
13531353RuntimeTlsMap::RuntimeTlsMap ()
1354- : initialPtr{0 }, initialLimit{0 }, indices{getAllocator ()} { }
1354+ : initialPtr{0 }, initialLimit{0 }, indices{getAllocator ()}, tcbs{ getAllocator ()} { }
13551355
13561356void initTlsObjects (Tcb *tcb, const frg::vector<SharedObject *, MemoryAllocator> &objects, bool checkInitialized) {
13571357 // Initialize TLS segments that follow the static model.
@@ -1437,64 +1437,48 @@ Tcb *allocateTcb() {
14371437 memset (tcb_ptr->dtvPointers , 0 , sizeof (void *) * runtimeTlsMap->indices .size ());
14381438 for (size_t i = 0 ; i < runtimeTlsMap->indices .size (); ++i) {
14391439 auto object = runtimeTlsMap->indices [i];
1440- if (object->tlsModel != TlsModel::initial)
1441- continue ;
1442-
1443- if constexpr (tlsAboveTp) {
1444- tcb_ptr->dtvPointers [i] = reinterpret_cast <char *>(tcb_ptr) + sizeof (Tcb) + object->tlsOffset ;
1445- } else {
1446- tcb_ptr->dtvPointers [i] = reinterpret_cast <char *>(tcb_ptr) + object->tlsOffset ;
1440+ if (object->tlsModel == TlsModel::initial) {
1441+ if constexpr (tlsAboveTp) {
1442+ tcb_ptr->dtvPointers [i] = reinterpret_cast <char *>(tcb_ptr) + sizeof (Tcb) + object->tlsOffset ;
1443+ } else {
1444+ tcb_ptr->dtvPointers [i] = reinterpret_cast <char *>(tcb_ptr) + object->tlsOffset ;
1445+ }
1446+ } else if (object->tlsModel == TlsModel::dynamic) {
1447+ auto buffer = getAllocator ().allocate (object->tlsSegmentSize );
1448+ __ensure (!(reinterpret_cast <uintptr_t >(buffer) & (object->tlsAlignment - 1 )));
1449+ memset (buffer, 0 , object->tlsSegmentSize );
1450+ memcpy (buffer, object->tlsImagePtr , object->tlsImageSize );
1451+ tcb_ptr->dtvPointers [i] = buffer;
14471452 }
14481453 }
14491454
1455+ runtimeTlsMap->tcbs .push_back (tcb_ptr);
1456+
14501457 return tcb_ptr;
14511458}
14521459
14531460void *accessDtv (SharedObject *object) {
14541461 Tcb *tcb_ptr = mlibc::get_current_tcb ();
1455-
1456- {
1457- frg::unique_lock lock{*runtimeTlsMapLock};
1458-
1459- // We might need to reallocate the DTV.
1460- if (object->tlsIndex >= tcb_ptr->dtvSize ) {
1461- auto ndtv = frg::construct_n<void *>(getAllocator (), runtimeTlsMap->indices .size ());
1462- memset (ndtv, 0 , sizeof (void *) * runtimeTlsMap->indices .size ());
1463- memcpy (ndtv, tcb_ptr->dtvPointers , sizeof (void *) * tcb_ptr->dtvSize );
1464- frg::destruct_n (getAllocator (), tcb_ptr->dtvPointers , tcb_ptr->dtvSize );
1465- tcb_ptr->dtvSize = runtimeTlsMap->indices .size ();
1466- tcb_ptr->dtvPointers = ndtv;
1467- }
1468- }
1469-
1470- // We might need to fill in a new DTV entry.
1471- if (!tcb_ptr->dtvPointers [object->tlsIndex ]) {
1472- __ensure (object->tlsModel == TlsModel::dynamic);
1473-
1474- auto buffer = getAllocator ().allocate (object->tlsSegmentSize );
1475- __ensure (!(reinterpret_cast <uintptr_t >(buffer) & (object->tlsAlignment - 1 )));
1476- memset (buffer, 0 , object->tlsSegmentSize );
1477- memcpy (buffer, object->tlsImagePtr , object->tlsImageSize );
1478- tcb_ptr->dtvPointers [object->tlsIndex ] = buffer;
1479-
1480- if (rtldConfig.debugVerbose ) {
1481- mlibc::infoLogger () << " rtld: accessDtv wrote tls image at " << buffer
1482- << " , size = 0x" << frg::hex_fmt{object->tlsSegmentSize } << frg::endlog;
1483- }
1484- }
1485-
1486- return (void *)((char *)tcb_ptr->dtvPointers [object->tlsIndex ] + TLS_DTV_OFFSET );
1462+ size_t size = __atomic_load_n (&tcb_ptr->dtvSize , __ATOMIC_ACQUIRE);
1463+ __ensure (object->tlsIndex < size);
1464+ void **pointers = __atomic_load_n (&tcb_ptr->dtvPointers , __ATOMIC_ACQUIRE);
1465+ void *ptr = pointers[object->tlsIndex ];
1466+ __ensure (ptr);
1467+ return (void *)((char *)ptr + TLS_DTV_OFFSET );
14871468}
14881469
14891470void *tryAccessDtv (SharedObject *object) {
14901471 Tcb *tcb_ptr = mlibc::get_current_tcb ();
14911472
1492- if (object->tlsIndex >= tcb_ptr->dtvSize )
1473+ size_t size = __atomic_load_n (&tcb_ptr->dtvSize , __ATOMIC_ACQUIRE);
1474+ if (object->tlsIndex >= size)
14931475 return nullptr ;
1494- if (!tcb_ptr->dtvPointers [object->tlsIndex ])
1476+ void **pointers = __atomic_load_n (&tcb_ptr->dtvPointers , __ATOMIC_ACQUIRE);
1477+ void *ptr = pointers[object->tlsIndex ];
1478+ if (!ptr)
14951479 return nullptr ;
14961480
1497- return (void *)((char *)tcb_ptr-> dtvPointers [object-> tlsIndex ] + TLS_DTV_OFFSET );
1481+ return (void *)((char *)ptr + TLS_DTV_OFFSET );
14981482}
14991483
15001484// --------------------------------------------------------
@@ -1577,14 +1561,12 @@ frg::optional<ObjectSymbol> resolveInObject(SharedObject *object, frg::string_vi
15771561
15781562 // Checks if the symbol's version matches the desired version.
15791563 auto correctVersion = [&] (SymbolVersion candVersion) {
1580- // Local version symbols shouldn't participate in symbol resolution.
1581- // Only time .dynsym can contain a local version symbol is if it's
1582- // undefined, so it should be discarded earlier.
1583- __ensure (!candVersion.isLocal ());
1564+ if (object->definedVersions .size ())
1565+ __ensure (!candVersion.isLocal ());
15841566
15851567 // Caller requested default version, ...
15861568 // ... and this symbol matches.
1587- if (!version && (candVersion.isDefault () || candVersion.isGlobal ()))
1569+ if (!version && (candVersion.isDefault () || candVersion.isGlobal () || candVersion. isLocal () ))
15881570 return true ;
15891571 // ... but this symbol isn't the default.
15901572 if (!version)
@@ -1594,9 +1576,9 @@ frg::optional<ObjectSymbol> resolveInObject(SharedObject *object, frg::string_vi
15941576 // LLD prior to version 22, and binutils between versions 2.35 and 2.45
15951577 // produce a global version symbol for this case, while newer ones produce
15961578 // a local version symbol.
1597- // In this case, accept either the global or default version.
1579+ // In this case, accept either the global, default, or local version.
15981580 if ((version->isLocal () || version->isGlobal ())
1599- && (candVersion.isGlobal () || candVersion.isDefault ()))
1581+ && (candVersion.isGlobal () || candVersion.isDefault () || candVersion. isLocal () ))
16001582 return true ;
16011583
16021584 // Otherwise, make sure the version is correct.
@@ -1939,6 +1921,50 @@ void Loader::_buildTlsMaps() {
19391921 }
19401922 }
19411923 }
1924+
1925+ size_t new_size = runtimeTlsMap->indices .size ();
1926+
1927+ for (auto tcb : runtimeTlsMap->tcbs ) {
1928+ if (tcb->didExit )
1929+ continue ;
1930+
1931+ size_t size = __atomic_load_n (&tcb->dtvSize , __ATOMIC_ACQUIRE);
1932+ if (new_size > size) {
1933+ void **ndtv = frg::construct_n<void *>(getAllocator (), new_size);
1934+ void **pointers = __atomic_load_n (&tcb->dtvPointers , __ATOMIC_ACQUIRE);
1935+ memcpy (ndtv, pointers, sizeof (void *) * size);
1936+ memset (ndtv + size, 0 , sizeof (void *) * (new_size - size));
1937+
1938+ for (size_t i = size; i < new_size; i++) {
1939+ auto object = runtimeTlsMap->indices [i];
1940+ if (object->tlsModel == TlsModel::dynamic) {
1941+ auto buffer = getAllocator ().allocate (object->tlsSegmentSize );
1942+ __ensure (!(reinterpret_cast <uintptr_t >(buffer) & (object->tlsAlignment - 1 )));
1943+ memset (buffer, 0 , object->tlsSegmentSize );
1944+ memcpy (buffer, object->tlsImagePtr , object->tlsImageSize );
1945+ ndtv[i] = buffer;
1946+ } else if (object->tlsModel == TlsModel::initial) {
1947+ char *tls_ptr;
1948+ if constexpr (tlsAboveTp) {
1949+ tls_ptr = reinterpret_cast <char *>(tcb) + sizeof (Tcb) + object->tlsOffset ;
1950+ } else {
1951+ tls_ptr = reinterpret_cast <char *>(tcb) + object->tlsOffset ;
1952+ }
1953+ ndtv[i] = tls_ptr;
1954+
1955+ // Initialize the TLS segment for this thread as well.
1956+ memset (tls_ptr, 0 , object->tlsSegmentSize );
1957+ memcpy (tls_ptr, object->tlsImagePtr , object->tlsImageSize );
1958+ }
1959+ }
1960+
1961+ // Note: We intentionally leak the old `dtvPointers` array here. Since other threads
1962+ // can concurrently access their own TCB's `dtvPointers` lock-free, freeing the old
1963+ // array immediately would lead to use-after-free races.
1964+ __atomic_store_n (&tcb->dtvPointers , ndtv, __ATOMIC_RELEASE);
1965+ __atomic_store_n (&tcb->dtvSize , new_size, __ATOMIC_RELEASE);
1966+ }
1967+ }
19421968}
19431969
19441970void Loader::initObjects (ObjectRepository *repository) {
@@ -1988,6 +2014,17 @@ void Loader::_scheduleInit(SharedObject *object) {
19882014 object->onInitStack = false ;
19892015}
19902016
2017+ // TODO: TLSDESC relocations aren't aarch64/x86_64 specific
2018+ #if defined(__aarch64__) || defined(__x86_64__)
2019+ extern " C" void *__mlibcTlsdescStatic (void *);
2020+ extern " C" void *__mlibcTlsdescDynamic (void *);
2021+
2022+ struct TlsdescData {
2023+ uintptr_t tlsIndex;
2024+ uintptr_t addend;
2025+ };
2026+ #endif
2027+
19912028void Loader::_processRelocations (Relocation &rel) {
19922029 // copy and irelative relocations have to be performed after all other relocations
19932030 if (rel.type () == R_COPY || rel.type () == R_IRELATIVE )
@@ -2094,6 +2131,53 @@ void Loader::_processRelocations(Relocation &rel) {
20942131 off += tls_offset + tlsOffsetFromTp;
20952132 rel.relocate (off);
20962133 } break ;
2134+ // TODO: TLSDESC relocations aren't aarch64/x86_64 specific, but require an assembly implementation
2135+ // of the resolver functions
2136+ #if defined(__aarch64__) || defined(__x86_64__)
2137+ case R_TLSDESC : {
2138+ size_t symValue = 0 ;
2139+ SharedObject *target = nullptr ;
2140+
2141+ if (rel.symbol_index ()) {
2142+ auto [sym, ver] = rel.object ()->getSymbolByIndex (rel.symbol_index ());
2143+ auto p = Scope::resolveGlobalOrLocal (*globalScope, rel.object ()->localScope , sym.getString (), rel.object ()->objectRts , 0 , ver);
2144+
2145+ if (!p) {
2146+ if (ELF_ST_BIND (sym.symbol ()->st_info ) != STB_WEAK ) {
2147+ mlibc::panicLogger () << " rtld: Unresolved TLSDESC for symbol "
2148+ << sym.getString () << " in object " << rel.object ()->name << frg::endlog;
2149+ }
2150+ target = nullptr ;
2151+ symValue = 0 ;
2152+ } else {
2153+ target = p->object ();
2154+ if (p->symbol ())
2155+ symValue = p->symbol ()->st_value ;
2156+ }
2157+ } else {
2158+ target = rel.object ();
2159+ }
2160+
2161+ if (!target) {
2162+ ((uint64_t *)rel.destination ())[0 ] = reinterpret_cast <uintptr_t >(&__mlibcTlsdescStatic);
2163+ ((uint64_t *)rel.destination ())[1 ] = 0 ;
2164+ } else if (target->tlsModel == TlsModel::initial) {
2165+ ((uint64_t *)rel.destination ())[0 ] = reinterpret_cast <uintptr_t >(&__mlibcTlsdescStatic);
2166+ uint64_t value = symValue + target->tlsOffset + tlsOffsetFromTp + rel.addend_norel ();
2167+ ((uint64_t *)rel.destination ())[1 ] = value;
2168+ } else {
2169+ __ensure (target->tlsIndex < mlibc::get_current_tcb ()->dtvSize );
2170+
2171+ // TODO: We should free this when the DSO gets destroyed
2172+ auto data = frg::construct<TlsdescData>(getAllocator ());
2173+ data->tlsIndex = target->tlsIndex ;
2174+ data->addend = symValue + rel.addend_norel ();
2175+
2176+ ((uint64_t *)rel.destination ())[0 ] = reinterpret_cast <uintptr_t >(&__mlibcTlsdescDynamic);
2177+ ((uint64_t *)rel.destination ())[1 ] = reinterpret_cast <uintptr_t >(data);
2178+ }
2179+ } break ;
2180+ #endif
20972181 default :
20982182 mlibc::panicLogger () << " Unexpected relocation type "
20992183 << (void *) rel.type () << frg::endlog;
@@ -2196,12 +2280,6 @@ void Loader::_processStaticRelocations(SharedObject *object) {
21962280 }
21972281}
21982282
2199- // TODO: TLSDESC relocations aren't aarch64/x86_64 specific
2200- #if defined(__aarch64__) || defined(__x86_64__)
2201- extern " C" void *__mlibcTlsdescStatic (void *);
2202- extern " C" void *__mlibcTlsdescDynamic (void *);
2203- #endif
2204-
22052283void Loader::_processLazyRelocations (SharedObject *object) {
22062284 if (object->globalOffsetTable == nullptr ) {
22072285 __ensure (object->lazyRelocTableOffset == 0 );
@@ -2278,9 +2356,12 @@ void Loader::_processLazyRelocations(SharedObject *object) {
22782356 auto p = Scope::resolveGlobalOrLocal (*globalScope, object->localScope , sym.getString (), object->objectRts , 0 , ver);
22792357
22802358 if (!p) {
2281- __ensure (ELF_ST_BIND (sym.symbol ()->st_info ) != STB_WEAK );
2282- mlibc::panicLogger () << " rtld: Unresolved TLSDESC for symbol "
2283- << sym.getString () << " in object " << object->name << frg::endlog;
2359+ if (ELF_ST_BIND (sym.symbol ()->st_info ) != STB_WEAK ) {
2360+ mlibc::panicLogger () << " rtld: Unresolved TLSDESC for symbol "
2361+ << sym.getString () << " in object " << object->name << frg::endlog;
2362+ }
2363+ target = nullptr ;
2364+ symValue = 0 ;
22842365 } else {
22852366 target = p->object ();
22862367 if (p->symbol ())
@@ -2290,21 +2371,14 @@ void Loader::_processLazyRelocations(SharedObject *object) {
22902371 target = object;
22912372 }
22922373
2293- __ensure (target);
2294-
2295- if (target->tlsModel == TlsModel::initial) {
2374+ if (!target) {
2375+ ((uint64_t *)rel_addr)[0 ] = reinterpret_cast <uintptr_t >(&__mlibcTlsdescStatic);
2376+ ((uint64_t *)rel_addr)[1 ] = 0 ;
2377+ } else if (target->tlsModel == TlsModel::initial) {
22962378 ((uint64_t *)rel_addr)[0 ] = reinterpret_cast <uintptr_t >(&__mlibcTlsdescStatic);
22972379 uint64_t value = symValue + target->tlsOffset + tlsOffsetFromTp + addend;
22982380 ((uint64_t *)rel_addr)[1 ] = value;
22992381 } else {
2300- struct TlsdescData {
2301- uintptr_t tlsIndex;
2302- uintptr_t addend;
2303- };
2304-
2305- // Access DTV for object to force the entry to be allocated and initialized
2306- accessDtv (target);
2307-
23082382 __ensure (target->tlsIndex < mlibc::get_current_tcb ()->dtvSize );
23092383
23102384 // TODO: We should free this when the DSO gets destroyed
0 commit comments