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