| author | |
| committer | |
| log | d0575310dce6d8592576951fb2a21640e00141f9 |
| tree | 104ad0d1ad8f585971a97ebde0435e002d1f8d9f |
| parent | b72d55ea5fa1de0c55e2cfb9aa65f4ec80986bc9 |
std: Fix PIE startup sequence9 files changed, 106 insertions(+), 115 deletions(-)
lib/std/os/linux.zig+1| ... | @@ -34,6 +34,7 @@ pub usingnamespace switch (native_arch) { | ... | @@ -34,6 +34,7 @@ pub usingnamespace switch (native_arch) { |
| 34 | }; | 34 | }; |
| 35 | pub usingnamespace @import("bits.zig"); | 35 | pub usingnamespace @import("bits.zig"); |
| 36 | pub const tls = @import("linux/tls.zig"); | 36 | pub const tls = @import("linux/tls.zig"); |
| 37 | pub const pie = @import("linux/start_pie.zig"); | ||
| 37 | pub const BPF = @import("linux/bpf.zig"); | 38 | pub const BPF = @import("linux/bpf.zig"); |
| 38 | pub usingnamespace @import("linux/io_uring.zig"); | 39 | pub usingnamespace @import("linux/io_uring.zig"); |
| 39 | 40 |
lib/std/os/linux/start_pie.zig+26-46| ... | @@ -8,33 +8,35 @@ const R_386_RELATIVE = 8; | ... | @@ -8,33 +8,35 @@ const R_386_RELATIVE = 8; |
| 8 | const R_ARM_RELATIVE = 23; | 8 | const R_ARM_RELATIVE = 23; |
| 9 | const R_AARCH64_RELATIVE = 1027; | 9 | const R_AARCH64_RELATIVE = 1027; |
| 10 | const R_RISCV_RELATIVE = 3; | 10 | const R_RISCV_RELATIVE = 3; |
| 11 | const R_SPARC_RELATIVE = 22; | ||
| 11 | 12 | ||
| 12 | const ARCH_RELATIVE_RELOC = switch (builtin.cpu.arch) { | 13 | const R_RELATIVE = switch (builtin.cpu.arch) { |
| 13 | .i386 => R_386_RELATIVE, | 14 | .i386 => R_386_RELATIVE, |
| 14 | .x86_64 => R_AMD64_RELATIVE, | 15 | .x86_64 => R_AMD64_RELATIVE, |
| 15 | .arm => R_ARM_RELATIVE, | 16 | .arm => R_ARM_RELATIVE, |
| 16 | .aarch64 => R_AARCH64_RELATIVE, | 17 | .aarch64 => R_AARCH64_RELATIVE, |
| 17 | .riscv64 => R_RISCV_RELATIVE, | 18 | .riscv64 => R_RISCV_RELATIVE, |
| 18 | else => @compileError("unsupported architecture"), | 19 | else => @compileError("Missing R_RELATIVE definition for this target"), |
| 19 | }; | 20 | }; |
| 20 | 21 | ||
| 21 | // Just a convoluted (but necessary) way to obtain the address of the _DYNAMIC[] | 22 | // Obtain a pointer to the _DYNAMIC array. |
| 22 | // vector as PC-relative so that we can use it before any relocation is applied | 23 | // We have to compute its address as a PC-relative quantity not to require a |
| 24 | // relocation that, at this point, is not yet applied. | ||
| 23 | fn getDynamicSymbol() [*]elf.Dyn { | 25 | fn getDynamicSymbol() [*]elf.Dyn { |
| 24 | const addr = switch (builtin.cpu.arch) { | 26 | return switch (builtin.cpu.arch) { |
| 25 | .i386 => asm volatile ( | 27 | .i386 => asm volatile ( |
| 26 | \\ .weak _DYNAMIC | 28 | \\ .weak _DYNAMIC |
| 27 | \\ .hidden _DYNAMIC | 29 | \\ .hidden _DYNAMIC |
| 28 | \\ call 1f | 30 | \\ call 1f |
| 29 | \\ 1: pop %[ret] | 31 | \\ 1: pop %[ret] |
| 30 | \\ lea _DYNAMIC-1b(%[ret]), %[ret] | 32 | \\ lea _DYNAMIC-1b(%[ret]), %[ret] |
| 31 | : [ret] "=r" (-> usize) | 33 | : [ret] "=r" (-> [*]elf.Dyn) |
| 32 | ), | 34 | ), |
| 33 | .x86_64 => asm volatile ( | 35 | .x86_64 => asm volatile ( |
| 34 | \\ .weak _DYNAMIC | 36 | \\ .weak _DYNAMIC |
| 35 | \\ .hidden _DYNAMIC | 37 | \\ .hidden _DYNAMIC |
| 36 | \\ lea _DYNAMIC(%%rip), %[ret] | 38 | \\ lea _DYNAMIC(%%rip), %[ret] |
| 37 | : [ret] "=r" (-> usize) | 39 | : [ret] "=r" (-> [*]elf.Dyn) |
| 38 | ), | 40 | ), |
| 39 | // Work around the limited offset range of `ldr` | 41 | // Work around the limited offset range of `ldr` |
| 40 | .arm => asm volatile ( | 42 | .arm => asm volatile ( |
| ... | @@ -45,7 +47,7 @@ fn getDynamicSymbol() [*]elf.Dyn { | ... | @@ -45,7 +47,7 @@ fn getDynamicSymbol() [*]elf.Dyn { |
| 45 | \\ b 2f | 47 | \\ b 2f |
| 46 | \\ 1: .word _DYNAMIC-1b | 48 | \\ 1: .word _DYNAMIC-1b |
| 47 | \\ 2: | 49 | \\ 2: |
| 48 | : [ret] "=r" (-> usize) | 50 | : [ret] "=r" (-> [*]elf.Dyn) |
| 49 | ), | 51 | ), |
| 50 | // A simple `adr` is not enough as it has a limited offset range | 52 | // A simple `adr` is not enough as it has a limited offset range |
| 51 | .aarch64 => asm volatile ( | 53 | .aarch64 => asm volatile ( |
| ... | @@ -53,61 +55,39 @@ fn getDynamicSymbol() [*]elf.Dyn { | ... | @@ -53,61 +55,39 @@ fn getDynamicSymbol() [*]elf.Dyn { |
| 53 | \\ .hidden _DYNAMIC | 55 | \\ .hidden _DYNAMIC |
| 54 | \\ adrp %[ret], _DYNAMIC | 56 | \\ adrp %[ret], _DYNAMIC |
| 55 | \\ add %[ret], %[ret], #:lo12:_DYNAMIC | 57 | \\ add %[ret], %[ret], #:lo12:_DYNAMIC |
| 56 | : [ret] "=r" (-> usize) | 58 | : [ret] "=r" (-> [*]elf.Dyn) |
| 57 | ), | 59 | ), |
| 58 | .riscv64 => asm volatile ( | 60 | .riscv64 => asm volatile ( |
| 59 | \\ .weak _DYNAMIC | 61 | \\ .weak _DYNAMIC |
| 60 | \\ .hidden _DYNAMIC | 62 | \\ .hidden _DYNAMIC |
| 61 | \\ lla %[ret], _DYNAMIC | 63 | \\ lla %[ret], _DYNAMIC |
| 62 | : [ret] "=r" (-> usize) | 64 | : [ret] "=r" (-> [*]elf.Dyn) |
| 63 | ), | 65 | ), |
| 64 | else => @compileError("???"), | 66 | else => { |
| 67 | @compileError("PIE startup is not yet supported for this target!"); | ||
| 68 | }, | ||
| 65 | }; | 69 | }; |
| 66 | return @intToPtr([*]elf.Dyn, addr); | ||
| 67 | } | 70 | } |
| 68 | 71 | ||
| 69 | pub fn apply_relocations() void { | 72 | pub fn relocate(phdrs: []elf.Phdr) void { |
| 70 | @setRuntimeSafety(false); | 73 | @setRuntimeSafety(false); |
| 71 | 74 | ||
| 72 | const dynv = getDynamicSymbol(); | 75 | const dynv = getDynamicSymbol(); |
| 73 | const auxv = std.os.linux.elf_aux_maybe.?; | 76 | // Recover the delta applied by the loader by comparing the effective and |
| 74 | var at_phent: usize = undefined; | 77 | // the theoretical load addresses for the `_DYNAMIC` symbol. |
| 75 | var at_phnum: usize = undefined; | 78 | const base_addr = base: { |
| 76 | var at_phdr: usize = undefined; | ||
| 77 | var at_hwcap: usize = undefined; | ||
| 78 | |||
| 79 | { | ||
| 80 | var i: usize = 0; | ||
| 81 | while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) { | ||
| 82 | switch (auxv[i].a_type) { | ||
| 83 | elf.AT_PHENT => at_phent = auxv[i].a_un.a_val, | ||
| 84 | elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val, | ||
| 85 | elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val, | ||
| 86 | else => continue, | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | // Sanity check | ||
| 92 | assert(at_phent == @sizeOf(elf.Phdr)); | ||
| 93 | |||
| 94 | // Search the TLS section | ||
| 95 | const phdrs = (@intToPtr([*]elf.Phdr, at_phdr))[0..at_phnum]; | ||
| 96 | |||
| 97 | const base_addr = blk: { | ||
| 98 | for (phdrs) |*phdr| { | 79 | for (phdrs) |*phdr| { |
| 99 | if (phdr.p_type == elf.PT_DYNAMIC) { | 80 | if (phdr.p_type != elf.PT_DYNAMIC) continue; |
| 100 | break :blk @ptrToInt(&dynv[0]) - phdr.p_vaddr; | 81 | break :base @ptrToInt(dynv) - phdr.p_vaddr; |
| 101 | } | ||
| 102 | } | 82 | } |
| 103 | unreachable; | 83 | // This is not supposed to happen for well-formed binaries. |
| 84 | std.os.abort(); | ||
| 104 | }; | 85 | }; |
| 105 | 86 | ||
| 106 | var rel_addr: usize = 0; | 87 | var rel_addr: usize = 0; |
| 107 | var rela_addr: usize = 0; | 88 | var rela_addr: usize = 0; |
| 108 | var rel_size: usize = 0; | 89 | var rel_size: usize = 0; |
| 109 | var rela_size: usize = 0; | 90 | var rela_size: usize = 0; |
| 110 | |||
| 111 | { | 91 | { |
| 112 | var i: usize = 0; | 92 | var i: usize = 0; |
| 113 | while (dynv[i].d_tag != elf.DT_NULL) : (i += 1) { | 93 | while (dynv[i].d_tag != elf.DT_NULL) : (i += 1) { |
| ... | @@ -121,18 +101,18 @@ pub fn apply_relocations() void { | ... | @@ -121,18 +101,18 @@ pub fn apply_relocations() void { |
| 121 | } | 101 | } |
| 122 | } | 102 | } |
| 123 | 103 | ||
| 124 | // Perform the relocations | 104 | // Apply the relocations. |
| 125 | if (rel_addr != 0) { | 105 | if (rel_addr != 0) { |
| 126 | const rel = std.mem.bytesAsSlice(elf.Rel, @intToPtr([*]u8, rel_addr)[0..rel_size]); | 106 | const rel = std.mem.bytesAsSlice(elf.Rel, @intToPtr([*]u8, rel_addr)[0..rel_size]); |
| 127 | for (rel) |r| { | 107 | for (rel) |r| { |
| 128 | if (r.r_type() != ARCH_RELATIVE_RELOC) continue; | 108 | if (r.r_type() != R_RELATIVE) continue; |
| 129 | @intToPtr(*usize, base_addr + r.r_offset).* += base_addr; | 109 | @intToPtr(*usize, base_addr + r.r_offset).* += base_addr; |
| 130 | } | 110 | } |
| 131 | } | 111 | } |
| 132 | if (rela_addr != 0) { | 112 | if (rela_addr != 0) { |
| 133 | const rela = std.mem.bytesAsSlice(elf.Rela, @intToPtr([*]u8, rela_addr)[0..rela_size]); | 113 | const rela = std.mem.bytesAsSlice(elf.Rela, @intToPtr([*]u8, rela_addr)[0..rela_size]); |
| 134 | for (rela) |r| { | 114 | for (rela) |r| { |
| 135 | if (r.r_type() != ARCH_RELATIVE_RELOC) continue; | 115 | if (r.r_type() != R_RELATIVE) continue; |
| 136 | @intToPtr(*usize, base_addr + r.r_offset).* += base_addr + @bitCast(usize, r.r_addend); | 116 | @intToPtr(*usize, base_addr + r.r_offset).* += base_addr + @bitCast(usize, r.r_addend); |
| 137 | } | 117 | } |
| 138 | } | 118 | } |
lib/std/os/linux/tls.zig+4-39| ... | @@ -190,53 +190,18 @@ pub fn setThreadPointer(addr: usize) void { | ... | @@ -190,53 +190,18 @@ pub fn setThreadPointer(addr: usize) void { |
| 190 | } | 190 | } |
| 191 | } | 191 | } |
| 192 | 192 | ||
| 193 | fn initTLS() void { | 193 | fn initTLS(phdrs: []elf.Phdr) void { |
| 194 | var tls_phdr: ?*elf.Phdr = null; | 194 | var tls_phdr: ?*elf.Phdr = null; |
| 195 | var img_base: usize = 0; | 195 | var img_base: usize = 0; |
| 196 | 196 | ||
| 197 | const auxv = std.os.linux.elf_aux_maybe.?; | ||
| 198 | var at_phent: usize = undefined; | ||
| 199 | var at_phnum: usize = undefined; | ||
| 200 | var at_phdr: usize = undefined; | ||
| 201 | var at_hwcap: usize = undefined; | ||
| 202 | |||
| 203 | var i: usize = 0; | ||
| 204 | while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) { | ||
| 205 | switch (auxv[i].a_type) { | ||
| 206 | elf.AT_PHENT => at_phent = auxv[i].a_un.a_val, | ||
| 207 | elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val, | ||
| 208 | elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val, | ||
| 209 | elf.AT_HWCAP => at_hwcap = auxv[i].a_un.a_val, | ||
| 210 | else => continue, | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | // Sanity check | ||
| 215 | assert(at_phent == @sizeOf(elf.Phdr)); | ||
| 216 | |||
| 217 | // Find the TLS section | ||
| 218 | const phdrs = (@intToPtr([*]elf.Phdr, at_phdr))[0..at_phnum]; | ||
| 219 | |||
| 220 | for (phdrs) |*phdr| { | 197 | for (phdrs) |*phdr| { |
| 221 | switch (phdr.p_type) { | 198 | switch (phdr.p_type) { |
| 222 | elf.PT_PHDR => img_base = at_phdr - phdr.p_vaddr, | 199 | elf.PT_PHDR => img_base = @ptrToInt(phdrs.ptr) - phdr.p_vaddr, |
| 223 | elf.PT_TLS => tls_phdr = phdr, | 200 | elf.PT_TLS => tls_phdr = phdr, |
| 224 | else => {}, | 201 | else => {}, |
| 225 | } | 202 | } |
| 226 | } | 203 | } |
| 227 | 204 | ||
| 228 | // ARMv6 targets (and earlier) have no support for TLS in hardware | ||
| 229 | // FIXME: Elide the check for targets >= ARMv7 when the target feature API | ||
| 230 | // becomes less verbose (and more usable). | ||
| 231 | if (comptime native_arch.isARM()) { | ||
| 232 | if (at_hwcap & std.os.linux.HWCAP_TLS == 0) { | ||
| 233 | // FIXME: Make __aeabi_read_tp call the kernel helper kuser_get_tls | ||
| 234 | // For the time being use a simple abort instead of a @panic call to | ||
| 235 | // keep the binary bloat under control. | ||
| 236 | std.os.abort(); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | var tls_align_factor: usize = undefined; | 205 | var tls_align_factor: usize = undefined; |
| 241 | var tls_data: []const u8 = undefined; | 206 | var tls_data: []const u8 = undefined; |
| 242 | var tls_data_alloc_size: usize = undefined; | 207 | var tls_data_alloc_size: usize = undefined; |
| ... | @@ -344,8 +309,8 @@ pub fn prepareTLS(area: []u8) usize { | ... | @@ -344,8 +309,8 @@ pub fn prepareTLS(area: []u8) usize { |
| 344 | // overhead. | 309 | // overhead. |
| 345 | var main_thread_tls_buffer: [0x2100]u8 align(mem.page_size) = undefined; | 310 | var main_thread_tls_buffer: [0x2100]u8 align(mem.page_size) = undefined; |
| 346 | 311 | ||
| 347 | pub fn initStaticTLS() void { | 312 | pub fn initStaticTLS(phdrs: []elf.Phdr) void { |
| 348 | initTLS(); | 313 | initTLS(phdrs); |
| 349 | 314 | ||
| 350 | const tls_area = blk: { | 315 | const tls_area = blk: { |
| 351 | // Fast path for the common case where the TLS data is really small, | 316 | // Fast path for the common case where the TLS data is really small, |
lib/std/start.zig+41-28| ... | @@ -10,6 +10,7 @@ const std = @import("std.zig"); | ... | @@ -10,6 +10,7 @@ const std = @import("std.zig"); |
| 10 | const builtin = @import("builtin"); | 10 | const builtin = @import("builtin"); |
| 11 | const assert = std.debug.assert; | 11 | const assert = std.debug.assert; |
| 12 | const uefi = std.os.uefi; | 12 | const uefi = std.os.uefi; |
| 13 | const elf = std.elf; | ||
| 13 | const tlcsprng = @import("crypto/tlcsprng.zig"); | 14 | const tlcsprng = @import("crypto/tlcsprng.zig"); |
| 14 | const native_arch = builtin.cpu.arch; | 15 | const native_arch = builtin.cpu.arch; |
| 15 | const native_os = builtin.os.tag; | 16 | const native_os = builtin.os.tag; |
| ... | @@ -281,49 +282,60 @@ fn posixCallMainAndExit() noreturn { | ... | @@ -281,49 +282,60 @@ fn posixCallMainAndExit() noreturn { |
| 281 | 282 | ||
| 282 | if (native_os == .linux) { | 283 | if (native_os == .linux) { |
| 283 | // Find the beginning of the auxiliary vector | 284 | // Find the beginning of the auxiliary vector |
| 284 | const auxv = @ptrCast([*]std.elf.Auxv, @alignCast(@alignOf(usize), envp.ptr + envp_count + 1)); | 285 | const auxv = @ptrCast([*]elf.Auxv, @alignCast(@alignOf(usize), envp.ptr + envp_count + 1)); |
| 285 | std.os.linux.elf_aux_maybe = auxv; | 286 | std.os.linux.elf_aux_maybe = auxv; |
| 286 | 287 | ||
| 287 | // Do this as early as possible, the aux vector is needed | 288 | var at_hwcap: usize = 0; |
| 289 | const phdrs = init: { | ||
| 290 | var i: usize = 0; | ||
| 291 | var at_phdr: usize = 0; | ||
| 292 | var at_phnum: usize = 0; | ||
| 293 | while (auxv[i].a_type != elf.AT_NULL) : (i += 1) { | ||
| 294 | switch (auxv[i].a_type) { | ||
| 295 | elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val, | ||
| 296 | elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val, | ||
| 297 | elf.AT_HWCAP => at_hwcap = auxv[i].a_un.a_val, | ||
| 298 | else => continue, | ||
| 299 | } | ||
| 300 | } | ||
| 301 | break :init @intToPtr([*]elf.Phdr, at_phdr)[0..at_phnum]; | ||
| 302 | }; | ||
| 303 | |||
| 304 | // Apply the initial relocations as early as possible in the startup | ||
| 305 | // process. | ||
| 288 | if (builtin.position_independent_executable) { | 306 | if (builtin.position_independent_executable) { |
| 289 | @import("os/linux/start_pie.zig").apply_relocations(); | 307 | std.os.linux.pie.relocate(phdrs); |
| 290 | } | 308 | } |
| 291 | 309 | ||
| 292 | // Initialize the TLS area. We do a runtime check here to make sure | 310 | // ARMv6 targets (and earlier) have no support for TLS in hardware. |
| 293 | // this code is truly being statically executed and not inside a dynamic | 311 | // FIXME: Elide the check for targets >= ARMv7 when the target feature API |
| 294 | // loader, otherwise this would clobber the thread ID register. | 312 | // becomes less verbose (and more usable). |
| 295 | const is_dynamic = @import("dynamic_library.zig").get_DYNAMIC() != null; | 313 | if (comptime native_arch.isARM()) { |
| 296 | if (!is_dynamic) { | 314 | if (at_hwcap & std.os.linux.HWCAP_TLS == 0) { |
| 297 | std.os.linux.tls.initStaticTLS(); | 315 | // FIXME: Make __aeabi_read_tp call the kernel helper kuser_get_tls |
| 316 | // For the time being use a simple abort instead of a @panic call to | ||
| 317 | // keep the binary bloat under control. | ||
| 318 | std.os.abort(); | ||
| 319 | } | ||
| 298 | } | 320 | } |
| 299 | 321 | ||
| 322 | // Initialize the TLS area. | ||
| 323 | std.os.linux.tls.initStaticTLS(phdrs); | ||
| 324 | |||
| 300 | // The way Linux executables represent stack size is via the PT_GNU_STACK | 325 | // The way Linux executables represent stack size is via the PT_GNU_STACK |
| 301 | // program header. However the kernel does not recognize it; it always gives 8 MiB. | 326 | // program header. However the kernel does not recognize it; it always gives 8 MiB. |
| 302 | // Here we look for the stack size in our program headers and use setrlimit | 327 | // Here we look for the stack size in our program headers and use setrlimit |
| 303 | // to ask for more stack space. | 328 | // to ask for more stack space. |
| 304 | { | 329 | expandStackSize(phdrs); |
| 305 | var i: usize = 0; | ||
| 306 | var at_phdr: usize = undefined; | ||
| 307 | var at_phnum: usize = undefined; | ||
| 308 | while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) { | ||
| 309 | switch (auxv[i].a_type) { | ||
| 310 | std.elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val, | ||
| 311 | std.elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val, | ||
| 312 | else => continue, | ||
| 313 | } | ||
| 314 | } | ||
| 315 | expandStackSize(at_phdr, at_phnum); | ||
| 316 | } | ||
| 317 | } | 330 | } |
| 318 | 331 | ||
| 319 | std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp })); | 332 | std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp })); |
| 320 | } | 333 | } |
| 321 | 334 | ||
| 322 | fn expandStackSize(at_phdr: usize, at_phnum: usize) void { | 335 | fn expandStackSize(phdrs: []elf.Phdr) void { |
| 323 | const phdrs = (@intToPtr([*]std.elf.Phdr, at_phdr))[0..at_phnum]; | ||
| 324 | for (phdrs) |*phdr| { | 336 | for (phdrs) |*phdr| { |
| 325 | switch (phdr.p_type) { | 337 | switch (phdr.p_type) { |
| 326 | std.elf.PT_GNU_STACK => { | 338 | elf.PT_GNU_STACK => { |
| 327 | const wanted_stack_size = phdr.p_memsz; | 339 | const wanted_stack_size = phdr.p_memsz; |
| 328 | assert(wanted_stack_size % std.mem.page_size == 0); | 340 | assert(wanted_stack_size % std.mem.page_size == 0); |
| 329 | 341 | ||
| ... | @@ -362,9 +374,10 @@ fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) callconv(.C) | ... | @@ -362,9 +374,10 @@ fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) callconv(.C) |
| 362 | const envp = @ptrCast([*][*:0]u8, c_envp)[0..env_count]; | 374 | const envp = @ptrCast([*][*:0]u8, c_envp)[0..env_count]; |
| 363 | 375 | ||
| 364 | if (builtin.os.tag == .linux) { | 376 | if (builtin.os.tag == .linux) { |
| 365 | const at_phdr = std.c.getauxval(std.elf.AT_PHDR); | 377 | const at_phdr = std.c.getauxval(elf.AT_PHDR); |
| 366 | const at_phnum = std.c.getauxval(std.elf.AT_PHNUM); | 378 | const at_phnum = std.c.getauxval(elf.AT_PHNUM); |
| 367 | expandStackSize(at_phdr, at_phnum); | 379 | const phdrs = (@intToPtr([*]elf.Phdr, at_phdr))[0..at_phnum]; |
| 380 | expandStackSize(phdrs); | ||
| 368 | } | 381 | } |
| 369 | 382 | ||
| 370 | return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp }); | 383 | return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp }); |
test/stage2/darwin.zig+1-1| ... | @@ -14,7 +14,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -14,7 +14,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 14 | { | 14 | { |
| 15 | var case = ctx.exe("hello world with updates", target); | 15 | var case = ctx.exe("hello world with updates", target); |
| 16 | case.addError("", &[_][]const u8{ | 16 | case.addError("", &[_][]const u8{ |
| 17 | ":84:9: error: struct 'test_case.test_case' has no member named 'main'", | 17 | ":85:9: error: struct 'test_case.test_case' has no member named 'main'", |
| 18 | }); | 18 | }); |
| 19 | 19 | ||
| 20 | // Incorrect return type | 20 | // Incorrect return type |
test/stage2/test.zig+1-1| ... | @@ -24,7 +24,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -24,7 +24,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 24 | var case = ctx.exe("hello world with updates", linux_x64); | 24 | var case = ctx.exe("hello world with updates", linux_x64); |
| 25 | 25 | ||
| 26 | case.addError("", &[_][]const u8{ | 26 | case.addError("", &[_][]const u8{ |
| 27 | ":84:9: error: struct 'test_case.test_case' has no member named 'main'", | 27 | ":85:9: error: struct 'test_case.test_case' has no member named 'main'", |
| 28 | }); | 28 | }); |
| 29 | 29 | ||
| 30 | // Incorrect return type | 30 | // Incorrect return type |
test/standalone.zig+5| ... | @@ -31,4 +31,9 @@ pub fn addCases(cases: *tests.StandaloneContext) void { | ... | @@ -31,4 +31,9 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 31 | cases.addBuildFile("test/stage1/c_abi/build.zig", .{}); | 31 | cases.addBuildFile("test/stage1/c_abi/build.zig", .{}); |
| 32 | } | 32 | } |
| 33 | cases.addBuildFile("test/standalone/c_compiler/build.zig", .{ .build_modes = true, .cross_targets = true }); | 33 | cases.addBuildFile("test/standalone/c_compiler/build.zig", .{ .build_modes = true, .cross_targets = true }); |
| 34 | |||
| 35 | // Try to build and run a PIE executable. | ||
| 36 | if (std.Target.current.os.tag == .linux) { | ||
| 37 | cases.addBuildFile("test/standalone/pie/build.zig", .{}); | ||
| 38 | } | ||
| 34 | } | 39 | } |
test/standalone/pie/build.zig created+12| ... | @@ -0,0 +1,12 @@ | ||
| 1 | const Builder = @import("std").build.Builder; | ||
| 2 | |||
| 3 | pub fn build(b: *Builder) void { | ||
| 4 | const main = b.addTest("main.zig"); | ||
| 5 | main.setBuildMode(b.standardReleaseOptions()); | ||
| 6 | main.pie = true; | ||
| 7 | |||
| 8 | const test_step = b.step("test", "Test the program"); | ||
| 9 | test_step.dependOn(&main.step); | ||
| 10 | |||
| 11 | b.default_step.dependOn(test_step); | ||
| 12 | } | ||
test/standalone/pie/main.zig created+15| ... | @@ -0,0 +1,15 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const elf = std.elf; | ||
| 3 | |||
| 4 | threadlocal var foo: u8 = 42; | ||
| 5 | |||
| 6 | test "Check ELF header" { | ||
| 7 | // PIE executables are marked as ET_DYN, regular exes as ET_EXEC. | ||
| 8 | const header = @intToPtr(*elf.Ehdr, std.process.getBaseAddress()); | ||
| 9 | try std.testing.expectEqual(elf.ET.DYN, header.e_type); | ||
| 10 | } | ||
| 11 | |||
| 12 | test "TLS is initialized" { | ||
| 13 | // Ensure the TLS is initialized by the startup code. | ||
| 14 | try std.testing.expectEqual(@as(u8, 42), foo); | ||
| 15 | } | ||