140 lines
4.3 KiB
Zig
140 lines
4.3 KiB
Zig
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 (
|
|
\\ .weak _DYNAMIC
|
|
\\ .hidden _DYNAMIC
|
|
\\ lla %[ret], _DYNAMIC
|
|
: [ret] "=r" (-> usize)
|
|
),
|
|
else => @compileError("???"),
|
|
};
|
|
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);
|
|
}
|
|
}
|
|
}
|