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