144c0c4d3SPawel Dziepak /*
244c0c4d3SPawel Dziepak * Copyright 2014, Paweł Dziepak, pdziepak@quarnos.org.
344c0c4d3SPawel Dziepak * Distributed under the terms of the MIT License.
444c0c4d3SPawel Dziepak */
544c0c4d3SPawel Dziepak
644c0c4d3SPawel Dziepak #include "elf_tls.h"
744c0c4d3SPawel Dziepak
844c0c4d3SPawel Dziepak #include <stdlib.h>
944c0c4d3SPawel Dziepak #include <string.h>
1044c0c4d3SPawel Dziepak
1144c0c4d3SPawel Dziepak #include <support/TLS.h>
1244c0c4d3SPawel Dziepak
1344c0c4d3SPawel Dziepak #include <tls.h>
1444c0c4d3SPawel Dziepak
15368dd377SMichael Lotz #include <util/kernel_cpp.h>
16368dd377SMichael Lotz
1744c0c4d3SPawel Dziepak
1844c0c4d3SPawel Dziepak class TLSBlock {
1944c0c4d3SPawel Dziepak public:
2044c0c4d3SPawel Dziepak inline TLSBlock();
2144c0c4d3SPawel Dziepak inline TLSBlock(void* pointer);
2244c0c4d3SPawel Dziepak
2344c0c4d3SPawel Dziepak inline status_t Initialize(unsigned dso);
2444c0c4d3SPawel Dziepak
2544c0c4d3SPawel Dziepak void Destroy();
2644c0c4d3SPawel Dziepak
IsInvalid() const2744c0c4d3SPawel Dziepak bool IsInvalid() const { return fPointer == NULL; }
2844c0c4d3SPawel Dziepak
operator +(addr_t offset) const2944c0c4d3SPawel Dziepak void* operator+(addr_t offset) const
30ebeab0cfSX512 { return (void*)((addr_t)fPointer + TLS_DTV_OFFSET + offset); }
3144c0c4d3SPawel Dziepak
3244c0c4d3SPawel Dziepak private:
3344c0c4d3SPawel Dziepak void* fPointer;
3444c0c4d3SPawel Dziepak };
3544c0c4d3SPawel Dziepak
3644c0c4d3SPawel Dziepak class Generation {
3744c0c4d3SPawel Dziepak public:
3844c0c4d3SPawel Dziepak inline Generation();
3944c0c4d3SPawel Dziepak
Counter() const4044c0c4d3SPawel Dziepak unsigned Counter() const { return fCounter; }
Size() const4144c0c4d3SPawel Dziepak unsigned Size() const { return fSize; }
4244c0c4d3SPawel Dziepak
SetCounter(unsigned counter)4344c0c4d3SPawel Dziepak void SetCounter(unsigned counter) { fCounter = counter; }
SetSize(unsigned size)4444c0c4d3SPawel Dziepak void SetSize(unsigned size) { fSize = size; }
4544c0c4d3SPawel Dziepak
4644c0c4d3SPawel Dziepak private:
4744c0c4d3SPawel Dziepak unsigned fCounter;
4844c0c4d3SPawel Dziepak unsigned fSize;
4944c0c4d3SPawel Dziepak };
5044c0c4d3SPawel Dziepak
5144c0c4d3SPawel Dziepak class DynamicThreadVector {
5244c0c4d3SPawel Dziepak public:
5344c0c4d3SPawel Dziepak inline DynamicThreadVector();
5444c0c4d3SPawel Dziepak
5544c0c4d3SPawel Dziepak void DestroyAll();
5644c0c4d3SPawel Dziepak
5744c0c4d3SPawel Dziepak inline TLSBlock& operator[](unsigned dso);
5844c0c4d3SPawel Dziepak
5944c0c4d3SPawel Dziepak private:
_DoesExist() const6044c0c4d3SPawel Dziepak bool _DoesExist() const { return *fVector != NULL; }
_Size() const6144c0c4d3SPawel Dziepak unsigned _Size() const
6244c0c4d3SPawel Dziepak { return _DoesExist()
6344c0c4d3SPawel Dziepak ? fGeneration->Size() : 0; }
6444c0c4d3SPawel Dziepak
6544c0c4d3SPawel Dziepak unsigned _Generation() const;
6644c0c4d3SPawel Dziepak
6744c0c4d3SPawel Dziepak status_t _ResizeVector(unsigned minimumSize);
6844c0c4d3SPawel Dziepak
6944c0c4d3SPawel Dziepak TLSBlock** fVector;
7044c0c4d3SPawel Dziepak Generation* fGeneration;
7144c0c4d3SPawel Dziepak TLSBlock fNullBlock;
7244c0c4d3SPawel Dziepak };
7344c0c4d3SPawel Dziepak
7444c0c4d3SPawel Dziepak
7544c0c4d3SPawel Dziepak TLSBlockTemplates* TLSBlockTemplates::fInstance;
7644c0c4d3SPawel Dziepak
7744c0c4d3SPawel Dziepak
7844c0c4d3SPawel Dziepak void
SetBaseAddress(addr_t baseAddress)7944c0c4d3SPawel Dziepak TLSBlockTemplate::SetBaseAddress(addr_t baseAddress)
8044c0c4d3SPawel Dziepak {
8144c0c4d3SPawel Dziepak fAddress = (void*)((addr_t)fAddress + baseAddress);
8244c0c4d3SPawel Dziepak }
8344c0c4d3SPawel Dziepak
8444c0c4d3SPawel Dziepak
8544c0c4d3SPawel Dziepak TLSBlock
CreateBlock()8644c0c4d3SPawel Dziepak TLSBlockTemplate::CreateBlock()
8744c0c4d3SPawel Dziepak {
88dbcbe07aSJérôme Duval void* pointer = malloc(fMemorySize + TLS_DTV_OFFSET);
8944c0c4d3SPawel Dziepak if (pointer == NULL)
9044c0c4d3SPawel Dziepak return TLSBlock();
91dbcbe07aSJérôme Duval memset(pointer, 0, TLS_DTV_OFFSET);
92ebeab0cfSX512 memcpy((char*)pointer + TLS_DTV_OFFSET, fAddress, fFileSize);
9344c0c4d3SPawel Dziepak if (fMemorySize > fFileSize)
94ebeab0cfSX512 memset((char*)pointer + TLS_DTV_OFFSET + fFileSize, 0, fMemorySize - fFileSize);
9544c0c4d3SPawel Dziepak return TLSBlock(pointer);
9644c0c4d3SPawel Dziepak }
9744c0c4d3SPawel Dziepak
9844c0c4d3SPawel Dziepak
9944c0c4d3SPawel Dziepak TLSBlockTemplates&
Get()10044c0c4d3SPawel Dziepak TLSBlockTemplates::Get()
10144c0c4d3SPawel Dziepak {
10244c0c4d3SPawel Dziepak if (fInstance == NULL)
10344c0c4d3SPawel Dziepak fInstance = new TLSBlockTemplates;
10444c0c4d3SPawel Dziepak return *fInstance;
10544c0c4d3SPawel Dziepak }
10644c0c4d3SPawel Dziepak
10744c0c4d3SPawel Dziepak
10844c0c4d3SPawel Dziepak unsigned
Register(const TLSBlockTemplate & block)10944c0c4d3SPawel Dziepak TLSBlockTemplates::Register(const TLSBlockTemplate& block)
11044c0c4d3SPawel Dziepak {
11144c0c4d3SPawel Dziepak unsigned dso;
11244c0c4d3SPawel Dziepak
11344c0c4d3SPawel Dziepak if (!fFreeDSOs.empty()) {
11444c0c4d3SPawel Dziepak dso = fFreeDSOs.back();
11544c0c4d3SPawel Dziepak fFreeDSOs.pop_back();
11644c0c4d3SPawel Dziepak fTemplates[dso] = block;
11744c0c4d3SPawel Dziepak } else {
11844c0c4d3SPawel Dziepak dso = fTemplates.size();
11944c0c4d3SPawel Dziepak fTemplates.push_back(block);
12044c0c4d3SPawel Dziepak }
12144c0c4d3SPawel Dziepak
122680ca3b1SPawel Dziepak fTemplates[dso].SetGeneration(fGeneration);
12344c0c4d3SPawel Dziepak return dso;
12444c0c4d3SPawel Dziepak }
12544c0c4d3SPawel Dziepak
12644c0c4d3SPawel Dziepak
12744c0c4d3SPawel Dziepak void
Unregister(unsigned dso)12844c0c4d3SPawel Dziepak TLSBlockTemplates::Unregister(unsigned dso)
12944c0c4d3SPawel Dziepak {
13044c0c4d3SPawel Dziepak if (dso == unsigned(-1))
13144c0c4d3SPawel Dziepak return;
13244c0c4d3SPawel Dziepak
133680ca3b1SPawel Dziepak fGeneration++;
13444c0c4d3SPawel Dziepak fFreeDSOs.push_back(dso);
13544c0c4d3SPawel Dziepak }
13644c0c4d3SPawel Dziepak
13744c0c4d3SPawel Dziepak
13844c0c4d3SPawel Dziepak void
SetBaseAddress(unsigned dso,addr_t baseAddress)13944c0c4d3SPawel Dziepak TLSBlockTemplates::SetBaseAddress(unsigned dso, addr_t baseAddress)
14044c0c4d3SPawel Dziepak {
14144c0c4d3SPawel Dziepak if (dso != unsigned(-1))
14244c0c4d3SPawel Dziepak fTemplates[dso].SetBaseAddress(baseAddress);
14344c0c4d3SPawel Dziepak }
14444c0c4d3SPawel Dziepak
14544c0c4d3SPawel Dziepak
14644c0c4d3SPawel Dziepak unsigned
GetGeneration(unsigned dso) const14744c0c4d3SPawel Dziepak TLSBlockTemplates::GetGeneration(unsigned dso) const
14844c0c4d3SPawel Dziepak {
14944c0c4d3SPawel Dziepak if (dso == unsigned(-1))
15044c0c4d3SPawel Dziepak return fGeneration;
15144c0c4d3SPawel Dziepak return fTemplates[dso].Generation();
15244c0c4d3SPawel Dziepak }
15344c0c4d3SPawel Dziepak
15444c0c4d3SPawel Dziepak
15544c0c4d3SPawel Dziepak TLSBlock
CreateBlock(unsigned dso)15644c0c4d3SPawel Dziepak TLSBlockTemplates::CreateBlock(unsigned dso)
15744c0c4d3SPawel Dziepak {
15844c0c4d3SPawel Dziepak return fTemplates[dso].CreateBlock();
15944c0c4d3SPawel Dziepak }
16044c0c4d3SPawel Dziepak
16144c0c4d3SPawel Dziepak
TLSBlockTemplates()162680ca3b1SPawel Dziepak TLSBlockTemplates::TLSBlockTemplates()
163680ca3b1SPawel Dziepak :
164680ca3b1SPawel Dziepak fGeneration(0)
165680ca3b1SPawel Dziepak {
166680ca3b1SPawel Dziepak }
167680ca3b1SPawel Dziepak
168680ca3b1SPawel Dziepak
TLSBlock()16944c0c4d3SPawel Dziepak TLSBlock::TLSBlock()
17044c0c4d3SPawel Dziepak :
17144c0c4d3SPawel Dziepak fPointer(NULL)
17244c0c4d3SPawel Dziepak {
17344c0c4d3SPawel Dziepak }
17444c0c4d3SPawel Dziepak
17544c0c4d3SPawel Dziepak
TLSBlock(void * pointer)17644c0c4d3SPawel Dziepak TLSBlock::TLSBlock(void* pointer)
17744c0c4d3SPawel Dziepak :
17844c0c4d3SPawel Dziepak fPointer(pointer)
17944c0c4d3SPawel Dziepak {
18044c0c4d3SPawel Dziepak }
18144c0c4d3SPawel Dziepak
18244c0c4d3SPawel Dziepak
18344c0c4d3SPawel Dziepak status_t
Initialize(unsigned dso)18444c0c4d3SPawel Dziepak TLSBlock::Initialize(unsigned dso)
18544c0c4d3SPawel Dziepak {
18644c0c4d3SPawel Dziepak fPointer = TLSBlockTemplates::Get().CreateBlock(dso).fPointer;
18744c0c4d3SPawel Dziepak return fPointer != NULL ? B_OK : B_NO_MEMORY;
18844c0c4d3SPawel Dziepak }
18944c0c4d3SPawel Dziepak
19044c0c4d3SPawel Dziepak
19144c0c4d3SPawel Dziepak void
Destroy()19244c0c4d3SPawel Dziepak TLSBlock::Destroy()
19344c0c4d3SPawel Dziepak {
19444c0c4d3SPawel Dziepak free(fPointer);
19544c0c4d3SPawel Dziepak fPointer = NULL;
19644c0c4d3SPawel Dziepak }
19744c0c4d3SPawel Dziepak
19844c0c4d3SPawel Dziepak
Generation()19944c0c4d3SPawel Dziepak Generation::Generation()
20044c0c4d3SPawel Dziepak :
20144c0c4d3SPawel Dziepak fCounter(0),
20244c0c4d3SPawel Dziepak fSize(0)
20344c0c4d3SPawel Dziepak {
20444c0c4d3SPawel Dziepak }
20544c0c4d3SPawel Dziepak
20644c0c4d3SPawel Dziepak
DynamicThreadVector()20744c0c4d3SPawel Dziepak DynamicThreadVector::DynamicThreadVector()
20844c0c4d3SPawel Dziepak :
20944c0c4d3SPawel Dziepak fVector((TLSBlock**)tls_address(TLS_DYNAMIC_THREAD_VECTOR)),
21044c0c4d3SPawel Dziepak fGeneration(NULL)
21144c0c4d3SPawel Dziepak {
21244c0c4d3SPawel Dziepak if (*fVector != NULL)
21344c0c4d3SPawel Dziepak fGeneration = (Generation*)*(void**)*fVector;
21444c0c4d3SPawel Dziepak }
21544c0c4d3SPawel Dziepak
21644c0c4d3SPawel Dziepak
21744c0c4d3SPawel Dziepak void
DestroyAll()21844c0c4d3SPawel Dziepak DynamicThreadVector::DestroyAll()
21944c0c4d3SPawel Dziepak {
22044c0c4d3SPawel Dziepak for (unsigned i = 0; i < _Size(); i++) {
22144c0c4d3SPawel Dziepak TLSBlock& block = (*fVector)[i + 1];
22244c0c4d3SPawel Dziepak if (!block.IsInvalid())
22344c0c4d3SPawel Dziepak block.Destroy();
22444c0c4d3SPawel Dziepak }
22544c0c4d3SPawel Dziepak
22644c0c4d3SPawel Dziepak free(*fVector);
22744c0c4d3SPawel Dziepak *fVector = NULL;
22844c0c4d3SPawel Dziepak
22944c0c4d3SPawel Dziepak delete fGeneration;
23044c0c4d3SPawel Dziepak }
23144c0c4d3SPawel Dziepak
23244c0c4d3SPawel Dziepak
23344c0c4d3SPawel Dziepak TLSBlock&
operator [](unsigned dso)23444c0c4d3SPawel Dziepak DynamicThreadVector::operator[](unsigned dso)
23544c0c4d3SPawel Dziepak {
23644c0c4d3SPawel Dziepak unsigned generation = TLSBlockTemplates::Get().GetGeneration(-1);
23744c0c4d3SPawel Dziepak if (_Generation() < generation) {
238aabcd6b3SAugustin Cavalier // We need to destroy any blocks whose DSO generation has changed
239aabcd6b3SAugustin Cavalier // to be greater than our own generation.
240aabcd6b3SAugustin Cavalier for (unsigned dsoIndex = 0; dsoIndex < _Size(); dsoIndex++) {
241aabcd6b3SAugustin Cavalier TLSBlock& block = (*fVector)[dsoIndex + 1];
24244c0c4d3SPawel Dziepak unsigned dsoGeneration
243aabcd6b3SAugustin Cavalier = TLSBlockTemplates::Get().GetGeneration(dsoIndex);
244aabcd6b3SAugustin Cavalier if (dsoGeneration > _Generation() && dsoGeneration <= generation)
24544c0c4d3SPawel Dziepak block.Destroy();
24644c0c4d3SPawel Dziepak }
24744c0c4d3SPawel Dziepak
24844c0c4d3SPawel Dziepak fGeneration->SetCounter(generation);
24944c0c4d3SPawel Dziepak }
25044c0c4d3SPawel Dziepak
25144c0c4d3SPawel Dziepak if (_Size() <= dso) {
25244c0c4d3SPawel Dziepak status_t result = _ResizeVector(dso + 1);
25344c0c4d3SPawel Dziepak if (result != B_OK)
25444c0c4d3SPawel Dziepak return fNullBlock;
25544c0c4d3SPawel Dziepak }
25644c0c4d3SPawel Dziepak
25744c0c4d3SPawel Dziepak TLSBlock& block = (*fVector)[dso + 1];
25844c0c4d3SPawel Dziepak if (block.IsInvalid()) {
25944c0c4d3SPawel Dziepak status_t result = block.Initialize(dso);
26044c0c4d3SPawel Dziepak if (result != B_OK)
26144c0c4d3SPawel Dziepak return fNullBlock;
262aabcd6b3SAugustin Cavalier }
26344c0c4d3SPawel Dziepak
26444c0c4d3SPawel Dziepak return block;
26544c0c4d3SPawel Dziepak }
26644c0c4d3SPawel Dziepak
26744c0c4d3SPawel Dziepak
26844c0c4d3SPawel Dziepak unsigned
_Generation() const26944c0c4d3SPawel Dziepak DynamicThreadVector::_Generation() const
27044c0c4d3SPawel Dziepak {
27144c0c4d3SPawel Dziepak if (fGeneration != NULL)
27244c0c4d3SPawel Dziepak return fGeneration->Counter();
27344c0c4d3SPawel Dziepak return unsigned(-1);
27444c0c4d3SPawel Dziepak }
27544c0c4d3SPawel Dziepak
27644c0c4d3SPawel Dziepak
27744c0c4d3SPawel Dziepak status_t
_ResizeVector(unsigned minimumSize)27844c0c4d3SPawel Dziepak DynamicThreadVector::_ResizeVector(unsigned minimumSize)
27944c0c4d3SPawel Dziepak {
28044c0c4d3SPawel Dziepak static const unsigned kInitialSize = 4;
28144c0c4d3SPawel Dziepak unsigned size = std::max(minimumSize, kInitialSize);
28244c0c4d3SPawel Dziepak unsigned oldSize = _Size();
28344c0c4d3SPawel Dziepak if (size <= oldSize)
28444c0c4d3SPawel Dziepak return B_OK;
28544c0c4d3SPawel Dziepak
28644c0c4d3SPawel Dziepak void* newVector = realloc(*fVector, (size + 1) * sizeof(TLSBlock));
28744c0c4d3SPawel Dziepak if (newVector == NULL)
28844c0c4d3SPawel Dziepak return B_NO_MEMORY;
28944c0c4d3SPawel Dziepak
29044c0c4d3SPawel Dziepak *fVector = (TLSBlock*)newVector;
291*6a519848SAugustin Cavalier memset((void*)(*fVector + oldSize + 1), 0, (size - oldSize) * sizeof(TLSBlock));
292368dd377SMichael Lotz if (fGeneration == NULL) {
29344c0c4d3SPawel Dziepak fGeneration = new Generation;
294368dd377SMichael Lotz if (fGeneration == NULL)
295368dd377SMichael Lotz return B_NO_MEMORY;
296368dd377SMichael Lotz }
297368dd377SMichael Lotz
29844c0c4d3SPawel Dziepak *(Generation**)*fVector = fGeneration;
29944c0c4d3SPawel Dziepak fGeneration->SetSize(size);
30044c0c4d3SPawel Dziepak
30144c0c4d3SPawel Dziepak return B_OK;
30244c0c4d3SPawel Dziepak }
30344c0c4d3SPawel Dziepak
30444c0c4d3SPawel Dziepak
30544c0c4d3SPawel Dziepak void*
get_tls_address(unsigned dso,addr_t offset)30644c0c4d3SPawel Dziepak get_tls_address(unsigned dso, addr_t offset)
30744c0c4d3SPawel Dziepak {
30844c0c4d3SPawel Dziepak DynamicThreadVector dynamicThreadVector;
30944c0c4d3SPawel Dziepak TLSBlock& block = dynamicThreadVector[dso];
31044c0c4d3SPawel Dziepak if (block.IsInvalid())
31144c0c4d3SPawel Dziepak return NULL;
31244c0c4d3SPawel Dziepak return block + offset;
31344c0c4d3SPawel Dziepak }
31444c0c4d3SPawel Dziepak
31544c0c4d3SPawel Dziepak
31644c0c4d3SPawel Dziepak void
destroy_thread_tls()31744c0c4d3SPawel Dziepak destroy_thread_tls()
31844c0c4d3SPawel Dziepak {
31944c0c4d3SPawel Dziepak DynamicThreadVector().DestroyAll();
32044c0c4d3SPawel Dziepak }
32144c0c4d3SPawel Dziepak
322