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)...@@ -1373,7 +1373,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
1373 try self.writePhdrTable();1373 try self.writePhdrTable();
1374 try self.writeShdrTable();1374 try self.writeShdrTable();
1375 try self.writeAtoms();1375 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
1378 if (self.entry_index == null and self.base.isExe()) {1385 if (self.entry_index == null and self.base.isExe()) {
1379 log.debug("flushing. no_entry_point_found = true", .{});1386 log.debug("flushing. no_entry_point_found = true", .{});
...@@ -4047,7 +4054,7 @@ fn updateSectionSizes(self: *Elf) !void {...@@ -4047,7 +4054,7 @@ fn updateSectionSizes(self: *Elf) !void {
4047 }4054 }
40484055
4049 if (self.plt_section_index) |index| {4056 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);
4051 }4058 }
40524059
4053 if (self.got_plt_section_index) |index| {4060 if (self.got_plt_section_index) |index| {
...@@ -4692,14 +4699,7 @@ fn writeSyntheticSections(self: *Elf) !void {...@@ -4692,14 +4699,7 @@ fn writeSyntheticSections(self: *Elf) !void {
4692 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;4699 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
4693 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);4700 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
4694 defer buffer.deinit();4701 defer buffer.deinit();
4695 eh_frame.writeEhFrame(self, buffer.writer()) catch |err| switch (err) {4702 try eh_frame.writeEhFrame(self, buffer.writer());
4696 error.RelocFailure => return error.FlushFailure,
4697 error.UnsupportedCpuArch => {
4698 try self.reportUnsupportedCpuArch();
4699 return error.FlushFailure;
4700 },
4701 else => |e| return e,
4702 };
4703 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);4703 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
4704 }4704 }
47054705
...@@ -4731,7 +4731,7 @@ fn writeSyntheticSections(self: *Elf) !void {...@@ -4731,7 +4731,7 @@ fn writeSyntheticSections(self: *Elf) !void {
47314731
4732 if (self.plt_section_index) |shndx| {4732 if (self.plt_section_index) |shndx| {
4733 const shdr = self.shdrs.items[shndx];4733 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));
4735 defer buffer.deinit();4735 defer buffer.deinit();
4736 try self.plt.write(self, buffer.writer());4736 try self.plt.write(self, buffer.writer());
4737 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);4737 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
src/link/Elf/LdScript.zig+1
...@@ -139,6 +139,7 @@ const Parser = struct {...@@ -139,6 +139,7 @@ const Parser = struct {
139 } else return error.UnexpectedToken;139 } else return error.UnexpectedToken;
140 };140 };
141 if (std.mem.eql(u8, value, "elf64-x86-64")) return .x86_64;141 if (std.mem.eql(u8, value, "elf64-x86-64")) return .x86_64;
142 if (std.mem.eql(u8, value, "elf64-littleaarch64")) return .aarch64;
142 return error.UnknownCpuArch;143 return error.UnknownCpuArch;
143 }144 }
144145
src/link/Elf/Object.zig+5-6
...@@ -371,17 +371,16 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:...@@ -371,17 +371,16 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:
371 const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {371 const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
372 elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u32, @intCast(i)),372 elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u32, @intCast(i)),
373 else => {},373 else => {},
374 } else {374 } else null;
375 // TODO: convert into an error
376 log.debug("{s}: missing reloc section for unwind info section", .{self.fmtPath()});
377 return;
378 };
379375
380 const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx);376 const raw = try self.preadShdrContentsAlloc(allocator, handle, shndx);
381 defer allocator.free(raw);377 defer allocator.free(raw);
382 const data_start = @as(u32, @intCast(self.eh_frame_data.items.len));378 const data_start = @as(u32, @intCast(self.eh_frame_data.items.len));
383 try self.eh_frame_data.appendSlice(allocator, raw);379 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{};
385 defer allocator.free(relocs);384 defer allocator.free(relocs);
386 const rel_start = @as(u32, @intCast(self.relocs.items.len));385 const rel_start = @as(u32, @intCast(self.relocs.items.len));
387 try self.relocs.appendUnalignedSlice(allocator, relocs);386 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 {...@@ -146,7 +146,8 @@ pub fn pltAddress(symbol: Symbol, elf_file: *Elf) u64 {
146 if (!symbol.flags.has_plt) return 0;146 if (!symbol.flags.has_plt) return 0;
147 const extras = symbol.extra(elf_file).?;147 const extras = symbol.extra(elf_file).?;
148 const shdr = elf_file.shdrs.items[elf_file.plt_section_index.?];148 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);
150}151}
151152
152pub fn gotPltAddress(symbol: Symbol, elf_file: *Elf) u64 {153pub fn gotPltAddress(symbol: Symbol, elf_file: *Elf) u64 {
src/link/Elf/eh_frame.zig+1
...@@ -214,6 +214,7 @@ pub const Iterator = struct {...@@ -214,6 +214,7 @@ pub const Iterator = struct {
214 const reader = stream.reader();214 const reader = stream.reader();
215215
216 const size = try reader.readInt(u32, .little);216 const size = try reader.readInt(u32, .little);
217 if (size == 0) return null;
217 if (size == 0xFFFFFFFF) @panic("TODO");218 if (size == 0xFFFFFFFF) @panic("TODO");
218219
219 const id = try reader.readInt(u32, .little);220 const id = try reader.readInt(u32, .little);
src/link/Elf/synthetic_sections.zig+117-33
...@@ -857,8 +857,6 @@ pub const PltSection = struct {...@@ -857,8 +857,6 @@ pub const PltSection = struct {
857 symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},857 symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
858 output_symtab_ctx: Elf.SymtabCtx = .{},858 output_symtab_ctx: Elf.SymtabCtx = .{},
859859
860 pub const preamble_size = 32;
861
862 pub fn deinit(plt: *PltSection, allocator: Allocator) void {860 pub fn deinit(plt: *PltSection, allocator: Allocator) void {
863 plt.symbols.deinit(allocator);861 plt.symbols.deinit(allocator);
864 }862 }
...@@ -877,39 +875,33 @@ pub const PltSection = struct {...@@ -877,39 +875,33 @@ pub const PltSection = struct {
877 try plt.symbols.append(gpa, sym_index);875 try plt.symbols.append(gpa, sym_index);
878 }876 }
879877
880 pub fn size(plt: PltSection) usize {878 pub fn size(plt: PltSection, elf_file: *Elf) usize {
881 return preamble_size + plt.symbols.items.len * 16;879 const cpu_arch = elf_file.getTarget().cpu.arch;
880 return preambleSize(cpu_arch) + plt.symbols.items.len * entrySize(cpu_arch);
882 }881 }
883882
884 pub fn write(plt: PltSection, elf_file: *Elf, writer: anytype) !void {883 pub fn preambleSize(cpu_arch: std.Target.Cpu.Arch) usize {
885 const plt_addr = elf_file.shdrs.items[elf_file.plt_section_index.?].sh_addr;884 return switch (cpu_arch) {
886 const got_plt_addr = elf_file.shdrs.items[elf_file.got_plt_section_index.?].sh_addr;885 .x86_64 => 32,
887 var preamble = [_]u8{886 .aarch64 => 8 * @sizeOf(u32),
888 0xf3, 0x0f, 0x1e, 0xfa, // endbr64887 else => @panic("TODO implement preambleSize for this cpu arch"),
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]
892 };888 };
893 var disp = @as(i64, @intCast(got_plt_addr + 8)) - @as(i64, @intCast(plt_addr + 8)) - 4;889 }
894 mem.writeInt(i32, preamble[8..][0..4], @as(i32, @intCast(disp)), .little);890
895 disp = @as(i64, @intCast(got_plt_addr + 16)) - @as(i64, @intCast(plt_addr + 14)) - 4;891 pub fn entrySize(cpu_arch: std.Target.Cpu.Arch) usize {
896 mem.writeInt(i32, preamble[14..][0..4], @as(i32, @intCast(disp)), .little);892 return switch (cpu_arch) {
897 try writer.writeAll(&preamble);893 .x86_64 => 16,
898 try writer.writeByteNTimes(0xcc, preamble_size - preamble.len);894 .aarch64 => 4 * @sizeOf(u32),
899895 else => @panic("TODO implement entrySize for this cpu arch"),
900 for (plt.symbols.items, 0..) |sym_index, i| {896 };
901 const sym = elf_file.symbol(sym_index);897 }
902 const target_addr = sym.gotPltAddress(elf_file);898
903 const source_addr = sym.pltAddress(elf_file);899 pub fn write(plt: PltSection, elf_file: *Elf, writer: anytype) !void {
904 disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 12)) - 4;900 const cpu_arch = elf_file.getTarget().cpu.arch;
905 var entry = [_]u8{901 switch (cpu_arch) {
906 0xf3, 0x0f, 0x1e, 0xfa, // endbr64902 .x86_64 => try x86_64.write(plt, elf_file, writer),
907 0x41, 0xbb, 0x00, 0x00, 0x00, 0x00, // mov r11d, N903 .aarch64 => try aarch64.write(plt, elf_file, writer),
908 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [rip] -> .got.plt[N]904 else => return error.UnsupportedCpuArch,
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);
913 }905 }
914 }906 }
915907
...@@ -946,6 +938,7 @@ pub const PltSection = struct {...@@ -946,6 +938,7 @@ pub const PltSection = struct {
946 }938 }
947939
948 pub fn writeSymtab(plt: PltSection, elf_file: *Elf) void {940 pub fn writeSymtab(plt: PltSection, elf_file: *Elf) void {
941 const cpu_arch = elf_file.getTarget().cpu.arch;
949 for (plt.symbols.items, plt.output_symtab_ctx.ilocal..) |sym_index, ilocal| {942 for (plt.symbols.items, plt.output_symtab_ctx.ilocal..) |sym_index, ilocal| {
950 const sym = elf_file.symbol(sym_index);943 const sym = elf_file.symbol(sym_index);
951 const st_name = @as(u32, @intCast(elf_file.strtab.items.len));944 const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
...@@ -958,7 +951,7 @@ pub const PltSection = struct {...@@ -958,7 +951,7 @@ pub const PltSection = struct {
958 .st_other = 0,951 .st_other = 0,
959 .st_shndx = @intCast(elf_file.plt_section_index.?),952 .st_shndx = @intCast(elf_file.plt_section_index.?),
960 .st_value = sym.pltAddress(elf_file),953 .st_value = sym.pltAddress(elf_file),
961 .st_size = 16,954 .st_size = entrySize(cpu_arch),
962 };955 };
963 }956 }
964 }957 }
...@@ -992,6 +985,97 @@ pub const PltSection = struct {...@@ -992,6 +985,97 @@ pub const PltSection = struct {
992 });985 });
993 }986 }
994 }987 }
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 };
995};1079};
9961080
997pub const GotPltSection = struct {1081pub const GotPltSection = struct {