diff options
| author | mlugg <mlugg@mlugg.co.uk> | 2024-08-28 18:25:14 +0100 |
|---|---|---|
| committer | mlugg <mlugg@mlugg.co.uk> | 2024-08-29 23:43:52 +0100 |
| commit | c62487da76b08a0dfb69fbf76501250ca065c140 (patch) | |
| tree | 1ef46d45f3ff9edaade3805d9ab5a9910cb8571b /src/link/Elf | |
| parent | ba8d3f69ca65738f27deea43e795f5e787a061f2 (diff) | |
| download | zig-c62487da76b08a0dfb69fbf76501250ca065c140.tar.gz zig-c62487da76b08a0dfb69fbf76501250ca065c140.zip | |
compiler: avoid field/decl name conflicts
Most of the required renames here are net wins for readaibility, I'd
say. The ones in `arch` are a little more verbose, but I think better. I
didn't bother renaming the non-conflicting functions in
`arch/arm/bits.zig` and `arch/aarch64/bits.zig`, since these backends
are pretty bit-rotted anyway AIUI.
Diffstat (limited to 'src/link/Elf')
| -rw-r--r-- | src/link/Elf/Atom.zig | 2 | ||||
| -rw-r--r-- | src/link/Elf/Thunk.zig | 144 | ||||
| -rw-r--r-- | src/link/Elf/thunks.zig | 234 |
3 files changed, 145 insertions, 235 deletions
diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 10a7aa2b79..ef2301f1cd 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -2251,6 +2251,6 @@ const Fde = eh_frame.Fde; const File = @import("file.zig").File; const Object = @import("Object.zig"); const Symbol = @import("Symbol.zig"); -const Thunk = @import("thunks.zig").Thunk; +const Thunk = @import("Thunk.zig"); const ZigObject = @import("ZigObject.zig"); const dev = @import("../../dev.zig"); diff --git a/src/link/Elf/Thunk.zig b/src/link/Elf/Thunk.zig new file mode 100644 index 0000000000..389ba7ffed --- /dev/null +++ b/src/link/Elf/Thunk.zig @@ -0,0 +1,144 @@ +value: i64 = 0, +output_section_index: u32 = 0, +symbols: std.AutoArrayHashMapUnmanaged(Elf.Ref, void) = .{}, +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 => try aarch64.write(thunk, elf_file, writer), + .x86_64, .riscv64 => 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.trampoline_size, + .x86_64, .riscv64 => unreachable, + else => @panic("unhandled arch"), + }; +} + +pub fn format( + thunk: Thunk, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = thunk; + _ = unused_fmt_string; + _ = options; + _ = writer; + @compileError("do not format Thunk directly"); +} + +pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(format2) { + return .{ .data = .{ + .thunk = thunk, + .elf_file = elf_file, + } }; +} + +const FormatContext = struct { + thunk: Thunk, + elf_file: *Elf, +}; + +fn format2( + ctx: FormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = options; + _ = unused_fmt_string; + const thunk = ctx.thunk; + const elf_file = ctx.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(" {} : {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); + const pages = try util.calcNumberOfPages(saddr, taddr); + try writer.writeInt(u32, Instruction.adrp(.x16, pages).toU32(), .little); + const off: u12 = @truncate(@as(u64, @bitCast(taddr))); + try writer.writeInt(u32, Instruction.add(.x16, .x16, off, false).toU32(), .little); + try writer.writeInt(u32, Instruction.br(.x16).toU32(), .little); + } + } + + const trampoline_size = 3 * @sizeOf(u32); + + const util = @import("../aarch64.zig"); + const Instruction = util.Instruction; +}; + +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(); diff --git a/src/link/Elf/thunks.zig b/src/link/Elf/thunks.zig deleted file mode 100644 index bc534639da..0000000000 --- a/src/link/Elf/thunks.zig +++ /dev/null @@ -1,234 +0,0 @@ -pub fn createThunks(shdr: *elf.Elf64_Shdr, shndx: u32, elf_file: *Elf) !void { - const gpa = elf_file.base.comp.gpa; - const cpu_arch = elf_file.getTarget().cpu.arch; - const max_distance = maxAllowedDistance(cpu_arch); - const atoms = elf_file.sections.items(.atom_list)[shndx].items; - assert(atoms.len > 0); - - for (atoms) |ref| { - elf_file.atom(ref).?.value = -1; - } - - var i: usize = 0; - while (i < atoms.len) { - const start = i; - const start_atom = elf_file.atom(atoms[start]).?; - assert(start_atom.alive); - start_atom.value = try advance(shdr, start_atom.size, start_atom.alignment); - i += 1; - - while (i < atoms.len) : (i += 1) { - const atom = elf_file.atom(atoms[i]).?; - assert(atom.alive); - if (@as(i64, @intCast(atom.alignment.forward(shdr.sh_size))) - start_atom.value >= max_distance) - break; - atom.value = try advance(shdr, atom.size, atom.alignment); - } - - // Insert a thunk at the group end - const thunk_index = try elf_file.addThunk(); - const thunk = elf_file.thunk(thunk_index); - thunk.output_section_index = shndx; - - // Scan relocs in the group and create trampolines for any unreachable callsite - for (atoms[start..i]) |ref| { - const atom = elf_file.atom(ref).?; - const file = atom.file(elf_file).?; - log.debug("atom({}) {s}", .{ ref, atom.name(elf_file) }); - for (atom.relocs(elf_file)) |rel| { - const is_reachable = switch (cpu_arch) { - .aarch64 => aarch64.isReachable(atom, rel, elf_file), - .x86_64, .riscv64 => unreachable, - else => @panic("unsupported arch"), - }; - if (is_reachable) continue; - const target = file.resolveSymbol(rel.r_sym(), elf_file); - try thunk.symbols.put(gpa, target, {}); - } - atom.addExtra(.{ .thunk = thunk_index }, elf_file); - } - - thunk.value = try advance(shdr, thunk.size(elf_file), Atom.Alignment.fromNonzeroByteUnits(2)); - - log.debug("thunk({d}) : {}", .{ thunk_index, thunk.fmt(elf_file) }); - } -} - -fn advance(shdr: *elf.Elf64_Shdr, size: u64, alignment: Atom.Alignment) !i64 { - const offset = alignment.forward(shdr.sh_size); - const padding = offset - shdr.sh_size; - shdr.sh_size += padding + size; - shdr.sh_addralign = @max(shdr.sh_addralign, alignment.toByteUnits() orelse 1); - return @intCast(offset); -} - -/// A branch will need an extender if its target is larger than -/// `2^(jump_bits - 1) - margin` where margin is some arbitrary number. -fn maxAllowedDistance(cpu_arch: std.Target.Cpu.Arch) u32 { - return switch (cpu_arch) { - .aarch64 => 0x500_000, - .x86_64, .riscv64 => unreachable, - else => @panic("unhandled arch"), - }; -} - -pub const Thunk = struct { - value: i64 = 0, - output_section_index: u32 = 0, - symbols: std.AutoArrayHashMapUnmanaged(Elf.Ref, void) = .{}, - 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 => try aarch64.write(thunk, elf_file, writer), - .x86_64, .riscv64 => 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.trampoline_size, - .x86_64, .riscv64 => unreachable, - else => @panic("unhandled arch"), - }; - } - - pub fn format( - thunk: Thunk, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = thunk; - _ = unused_fmt_string; - _ = options; - _ = writer; - @compileError("do not format Thunk directly"); - } - - pub fn fmt(thunk: Thunk, elf_file: *Elf) std.fmt.Formatter(format2) { - return .{ .data = .{ - .thunk = thunk, - .elf_file = elf_file, - } }; - } - - const FormatContext = struct { - thunk: Thunk, - elf_file: *Elf, - }; - - fn format2( - ctx: FormatContext, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = options; - _ = unused_fmt_string; - const thunk = ctx.thunk; - const elf_file = ctx.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(" {} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value }); - } - } - - pub const Index = u32; -}; - -const aarch64 = struct { - fn isReachable(atom: *const Atom, rel: elf.Elf64_Rela, elf_file: *Elf) bool { - const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type()); - if (r_type != .CALL26 and r_type != .JUMP26) return true; - const file = atom.file(elf_file).?; - const target_ref = file.resolveSymbol(rel.r_sym(), elf_file); - const target = elf_file.symbol(target_ref).?; - if (target.flags.has_plt) return false; - if (atom.output_section_index != target.output_section_index) return false; - const target_atom = target.atom(elf_file).?; - if (target_atom.value == -1) return false; - const saddr = atom.address(elf_file) + @as(i64, @intCast(rel.r_offset)); - const taddr = target.address(.{}, elf_file); - _ = math.cast(i28, taddr + rel.r_addend - saddr) orelse return false; - return true; - } - - 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); - const pages = try util.calcNumberOfPages(saddr, taddr); - try writer.writeInt(u32, Instruction.adrp(.x16, pages).toU32(), .little); - const off: u12 = @truncate(@as(u64, @bitCast(taddr))); - try writer.writeInt(u32, Instruction.add(.x16, .x16, off, false).toU32(), .little); - try writer.writeInt(u32, Instruction.br(.x16).toU32(), .little); - } - } - - const trampoline_size = 3 * @sizeOf(u32); - - const util = @import("../aarch64.zig"); - const Instruction = util.Instruction; -}; - -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"); |
