| ... | @@ -14,7 +14,8 @@ const mem = std.mem; | ... | @@ -14,7 +14,8 @@ const mem = std.mem; |
| 14 | const elf = std.elf; | 14 | const elf = std.elf; |
| 15 | const math = std.math; | 15 | const math = std.math; |
| 16 | const assert = std.debug.assert; | 16 | const assert = std.debug.assert; |
| 17 | const native_arch = @import("builtin").cpu.arch; | 17 | const builtin = @import("builtin"); |
| | 18 | const native_arch = builtin.cpu.arch; |
| 18 | const linux = std.os.linux; | 19 | const linux = std.os.linux; |
| 19 | const page_size_min = std.heap.page_size_min; | 20 | const page_size_min = std.heap.page_size_min; |
| 20 | | 21 | |
| ... | @@ -41,13 +42,13 @@ const Variant = enum { | ... | @@ -41,13 +42,13 @@ const Variant = enum { |
| 41 | I_original, | 42 | I_original, |
| 42 | /// The modified Variant I: | 43 | /// The modified Variant I: |
| 43 | /// | 44 | /// |
| 44 | /// --------------------------------------------------- | 45 | /// -------------------------------------------- |
| 45 | /// | DTV | Zig TCB | ABI TCB | [Offset] | TLS Blocks | | 46 | /// | DTV | Zig TCB | ABI TCB | TLS Blocks | |
| 46 | /// -------------------------------------^------------- | 47 | /// ------------------------------^------------- |
| 47 | /// `-- The TP register points here. | 48 | /// `-- The TP register points here (*inside* the TLS blocks). |
| 48 | /// | 49 | /// |
| 49 | /// The offset (which can be zero) is applied to the TP only; there is never a physical gap | 50 | /// The offset from the start of the TLS blocks to the TP register is `current_tp_offset`. It |
| 50 | /// between the ABI TCB and the TLS blocks. This implies that we only need to align the TP. | 51 | /// may be zero, in which case the TP register points to the start of the TLS blocks. |
| 51 | /// | 52 | /// |
| 52 | /// The first (and only) word in the ABI TCB points to the DTV. | 53 | /// The first (and only) word in the ABI TCB points to the DTV. |
| 53 | I_modified, | 54 | I_modified, |
| ... | @@ -106,7 +107,7 @@ const current_variant: Variant = switch (native_arch) { | ... | @@ -106,7 +107,7 @@ const current_variant: Variant = switch (native_arch) { |
| 106 | else => @compileError("undefined TLS variant for this architecture"), | 107 | else => @compileError("undefined TLS variant for this architecture"), |
| 107 | }; | 108 | }; |
| 108 | | 109 | |
| 109 | /// The Offset value for the modified Variant I. | 110 | /// The offset value for the modified Variant I. |
| 110 | const current_tp_offset = switch (native_arch) { | 111 | const current_tp_offset = switch (native_arch) { |
| 111 | .m68k, | 112 | .m68k, |
| 112 | .mips, | 113 | .mips, |
| ... | @@ -379,6 +380,106 @@ pub fn setThreadPointer(addr: usize) void { | ... | @@ -379,6 +380,106 @@ pub fn setThreadPointer(addr: usize) void { |
| 379 | } | 380 | } |
| 380 | } | 381 | } |
| 381 | | 382 | |
| | 383 | pub fn getThreadPointer() usize { |
| | 384 | @setRuntimeSafety(false); |
| | 385 | @disableInstrumentation(); |
| | 386 | |
| | 387 | return switch (native_arch) { |
| | 388 | .aarch64, .aarch64_be => asm ( |
| | 389 | \\ mrs %[ret], tpidr_el0 |
| | 390 | : [ret] "=r" (-> usize), |
| | 391 | ), |
| | 392 | .alpha => asm ( |
| | 393 | \\ rduniq |
| | 394 | : [ret] "={$0}" (-> usize), |
| | 395 | ), |
| | 396 | .arc, .arceb => asm ( |
| | 397 | \\ mov %[ret], r25 |
| | 398 | : [ret] "=r" (-> usize), |
| | 399 | ), |
| | 400 | .arm, .armeb, .thumb, .thumbeb => asm ( |
| | 401 | \\ mrc p15, 0, %[ret], c13, c0, 3 |
| | 402 | : [ret] "=r" (-> usize), |
| | 403 | ), |
| | 404 | .csky => asm ( |
| | 405 | \\ mov %[ret], r31 |
| | 406 | : [ret] "=r" (-> usize), |
| | 407 | ), |
| | 408 | .hexagon => asm ( |
| | 409 | \\ %[ret] = ugp |
| | 410 | : [ret] "=r" (-> usize), |
| | 411 | ), |
| | 412 | .hppa => asm ( |
| | 413 | \\ mfctl %%cr27, %[ret] |
| | 414 | : [ret] "=r" (-> usize), |
| | 415 | ), |
| | 416 | .loongarch32, .loongarch64 => asm ( |
| | 417 | \\ move %[ret], $tp |
| | 418 | : [ret] "=r" (-> usize), |
| | 419 | ), |
| | 420 | .m68k => linux.syscall1(.get_thread_area), |
| | 421 | .mips, .mipsel, .mips64, .mips64el => asm ( |
| | 422 | \\ rdhwr %[ret], $29 |
| | 423 | : [ret] "=r" (-> usize), |
| | 424 | ), |
| | 425 | .microblaze, .microblazeel => asm ( |
| | 426 | \\ ori %[ret], r21, 0 |
| | 427 | : [ret] "=r" (-> usize), |
| | 428 | ), |
| | 429 | .or1k => asm ( |
| | 430 | \\ l.ori %[ret], r10, 0 |
| | 431 | : [ret] "=r" (-> usize), |
| | 432 | ), |
| | 433 | .riscv32, .riscv64 => asm ( |
| | 434 | \\ mv %[ret], tp |
| | 435 | : [ret] "=r" (-> usize), |
| | 436 | ), |
| | 437 | .powerpc, .powerpcle => asm ( |
| | 438 | \\ mr %[ret], 2 |
| | 439 | : [ret] "=r" (-> usize), |
| | 440 | ), |
| | 441 | .powerpc64, .powerpc64le => asm ( |
| | 442 | \\ mr %[ret], 13 |
| | 443 | : [ret] "=r" (-> usize), |
| | 444 | ), |
| | 445 | .s390x => asm ( |
| | 446 | \\ ear %[ret], %%a0 |
| | 447 | \\ sllg %[ret], %[ret], 32 |
| | 448 | \\ ear %[ret], %%a1 |
| | 449 | : [ret] "=r" (-> usize), |
| | 450 | ), |
| | 451 | .sh, .sheb => asm ( |
| | 452 | \\ stc %[ret], gbr |
| | 453 | : [ret] "=r" (-> usize), |
| | 454 | ), |
| | 455 | .sparc, .sparc64 => asm ( |
| | 456 | \\ mov %%g7, %[ret] |
| | 457 | : [ret] "=r" (-> usize), |
| | 458 | ), |
| | 459 | .x86 => asm ( |
| | 460 | \\ movl %%gs:0, %[ret] |
| | 461 | : [ret] "=r" (-> usize), |
| | 462 | ), |
| | 463 | .x86_64 => switch (@sizeOf(usize)) { |
| | 464 | 8 => asm ( |
| | 465 | \\ movq %%fs:0, %[ret] |
| | 466 | : [ret] "=r" (-> usize), |
| | 467 | ), |
| | 468 | // On x32, usize is 32 bits. |
| | 469 | 4 => asm ( |
| | 470 | \\ movl %%fs:0, %[ret] |
| | 471 | : [ret] "=r" (-> usize), |
| | 472 | ), |
| | 473 | else => comptime unreachable, |
| | 474 | }, |
| | 475 | .xtensa, .xtensaeb => asm ( |
| | 476 | \\ rur %[ret], threadptr |
| | 477 | : [ret] "=r" (-> usize), |
| | 478 | ), |
| | 479 | else => @compileError("Unsupported architecture"), |
| | 480 | }; |
| | 481 | } |
| | 482 | |
| 382 | fn computeAreaDesc(phdrs: []elf.Phdr) void { | 483 | fn computeAreaDesc(phdrs: []elf.Phdr) void { |
| 383 | @setRuntimeSafety(false); | 484 | @setRuntimeSafety(false); |
| 384 | @disableInstrumentation(); | 485 | @disableInstrumentation(); |
| ... | @@ -616,3 +717,32 @@ inline fn mmap_tls(length: usize) usize { | ... | @@ -616,3 +717,32 @@ inline fn mmap_tls(length: usize) usize { |
| 616 | }); | 717 | }); |
| 617 | } | 718 | } |
| 618 | } | 719 | } |
| | 720 | |
| | 721 | comptime { |
| | 722 | assert(!builtin.link_libc); // otherwise libc should control TLS |
| | 723 | |
| | 724 | if (builtin.output_mode == .Exe and builtin.link_mode == .static) { |
| | 725 | // This is a static executable without libc, so it is our job to provide the TLS accessor |
| | 726 | // function for the GD and LD models. This function is unlikely to actually be used, since |
| | 727 | // the linker should be able to relax every TLS access to the LE model and therefore |
| | 728 | // eliminate all calls to this function, but that isn't guaranteed. |
| | 729 | _ = struct { |
| | 730 | const TlsIndex = switch (native_arch) { |
| | 731 | .x86_64 => extern struct { module: u64, offset: u64 }, // Even for x32... |
| | 732 | else => extern struct { module: usize, offset: usize }, // ...but not MIPS N32! |
| | 733 | }; |
| | 734 | export fn __tls_get_addr(ti: *const TlsIndex) *anyopaque { |
| | 735 | assert(ti.module == 1); // The executable's module ID is always 1 |
| | 736 | const tp = getThreadPointer(); |
| | 737 | const block: [*]u8 = switch (current_variant) { |
| | 738 | .I_original => @ptrFromInt(tp -% area_desc.abi_tcb.offset +% area_desc.block.offset), |
| | 739 | .I_modified => @ptrFromInt(tp -% current_tp_offset), |
| | 740 | // The `.I_original` approach would also work for `.II`, but there is an |
| | 741 | // alternative strategy which is one less operation: |
| | 742 | .II => @ptrFromInt(tp -% area_desc.block.size), |
| | 743 | }; |
| | 744 | return block[@intCast(ti.offset)..]; |
| | 745 | } |
| | 746 | }; |
| | 747 | } |
| | 748 | } |