tls.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. /*++
  2. Copyright (c) 2015 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. tls.c
  9. Abstract:
  10. This module handles thread-local storage support for the base Minoca OS
  11. library.
  12. Author:
  13. Evan Green 16-Apr-2015
  14. Environment:
  15. User Mode
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. #include "osbasep.h"
  21. //
  22. // ---------------------------------------------------------------- Definitions
  23. //
  24. //
  25. // Define the allocation tag used by TLS regions: mTLS.
  26. //
  27. #define TLS_ALLOCATION_TAG 0x534C546D
  28. //
  29. // ------------------------------------------------------ Data Type Definitions
  30. //
  31. /*++
  32. Structure Description:
  33. This structure stores the thread control block, a structure used in user
  34. mode to contain information unique to each thread.
  35. Members:
  36. Self - Stores a pointer to the thread control block itself. This member
  37. is mandated by many application ABIs.
  38. TlsVector - Stores an array of pointers to TLS regions for each module. The
  39. first element is a generation number, indicating whether or not the
  40. array needs to be resized. This member is access directly from assembly.
  41. ModuleCount - Stores the count of loaded modules this thread is aware of.
  42. BaseAllocation - Stores a pointer to the actual allocation pointer returned
  43. to free this structure and all the initial TLS blocks.
  44. StackGuard - Stores the stack guard value. This is referenced directly by
  45. GCC, and must be at offset 0x14 on 32-bit systems, 0x28 on 64-bit
  46. systems.
  47. BaseAllocationSize - Stores the size of the base allocation region in bytes.
  48. ListEntry - Stores pointers to the next and previous threads in the OS
  49. Library thread list.
  50. --*/
  51. typedef struct _THREAD_CONTROL_BLOCK {
  52. PVOID Self;
  53. PVOID *TlsVector;
  54. UINTN ModuleCount;
  55. PVOID BaseAllocation;
  56. UINTN StackGuard;
  57. UINTN BaseAllocationSize;
  58. LIST_ENTRY ListEntry;
  59. } THREAD_CONTROL_BLOCK, *PTHREAD_CONTROL_BLOCK;
  60. //
  61. // ----------------------------------------------- Internal Function Prototypes
  62. //
  63. PTHREAD_CONTROL_BLOCK
  64. OspGetThreadControlBlock (
  65. VOID
  66. );
  67. //
  68. // -------------------------------------------------------------------- Globals
  69. //
  70. //
  71. // Store a list entry of active thread control structures.
  72. //
  73. LIST_ENTRY OsThreadList;
  74. OS_LOCK OsThreadListLock;
  75. //
  76. // ------------------------------------------------------------------ Functions
  77. //
  78. OS_API
  79. PVOID
  80. OsGetTlsAddress (
  81. PTLS_INDEX Entry
  82. )
  83. /*++
  84. Routine Description:
  85. This routine returns the address of the given thread local storage symbol.
  86. This routine supports a C library call, references to which are emitted
  87. directly by the compiler.
  88. Arguments:
  89. Entry - Supplies a pointer to the TLS entry to get the symbol address for.
  90. Return Value:
  91. Returns a pointer to the thread local storage symbol on success.
  92. --*/
  93. {
  94. UINTN Alignment;
  95. PVOID *Allocation;
  96. UINTN AllocationSize;
  97. UINTN AvailableSize;
  98. PLIST_ENTRY CurrentEntry;
  99. PVOID *DynamicThreadVector;
  100. UINTN Generation;
  101. PLOADED_IMAGE Image;
  102. UINTN ModuleId;
  103. UINTN NeededSize;
  104. PVOID *NewVector;
  105. UINTN Offset;
  106. PVOID *Region;
  107. PTHREAD_CONTROL_BLOCK ThreadControlBlock;
  108. UINTN ThreadGeneration;
  109. ModuleId = Entry->Module;
  110. Offset = Entry->Offset;
  111. ThreadControlBlock = OspGetThreadControlBlock();
  112. DynamicThreadVector = ThreadControlBlock->TlsVector;
  113. //
  114. // Reallocate the TLS vector if it's behind and doesn't even have this
  115. // module.
  116. //
  117. ThreadGeneration = (UINTN)(DynamicThreadVector[0]);
  118. if (ThreadGeneration < Entry->Module) {
  119. Generation = OsImModuleGeneration;
  120. ASSERT((ModuleId != 0) && (Generation >= ModuleId));
  121. NeededSize = (Generation + 1) * sizeof(PVOID);
  122. //
  123. // If the TLS vector is already not part of the initial allocation,
  124. // just reallocate it from the heap.
  125. //
  126. if (ThreadControlBlock->TlsVector !=
  127. (PVOID *)(ThreadControlBlock + 1)) {
  128. NewVector = OsHeapReallocate(DynamicThreadVector,
  129. NeededSize,
  130. TLS_ALLOCATION_TAG);
  131. if (NewVector == NULL) {
  132. return NULL;
  133. }
  134. RtlZeroMemory(&(NewVector[ThreadGeneration]),
  135. NeededSize - (ThreadGeneration * sizeof(PVOID)));
  136. //
  137. // The TLS vector is part of the initial allocation. See if there's
  138. // still room to grow in that initial allocation. There probably is.
  139. //
  140. } else {
  141. AvailableSize = (ThreadControlBlock->BaseAllocation +
  142. ThreadControlBlock->BaseAllocationSize) -
  143. (PVOID)(ThreadControlBlock + 1);
  144. //
  145. // If there is enough space, just continue using the same vector.
  146. // If there's not, allocate it from the heap.
  147. //
  148. if (AvailableSize >= NeededSize) {
  149. NewVector = ThreadControlBlock->TlsVector;
  150. } else {
  151. NewVector = OsHeapAllocate(NeededSize, TLS_ALLOCATION_TAG);
  152. if (NewVector == NULL) {
  153. return NULL;
  154. }
  155. RtlCopyMemory(NewVector,
  156. ThreadControlBlock->TlsVector,
  157. ThreadGeneration * sizeof(PVOID));
  158. RtlZeroMemory(&(NewVector[ThreadGeneration]),
  159. NeededSize - (ThreadGeneration * sizeof(PVOID)));
  160. }
  161. }
  162. NewVector[0] = (PVOID)Generation;
  163. ThreadControlBlock->TlsVector = NewVector;
  164. ThreadControlBlock->ModuleCount = Generation + 1;
  165. DynamicThreadVector = NewVector;
  166. }
  167. //
  168. // Go initialize the TLS section if this is the first time the module has
  169. // accessed TLS data on this thread.
  170. //
  171. DynamicThreadVector += ModuleId;
  172. if (*DynamicThreadVector == NULL) {
  173. //
  174. // Go find the module.
  175. //
  176. Image = NULL;
  177. OspAcquireImageLock(FALSE);
  178. CurrentEntry = OsLoadedImagesHead.Next;
  179. while (CurrentEntry != &OsLoadedImagesHead) {
  180. Image = LIST_VALUE(CurrentEntry, LOADED_IMAGE, ListEntry);
  181. if (Image->ModuleNumber == ModuleId) {
  182. break;
  183. }
  184. CurrentEntry = CurrentEntry->Next;
  185. }
  186. ASSERT(CurrentEntry != &OsLoadedImagesHead);
  187. ASSERT(Offset < Image->TlsSize);
  188. //
  189. // Allocate enough space to align the region, and store the original
  190. // allocation pointer of the region.
  191. //
  192. Alignment = Image->TlsAlignment;
  193. AllocationSize = sizeof(PVOID) + Image->TlsSize + Alignment;
  194. Allocation = OsHeapAllocate(AllocationSize, TLS_ALLOCATION_TAG);
  195. if (Allocation != NULL) {
  196. //
  197. // Add space for the allocation pointer, then align up to the
  198. // required alignment. Set the actual address of the allocation
  199. // just before what gets set in the vector.
  200. //
  201. if (Alignment <= 1) {
  202. Region = Allocation + sizeof(PVOID);
  203. } else {
  204. Region = (PVOID)(UINTN)ALIGN_RANGE_UP(
  205. (UINTN)Allocation + sizeof(PVOID),
  206. Alignment);
  207. }
  208. *(Region - 1) = Allocation;
  209. *DynamicThreadVector = Region;
  210. RtlCopyMemory(Region, Image->TlsImage, Image->TlsImageSize);
  211. RtlZeroMemory((PVOID)Region + Image->TlsImageSize,
  212. Image->TlsSize - Image->TlsImageSize);
  213. }
  214. OspReleaseImageLock();
  215. }
  216. return *DynamicThreadVector + Offset;
  217. }
  218. OS_API
  219. UINTN
  220. OsGetThreadId (
  221. VOID
  222. )
  223. /*++
  224. Routine Description:
  225. This routine returns the currently running thread's identifier.
  226. Arguments:
  227. None.
  228. Return Value:
  229. Returns the current thread's ID. This number will be unique to the current
  230. thread as long as the thread is running.
  231. --*/
  232. {
  233. //
  234. // For now just return the pointer to the thread control block as a unique
  235. // number.
  236. //
  237. return (UINTN)OspGetThreadControlBlock();
  238. }
  239. OS_API
  240. KSTATUS
  241. OsSetThreadPointer (
  242. PVOID Pointer
  243. )
  244. /*++
  245. Routine Description:
  246. This routine sets the thread control pointer, which points to the thread
  247. control block. This function should only be called by the C library, not by
  248. user applications.
  249. Arguments:
  250. Pointer - Supplies a pointer to associate with the thread in an
  251. architecture-specific way.
  252. Return Value:
  253. Status code.
  254. --*/
  255. {
  256. return OsSystemCall(SystemCallSetThreadPointer, Pointer);
  257. }
  258. VOID
  259. OspInitializeThreadSupport (
  260. VOID
  261. )
  262. /*++
  263. Routine Description:
  264. This routine initializes thread and TLS support in the OS library.
  265. Arguments:
  266. None.
  267. Return Value:
  268. None.
  269. --*/
  270. {
  271. INITIALIZE_LIST_HEAD(&OsThreadList);
  272. OsInitializeLockDefault(&OsThreadListLock);
  273. return;
  274. }
  275. KSTATUS
  276. OspTlsAllocate (
  277. PLIST_ENTRY ImageList,
  278. PVOID *ThreadData
  279. )
  280. /*++
  281. Routine Description:
  282. This routine creates the OS library data necessary to manage a new thread.
  283. This function is usually called by the C library.
  284. Arguments:
  285. ImageList - Supplies a pointer to the head of the list of loaded images.
  286. Elements on this list have type LOADED_IMAGE.
  287. ThreadData - Supplies a pointer where a pointer to the thread data will be
  288. returned on success. It is the callers responsibility to destroy this
  289. thread data.
  290. Return Value:
  291. Status code.
  292. --*/
  293. {
  294. PVOID Allocation;
  295. UINTN AllocationSize;
  296. PLIST_ENTRY CurrentEntry;
  297. PVOID CurrentPointer;
  298. UINTN CurrentSize;
  299. PLOADED_IMAGE Image;
  300. ULONG MapFlags;
  301. UINTN ModuleCount;
  302. KSTATUS Status;
  303. PTHREAD_CONTROL_BLOCK ThreadControlBlock;
  304. UINTN VectorSize;
  305. Allocation = NULL;
  306. //
  307. // Figure out how much to allocate for the thread control block and the
  308. // initial TLS allocations.
  309. //
  310. ModuleCount = 0;
  311. AllocationSize = 0;
  312. ThreadControlBlock = NULL;
  313. CurrentEntry = ImageList->Next;
  314. while (CurrentEntry != ImageList) {
  315. Image = LIST_VALUE(CurrentEntry, LOADED_IMAGE, ListEntry);
  316. CurrentEntry = CurrentEntry->Next;
  317. if (Image->ModuleNumber > ModuleCount) {
  318. ModuleCount = Image->ModuleNumber;
  319. }
  320. if (Image->TlsAlignment <= 1) {
  321. AllocationSize += Image->TlsSize;
  322. } else {
  323. AllocationSize = ALIGN_RANGE_UP(AllocationSize + Image->TlsSize,
  324. Image->TlsAlignment);
  325. }
  326. }
  327. ASSERT(ModuleCount != 0);
  328. ModuleCount += 1;
  329. AllocationSize = ALIGN_RANGE_UP(AllocationSize, sizeof(ULONGLONG));
  330. AllocationSize += sizeof(THREAD_CONTROL_BLOCK);
  331. VectorSize = ModuleCount * sizeof(PVOID);
  332. AllocationSize += VectorSize;
  333. AllocationSize = ALIGN_RANGE_UP(AllocationSize, OsPageSize);
  334. //
  335. // Allocate the region. Don't use the heap since it acquires locks which
  336. // might be left held if fork is called.
  337. //
  338. MapFlags = SYS_MAP_FLAG_ANONYMOUS | SYS_MAP_FLAG_READ | SYS_MAP_FLAG_WRITE;
  339. Status = OsMemoryMap(INVALID_HANDLE,
  340. 0,
  341. AllocationSize,
  342. MapFlags,
  343. &Allocation);
  344. if (!KSUCCESS(Status)) {
  345. goto TlsAllocateEnd;
  346. }
  347. //
  348. // The structure looks like this: |<<< Thread Pointer.
  349. // | TLS | TLS | ... | TLS | TLS | TCB | Dt |
  350. // m m-1 2 1 v
  351. //
  352. // So the thread control block is at the very end (almost).
  353. //
  354. ThreadControlBlock = Allocation + AllocationSize - VectorSize -
  355. sizeof(THREAD_CONTROL_BLOCK);
  356. ThreadControlBlock->Self = ThreadControlBlock;
  357. ThreadControlBlock->ModuleCount = ModuleCount;
  358. ThreadControlBlock->BaseAllocation = Allocation;
  359. ThreadControlBlock->TlsVector = (PVOID *)(ThreadControlBlock + 1);
  360. ThreadControlBlock->TlsVector[0] = (PVOID)OsImModuleGeneration;
  361. ThreadControlBlock->BaseAllocationSize = AllocationSize;
  362. //
  363. // Loop through the modules again, assigning space and initializing the
  364. // image.
  365. //
  366. CurrentEntry = ImageList->Next;
  367. CurrentSize = 0;
  368. while (CurrentEntry != ImageList) {
  369. Image = LIST_VALUE(CurrentEntry, LOADED_IMAGE, ListEntry);
  370. CurrentEntry = CurrentEntry->Next;
  371. ASSERT((Image->ModuleNumber <= ThreadControlBlock->ModuleCount) &&
  372. (Image->ModuleNumber != 0));
  373. if (Image->TlsSize == 0) {
  374. continue;
  375. }
  376. if (Image->TlsAlignment <= 1) {
  377. CurrentSize += Image->TlsSize;
  378. } else {
  379. CurrentSize = ALIGN_RANGE_UP(CurrentSize + Image->TlsSize,
  380. Image->TlsAlignment);
  381. }
  382. ASSERT((Image->TlsOffset == (UINTN)-1) ||
  383. (Image->TlsOffset == CurrentSize));
  384. Image->TlsOffset = CurrentSize;
  385. CurrentPointer = ((PVOID)ThreadControlBlock) - CurrentSize;
  386. //
  387. // It would be bad if a module number was double allocated.
  388. //
  389. ASSERT(ThreadControlBlock->TlsVector[Image->ModuleNumber] == NULL);
  390. ASSERT(CurrentPointer >= Allocation);
  391. //
  392. // Set the vector pointer for this module, and initialize the image.
  393. //
  394. ThreadControlBlock->TlsVector[Image->ModuleNumber] = CurrentPointer;
  395. if (Image->TlsImageSize != 0) {
  396. ASSERT(Image->TlsImageSize <= Image->TlsSize);
  397. RtlCopyMemory(CurrentPointer, Image->TlsImage, Image->TlsImageSize);
  398. }
  399. }
  400. //
  401. // Stick it on the thread list.
  402. //
  403. OsAcquireLock(&OsThreadListLock);
  404. INSERT_BEFORE(&(ThreadControlBlock->ListEntry), &OsThreadList);
  405. OsReleaseLock(&OsThreadListLock);
  406. Status = STATUS_SUCCESS;
  407. TlsAllocateEnd:
  408. if (!KSUCCESS(Status)) {
  409. if (Allocation != NULL) {
  410. OsMemoryUnmap(Allocation, AllocationSize);
  411. Allocation = NULL;
  412. }
  413. }
  414. *ThreadData = ThreadControlBlock;
  415. return Status;
  416. }
  417. VOID
  418. OspTlsDestroy (
  419. PVOID ThreadData
  420. )
  421. /*++
  422. Routine Description:
  423. This routine destroys a previously created thread data structure. Callers
  424. may not use OS library assisted TLS after this routine completes. Signals
  425. should also probably be masked.
  426. Arguments:
  427. ThreadData - Supplies a pointer to the thread data to destroy.
  428. Return Value:
  429. None.
  430. --*/
  431. {
  432. PVOID *AllocationPointer;
  433. UINTN Index;
  434. PTHREAD_CONTROL_BLOCK ThreadControlBlock;
  435. PVOID TlsBlock;
  436. ThreadControlBlock = ThreadData;
  437. for (Index = 1; Index < ThreadControlBlock->ModuleCount; Index += 1) {
  438. TlsBlock = ThreadControlBlock->TlsVector[Index];
  439. //
  440. // Don't free empty slots or slots that were in the initial allocation.
  441. //
  442. if ((TlsBlock == NULL) ||
  443. ((TlsBlock >= ThreadControlBlock->BaseAllocation) &&
  444. (TlsBlock < (PVOID)ThreadControlBlock))) {
  445. continue;
  446. }
  447. //
  448. // The value in this array may have been moved up from the actual
  449. // allocation due to alignment requirements. So the actual address of
  450. // the allocation is stored right below the region.
  451. //
  452. AllocationPointer = TlsBlock;
  453. OsHeapFree(*(AllocationPointer - 1));
  454. }
  455. //
  456. // If the TLS vector is not part of the initial allocation, free it.
  457. //
  458. if (ThreadControlBlock->TlsVector != (PVOID *)(ThreadControlBlock + 1)) {
  459. OsHeapFree(ThreadControlBlock->TlsVector);
  460. }
  461. OsAcquireLock(&OsThreadListLock);
  462. LIST_REMOVE(&(ThreadControlBlock->ListEntry));
  463. OsReleaseLock(&OsThreadListLock);
  464. ThreadControlBlock->Self = NULL;
  465. OsMemoryUnmap(ThreadControlBlock->BaseAllocation,
  466. ThreadControlBlock->BaseAllocationSize);
  467. return;
  468. }
  469. VOID
  470. OspTlsTearDownModule (
  471. PLOADED_IMAGE Image
  472. )
  473. /*++
  474. Routine Description:
  475. This routine is called when a module is unloaded. It goes through and
  476. frees all the TLS images for the module.
  477. Arguments:
  478. Image - Supplies a pointer to the image being unloaded.
  479. Return Value:
  480. None.
  481. --*/
  482. {
  483. PLIST_ENTRY CurrentEntry;
  484. UINTN ModuleNumber;
  485. PTHREAD_CONTROL_BLOCK ThreadControlBlock;
  486. PVOID *TlsData;
  487. if (Image->TlsSize == 0) {
  488. return;
  489. }
  490. OsAcquireLock(&OsThreadListLock);
  491. //
  492. // Loop through all threads and destroy the TLS block for this image.
  493. // The list is guarded by the image list lock held by the caller.
  494. //
  495. ModuleNumber = Image->ModuleNumber;
  496. CurrentEntry = OsThreadList.Next;
  497. while (CurrentEntry != &OsThreadList) {
  498. ThreadControlBlock = LIST_VALUE(CurrentEntry,
  499. THREAD_CONTROL_BLOCK,
  500. ListEntry);
  501. CurrentEntry = CurrentEntry->Next;
  502. if (ThreadControlBlock->ModuleCount < ModuleNumber) {
  503. continue;
  504. }
  505. TlsData = ThreadControlBlock->TlsVector[ModuleNumber];
  506. if (TlsData != NULL) {
  507. //
  508. // The actual allocation pointer is stored just below the TLS data
  509. // itself, as the buffer may have been scootched up for alignment.
  510. // Don't try to free something from part of the initial allocation.
  511. //
  512. if (!(((PVOID)TlsData >= ThreadControlBlock->BaseAllocation) &&
  513. ((PVOID)TlsData < (PVOID)ThreadControlBlock))) {
  514. OsHeapFree(*(TlsData - 1));
  515. }
  516. ThreadControlBlock->TlsVector[ModuleNumber] = NULL;
  517. }
  518. }
  519. OsReleaseLock(&OsThreadListLock);
  520. return;
  521. }
  522. //
  523. // --------------------------------------------------------- Internal Functions
  524. //