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