authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-02-23 21:41:14+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-02-23 21:41:14+01:00
log7230b68b350b16c637e84f3ff224be24d23214ce
tree442e6a70d71db0787c993723a39379ab9562851a
parent256c5934bfc19d3b8a1cf01bc07c9ad86a6c6524
parentf8989a9c6948f6317799defd6de8bb7e2833f8cb
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #19034 from ziglang/elf-riscv

elf: add basic aarch64 and riscv64 support

14 files changed, 1378 insertions(+), 636 deletions(-)

ci/x86_64-linux-debug.sh+1-1
......@@ -12,7 +12,7 @@ CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.12.0-dev.203+d3bc1cfc4"
1212PREFIX="$HOME/deps/$CACHE_BASENAME"
1313ZIG="$PREFIX/bin/zig"
1414
15export PATH="$HOME/deps/wasmtime-v10.0.2-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-6.1.0.1/bin:$PATH"
15export PATH="$HOME/deps/wasmtime-v10.0.2-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-8.2.1/bin:$PATH"
1616
1717# Make the `zig version` number consistent.
1818# This will affect the cmake command below.
ci/x86_64-linux-release.sh+1-1
......@@ -12,7 +12,7 @@ CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.12.0-dev.203+d3bc1cfc4"
1212PREFIX="$HOME/deps/$CACHE_BASENAME"
1313ZIG="$PREFIX/bin/zig"
1414
15export PATH="$HOME/deps/wasmtime-v10.0.2-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-6.1.0.1/bin:$PATH"
15export PATH="$HOME/deps/wasmtime-v10.0.2-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-8.2.1/bin:$PATH"
1616
1717# Make the `zig version` number consistent.
1818# This will affect the cmake command below.
src/link/Elf.zig+50-17
......@@ -189,6 +189,7 @@ gnu_eh_frame_hdr_index: ?Symbol.Index = null,
189189dso_handle_index: ?Symbol.Index = null,
190190rela_iplt_start_index: ?Symbol.Index = null,
191191rela_iplt_end_index: ?Symbol.Index = null,
192global_pointer_index: ?Symbol.Index = null,
192193start_stop_indexes: std.ArrayListUnmanaged(u32) = .{},
193194
194195/// An array of symbols parsed across all input files.
......@@ -1343,6 +1344,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
13431344 // Beyond this point, everything has been allocated a virtual address and we can resolve
13441345 // the relocations, and commit objects to file.
13451346 if (self.zigObjectPtr()) |zig_object| {
1347 var has_reloc_errors = false;
13461348 for (zig_object.atoms.items) |atom_index| {
13471349 const atom_ptr = self.atom(atom_index) orelse continue;
13481350 if (!atom_ptr.flags.alive) continue;
......@@ -1353,10 +1355,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
13531355 defer gpa.free(code);
13541356 const file_offset = shdr.sh_offset + atom_ptr.value;
13551357 atom_ptr.resolveRelocsAlloc(self, code) catch |err| switch (err) {
1356 // TODO
1357 error.RelaxFail, error.InvalidInstruction, error.CannotEncode => {
1358 log.err("relaxing intructions failed; TODO this should be a fatal linker error", .{});
1359 },
1358 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,
13601359 error.UnsupportedCpuArch => {
13611360 try self.reportUnsupportedCpuArch();
13621361 return error.FlushFailure;
......@@ -1365,19 +1364,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
13651364 };
13661365 try self.base.file.?.pwriteAll(code, file_offset);
13671366 }
1367
1368 if (has_reloc_errors) return error.FlushFailure;
13681369 }
13691370
13701371 try self.writePhdrTable();
13711372 try self.writeShdrTable();
13721373 try self.writeAtoms();
1373
1374 self.writeSyntheticSections() catch |err| switch (err) {
1375 error.UnsupportedCpuArch => {
1376 try self.reportUnsupportedCpuArch();
1377 return error.FlushFailure;
1378 },
1379 else => |e| return e,
1380 };
1374 try self.writeSyntheticSections();
13811375
13821376 if (self.entry_index == null and self.base.isExe()) {
13831377 log.debug("flushing. no_entry_point_found = true", .{});
......@@ -2048,18 +2042,23 @@ fn scanRelocs(self: *Elf) !void {
20482042 if (self.zigObjectPtr()) |zo| objects.appendAssumeCapacity(zo.index);
20492043 objects.appendSliceAssumeCapacity(self.objects.items);
20502044
2045 var has_reloc_errors = false;
20512046 for (objects.items) |index| {
20522047 self.file(index).?.scanRelocs(self, &undefs) catch |err| switch (err) {
2048 error.RelaxFailure => unreachable,
20532049 error.UnsupportedCpuArch => {
20542050 try self.reportUnsupportedCpuArch();
20552051 return error.FlushFailure;
20562052 },
2053 error.RelocFailure => has_reloc_errors = true,
20572054 else => |e| return e,
20582055 };
20592056 }
20602057
20612058 try self.reportUndefinedSymbols(&undefs);
20622059
2060 if (has_reloc_errors) return error.FlushFailure;
2061
20632062 for (self.symbols.items, 0..) |*sym, i| {
20642063 const index = @as(u32, @intCast(i));
20652064 if (!sym.isLocal(self) and !sym.flags.has_dynamic) {
......@@ -3095,6 +3094,10 @@ fn addLinkerDefinedSymbols(self: *Elf) !void {
30953094 }
30963095 }
30973096
3097 if (self.getTarget().cpu.arch == .riscv64 and self.base.isDynLib()) {
3098 self.global_pointer_index = try linker_defined.addGlobal("__global_pointer$", self);
3099 }
3100
30983101 linker_defined.resolveSymbols(self);
30993102}
31003103
......@@ -3222,6 +3225,19 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void {
32223225 stop.output_section_index = shndx;
32233226 }
32243227 }
3228
3229 // __global_pointer$
3230 if (self.global_pointer_index) |index| {
3231 const sym = self.symbol(index);
3232 if (self.sectionByName(".sdata")) |shndx| {
3233 const shdr = self.shdrs.items[shndx];
3234 sym.value = shdr.sh_addr + 0x800;
3235 sym.output_section_index = shndx;
3236 } else {
3237 sym.value = 0;
3238 sym.output_section_index = 0;
3239 }
3240 }
32253241}
32263242
32273243fn checkDuplicates(self: *Elf) !void {
......@@ -4431,6 +4447,8 @@ fn writeAtoms(self: *Elf) !void {
44314447 undefs.deinit();
44324448 }
44334449
4450 var has_reloc_errors = false;
4451
44344452 // TODO iterate over `output_sections` directly
44354453 for (self.shdrs.items, 0..) |shdr, shndx| {
44364454 if (shdr.sh_type == elf.SHT_NULL) continue;
......@@ -4493,14 +4511,11 @@ fn writeAtoms(self: *Elf) !void {
44934511 else
44944512 atom_ptr.resolveRelocsAlloc(self, out_code);
44954513 _ = res catch |err| switch (err) {
4496 // TODO
4497 error.RelaxFail, error.InvalidInstruction, error.CannotEncode => {
4498 log.err("relaxing intructions failed; TODO this should be a fatal linker error", .{});
4499 },
45004514 error.UnsupportedCpuArch => {
45014515 try self.reportUnsupportedCpuArch();
45024516 return error.FlushFailure;
45034517 },
4518 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,
45044519 else => |e| return e,
45054520 };
45064521 }
......@@ -4509,6 +4524,8 @@ fn writeAtoms(self: *Elf) !void {
45094524 }
45104525
45114526 try self.reportUndefinedSymbols(&undefs);
4527
4528 if (has_reloc_errors) return error.FlushFailure;
45124529}
45134530
45144531pub fn updateSymtabSize(self: *Elf) !void {
......@@ -4665,7 +4682,14 @@ fn writeSyntheticSections(self: *Elf) !void {
46654682 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
46664683 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
46674684 defer buffer.deinit();
4668 try eh_frame.writeEhFrame(self, buffer.writer());
4685 eh_frame.writeEhFrame(self, buffer.writer()) catch |err| switch (err) {
4686 error.RelocFailure => return error.FlushFailure,
4687 error.UnsupportedCpuArch => {
4688 try self.reportUnsupportedCpuArch();
4689 return error.FlushFailure;
4690 },
4691 else => |e| return e,
4692 };
46694693 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
46704694 }
46714695
......@@ -5526,6 +5550,15 @@ pub fn comdatGroupOwner(self: *Elf, index: ComdatGroupOwner.Index) *ComdatGroupO
55265550 return &self.comdat_groups_owners.items[index];
55275551}
55285552
5553pub fn gotAddress(self: *Elf) u64 {
5554 const shndx = blk: {
5555 if (self.getTarget().cpu.arch == .x86_64 and self.got_plt_section_index != null)
5556 break :blk self.got_plt_section_index.?;
5557 break :blk if (self.got_section_index) |shndx| shndx else null;
5558 };
5559 return if (shndx) |index| self.shdrs.items[index].sh_addr else 0;
5560}
5561
55295562pub fn tpAddress(self: *Elf) u64 {
55305563 const index = self.phdr_tls_index orelse return 0;
55315564 const phdr = self.phdrs.items[index];
src/link/Elf/Atom.zig+936-411
......@@ -300,7 +300,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
300300 self.* = .{};
301301}
302302
303pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
303pub fn relocs(self: Atom, elf_file: *Elf) []const elf.Elf64_Rela {
304304 const shndx = self.relocsShndx() orelse return &[0]elf.Elf64_Rela{};
305305 return switch (self.file(elf_file).?) {
306306 .zig_object => |x| x.relocs.items[shndx].items,
......@@ -394,11 +394,62 @@ pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool {
394394 return false;
395395}
396396
397pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void {
398 switch (elf_file.getTarget().cpu.arch) {
399 .x86_64 => try x86_64.scanRelocs(self, elf_file, code, undefs),
400 else => return error.UnsupportedCpuArch,
397pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) RelocError!void {
398 const cpu_arch = elf_file.getTarget().cpu.arch;
399 const file_ptr = self.file(elf_file).?;
400 const rels = self.relocs(elf_file);
401
402 var has_reloc_errors = false;
403 var it = RelocsIterator{ .relocs = rels };
404 while (it.next()) |rel| {
405 const r_kind = relocation.decode(rel.r_type(), cpu_arch);
406 if (r_kind == .none) continue;
407
408 const symbol_index = switch (file_ptr) {
409 .zig_object => |x| x.symbol(rel.r_sym()),
410 .object => |x| x.symbols.items[rel.r_sym()],
411 else => unreachable,
412 };
413 const symbol = elf_file.symbol(symbol_index);
414
415 // Check for violation of One Definition Rule for COMDATs.
416 if (symbol.file(elf_file) == null) {
417 // TODO convert into an error
418 log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{
419 file_ptr.fmtPath(),
420 self.name(elf_file),
421 symbol.name(elf_file),
422 });
423 continue;
424 }
425
426 // Report an undefined symbol.
427 if (try self.reportUndefined(elf_file, symbol, symbol_index, rel, undefs)) continue;
428
429 if (symbol.isIFunc(elf_file)) {
430 symbol.flags.needs_got = true;
431 symbol.flags.needs_plt = true;
432 }
433
434 // While traversing relocations, mark symbols that require special handling such as
435 // pointer indirection via GOT, or a stub trampoline via PLT.
436 switch (cpu_arch) {
437 .x86_64 => x86_64.scanReloc(self, elf_file, rel, symbol, code, &it) catch |err| switch (err) {
438 error.RelocFailure => has_reloc_errors = true,
439 else => |e| return e,
440 },
441 .aarch64 => aarch64.scanReloc(self, elf_file, rel, symbol, code, &it) catch |err| switch (err) {
442 error.RelocFailure => has_reloc_errors = true,
443 else => |e| return e,
444 },
445 .riscv64 => riscv.scanReloc(self, elf_file, rel, symbol, code, &it) catch |err| switch (err) {
446 error.RelocFailure => has_reloc_errors = true,
447 else => |e| return e,
448 },
449 else => return error.UnsupportedCpuArch,
450 }
401451 }
452 if (has_reloc_errors) return error.RelocFailure;
402453}
403454
404455fn scanReloc(
......@@ -407,7 +458,7 @@ fn scanReloc(
407458 rel: elf.Elf64_Rela,
408459 action: RelocAction,
409460 elf_file: *Elf,
410) error{OutOfMemory}!void {
461) RelocError!void {
411462 const is_writeable = self.inputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0;
412463 const num_dynrelocs = switch (self.file(elf_file).?) {
413464 .linker_defined => unreachable,
......@@ -554,7 +605,7 @@ fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 {
554605 return 3;
555606}
556607
557fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) error{OutOfMemory}!void {
608fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) RelocError!void {
558609 var err = try elf_file.addErrorWithNotes(1);
559610 try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {} at offset 0x{x}", .{
560611 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
......@@ -564,6 +615,7 @@ fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) er
564615 self.file(elf_file).?.fmtPath(),
565616 self.name(elf_file),
566617 });
618 return error.RelocFailure;
567619}
568620
569621fn reportTextRelocError(
......@@ -571,7 +623,7 @@ fn reportTextRelocError(
571623 symbol: *const Symbol,
572624 rel: elf.Elf64_Rela,
573625 elf_file: *Elf,
574) error{OutOfMemory}!void {
626) RelocError!void {
575627 var err = try elf_file.addErrorWithNotes(1);
576628 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
577629 rel.r_offset,
......@@ -581,6 +633,7 @@ fn reportTextRelocError(
581633 self.file(elf_file).?.fmtPath(),
582634 self.name(elf_file),
583635 });
636 return error.RelocFailure;
584637}
585638
586639fn reportPicError(
......@@ -588,7 +641,7 @@ fn reportPicError(
588641 symbol: *const Symbol,
589642 rel: elf.Elf64_Rela,
590643 elf_file: *Elf,
591) error{OutOfMemory}!void {
644) RelocError!void {
592645 var err = try elf_file.addErrorWithNotes(2);
593646 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
594647 rel.r_offset,
......@@ -599,6 +652,7 @@ fn reportPicError(
599652 self.name(elf_file),
600653 });
601654 try err.addNote(elf_file, "recompile with -fPIC", .{});
655 return error.RelocFailure;
602656}
603657
604658fn reportNoPicError(
......@@ -606,7 +660,7 @@ fn reportNoPicError(
606660 symbol: *const Symbol,
607661 rel: elf.Elf64_Rela,
608662 elf_file: *Elf,
609) error{OutOfMemory}!void {
663) RelocError!void {
610664 var err = try elf_file.addErrorWithNotes(2);
611665 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
612666 rel.r_offset,
......@@ -617,6 +671,7 @@ fn reportNoPicError(
617671 self.name(elf_file),
618672 });
619673 try err.addNote(elf_file, "recompile with -fno-PIC", .{});
674 return error.RelocFailure;
620675}
621676
622677// This function will report any undefined non-weak symbols that are not imports.
......@@ -627,7 +682,7 @@ fn reportUndefined(
627682 sym_index: Symbol.Index,
628683 rel: elf.Elf64_Rela,
629684 undefs: anytype,
630) !void {
685) !bool {
631686 const comp = elf_file.base.comp;
632687 const gpa = comp.gpa;
633688 const rel_esym = switch (self.file(elf_file).?) {
......@@ -647,16 +702,95 @@ fn reportUndefined(
647702 gop.value_ptr.* = std.ArrayList(Atom.Index).init(gpa);
648703 }
649704 try gop.value_ptr.append(self.atom_index);
705 return true;
650706 }
707
708 return false;
651709}
652710
653pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
711pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) RelocError!void {
654712 relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) });
655713
656 switch (elf_file.getTarget().cpu.arch) {
657 .x86_64 => try x86_64.resolveRelocsAlloc(self, elf_file, code),
658 else => return error.UnsupportedCpuArch,
714 const cpu_arch = elf_file.getTarget().cpu.arch;
715 const file_ptr = self.file(elf_file).?;
716 var stream = std.io.fixedBufferStream(code);
717
718 const rels = self.relocs(elf_file);
719 var it = RelocsIterator{ .relocs = rels };
720 var has_reloc_errors = false;
721 while (it.next()) |rel| {
722 const r_kind = relocation.decode(rel.r_type(), cpu_arch);
723 if (r_kind == .none) continue;
724
725 const target = switch (file_ptr) {
726 .zig_object => |x| elf_file.symbol(x.symbol(rel.r_sym())),
727 .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]),
728 else => unreachable,
729 };
730 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
731
732 // We will use equation format to resolve relocations:
733 // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/
734 //
735 // Address of the source atom.
736 const P = @as(i64, @intCast(self.address(elf_file) + rel.r_offset));
737 // Addend from the relocation.
738 const A = rel.r_addend;
739 // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub.
740 const S = @as(i64, @intCast(target.address(.{}, elf_file)));
741 // Address of the global offset table.
742 const GOT = @as(i64, @intCast(elf_file.gotAddress()));
743 // Address of the .zig.got table entry if any.
744 const ZIG_GOT = @as(i64, @intCast(target.zigGotAddress(elf_file)));
745 // Relative offset to the start of the global offset table.
746 const G = @as(i64, @intCast(target.gotAddress(elf_file))) - GOT;
747 // // Address of the thread pointer.
748 const TP = @as(i64, @intCast(elf_file.tpAddress()));
749 // Address of the dynamic thread pointer.
750 const DTP = @as(i64, @intCast(elf_file.dtpAddress()));
751
752 relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ZG({x}) ({s})", .{
753 relocation.fmtRelocType(rel.r_type(), cpu_arch),
754 r_offset,
755 P,
756 S + A,
757 G + GOT + A,
758 ZIG_GOT + A,
759 target.name(elf_file),
760 });
761
762 try stream.seekTo(r_offset);
763
764 const args = ResolveArgs{ P, A, S, GOT, G, TP, DTP, ZIG_GOT };
765
766 switch (cpu_arch) {
767 .x86_64 => x86_64.resolveRelocAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) {
768 error.RelocFailure,
769 error.RelaxFailure,
770 error.InvalidInstruction,
771 error.CannotEncode,
772 => has_reloc_errors = true,
773 else => |e| return e,
774 },
775 .aarch64 => aarch64.resolveRelocAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) {
776 error.RelocFailure,
777 error.RelaxFailure,
778 error.UnexpectedRemainder,
779 error.DivisionByZero,
780 => has_reloc_errors = true,
781 else => |e| return e,
782 },
783 .riscv64 => riscv.resolveRelocAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) {
784 error.RelocFailure,
785 error.RelaxFailure,
786 => has_reloc_errors = true,
787 else => |e| return e,
788 },
789 else => return error.UnsupportedCpuArch,
790 }
659791 }
792
793 if (has_reloc_errors) return error.RelaxFailure;
660794}
661795
662796fn resolveDynAbsReloc(
......@@ -761,10 +895,83 @@ fn applyDynamicReloc(value: i64, elf_file: *Elf, writer: anytype) !void {
761895pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void {
762896 relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) });
763897
764 switch (elf_file.getTarget().cpu.arch) {
765 .x86_64 => try x86_64.resolveRelocsNonAlloc(self, elf_file, code, undefs),
766 else => return error.UnsupportedCpuArch,
898 const cpu_arch = elf_file.getTarget().cpu.arch;
899 const file_ptr = self.file(elf_file).?;
900 var stream = std.io.fixedBufferStream(code);
901
902 const rels = self.relocs(elf_file);
903 var has_reloc_errors = false;
904 var it = RelocsIterator{ .relocs = rels };
905 while (it.next()) |rel| {
906 const r_kind = relocation.decode(rel.r_type(), cpu_arch);
907 if (r_kind == .none) continue;
908
909 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
910
911 const target_index = switch (file_ptr) {
912 .zig_object => |x| x.symbol(rel.r_sym()),
913 .object => |x| x.symbols.items[rel.r_sym()],
914 else => unreachable,
915 };
916 const target = elf_file.symbol(target_index);
917
918 // Check for violation of One Definition Rule for COMDATs.
919 if (target.file(elf_file) == null) {
920 // TODO convert into an error
921 log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{
922 file_ptr.fmtPath(),
923 self.name(elf_file),
924 target.name(elf_file),
925 });
926 continue;
927 }
928
929 // Report an undefined symbol.
930 if (try self.reportUndefined(elf_file, target, target_index, rel, undefs)) continue;
931
932 // We will use equation format to resolve relocations:
933 // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/
934 //
935 const P = @as(i64, @intCast(self.address(elf_file) + rel.r_offset));
936 // Addend from the relocation.
937 const A = rel.r_addend;
938 // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub.
939 const S = @as(i64, @intCast(target.address(.{}, elf_file)));
940 // Address of the global offset table.
941 const GOT = @as(i64, @intCast(elf_file.gotAddress()));
942 // Address of the dynamic thread pointer.
943 const DTP = @as(i64, @intCast(elf_file.dtpAddress()));
944
945 const args = ResolveArgs{ P, A, S, GOT, 0, 0, DTP, 0 };
946
947 relocs_log.debug(" {}: {x}: [{x} => {x}] ({s})", .{
948 relocation.fmtRelocType(rel.r_type(), cpu_arch),
949 rel.r_offset,
950 P,
951 S + A,
952 target.name(elf_file),
953 });
954
955 try stream.seekTo(r_offset);
956
957 switch (cpu_arch) {
958 .x86_64 => x86_64.resolveRelocNonAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) {
959 error.RelocFailure => has_reloc_errors = true,
960 else => |e| return e,
961 },
962 .aarch64 => aarch64.resolveRelocNonAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) {
963 error.RelocFailure => has_reloc_errors = true,
964 else => |e| return e,
965 },
966 .riscv64 => riscv.resolveRelocNonAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) {
967 error.RelocFailure => has_reloc_errors = true,
968 else => |e| return e,
969 },
970 else => return error.UnsupportedCpuArch,
971 }
767972 }
973
974 if (has_reloc_errors) return error.RelocFailure;
768975}
769976
770977pub fn format(
......@@ -831,429 +1038,313 @@ pub const Flags = packed struct {
8311038};
8321039
8331040const x86_64 = struct {
834 fn scanRelocs(atom: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void {
1041 fn scanReloc(
1042 atom: Atom,
1043 elf_file: *Elf,
1044 rel: elf.Elf64_Rela,
1045 symbol: *Symbol,
1046 code: ?[]const u8,
1047 it: *RelocsIterator,
1048 ) !void {
8351049 const is_static = elf_file.base.isStatic();
8361050 const is_dyn_lib = elf_file.base.isDynLib();
837 const file_ptr = atom.file(elf_file).?;
838 const rels = atom.relocs(elf_file);
839 var i: usize = 0;
840 while (i < rels.len) : (i += 1) {
841 const rel = rels[i];
842 const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());
843
844 if (r_type == .NONE) continue;
845
846 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
847
848 const symbol_index = switch (file_ptr) {
849 .zig_object => |x| x.symbol(rel.r_sym()),
850 .object => |x| x.symbols.items[rel.r_sym()],
851 else => unreachable,
852 };
853 const symbol = elf_file.symbol(symbol_index);
854
855 // Check for violation of One Definition Rule for COMDATs.
856 if (symbol.file(elf_file) == null) {
857 // TODO convert into an error
858 log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{
859 file_ptr.fmtPath(),
860 atom.name(elf_file),
861 symbol.name(elf_file),
862 });
863 continue;
864 }
8651051
866 // Report an undefined symbol.
867 try atom.reportUndefined(elf_file, symbol, symbol_index, rel, undefs);
1052 const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());
1053 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
8681054
869 if (symbol.isIFunc(elf_file)) {
870 symbol.flags.needs_got = true;
871 symbol.flags.needs_plt = true;
872 }
1055 switch (r_type) {
1056 .@"64" => {
1057 try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
1058 },
8731059
874 // While traversing relocations, mark symbols that require special handling such as
875 // pointer indirection via GOT, or a stub trampoline via PLT.
876 switch (r_type) {
877 .@"64" => {
878 try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
879 },
1060 .@"32",
1061 .@"32S",
1062 => {
1063 try atom.scanReloc(symbol, rel, absRelocAction(symbol, elf_file), elf_file);
1064 },
8801065
881 .@"32",
882 .@"32S",
883 => {
884 try atom.scanReloc(symbol, rel, absRelocAction(symbol, elf_file), elf_file);
885 },
1066 .GOT32,
1067 .GOTPC32,
1068 .GOTPC64,
1069 .GOTPCREL,
1070 .GOTPCREL64,
1071 .GOTPCRELX,
1072 .REX_GOTPCRELX,
1073 => {
1074 symbol.flags.needs_got = true;
1075 },
8861076
887 .GOT32,
888 .GOTPC32,
889 .GOTPC64,
890 .GOTPCREL,
891 .GOTPCREL64,
892 .GOTPCRELX,
893 .REX_GOTPCRELX,
894 => {
895 symbol.flags.needs_got = true;
896 },
1077 .PLT32,
1078 .PLTOFF64,
1079 => {
1080 if (symbol.flags.import) {
1081 symbol.flags.needs_plt = true;
1082 }
1083 },
8971084
898 .PLT32,
899 .PLTOFF64,
900 => {
901 if (symbol.flags.import) {
902 symbol.flags.needs_plt = true;
903 }
904 },
1085 .PC32 => {
1086 try atom.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file);
1087 },
9051088
906 .PC32 => {
907 try atom.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file);
908 },
1089 .TLSGD => {
1090 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr
9091091
910 .TLSGD => {
911 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr
1092 if (is_static or (!symbol.flags.import and !is_dyn_lib)) {
1093 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a
1094 // We skip the next relocation.
1095 it.skip(1);
1096 } else if (!symbol.flags.import and is_dyn_lib) {
1097 symbol.flags.needs_gottp = true;
1098 it.skip(1);
1099 } else {
1100 symbol.flags.needs_tlsgd = true;
1101 }
1102 },
9121103
913 if (is_static or (!symbol.flags.import and !is_dyn_lib)) {
914 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a
915 // We skip the next relocation.
916 i += 1;
917 } else if (!symbol.flags.import and is_dyn_lib) {
918 symbol.flags.needs_gottp = true;
919 i += 1;
920 } else {
921 symbol.flags.needs_tlsgd = true;
922 }
923 },
1104 .TLSLD => {
1105 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr
9241106
925 .TLSLD => {
926 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr
1107 if (is_static or !is_dyn_lib) {
1108 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a
1109 // We skip the next relocation.
1110 it.skip(1);
1111 } else {
1112 elf_file.got.flags.needs_tlsld = true;
1113 }
1114 },
9271115
928 if (is_static or !is_dyn_lib) {
929 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a
930 // We skip the next relocation.
931 i += 1;
932 } else {
933 elf_file.got.flags.needs_tlsld = true;
934 }
935 },
1116 .GOTTPOFF => {
1117 const should_relax = blk: {
1118 if (is_dyn_lib or symbol.flags.import) break :blk false;
1119 if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false;
1120 break :blk true;
1121 };
1122 if (!should_relax) {
1123 symbol.flags.needs_gottp = true;
1124 }
1125 },
9361126
937 .GOTTPOFF => {
938 const should_relax = blk: {
939 if (is_dyn_lib or symbol.flags.import) break :blk false;
940 if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false;
941 break :blk true;
942 };
943 if (!should_relax) {
944 symbol.flags.needs_gottp = true;
945 }
946 },
1127 .GOTPC32_TLSDESC => {
1128 const should_relax = is_static or (!is_dyn_lib and !symbol.flags.import);
1129 if (!should_relax) {
1130 symbol.flags.needs_tlsdesc = true;
1131 }
1132 },
9471133
948 .GOTPC32_TLSDESC => {
949 const should_relax = is_static or (!is_dyn_lib and !symbol.flags.import);
950 if (!should_relax) {
951 symbol.flags.needs_tlsdesc = true;
952 }
953 },
1134 .TPOFF32,
1135 .TPOFF64,
1136 => {
1137 if (is_dyn_lib) try atom.reportPicError(symbol, rel, elf_file);
1138 },
9541139
955 .TPOFF32,
956 .TPOFF64,
1140 .GOTOFF64,
1141 .DTPOFF32,
1142 .DTPOFF64,
1143 .SIZE32,
1144 .SIZE64,
1145 .TLSDESC_CALL,
1146 => {},
1147
1148 else => |x| switch (@intFromEnum(x)) {
1149 // Zig custom relocations
1150 Elf.R_ZIG_GOT32,
1151 Elf.R_ZIG_GOTPCREL,
9571152 => {
958 if (is_dyn_lib) try atom.reportPicError(symbol, rel, elf_file);
1153 assert(symbol.flags.has_zig_got);
9591154 },
9601155
961 .GOTOFF64,
962 .DTPOFF32,
963 .DTPOFF64,
964 .SIZE32,
965 .SIZE64,
966 .TLSDESC_CALL,
967 => {},
968
969 else => |x| switch (@intFromEnum(x)) {
970 // Zig custom relocations
971 Elf.R_ZIG_GOT32,
972 Elf.R_ZIG_GOTPCREL,
973 => {
974 assert(symbol.flags.has_zig_got);
975 },
976
977 else => try atom.reportUnhandledRelocError(rel, elf_file),
978 },
979 }
1156 else => try atom.reportUnhandledRelocError(rel, elf_file),
1157 },
9801158 }
9811159 }
9821160
983 fn resolveRelocsAlloc(atom: Atom, elf_file: *Elf, code: []u8) !void {
984 const file_ptr = atom.file(elf_file).?;
985 var stream = std.io.fixedBufferStream(code);
986 const cwriter = stream.writer();
1161 fn resolveRelocAlloc(
1162 atom: Atom,
1163 elf_file: *Elf,
1164 rel: elf.Elf64_Rela,
1165 target: *const Symbol,
1166 args: ResolveArgs,
1167 it: *RelocsIterator,
1168 code: []u8,
1169 stream: anytype,
1170 ) (error{ InvalidInstruction, CannotEncode } || RelocError)!void {
1171 const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());
1172 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
9871173
988 const rels = atom.relocs(elf_file);
989 var i: usize = 0;
990 while (i < rels.len) : (i += 1) {
991 const rel = rels[i];
992 const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());
993 if (r_type == .NONE) continue;
994
995 const target = switch (file_ptr) {
996 .zig_object => |x| elf_file.symbol(x.symbol(rel.r_sym())),
997 .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]),
998 else => unreachable,
999 };
1000 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
1001
1002 // We will use equation format to resolve relocations:
1003 // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/
1004 //
1005 // Address of the source atom.
1006 const P = @as(i64, @intCast(atom.address(elf_file) + rel.r_offset));
1007 // Addend from the relocation.
1008 const A = rel.r_addend;
1009 // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub.
1010 const S = @as(i64, @intCast(target.address(.{}, elf_file)));
1011 // Address of the global offset table.
1012 const GOT = blk: {
1013 const shndx = if (elf_file.got_plt_section_index) |shndx|
1014 shndx
1015 else if (elf_file.got_section_index) |shndx|
1016 shndx
1017 else
1018 null;
1019 break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0;
1020 };
1021 // Address of the .zig.got table entry if any.
1022 const ZIG_GOT = @as(i64, @intCast(target.zigGotAddress(elf_file)));
1023 // Relative offset to the start of the global offset table.
1024 const G = @as(i64, @intCast(target.gotAddress(elf_file))) - GOT;
1025 // // Address of the thread pointer.
1026 const TP = @as(i64, @intCast(elf_file.tpAddress()));
1027 // Address of the dynamic thread pointer.
1028 const DTP = @as(i64, @intCast(elf_file.dtpAddress()));
1029
1030 relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ZG({x}) ({s})", .{
1031 relocation.fmtRelocType(rel.r_type(), .x86_64),
1032 r_offset,
1033 P,
1034 S + A,
1035 G + GOT + A,
1036 ZIG_GOT + A,
1037 target.name(elf_file),
1038 });
1174 const cwriter = stream.writer();
10391175
1040 try stream.seekTo(r_offset);
1176 const P, const A, const S, const GOT, const G, const TP, const DTP, const ZIG_GOT = args;
10411177
1042 switch (r_type) {
1043 .NONE => unreachable,
1178 switch (r_type) {
1179 .NONE => unreachable,
10441180
1045 .@"64" => {
1046 try atom.resolveDynAbsReloc(
1047 target,
1048 rel,
1049 dynAbsRelocAction(target, elf_file),
1050 elf_file,
1051 cwriter,
1052 );
1053 },
1181 .@"64" => {
1182 try atom.resolveDynAbsReloc(
1183 target,
1184 rel,
1185 dynAbsRelocAction(target, elf_file),
1186 elf_file,
1187 cwriter,
1188 );
1189 },
10541190
1055 .PLT32,
1056 .PC32,
1057 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little),
1191 .PLT32,
1192 .PC32,
1193 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little),
10581194
1059 .GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little),
1060 .GOTPC32 => try cwriter.writeInt(i32, @as(i32, @intCast(GOT + A - P)), .little),
1061 .GOTPC64 => try cwriter.writeInt(i64, GOT + A - P, .little),
1195 .GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little),
1196 .GOTPC32 => try cwriter.writeInt(i32, @as(i32, @intCast(GOT + A - P)), .little),
1197 .GOTPC64 => try cwriter.writeInt(i64, GOT + A - P, .little),
10621198
1063 .GOTPCRELX => {
1064 if (!target.flags.import and !target.isIFunc(elf_file) and !target.isAbs(elf_file)) blk: {
1065 x86_64.relaxGotpcrelx(code[r_offset - 2 ..]) catch break :blk;
1066 try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little);
1067 continue;
1068 }
1069 try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little);
1070 },
1199 .GOTPCRELX => {
1200 if (!target.flags.import and !target.isIFunc(elf_file) and !target.isAbs(elf_file)) blk: {
1201 x86_64.relaxGotpcrelx(code[r_offset - 2 ..]) catch break :blk;
1202 try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little);
1203 return;
1204 }
1205 try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little);
1206 },
10711207
1072 .REX_GOTPCRELX => {
1073 if (!target.flags.import and !target.isIFunc(elf_file) and !target.isAbs(elf_file)) blk: {
1074 x86_64.relaxRexGotpcrelx(code[r_offset - 3 ..]) catch break :blk;
1075 try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little);
1076 continue;
1077 }
1078 try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little);
1079 },
1208 .REX_GOTPCRELX => {
1209 if (!target.flags.import and !target.isIFunc(elf_file) and !target.isAbs(elf_file)) blk: {
1210 x86_64.relaxRexGotpcrelx(code[r_offset - 3 ..]) catch break :blk;
1211 try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little);
1212 return;
1213 }
1214 try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little);
1215 },
10801216
1081 .@"32" => try cwriter.writeInt(u32, @as(u32, @truncate(@as(u64, @intCast(S + A)))), .little),
1082 .@"32S" => try cwriter.writeInt(i32, @as(i32, @truncate(S + A)), .little),
1217 .@"32" => try cwriter.writeInt(u32, @as(u32, @truncate(@as(u64, @intCast(S + A)))), .little),
1218 .@"32S" => try cwriter.writeInt(i32, @as(i32, @truncate(S + A)), .little),
10831219
1084 .TPOFF32 => try cwriter.writeInt(i32, @as(i32, @truncate(S + A - TP)), .little),
1085 .TPOFF64 => try cwriter.writeInt(i64, S + A - TP, .little),
1220 .TPOFF32 => try cwriter.writeInt(i32, @as(i32, @truncate(S + A - TP)), .little),
1221 .TPOFF64 => try cwriter.writeInt(i64, S + A - TP, .little),
10861222
1087 .DTPOFF32 => try cwriter.writeInt(i32, @as(i32, @truncate(S + A - DTP)), .little),
1088 .DTPOFF64 => try cwriter.writeInt(i64, S + A - DTP, .little),
1223 .DTPOFF32 => try cwriter.writeInt(i32, @as(i32, @truncate(S + A - DTP)), .little),
1224 .DTPOFF64 => try cwriter.writeInt(i64, S + A - DTP, .little),
10891225
1090 .TLSGD => {
1091 if (target.flags.has_tlsgd) {
1092 const S_ = @as(i64, @intCast(target.tlsGdAddress(elf_file)));
1093 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
1094 } else if (target.flags.has_gottp) {
1095 const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));
1096 try x86_64.relaxTlsGdToIe(atom, rels[i .. i + 2], @intCast(S_ - P), elf_file, &stream);
1097 i += 1;
1098 } else {
1099 try x86_64.relaxTlsGdToLe(
1100 atom,
1101 rels[i .. i + 2],
1102 @as(i32, @intCast(S - TP)),
1103 elf_file,
1104 &stream,
1105 );
1106 i += 1;
1107 }
1108 },
1226 .TLSGD => {
1227 if (target.flags.has_tlsgd) {
1228 const S_ = @as(i64, @intCast(target.tlsGdAddress(elf_file)));
1229 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
1230 } else if (target.flags.has_gottp) {
1231 const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));
1232 try x86_64.relaxTlsGdToIe(atom, &.{ rel, it.next().? }, @intCast(S_ - P), elf_file, stream);
1233 } else {
1234 try x86_64.relaxTlsGdToLe(
1235 atom,
1236 &.{ rel, it.next().? },
1237 @as(i32, @intCast(S - TP)),
1238 elf_file,
1239 stream,
1240 );
1241 }
1242 },
11091243
1110 .TLSLD => {
1111 if (elf_file.got.tlsld_index) |entry_index| {
1112 const tlsld_entry = elf_file.got.entries.items[entry_index];
1113 const S_ = @as(i64, @intCast(tlsld_entry.address(elf_file)));
1114 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
1115 } else {
1116 try x86_64.relaxTlsLdToLe(
1117 atom,
1118 rels[i .. i + 2],
1119 @as(i32, @intCast(TP - @as(i64, @intCast(elf_file.tlsAddress())))),
1120 elf_file,
1121 &stream,
1122 );
1123 i += 1;
1124 }
1125 },
1244 .TLSLD => {
1245 if (elf_file.got.tlsld_index) |entry_index| {
1246 const tlsld_entry = elf_file.got.entries.items[entry_index];
1247 const S_ = @as(i64, @intCast(tlsld_entry.address(elf_file)));
1248 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
1249 } else {
1250 try x86_64.relaxTlsLdToLe(
1251 atom,
1252 &.{ rel, it.next().? },
1253 @as(i32, @intCast(TP - @as(i64, @intCast(elf_file.tlsAddress())))),
1254 elf_file,
1255 stream,
1256 );
1257 }
1258 },
11261259
1127 .GOTPC32_TLSDESC => {
1128 if (target.flags.has_tlsdesc) {
1129 const S_ = @as(i64, @intCast(target.tlsDescAddress(elf_file)));
1130 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
1131 } else {
1132 try x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]);
1133 try cwriter.writeInt(i32, @as(i32, @intCast(S - TP)), .little);
1134 }
1135 },
1260 .GOTPC32_TLSDESC => {
1261 if (target.flags.has_tlsdesc) {
1262 const S_ = @as(i64, @intCast(target.tlsDescAddress(elf_file)));
1263 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
1264 } else {
1265 x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch {
1266 var err = try elf_file.addErrorWithNotes(1);
1267 try err.addMsg(elf_file, "could not relax {s}", .{@tagName(r_type)});
1268 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1269 atom.file(elf_file).?.fmtPath(),
1270 atom.name(elf_file),
1271 rel.r_offset,
1272 });
1273 return error.RelaxFailure;
1274 };
1275 try cwriter.writeInt(i32, @as(i32, @intCast(S - TP)), .little);
1276 }
1277 },
11361278
1137 .TLSDESC_CALL => if (!target.flags.has_tlsdesc) {
1138 // call -> nop
1139 try cwriter.writeAll(&.{ 0x66, 0x90 });
1140 },
1279 .TLSDESC_CALL => if (!target.flags.has_tlsdesc) {
1280 // call -> nop
1281 try cwriter.writeAll(&.{ 0x66, 0x90 });
1282 },
11411283
1142 .GOTTPOFF => {
1143 if (target.flags.has_gottp) {
1144 const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));
1145 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
1146 } else {
1147 x86_64.relaxGotTpOff(code[r_offset - 3 ..]) catch unreachable;
1148 try cwriter.writeInt(i32, @as(i32, @intCast(S - TP)), .little);
1149 }
1150 },
1284 .GOTTPOFF => {
1285 if (target.flags.has_gottp) {
1286 const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));
1287 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
1288 } else {
1289 x86_64.relaxGotTpOff(code[r_offset - 3 ..]);
1290 try cwriter.writeInt(i32, @as(i32, @intCast(S - TP)), .little);
1291 }
1292 },
11511293
1152 .GOT32 => try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A)), .little),
1294 .GOT32 => try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A)), .little),
11531295
1154 else => |x| switch (@intFromEnum(x)) {
1155 // Zig custom relocations
1156 Elf.R_ZIG_GOT32 => try cwriter.writeInt(u32, @as(u32, @intCast(ZIG_GOT + A)), .little),
1157 Elf.R_ZIG_GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(ZIG_GOT + A - P)), .little),
1296 else => |x| switch (@intFromEnum(x)) {
1297 // Zig custom relocations
1298 Elf.R_ZIG_GOT32 => try cwriter.writeInt(u32, @as(u32, @intCast(ZIG_GOT + A)), .little),
1299 Elf.R_ZIG_GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(ZIG_GOT + A - P)), .little),
11581300
1159 else => {},
1160 },
1161 }
1301 else => try atom.reportUnhandledRelocError(rel, elf_file),
1302 },
11621303 }
11631304 }
11641305
1165 fn resolveRelocsNonAlloc(atom: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void {
1166 const file_ptr = atom.file(elf_file).?;
1167 var stream = std.io.fixedBufferStream(code);
1306 fn resolveRelocNonAlloc(
1307 atom: Atom,
1308 elf_file: *Elf,
1309 rel: elf.Elf64_Rela,
1310 target: *const Symbol,
1311 args: ResolveArgs,
1312 it: *RelocsIterator,
1313 code: []u8,
1314 stream: anytype,
1315 ) !void {
1316 _ = code;
1317 _ = it;
1318 const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());
11681319 const cwriter = stream.writer();
11691320
1170 const rels = atom.relocs(elf_file);
1171 var i: usize = 0;
1172 while (i < rels.len) : (i += 1) {
1173 const rel = rels[i];
1174 const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());
1175 if (r_type == .NONE) continue;
1176
1177 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
1178
1179 const target_index = switch (file_ptr) {
1180 .zig_object => |x| x.symbol(rel.r_sym()),
1181 .object => |x| x.symbols.items[rel.r_sym()],
1182 else => unreachable,
1183 };
1184 const target = elf_file.symbol(target_index);
1185
1186 // Check for violation of One Definition Rule for COMDATs.
1187 if (target.file(elf_file) == null) {
1188 // TODO convert into an error
1189 log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{
1190 file_ptr.fmtPath(),
1191 atom.name(elf_file),
1192 target.name(elf_file),
1193 });
1194 continue;
1195 }
1196
1197 // Report an undefined symbol.
1198 try atom.reportUndefined(elf_file, target, target_index, rel, undefs);
1199
1200 // We will use equation format to resolve relocations:
1201 // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/
1202 //
1203 const P = @as(i64, @intCast(atom.address(elf_file) + rel.r_offset));
1204 // Addend from the relocation.
1205 const A = rel.r_addend;
1206 // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub.
1207 const S = @as(i64, @intCast(target.address(.{}, elf_file)));
1208 // Address of the global offset table.
1209 const GOT = blk: {
1210 const shndx = if (elf_file.got_plt_section_index) |shndx|
1211 shndx
1212 else if (elf_file.got_section_index) |shndx|
1213 shndx
1214 else
1215 null;
1216 break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0;
1217 };
1218 // Address of the dynamic thread pointer.
1219 const DTP = @as(i64, @intCast(elf_file.dtpAddress()));
1220
1221 relocs_log.debug(" {}: {x}: [{x} => {x}] ({s})", .{
1222 relocation.fmtRelocType(rel.r_type(), .x86_64),
1223 rel.r_offset,
1224 P,
1225 S + A,
1226 target.name(elf_file),
1227 });
1228
1229 try stream.seekTo(r_offset);
1230
1231 switch (r_type) {
1232 .NONE => unreachable,
1233 .@"8" => try cwriter.writeInt(u8, @as(u8, @bitCast(@as(i8, @intCast(S + A)))), .little),
1234 .@"16" => try cwriter.writeInt(u16, @as(u16, @bitCast(@as(i16, @intCast(S + A)))), .little),
1235 .@"32" => try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(S + A)))), .little),
1236 .@"32S" => try cwriter.writeInt(i32, @as(i32, @intCast(S + A)), .little),
1237 .@"64" => try cwriter.writeInt(i64, S + A, .little),
1238 .DTPOFF32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - DTP)), .little),
1239 .DTPOFF64 => try cwriter.writeInt(i64, S + A - DTP, .little),
1240 .GOTOFF64 => try cwriter.writeInt(i64, S + A - GOT, .little),
1241 .GOTPC64 => try cwriter.writeInt(i64, GOT + A, .little),
1242 .SIZE32 => {
1243 const size = @as(i64, @intCast(target.elfSym(elf_file).st_size));
1244 try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(size + A)))), .little);
1245 },
1246 .SIZE64 => {
1247 const size = @as(i64, @intCast(target.elfSym(elf_file).st_size));
1248 try cwriter.writeInt(i64, @as(i64, @intCast(size + A)), .little);
1249 },
1250 else => try atom.reportUnhandledRelocError(rel, elf_file),
1251 }
1321 _, const A, const S, const GOT, _, _, const DTP, _ = args;
1322
1323 switch (r_type) {
1324 .NONE => unreachable,
1325 .@"8" => try cwriter.writeInt(u8, @as(u8, @bitCast(@as(i8, @intCast(S + A)))), .little),
1326 .@"16" => try cwriter.writeInt(u16, @as(u16, @bitCast(@as(i16, @intCast(S + A)))), .little),
1327 .@"32" => try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(S + A)))), .little),
1328 .@"32S" => try cwriter.writeInt(i32, @as(i32, @intCast(S + A)), .little),
1329 .@"64" => try cwriter.writeInt(i64, S + A, .little),
1330 .DTPOFF32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - DTP)), .little),
1331 .DTPOFF64 => try cwriter.writeInt(i64, S + A - DTP, .little),
1332 .GOTOFF64 => try cwriter.writeInt(i64, S + A - GOT, .little),
1333 .GOTPC64 => try cwriter.writeInt(i64, GOT + A, .little),
1334 .SIZE32 => {
1335 const size = @as(i64, @intCast(target.elfSym(elf_file).st_size));
1336 try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(size + A)))), .little);
1337 },
1338 .SIZE64 => {
1339 const size = @as(i64, @intCast(target.elfSym(elf_file).st_size));
1340 try cwriter.writeInt(i64, @as(i64, @intCast(size + A)), .little);
1341 },
1342 else => try atom.reportUnhandledRelocError(rel, elf_file),
12521343 }
12531344 }
12541345
12551346 fn relaxGotpcrelx(code: []u8) !void {
1256 const old_inst = disassemble(code) orelse return error.RelaxFail;
1347 const old_inst = disassemble(code) orelse return error.RelaxFailure;
12571348 const inst = switch (old_inst.encoding.mnemonic) {
12581349 .call => try Instruction.new(old_inst.prefix, .call, &.{
12591350 // TODO: hack to force imm32s in the assembler
......@@ -1263,28 +1354,28 @@ const x86_64 = struct {
12631354 // TODO: hack to force imm32s in the assembler
12641355 .{ .imm = Immediate.s(-129) },
12651356 }),
1266 else => return error.RelaxFail,
1357 else => return error.RelaxFailure,
12671358 };
12681359 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
12691360 const nop = try Instruction.new(.none, .nop, &.{});
1270 encode(&.{ nop, inst }, code) catch return error.RelaxFail;
1361 try encode(&.{ nop, inst }, code);
12711362 }
12721363
12731364 fn relaxRexGotpcrelx(code: []u8) !void {
1274 const old_inst = disassemble(code) orelse return error.RelaxFail;
1365 const old_inst = disassemble(code) orelse return error.RelaxFailure;
12751366 switch (old_inst.encoding.mnemonic) {
12761367 .mov => {
12771368 const inst = try Instruction.new(old_inst.prefix, .lea, &old_inst.ops);
12781369 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
1279 encode(&.{inst}, code) catch return error.RelaxFail;
1370 try encode(&.{inst}, code);
12801371 },
1281 else => return error.RelaxFail,
1372 else => return error.RelaxFailure,
12821373 }
12831374 }
12841375
12851376 fn relaxTlsGdToIe(
12861377 self: Atom,
1287 rels: []align(1) const elf.Elf64_Rela,
1378 rels: []const elf.Elf64_Rela,
12881379 value: i32,
12891380 elf_file: *Elf,
12901381 stream: anytype,
......@@ -1307,7 +1398,7 @@ const x86_64 = struct {
13071398
13081399 else => {
13091400 var err = try elf_file.addErrorWithNotes(1);
1310 try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{
1401 try err.addMsg(elf_file, "TODO: rewrite {} when followed by {}", .{
13111402 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
13121403 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
13131404 });
......@@ -1316,13 +1407,14 @@ const x86_64 = struct {
13161407 self.name(elf_file),
13171408 rels[0].r_offset,
13181409 });
1410 return error.RelaxFailure;
13191411 },
13201412 }
13211413 }
13221414
13231415 fn relaxTlsLdToLe(
13241416 self: Atom,
1325 rels: []align(1) const elf.Elf64_Rela,
1417 rels: []const elf.Elf64_Rela,
13261418 value: i32,
13271419 elf_file: *Elf,
13281420 stream: anytype,
......@@ -1360,7 +1452,7 @@ const x86_64 = struct {
13601452
13611453 else => {
13621454 var err = try elf_file.addErrorWithNotes(1);
1363 try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{
1455 try err.addMsg(elf_file, "TODO: rewrite {} when followed by {}", .{
13641456 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
13651457 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
13661458 });
......@@ -1369,6 +1461,7 @@ const x86_64 = struct {
13691461 self.name(elf_file),
13701462 rels[0].r_offset,
13711463 });
1464 return error.RelaxFailure;
13721465 },
13731466 }
13741467 }
......@@ -1388,24 +1481,24 @@ const x86_64 = struct {
13881481 }
13891482 }
13901483
1391 fn relaxGotTpOff(code: []u8) !void {
1392 const old_inst = disassemble(code) orelse return error.RelaxFail;
1484 fn relaxGotTpOff(code: []u8) void {
1485 const old_inst = disassemble(code) orelse unreachable;
13931486 switch (old_inst.encoding.mnemonic) {
13941487 .mov => {
1395 const inst = try Instruction.new(old_inst.prefix, .mov, &.{
1488 const inst = Instruction.new(old_inst.prefix, .mov, &.{
13961489 old_inst.ops[0],
13971490 // TODO: hack to force imm32s in the assembler
13981491 .{ .imm = Immediate.s(-129) },
1399 });
1492 }) catch unreachable;
14001493 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
1401 encode(&.{inst}, code) catch return error.RelaxFail;
1494 encode(&.{inst}, code) catch unreachable;
14021495 },
1403 else => return error.RelaxFail,
1496 else => unreachable,
14041497 }
14051498 }
14061499
14071500 fn relaxGotPcTlsDesc(code: []u8) !void {
1408 const old_inst = disassemble(code) orelse return error.RelaxFail;
1501 const old_inst = disassemble(code) orelse return error.RelaxFailure;
14091502 switch (old_inst.encoding.mnemonic) {
14101503 .lea => {
14111504 const inst = try Instruction.new(old_inst.prefix, .mov, &.{
......@@ -1414,15 +1507,15 @@ const x86_64 = struct {
14141507 .{ .imm = Immediate.s(-129) },
14151508 });
14161509 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
1417 encode(&.{inst}, code) catch return error.RelaxFail;
1510 try encode(&.{inst}, code);
14181511 },
1419 else => return error.RelaxFail,
1512 else => return error.RelaxFailure,
14201513 }
14211514 }
14221515
14231516 fn relaxTlsGdToLe(
14241517 self: Atom,
1425 rels: []align(1) const elf.Elf64_Rela,
1518 rels: []const elf.Elf64_Rela,
14261519 value: i32,
14271520 elf_file: *Elf,
14281521 stream: anytype,
......@@ -1460,6 +1553,7 @@ const x86_64 = struct {
14601553 self.name(elf_file),
14611554 rels[0].r_offset,
14621555 });
1556 return error.RelaxFailure;
14631557 },
14641558 }
14651559 }
......@@ -1485,15 +1579,446 @@ const x86_64 = struct {
14851579 const Instruction = encoder.Instruction;
14861580};
14871581
1582const aarch64 = struct {
1583 fn scanReloc(
1584 atom: Atom,
1585 elf_file: *Elf,
1586 rel: elf.Elf64_Rela,
1587 symbol: *Symbol,
1588 code: ?[]const u8,
1589 it: *RelocsIterator,
1590 ) !void {
1591 _ = code;
1592 _ = it;
1593
1594 const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type());
1595 switch (r_type) {
1596 .ABS64 => {
1597 try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
1598 },
1599
1600 .ADR_PREL_PG_HI21 => {
1601 try atom.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file);
1602 },
1603
1604 .ADR_GOT_PAGE => {
1605 // TODO: relax if possible
1606 symbol.flags.needs_got = true;
1607 },
1608
1609 .LD64_GOT_LO12_NC,
1610 .LD64_GOTPAGE_LO15,
1611 => {
1612 symbol.flags.needs_got = true;
1613 },
1614
1615 .CALL26,
1616 .JUMP26,
1617 => {
1618 if (symbol.flags.import) {
1619 symbol.flags.needs_plt = true;
1620 }
1621 },
1622
1623 .ADD_ABS_LO12_NC,
1624 .ADR_PREL_LO21,
1625 .LDST8_ABS_LO12_NC,
1626 .LDST16_ABS_LO12_NC,
1627 .LDST32_ABS_LO12_NC,
1628 .LDST64_ABS_LO12_NC,
1629 .LDST128_ABS_LO12_NC,
1630 => {},
1631
1632 else => try atom.reportUnhandledRelocError(rel, elf_file),
1633 }
1634 }
1635
1636 fn resolveRelocAlloc(
1637 atom: Atom,
1638 elf_file: *Elf,
1639 rel: elf.Elf64_Rela,
1640 target: *const Symbol,
1641 args: ResolveArgs,
1642 it: *RelocsIterator,
1643 code: []u8,
1644 stream: anytype,
1645 ) (error{ UnexpectedRemainder, DivisionByZero } || RelocError)!void {
1646 _ = it;
1647
1648 const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type());
1649 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
1650 const cwriter = stream.writer();
1651
1652 const P, const A, const S, const GOT, const G, const TP, const DTP, const ZIG_GOT = args;
1653 _ = TP;
1654 _ = DTP;
1655 _ = ZIG_GOT;
1656
1657 switch (r_type) {
1658 .NONE => unreachable,
1659 .ABS64 => {
1660 try atom.resolveDynAbsReloc(
1661 target,
1662 rel,
1663 dynAbsRelocAction(target, elf_file),
1664 elf_file,
1665 cwriter,
1666 );
1667 },
1668
1669 .CALL26,
1670 .JUMP26,
1671 => {
1672 // TODO: add thunk support
1673 const disp: i28 = math.cast(i28, S + A - P) orelse {
1674 var err = try elf_file.addErrorWithNotes(1);
1675 try err.addMsg(elf_file, "TODO: branch relocation target ({s}) exceeds max jump distance", .{
1676 target.name(elf_file),
1677 });
1678 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1679 atom.file(elf_file).?.fmtPath(),
1680 atom.name(elf_file),
1681 r_offset,
1682 });
1683 return;
1684 };
1685 try aarch64_util.writeBranchImm(disp, code[r_offset..][0..4]);
1686 },
1687
1688 .ADR_PREL_PG_HI21 => {
1689 // TODO: check for relaxation of ADRP+ADD
1690 const saddr = @as(u64, @intCast(P));
1691 const taddr = @as(u64, @intCast(S + A));
1692 const pages = @as(u21, @bitCast(try aarch64_util.calcNumberOfPages(saddr, taddr)));
1693 try aarch64_util.writePages(pages, code[r_offset..][0..4]);
1694 },
1695
1696 .ADR_GOT_PAGE => if (target.flags.has_got) {
1697 const saddr = @as(u64, @intCast(P));
1698 const taddr = @as(u64, @intCast(G + GOT + A));
1699 const pages = @as(u21, @bitCast(try aarch64_util.calcNumberOfPages(saddr, taddr)));
1700 try aarch64_util.writePages(pages, code[r_offset..][0..4]);
1701 } else {
1702 // TODO: relax
1703 var err = try elf_file.addErrorWithNotes(1);
1704 try err.addMsg(elf_file, "TODO: relax ADR_GOT_PAGE", .{});
1705 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1706 atom.file(elf_file).?.fmtPath(),
1707 atom.name(elf_file),
1708 r_offset,
1709 });
1710 },
1711
1712 .LD64_GOT_LO12_NC => {
1713 assert(target.flags.has_got);
1714 const taddr = @as(u64, @intCast(G + GOT + A));
1715 try aarch64_util.writePageOffset(.load_store_64, taddr, code[r_offset..][0..4]);
1716 },
1717
1718 .ADD_ABS_LO12_NC,
1719 .LDST8_ABS_LO12_NC,
1720 .LDST16_ABS_LO12_NC,
1721 .LDST32_ABS_LO12_NC,
1722 .LDST64_ABS_LO12_NC,
1723 .LDST128_ABS_LO12_NC,
1724 => {
1725 // TODO: NC means no overflow check
1726 const taddr = @as(u64, @intCast(S + A));
1727 const kind: aarch64_util.PageOffsetInstKind = switch (r_type) {
1728 .ADD_ABS_LO12_NC => .arithmetic,
1729 .LDST8_ABS_LO12_NC => .load_store_8,
1730 .LDST16_ABS_LO12_NC => .load_store_16,
1731 .LDST32_ABS_LO12_NC => .load_store_32,
1732 .LDST64_ABS_LO12_NC => .load_store_64,
1733 .LDST128_ABS_LO12_NC => .load_store_128,
1734 else => unreachable,
1735 };
1736 try aarch64_util.writePageOffset(kind, taddr, code[r_offset..][0..4]);
1737 },
1738
1739 else => try atom.reportUnhandledRelocError(rel, elf_file),
1740 }
1741 }
1742
1743 fn resolveRelocNonAlloc(
1744 atom: Atom,
1745 elf_file: *Elf,
1746 rel: elf.Elf64_Rela,
1747 target: *const Symbol,
1748 args: ResolveArgs,
1749 it: *RelocsIterator,
1750 code: []u8,
1751 stream: anytype,
1752 ) !void {
1753 _ = it;
1754 _ = code;
1755 _ = target;
1756
1757 const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type());
1758 const cwriter = stream.writer();
1759
1760 _, const A, const S, _, _, _, _, _ = args;
1761
1762 switch (r_type) {
1763 .NONE => unreachable,
1764 .ABS32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A)), .little),
1765 .ABS64 => try cwriter.writeInt(i64, S + A, .little),
1766 else => try atom.reportUnhandledRelocError(rel, elf_file),
1767 }
1768 }
1769
1770 const aarch64_util = @import("../aarch64.zig");
1771};
1772
1773const riscv = struct {
1774 fn scanReloc(
1775 atom: Atom,
1776 elf_file: *Elf,
1777 rel: elf.Elf64_Rela,
1778 symbol: *Symbol,
1779 code: ?[]const u8,
1780 it: *RelocsIterator,
1781 ) !void {
1782 _ = code;
1783 _ = it;
1784
1785 const r_type: elf.R_RISCV = @enumFromInt(rel.r_type());
1786
1787 switch (r_type) {
1788 .@"64" => {
1789 try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
1790 },
1791
1792 .HI20 => {
1793 try atom.scanReloc(symbol, rel, absRelocAction(symbol, elf_file), elf_file);
1794 },
1795
1796 .CALL_PLT => if (symbol.flags.import) {
1797 symbol.flags.needs_plt = true;
1798 },
1799
1800 .GOT_HI20 => {
1801 symbol.flags.needs_got = true;
1802 },
1803
1804 .PCREL_HI20,
1805 .PCREL_LO12_I,
1806 .PCREL_LO12_S,
1807 .LO12_I,
1808 .ADD32,
1809 .SUB32,
1810 => {},
1811
1812 else => try atom.reportUnhandledRelocError(rel, elf_file),
1813 }
1814 }
1815
1816 fn resolveRelocAlloc(
1817 atom: Atom,
1818 elf_file: *Elf,
1819 rel: elf.Elf64_Rela,
1820 target: *const Symbol,
1821 args: ResolveArgs,
1822 it: *RelocsIterator,
1823 code: []u8,
1824 stream: anytype,
1825 ) !void {
1826 const r_type: elf.R_RISCV = @enumFromInt(rel.r_type());
1827 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
1828 const cwriter = stream.writer();
1829
1830 const P, const A, const S, const GOT, const G, const TP, const DTP, const ZIG_GOT = args;
1831 _ = TP;
1832 _ = DTP;
1833 _ = ZIG_GOT;
1834
1835 switch (r_type) {
1836 .NONE => unreachable,
1837
1838 .@"64" => {
1839 try atom.resolveDynAbsReloc(
1840 target,
1841 rel,
1842 dynAbsRelocAction(target, elf_file),
1843 elf_file,
1844 cwriter,
1845 );
1846 },
1847
1848 .ADD32 => riscv_util.writeAddend(i32, .add, code[r_offset..][0..4], S + A),
1849 .SUB32 => riscv_util.writeAddend(i32, .sub, code[r_offset..][0..4], S + A),
1850
1851 .HI20 => {
1852 const value: u32 = @bitCast(math.cast(i32, S + A) orelse return error.Overflow);
1853 riscv_util.writeInstU(code[r_offset..][0..4], value);
1854 },
1855
1856 .LO12_I => {
1857 const value: u32 = @bitCast(math.cast(i32, S + A) orelse return error.Overflow);
1858 riscv_util.writeInstI(code[r_offset..][0..4], value);
1859 },
1860
1861 .GOT_HI20 => {
1862 assert(target.flags.has_got);
1863 const disp: u32 = @bitCast(math.cast(i32, G + GOT + A - P) orelse return error.Overflow);
1864 riscv_util.writeInstU(code[r_offset..][0..4], disp);
1865 },
1866
1867 .CALL_PLT => {
1868 // TODO: relax
1869 const disp: u32 = @bitCast(math.cast(i32, S + A - P) orelse return error.Overflow);
1870 riscv_util.writeInstU(code[r_offset..][0..4], disp); // auipc
1871 riscv_util.writeInstI(code[r_offset + 4 ..][0..4], disp); // jalr
1872 },
1873
1874 .PCREL_HI20 => {
1875 const disp: u32 = @bitCast(math.cast(i32, S + A - P) orelse return error.Overflow);
1876 riscv_util.writeInstU(code[r_offset..][0..4], disp);
1877 },
1878
1879 .PCREL_LO12_I,
1880 .PCREL_LO12_S,
1881 => {
1882 assert(A == 0); // according to the spec
1883 // We need to find the paired reloc for this relocation.
1884 const file_ptr = atom.file(elf_file).?;
1885 const atom_addr = atom.address(elf_file);
1886 const pos = it.pos;
1887 const pair = while (it.prev()) |pair| {
1888 if (S == atom_addr + pair.r_offset) break pair;
1889 } else {
1890 // TODO: implement searching forward
1891 var err = try elf_file.addErrorWithNotes(1);
1892 try err.addMsg(elf_file, "TODO: find HI20 paired reloc scanning forward", .{});
1893 try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
1894 atom.file(elf_file).?.fmtPath(),
1895 atom.name(elf_file),
1896 rel.r_offset,
1897 });
1898 return error.RelocFailure;
1899 };
1900 it.pos = pos;
1901 const target_ = switch (file_ptr) {
1902 .zig_object => |x| elf_file.symbol(x.symbol(pair.r_sym())),
1903 .object => |x| elf_file.symbol(x.symbols.items[pair.r_sym()]),
1904 else => unreachable,
1905 };
1906 const S_ = @as(i64, @intCast(target_.address(.{}, elf_file)));
1907 const A_ = pair.r_addend;
1908 const P_ = @as(i64, @intCast(atom_addr + pair.r_offset));
1909 const G_ = @as(i64, @intCast(target_.gotAddress(elf_file))) - GOT;
1910 const disp = switch (@as(elf.R_RISCV, @enumFromInt(pair.r_type()))) {
1911 .PCREL_HI20 => math.cast(i32, S_ + A_ - P_) orelse return error.Overflow,
1912 .GOT_HI20 => math.cast(i32, G_ + GOT + A_ - P_) orelse return error.Overflow,
1913 else => unreachable,
1914 };
1915 relocs_log.debug(" [{x} => {x}]", .{ P_, disp + P_ });
1916 switch (r_type) {
1917 .PCREL_LO12_I => riscv_util.writeInstI(code[r_offset..][0..4], @bitCast(disp)),
1918 .PCREL_LO12_S => riscv_util.writeInstS(code[r_offset..][0..4], @bitCast(disp)),
1919 else => unreachable,
1920 }
1921 },
1922
1923 else => try atom.reportUnhandledRelocError(rel, elf_file),
1924 }
1925 }
1926
1927 fn resolveRelocNonAlloc(
1928 atom: Atom,
1929 elf_file: *Elf,
1930 rel: elf.Elf64_Rela,
1931 target: *const Symbol,
1932 args: ResolveArgs,
1933 it: *RelocsIterator,
1934 code: []u8,
1935 stream: anytype,
1936 ) !void {
1937 _ = target;
1938 _ = it;
1939
1940 const r_type: elf.R_RISCV = @enumFromInt(rel.r_type());
1941 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
1942 const cwriter = stream.writer();
1943
1944 _, const A, const S, const GOT, _, _, const DTP, _ = args;
1945 _ = GOT;
1946 _ = DTP;
1947
1948 switch (r_type) {
1949 .NONE => unreachable,
1950
1951 .@"32" => try cwriter.writeInt(i32, @as(i32, @intCast(S + A)), .little),
1952 .@"64" => try cwriter.writeInt(i64, S + A, .little),
1953
1954 .ADD8 => riscv_util.writeAddend(i8, .add, code[r_offset..][0..1], S + A),
1955 .SUB8 => riscv_util.writeAddend(i8, .sub, code[r_offset..][0..1], S + A),
1956 .ADD16 => riscv_util.writeAddend(i16, .add, code[r_offset..][0..2], S + A),
1957 .SUB16 => riscv_util.writeAddend(i16, .sub, code[r_offset..][0..2], S + A),
1958 .ADD32 => riscv_util.writeAddend(i32, .add, code[r_offset..][0..4], S + A),
1959 .SUB32 => riscv_util.writeAddend(i32, .sub, code[r_offset..][0..4], S + A),
1960 .ADD64 => riscv_util.writeAddend(i64, .add, code[r_offset..][0..8], S + A),
1961 .SUB64 => riscv_util.writeAddend(i64, .sub, code[r_offset..][0..8], S + A),
1962
1963 .SET8 => mem.writeInt(i8, code[r_offset..][0..1], @as(i8, @truncate(S + A)), .little),
1964 .SET16 => mem.writeInt(i16, code[r_offset..][0..2], @as(i16, @truncate(S + A)), .little),
1965 .SET32 => mem.writeInt(i32, code[r_offset..][0..4], @as(i32, @truncate(S + A)), .little),
1966
1967 .SET6 => riscv_util.writeSetSub6(.set, code[r_offset..][0..1], S + A),
1968 .SUB6 => riscv_util.writeSetSub6(.sub, code[r_offset..][0..1], S + A),
1969
1970 else => try atom.reportUnhandledRelocError(rel, elf_file),
1971 }
1972 }
1973
1974 const riscv_util = @import("../riscv.zig");
1975};
1976
1977const ResolveArgs = struct { i64, i64, i64, i64, i64, i64, i64, i64 };
1978
1979const RelocError = error{
1980 Overflow,
1981 OutOfMemory,
1982 NoSpaceLeft,
1983 RelocFailure,
1984 RelaxFailure,
1985 UnsupportedCpuArch,
1986};
1987
1988const RelocsIterator = struct {
1989 relocs: []const elf.Elf64_Rela,
1990 pos: i64 = -1,
1991
1992 fn next(it: *RelocsIterator) ?elf.Elf64_Rela {
1993 it.pos += 1;
1994 if (it.pos >= it.relocs.len) return null;
1995 return it.relocs[@intCast(it.pos)];
1996 }
1997
1998 fn prev(it: *RelocsIterator) ?elf.Elf64_Rela {
1999 if (it.pos == -1) return null;
2000 const rel = it.relocs[@intCast(it.pos)];
2001 it.pos -= 1;
2002 return rel;
2003 }
2004
2005 fn skip(it: *RelocsIterator, num: usize) void {
2006 assert(num > 0);
2007 it.pos += @intCast(num);
2008 }
2009};
2010
14882011const std = @import("std");
14892012const assert = std.debug.assert;
14902013const elf = std.elf;
14912014const eh_frame = @import("eh_frame.zig");
14922015const log = std.log.scoped(.link);
2016const math = std.math;
2017const mem = std.mem;
14932018const relocs_log = std.log.scoped(.link_relocs);
14942019const relocation = @import("relocation.zig");
14952020
1496const Allocator = std.mem.Allocator;
2021const Allocator = mem.Allocator;
14972022const Atom = @This();
14982023const Elf = @import("../Elf.zig");
14992024const Fde = eh_frame.Fde;
src/link/Elf/Object.zig+21-3
......@@ -245,6 +245,9 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file:
245245 atom.rel_index = @intCast(self.relocs.items.len);
246246 atom.rel_num = @intCast(relocs.len);
247247 try self.relocs.appendUnalignedSlice(allocator, relocs);
248 if (elf_file.getTarget().cpu.arch == .riscv64) {
249 sortRelocs(self.relocs.items[atom.rel_index..][0..atom.rel_num]);
250 }
248251 }
249252 },
250253 else => {},
......@@ -333,6 +336,7 @@ fn skipShdr(self: *Object, index: u32, elf_file: *Elf) bool {
333336 if (mem.startsWith(u8, name, ".note")) break :blk true;
334337 if (mem.startsWith(u8, name, ".comment")) break :blk true;
335338 if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true;
339 if (mem.startsWith(u8, name, ".riscv.attributes")) break :blk true; // TODO: riscv attributes
336340 if (comp.config.debug_format == .strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and
337341 mem.startsWith(u8, name, ".debug")) break :blk true;
338342 break :blk false;
......@@ -381,12 +385,15 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:
381385 defer allocator.free(relocs);
382386 const rel_start = @as(u32, @intCast(self.relocs.items.len));
383387 try self.relocs.appendUnalignedSlice(allocator, relocs);
388 if (elf_file.getTarget().cpu.arch == .riscv64) {
389 sortRelocs(self.relocs.items[rel_start..][0..relocs.len]);
390 }
384391 const fdes_start = self.fdes.items.len;
385392 const cies_start = self.cies.items.len;
386393
387394 var it = eh_frame.Iterator{ .data = raw };
388395 while (try it.next()) |rec| {
389 const rel_range = filterRelocs(relocs, rec.offset, rec.size + 4);
396 const rel_range = filterRelocs(self.relocs.items[rel_start..][0..relocs.len], rec.offset, rec.size + 4);
390397 switch (rec.tag) {
391398 .cie => try self.cies.append(allocator, .{
392399 .offset = data_start + rec.offset,
......@@ -449,8 +456,18 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:
449456 }
450457}
451458
459fn sortRelocs(relocs: []elf.Elf64_Rela) void {
460 const sortFn = struct {
461 fn lessThan(c: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
462 _ = c;
463 return lhs.r_offset < rhs.r_offset;
464 }
465 }.lessThan;
466 mem.sort(elf.Elf64_Rela, relocs, {}, sortFn);
467}
468
452469fn filterRelocs(
453 relocs: []align(1) const elf.Elf64_Rela,
470 relocs: []const elf.Elf64_Rela,
454471 start: u64,
455472 len: u64,
456473) struct { start: u64, len: u64 } {
......@@ -832,7 +849,8 @@ pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void {
832849 if (local.atom(elf_file)) |atom| if (!atom.flags.alive) continue;
833850 const esym = local.elfSym(elf_file);
834851 switch (esym.st_type()) {
835 elf.STT_SECTION, elf.STT_NOTYPE => continue,
852 elf.STT_SECTION => continue,
853 elf.STT_NOTYPE => if (esym.st_shndx == elf.SHN_UNDEF) continue,
836854 else => {},
837855 }
838856 local.flags.output_symtab = true;
src/link/Elf/eh_frame.zig+52-5
......@@ -317,7 +317,9 @@ fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file:
317317 });
318318
319319 switch (cpu_arch) {
320 .x86_64 => x86_64.resolveReloc(rel, P, S + A, contents[offset..]),
320 .x86_64 => try x86_64.resolveReloc(rec, elf_file, rel, P, S + A, contents[offset..]),
321 .aarch64 => try aarch64.resolveReloc(rec, elf_file, rel, P, S + A, contents[offset..]),
322 .riscv64 => try riscv.resolveReloc(rec, elf_file, rel, P, S + A, contents[offset..]),
321323 else => return error.UnsupportedCpuArch,
322324 }
323325}
......@@ -325,6 +327,8 @@ fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file:
325327pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
326328 relocs_log.debug("{x}: .eh_frame", .{elf_file.shdrs.items[elf_file.eh_frame_section_index.?].sh_addr});
327329
330 var has_reloc_errors = false;
331
328332 for (elf_file.objects.items) |index| {
329333 const object = elf_file.file(index).?.object;
330334
......@@ -335,7 +339,10 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
335339
336340 for (cie.relocs(elf_file)) |rel| {
337341 const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
338 try resolveReloc(cie, sym, rel, elf_file, contents);
342 resolveReloc(cie, sym, rel, elf_file, contents) catch |err| switch (err) {
343 error.RelocFailure => has_reloc_errors = true,
344 else => |e| return e,
345 };
339346 }
340347
341348 try writer.writeAll(contents);
......@@ -359,7 +366,10 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
359366
360367 for (fde.relocs(elf_file)) |rel| {
361368 const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
362 try resolveReloc(fde, sym, rel, elf_file, contents);
369 resolveReloc(fde, sym, rel, elf_file, contents) catch |err| switch (err) {
370 error.RelocFailure => has_reloc_errors = true,
371 else => |e| return e,
372 };
363373 }
364374
365375 try writer.writeAll(contents);
......@@ -367,6 +377,8 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
367377 }
368378
369379 try writer.writeInt(u32, 0, .little);
380
381 if (has_reloc_errors) return error.RelocFailure;
370382}
371383
372384pub fn writeEhFrameObject(elf_file: *Elf, writer: anytype) !void {
......@@ -540,18 +552,53 @@ const EH_PE = struct {
540552};
541553
542554const x86_64 = struct {
543 fn resolveReloc(rel: elf.Elf64_Rela, source: i64, target: i64, data: []u8) void {
555 fn resolveReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela, source: i64, target: i64, data: []u8) !void {
544556 const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());
545557 switch (r_type) {
558 .NONE => {},
546559 .@"32" => std.mem.writeInt(i32, data[0..4], @as(i32, @truncate(target)), .little),
547560 .@"64" => std.mem.writeInt(i64, data[0..8], target, .little),
548561 .PC32 => std.mem.writeInt(i32, data[0..4], @as(i32, @intCast(target - source)), .little),
549562 .PC64 => std.mem.writeInt(i64, data[0..8], target - source, .little),
550 else => unreachable,
563 else => try reportInvalidReloc(rec, elf_file, rel),
564 }
565 }
566};
567
568const aarch64 = struct {
569 fn resolveReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela, source: i64, target: i64, data: []u8) !void {
570 const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type());
571 switch (r_type) {
572 .NONE => {},
573 .ABS64 => std.mem.writeInt(i64, data[0..8], target, .little),
574 .PREL32 => std.mem.writeInt(i32, data[0..4], @as(i32, @intCast(target - source)), .little),
575 .PREL64 => std.mem.writeInt(i64, data[0..8], target - source, .little),
576 else => try reportInvalidReloc(rec, elf_file, rel),
551577 }
552578 }
553579};
554580
581const riscv = struct {
582 fn resolveReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela, source: i64, target: i64, data: []u8) !void {
583 const r_type: elf.R_RISCV = @enumFromInt(rel.r_type());
584 switch (r_type) {
585 .NONE => {},
586 .@"32_PCREL" => std.mem.writeInt(i32, data[0..4], @as(i32, @intCast(target - source)), .little),
587 else => try reportInvalidReloc(rec, elf_file, rel),
588 }
589 }
590};
591
592fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void {
593 var err = try elf_file.addErrorWithNotes(1);
594 try err.addMsg(elf_file, "invalid relocation type {} at offset 0x{x}", .{
595 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
596 rel.r_offset,
597 });
598 try err.addNote(elf_file, "in {}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()});
599 return error.RelocFailure;
600}
601
555602const std = @import("std");
556603const assert = std.debug.assert;
557604const elf = std.elf;
src/link/Elf/relocation.zig+11-6
......@@ -1,4 +1,6 @@
11pub const Kind = enum {
2 none,
3 other,
24 abs,
35 copy,
46 rel,
......@@ -13,23 +15,24 @@ pub const Kind = enum {
1315
1416fn Table(comptime len: comptime_int, comptime RelType: type, comptime mapping: [len]struct { Kind, RelType }) type {
1517 return struct {
16 fn decode(r_type: u32) ?Kind {
18 fn decode(r_type: u32) Kind {
1719 inline for (mapping) |entry| {
1820 if (@intFromEnum(entry[1]) == r_type) return entry[0];
1921 }
20 return null;
22 return .other;
2123 }
2224
2325 fn encode(comptime kind: Kind) u32 {
2426 inline for (mapping) |entry| {
2527 if (entry[0] == kind) return @intFromEnum(entry[1]);
2628 }
27 unreachable;
29 @panic("encoding .other is ambiguous");
2830 }
2931 };
3032}
3133
32const x86_64_relocs = Table(10, elf.R_X86_64, .{
34const x86_64_relocs = Table(11, elf.R_X86_64, .{
35 .{ .none, .NONE },
3336 .{ .abs, .@"64" },
3437 .{ .copy, .COPY },
3538 .{ .rel, .RELATIVE },
......@@ -42,7 +45,8 @@ const x86_64_relocs = Table(10, elf.R_X86_64, .{
4245 .{ .tlsdesc, .TLSDESC },
4346});
4447
45const aarch64_relocs = Table(10, elf.R_AARCH64, .{
48const aarch64_relocs = Table(11, elf.R_AARCH64, .{
49 .{ .none, .NONE },
4650 .{ .abs, .ABS64 },
4751 .{ .copy, .COPY },
4852 .{ .rel, .RELATIVE },
......@@ -55,7 +59,8 @@ const aarch64_relocs = Table(10, elf.R_AARCH64, .{
5559 .{ .tlsdesc, .TLSDESC },
5660});
5761
58const riscv64_relocs = Table(10, elf.R_RISCV, .{
62const riscv64_relocs = Table(11, elf.R_RISCV, .{
63 .{ .none, .NONE },
5964 .{ .abs, .@"64" },
6065 .{ .copy, .COPY },
6166 .{ .rel, .RELATIVE },
src/link/MachO/Atom.zig+10-60
......@@ -699,14 +699,7 @@ fn resolveRelocInner(
699699 const S_: i64 = @intCast(thunk.getTargetAddress(rel.target, macho_file));
700700 break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow;
701701 };
702 var inst = aarch64.Instruction{
703 .unconditional_branch_immediate = mem.bytesToValue(std.meta.TagPayload(
704 aarch64.Instruction,
705 aarch64.Instruction.unconditional_branch_immediate,
706 ), code[rel_offset..][0..4]),
707 };
708 inst.unconditional_branch_immediate.imm26 = @as(u26, @truncate(@as(u28, @bitCast(disp >> 2))));
709 try writer.writeInt(u32, inst.toU32(), .little);
702 try aarch64.writeBranchImm(disp, code[rel_offset..][0..4]);
710703 },
711704 else => unreachable,
712705 }
......@@ -776,16 +769,8 @@ fn resolveRelocInner(
776769 };
777770 break :target math.cast(u64, target) orelse return error.Overflow;
778771 };
779 const pages = @as(u21, @bitCast(try Relocation.calcNumberOfPages(source, target)));
780 var inst = aarch64.Instruction{
781 .pc_relative_address = mem.bytesToValue(std.meta.TagPayload(
782 aarch64.Instruction,
783 aarch64.Instruction.pc_relative_address,
784 ), code[rel_offset..][0..4]),
785 };
786 inst.pc_relative_address.immhi = @as(u19, @truncate(pages >> 2));
787 inst.pc_relative_address.immlo = @as(u2, @truncate(pages));
788 try writer.writeInt(u32, inst.toU32(), .little);
772 const pages = @as(u21, @bitCast(try aarch64.calcNumberOfPages(source, target)));
773 try aarch64.writePages(pages, code[rel_offset..][0..4]);
789774 },
790775
791776 .pageoff => {
......@@ -794,35 +779,8 @@ fn resolveRelocInner(
794779 assert(!rel.meta.pcrel);
795780 const target = math.cast(u64, S + A) orelse return error.Overflow;
796781 const inst_code = code[rel_offset..][0..4];
797 if (Relocation.isArithmeticOp(inst_code)) {
798 const off = try Relocation.calcPageOffset(target, .arithmetic);
799 var inst = aarch64.Instruction{
800 .add_subtract_immediate = mem.bytesToValue(std.meta.TagPayload(
801 aarch64.Instruction,
802 aarch64.Instruction.add_subtract_immediate,
803 ), inst_code),
804 };
805 inst.add_subtract_immediate.imm12 = off;
806 try writer.writeInt(u32, inst.toU32(), .little);
807 } else {
808 var inst = aarch64.Instruction{
809 .load_store_register = mem.bytesToValue(std.meta.TagPayload(
810 aarch64.Instruction,
811 aarch64.Instruction.load_store_register,
812 ), inst_code),
813 };
814 const off = try Relocation.calcPageOffset(target, switch (inst.load_store_register.size) {
815 0 => if (inst.load_store_register.v == 1)
816 Relocation.PageOffsetInstKind.load_store_128
817 else
818 Relocation.PageOffsetInstKind.load_store_8,
819 1 => .load_store_16,
820 2 => .load_store_32,
821 3 => .load_store_64,
822 });
823 inst.load_store_register.offset = off;
824 try writer.writeInt(u32, inst.toU32(), .little);
825 }
782 const kind = aarch64.classifyInst(inst_code);
783 try aarch64.writePageOffset(kind, target, inst_code);
826784 },
827785
828786 .got_load_pageoff => {
......@@ -830,15 +788,7 @@ fn resolveRelocInner(
830788 assert(rel.meta.length == 2);
831789 assert(!rel.meta.pcrel);
832790 const target = math.cast(u64, G + A) orelse return error.Overflow;
833 const off = try Relocation.calcPageOffset(target, .load_store_64);
834 var inst: aarch64.Instruction = .{
835 .load_store_register = mem.bytesToValue(std.meta.TagPayload(
836 aarch64.Instruction,
837 aarch64.Instruction.load_store_register,
838 ), code[rel_offset..][0..4]),
839 };
840 inst.load_store_register.offset = off;
841 try writer.writeInt(u32, inst.toU32(), .little);
791 try aarch64.writePageOffset(.load_store_64, target, code[rel_offset..][0..4]);
842792 },
843793
844794 .tlvp_pageoff => {
......@@ -863,7 +813,7 @@ fn resolveRelocInner(
863813
864814 const inst_code = code[rel_offset..][0..4];
865815 const reg_info: RegInfo = blk: {
866 if (Relocation.isArithmeticOp(inst_code)) {
816 if (aarch64.isArithmeticOp(inst_code)) {
867817 const inst = mem.bytesToValue(std.meta.TagPayload(
868818 aarch64.Instruction,
869819 aarch64.Instruction.add_subtract_immediate,
......@@ -890,7 +840,7 @@ fn resolveRelocInner(
890840 .load_store_register = .{
891841 .rt = reg_info.rd,
892842 .rn = reg_info.rn,
893 .offset = try Relocation.calcPageOffset(target, .load_store_64),
843 .offset = try aarch64.calcPageOffset(.load_store_64, target),
894844 .opc = 0b01,
895845 .op1 = 0b01,
896846 .v = 0,
......@@ -900,7 +850,7 @@ fn resolveRelocInner(
900850 .add_subtract_immediate = .{
901851 .rd = reg_info.rd,
902852 .rn = reg_info.rn,
903 .imm12 = try Relocation.calcPageOffset(target, .arithmetic),
853 .imm12 = try aarch64.calcPageOffset(.arithmetic, target),
904854 .sh = 0,
905855 .s = 0,
906856 .op = 0,
......@@ -1183,7 +1133,7 @@ pub const Loc = struct {
11831133
11841134pub const Alignment = @import("../../InternPool.zig").Alignment;
11851135
1186const aarch64 = @import("../../arch/aarch64/bits.zig");
1136const aarch64 = @import("../aarch64.zig");
11871137const assert = std.debug.assert;
11881138const bind = @import("dyld_info/bind.zig");
11891139const macho = std.macho;
src/link/MachO/Relocation.zig-32
......@@ -60,38 +60,6 @@ pub fn lessThan(ctx: void, lhs: Relocation, rhs: Relocation) bool {
6060 return lhs.offset < rhs.offset;
6161}
6262
63pub fn calcNumberOfPages(saddr: u64, taddr: u64) error{Overflow}!i21 {
64 const spage = math.cast(i32, saddr >> 12) orelse return error.Overflow;
65 const tpage = math.cast(i32, taddr >> 12) orelse return error.Overflow;
66 const pages = math.cast(i21, tpage - spage) orelse return error.Overflow;
67 return pages;
68}
69
70pub const PageOffsetInstKind = enum {
71 arithmetic,
72 load_store_8,
73 load_store_16,
74 load_store_32,
75 load_store_64,
76 load_store_128,
77};
78
79pub fn calcPageOffset(taddr: u64, kind: PageOffsetInstKind) !u12 {
80 const narrowed = @as(u12, @truncate(taddr));
81 return switch (kind) {
82 .arithmetic, .load_store_8 => narrowed,
83 .load_store_16 => try math.divExact(u12, narrowed, 2),
84 .load_store_32 => try math.divExact(u12, narrowed, 4),
85 .load_store_64 => try math.divExact(u12, narrowed, 8),
86 .load_store_128 => try math.divExact(u12, narrowed, 16),
87 };
88}
89
90pub inline fn isArithmeticOp(inst: *const [4]u8) bool {
91 const group_decode = @as(u5, @truncate(inst[3]));
92 return ((group_decode >> 2) == 4);
93}
94
9563pub const Type = enum {
9664 // x86_64
9765 /// RIP-relative displacement (X86_64_RELOC_SIGNED)
src/link/MachO/synthetic.zig+11-12
......@@ -267,9 +267,9 @@ pub const StubsSection = struct {
267267 },
268268 .aarch64 => {
269269 // TODO relax if possible
270 const pages = try Relocation.calcNumberOfPages(source, target);
270 const pages = try aarch64.calcNumberOfPages(source, target);
271271 try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);
272 const off = try Relocation.calcPageOffset(target, .load_store_64);
272 const off = try aarch64.calcPageOffset(.load_store_64, target);
273273 try writer.writeInt(
274274 u32,
275275 aarch64.Instruction.ldr(.x16, .x16, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(),
......@@ -411,9 +411,9 @@ pub const StubsHelperSection = struct {
411411 .aarch64 => {
412412 {
413413 // TODO relax if possible
414 const pages = try Relocation.calcNumberOfPages(sect.addr, dyld_private_addr);
414 const pages = try aarch64.calcNumberOfPages(sect.addr, dyld_private_addr);
415415 try writer.writeInt(u32, aarch64.Instruction.adrp(.x17, pages).toU32(), .little);
416 const off = try Relocation.calcPageOffset(dyld_private_addr, .arithmetic);
416 const off = try aarch64.calcPageOffset(.arithmetic, dyld_private_addr);
417417 try writer.writeInt(u32, aarch64.Instruction.add(.x17, .x17, off, false).toU32(), .little);
418418 }
419419 try writer.writeInt(u32, aarch64.Instruction.stp(
......@@ -424,9 +424,9 @@ pub const StubsHelperSection = struct {
424424 ).toU32(), .little);
425425 {
426426 // TODO relax if possible
427 const pages = try Relocation.calcNumberOfPages(sect.addr + 12, dyld_stub_binder_addr);
427 const pages = try aarch64.calcNumberOfPages(sect.addr + 12, dyld_stub_binder_addr);
428428 try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);
429 const off = try Relocation.calcPageOffset(dyld_stub_binder_addr, .load_store_64);
429 const off = try aarch64.calcPageOffset(.load_store_64, dyld_stub_binder_addr);
430430 try writer.writeInt(u32, aarch64.Instruction.ldr(
431431 .x16,
432432 .x16,
......@@ -679,9 +679,9 @@ pub const ObjcStubsSection = struct {
679679 {
680680 const target = sym.getObjcSelrefsAddress(macho_file);
681681 const source = addr;
682 const pages = try Relocation.calcNumberOfPages(source, target);
682 const pages = try aarch64.calcNumberOfPages(source, target);
683683 try writer.writeInt(u32, aarch64.Instruction.adrp(.x1, pages).toU32(), .little);
684 const off = try Relocation.calcPageOffset(target, .load_store_64);
684 const off = try aarch64.calcPageOffset(.load_store_64, target);
685685 try writer.writeInt(
686686 u32,
687687 aarch64.Instruction.ldr(.x1, .x1, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(),
......@@ -692,9 +692,9 @@ pub const ObjcStubsSection = struct {
692692 const target_sym = macho_file.getSymbol(macho_file.objc_msg_send_index.?);
693693 const target = target_sym.getGotAddress(macho_file);
694694 const source = addr + 2 * @sizeOf(u32);
695 const pages = try Relocation.calcNumberOfPages(source, target);
695 const pages = try aarch64.calcNumberOfPages(source, target);
696696 try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);
697 const off = try Relocation.calcPageOffset(target, .load_store_64);
697 const off = try aarch64.calcPageOffset(.load_store_64, target);
698698 try writer.writeInt(
699699 u32,
700700 aarch64.Instruction.ldr(.x16, .x16, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(),
......@@ -778,7 +778,7 @@ pub const WeakBindSection = bind.WeakBind;
778778pub const LazyBindSection = bind.LazyBind;
779779pub const ExportTrieSection = Trie;
780780
781const aarch64 = @import("../../arch/aarch64/bits.zig");
781const aarch64 = @import("../aarch64.zig");
782782const assert = std.debug.assert;
783783const bind = @import("dyld_info/bind.zig");
784784const math = std.math;
......@@ -788,6 +788,5 @@ const trace = @import("../../tracy.zig").trace;
788788const Allocator = std.mem.Allocator;
789789const MachO = @import("../MachO.zig");
790790const Rebase = @import("dyld_info/Rebase.zig");
791const Relocation = @import("Relocation.zig");
792791const Symbol = @import("Symbol.zig");
793792const Trie = @import("dyld_info/Trie.zig");
src/link/MachO/thunks.zig+3-3
......@@ -99,9 +99,9 @@ pub const Thunk = struct {
9999 const sym = macho_file.getSymbol(sym_index);
100100 const saddr = thunk.getAddress(macho_file) + i * trampoline_size;
101101 const taddr = sym.getAddress(.{}, macho_file);
102 const pages = try Relocation.calcNumberOfPages(saddr, taddr);
102 const pages = try aarch64.calcNumberOfPages(saddr, taddr);
103103 try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);
104 const off = try Relocation.calcPageOffset(taddr, .arithmetic);
104 const off = try aarch64.calcPageOffset(.arithmetic, taddr);
105105 try writer.writeInt(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32(), .little);
106106 try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little);
107107 }
......@@ -164,7 +164,7 @@ const max_distance = (1 << (jump_bits - 1));
164164/// and assume margin to be 5MiB.
165165const max_allowed_distance = max_distance - 0x500_000;
166166
167const aarch64 = @import("../../arch/aarch64/bits.zig");
167const aarch64 = @import("../aarch64.zig");
168168const assert = std.debug.assert;
169169const log = std.log.scoped(.link);
170170const macho = std.macho;
src/link/aarch64.zig created+106
......@@ -0,0 +1,106 @@
1pub inline fn isArithmeticOp(inst: *const [4]u8) bool {
2 const group_decode = @as(u5, @truncate(inst[3]));
3 return ((group_decode >> 2) == 4);
4}
5
6pub const PageOffsetInstKind = enum {
7 arithmetic,
8 load_store_8,
9 load_store_16,
10 load_store_32,
11 load_store_64,
12 load_store_128,
13};
14
15pub fn classifyInst(code: *const [4]u8) PageOffsetInstKind {
16 if (isArithmeticOp(code)) return .arithmetic;
17 const inst = Instruction{
18 .load_store_register = mem.bytesToValue(std.meta.TagPayload(
19 Instruction,
20 Instruction.load_store_register,
21 ), code),
22 };
23 return switch (inst.load_store_register.size) {
24 0 => if (inst.load_store_register.v == 1) .load_store_128 else .load_store_8,
25 1 => .load_store_16,
26 2 => .load_store_32,
27 3 => .load_store_64,
28 };
29}
30
31pub fn calcPageOffset(kind: PageOffsetInstKind, taddr: u64) !u12 {
32 const narrowed = @as(u12, @truncate(taddr));
33 return switch (kind) {
34 .arithmetic, .load_store_8 => narrowed,
35 .load_store_16 => try math.divExact(u12, narrowed, 2),
36 .load_store_32 => try math.divExact(u12, narrowed, 4),
37 .load_store_64 => try math.divExact(u12, narrowed, 8),
38 .load_store_128 => try math.divExact(u12, narrowed, 16),
39 };
40}
41
42pub fn writePageOffset(kind: PageOffsetInstKind, taddr: u64, code: *[4]u8) !void {
43 const value = try calcPageOffset(kind, taddr);
44 switch (kind) {
45 .arithmetic => {
46 var inst = Instruction{
47 .add_subtract_immediate = mem.bytesToValue(std.meta.TagPayload(
48 Instruction,
49 Instruction.add_subtract_immediate,
50 ), code),
51 };
52 inst.add_subtract_immediate.imm12 = value;
53 mem.writeInt(u32, code, inst.toU32(), .little);
54 },
55 else => {
56 var inst: Instruction = .{
57 .load_store_register = mem.bytesToValue(std.meta.TagPayload(
58 Instruction,
59 Instruction.load_store_register,
60 ), code),
61 };
62 inst.load_store_register.offset = value;
63 mem.writeInt(u32, code, inst.toU32(), .little);
64 },
65 }
66}
67
68pub fn calcNumberOfPages(saddr: u64, taddr: u64) error{Overflow}!i21 {
69 const spage = math.cast(i32, saddr >> 12) orelse return error.Overflow;
70 const tpage = math.cast(i32, taddr >> 12) orelse return error.Overflow;
71 const pages = math.cast(i21, tpage - spage) orelse return error.Overflow;
72 return pages;
73}
74
75pub fn writePages(pages: u21, code: *[4]u8) !void {
76 var inst = Instruction{
77 .pc_relative_address = mem.bytesToValue(std.meta.TagPayload(
78 Instruction,
79 Instruction.pc_relative_address,
80 ), code),
81 };
82 inst.pc_relative_address.immhi = @as(u19, @truncate(pages >> 2));
83 inst.pc_relative_address.immlo = @as(u2, @truncate(pages));
84 mem.writeInt(u32, code, inst.toU32(), .little);
85}
86
87pub fn writeBranchImm(disp: i28, code: *[4]u8) !void {
88 var inst = Instruction{
89 .unconditional_branch_immediate = mem.bytesToValue(std.meta.TagPayload(
90 Instruction,
91 Instruction.unconditional_branch_immediate,
92 ), code),
93 };
94 inst.unconditional_branch_immediate.imm26 = @as(u26, @truncate(@as(u28, @bitCast(disp >> 2))));
95 mem.writeInt(u32, code, inst.toU32(), .little);
96}
97
98const assert = std.debug.assert;
99const bits = @import("../arch/aarch64/bits.zig");
100const builtin = @import("builtin");
101const math = std.math;
102const mem = std.mem;
103const std = @import("std");
104
105pub const Instruction = bits.Instruction;
106pub const Register = bits.Register;
src/link/riscv.zig created+74
......@@ -0,0 +1,74 @@
1pub fn writeSetSub6(comptime op: enum { set, sub }, code: *[1]u8, addend: anytype) void {
2 const mask: u8 = 0b11_000000;
3 const actual: i8 = @truncate(addend);
4 var value: u8 = mem.readInt(u8, code, .little);
5 switch (op) {
6 .set => value = (value & mask) | @as(u8, @bitCast(actual & ~mask)),
7 .sub => value = (value & mask) | (@as(u8, @bitCast(@as(i8, @bitCast(value)) -| actual)) & ~mask),
8 }
9 mem.writeInt(u8, code, value, .little);
10}
11
12pub fn writeAddend(
13 comptime Int: type,
14 comptime op: enum { add, sub },
15 code: *[@typeInfo(Int).Int.bits / 8]u8,
16 value: anytype,
17) void {
18 var V: Int = mem.readInt(Int, code, .little);
19 const addend: Int = @truncate(value);
20 switch (op) {
21 .add => V +|= addend, // TODO: I think saturating arithmetic is correct here
22 .sub => V -|= addend,
23 }
24 mem.writeInt(Int, code, V, .little);
25}
26
27pub fn writeInstU(code: *[4]u8, value: u32) void {
28 var inst = Instruction{
29 .U = mem.bytesToValue(std.meta.TagPayload(
30 Instruction,
31 Instruction.U,
32 ), code),
33 };
34 const compensated: u32 = @bitCast(@as(i32, @bitCast(value)) + 0x800);
35 inst.U.imm12_31 = bitSlice(compensated, 31, 12);
36 mem.writeInt(u32, code, inst.toU32(), .little);
37}
38
39pub fn writeInstI(code: *[4]u8, value: u32) void {
40 var inst = Instruction{
41 .I = mem.bytesToValue(std.meta.TagPayload(
42 Instruction,
43 Instruction.I,
44 ), code),
45 };
46 inst.I.imm0_11 = bitSlice(value, 11, 0);
47 mem.writeInt(u32, code, inst.toU32(), .little);
48}
49
50pub fn writeInstS(code: *[4]u8, value: u32) void {
51 var inst = Instruction{
52 .S = mem.bytesToValue(std.meta.TagPayload(
53 Instruction,
54 Instruction.S,
55 ), code),
56 };
57 inst.S.imm0_4 = bitSlice(value, 4, 0);
58 inst.S.imm5_11 = bitSlice(value, 11, 5);
59 mem.writeInt(u32, code, inst.toU32(), .little);
60}
61
62fn bitSlice(
63 value: anytype,
64 comptime high: comptime_int,
65 comptime low: comptime_int,
66) std.math.IntFittingRange(0, 1 << high - low) {
67 return @truncate((value >> low) & (1 << (high - low + 1)) - 1);
68}
69
70const bits = @import("../arch/riscv64/bits.zig");
71const mem = std.mem;
72const std = @import("std");
73
74pub const Instruction = bits.Instruction;
test/link/elf.zig+102-85
......@@ -10,124 +10,141 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
1010 .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs
1111 .os_tag = .linux,
1212 });
13 const musl_target = b.resolveTargetQuery(.{
13 const x86_64_musl = b.resolveTargetQuery(.{
1414 .cpu_arch = .x86_64,
1515 .os_tag = .linux,
1616 .abi = .musl,
1717 });
18 const glibc_target = b.resolveTargetQuery(.{
18 const x86_64_gnu = b.resolveTargetQuery(.{
1919 .cpu_arch = .x86_64,
2020 .os_tag = .linux,
2121 .abi = .gnu,
2222 });
23 const aarch64_musl = b.resolveTargetQuery(.{
24 .cpu_arch = .aarch64,
25 .os_tag = .linux,
26 .abi = .musl,
27 });
28 const riscv64_musl = b.resolveTargetQuery(.{
29 .cpu_arch = .riscv64,
30 .os_tag = .linux,
31 .abi = .musl,
32 });
2333
34 // x86_64 tests
2435 // Exercise linker in -r mode
25 elf_step.dependOn(testEmitRelocatable(b, .{ .use_llvm = false, .target = musl_target }));
26 elf_step.dependOn(testEmitRelocatable(b, .{ .target = musl_target }));
27 elf_step.dependOn(testRelocatableArchive(b, .{ .target = musl_target }));
28 elf_step.dependOn(testRelocatableEhFrame(b, .{ .target = musl_target }));
29 elf_step.dependOn(testRelocatableNoEhFrame(b, .{ .target = musl_target }));
36 elf_step.dependOn(testEmitRelocatable(b, .{ .use_llvm = false, .target = x86_64_musl }));
37 elf_step.dependOn(testEmitRelocatable(b, .{ .target = x86_64_musl }));
38 elf_step.dependOn(testRelocatableArchive(b, .{ .target = x86_64_musl }));
39 elf_step.dependOn(testRelocatableEhFrame(b, .{ .target = x86_64_musl }));
40 elf_step.dependOn(testRelocatableNoEhFrame(b, .{ .target = x86_64_musl }));
3041
3142 // Exercise linker in ar mode
32 elf_step.dependOn(testEmitStaticLib(b, .{ .target = musl_target }));
33 elf_step.dependOn(testEmitStaticLibZig(b, .{ .use_llvm = false, .target = musl_target }));
43 elf_step.dependOn(testEmitStaticLib(b, .{ .target = x86_64_musl }));
44 elf_step.dependOn(testEmitStaticLibZig(b, .{ .use_llvm = false, .target = x86_64_musl }));
3445
3546 // Exercise linker with self-hosted backend (no LLVM)
3647 elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target }));
3748 elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target }));
3849 elf_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = default_target }));
3950 elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target }));
40 elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = glibc_target }));
41 elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = musl_target }));
51 elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = x86_64_gnu }));
52 elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = x86_64_musl }));
4253
4354 // Exercise linker with LLVM backend
4455 // musl tests
45 elf_step.dependOn(testAbsSymbols(b, .{ .target = musl_target }));
46 elf_step.dependOn(testCommonSymbols(b, .{ .target = musl_target }));
47 elf_step.dependOn(testCommonSymbolsInArchive(b, .{ .target = musl_target }));
48 elf_step.dependOn(testEmptyObject(b, .{ .target = musl_target }));
49 elf_step.dependOn(testEntryPoint(b, .{ .target = musl_target }));
50 elf_step.dependOn(testGcSections(b, .{ .target = musl_target }));
51 elf_step.dependOn(testImageBase(b, .{ .target = musl_target }));
52 elf_step.dependOn(testInitArrayOrder(b, .{ .target = musl_target }));
53 elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = musl_target }));
56 elf_step.dependOn(testAbsSymbols(b, .{ .target = x86_64_musl }));
57 elf_step.dependOn(testCommonSymbols(b, .{ .target = x86_64_musl }));
58 elf_step.dependOn(testCommonSymbolsInArchive(b, .{ .target = x86_64_musl }));
59 elf_step.dependOn(testEmptyObject(b, .{ .target = x86_64_musl }));
60 elf_step.dependOn(testEntryPoint(b, .{ .target = x86_64_musl }));
61 elf_step.dependOn(testGcSections(b, .{ .target = x86_64_musl }));
62 elf_step.dependOn(testImageBase(b, .{ .target = x86_64_musl }));
63 elf_step.dependOn(testInitArrayOrder(b, .{ .target = x86_64_musl }));
64 elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = x86_64_musl }));
5465 // https://github.com/ziglang/zig/issues/17449
55 // elf_step.dependOn(testLargeBss(b, .{ .target = musl_target }));
56 elf_step.dependOn(testLinkingC(b, .{ .target = musl_target }));
57 elf_step.dependOn(testLinkingCpp(b, .{ .target = musl_target }));
58 elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target }));
66 // elf_step.dependOn(testLargeBss(b, .{ .target = x86_64_musl }));
67 elf_step.dependOn(testLinkingC(b, .{ .target = x86_64_musl }));
68 elf_step.dependOn(testLinkingCpp(b, .{ .target = x86_64_musl }));
69 elf_step.dependOn(testLinkingZig(b, .{ .target = x86_64_musl }));
5970 // https://github.com/ziglang/zig/issues/17451
60 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = musl_target }));
61 elf_step.dependOn(testTlsStatic(b, .{ .target = musl_target }));
62 elf_step.dependOn(testStrip(b, .{ .target = musl_target }));
71 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = x86_64_musl }));
72 elf_step.dependOn(testTlsStatic(b, .{ .target = x86_64_musl }));
73 elf_step.dependOn(testStrip(b, .{ .target = x86_64_musl }));
6374
6475 // glibc tests
65 elf_step.dependOn(testAsNeeded(b, .{ .target = glibc_target }));
76 elf_step.dependOn(testAsNeeded(b, .{ .target = x86_64_gnu }));
6677 // https://github.com/ziglang/zig/issues/17430
67 // elf_step.dependOn(testCanonicalPlt(b, .{ .target = glibc_target }));
68 elf_step.dependOn(testCopyrel(b, .{ .target = glibc_target }));
78 // elf_step.dependOn(testCanonicalPlt(b, .{ .target = x86_64_gnu }));
79 elf_step.dependOn(testCopyrel(b, .{ .target = x86_64_gnu }));
6980 // https://github.com/ziglang/zig/issues/17430
70 // elf_step.dependOn(testCopyrelAlias(b, .{ .target = glibc_target }));
81 // elf_step.dependOn(testCopyrelAlias(b, .{ .target = x86_64_gnu }));
7182 // https://github.com/ziglang/zig/issues/17430
72 // elf_step.dependOn(testCopyrelAlignment(b, .{ .target = glibc_target }));
73 elf_step.dependOn(testDsoPlt(b, .{ .target = glibc_target }));
74 elf_step.dependOn(testDsoUndef(b, .{ .target = glibc_target }));
75 elf_step.dependOn(testExportDynamic(b, .{ .target = glibc_target }));
76 elf_step.dependOn(testExportSymbolsFromExe(b, .{ .target = glibc_target }));
83 // elf_step.dependOn(testCopyrelAlignment(b, .{ .target = x86_64_gnu }));
84 elf_step.dependOn(testDsoPlt(b, .{ .target = x86_64_gnu }));
85 elf_step.dependOn(testDsoUndef(b, .{ .target = x86_64_gnu }));
86 elf_step.dependOn(testExportDynamic(b, .{ .target = x86_64_gnu }));
87 elf_step.dependOn(testExportSymbolsFromExe(b, .{ .target = x86_64_gnu }));
7788 // https://github.com/ziglang/zig/issues/17430
78 // elf_step.dependOn(testFuncAddress(b, .{ .target = glibc_target }));
79 elf_step.dependOn(testHiddenWeakUndef(b, .{ .target = glibc_target }));
80 elf_step.dependOn(testIFuncAlias(b, .{ .target = glibc_target }));
89 // elf_step.dependOn(testFuncAddress(b, .{ .target = x86_64_gnu }));
90 elf_step.dependOn(testHiddenWeakUndef(b, .{ .target = x86_64_gnu }));
91 elf_step.dependOn(testIFuncAlias(b, .{ .target = x86_64_gnu }));
8192 // https://github.com/ziglang/zig/issues/17430
82 // elf_step.dependOn(testIFuncDlopen(b, .{ .target = glibc_target }));
83 elf_step.dependOn(testIFuncDso(b, .{ .target = glibc_target }));
84 elf_step.dependOn(testIFuncDynamic(b, .{ .target = glibc_target }));
85 elf_step.dependOn(testIFuncExport(b, .{ .target = glibc_target }));
86 elf_step.dependOn(testIFuncFuncPtr(b, .{ .target = glibc_target }));
87 elf_step.dependOn(testIFuncNoPlt(b, .{ .target = glibc_target }));
93 // elf_step.dependOn(testIFuncDlopen(b, .{ .target = x86_64_gnu }));
94 elf_step.dependOn(testIFuncDso(b, .{ .target = x86_64_gnu }));
95 elf_step.dependOn(testIFuncDynamic(b, .{ .target = x86_64_gnu }));
96 elf_step.dependOn(testIFuncExport(b, .{ .target = x86_64_gnu }));
97 elf_step.dependOn(testIFuncFuncPtr(b, .{ .target = x86_64_gnu }));
98 elf_step.dependOn(testIFuncNoPlt(b, .{ .target = x86_64_gnu }));
8899 // https://github.com/ziglang/zig/issues/17430 ??
89 // elf_step.dependOn(testIFuncStatic(b, .{ .target = glibc_target }));
90 // elf_step.dependOn(testIFuncStaticPie(b, .{ .target = glibc_target }));
91 elf_step.dependOn(testInitArrayOrder(b, .{ .target = glibc_target }));
92 elf_step.dependOn(testLargeAlignmentDso(b, .{ .target = glibc_target }));
93 elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = glibc_target }));
94 elf_step.dependOn(testLargeBss(b, .{ .target = glibc_target }));
95 elf_step.dependOn(testLinkOrder(b, .{ .target = glibc_target }));
96 elf_step.dependOn(testLdScript(b, .{ .target = glibc_target }));
97 elf_step.dependOn(testLdScriptPathError(b, .{ .target = glibc_target }));
98 elf_step.dependOn(testLdScriptAllowUndefinedVersion(b, .{ .target = glibc_target, .use_lld = true }));
99 elf_step.dependOn(testLdScriptDisallowUndefinedVersion(b, .{ .target = glibc_target, .use_lld = true }));
100 elf_step.dependOn(testMismatchedCpuArchitectureError(b, .{ .target = glibc_target }));
100 // elf_step.dependOn(testIFuncStatic(b, .{ .target = x86_64_gnu }));
101 // elf_step.dependOn(testIFuncStaticPie(b, .{ .target = x86_64_gnu }));
102 elf_step.dependOn(testInitArrayOrder(b, .{ .target = x86_64_gnu }));
103 elf_step.dependOn(testLargeAlignmentDso(b, .{ .target = x86_64_gnu }));
104 elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = x86_64_gnu }));
105 elf_step.dependOn(testLargeBss(b, .{ .target = x86_64_gnu }));
106 elf_step.dependOn(testLinkOrder(b, .{ .target = x86_64_gnu }));
107 elf_step.dependOn(testLdScript(b, .{ .target = x86_64_gnu }));
108 elf_step.dependOn(testLdScriptPathError(b, .{ .target = x86_64_gnu }));
109 elf_step.dependOn(testLdScriptAllowUndefinedVersion(b, .{ .target = x86_64_gnu, .use_lld = true }));
110 elf_step.dependOn(testLdScriptDisallowUndefinedVersion(b, .{ .target = x86_64_gnu, .use_lld = true }));
111 elf_step.dependOn(testMismatchedCpuArchitectureError(b, .{ .target = x86_64_gnu }));
101112 // https://github.com/ziglang/zig/issues/17451
102 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = glibc_target }));
103 elf_step.dependOn(testPie(b, .{ .target = glibc_target }));
104 elf_step.dependOn(testPltGot(b, .{ .target = glibc_target }));
105 elf_step.dependOn(testPreinitArray(b, .{ .target = glibc_target }));
106 elf_step.dependOn(testSharedAbsSymbol(b, .{ .target = glibc_target }));
107 elf_step.dependOn(testTlsDfStaticTls(b, .{ .target = glibc_target }));
108 elf_step.dependOn(testTlsDso(b, .{ .target = glibc_target }));
109 elf_step.dependOn(testTlsGd(b, .{ .target = glibc_target }));
110 elf_step.dependOn(testTlsGdNoPlt(b, .{ .target = glibc_target }));
111 elf_step.dependOn(testTlsGdToIe(b, .{ .target = glibc_target }));
112 elf_step.dependOn(testTlsIe(b, .{ .target = glibc_target }));
113 elf_step.dependOn(testTlsLargeAlignment(b, .{ .target = glibc_target }));
114 elf_step.dependOn(testTlsLargeTbss(b, .{ .target = glibc_target }));
115 elf_step.dependOn(testTlsLargeStaticImage(b, .{ .target = glibc_target }));
116 elf_step.dependOn(testTlsLd(b, .{ .target = glibc_target }));
117 elf_step.dependOn(testTlsLdDso(b, .{ .target = glibc_target }));
118 elf_step.dependOn(testTlsLdNoPlt(b, .{ .target = glibc_target }));
113 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = x86_64_gnu }));
114 elf_step.dependOn(testPie(b, .{ .target = x86_64_gnu }));
115 elf_step.dependOn(testPltGot(b, .{ .target = x86_64_gnu }));
116 elf_step.dependOn(testPreinitArray(b, .{ .target = x86_64_gnu }));
117 elf_step.dependOn(testSharedAbsSymbol(b, .{ .target = x86_64_gnu }));
118 elf_step.dependOn(testTlsDfStaticTls(b, .{ .target = x86_64_gnu }));
119 elf_step.dependOn(testTlsDso(b, .{ .target = x86_64_gnu }));
120 elf_step.dependOn(testTlsGd(b, .{ .target = x86_64_gnu }));
121 elf_step.dependOn(testTlsGdNoPlt(b, .{ .target = x86_64_gnu }));
122 elf_step.dependOn(testTlsGdToIe(b, .{ .target = x86_64_gnu }));
123 elf_step.dependOn(testTlsIe(b, .{ .target = x86_64_gnu }));
124 elf_step.dependOn(testTlsLargeAlignment(b, .{ .target = x86_64_gnu }));
125 elf_step.dependOn(testTlsLargeTbss(b, .{ .target = x86_64_gnu }));
126 elf_step.dependOn(testTlsLargeStaticImage(b, .{ .target = x86_64_gnu }));
127 elf_step.dependOn(testTlsLd(b, .{ .target = x86_64_gnu }));
128 elf_step.dependOn(testTlsLdDso(b, .{ .target = x86_64_gnu }));
129 elf_step.dependOn(testTlsLdNoPlt(b, .{ .target = x86_64_gnu }));
119130 // https://github.com/ziglang/zig/issues/17430
120 // elf_step.dependOn(testTlsNoPic(b, .{ .target = glibc_target }));
121 elf_step.dependOn(testTlsOffsetAlignment(b, .{ .target = glibc_target }));
122 elf_step.dependOn(testTlsPic(b, .{ .target = glibc_target }));
123 elf_step.dependOn(testTlsSmallAlignment(b, .{ .target = glibc_target }));
124 elf_step.dependOn(testUnknownFileTypeError(b, .{ .target = glibc_target }));
125 elf_step.dependOn(testUnresolvedError(b, .{ .target = glibc_target }));
126 elf_step.dependOn(testWeakExports(b, .{ .target = glibc_target }));
127 elf_step.dependOn(testWeakUndefsDso(b, .{ .target = glibc_target }));
128 elf_step.dependOn(testZNow(b, .{ .target = glibc_target }));
129 elf_step.dependOn(testZStackSize(b, .{ .target = glibc_target }));
130 elf_step.dependOn(testZText(b, .{ .target = glibc_target }));
131 // elf_step.dependOn(testTlsNoPic(b, .{ .target = x86_64_gnu }));
132 elf_step.dependOn(testTlsOffsetAlignment(b, .{ .target = x86_64_gnu }));
133 elf_step.dependOn(testTlsPic(b, .{ .target = x86_64_gnu }));
134 elf_step.dependOn(testTlsSmallAlignment(b, .{ .target = x86_64_gnu }));
135 elf_step.dependOn(testUnknownFileTypeError(b, .{ .target = x86_64_gnu }));
136 elf_step.dependOn(testUnresolvedError(b, .{ .target = x86_64_gnu }));
137 elf_step.dependOn(testWeakExports(b, .{ .target = x86_64_gnu }));
138 elf_step.dependOn(testWeakUndefsDso(b, .{ .target = x86_64_gnu }));
139 elf_step.dependOn(testZNow(b, .{ .target = x86_64_gnu }));
140 elf_step.dependOn(testZStackSize(b, .{ .target = x86_64_gnu }));
141 elf_step.dependOn(testZText(b, .{ .target = x86_64_gnu }));
142
143 // aarch64 tests
144 elf_step.dependOn(testLinkingC(b, .{ .target = aarch64_musl }));
145
146 // riscv64 tests
147 elf_step.dependOn(testLinkingC(b, .{ .target = riscv64_musl }));
131148
132149 return elf_step;
133150}