+static int
+try_offset_access (void *value, guint32 idx)
+{
+ register void* me __asm__ ("r2");
+ void ***p = (void***)((char*)me + 284);
+ int idx1 = idx / 32;
+ int idx2 = idx % 32;
+ if (!p [idx1])
+ return 0;
+ if (value != p[idx1][idx2])
+ return 0;
+ return 1;
+}
+
+static void
+setup_tls_access (void)
+{
+ guint32 ptk;
+ guint32 *ins, *code;
+ guint32 cmplwi_1023, li_0x48, blr_ins;
+ if (tls_mode == TLS_MODE_FAILED)
+ return;
+
+ if (g_getenv ("MONO_NO_TLS")) {
+ tls_mode = TLS_MODE_FAILED;
+ return;
+ }
+
+ if (tls_mode == TLS_MODE_DETECT) {
+ ins = (guint32*)pthread_getspecific;
+ /* uncond branch to the real method */
+ if ((*ins >> 26) == 18) {
+ gint32 val;
+ val = (*ins & ~3) << 6;
+ val >>= 6;
+ if (*ins & 2) {
+ /* absolute */
+ ins = (guint32*)val;
+ } else {
+ ins = (guint32*) ((char*)ins + val);
+ }
+ }
+ code = &cmplwi_1023;
+ ppc_cmpli (code, 0, 0, ppc_r3, 1023);
+ code = &li_0x48;
+ ppc_li (code, ppc_r4, 0x48);
+ code = &blr_ins;
+ ppc_blr (code);
+ if (*ins == cmplwi_1023) {
+ int found_lwz_284 = 0;
+ for (ptk = 0; ptk < 20; ++ptk) {
+ ++ins;
+ if (!*ins || *ins == blr_ins)
+ break;
+ if ((guint16)*ins == 284 && (*ins >> 26) == 32) {
+ found_lwz_284 = 1;
+ break;
+ }
+ }
+ if (!found_lwz_284) {
+ tls_mode = TLS_MODE_FAILED;
+ return;
+ }
+ tls_mode = TLS_MODE_LTHREADS;
+ } else if (*ins == li_0x48) {
+ ++ins;
+ /* uncond branch to the real method */
+ if ((*ins >> 26) == 18) {
+ gint32 val;
+ val = (*ins & ~3) << 6;
+ val >>= 6;
+ if (*ins & 2) {
+ /* absolute */
+ ins = (guint32*)val;
+ } else {
+ ins = (guint32*) ((char*)ins + val);
+ }
+ code = &val;
+ ppc_li (code, ppc_r0, 0x7FF2);
+ if (ins [1] == val) {
+ /* Darwin on G4, implement */
+ tls_mode = TLS_MODE_FAILED;
+ return;
+ } else {
+ code = &val;
+ ppc_mfspr (code, ppc_r3, 104);
+ if (ins [1] != val) {
+ tls_mode = TLS_MODE_FAILED;
+ return;
+ }
+ tls_mode = TLS_MODE_DARWIN_G5;
+ }
+ } else {
+ tls_mode = TLS_MODE_FAILED;
+ return;
+ }
+ } else {
+ tls_mode = TLS_MODE_FAILED;
+ return;
+ }
+ }
+ if (monodomain_key == -1) {
+ ptk = mono_domain_get_tls_key ();
+ if (ptk < 1024) {
+ ptk = mono_pthread_key_for_tls (ptk);
+ if (ptk < 1024) {
+ monodomain_key = ptk;
+ }
+ }
+ }
+ if (lmf_pthread_key == -1) {
+ ptk = mono_pthread_key_for_tls (mono_jit_tls_id);
+ if (ptk < 1024) {
+ /*g_print ("MonoLMF at: %d\n", ptk);*/
+ /*if (!try_offset_access (mono_get_lmf_addr (), ptk)) {
+ init_tls_failed = 1;
+ return;
+ }*/
+ lmf_pthread_key = ptk;
+ }
+ }
+ if (monothread_key == -1) {
+ ptk = mono_thread_get_tls_key ();
+ if (ptk < 1024) {
+ ptk = mono_pthread_key_for_tls (ptk);
+ if (ptk < 1024) {
+ monothread_key = ptk;
+ /*g_print ("thread inited: %d\n", ptk);*/
+ }
+ } else {
+ /*g_print ("thread not inited yet %d\n", ptk);*/
+ }
+ }
+}
+