1/*
2 * Copyright 2014, Pawe�� Dziepak, pdziepak@quarnos.org.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "elf_tls.h"
7
8#include <stdlib.h>
9#include <string.h>
10
11#include <support/TLS.h>
12
13#include <tls.h>
14
15#include <util/kernel_cpp.h>
16
17
18class TLSBlock {
19public:
20	inline				TLSBlock();
21	inline				TLSBlock(void* pointer);
22
23	inline	status_t	Initialize(unsigned dso);
24
25			void		Destroy();
26
27			bool		IsInvalid() const	{ return fPointer == NULL; }
28
29			void*		operator+(addr_t offset) const
30							{ return (void*)((addr_t)fPointer + TLS_DTV_OFFSET + offset); }
31
32private:
33			void*		fPointer;
34};
35
36class Generation {
37public:
38	inline				Generation();
39
40			unsigned	Counter() const	{ return fCounter; }
41			unsigned	Size() const	{ return fSize; }
42
43			void		SetCounter(unsigned counter)	{ fCounter = counter; }
44			void		SetSize(unsigned size)	{ fSize = size; }
45
46private:
47			unsigned	fCounter;
48			unsigned	fSize;
49};
50
51class DynamicThreadVector {
52public:
53	inline				DynamicThreadVector();
54
55			void		DestroyAll();
56
57	inline	TLSBlock&	operator[](unsigned dso);
58
59private:
60			bool		_DoesExist() const	{ return *fVector != NULL; }
61			unsigned	_Size() const
62							{ return _DoesExist()
63									? fGeneration->Size() : 0; }
64
65			unsigned	_Generation() const;
66
67			status_t	_ResizeVector(unsigned minimumSize);
68
69			TLSBlock**	fVector;
70			Generation*	fGeneration;
71			TLSBlock	fNullBlock;
72};
73
74
75TLSBlockTemplates*	TLSBlockTemplates::fInstance;
76
77
78void
79TLSBlockTemplate::SetBaseAddress(addr_t baseAddress)
80{
81	fAddress = (void*)((addr_t)fAddress + baseAddress);
82}
83
84
85TLSBlock
86TLSBlockTemplate::CreateBlock()
87{
88	void* pointer = malloc(fMemorySize + TLS_DTV_OFFSET);
89	if (pointer == NULL)
90		return TLSBlock();
91	memset(pointer, 0, TLS_DTV_OFFSET);
92	memcpy((char*)pointer + TLS_DTV_OFFSET, fAddress, fFileSize);
93	if (fMemorySize > fFileSize)
94		memset((char*)pointer + TLS_DTV_OFFSET + fFileSize, 0, fMemorySize - fFileSize);
95	return TLSBlock(pointer);
96}
97
98
99TLSBlockTemplates&
100TLSBlockTemplates::Get()
101{
102	if (fInstance == NULL)
103		fInstance = new TLSBlockTemplates;
104	return *fInstance;
105}
106
107
108unsigned
109TLSBlockTemplates::Register(const TLSBlockTemplate& block)
110{
111	unsigned dso;
112
113	if (!fFreeDSOs.empty()) {
114		dso = fFreeDSOs.back();
115		fFreeDSOs.pop_back();
116		fTemplates[dso] = block;
117	} else {
118		dso = fTemplates.size();
119		fTemplates.push_back(block);
120	}
121
122	fTemplates[dso].SetGeneration(fGeneration);
123	return dso;
124}
125
126
127void
128TLSBlockTemplates::Unregister(unsigned dso)
129{
130	if (dso == unsigned(-1))
131		return;
132
133	fGeneration++;
134	fFreeDSOs.push_back(dso);
135}
136
137
138void
139TLSBlockTemplates::SetBaseAddress(unsigned dso, addr_t baseAddress)
140{
141	if (dso != unsigned(-1))
142		fTemplates[dso].SetBaseAddress(baseAddress);
143}
144
145
146unsigned
147TLSBlockTemplates::GetGeneration(unsigned dso) const
148{
149	if (dso == unsigned(-1))
150		return fGeneration;
151	return fTemplates[dso].Generation();
152}
153
154
155TLSBlock
156TLSBlockTemplates::CreateBlock(unsigned dso)
157{
158	return fTemplates[dso].CreateBlock();
159}
160
161
162TLSBlockTemplates::TLSBlockTemplates()
163	:
164	fGeneration(0)
165{
166}
167
168
169TLSBlock::TLSBlock()
170	:
171	fPointer(NULL)
172{
173}
174
175
176TLSBlock::TLSBlock(void* pointer)
177	:
178	fPointer(pointer)
179{
180}
181
182
183status_t
184TLSBlock::Initialize(unsigned dso)
185{
186	fPointer = TLSBlockTemplates::Get().CreateBlock(dso).fPointer;
187	return fPointer != NULL ? B_OK : B_NO_MEMORY;
188}
189
190
191void
192TLSBlock::Destroy()
193{
194	free(fPointer);
195	fPointer = NULL;
196}
197
198
199Generation::Generation()
200	:
201	fCounter(0),
202	fSize(0)
203{
204}
205
206
207DynamicThreadVector::DynamicThreadVector()
208	:
209	fVector((TLSBlock**)tls_address(TLS_DYNAMIC_THREAD_VECTOR)),
210	fGeneration(NULL)
211{
212	if (*fVector != NULL)
213		fGeneration = (Generation*)*(void**)*fVector;
214}
215
216
217void
218DynamicThreadVector::DestroyAll()
219{
220	for (unsigned i = 0; i < _Size(); i++) {
221		TLSBlock& block = (*fVector)[i + 1];
222		if (!block.IsInvalid())
223			block.Destroy();
224	}
225
226	free(*fVector);
227	*fVector = NULL;
228
229	delete fGeneration;
230}
231
232
233TLSBlock&
234DynamicThreadVector::operator[](unsigned dso)
235{
236	unsigned generation = TLSBlockTemplates::Get().GetGeneration(-1);
237	if (_Generation() < generation) {
238		// We need to destroy any blocks whose DSO generation has changed
239		// to be greater than our own generation.
240		for (unsigned dsoIndex = 0; dsoIndex < _Size(); dsoIndex++) {
241			TLSBlock& block = (*fVector)[dsoIndex + 1];
242			unsigned dsoGeneration
243				= TLSBlockTemplates::Get().GetGeneration(dsoIndex);
244			if (dsoGeneration > _Generation() && dsoGeneration <= generation)
245				block.Destroy();
246		}
247
248		fGeneration->SetCounter(generation);
249	}
250
251	if (_Size() <= dso) {
252		status_t result = _ResizeVector(dso + 1);
253		if (result != B_OK)
254			return fNullBlock;
255	}
256
257	TLSBlock& block = (*fVector)[dso + 1];
258	if (block.IsInvalid()) {
259		status_t result = block.Initialize(dso);
260		if (result != B_OK)
261			return fNullBlock;
262	}
263
264	return block;
265}
266
267
268unsigned
269DynamicThreadVector::_Generation() const
270{
271	if (fGeneration != NULL)
272		return fGeneration->Counter();
273	return unsigned(-1);
274}
275
276
277status_t
278DynamicThreadVector::_ResizeVector(unsigned minimumSize)
279{
280	static const unsigned kInitialSize = 4;
281	unsigned size = std::max(minimumSize, kInitialSize);
282	unsigned oldSize = _Size();
283	if (size <= oldSize)
284		return B_OK;
285
286	void* newVector = realloc(*fVector, (size + 1) * sizeof(TLSBlock));
287	if (newVector == NULL)
288		return B_NO_MEMORY;
289
290	*fVector = (TLSBlock*)newVector;
291	memset(*fVector + oldSize + 1, 0, (size - oldSize) * sizeof(TLSBlock));
292	if (fGeneration == NULL) {
293		fGeneration = new Generation;
294		if (fGeneration == NULL)
295			return B_NO_MEMORY;
296	}
297
298	*(Generation**)*fVector = fGeneration;
299	fGeneration->SetSize(size);
300
301	return B_OK;
302}
303
304
305void*
306get_tls_address(unsigned dso, addr_t offset)
307{
308	DynamicThreadVector dynamicThreadVector;
309	TLSBlock& block = dynamicThreadVector[dso];
310	if (block.IsInvalid())
311		return NULL;
312	return block + offset;
313}
314
315
316void
317destroy_thread_tls()
318{
319	DynamicThreadVector().DestroyAll();
320}
321
322