Skip to content

Commit 2e24ade

Browse files
committed
options/internal: fix TLSDESC relocation handling
This implements TLSDESC handling for static relocations, as well as fixing the DTV sizing issues for the non-calling threads.
1 parent a2b94ac commit 2e24ade

4 files changed

Lines changed: 157 additions & 75 deletions

File tree

options/rtld/aarch64/runtime.S

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@ __mlibcTlsdescStatic:
1313
.type __mlibcTlsdescDynamic,@function
1414
__mlibcTlsdescDynamic:
1515
stp x1, x2, [sp, #-16]!
16-
ldr x0, [x0, #8]
17-
ldp x1, x2, [x0] // tlsIndex, addend
18-
mrs x0, tpidr_el0 // tp
19-
ldr x0, [x0, #-104] // tp->dtvPointers
20-
ldr x0, [x0, x1, lsl 3] // [tlsIndex]
16+
ldr x1, [x0, #8] // TlsdescData *
17+
ldp x0, x2, [x1] // tlsIndex, addend
18+
19+
mrs x1, tpidr_el0 // tp
20+
sub x1, x1, #104 // address of tp->dtvPointers
21+
ldar x1, [x1] // load dtvPointers with acquire
22+
ldr x0, [x1, x0, lsl 3] // dtvPointers[tlsIndex]
23+
2124
add x0, x0, x2 // + addend
2225
mrs x1, tpidr_el0 // tp
2326
sub x0, x0, x1 // result - tp

options/rtld/generic/linker.cpp

Lines changed: 139 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,7 +1351,7 @@ void doDestruct(SharedObject *object) {
13511351
// --------------------------------------------------------
13521352

13531353
RuntimeTlsMap::RuntimeTlsMap()
1354-
: initialPtr{0}, initialLimit{0}, indices{getAllocator()} { }
1354+
: initialPtr{0}, initialLimit{0}, indices{getAllocator()}, tcbs{getAllocator()} { }
13551355

13561356
void 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

14531460
void *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

14891470
void *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
// --------------------------------------------------------
@@ -1937,6 +1921,50 @@ void Loader::_buildTlsMaps() {
19371921
}
19381922
}
19391923
}
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+
}
19401968
}
19411969

19421970
void Loader::initObjects(ObjectRepository *repository) {
@@ -1986,6 +2014,17 @@ void Loader::_scheduleInit(SharedObject *object) {
19862014
object->onInitStack = false;
19872015
}
19882016

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+
19892028
void Loader::_processRelocations(Relocation &rel) {
19902029
// copy and irelative relocations have to be performed after all other relocations
19912030
if(rel.type() == R_COPY || rel.type() == R_IRELATIVE)
@@ -2092,6 +2131,53 @@ void Loader::_processRelocations(Relocation &rel) {
20922131
off += tls_offset + tlsOffsetFromTp;
20932132
rel.relocate(off);
20942133
} 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
20952181
default:
20962182
mlibc::panicLogger() << "Unexpected relocation type "
20972183
<< (void *) rel.type() << frg::endlog;
@@ -2194,12 +2280,6 @@ void Loader::_processStaticRelocations(SharedObject *object) {
21942280
}
21952281
}
21962282

2197-
// TODO: TLSDESC relocations aren't aarch64/x86_64 specific
2198-
#if defined(__aarch64__) || defined(__x86_64__)
2199-
extern "C" void *__mlibcTlsdescStatic(void *);
2200-
extern "C" void *__mlibcTlsdescDynamic(void *);
2201-
#endif
2202-
22032283
void Loader::_processLazyRelocations(SharedObject *object) {
22042284
if(object->globalOffsetTable == nullptr) {
22052285
__ensure(object->lazyRelocTableOffset == 0);
@@ -2276,9 +2356,12 @@ void Loader::_processLazyRelocations(SharedObject *object) {
22762356
auto p = Scope::resolveGlobalOrLocal(*globalScope, object->localScope, sym.getString(), object->objectRts, 0, ver);
22772357

22782358
if (!p) {
2279-
__ensure(ELF_ST_BIND(sym.symbol()->st_info) != STB_WEAK);
2280-
mlibc::panicLogger() << "rtld: Unresolved TLSDESC for symbol "
2281-
<< 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;
22822365
} else {
22832366
target = p->object();
22842367
if (p->symbol())
@@ -2288,21 +2371,14 @@ void Loader::_processLazyRelocations(SharedObject *object) {
22882371
target = object;
22892372
}
22902373

2291-
__ensure(target);
2292-
2293-
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) {
22942378
((uint64_t *)rel_addr)[0] = reinterpret_cast<uintptr_t>(&__mlibcTlsdescStatic);
22952379
uint64_t value = symValue + target->tlsOffset + tlsOffsetFromTp + addend;
22962380
((uint64_t *)rel_addr)[1] = value;
22972381
} else {
2298-
struct TlsdescData {
2299-
uintptr_t tlsIndex;
2300-
uintptr_t addend;
2301-
};
2302-
2303-
// Access DTV for object to force the entry to be allocated and initialized
2304-
accessDtv(target);
2305-
23062382
__ensure(target->tlsIndex < mlibc::get_current_tcb()->dtvSize);
23072383

23082384
// TODO: We should free this when the DSO gets destroyed

options/rtld/generic/linker.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,9 @@ struct RuntimeTlsMap {
345345

346346
// TLS indices.
347347
frg::vector<SharedObject *, MemoryAllocator> indices;
348+
349+
// Track all allocated TCBs.
350+
frg::vector<Tcb *, MemoryAllocator> tcbs;
348351
};
349352

350353
extern frg::manual_box<FutexLock> runtimeTlsMapLock;

options/rtld/x86_64/runtime.S

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ __mlibcTlsdescDynamic:
1212
push %rbx
1313
push %rcx
1414

15-
mov 8(%rax), %rax
15+
mov 8(%rax), %rbx # TlsdescData*
16+
mov (%rbx), %rcx # tlsIndex
1617

17-
mov (%rax), %rbx // index
18-
mov 8(%rax), %rcx // addend
18+
mov %fs:16, %rax # dtvPointers
19+
mov (%rax, %rcx, 8), %rax # dtvPointers[tlsIndex]
1920

20-
mov %fs:16, %rax // *tp->dtvPointers
21-
mov (%rax, %rbx, 8), %rax // dtvPointers[0][index]
22-
add %rcx, %rax // + addend
21+
mov 8(%rbx), %rcx # addend
22+
add %rcx, %rax
2323
sub %fs:0, %rax
2424

2525
pop %rcx
@@ -53,7 +53,7 @@ pltRelocateStub:
5353
pop %rdx
5454
pop %rcx
5555
pop %rax
56-
56+
5757
pop %rdi
5858
pop %rsi
5959
add $16, %rsp

0 commit comments

Comments
 (0)