aboutsummaryrefslogtreecommitdiff
path: root/lib/std/os/linux
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2020-11-22 17:30:34 -0700
committerAndrew Kelley <andrew@ziglang.org>2020-11-22 17:30:34 -0700
commitc7170e4a5480581db5f30913eebd9ad4f7cd121e (patch)
treefba79b8f4241c94f886e7708d18081e8fc0e31fe /lib/std/os/linux
parent98d5bfbd4d21e99363a0a68ef5a0d0104c302ecb (diff)
parentabc717f203060f7ab16d36f2afe681d838b46801 (diff)
downloadzig-c7170e4a5480581db5f30913eebd9ad4f7cd121e.tar.gz
zig-c7170e4a5480581db5f30913eebd9ad4f7cd121e.zip
Support PIE (Position Independent Executables)
Closes #4503 Revives #3960 Merges branch 'pie' into master
Diffstat (limited to 'lib/std/os/linux')
-rw-r--r--lib/std/os/linux/start_pie.zig138
1 files changed, 138 insertions, 0 deletions
diff --git a/lib/std/os/linux/start_pie.zig b/lib/std/os/linux/start_pie.zig
new file mode 100644
index 0000000000..551386f312
--- /dev/null
+++ b/lib/std/os/linux/start_pie.zig
@@ -0,0 +1,138 @@
+const std = @import("std");
+const elf = std.elf;
+const builtin = @import("builtin");
+const assert = std.debug.assert;
+
+const R_AMD64_RELATIVE = 8;
+const R_386_RELATIVE = 8;
+const R_ARM_RELATIVE = 23;
+const R_AARCH64_RELATIVE = 1027;
+const R_RISCV_RELATIVE = 3;
+
+const ARCH_RELATIVE_RELOC = switch (builtin.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"),
+};
+
+// 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
+fn getDynamicSymbol() [*]elf.Dyn {
+ const addr = switch (builtin.arch) {
+ .i386 => asm volatile (
+ \\ .weak _DYNAMIC
+ \\ .hidden _DYNAMIC
+ \\ call 1f
+ \\ 1: pop %[ret]
+ \\ lea _DYNAMIC-1b(%[ret]), %[ret]
+ : [ret] "=r" (-> usize)
+ ),
+ .x86_64 => asm volatile (
+ \\ .weak _DYNAMIC
+ \\ .hidden _DYNAMIC
+ \\ lea _DYNAMIC(%%rip), %[ret]
+ : [ret] "=r" (-> usize)
+ ),
+ // Work around the limited offset range of `ldr`
+ .arm => asm volatile (
+ \\ .weak _DYNAMIC
+ \\ .hidden _DYNAMIC
+ \\ ldr %[ret], 1f
+ \\ add %[ret], pc
+ \\ b 2f
+ \\ 1: .word _DYNAMIC-1b
+ \\ 2:
+ : [ret] "=r" (-> usize)
+ ),
+ // A simple `adr` is not enough as it has a limited offset range
+ .aarch64 => asm volatile (
+ \\ .weak _DYNAMIC
+ \\ .hidden _DYNAMIC
+ \\ adrp %[ret], _DYNAMIC
+ \\ add %[ret], %[ret], #:lo12:_DYNAMIC
+ : [ret] "=r" (-> usize)
+ ),
+ .riscv64 => asm volatile (
+ \\ lla %[ret], _DYNAMIC
+ : [ret] "=r" (-> usize)
+ ),
+ else => @compileError("???"),
+ };
+ if (addr == 0) unreachable;
+ return @intToPtr([*]elf.Dyn, addr);
+}
+
+pub fn apply_relocations() 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: {
+ for (phdrs) |*phdr| {
+ if (phdr.p_type == elf.PT_DYNAMIC) {
+ break :blk @ptrToInt(&dynv[0]) - phdr.p_vaddr;
+ }
+ }
+ unreachable;
+ };
+
+ 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) {
+ switch (dynv[i].d_tag) {
+ elf.DT_REL => rel_addr = base_addr + dynv[i].d_val,
+ elf.DT_RELA => rela_addr = base_addr + dynv[i].d_val,
+ elf.DT_RELSZ => rel_size = dynv[i].d_val,
+ elf.DT_RELASZ => rela_size = dynv[i].d_val,
+ else => {},
+ }
+ }
+ }
+
+ // Perform 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;
+ @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;
+ @intToPtr(*usize, base_addr + r.r_offset).* += base_addr + @bitCast(usize, r.r_addend);
+ }
+ }
+}