authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-03-05 23:15:39+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-03-08 22:46:17+01:00
log1cf45fb20916568659fcce33dfcfd97013712270
tree60690af4f3f8b4292b236b28e0d8f1af6118fc67
parentf3227598ebe9ac7e330fea0259d4290ee31e96b9

elf+aarch64: implement enough to link dynamically with gcc as the driver


6 files changed, 137 insertions(+), 51 deletions(-)

src/link/Elf.zig+11-11
......@@ -1373,7 +1373,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
13731373 try self.writePhdrTable();
13741374 try self.writeShdrTable();
13751375 try self.writeAtoms();
1376 try self.writeSyntheticSections();
1376 self.writeSyntheticSections() catch |err| switch (err) {
1377 error.RelocFailure => return error.FlushFailure,
1378 error.UnsupportedCpuArch => {
1379 try self.reportUnsupportedCpuArch();
1380 return error.FlushFailure;
1381 },
1382 else => |e| return e,
1383 };
13771384
13781385 if (self.entry_index == null and self.base.isExe()) {
13791386 log.debug("flushing. no_entry_point_found = true", .{});
......@@ -4047,7 +4054,7 @@ fn updateSectionSizes(self: *Elf) !void {
40474054 }
40484055
40494056 if (self.plt_section_index) |index| {
4050 self.shdrs.items[index].sh_size = self.plt.size();
4057 self.shdrs.items[index].sh_size = self.plt.size(self);
40514058 }
40524059
40534060 if (self.got_plt_section_index) |index| {
......@@ -4692,14 +4699,7 @@ fn writeSyntheticSections(self: *Elf) !void {
46924699 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
46934700 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
46944701 defer buffer.deinit();
4695 eh_frame.writeEhFrame(self, buffer.writer()) catch |err| switch (err) {
4696 error.RelocFailure => return error.FlushFailure,
4697 error.UnsupportedCpuArch => {
4698 try self.reportUnsupportedCpuArch();
4699 return error.FlushFailure;
4700 },
4701 else => |e| return e,
4702 };
4702 try eh_frame.writeEhFrame(self, buffer.writer());
47034703 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
47044704 }
47054705
......@@ -4731,7 +4731,7 @@ fn writeSyntheticSections(self: *Elf) !void {
47314731
47324732 if (self.plt_section_index) |shndx| {
47334733 const shdr = self.shdrs.items[shndx];
4734 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt.size());
4734 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt.size(self));
47354735 defer buffer.deinit();
47364736 try self.plt.write(self, buffer.writer());
47374737 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
src/link/Elf/LdScript.zig+1
......@@ -139,6 +139,7 @@ const Parser = struct {
139139 } else return error.UnexpectedToken;
140140 };
141141 if (std.mem.eql(u8, value, "elf64-x86-64")) return .x86_64;
142 if (std.mem.eql(u8, value, "elf64-littleaarch64")) return .aarch64;
142143 return error.UnknownCpuArch;
143144 }
144145
src/link/Elf/Object.zig+5-6
......@@ -371,17 +371,16 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:
371371 const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
372372 elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u32, @intCast(i)),
373373 else => {},
374 } else {
375 // TODO: convert into an error
376 log.debug("{s}: missing reloc section for unwind info section", .{self.fmtPath()});
377 return;
378 };
374 } else null;
379375
380376 const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx);
381377 defer allocator.free(raw);
382378 const data_start = @as(u32, @intCast(self.eh_frame_data.items.len));
383379 try self.eh_frame_data.appendSlice(allocator, raw);
384 const relocs = try self.preadRelocsAlloc(allocator, handle, relocs_shndx);
380 const relocs = if (relocs_shndx) |index|
381 try self.preadRelocsAlloc(allocator, handle, index)
382 else
383 &[0]elf.Elf64_Rela{};
385384 defer allocator.free(relocs);
386385 const rel_start = @as(u32, @intCast(self.relocs.items.len));
387386 try self.relocs.appendUnalignedSlice(allocator, relocs);
src/link/Elf/Symbol.zig+2-1
......@@ -146,7 +146,8 @@ pub fn pltAddress(symbol: Symbol, elf_file: *Elf) u64 {
146146 if (!symbol.flags.has_plt) return 0;
147147 const extras = symbol.extra(elf_file).?;
148148 const shdr = elf_file.shdrs.items[elf_file.plt_section_index.?];
149 return shdr.sh_addr + extras.plt * 16 + PltSection.preamble_size;
149 const cpu_arch = elf_file.getTarget().cpu.arch;
150 return shdr.sh_addr + extras.plt * PltSection.entrySize(cpu_arch) + PltSection.preambleSize(cpu_arch);
150151}
151152
152153pub fn gotPltAddress(symbol: Symbol, elf_file: *Elf) u64 {
src/link/Elf/eh_frame.zig+1
......@@ -214,6 +214,7 @@ pub const Iterator = struct {
214214 const reader = stream.reader();
215215
216216 const size = try reader.readInt(u32, .little);
217 if (size == 0) return null;
217218 if (size == 0xFFFFFFFF) @panic("TODO");
218219
219220 const id = try reader.readInt(u32, .little);
src/link/Elf/synthetic_sections.zig+117-33
......@@ -857,8 +857,6 @@ pub const PltSection = struct {
857857 symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
858858 output_symtab_ctx: Elf.SymtabCtx = .{},
859859
860 pub const preamble_size = 32;
861
862860 pub fn deinit(plt: *PltSection, allocator: Allocator) void {
863861 plt.symbols.deinit(allocator);
864862 }
......@@ -877,39 +875,33 @@ pub const PltSection = struct {
877875 try plt.symbols.append(gpa, sym_index);
878876 }
879877
880 pub fn size(plt: PltSection) usize {
881 return preamble_size + plt.symbols.items.len * 16;
878 pub fn size(plt: PltSection, elf_file: *Elf) usize {
879 const cpu_arch = elf_file.getTarget().cpu.arch;
880 return preambleSize(cpu_arch) + plt.symbols.items.len * entrySize(cpu_arch);
882881 }
883882
884 pub fn write(plt: PltSection, elf_file: *Elf, writer: anytype) !void {
885 const plt_addr = elf_file.shdrs.items[elf_file.plt_section_index.?].sh_addr;
886 const got_plt_addr = elf_file.shdrs.items[elf_file.got_plt_section_index.?].sh_addr;
887 var preamble = [_]u8{
888 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
889 0x41, 0x53, // push r11
890 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // push qword ptr [rip] -> .got.plt[1]
891 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[2]
883 pub fn preambleSize(cpu_arch: std.Target.Cpu.Arch) usize {
884 return switch (cpu_arch) {
885 .x86_64 => 32,
886 .aarch64 => 8 * @sizeOf(u32),
887 else => @panic("TODO implement preambleSize for this cpu arch"),
892888 };
893 var disp = @as(i64, @intCast(got_plt_addr + 8)) - @as(i64, @intCast(plt_addr + 8)) - 4;
894 mem.writeInt(i32, preamble[8..][0..4], @as(i32, @intCast(disp)), .little);
895 disp = @as(i64, @intCast(got_plt_addr + 16)) - @as(i64, @intCast(plt_addr + 14)) - 4;
896 mem.writeInt(i32, preamble[14..][0..4], @as(i32, @intCast(disp)), .little);
897 try writer.writeAll(&preamble);
898 try writer.writeByteNTimes(0xcc, preamble_size - preamble.len);
899
900 for (plt.symbols.items, 0..) |sym_index, i| {
901 const sym = elf_file.symbol(sym_index);
902 const target_addr = sym.gotPltAddress(elf_file);
903 const source_addr = sym.pltAddress(elf_file);
904 disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 12)) - 4;
905 var entry = [_]u8{
906 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
907 0x41, 0xbb, 0x00, 0x00, 0x00, 0x00, // mov r11d, N
908 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[N]
909 };
910 mem.writeInt(i32, entry[6..][0..4], @as(i32, @intCast(i)), .little);
911 mem.writeInt(i32, entry[12..][0..4], @as(i32, @intCast(disp)), .little);
912 try writer.writeAll(&entry);
889 }
890
891 pub fn entrySize(cpu_arch: std.Target.Cpu.Arch) usize {
892 return switch (cpu_arch) {
893 .x86_64 => 16,
894 .aarch64 => 4 * @sizeOf(u32),
895 else => @panic("TODO implement entrySize for this cpu arch"),
896 };
897 }
898
899 pub fn write(plt: PltSection, elf_file: *Elf, writer: anytype) !void {
900 const cpu_arch = elf_file.getTarget().cpu.arch;
901 switch (cpu_arch) {
902 .x86_64 => try x86_64.write(plt, elf_file, writer),
903 .aarch64 => try aarch64.write(plt, elf_file, writer),
904 else => return error.UnsupportedCpuArch,
913905 }
914906 }
915907
......@@ -946,6 +938,7 @@ pub const PltSection = struct {
946938 }
947939
948940 pub fn writeSymtab(plt: PltSection, elf_file: *Elf) void {
941 const cpu_arch = elf_file.getTarget().cpu.arch;
949942 for (plt.symbols.items, plt.output_symtab_ctx.ilocal..) |sym_index, ilocal| {
950943 const sym = elf_file.symbol(sym_index);
951944 const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
......@@ -958,7 +951,7 @@ pub const PltSection = struct {
958951 .st_other = 0,
959952 .st_shndx = @intCast(elf_file.plt_section_index.?),
960953 .st_value = sym.pltAddress(elf_file),
961 .st_size = 16,
954 .st_size = entrySize(cpu_arch),
962955 };
963956 }
964957 }
......@@ -992,6 +985,97 @@ pub const PltSection = struct {
992985 });
993986 }
994987 }
988
989 const x86_64 = struct {
990 fn write(plt: PltSection, elf_file: *Elf, writer: anytype) !void {
991 const plt_addr = elf_file.shdrs.items[elf_file.plt_section_index.?].sh_addr;
992 const got_plt_addr = elf_file.shdrs.items[elf_file.got_plt_section_index.?].sh_addr;
993 var preamble = [_]u8{
994 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
995 0x41, 0x53, // push r11
996 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // push qword ptr [rip] -> .got.plt[1]
997 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[2]
998 };
999 var disp = @as(i64, @intCast(got_plt_addr + 8)) - @as(i64, @intCast(plt_addr + 8)) - 4;
1000 mem.writeInt(i32, preamble[8..][0..4], @as(i32, @intCast(disp)), .little);
1001 disp = @as(i64, @intCast(got_plt_addr + 16)) - @as(i64, @intCast(plt_addr + 14)) - 4;
1002 mem.writeInt(i32, preamble[14..][0..4], @as(i32, @intCast(disp)), .little);
1003 try writer.writeAll(&preamble);
1004 try writer.writeByteNTimes(0xcc, preambleSize(.x86_64) - preamble.len);
1005
1006 for (plt.symbols.items, 0..) |sym_index, i| {
1007 const sym = elf_file.symbol(sym_index);
1008 const target_addr = sym.gotPltAddress(elf_file);
1009 const source_addr = sym.pltAddress(elf_file);
1010 disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 12)) - 4;
1011 var entry = [_]u8{
1012 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
1013 0x41, 0xbb, 0x00, 0x00, 0x00, 0x00, // mov r11d, N
1014 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[N]
1015 };
1016 mem.writeInt(i32, entry[6..][0..4], @as(i32, @intCast(i)), .little);
1017 mem.writeInt(i32, entry[12..][0..4], @as(i32, @intCast(disp)), .little);
1018 try writer.writeAll(&entry);
1019 }
1020 }
1021 };
1022
1023 const aarch64 = struct {
1024 fn write(plt: PltSection, elf_file: *Elf, writer: anytype) !void {
1025 {
1026 const plt_addr = elf_file.shdrs.items[elf_file.plt_section_index.?].sh_addr;
1027 const got_plt_addr = elf_file.shdrs.items[elf_file.got_plt_section_index.?].sh_addr;
1028 // TODO: relax if possible
1029 // .got.plt[2]
1030 const pages = try aarch64_util.calcNumberOfPages(plt_addr + 4, got_plt_addr + 16);
1031 const ldr_off = try aarch64_util.calcPageOffset(.load_store_64, got_plt_addr + 16);
1032 const add_off = try aarch64_util.calcPageOffset(.arithmetic, got_plt_addr + 16);
1033
1034 const preamble = &[_]Instruction{
1035 Instruction.stp(
1036 .x16,
1037 .x30,
1038 Register.sp,
1039 Instruction.LoadStorePairOffset.pre_index(-16),
1040 ),
1041 Instruction.adrp(.x16, pages),
1042 Instruction.ldr(.x17, .x16, Instruction.LoadStoreOffset.imm(ldr_off)),
1043 Instruction.add(.x16, .x16, add_off, false),
1044 Instruction.br(.x17),
1045 Instruction.nop(),
1046 Instruction.nop(),
1047 Instruction.nop(),
1048 };
1049 comptime assert(preamble.len == 8);
1050 for (preamble) |inst| {
1051 try writer.writeInt(u32, inst.toU32(), .little);
1052 }
1053 }
1054
1055 for (plt.symbols.items) |sym_index| {
1056 const sym = elf_file.symbol(sym_index);
1057 const target_addr = sym.gotPltAddress(elf_file);
1058 const source_addr = sym.pltAddress(elf_file);
1059 const pages = try aarch64_util.calcNumberOfPages(source_addr, target_addr);
1060 const ldr_off = try aarch64_util.calcPageOffset(.load_store_64, target_addr);
1061 const add_off = try aarch64_util.calcPageOffset(.arithmetic, target_addr);
1062 const insts = &[_]Instruction{
1063 Instruction.adrp(.x16, pages),
1064 Instruction.ldr(.x17, .x16, Instruction.LoadStoreOffset.imm(ldr_off)),
1065 Instruction.add(.x16, .x16, add_off, false),
1066 Instruction.br(.x17),
1067 };
1068 comptime assert(insts.len == 4);
1069 for (insts) |inst| {
1070 try writer.writeInt(u32, inst.toU32(), .little);
1071 }
1072 }
1073 }
1074
1075 const aarch64_util = @import("../aarch64.zig");
1076 const Instruction = aarch64_util.Instruction;
1077 const Register = aarch64_util.Register;
1078 };
9951079};
9961080
9971081pub const GotPltSection = struct {