authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-03-08 22:44:37+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-03-08 22:46:18+01:00
log859f9a22c4ac28a4e6b003b6e07c2c9912fcb074
tree1020151bec8fb6c54eb15334d7095a8b4b16605b
parent7c5ddb6ae410a861e139ce36dec94434392bfdad

elf+aarch64: implement basic thunk support


5 files changed, 319 insertions(+), 15 deletions(-)

CMakeLists.txt+1
...@@ -597,6 +597,7 @@ set(ZIG_STAGE2_SOURCES...@@ -597,6 +597,7 @@ set(ZIG_STAGE2_SOURCES
597 "${CMAKE_SOURCE_DIR}/src/link/Elf/relocatable.zig"597 "${CMAKE_SOURCE_DIR}/src/link/Elf/relocatable.zig"
598 "${CMAKE_SOURCE_DIR}/src/link/Elf/relocation.zig"598 "${CMAKE_SOURCE_DIR}/src/link/Elf/relocation.zig"
599 "${CMAKE_SOURCE_DIR}/src/link/Elf/synthetic_sections.zig"599 "${CMAKE_SOURCE_DIR}/src/link/Elf/synthetic_sections.zig"
600 "${CMAKE_SOURCE_DIR}/src/link/Elf/thunks.zig"
600 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"601 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
601 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"602 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
602 "${CMAKE_SOURCE_DIR}/src/link/MachO/Atom.zig"603 "${CMAKE_SOURCE_DIR}/src/link/MachO/Atom.zig"
src/link/Elf.zig+56-2
...@@ -206,6 +206,9 @@ num_ifunc_dynrelocs: usize = 0,...@@ -206,6 +206,9 @@ num_ifunc_dynrelocs: usize = 0,
206/// List of atoms that are owned directly by the linker.206/// List of atoms that are owned directly by the linker.
207atoms: std.ArrayListUnmanaged(Atom) = .{},207atoms: std.ArrayListUnmanaged(Atom) = .{},
208208
209/// List of range extension thunks.
210thunks: std.ArrayListUnmanaged(Thunk) = .{},
211
209/// Table of last atom index in a section and matching atom free list if any.212/// Table of last atom index in a section and matching atom free list if any.
210last_atom_and_free_list_table: LastAtomAndFreeListTable = .{},213last_atom_and_free_list_table: LastAtomAndFreeListTable = .{},
211214
...@@ -255,7 +258,7 @@ pub fn createEmpty(...@@ -255,7 +258,7 @@ pub fn createEmpty(
255 };258 };
256259
257 const page_size: u32 = switch (target.cpu.arch) {260 const page_size: u32 = switch (target.cpu.arch) {
258 .powerpc64le => 0x10000,261 .aarch64, .powerpc64le => 0x10000,
259 .sparc64 => 0x2000,262 .sparc64 => 0x2000,
260 else => 0x1000,263 else => 0x1000,
261 };264 };
...@@ -488,6 +491,7 @@ pub fn deinit(self: *Elf) void {...@@ -488,6 +491,7 @@ pub fn deinit(self: *Elf) void {
488 self.start_stop_indexes.deinit(gpa);491 self.start_stop_indexes.deinit(gpa);
489492
490 self.atoms.deinit(gpa);493 self.atoms.deinit(gpa);
494 self.thunks.deinit(gpa);
491 for (self.last_atom_and_free_list_table.values()) |*value| {495 for (self.last_atom_and_free_list_table.values()) |*value| {
492 value.free_list.deinit(gpa);496 value.free_list.deinit(gpa);
493 }497 }
...@@ -3593,7 +3597,7 @@ fn sortInitFini(self: *Elf) !void {...@@ -3593,7 +3597,7 @@ fn sortInitFini(self: *Elf) !void {
3593 }3597 }
3594 };3598 };
35953599
3596 for (self.shdrs.items, 0..) |*shdr, shndx| {3600 for (self.shdrs.items, 0..) |shdr, shndx| {
3597 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;3601 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
35983602
3599 var is_init_fini = false;3603 var is_init_fini = false;
...@@ -4038,6 +4042,8 @@ fn updateSectionSizes(self: *Elf) !void {...@@ -4038,6 +4042,8 @@ fn updateSectionSizes(self: *Elf) !void {
4038 const target = self.base.comp.root_mod.resolved_target.result;4042 const target = self.base.comp.root_mod.resolved_target.result;
4039 for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| {4043 for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| {
4040 const shdr = &self.shdrs.items[shndx];4044 const shdr = &self.shdrs.items[shndx];
4045 if (atom_list.items.len == 0) continue;
4046 if (self.requiresThunks() and shdr.sh_flags & elf.SHF_EXECINSTR != 0) continue;
4041 for (atom_list.items) |atom_index| {4047 for (atom_list.items) |atom_index| {
4042 const atom_ptr = self.atom(atom_index) orelse continue;4048 const atom_ptr = self.atom(atom_index) orelse continue;
4043 if (!atom_ptr.flags.alive) continue;4049 if (!atom_ptr.flags.alive) continue;
...@@ -4049,6 +4055,17 @@ fn updateSectionSizes(self: *Elf) !void {...@@ -4049,6 +4055,17 @@ fn updateSectionSizes(self: *Elf) !void {
4049 }4055 }
4050 }4056 }
40514057
4058 if (self.requiresThunks()) {
4059 for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| {
4060 const shdr = self.shdrs.items[shndx];
4061 if (shdr.sh_flags & elf.SHF_EXECINSTR == 0) continue;
4062 if (atom_list.items.len == 0) continue;
4063
4064 // Create jump/branch range extenders if needed.
4065 try thunks.createThunks(shndx, self);
4066 }
4067 }
4068
4052 if (self.eh_frame_section_index) |index| {4069 if (self.eh_frame_section_index) |index| {
4053 self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self);4070 self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self);
4054 }4071 }
...@@ -4576,6 +4593,13 @@ pub fn updateSymtabSize(self: *Elf) !void {...@@ -4576,6 +4593,13 @@ pub fn updateSymtabSize(self: *Elf) !void {
4576 nlocals += 1;4593 nlocals += 1;
4577 }4594 }
45784595
4596 for (self.thunks.items) |*th| {
4597 th.output_symtab_ctx.ilocal = nlocals + 1;
4598 th.calcSymtabSize(self);
4599 nlocals += th.output_symtab_ctx.nlocals;
4600 strsize += th.output_symtab_ctx.strsize;
4601 }
4602
4579 for (files.items) |index| {4603 for (files.items) |index| {
4580 const file_ptr = self.file(index).?;4604 const file_ptr = self.file(index).?;
4581 const ctx = switch (file_ptr) {4605 const ctx = switch (file_ptr) {
...@@ -4806,6 +4830,10 @@ pub fn writeSymtab(self: *Elf) !void {...@@ -4806,6 +4830,10 @@ pub fn writeSymtab(self: *Elf) !void {
48064830
4807 self.writeSectionSymbols();4831 self.writeSectionSymbols();
48084832
4833 for (self.thunks.items) |th| {
4834 th.writeSymtab(self);
4835 }
4836
4809 if (self.zigObjectPtr()) |zig_object| {4837 if (self.zigObjectPtr()) |zig_object| {
4810 zig_object.asFile().writeSymtab(self);4838 zig_object.asFile().writeSymtab(self);
4811 }4839 }
...@@ -5401,6 +5429,18 @@ pub fn addAtom(self: *Elf) !Atom.Index {...@@ -5401,6 +5429,18 @@ pub fn addAtom(self: *Elf) !Atom.Index {
5401 return index;5429 return index;
5402}5430}
54035431
5432pub fn addThunk(self: *Elf) !Thunk.Index {
5433 const index = @as(Thunk.Index, @intCast(self.thunks.items.len));
5434 const th = try self.thunks.addOne(self.base.comp.gpa);
5435 th.* = .{};
5436 return index;
5437}
5438
5439pub fn thunk(self: *Elf, index: Thunk.Index) *Thunk {
5440 assert(index < self.thunks.items.len);
5441 return &self.thunks.items[index];
5442}
5443
5404pub fn file(self: *Elf, index: File.Index) ?File {5444pub fn file(self: *Elf, index: File.Index) ?File {
5405 const tag = self.files.items(.tags)[index];5445 const tag = self.files.items(.tags)[index];
5406 return switch (tag) {5446 return switch (tag) {
...@@ -5957,6 +5997,10 @@ fn fmtDumpState(...@@ -5957,6 +5997,10 @@ fn fmtDumpState(
5957 try writer.print("linker_defined({d}) : (linker defined)\n", .{index});5997 try writer.print("linker_defined({d}) : (linker defined)\n", .{index});
5958 try writer.print("{}\n", .{linker_defined.fmtSymtab(self)});5998 try writer.print("{}\n", .{linker_defined.fmtSymtab(self)});
5959 }5999 }
6000 try writer.writeAll("thunks\n");
6001 for (self.thunks.items, 0..) |th, index| {
6002 try writer.print("thunk({d}) : {}\n", .{ index, th.fmt(self) });
6003 }
5960 try writer.print("{}\n", .{self.zig_got.fmt(self)});6004 try writer.print("{}\n", .{self.zig_got.fmt(self)});
5961 try writer.print("{}\n", .{self.got.fmt(self)});6005 try writer.print("{}\n", .{self.got.fmt(self)});
5962 try writer.print("{}\n", .{self.plt.fmt(self)});6006 try writer.print("{}\n", .{self.plt.fmt(self)});
...@@ -6024,6 +6068,14 @@ pub fn getTarget(self: Elf) std.Target {...@@ -6024,6 +6068,14 @@ pub fn getTarget(self: Elf) std.Target {
6024 return self.base.comp.root_mod.resolved_target.result;6068 return self.base.comp.root_mod.resolved_target.result;
6025}6069}
60266070
6071fn requiresThunks(self: Elf) bool {
6072 return switch (self.getTarget().cpu.arch) {
6073 .aarch64 => true,
6074 .x86_64, .riscv64 => false,
6075 else => @panic("TODO unimplemented architecture"),
6076 };
6077}
6078
6027/// The following three values are only observed at compile-time and used to emit a compile error6079/// The following three values are only observed at compile-time and used to emit a compile error
6028/// to remind the programmer to update expected maximum numbers of different program header types6080/// to remind the programmer to update expected maximum numbers of different program header types
6029/// so that we reserve enough space for the program header table up-front.6081/// so that we reserve enough space for the program header table up-front.
...@@ -6154,6 +6206,7 @@ const musl = @import("../musl.zig");...@@ -6154,6 +6206,7 @@ const musl = @import("../musl.zig");
6154const relocatable = @import("Elf/relocatable.zig");6206const relocatable = @import("Elf/relocatable.zig");
6155const relocation = @import("Elf/relocation.zig");6207const relocation = @import("Elf/relocation.zig");
6156const target_util = @import("../target.zig");6208const target_util = @import("../target.zig");
6209const thunks = @import("Elf/thunks.zig");
6157const trace = @import("../tracy.zig").trace;6210const trace = @import("../tracy.zig").trace;
6158const synthetic_sections = @import("Elf/synthetic_sections.zig");6211const synthetic_sections = @import("Elf/synthetic_sections.zig");
61596212
...@@ -6186,6 +6239,7 @@ const PltGotSection = synthetic_sections.PltGotSection;...@@ -6186,6 +6239,7 @@ const PltGotSection = synthetic_sections.PltGotSection;
6186const SharedObject = @import("Elf/SharedObject.zig");6239const SharedObject = @import("Elf/SharedObject.zig");
6187const Symbol = @import("Elf/Symbol.zig");6240const Symbol = @import("Elf/Symbol.zig");
6188const StringTable = @import("StringTable.zig");6241const StringTable = @import("StringTable.zig");
6242const Thunk = thunks.Thunk;
6189const TypedValue = @import("../TypedValue.zig");6243const TypedValue = @import("../TypedValue.zig");
6190const VerneedSection = synthetic_sections.VerneedSection;6244const VerneedSection = synthetic_sections.VerneedSection;
6191const ZigGotSection = synthetic_sections.ZigGotSection;6245const ZigGotSection = synthetic_sections.ZigGotSection;
src/link/Elf/Atom.zig+18-12
...@@ -31,6 +31,9 @@ rel_num: u32 = 0,...@@ -31,6 +31,9 @@ rel_num: u32 = 0,
31/// Index of this atom in the linker's atoms table.31/// Index of this atom in the linker's atoms table.
32atom_index: Index = 0,32atom_index: Index = 0,
3333
34/// Index of the thunk for this atom.
35thunk_index: Thunk.Index = 0,
36
34/// Flags we use for state tracking.37/// Flags we use for state tracking.
35flags: Flags = .{},38flags: Flags = .{},
3639
...@@ -64,6 +67,10 @@ pub fn file(self: Atom, elf_file: *Elf) ?File {...@@ -64,6 +67,10 @@ pub fn file(self: Atom, elf_file: *Elf) ?File {
64 return elf_file.file(self.file_index);67 return elf_file.file(self.file_index);
65}68}
6669
70pub fn thunk(self: Atom, elf_file: *Elf) *Thunk {
71 return elf_file.thunk(self.thunk_index);
72}
73
67pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr {74pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr {
68 return switch (self.file(elf_file).?) {75 return switch (self.file(elf_file).?) {
69 .object => |x| x.shdrs.items[self.input_section_index],76 .object => |x| x.shdrs.items[self.input_section_index],
...@@ -1681,6 +1688,7 @@ const aarch64 = struct {...@@ -1681,6 +1688,7 @@ const aarch64 = struct {
1681 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;1688 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
1682 const cwriter = stream.writer();1689 const cwriter = stream.writer();
1683 const code = code_buffer[rel.r_offset..][0..4];1690 const code = code_buffer[rel.r_offset..][0..4];
1691 const file_ptr = atom.file(elf_file).?;
16841692
1685 const P, const A, const S, const GOT, const G, const TP, const DTP, const ZIG_GOT = args;1693 const P, const A, const S, const GOT, const G, const TP, const DTP, const ZIG_GOT = args;
1686 _ = DTP;1694 _ = DTP;
...@@ -1701,18 +1709,15 @@ const aarch64 = struct {...@@ -1701,18 +1709,15 @@ const aarch64 = struct {
1701 .CALL26,1709 .CALL26,
1702 .JUMP26,1710 .JUMP26,
1703 => {1711 => {
1704 // TODO: add thunk support1712 const disp: i28 = math.cast(i28, S + A - P) orelse blk: {
1705 const disp: i28 = math.cast(i28, S + A - P) orelse {1713 const th = atom.thunk(elf_file);
1706 var err = try elf_file.addErrorWithNotes(1);1714 const target_index = switch (file_ptr) {
1707 try err.addMsg(elf_file, "TODO: branch relocation target ({s}) exceeds max jump distance", .{1715 .zig_object => |x| x.symbol(rel.r_sym()),
1708 target.name(elf_file),1716 .object => |x| x.symbols.items[rel.r_sym()],
1709 });1717 else => unreachable,
1710 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{1718 };
1711 atom.file(elf_file).?.fmtPath(),1719 const S_: i64 = @intCast(th.targetAddress(target_index, elf_file));
1712 atom.name(elf_file),1720 break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow;
1713 r_offset,
1714 });
1715 return;
1716 };1721 };
1717 aarch64_util.writeBranchImm(disp, code);1722 aarch64_util.writeBranchImm(disp, code);
1718 },1723 },
...@@ -2173,3 +2178,4 @@ const Fde = eh_frame.Fde;...@@ -2173,3 +2178,4 @@ const Fde = eh_frame.Fde;
2173const File = @import("file.zig").File;2178const File = @import("file.zig").File;
2174const Object = @import("Object.zig");2179const Object = @import("Object.zig");
2175const Symbol = @import("Symbol.zig");2180const Symbol = @import("Symbol.zig");
2181const Thunk = @import("thunks.zig").Thunk;
src/link/Elf/Symbol.zig+1-1
...@@ -253,7 +253,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {...@@ -253,7 +253,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
253 break :blk 0;253 break :blk 0;
254 }254 }
255 if (st_shndx == elf.SHN_ABS or st_shndx == elf.SHN_COMMON) break :blk symbol.address(.{ .plt = false }, elf_file);255 if (st_shndx == elf.SHN_ABS or st_shndx == elf.SHN_COMMON) break :blk symbol.address(.{ .plt = false }, elf_file);
256 const shdr = &elf_file.shdrs.items[st_shndx];256 const shdr = elf_file.shdrs.items[st_shndx];
257 if (shdr.sh_flags & elf.SHF_TLS != 0 and file_ptr != .linker_defined)257 if (shdr.sh_flags & elf.SHF_TLS != 0 and file_ptr != .linker_defined)
258 break :blk symbol.address(.{ .plt = false }, elf_file) - elf_file.tlsAddress();258 break :blk symbol.address(.{ .plt = false }, elf_file) - elf_file.tlsAddress();
259 break :blk symbol.address(.{ .plt = false }, elf_file);259 break :blk symbol.address(.{ .plt = false }, elf_file);
src/link/Elf/thunks.zig created+243
...@@ -0,0 +1,243 @@
1pub fn createThunks(shndx: u32, elf_file: *Elf) !void {
2 const gpa = elf_file.base.comp.gpa;
3 const cpu_arch = elf_file.getTarget().cpu.arch;
4 const shdr = &elf_file.shdrs.items[shndx];
5 const atoms = elf_file.output_sections.get(shndx).?.items;
6 assert(atoms.len > 0);
7
8 for (atoms) |atom_index| {
9 elf_file.atom(atom_index).?.value = @bitCast(@as(i64, -1));
10 }
11
12 var i: usize = 0;
13 while (i < atoms.len) {
14 const start = i;
15 const start_atom = elf_file.atom(atoms[start]).?;
16 assert(start_atom.flags.alive);
17 start_atom.value = try advance(shdr, start_atom.size, start_atom.alignment);
18 i += 1;
19
20 while (i < atoms.len and
21 shdr.sh_size - start_atom.value < maxAllowedDistance(cpu_arch)) : (i += 1)
22 {
23 const atom_index = atoms[i];
24 const atom = elf_file.atom(atom_index).?;
25 assert(atom.flags.alive);
26 atom.value = try advance(shdr, atom.size, atom.alignment);
27 }
28
29 // Insert a thunk at the group end
30 const thunk_index = try elf_file.addThunk();
31 const thunk = elf_file.thunk(thunk_index);
32 thunk.output_section_index = shndx;
33
34 // Scan relocs in the group and create trampolines for any unreachable callsite
35 for (atoms[start..i]) |atom_index| {
36 const atom = elf_file.atom(atom_index).?;
37 const file = atom.file(elf_file).?;
38 log.debug("atom({d}) {s}", .{ atom_index, atom.name(elf_file) });
39 for (atom.relocs(elf_file)) |rel| {
40 const is_reachable = switch (cpu_arch) {
41 .aarch64 => aarch64.isReachable(atom, rel, elf_file),
42 .x86_64, .riscv64 => unreachable,
43 else => @panic("unsupported arch"),
44 };
45 if (is_reachable) continue;
46 const target = switch (file) {
47 .zig_object => |x| x.symbol(rel.r_sym()),
48 .object => |x| x.symbols.items[rel.r_sym()],
49 else => unreachable,
50 };
51 try thunk.symbols.put(gpa, target, {});
52 }
53 atom.thunk_index = thunk_index;
54 }
55
56 thunk.value = try advance(shdr, thunk.size(elf_file), Atom.Alignment.fromNonzeroByteUnits(2));
57
58 log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(elf_file) });
59 }
60}
61
62fn advance(shdr: *elf.Elf64_Shdr, size: u64, alignment: Atom.Alignment) !u64 {
63 const offset = alignment.forward(shdr.sh_size);
64 const padding = offset - shdr.sh_size;
65 shdr.sh_size += padding + size;
66 shdr.sh_addralign = @max(shdr.sh_addralign, alignment.toByteUnits(1));
67 return offset;
68}
69
70/// A branch will need an extender if its target is larger than
71/// `2^(jump_bits - 1) - margin` where margin is some arbitrary number.
72fn maxAllowedDistance(cpu_arch: std.Target.Cpu.Arch) u32 {
73 return switch (cpu_arch) {
74 .aarch64 => 0x500_000,
75 .x86_64, .riscv64 => unreachable,
76 else => @panic("unhandled arch"),
77 };
78}
79
80pub const Thunk = struct {
81 value: u64 = 0,
82 output_section_index: u32 = 0,
83 symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{},
84 output_symtab_ctx: Elf.SymtabCtx = .{},
85
86 pub fn deinit(thunk: *Thunk, allocator: Allocator) void {
87 thunk.symbols.deinit(allocator);
88 }
89
90 pub fn size(thunk: Thunk, elf_file: *Elf) usize {
91 const cpu_arch = elf_file.getTarget().cpu.arch;
92 return thunk.symbols.keys().len * trampolineSize(cpu_arch);
93 }
94
95 pub fn address(thunk: Thunk, elf_file: *Elf) u64 {
96 const shdr = elf_file.shdrs.items[thunk.output_section_index];
97 return shdr.sh_addr + thunk.value;
98 }
99
100 pub fn targetAddress(thunk: Thunk, sym_index: Symbol.Index, elf_file: *Elf) u64 {
101 const cpu_arch = elf_file.getTarget().cpu.arch;
102 return thunk.address(elf_file) + thunk.symbols.getIndex(sym_index).? * trampolineSize(cpu_arch);
103 }
104
105 pub fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void {
106 switch (elf_file.options.cpu_arch.?) {
107 .aarch64 => try aarch64.write(thunk, elf_file, writer),
108 .x86_64, .riscv64 => unreachable,
109 else => @panic("unhandled arch"),
110 }
111 }
112
113 pub fn calcSymtabSize(thunk: *Thunk, elf_file: *Elf) void {
114 thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len));
115 for (thunk.symbols.keys()) |sym_index| {
116 const sym = elf_file.symbol(sym_index);
117 thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.name(elf_file).len + "$thunk".len + 1));
118 }
119 }
120
121 pub fn writeSymtab(thunk: Thunk, elf_file: *Elf) void {
122 const cpu_arch = elf_file.getTarget().cpu.arch;
123 for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |sym_index, ilocal| {
124 const sym = elf_file.symbol(sym_index);
125 const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
126 elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
127 elf_file.strtab.appendSliceAssumeCapacity("$thunk");
128 elf_file.strtab.appendAssumeCapacity(0);
129 elf_file.symtab.items[ilocal] = .{
130 .st_name = st_name,
131 .st_info = elf.STT_FUNC,
132 .st_other = 0,
133 .st_shndx = @intCast(thunk.output_section_index),
134 .st_value = thunk.targetAddress(sym_index, elf_file),
135 .st_size = trampolineSize(cpu_arch),
136 };
137 }
138 }
139
140 fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) usize {
141 return switch (cpu_arch) {
142 .aarch64 => aarch64.trampoline_size,
143 .x86_64, .riscv64 => unreachable,
144 else => @panic("unhandled arch"),
145 };
146 }
147
148 pub fn format(
149 thunk: Thunk,
150 comptime unused_fmt_string: []const u8,
151 options: std.fmt.FormatOptions,
152 writer: anytype,
153 ) !void {
154 _ = thunk;
155 _ = unused_fmt_string;
156 _ = options;
157 _ = writer;
158 @compileError("do not format Thunk directly");
159 }
160
161 pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(format2) {
162 return .{ .data = .{
163 .thunk = thunk,
164 .elf_file = elf_file,
165 } };
166 }
167
168 const FormatContext = struct {
169 thunk: Thunk,
170 elf_file: *Elf,
171 };
172
173 fn format2(
174 ctx: FormatContext,
175 comptime unused_fmt_string: []const u8,
176 options: std.fmt.FormatOptions,
177 writer: anytype,
178 ) !void {
179 _ = options;
180 _ = unused_fmt_string;
181 const thunk = ctx.thunk;
182 const elf_file = ctx.elf_file;
183 try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) });
184 for (thunk.symbols.keys()) |index| {
185 const sym = elf_file.symbol(index);
186 try writer.print(" %{d} : {s} : @{x}\n", .{ index, sym.name(elf_file), sym.value });
187 }
188 }
189
190 pub const Index = u32;
191};
192
193const aarch64 = struct {
194 fn isReachable(atom: *const Atom, rel: elf.Elf64_Rela, elf_file: *Elf) bool {
195 const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type());
196 if (r_type != .CALL26 and r_type != .JUMP26) return true;
197 const file = atom.file(elf_file).?;
198 const target_index = switch (file) {
199 .zig_object => |x| x.symbol(rel.r_sym()),
200 .object => |x| x.symbols.items[rel.r_sym()],
201 else => unreachable,
202 };
203 const target = elf_file.symbol(target_index);
204 if (target.flags.has_plt) return false;
205 if (atom.output_section_index != target.output_section_index) return false;
206 const target_atom = target.atom(elf_file).?;
207 if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false;
208 const saddr = @as(i64, @intCast(atom.address(elf_file) + rel.r_offset));
209 const taddr: i64 = @intCast(target.address(.{}, elf_file));
210 _ = math.cast(i28, taddr + rel.r_addend - saddr) orelse return false;
211 return true;
212 }
213
214 fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void {
215 for (thunk.symbols.keys(), 0..) |sym_index, i| {
216 const sym = elf_file.symbol(sym_index);
217 const saddr = thunk.address(elf_file) + i * trampoline_size;
218 const taddr = sym.address(.{}, elf_file);
219 const pages = try util.calcNumberOfPages(saddr, taddr);
220 try writer.writeInt(u32, Instruction.adrp(.x16, pages).toU32(), .little);
221 const off: u12 = @truncate(taddr);
222 try writer.writeInt(u32, Instruction.add(.x16, .x16, off, false).toU32(), .little);
223 try writer.writeInt(u32, Instruction.br(.x16).toU32(), .little);
224 }
225 }
226
227 const trampoline_size = 3 * @sizeOf(u32);
228
229 const util = @import("../aarch64.zig");
230 const Instruction = util.Instruction;
231};
232
233const assert = std.debug.assert;
234const elf = std.elf;
235const log = std.log.scoped(.link);
236const math = std.math;
237const mem = std.mem;
238const std = @import("std");
239
240const Allocator = mem.Allocator;
241const Atom = @import("Atom.zig");
242const Elf = @import("../Elf.zig");
243const Symbol = @import("Symbol.zig");