aboutsummaryrefslogtreecommitdiff
path: root/src/link/Elf/Thunk.zig
blob: 1700bb8276dd4e534629e6ff30eaa9220ada8a5d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
value: i64 = 0,
output_section_index: u32 = 0,
symbols: std.AutoArrayHashMapUnmanaged(Elf.Ref, void) = .empty,
output_symtab_ctx: Elf.SymtabCtx = .{},

pub fn deinit(thunk: *Thunk, allocator: Allocator) void {
    thunk.symbols.deinit(allocator);
}

pub fn size(thunk: Thunk, elf_file: *Elf) usize {
    const cpu_arch = elf_file.getTarget().cpu.arch;
    return thunk.symbols.keys().len * trampolineSize(cpu_arch);
}

pub fn address(thunk: Thunk, elf_file: *Elf) i64 {
    const shdr = elf_file.sections.items(.shdr)[thunk.output_section_index];
    return @as(i64, @intCast(shdr.sh_addr)) + thunk.value;
}

pub fn targetAddress(thunk: Thunk, ref: Elf.Ref, elf_file: *Elf) i64 {
    const cpu_arch = elf_file.getTarget().cpu.arch;
    return thunk.address(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(ref).? * trampolineSize(cpu_arch)));
}

pub fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void {
    switch (elf_file.getTarget().cpu.arch) {
        .aarch64, .aarch64_be => try aarch64.write(thunk, elf_file, writer),
        .x86_64, .riscv64, .riscv64be => unreachable,
        else => @panic("unhandled arch"),
    }
}

pub fn calcSymtabSize(thunk: *Thunk, elf_file: *Elf) void {
    thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len));
    for (thunk.symbols.keys()) |ref| {
        const sym = elf_file.symbol(ref).?;
        thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.name(elf_file).len + "$thunk".len + 1));
    }
}

pub fn writeSymtab(thunk: Thunk, elf_file: *Elf) void {
    const cpu_arch = elf_file.getTarget().cpu.arch;
    for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| {
        const sym = elf_file.symbol(ref).?;
        const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
        elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
        elf_file.strtab.appendSliceAssumeCapacity("$thunk");
        elf_file.strtab.appendAssumeCapacity(0);
        elf_file.symtab.items[ilocal] = .{
            .st_name = st_name,
            .st_info = elf.STT_FUNC,
            .st_other = 0,
            .st_shndx = @intCast(thunk.output_section_index),
            .st_value = @intCast(thunk.targetAddress(ref, elf_file)),
            .st_size = trampolineSize(cpu_arch),
        };
    }
}

fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) usize {
    return switch (cpu_arch) {
        .aarch64, .aarch64_be => aarch64.trampoline_size,
        .x86_64, .riscv64, .riscv64be => unreachable,
        else => @panic("unhandled arch"),
    };
}

pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Alt(Format, Format.default) {
    return .{ .data = .{
        .thunk = thunk,
        .elf_file = elf_file,
    } };
}

const Format = struct {
    thunk: Thunk,
    elf_file: *Elf,

    fn default(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void {
        const thunk = f.thunk;
        const elf_file = f.elf_file;
        try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) });
        for (thunk.symbols.keys()) |ref| {
            const sym = elf_file.symbol(ref).?;
            try writer.print("  {f} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value });
        }
    }
};

pub const Index = u32;

const aarch64 = struct {
    fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void {
        for (thunk.symbols.keys(), 0..) |ref, i| {
            const sym = elf_file.symbol(ref).?;
            const saddr = thunk.address(elf_file) + @as(i64, @intCast(i * trampoline_size));
            const taddr = sym.address(.{}, elf_file);
            try writer.writeInt(u32, @bitCast(
                util.encoding.Instruction.adrp(.x16, try util.calcNumberOfPages(saddr, taddr) << 12),
            ), .little);
            try writer.writeInt(u32, @bitCast(util.encoding.Instruction.add(
                .x16,
                .x16,
                .{ .immediate = @truncate(@as(u64, @bitCast(taddr))) },
            )), .little);
            try writer.writeInt(u32, @bitCast(util.encoding.Instruction.br(.x16)), .little);
        }
    }

    const trampoline_size = 3 * @sizeOf(u32);

    const util = @import("../aarch64.zig");
};

const assert = std.debug.assert;
const elf = std.elf;
const log = std.log.scoped(.link);
const math = std.math;
const mem = std.mem;
const std = @import("std");

const Allocator = mem.Allocator;
const Atom = @import("Atom.zig");
const Elf = @import("../Elf.zig");
const Symbol = @import("Symbol.zig");

const Thunk = @This();