void tls_test(void) { get_tls_info(&g_save_info); do_tls_test(0); do_tls_test(0xffffffff); do_tls_test(0x55555555); do_tls_test(0xaaaaaaaa); put_tls_info(&g_save_info); }
void *__nacl_tls_initialize_memory(void *combined_area, size_t tdb_size) { const struct tls_info *info = get_tls_info(); size_t tls_size = info->tdata_size + info->tbss_size; char *combined_area_end = (char *) combined_area + __nacl_tls_combined_size(tdb_size); void *tp = tp_from_combined_area(info, combined_area, tdb_size); char *start = tp; if (__nacl_tp_tls_offset(0) > 0) { /* * From $tp, we skip the header size and then must round up from * there to the required alignment (which is what the linker will * will do when calculating TPOFF relocations at link time). The * end result is that the offset from $tp matches the one chosen * by the linker exactly and that the final address is aligned to * info->tls_alignment (since $tp was already aligned to at least * that much). */ start += aligned_size(__nacl_tp_tls_offset(tls_size), info->tls_alignment); } else { /* * We'll subtract the aligned size of the TLS block from $tp, which * must itself already be adequately aligned. */ start += __nacl_tp_tls_offset(aligned_size(tls_size, info->tls_alignment)); } /* Sanity check. (But avoid pulling in assert() here.) */ if (start + info->tdata_size + info->tbss_size > combined_area_end) simple_abort(); memcpy(start, info->tdata_start, info->tdata_size); memset(start + info->tdata_size, 0, info->tbss_size); if (__nacl_tp_tdb_offset(tdb_size) == 0) { /* * On x86 (but not on ARM), the TDB sits directly at $tp and the * first word there must hold the $tp pointer itself. */ void *tdb = (char *) tp + __nacl_tp_tdb_offset(tdb_size); *(void **) tdb = tdb; } return tp; }
size_t __nacl_tls_combined_size(size_t tdb_size) { const struct tls_info *info = get_tls_info(); size_t tls_size = info->tdata_size + info->tbss_size; ptrdiff_t tlsoff = __nacl_tp_tls_offset(tls_size); size_t combined_size = tls_size + tdb_size; /* * __nacl_tls_initialize_memory() accepts a non-aligned pointer; it * aligns the thread pointer itself. We have to reserve some extra * space to allow this alignment padding to occur. */ combined_size += info->tls_alignment - 1; if (tlsoff > 0) { /* * ARM case: We have to add ARM's 8 byte header, because that is * not incorporated into tls_size. Furthermore, the header is * padded out to tls_alignment. */ combined_size += aligned_size(tlsoff, info->tls_alignment); } return combined_size; }