1 /**
2  * Generate elems for fixed, PIC, and PIE code generation.
3  *
4  * Compiler implementation of the
5  * $(LINK2 https://www.dlang.org, D programming language).
6  *
7  * Copyright:   Copyright (C) 1985-1998 by Symantec
8  *              Copyright (C) 2000-2023 by The D Language Foundation, All Rights Reserved
9  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
10  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
11  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/elpicpie.d, backend/elpicpie.d)
12  */
13 
14 module dmd.backend.elpicpie;
15 
16 import core.stdc.stdarg;
17 import core.stdc.stdio;
18 import core.stdc.stdlib;
19 import core.stdc.string;
20 
21 import dmd.backend.cdef;
22 import dmd.backend.cc;
23 import dmd.backend.code;
24 import dmd.backend.code_x86;
25 import dmd.backend.el;
26 import dmd.backend.global;
27 import dmd.backend.obj;
28 import dmd.backend.oper;
29 import dmd.backend.rtlsym;
30 import dmd.backend.ty;
31 import dmd.backend.type;
32 
33 
34 nothrow:
35 @safe:
36 
37 /**************************
38  * Make an elem out of a symbol.
39  */
40 @trusted
41 elem * el_var(Symbol *s)
42 {
43     elem *e;
44     //printf("el_var(s = '%s')\n", s.Sident.ptr);
45     //printf("%x\n", s.Stype.Tty);
46     if (config.exe & EX_posix)
47     {
48         if (config.flags3 & CFG3pie &&
49             s.Stype.Tty & mTYthread &&
50             (s.Sclass == SC.global ||
51              s.Sclass == SC.static_ ||
52              s.Sclass == SC.locstat))
53         {
54         }
55         else
56         {
57             if (config.flags3 & CFG3pie &&
58                 s.Stype.Tty & mTYthread)
59             {
60                 return el_pievar(s);            // Position Independent Executable
61             }
62 
63             if (config.flags3 & CFG3pic &&
64                 !tyfunc(s.ty()))
65             {
66                 return el_picvar(s);            // Position Independent Code
67             }
68         }
69     }
70 
71     if (config.exe & (EX_OSX | EX_OSX64))
72     {
73     }
74     else if (config.exe & EX_posix)
75     {
76         if (config.flags3 & CFG3pic && tyfunc(s.ty()))
77         {
78             switch (s.Sclass)
79             {
80                 case SC.comdat:
81                 case SC.comdef:
82                 case SC.global:
83                 case SC.extern_:
84                     el_alloc_localgot();
85                     break;
86 
87                 default:
88                     break;
89             }
90         }
91     }
92     symbol_debug(s);
93     type_debug(s.Stype);
94     e = el_calloc();
95     e.Eoper = OPvar;
96     e.EV.Vsym = s;
97     type_debug(s.Stype);
98     e.Ety = s.ty();
99     if (s.Stype.Tty & mTYthread)
100     {
101         //printf("thread local %s\n", s.Sident.ptr);
102 if (config.exe & (EX_OSX | EX_OSX64))
103 {
104 }
105 else if (config.exe & EX_posix)
106 {
107         /* For 32 bit:
108          * Generate for var locals:
109          *      MOV reg,GS:[00000000]   // add GS: override in back end
110          *      ADD reg, offset s@TLS_LE
111          *      e => *(&s + *(GS:0))
112          * For var globals:
113          *      MOV reg,GS:[00000000]
114          *      ADD reg, s@TLS_IE
115          *      e => *(s + *(GS:0))
116          * note different fixup
117          *****************************************
118          * For 64 bit:
119          * Generate for var locals:
120          *      MOV reg,FS:s@TPOFF32
121          * For var globals:
122          *      MOV RAX,s@GOTTPOFF[RIP]
123          *      MOV reg,FS:[RAX]
124          *
125          * For address of locals:
126          *      MOV RAX,FS:[00]
127          *      LEA reg,s@TPOFF32[RAX]
128          *      e => &s + *(FS:0)
129          * For address of globals:
130          *      MOV reg,FS:[00]
131          *      MOV RAX,s@GOTTPOFF[RIP]
132          *      ADD reg,RAX
133          *      e => s + *(FS:0)
134          * This leaves us with a problem, as the 'var' version cannot simply have
135          * its address taken, as what is the address of FS:s ? The (not so efficient)
136          * solution is to just use the second address form, and * it.
137          * Turns out that is identical to the 32 bit version, except GS => FS and the
138          * fixups are different.
139          * In the future, we should figure out a way to optimize to the 'var' version.
140          */
141         if (I64)
142             Obj.refGOTsym();
143         elem *e1 = el_calloc();
144         e1.EV.Vsym = s;
145         if (s.Sclass == SC.global ||
146             s.Sclass == SC.static_ ||
147             s.Sclass == SC.locstat)
148         {
149             e1.Eoper = OPrelconst;
150             e1.Ety = TYnptr;
151         }
152         else
153         {
154             e1.Eoper = OPvar;
155             e1.Ety = TYnptr;
156         }
157 
158         elem* e2 = el_una(OPind, TYsize, el_long(TYfgPtr, 0)); // I64: FS:[0000], I32: GS:[0000]
159 
160         e.Eoper = OPind;
161         e.EV.E1 = el_bin(OPadd,e1.Ety,e2,e1);
162         e.EV.E2 = null;
163 }
164 else if (config.exe & EX_windos)
165 {
166         /*
167             Win32:
168                 mov     EAX,FS:__tls_array
169                 mov     ECX,__tls_index
170                 mov     EAX,[ECX*4][EAX]
171                 inc     dword ptr _t[EAX]
172 
173                 e => *(&s + *(FS:_tls_array + _tls_index * 4))
174 
175                 If this is an executable app, not a dll, _tls_index
176                 can be assumed to be 0.
177 
178             Win64:
179 
180                 mov     EAX,&s
181                 mov     RDX,GS:__tls_array
182                 mov     ECX,_tls_index[RIP]
183                 mov     RCX,[RCX*8][RDX]
184                 mov     EAX,[RCX][RAX]
185 
186                 e => *(&s + *(GS:[80] + _tls_index * 8))
187 
188                 If this is an executable app, not a dll, _tls_index
189                 can be assumed to be 0.
190          */
191         elem* e1,e2,ea;
192 
193         e1 = el_calloc();
194         e1.Eoper = OPrelconst;
195         e1.EV.Vsym = s;
196         e1.Ety = TYnptr;
197 
198         if (false && config.wflags & WFexe) // disabled to work with betterC/importC
199         {
200             // e => *(&s + *(FS:_tls_array))
201             e2 = el_var(getRtlsym(RTLSYM.TLS_ARRAY));
202         }
203         else
204         {
205             e2 = el_bin(OPmul,TYint,el_var(getRtlsym(RTLSYM.TLS_INDEX)),el_long(TYint,REGSIZE));
206             ea = el_var(getRtlsym(RTLSYM.TLS_ARRAY));
207             e2 = el_bin(OPadd,ea.Ety,ea,e2);
208         }
209         e2 = el_una(OPind,TYsize_t,e2);
210 
211         e.Eoper = OPind;
212         e.EV.E1 = el_bin(OPadd,e1.Ety,e1,e2);
213         e.EV.E2 = null;
214 }
215     }
216     return e;
217 }
218 
219 /**************************
220  * Make a pointer to a `Symbol`.
221  * Params: s = symbol
222  * Returns: `elem` with address of `s`
223  */
224 
225 @trusted
226 elem * el_ptr(Symbol *s)
227 {
228     //printf("el_ptr(s = '%s')\n", s.Sident.ptr);
229     //printf("el_ptr\n");
230     symbol_debug(s);
231     type_debug(s.Stype);
232 
233     const typtr = s.symbol_pointerType();
234 
235     if (config.exe & (EX_OSX | EX_OSX64))
236     {
237         if (config.flags3 & CFG3pic && tyfunc(s.ty()) && I32)
238         {
239             /* Cannot access address of code from code.
240              * Instead, create a data variable, put the address of the
241              * code in that data variable, and return the elem for
242              * that data variable.
243              */
244             Symbol *sd = symboldata(Offset(DATA), typtr);
245             sd.Sseg = DATA;
246             Obj.data_start(sd, _tysize[TYnptr], DATA);
247             Offset(DATA) += Obj.reftoident(DATA, Offset(DATA), s, 0, CFoff);
248             elem* e = el_picvar(sd);
249             e.Ety = typtr;
250             return e;
251         }
252     }
253 
254     if (config.exe & EX_posix)
255     {
256         if (config.flags3 & CFG3pie &&
257             s.Stype.Tty & mTYthread)
258         {
259             elem* e = el_pieptr(s);            // Position Independent Executable
260             e.Ety = typtr;
261             return e;
262         }
263 
264         if (config.flags3 & CFG3pie &&
265             tyfunc(s.ty()) &&
266             (s.Sclass == SC.global || s.Sclass == SC.comdat ||
267              s.Sclass == SC.comdef || s.Sclass == SC.extern_))
268         {
269             elem* e = el_calloc();
270             e.Eoper = OPvar;
271             e.EV.Vsym = s;
272             if (I64)
273                 e.Ety = typtr;
274             else if (I32)
275             {
276                 e.Ety = TYnptr;
277                 e.Eoper = OPrelconst;
278                 e = el_bin(OPadd, TYnptr, e, el_var(el_alloc_localgot()));
279                 e = el_una(OPind, typtr, e);
280             }
281             else
282                 assert(0);
283             return e;
284         }
285     }
286 
287     elem *e;
288 
289     if (config.exe & EX_posix)
290     {
291         if (config.flags3 & CFG3pic &&
292             tyfunc(s.ty()))
293         {
294             e = el_picvar(s);
295         }
296         else
297             e = el_var(s);
298     }
299     else
300         e = el_var(s);
301 
302     if (e.Eoper == OPvar)
303     {
304         e.Ety = typtr;
305         e.Eoper = OPrelconst;
306     }
307     else
308     {
309         e = el_una(OPaddr, typtr, e);
310         e = doptelem(e, GOALvalue | GOALflags);
311     }
312     return e;
313 }
314 
315 
316 /***************************************
317  * Allocate localgot symbol.
318  */
319 
320 @trusted
321 private Symbol *el_alloc_localgot()
322 {
323     if (config.exe & EX_windos)
324         return null;
325 
326     /* Since localgot is a local variable to each function,
327      * localgot must be set back to null
328      * at the start of code gen for each function.
329      */
330     if (I32 && !localgot)
331     {
332         //printf("el_alloc_localgot()\n");
333         char[15] name = void;
334         __gshared int tmpnum;
335         const length = snprintf(name.ptr, name.length, "_LOCALGOT%d".ptr, tmpnum++);
336         type *t = type_fake(TYnptr);
337         /* Make it volatile because we need it for calling functions, but that isn't
338          * noticed by the data flow analysis. Hence, it may get deleted if we don't
339          * make it volatile.
340          */
341         type_setcv(&t, mTYvolatile);
342         localgot = symbol_name(name[0 .. length], SC.auto_, t);
343         symbol_add(localgot);
344         localgot.Sfl = FLauto;
345         localgot.Sflags = SFLfree | SFLunambig | GTregcand;
346     }
347     return localgot;
348 }
349 
350 
351 /**************************
352  * Make an elem out of a symbol, PIC style.
353  */
354 
355 @trusted
356 private elem *el_picvar(Symbol *s)
357 {
358     if (config.exe & (EX_OSX | EX_OSX64))
359         return el_picvar_OSX(s);
360     else if (config.exe & EX_posix)
361         return el_picvar_posix(s);
362     assert(0);
363 }
364 
365 @trusted
366 private elem *el_picvar_OSX(Symbol *s)
367 {
368     elem *e;
369     int x;
370 
371     //printf("el_picvar(s = '%s') Sclass = %s\n", s.Sident.ptr, class_str(s.Sclass));
372     //symbol_print(s);
373     symbol_debug(s);
374     type_debug(s.Stype);
375     e = el_calloc();
376     e.Eoper = OPvar;
377     e.EV.Vsym = s;
378     e.Ety = s.ty();
379 
380     switch (s.Sclass)
381     {
382         case SC.static_:
383         case SC.locstat:
384             if (s.Stype.Tty & mTYthread)
385                 x = 1;
386             else
387                 x = 0;
388             goto case_got;
389 
390         case SC.comdat:
391         case SC.comdef:
392             if (0 && I64)
393             {
394                 x = 0;
395                 goto case_got;
396             }
397             goto case SC.global;
398 
399         case SC.global:
400         case SC.extern_:
401             static if (0)
402             {
403                 if (s.Stype.Tty & mTYthread)
404                     x = 0;
405                 else
406                     x = 1;
407             }
408             else
409                 x = 1;
410 
411         case_got:
412         {
413             const op = e.Eoper;
414             tym_t tym = e.Ety;
415             e.Eoper = OPrelconst;
416             e.Ety = TYnptr;
417             if (I32)
418                 e = el_bin(OPadd, TYnptr, e, el_var(el_alloc_localgot()));
419 static if (1)
420 {
421             if (I32 && s.Stype.Tty & mTYthread)
422             {
423                 if (!tls_get_addr_sym)
424                 {
425                     /* void *___tls_get_addr(void *ptr);
426                      * Parameter ptr is passed in RDI, matching TYnfunc calling convention.
427                      */
428                     tls_get_addr_sym = symbol_name("___tls_get_addr",SC.global,type_fake(TYnfunc));
429                     symbol_keep(tls_get_addr_sym);
430                 }
431                 if (x == 1)
432                     e = el_una(OPind, TYnptr, e);
433                 e = el_bin(OPcallns, TYnptr, el_var(tls_get_addr_sym), e);
434                 if (op == OPvar)
435                     e = el_una(OPind, TYnptr, e);
436             }
437 }
438             if (I64 || !(s.Stype.Tty & mTYthread))
439             {
440                 switch (op * 2 + x)
441                 {
442                     case OPvar * 2 + 1:
443                         e = el_una(OPind, TYnptr, e);
444                         e = el_una(OPind, TYnptr, e);
445                         break;
446 
447                     case OPvar * 2 + 0:
448                     case OPrelconst * 2 + 1:
449                         e = el_una(OPind, TYnptr, e);
450                         break;
451 
452                     case OPrelconst * 2 + 0:
453                         break;
454 
455                     default:
456                         assert(0);
457                 }
458             }
459 static if (1)
460 {
461             /**
462              * A thread local variable is outputted like the following D struct:
463              *
464              * struct TLVDescriptor(T)
465              * {
466              *     extern(C) T* function (TLVDescriptor*) thunk;
467              *     size_t key;
468              *     size_t offset;
469              * }
470              *
471              * To access the value of the variable, the variable is accessed
472              * like a plain global (__gshared) variable of the type
473              * TLVDescriptor. The thunk is called and a pointer to the variable
474              * itself is passed as the argument. The return value of the thunk
475              * is a pointer to the value of the thread local variable.
476              *
477              * module foo;
478              *
479              * int bar;
480              * pragma(mangle, "_D3foo3bari") extern __gshared TLVDescriptor!(int) barTLV;
481              *
482              * int a = *barTLV.thunk(&barTLV);
483              */
484             if (I64 && s.Stype.Tty & mTYthread)
485             {
486                 e = el_una(OPaddr, TYnptr, e);
487                 e = el_bin(OPadd, TYnptr, e, el_long(TYullong, 0));
488                 e = el_una(OPind, TYnptr, e);
489                 e = el_una(OPind, TYnfunc, e);
490 
491                 elem *e2 = el_calloc();
492                 e2.Eoper = OPvar;
493                 e2.EV.Vsym = s;
494                 e2.Ety = s.ty();
495                 e2.Eoper = OPrelconst;
496                 e2.Ety = TYnptr;
497 
498                 e2 = el_una(OPind, TYnptr, e2);
499                 e2 = el_una(OPind, TYnptr, e2);
500                 e2 = el_una(OPaddr, TYnptr, e2);
501                 e2 = doptelem(e2, GOALvalue | GOALflags);
502                 e2 = el_bin(OPadd, TYnptr, e2, el_long(TYullong, 0));
503                 e2 = el_bin(OPcall, TYnptr, e, e2);
504                 e2 = el_una(OPind, TYint, e2);
505                 e = e2;
506             }
507 }
508             e.Ety = tym;
509             break;
510         }
511         default:
512             break;
513     }
514     return e;
515 }
516 
517 @trusted
518 private elem *el_picvar_posix(Symbol *s)
519 {
520     elem *e;
521     int x;
522 
523     //printf("el_picvar(s = '%s')\n", s.Sident.ptr);
524     symbol_debug(s);
525     type_debug(s.Stype);
526     e = el_calloc();
527     e.Eoper = OPvar;
528     e.EV.Vsym = s;
529     e.Ety = s.ty();
530 
531     /* For 32 bit PIC:
532      *      CALL __i686.get_pc_thunk.bx@PC32
533      *      ADD  EBX,offset _GLOBAL_OFFSET_TABLE_@GOTPC[2]
534      * Generate for var locals:
535      *      MOV  reg,s@GOTOFF[014h][EBX]
536      * For var globals:
537      *      MOV  EAX,s@GOT32[EBX]
538      *      MOV  reg,[EAX]
539      * For TLS var locals and globals:
540      *      LEA  EAX,s@TLS_GD[1*EBX+0] // must use SIB addressing
541      *      CALL ___tls_get_addr@PLT32
542      *      MOV  reg,[EAX]
543      *****************************************
544      * Generate for var locals:
545      *      MOV reg,s@PC32[RIP]
546      * For var globals:
547      *      MOV RAX,s@GOTPCREL[RIP]
548      *      MOV reg,[RAX]
549      * For TLS var locals and globals:
550      *      0x66
551      *      LEA DI,s@TLSGD[RIP]
552      *      0x66
553      *      0x66
554      *      0x48 (REX | REX_W)
555      *      CALL __tls_get_addr@PLT32
556      *      MOV reg,[RAX]
557      */
558 
559     if (I64)
560     {
561         switch (s.Sclass)
562         {
563             case SC.static_:
564             case SC.locstat:
565                 if (config.flags3 & CFG3pie)
566                     x = 0;
567                 else if (s.Stype.Tty & mTYthread)
568                     x = 1;
569                 else
570                     x = 0;
571                 goto case_got64;
572 
573             case SC.global:
574                 if (config.flags3 & CFG3pie)
575                     x = 0;
576                 else
577                     x = 1;
578                 goto case_got64;
579 
580             case SC.comdat:
581             case SC.comdef:
582             case SC.extern_:
583                 x = 1;
584                 goto case_got64;
585 
586             case_got64:
587             {
588                 Obj.refGOTsym();
589                 const op = e.Eoper;
590                 tym_t tym = e.Ety;
591                 e.Ety = TYnptr;
592 
593                 if (s.Stype.Tty & mTYthread)
594                 {
595                     /* Add "volatile" to prevent e from being common subexpressioned.
596                      * This is so we can preserve the magic sequence of instructions
597                      * that the gnu linker patches:
598                      *   lea EDI,x@tlsgd[RIP], call __tls_get_addr@plt
599                      *      =>
600                      *   mov EAX,gs[0], sub EAX,x@tpoff
601                      */
602                     e.Eoper = OPrelconst;
603                     e.Ety |= mTYvolatile;
604                     if (!tls_get_addr_sym)
605                     {
606                         /* void *__tls_get_addr(void *ptr);
607                          * Parameter ptr is passed in RDI, matching TYnfunc calling convention.
608                          */
609                         tls_get_addr_sym = symbol_name("__tls_get_addr",SC.global,type_fake(TYnfunc));
610                         symbol_keep(tls_get_addr_sym);
611                     }
612                     e = el_bin(OPcall, TYnptr, el_var(tls_get_addr_sym), e);
613                 }
614 
615                 switch (op * 2 + x)
616                 {
617                     case OPvar * 2 + 1:
618                         e = el_una(OPind, TYnptr, e);
619                         break;
620 
621                     case OPvar * 2 + 0:
622                     case OPrelconst * 2 + 1:
623                         break;
624 
625                     case OPrelconst * 2 + 0:
626                         e = el_una(OPaddr, TYnptr, e);
627                         break;
628 
629                     default:
630                         assert(0);
631                 }
632                 e.Ety = tym;
633             }
634                 break;
635 
636             default:
637                 break;
638         }
639     }
640     else
641     {
642         switch (s.Sclass)
643         {
644             /* local (and thread) symbols get only one level of indirection;
645              * all globally known symbols get two.
646              */
647             case SC.static_:
648             case SC.locstat:
649                 x = 0;
650                 goto case_got;
651 
652             case SC.global:
653                 if (config.flags3 & CFG3pie)
654                     x = 0;
655                 else if (s.Stype.Tty & mTYthread)
656                     x = 0;
657                 else
658                     x = 1;
659                 goto case_got;
660 
661             case SC.comdat:
662             case SC.comdef:
663             case SC.extern_:
664                 if (s.Stype.Tty & mTYthread)
665                     x = 0;
666                 else
667                     x = 1;
668             case_got:
669             {
670                 const op = e.Eoper;
671                 tym_t tym = e.Ety;
672                 e.Eoper = OPrelconst;
673                 e.Ety = TYnptr;
674 
675                 if (s.Stype.Tty & mTYthread)
676                 {
677                     /* Add "volatile" to prevent e from being common subexpressioned.
678                      * This is so we can preserve the magic sequence of instructions
679                      * that the gnu linker patches:
680                      *   lea EAX,x@tlsgd[1*EBX+0], call __tls_get_addr@plt
681                      *      =>
682                      *   mov EAX,gs[0], sub EAX,x@tpoff
683                      * elf32-i386.c
684                      */
685                     e.Ety |= mTYvolatile;
686                     if (!tls_get_addr_sym)
687                     {
688                         /* void *___tls_get_addr(void *ptr);
689                          * Parameter ptr is passed in EAX, matching TYjfunc calling convention.
690                          */
691                         tls_get_addr_sym = symbol_name("___tls_get_addr",SC.global,type_fake(TYjfunc));
692                         symbol_keep(tls_get_addr_sym);
693                     }
694                     e = el_bin(OPcall, TYnptr, el_var(tls_get_addr_sym), e);
695                 }
696                 else
697                 {
698                     e = el_bin(OPadd, TYnptr, e, el_var(el_alloc_localgot()));
699                 }
700 
701                 switch (op * 2 + x)
702                 {
703                     case OPvar * 2 + 1:
704                         e = el_una(OPind, TYnptr, e);
705                         e = el_una(OPind, TYnptr, e);
706                         break;
707 
708                     case OPvar * 2 + 0:
709                     case OPrelconst * 2 + 1:
710                         e = el_una(OPind, TYnptr, e);
711                         break;
712 
713                     case OPrelconst * 2 + 0:
714                         break;
715 
716                     default:
717                         assert(0);
718                 }
719                 e.Ety = tym;
720                 break;
721             }
722             default:
723                 break;
724         }
725     }
726     return e;
727 }
728 
729 /**********************************************
730  * Create an elem for TLS variable `s`.
731  * Use PIE protocol.
732  * Params: s = variable's symbol
733  * Returns: elem created
734  */
735 @trusted
736 private elem *el_pievar(Symbol *s)
737 {
738     if (config.exe & (EX_OSX | EX_OSX64))
739         assert(0);
740 
741     int x;
742 
743     //printf("el_pievar(s = '%s')\n", s.Sident.ptr);
744     symbol_debug(s);
745     type_debug(s.Stype);
746     auto e = el_calloc();
747     e.Eoper = OPvar;
748     e.EV.Vsym = s;
749     e.Ety = s.ty();
750 
751     if (I64)
752     {
753         switch (s.Sclass)
754         {
755             case SC.static_:
756             case SC.locstat:
757             case SC.global:
758                 assert(0);
759 
760             case SC.comdat:
761             case SC.comdef:
762             case SC.extern_:
763             {
764                 /* Generate:
765                  *   mov RAX,extern_tls@GOTTPOFF[RIP]
766                  *   mov EAX,FS:[RAX]
767                  */
768                 Obj.refGOTsym();
769                 tym_t tym = e.Ety;
770                 e.Ety = TYfgPtr;
771 
772                 e = el_una(OPind, tym, e);
773                 break;
774             }
775             default:
776                 break;
777         }
778     }
779     else
780     {
781         switch (s.Sclass)
782         {
783             case SC.static_:
784             case SC.locstat:
785             case SC.global:
786                 assert(0);
787 
788             case SC.comdat:
789             case SC.comdef:
790             case SC.extern_:
791             {
792                 /* Generate:
793                  *   mov EAX,extern_tls@TLS_GOTIE[ECX]
794                  *   mov EAX,GS:[EAX]
795                  */
796                 tym_t tym = e.Ety;
797                 e.Eoper = OPrelconst;
798                 e.Ety = TYnptr;
799 
800                 e = el_bin(OPadd, TYnptr, e, el_var(el_alloc_localgot()));
801                 e = el_una(OPind, TYfgPtr, e);
802                 e = el_una(OPind, tym, e);
803                 break;
804             }
805             default:
806                 break;
807         }
808     }
809     return e;
810 }
811 
812 /**********************************************
813  * Create an address for TLS variable `s`.
814  * Use PIE protocol.
815  * Params: s = variable's symbol
816  * Returns: elem created
817  */
818 @trusted
819 private elem *el_pieptr(Symbol *s)
820 {
821     if (config.exe & (EX_OSX | EX_OSX64))
822         assert(0);
823 
824     int x;
825 
826     //printf("el_pieptr(s = '%s')\n", s.Sident.ptr);
827     symbol_debug(s);
828     type_debug(s.Stype);
829     auto e = el_calloc();
830     e.Eoper = OPrelconst;
831     e.EV.Vsym = s;
832     e.Ety = TYnptr;
833 
834     elem* e0 = el_una(OPind, TYsize, el_long(TYfgPtr, 0)); // I64: FS:[0000], I32: GS:[0000]
835 
836     if (I64)
837     {
838         Obj.refGOTsym();    // even though not used, generate reference to _GLOBAL_OFFSET_TABLE_
839         switch (s.Sclass)
840         {
841             case SC.static_:
842             case SC.locstat:
843             case SC.global:
844             {
845                 /* Generate:
846                  *   mov RAX,FS:[0000]
847                  *   add EAX,offset FLAG:global_tls@TPOFF32
848                  */
849                 e = el_bin(OPadd, TYnptr, e0, e);
850                 break;
851             }
852 
853             case SC.comdat:
854             case SC.comdef:
855             case SC.extern_:
856             {
857                 /* Generate:
858                  *   mov RAX,extern_tls@GOTTPOFF[RIP]
859                  *   mov RDX,FS:[0000]
860                  *   add RAX,EDX
861                  */
862                 e.Eoper = OPvar;
863                 e = el_bin(OPadd, TYnptr, e0, e);
864                 break;
865             }
866             default:
867                 break;
868         }
869     }
870     else
871     {
872         switch (s.Sclass)
873         {
874             case SC.static_:
875             case SC.locstat:
876             {
877                 /* Generate:
878                  *   mov LEA,global_tls@TLS_LE[ECX]
879                  *   mov EDX,GS:[0000]
880                  *   add EAX,EDX
881                  */
882                 e = el_bin(OPadd, TYnptr, e, el_var(el_alloc_localgot()));
883                 e = el_bin(OPadd, TYnptr, e, e0);
884                 break;
885             }
886 
887             case SC.global:
888             {
889                 /* Generate:
890                  *   mov EAX,global_tls@TLS_LE[ECX]
891                  *   mov EDX,GS:[0000]
892                  *   add EAX,EDX
893                  */
894                 e = el_bin(OPadd, TYnptr, e, el_var(el_alloc_localgot()));
895                 e = el_una(OPind, TYnptr, e);
896                 e = el_bin(OPadd, TYnptr, e, e0);
897                 break;
898             }
899 
900             case SC.comdat:
901             case SC.comdef:
902             case SC.extern_:
903             {
904                 /* Generate:
905                  *   mov EAX,extern_tls@TLS_GOTIE[ECX]
906                  *   mov EDX,GS:[0000]
907                  *   add EAX,EDX
908                  */
909                 e = el_bin(OPadd, TYnptr, e, el_var(el_alloc_localgot()));
910                 e = el_una(OPind, TYnptr, e);
911                 e = el_bin(OPadd, TYnptr, e, e0);
912                 break;
913             }
914             default:
915                 break;
916         }
917     }
918     return e;
919 }