aboutsummaryrefslogtreecommitdiff
path: root/lib/std/os
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-06-20 20:01:39 -0400
committerGitHub <noreply@github.com>2021-06-20 20:01:39 -0400
commitfc1feebdc0ffd4730c724780265d4ef6a9515dc8 (patch)
tree4c3392e333ae2149e537e3f4222e5e6874fc41e3 /lib/std/os
parent00982f75e92119aac6182ab9876adfb13305d1ed (diff)
parentaa6422d792a2642c5ea53a7c35f06b1cb61379b2 (diff)
downloadzig-fc1feebdc0ffd4730c724780265d4ef6a9515dc8.tar.gz
zig-fc1feebdc0ffd4730c724780265d4ef6a9515dc8.zip
Merge pull request #9168 from LemonBoy/fix-pie
std: Fix PIE startup sequence
Diffstat (limited to 'lib/std/os')
-rw-r--r--lib/std/os/linux.zig1
-rw-r--r--lib/std/os/linux/start_pie.zig72
-rw-r--r--lib/std/os/linux/tls.zig43
3 files changed, 31 insertions, 85 deletions
diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig
index 4b3454de52..792b4dff96 100644
--- a/lib/std/os/linux.zig
+++ b/lib/std/os/linux.zig
@@ -34,6 +34,7 @@ pub usingnamespace switch (native_arch) {
};
pub usingnamespace @import("bits.zig");
pub const tls = @import("linux/tls.zig");
+pub const pie = @import("linux/start_pie.zig");
pub const BPF = @import("linux/bpf.zig");
pub usingnamespace @import("linux/io_uring.zig");
diff --git a/lib/std/os/linux/start_pie.zig b/lib/std/os/linux/start_pie.zig
index 9ff6124359..19e1a29bc5 100644
--- a/lib/std/os/linux/start_pie.zig
+++ b/lib/std/os/linux/start_pie.zig
@@ -8,33 +8,35 @@ const R_386_RELATIVE = 8;
const R_ARM_RELATIVE = 23;
const R_AARCH64_RELATIVE = 1027;
const R_RISCV_RELATIVE = 3;
+const R_SPARC_RELATIVE = 22;
-const ARCH_RELATIVE_RELOC = switch (builtin.cpu.arch) {
+const R_RELATIVE = switch (builtin.cpu.arch) {
.i386 => R_386_RELATIVE,
.x86_64 => R_AMD64_RELATIVE,
.arm => R_ARM_RELATIVE,
.aarch64 => R_AARCH64_RELATIVE,
.riscv64 => R_RISCV_RELATIVE,
- else => @compileError("unsupported architecture"),
+ else => @compileError("Missing R_RELATIVE definition for this target"),
};
-// Just a convoluted (but necessary) way to obtain the address of the _DYNAMIC[]
-// vector as PC-relative so that we can use it before any relocation is applied
+// Obtain a pointer to the _DYNAMIC array.
+// We have to compute its address as a PC-relative quantity not to require a
+// relocation that, at this point, is not yet applied.
fn getDynamicSymbol() [*]elf.Dyn {
- const addr = switch (builtin.cpu.arch) {
+ return switch (builtin.cpu.arch) {
.i386 => asm volatile (
\\ .weak _DYNAMIC
\\ .hidden _DYNAMIC
\\ call 1f
\\ 1: pop %[ret]
\\ lea _DYNAMIC-1b(%[ret]), %[ret]
- : [ret] "=r" (-> usize)
+ : [ret] "=r" (-> [*]elf.Dyn)
),
.x86_64 => asm volatile (
\\ .weak _DYNAMIC
\\ .hidden _DYNAMIC
\\ lea _DYNAMIC(%%rip), %[ret]
- : [ret] "=r" (-> usize)
+ : [ret] "=r" (-> [*]elf.Dyn)
),
// Work around the limited offset range of `ldr`
.arm => asm volatile (
@@ -45,7 +47,7 @@ fn getDynamicSymbol() [*]elf.Dyn {
\\ b 2f
\\ 1: .word _DYNAMIC-1b
\\ 2:
- : [ret] "=r" (-> usize)
+ : [ret] "=r" (-> [*]elf.Dyn)
),
// A simple `adr` is not enough as it has a limited offset range
.aarch64 => asm volatile (
@@ -53,61 +55,39 @@ fn getDynamicSymbol() [*]elf.Dyn {
\\ .hidden _DYNAMIC
\\ adrp %[ret], _DYNAMIC
\\ add %[ret], %[ret], #:lo12:_DYNAMIC
- : [ret] "=r" (-> usize)
+ : [ret] "=r" (-> [*]elf.Dyn)
),
.riscv64 => asm volatile (
\\ .weak _DYNAMIC
\\ .hidden _DYNAMIC
\\ lla %[ret], _DYNAMIC
- : [ret] "=r" (-> usize)
+ : [ret] "=r" (-> [*]elf.Dyn)
),
- else => @compileError("???"),
+ else => {
+ @compileError("PIE startup is not yet supported for this target!");
+ },
};
- return @intToPtr([*]elf.Dyn, addr);
}
-pub fn apply_relocations() void {
+pub fn relocate(phdrs: []elf.Phdr) void {
@setRuntimeSafety(false);
const dynv = getDynamicSymbol();
- const auxv = std.os.linux.elf_aux_maybe.?;
- var at_phent: usize = undefined;
- var at_phnum: usize = undefined;
- var at_phdr: usize = undefined;
- var at_hwcap: usize = undefined;
-
- {
- var i: usize = 0;
- while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) {
- switch (auxv[i].a_type) {
- elf.AT_PHENT => at_phent = auxv[i].a_un.a_val,
- elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val,
- elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val,
- else => continue,
- }
- }
- }
-
- // Sanity check
- assert(at_phent == @sizeOf(elf.Phdr));
-
- // Search the TLS section
- const phdrs = (@intToPtr([*]elf.Phdr, at_phdr))[0..at_phnum];
-
- const base_addr = blk: {
+ // Recover the delta applied by the loader by comparing the effective and
+ // the theoretical load addresses for the `_DYNAMIC` symbol.
+ const base_addr = base: {
for (phdrs) |*phdr| {
- if (phdr.p_type == elf.PT_DYNAMIC) {
- break :blk @ptrToInt(&dynv[0]) - phdr.p_vaddr;
- }
+ if (phdr.p_type != elf.PT_DYNAMIC) continue;
+ break :base @ptrToInt(dynv) - phdr.p_vaddr;
}
- unreachable;
+ // This is not supposed to happen for well-formed binaries.
+ std.os.abort();
};
var rel_addr: usize = 0;
var rela_addr: usize = 0;
var rel_size: usize = 0;
var rela_size: usize = 0;
-
{
var i: usize = 0;
while (dynv[i].d_tag != elf.DT_NULL) : (i += 1) {
@@ -121,18 +101,18 @@ pub fn apply_relocations() void {
}
}
- // Perform the relocations
+ // Apply the relocations.
if (rel_addr != 0) {
const rel = std.mem.bytesAsSlice(elf.Rel, @intToPtr([*]u8, rel_addr)[0..rel_size]);
for (rel) |r| {
- if (r.r_type() != ARCH_RELATIVE_RELOC) continue;
+ if (r.r_type() != R_RELATIVE) continue;
@intToPtr(*usize, base_addr + r.r_offset).* += base_addr;
}
}
if (rela_addr != 0) {
const rela = std.mem.bytesAsSlice(elf.Rela, @intToPtr([*]u8, rela_addr)[0..rela_size]);
for (rela) |r| {
- if (r.r_type() != ARCH_RELATIVE_RELOC) continue;
+ if (r.r_type() != R_RELATIVE) continue;
@intToPtr(*usize, base_addr + r.r_offset).* += base_addr + @bitCast(usize, r.r_addend);
}
}
diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig
index 502868f9d4..5649cadd8b 100644
--- a/lib/std/os/linux/tls.zig
+++ b/lib/std/os/linux/tls.zig
@@ -190,53 +190,18 @@ pub fn setThreadPointer(addr: usize) void {
}
}
-fn initTLS() void {
+fn initTLS(phdrs: []elf.Phdr) void {
var tls_phdr: ?*elf.Phdr = null;
var img_base: usize = 0;
- const auxv = std.os.linux.elf_aux_maybe.?;
- var at_phent: usize = undefined;
- var at_phnum: usize = undefined;
- var at_phdr: usize = undefined;
- var at_hwcap: usize = undefined;
-
- var i: usize = 0;
- while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) {
- switch (auxv[i].a_type) {
- elf.AT_PHENT => at_phent = auxv[i].a_un.a_val,
- elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val,
- elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val,
- elf.AT_HWCAP => at_hwcap = auxv[i].a_un.a_val,
- else => continue,
- }
- }
-
- // Sanity check
- assert(at_phent == @sizeOf(elf.Phdr));
-
- // Find the TLS section
- const phdrs = (@intToPtr([*]elf.Phdr, at_phdr))[0..at_phnum];
-
for (phdrs) |*phdr| {
switch (phdr.p_type) {
- elf.PT_PHDR => img_base = at_phdr - phdr.p_vaddr,
+ elf.PT_PHDR => img_base = @ptrToInt(phdrs.ptr) - phdr.p_vaddr,
elf.PT_TLS => tls_phdr = phdr,
else => {},
}
}
- // ARMv6 targets (and earlier) have no support for TLS in hardware
- // FIXME: Elide the check for targets >= ARMv7 when the target feature API
- // becomes less verbose (and more usable).
- if (comptime native_arch.isARM()) {
- if (at_hwcap & std.os.linux.HWCAP_TLS == 0) {
- // FIXME: Make __aeabi_read_tp call the kernel helper kuser_get_tls
- // For the time being use a simple abort instead of a @panic call to
- // keep the binary bloat under control.
- std.os.abort();
- }
- }
-
var tls_align_factor: usize = undefined;
var tls_data: []const u8 = undefined;
var tls_data_alloc_size: usize = undefined;
@@ -344,8 +309,8 @@ pub fn prepareTLS(area: []u8) usize {
// overhead.
var main_thread_tls_buffer: [0x2100]u8 align(mem.page_size) = undefined;
-pub fn initStaticTLS() void {
- initTLS();
+pub fn initStaticTLS(phdrs: []elf.Phdr) void {
+ initTLS(phdrs);
const tls_area = blk: {
// Fast path for the common case where the TLS data is really small,