xref: /haiku/src/system/runtime_loader/elf_tls.cpp (revision 6a519848d84ed4d5bc932e35316479004c988b88)
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