From 1cf45fb20916568659fcce33dfcfd97013712270 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 5 Mar 2024 23:15:39 +0100 Subject: [PATCH] elf+aarch64: implement enough to link dynamically with gcc as the driver --- src/link/Elf.zig | 22 ++-- src/link/Elf/LdScript.zig | 1 + src/link/Elf/Object.zig | 11 +- src/link/Elf/Symbol.zig | 3 +- src/link/Elf/eh_frame.zig | 1 + src/link/Elf/synthetic_sections.zig | 150 ++++++++++++++++++++++------ 6 files changed, 137 insertions(+), 51 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 1d7dca9517..3c12c40254 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1373,7 +1373,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) try self.writePhdrTable(); try self.writeShdrTable(); try self.writeAtoms(); - try self.writeSyntheticSections(); + self.writeSyntheticSections() catch |err| switch (err) { + error.RelocFailure => return error.FlushFailure, + error.UnsupportedCpuArch => { + try self.reportUnsupportedCpuArch(); + return error.FlushFailure; + }, + else => |e| return e, + }; if (self.entry_index == null and self.base.isExe()) { log.debug("flushing. no_entry_point_found = true", .{}); @@ -4047,7 +4054,7 @@ fn updateSectionSizes(self: *Elf) !void { } if (self.plt_section_index) |index| { - self.shdrs.items[index].sh_size = self.plt.size(); + self.shdrs.items[index].sh_size = self.plt.size(self); } if (self.got_plt_section_index) |index| { @@ -4692,14 +4699,7 @@ fn writeSyntheticSections(self: *Elf) !void { const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); defer buffer.deinit(); - eh_frame.writeEhFrame(self, buffer.writer()) catch |err| switch (err) { - error.RelocFailure => return error.FlushFailure, - error.UnsupportedCpuArch => { - try self.reportUnsupportedCpuArch(); - return error.FlushFailure; - }, - else => |e| return e, - }; + try eh_frame.writeEhFrame(self, buffer.writer()); try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); } @@ -4731,7 +4731,7 @@ fn writeSyntheticSections(self: *Elf) !void { if (self.plt_section_index) |shndx| { const shdr = self.shdrs.items[shndx]; - var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt.size()); + var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt.size(self)); defer buffer.deinit(); try self.plt.write(self, buffer.writer()); try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); diff --git a/src/link/Elf/LdScript.zig b/src/link/Elf/LdScript.zig index 34cecf5879..8b48ebd39f 100644 --- a/src/link/Elf/LdScript.zig +++ b/src/link/Elf/LdScript.zig @@ -139,6 +139,7 @@ const Parser = struct { } else return error.UnexpectedToken; }; if (std.mem.eql(u8, value, "elf64-x86-64")) return .x86_64; + if (std.mem.eql(u8, value, "elf64-littleaarch64")) return .aarch64; return error.UnknownCpuArch; } diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index d1b35ad286..cc135f2f97 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -371,17 +371,16 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx: const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u32, @intCast(i)), else => {}, - } else { - // TODO: convert into an error - log.debug("{s}: missing reloc section for unwind info section", .{self.fmtPath()}); - return; - }; + } else null; const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx); defer allocator.free(raw); const data_start = @as(u32, @intCast(self.eh_frame_data.items.len)); try self.eh_frame_data.appendSlice(allocator, raw); - const relocs = try self.preadRelocsAlloc(allocator, handle, relocs_shndx); + const relocs = if (relocs_shndx) |index| + try self.preadRelocsAlloc(allocator, handle, index) + else + &[0]elf.Elf64_Rela{}; defer allocator.free(relocs); const rel_start = @as(u32, @intCast(self.relocs.items.len)); try self.relocs.appendUnalignedSlice(allocator, relocs); diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index ea1b4f4b6b..d41307a342 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -146,7 +146,8 @@ pub fn pltAddress(symbol: Symbol, elf_file: *Elf) u64 { if (!symbol.flags.has_plt) return 0; const extras = symbol.extra(elf_file).?; const shdr = elf_file.shdrs.items[elf_file.plt_section_index.?]; - return shdr.sh_addr + extras.plt * 16 + PltSection.preamble_size; + const cpu_arch = elf_file.getTarget().cpu.arch; + return shdr.sh_addr + extras.plt * PltSection.entrySize(cpu_arch) + PltSection.preambleSize(cpu_arch); } pub fn gotPltAddress(symbol: Symbol, elf_file: *Elf) u64 { diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index 048d4c6d5c..3b4668da1d 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -214,6 +214,7 @@ pub const Iterator = struct { const reader = stream.reader(); const size = try reader.readInt(u32, .little); + if (size == 0) return null; if (size == 0xFFFFFFFF) @panic("TODO"); const id = try reader.readInt(u32, .little); diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index 506f4394b3..abedf72aaf 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -857,8 +857,6 @@ pub const PltSection = struct { symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, output_symtab_ctx: Elf.SymtabCtx = .{}, - pub const preamble_size = 32; - pub fn deinit(plt: *PltSection, allocator: Allocator) void { plt.symbols.deinit(allocator); } @@ -877,39 +875,33 @@ pub const PltSection = struct { try plt.symbols.append(gpa, sym_index); } - pub fn size(plt: PltSection) usize { - return preamble_size + plt.symbols.items.len * 16; + pub fn size(plt: PltSection, elf_file: *Elf) usize { + const cpu_arch = elf_file.getTarget().cpu.arch; + return preambleSize(cpu_arch) + plt.symbols.items.len * entrySize(cpu_arch); + } + + pub fn preambleSize(cpu_arch: std.Target.Cpu.Arch) usize { + return switch (cpu_arch) { + .x86_64 => 32, + .aarch64 => 8 * @sizeOf(u32), + else => @panic("TODO implement preambleSize for this cpu arch"), + }; + } + + pub fn entrySize(cpu_arch: std.Target.Cpu.Arch) usize { + return switch (cpu_arch) { + .x86_64 => 16, + .aarch64 => 4 * @sizeOf(u32), + else => @panic("TODO implement entrySize for this cpu arch"), + }; } pub fn write(plt: PltSection, elf_file: *Elf, writer: anytype) !void { - const plt_addr = elf_file.shdrs.items[elf_file.plt_section_index.?].sh_addr; - const got_plt_addr = elf_file.shdrs.items[elf_file.got_plt_section_index.?].sh_addr; - var preamble = [_]u8{ - 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 - 0x41, 0x53, // push r11 - 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // push qword ptr [rip] -> .got.plt[1] - 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[2] - }; - var disp = @as(i64, @intCast(got_plt_addr + 8)) - @as(i64, @intCast(plt_addr + 8)) - 4; - mem.writeInt(i32, preamble[8..][0..4], @as(i32, @intCast(disp)), .little); - disp = @as(i64, @intCast(got_plt_addr + 16)) - @as(i64, @intCast(plt_addr + 14)) - 4; - mem.writeInt(i32, preamble[14..][0..4], @as(i32, @intCast(disp)), .little); - try writer.writeAll(&preamble); - try writer.writeByteNTimes(0xcc, preamble_size - preamble.len); - - for (plt.symbols.items, 0..) |sym_index, i| { - const sym = elf_file.symbol(sym_index); - const target_addr = sym.gotPltAddress(elf_file); - const source_addr = sym.pltAddress(elf_file); - disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 12)) - 4; - var entry = [_]u8{ - 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 - 0x41, 0xbb, 0x00, 0x00, 0x00, 0x00, // mov r11d, N - 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[N] - }; - mem.writeInt(i32, entry[6..][0..4], @as(i32, @intCast(i)), .little); - mem.writeInt(i32, entry[12..][0..4], @as(i32, @intCast(disp)), .little); - try writer.writeAll(&entry); + const cpu_arch = elf_file.getTarget().cpu.arch; + switch (cpu_arch) { + .x86_64 => try x86_64.write(plt, elf_file, writer), + .aarch64 => try aarch64.write(plt, elf_file, writer), + else => return error.UnsupportedCpuArch, } } @@ -946,6 +938,7 @@ pub const PltSection = struct { } pub fn writeSymtab(plt: PltSection, elf_file: *Elf) void { + const cpu_arch = elf_file.getTarget().cpu.arch; for (plt.symbols.items, plt.output_symtab_ctx.ilocal..) |sym_index, ilocal| { const sym = elf_file.symbol(sym_index); const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); @@ -958,7 +951,7 @@ pub const PltSection = struct { .st_other = 0, .st_shndx = @intCast(elf_file.plt_section_index.?), .st_value = sym.pltAddress(elf_file), - .st_size = 16, + .st_size = entrySize(cpu_arch), }; } } @@ -992,6 +985,97 @@ pub const PltSection = struct { }); } } + + const x86_64 = struct { + fn write(plt: PltSection, elf_file: *Elf, writer: anytype) !void { + const plt_addr = elf_file.shdrs.items[elf_file.plt_section_index.?].sh_addr; + const got_plt_addr = elf_file.shdrs.items[elf_file.got_plt_section_index.?].sh_addr; + var preamble = [_]u8{ + 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 + 0x41, 0x53, // push r11 + 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // push qword ptr [rip] -> .got.plt[1] + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[2] + }; + var disp = @as(i64, @intCast(got_plt_addr + 8)) - @as(i64, @intCast(plt_addr + 8)) - 4; + mem.writeInt(i32, preamble[8..][0..4], @as(i32, @intCast(disp)), .little); + disp = @as(i64, @intCast(got_plt_addr + 16)) - @as(i64, @intCast(plt_addr + 14)) - 4; + mem.writeInt(i32, preamble[14..][0..4], @as(i32, @intCast(disp)), .little); + try writer.writeAll(&preamble); + try writer.writeByteNTimes(0xcc, preambleSize(.x86_64) - preamble.len); + + for (plt.symbols.items, 0..) |sym_index, i| { + const sym = elf_file.symbol(sym_index); + const target_addr = sym.gotPltAddress(elf_file); + const source_addr = sym.pltAddress(elf_file); + disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 12)) - 4; + var entry = [_]u8{ + 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 + 0x41, 0xbb, 0x00, 0x00, 0x00, 0x00, // mov r11d, N + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[N] + }; + mem.writeInt(i32, entry[6..][0..4], @as(i32, @intCast(i)), .little); + mem.writeInt(i32, entry[12..][0..4], @as(i32, @intCast(disp)), .little); + try writer.writeAll(&entry); + } + } + }; + + const aarch64 = struct { + fn write(plt: PltSection, elf_file: *Elf, writer: anytype) !void { + { + const plt_addr = elf_file.shdrs.items[elf_file.plt_section_index.?].sh_addr; + const got_plt_addr = elf_file.shdrs.items[elf_file.got_plt_section_index.?].sh_addr; + // TODO: relax if possible + // .got.plt[2] + const pages = try aarch64_util.calcNumberOfPages(plt_addr + 4, got_plt_addr + 16); + const ldr_off = try aarch64_util.calcPageOffset(.load_store_64, got_plt_addr + 16); + const add_off = try aarch64_util.calcPageOffset(.arithmetic, got_plt_addr + 16); + + const preamble = &[_]Instruction{ + Instruction.stp( + .x16, + .x30, + Register.sp, + Instruction.LoadStorePairOffset.pre_index(-16), + ), + Instruction.adrp(.x16, pages), + Instruction.ldr(.x17, .x16, Instruction.LoadStoreOffset.imm(ldr_off)), + Instruction.add(.x16, .x16, add_off, false), + Instruction.br(.x17), + Instruction.nop(), + Instruction.nop(), + Instruction.nop(), + }; + comptime assert(preamble.len == 8); + for (preamble) |inst| { + try writer.writeInt(u32, inst.toU32(), .little); + } + } + + for (plt.symbols.items) |sym_index| { + const sym = elf_file.symbol(sym_index); + const target_addr = sym.gotPltAddress(elf_file); + const source_addr = sym.pltAddress(elf_file); + const pages = try aarch64_util.calcNumberOfPages(source_addr, target_addr); + const ldr_off = try aarch64_util.calcPageOffset(.load_store_64, target_addr); + const add_off = try aarch64_util.calcPageOffset(.arithmetic, target_addr); + const insts = &[_]Instruction{ + Instruction.adrp(.x16, pages), + Instruction.ldr(.x17, .x16, Instruction.LoadStoreOffset.imm(ldr_off)), + Instruction.add(.x16, .x16, add_off, false), + Instruction.br(.x17), + }; + comptime assert(insts.len == 4); + for (insts) |inst| { + try writer.writeInt(u32, inst.toU32(), .little); + } + } + } + + const aarch64_util = @import("../aarch64.zig"); + const Instruction = aarch64_util.Instruction; + const Register = aarch64_util.Register; + }; }; pub const GotPltSection = struct {