xref: /haiku/src/system/runtime_loader/elf_tls.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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 
18 class TLSBlock {
19 public:
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 
32 private:
33 			void*		fPointer;
34 };
35 
36 class Generation {
37 public:
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 
46 private:
47 			unsigned	fCounter;
48 			unsigned	fSize;
49 };
50 
51 class DynamicThreadVector {
52 public:
53 	inline				DynamicThreadVector();
54 
55 			void		DestroyAll();
56 
57 	inline	TLSBlock&	operator[](unsigned dso);
58 
59 private:
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 
75 TLSBlockTemplates*	TLSBlockTemplates::fInstance;
76 
77 
78 void
79 TLSBlockTemplate::SetBaseAddress(addr_t baseAddress)
80 {
81 	fAddress = (void*)((addr_t)fAddress + baseAddress);
82 }
83 
84 
85 TLSBlock
86 TLSBlockTemplate::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 
99 TLSBlockTemplates&
100 TLSBlockTemplates::Get()
101 {
102 	if (fInstance == NULL)
103 		fInstance = new TLSBlockTemplates;
104 	return *fInstance;
105 }
106 
107 
108 unsigned
109 TLSBlockTemplates::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 
127 void
128 TLSBlockTemplates::Unregister(unsigned dso)
129 {
130 	if (dso == unsigned(-1))
131 		return;
132 
133 	fGeneration++;
134 	fFreeDSOs.push_back(dso);
135 }
136 
137 
138 void
139 TLSBlockTemplates::SetBaseAddress(unsigned dso, addr_t baseAddress)
140 {
141 	if (dso != unsigned(-1))
142 		fTemplates[dso].SetBaseAddress(baseAddress);
143 }
144 
145 
146 unsigned
147 TLSBlockTemplates::GetGeneration(unsigned dso) const
148 {
149 	if (dso == unsigned(-1))
150 		return fGeneration;
151 	return fTemplates[dso].Generation();
152 }
153 
154 
155 TLSBlock
156 TLSBlockTemplates::CreateBlock(unsigned dso)
157 {
158 	return fTemplates[dso].CreateBlock();
159 }
160 
161 
162 TLSBlockTemplates::TLSBlockTemplates()
163 	:
164 	fGeneration(0)
165 {
166 }
167 
168 
169 TLSBlock::TLSBlock()
170 	:
171 	fPointer(NULL)
172 {
173 }
174 
175 
176 TLSBlock::TLSBlock(void* pointer)
177 	:
178 	fPointer(pointer)
179 {
180 }
181 
182 
183 status_t
184 TLSBlock::Initialize(unsigned dso)
185 {
186 	fPointer = TLSBlockTemplates::Get().CreateBlock(dso).fPointer;
187 	return fPointer != NULL ? B_OK : B_NO_MEMORY;
188 }
189 
190 
191 void
192 TLSBlock::Destroy()
193 {
194 	free(fPointer);
195 	fPointer = NULL;
196 }
197 
198 
199 Generation::Generation()
200 	:
201 	fCounter(0),
202 	fSize(0)
203 {
204 }
205 
206 
207 DynamicThreadVector::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 
217 void
218 DynamicThreadVector::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 
233 TLSBlock&
234 DynamicThreadVector::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 
268 unsigned
269 DynamicThreadVector::_Generation() const
270 {
271 	if (fGeneration != NULL)
272 		return fGeneration->Counter();
273 	return unsigned(-1);
274 }
275 
276 
277 status_t
278 DynamicThreadVector::_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 
305 void*
306 get_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 
316 void
317 destroy_thread_tls()
318 {
319 	DynamicThreadVector().DestroyAll();
320 }
321 
322