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