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 }