| 1 | value: i64 = 0, |
| 2 | output_section_index: u32 = 0, |
| 3 | symbols: std.array_hash_map.Auto(Elf.Ref, void) = .empty, |
| 4 | output_symtab_ctx: Elf.SymtabCtx = .{}, |
| 5 | |
| 6 | pub fn deinit(thunk: *Thunk, allocator: Allocator) void { |
| 7 | thunk.symbols.deinit(allocator); |
| 8 | } |
| 9 | |
| 10 | pub fn size(thunk: Thunk, elf_file: *Elf) usize { |
| 11 | const cpu_arch = elf_file.getTarget().cpu.arch; |
| 12 | return thunk.symbols.keys().len * trampolineSize(cpu_arch); |
| 13 | } |
| 14 | |
| 15 | pub fn address(thunk: Thunk, elf_file: *Elf) i64 { |
| 16 | const shdr = elf_file.sections.items(.shdr)[thunk.output_section_index]; |
| 17 | return @as(i64, @intCast(shdr.sh_addr)) + thunk.value; |
| 18 | } |
| 19 | |
| 20 | pub fn targetAddress(thunk: Thunk, ref: Elf.Ref, elf_file: *Elf) i64 { |
| 21 | const cpu_arch = elf_file.getTarget().cpu.arch; |
| 22 | return thunk.address(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(ref).? * trampolineSize(cpu_arch))); |
| 23 | } |
| 24 | |
| 25 | pub fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void { |
| 26 | switch (elf_file.getTarget().cpu.arch) { |
| 27 | .aarch64, .aarch64_be => try aarch64.write(thunk, elf_file, writer), |
| 28 | .x86_64, .riscv64, .riscv64be => unreachable, |
| 29 | else => @panic("unhandled arch"), |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | pub fn calcSymtabSize(thunk: *Thunk, elf_file: *Elf) void { |
| 34 | thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len)); |
| 35 | for (thunk.symbols.keys()) |ref| { |
| 36 | const sym = elf_file.symbol(ref).?; |
| 37 | thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.name(elf_file).len + "$thunk".len + 1)); |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | pub fn writeSymtab(thunk: Thunk, elf_file: *Elf) void { |
| 42 | const cpu_arch = elf_file.getTarget().cpu.arch; |
| 43 | for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| { |
| 44 | const sym = elf_file.symbol(ref).?; |
| 45 | const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); |
| 46 | elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file)); |
| 47 | elf_file.strtab.appendSliceAssumeCapacity("$thunk"); |
| 48 | elf_file.strtab.appendAssumeCapacity(0); |
| 49 | elf_file.symtab.items[ilocal] = .{ |
| 50 | .st_name = st_name, |
| 51 | .st_info = elf.STT_FUNC, |
| 52 | .st_other = 0, |
| 53 | .st_shndx = @intCast(thunk.output_section_index), |
| 54 | .st_value = @intCast(thunk.targetAddress(ref, elf_file)), |
| 55 | .st_size = trampolineSize(cpu_arch), |
| 56 | }; |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) usize { |
| 61 | return switch (cpu_arch) { |
| 62 | .aarch64, .aarch64_be => aarch64.trampoline_size, |
| 63 | .x86_64, .riscv64, .riscv64be => unreachable, |
| 64 | else => @panic("unhandled arch"), |
| 65 | }; |
| 66 | } |
| 67 | |
| 68 | pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Alt(Format, Format.default) { |
| 69 | return .{ .data = .{ |
| 70 | .thunk = thunk, |
| 71 | .elf_file = elf_file, |
| 72 | } }; |
| 73 | } |
| 74 | |
| 75 | const Format = struct { |
| 76 | thunk: Thunk, |
| 77 | elf_file: *Elf, |
| 78 | |
| 79 | fn default(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void { |
| 80 | const thunk = f.thunk; |
| 81 | const elf_file = f.elf_file; |
| 82 | try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) }); |
| 83 | for (thunk.symbols.keys()) |ref| { |
| 84 | const sym = elf_file.symbol(ref).?; |
| 85 | try writer.print(" {f} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value }); |
| 86 | } |
| 87 | } |
| 88 | }; |
| 89 | |
| 90 | pub const Index = u32; |
| 91 | |
| 92 | const aarch64 = struct { |
| 93 | fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void { |
| 94 | for (thunk.symbols.keys(), 0..) |ref, i| { |
| 95 | const sym = elf_file.symbol(ref).?; |
| 96 | const saddr = thunk.address(elf_file) + @as(i64, @intCast(i * trampoline_size)); |
| 97 | const taddr = sym.address(.{}, elf_file); |
| 98 | try writer.writeInt(u32, @bitCast( |
| 99 | util.encoding.Instruction.adrp(.x16, try util.calcNumberOfPages(saddr, taddr) << 12), |
| 100 | ), .little); |
| 101 | try writer.writeInt(u32, @bitCast(util.encoding.Instruction.add( |
| 102 | .x16, |
| 103 | .x16, |
| 104 | .{ .immediate = @truncate(@as(u64, @bitCast(taddr))) }, |
| 105 | )), .little); |
| 106 | try writer.writeInt(u32, @bitCast(util.encoding.Instruction.br(.x16)), .little); |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | const trampoline_size = 3 * @sizeOf(u32); |
| 111 | |
| 112 | const util = @import("../aarch64.zig"); |
| 113 | }; |
| 114 | |
| 115 | const assert = std.debug.assert; |
| 116 | const elf = std.elf; |
| 117 | const log = std.log.scoped(.link); |
| 118 | const math = std.math; |
| 119 | const mem = std.mem; |
| 120 | const std = @import("std"); |
| 121 | |
| 122 | const Allocator = mem.Allocator; |
| 123 | const Atom = @import("Atom.zig"); |
| 124 | const Elf = @import("../Elf.zig"); |
| 125 | const Symbol = @import("Symbol.zig"); |
| 126 | |
| 127 | const Thunk = @This(); |