| author | |
| committer | |
| log | 859f9a22c4ac28a4e6b003b6e07c2c9912fcb074 |
| tree | 1020151bec8fb6c54eb15334d7095a8b4b16605b |
| parent | 7c5ddb6ae410a861e139ce36dec94434392bfdad |
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. |
| 207 | atoms: std.ArrayListUnmanaged(Atom) = .{}, | 207 | atoms: std.ArrayListUnmanaged(Atom) = .{}, |
| 208 | 208 | ||
| 209 | /// List of range extension thunks. | ||
| 210 | thunks: 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. |
| 210 | last_atom_and_free_list_table: LastAtomAndFreeListTable = .{}, | 213 | last_atom_and_free_list_table: LastAtomAndFreeListTable = .{}, |
| 211 | 214 | ||
| ... | @@ -255,7 +258,7 @@ pub fn createEmpty( | ... | @@ -255,7 +258,7 @@ pub fn createEmpty( |
| 255 | }; | 258 | }; |
| 256 | 259 | ||
| 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); |
| 489 | 492 | ||
| 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 | }; |
| 3595 | 3599 | ||
| 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; |
| 3598 | 3602 | ||
| 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 | } |
| 4051 | 4057 | ||
| 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 | } |
| 4578 | 4595 | ||
| 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 { |
| 4806 | 4830 | ||
| 4807 | self.writeSectionSymbols(); | 4831 | self.writeSectionSymbols(); |
| 4808 | 4832 | ||
| 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 | } |
| 5403 | 5431 | ||
| 5432 | pub 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 | |||
| 5439 | pub fn thunk(self: *Elf, index: Thunk.Index) *Thunk { | ||
| 5440 | assert(index < self.thunks.items.len); | ||
| 5441 | return &self.thunks.items[index]; | ||
| 5442 | } | ||
| 5443 | |||
| 5404 | pub fn file(self: *Elf, index: File.Index) ?File { | 5444 | pub 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 | } |
| 6026 | 6070 | ||
| 6071 | fn 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 error | 6079 | /// 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 types | 6080 | /// 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"); |
| 6154 | const relocatable = @import("Elf/relocatable.zig"); | 6206 | const relocatable = @import("Elf/relocatable.zig"); |
| 6155 | const relocation = @import("Elf/relocation.zig"); | 6207 | const relocation = @import("Elf/relocation.zig"); |
| 6156 | const target_util = @import("../target.zig"); | 6208 | const target_util = @import("../target.zig"); |
| 6209 | const thunks = @import("Elf/thunks.zig"); | ||
| 6157 | const trace = @import("../tracy.zig").trace; | 6210 | const trace = @import("../tracy.zig").trace; |
| 6158 | const synthetic_sections = @import("Elf/synthetic_sections.zig"); | 6211 | const synthetic_sections = @import("Elf/synthetic_sections.zig"); |
| 6159 | 6212 | ||
| ... | @@ -6186,6 +6239,7 @@ const PltGotSection = synthetic_sections.PltGotSection; | ... | @@ -6186,6 +6239,7 @@ const PltGotSection = synthetic_sections.PltGotSection; |
| 6186 | const SharedObject = @import("Elf/SharedObject.zig"); | 6239 | const SharedObject = @import("Elf/SharedObject.zig"); |
| 6187 | const Symbol = @import("Elf/Symbol.zig"); | 6240 | const Symbol = @import("Elf/Symbol.zig"); |
| 6188 | const StringTable = @import("StringTable.zig"); | 6241 | const StringTable = @import("StringTable.zig"); |
| 6242 | const Thunk = thunks.Thunk; | ||
| 6189 | const TypedValue = @import("../TypedValue.zig"); | 6243 | const TypedValue = @import("../TypedValue.zig"); |
| 6190 | const VerneedSection = synthetic_sections.VerneedSection; | 6244 | const VerneedSection = synthetic_sections.VerneedSection; |
| 6191 | const ZigGotSection = synthetic_sections.ZigGotSection; | 6245 | const 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. |
| 32 | atom_index: Index = 0, | 32 | atom_index: Index = 0, |
| 33 | 33 | ||
| 34 | /// Index of the thunk for this atom. | ||
| 35 | thunk_index: Thunk.Index = 0, | ||
| 36 | |||
| 34 | /// Flags we use for state tracking. | 37 | /// Flags we use for state tracking. |
| 35 | flags: Flags = .{}, | 38 | flags: Flags = .{}, |
| 36 | 39 | ||
| ... | @@ -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 | } |
| 66 | 69 | ||
| 70 | pub fn thunk(self: Atom, elf_file: *Elf) *Thunk { | ||
| 71 | return elf_file.thunk(self.thunk_index); | ||
| 72 | } | ||
| 73 | |||
| 67 | pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr { | 74 | pub 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).?; | ||
| 1684 | 1692 | ||
| 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 support | 1712 | 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; |
| 2173 | const File = @import("file.zig").File; | 2178 | const File = @import("file.zig").File; |
| 2174 | const Object = @import("Object.zig"); | 2179 | const Object = @import("Object.zig"); |
| 2175 | const Symbol = @import("Symbol.zig"); | 2180 | const Symbol = @import("Symbol.zig"); |
| 2181 | const 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 @@ | ||
| 1 | pub 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 | |||
| 62 | fn 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. | ||
| 72 | fn 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 | |||
| 80 | pub 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 | |||
| 193 | const 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 | |||
| 233 | const assert = std.debug.assert; | ||
| 234 | const elf = std.elf; | ||
| 235 | const log = std.log.scoped(.link); | ||
| 236 | const math = std.math; | ||
| 237 | const mem = std.mem; | ||
| 238 | const std = @import("std"); | ||
| 239 | |||
| 240 | const Allocator = mem.Allocator; | ||
| 241 | const Atom = @import("Atom.zig"); | ||
| 242 | const Elf = @import("../Elf.zig"); | ||
| 243 | const Symbol = @import("Symbol.zig"); | ||