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"...@@ -12,7 +12,7 @@ CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.12.0-dev.203+d3bc1cfc4"
12PREFIX="$HOME/deps/$CACHE_BASENAME"12PREFIX="$HOME/deps/$CACHE_BASENAME"
13ZIG="$PREFIX/bin/zig"13ZIG="$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
17# Make the `zig version` number consistent.17# Make the `zig version` number consistent.
18# This will affect the cmake command below.18# 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"...@@ -12,7 +12,7 @@ CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.12.0-dev.203+d3bc1cfc4"
12PREFIX="$HOME/deps/$CACHE_BASENAME"12PREFIX="$HOME/deps/$CACHE_BASENAME"
13ZIG="$PREFIX/bin/zig"13ZIG="$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
17# Make the `zig version` number consistent.17# Make the `zig version` number consistent.
18# This will affect the cmake command below.18# 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,...@@ -189,6 +189,7 @@ gnu_eh_frame_hdr_index: ?Symbol.Index = null,
189dso_handle_index: ?Symbol.Index = null,189dso_handle_index: ?Symbol.Index = null,
190rela_iplt_start_index: ?Symbol.Index = null,190rela_iplt_start_index: ?Symbol.Index = null,
191rela_iplt_end_index: ?Symbol.Index = null,191rela_iplt_end_index: ?Symbol.Index = null,
192global_pointer_index: ?Symbol.Index = null,
192start_stop_indexes: std.ArrayListUnmanaged(u32) = .{},193start_stop_indexes: std.ArrayListUnmanaged(u32) = .{},
193194
194/// An array of symbols parsed across all input files.195/// 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)...@@ -1343,6 +1344,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
1343 // Beyond this point, everything has been allocated a virtual address and we can resolve1344 // Beyond this point, everything has been allocated a virtual address and we can resolve
1344 // the relocations, and commit objects to file.1345 // the relocations, and commit objects to file.
1345 if (self.zigObjectPtr()) |zig_object| {1346 if (self.zigObjectPtr()) |zig_object| {
1347 var has_reloc_errors = false;
1346 for (zig_object.atoms.items) |atom_index| {1348 for (zig_object.atoms.items) |atom_index| {
1347 const atom_ptr = self.atom(atom_index) orelse continue;1349 const atom_ptr = self.atom(atom_index) orelse continue;
1348 if (!atom_ptr.flags.alive) continue;1350 if (!atom_ptr.flags.alive) continue;
...@@ -1353,10 +1355,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)...@@ -1353,10 +1355,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
1353 defer gpa.free(code);1355 defer gpa.free(code);
1354 const file_offset = shdr.sh_offset + atom_ptr.value;1356 const file_offset = shdr.sh_offset + atom_ptr.value;
1355 atom_ptr.resolveRelocsAlloc(self, code) catch |err| switch (err) {1357 atom_ptr.resolveRelocsAlloc(self, code) catch |err| switch (err) {
1356 // TODO1358 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,
1357 error.RelaxFail, error.InvalidInstruction, error.CannotEncode => {
1358 log.err("relaxing intructions failed; TODO this should be a fatal linker error", .{});
1359 },
1360 error.UnsupportedCpuArch => {1359 error.UnsupportedCpuArch => {
1361 try self.reportUnsupportedCpuArch();1360 try self.reportUnsupportedCpuArch();
1362 return error.FlushFailure;1361 return error.FlushFailure;
...@@ -1365,19 +1364,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)...@@ -1365,19 +1364,14 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
1365 };1364 };
1366 try self.base.file.?.pwriteAll(code, file_offset);1365 try self.base.file.?.pwriteAll(code, file_offset);
1367 }1366 }
1367
1368 if (has_reloc_errors) return error.FlushFailure;
1368 }1369 }
13691370
1370 try self.writePhdrTable();1371 try self.writePhdrTable();
1371 try self.writeShdrTable();1372 try self.writeShdrTable();
1372 try self.writeAtoms();1373 try self.writeAtoms();
13731374 try self.writeSyntheticSections();
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 };
13811375
1382 if (self.entry_index == null and self.base.isExe()) {1376 if (self.entry_index == null and self.base.isExe()) {
1383 log.debug("flushing. no_entry_point_found = true", .{});1377 log.debug("flushing. no_entry_point_found = true", .{});
...@@ -2048,18 +2042,23 @@ fn scanRelocs(self: *Elf) !void {...@@ -2048,18 +2042,23 @@ fn scanRelocs(self: *Elf) !void {
2048 if (self.zigObjectPtr()) |zo| objects.appendAssumeCapacity(zo.index);2042 if (self.zigObjectPtr()) |zo| objects.appendAssumeCapacity(zo.index);
2049 objects.appendSliceAssumeCapacity(self.objects.items);2043 objects.appendSliceAssumeCapacity(self.objects.items);
20502044
2045 var has_reloc_errors = false;
2051 for (objects.items) |index| {2046 for (objects.items) |index| {
2052 self.file(index).?.scanRelocs(self, &undefs) catch |err| switch (err) {2047 self.file(index).?.scanRelocs(self, &undefs) catch |err| switch (err) {
2048 error.RelaxFailure => unreachable,
2053 error.UnsupportedCpuArch => {2049 error.UnsupportedCpuArch => {
2054 try self.reportUnsupportedCpuArch();2050 try self.reportUnsupportedCpuArch();
2055 return error.FlushFailure;2051 return error.FlushFailure;
2056 },2052 },
2053 error.RelocFailure => has_reloc_errors = true,
2057 else => |e| return e,2054 else => |e| return e,
2058 };2055 };
2059 }2056 }
20602057
2061 try self.reportUndefinedSymbols(&undefs);2058 try self.reportUndefinedSymbols(&undefs);
20622059
2060 if (has_reloc_errors) return error.FlushFailure;
2061
2063 for (self.symbols.items, 0..) |*sym, i| {2062 for (self.symbols.items, 0..) |*sym, i| {
2064 const index = @as(u32, @intCast(i));2063 const index = @as(u32, @intCast(i));
2065 if (!sym.isLocal(self) and !sym.flags.has_dynamic) {2064 if (!sym.isLocal(self) and !sym.flags.has_dynamic) {
...@@ -3095,6 +3094,10 @@ fn addLinkerDefinedSymbols(self: *Elf) !void {...@@ -3095,6 +3094,10 @@ fn addLinkerDefinedSymbols(self: *Elf) !void {
3095 }3094 }
3096 }3095 }
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
3098 linker_defined.resolveSymbols(self);3101 linker_defined.resolveSymbols(self);
3099}3102}
31003103
...@@ -3222,6 +3225,19 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void {...@@ -3222,6 +3225,19 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void {
3222 stop.output_section_index = shndx;3225 stop.output_section_index = shndx;
3223 }3226 }
3224 }3227 }
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 }
3225}3241}
32263242
3227fn checkDuplicates(self: *Elf) !void {3243fn checkDuplicates(self: *Elf) !void {
...@@ -4431,6 +4447,8 @@ fn writeAtoms(self: *Elf) !void {...@@ -4431,6 +4447,8 @@ fn writeAtoms(self: *Elf) !void {
4431 undefs.deinit();4447 undefs.deinit();
4432 }4448 }
44334449
4450 var has_reloc_errors = false;
4451
4434 // TODO iterate over `output_sections` directly4452 // TODO iterate over `output_sections` directly
4435 for (self.shdrs.items, 0..) |shdr, shndx| {4453 for (self.shdrs.items, 0..) |shdr, shndx| {
4436 if (shdr.sh_type == elf.SHT_NULL) continue;4454 if (shdr.sh_type == elf.SHT_NULL) continue;
...@@ -4493,14 +4511,11 @@ fn writeAtoms(self: *Elf) !void {...@@ -4493,14 +4511,11 @@ fn writeAtoms(self: *Elf) !void {
4493 else4511 else
4494 atom_ptr.resolveRelocsAlloc(self, out_code);4512 atom_ptr.resolveRelocsAlloc(self, out_code);
4495 _ = res catch |err| switch (err) {4513 _ = 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 },
4500 error.UnsupportedCpuArch => {4514 error.UnsupportedCpuArch => {
4501 try self.reportUnsupportedCpuArch();4515 try self.reportUnsupportedCpuArch();
4502 return error.FlushFailure;4516 return error.FlushFailure;
4503 },4517 },
4518 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,
4504 else => |e| return e,4519 else => |e| return e,
4505 };4520 };
4506 }4521 }
...@@ -4509,6 +4524,8 @@ fn writeAtoms(self: *Elf) !void {...@@ -4509,6 +4524,8 @@ fn writeAtoms(self: *Elf) !void {
4509 }4524 }
45104525
4511 try self.reportUndefinedSymbols(&undefs);4526 try self.reportUndefinedSymbols(&undefs);
4527
4528 if (has_reloc_errors) return error.FlushFailure;
4512}4529}
45134530
4514pub fn updateSymtabSize(self: *Elf) !void {4531pub fn updateSymtabSize(self: *Elf) !void {
...@@ -4665,7 +4682,14 @@ fn writeSyntheticSections(self: *Elf) !void {...@@ -4665,7 +4682,14 @@ fn writeSyntheticSections(self: *Elf) !void {
4665 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;4682 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
4666 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);4683 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
4667 defer buffer.deinit();4684 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 };
4669 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);4693 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
4670 }4694 }
46714695
...@@ -5526,6 +5550,15 @@ pub fn comdatGroupOwner(self: *Elf, index: ComdatGroupOwner.Index) *ComdatGroupO...@@ -5526,6 +5550,15 @@ pub fn comdatGroupOwner(self: *Elf, index: ComdatGroupOwner.Index) *ComdatGroupO
5526 return &self.comdat_groups_owners.items[index];5550 return &self.comdat_groups_owners.items[index];
5527}5551}
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
5529pub fn tpAddress(self: *Elf) u64 {5562pub fn tpAddress(self: *Elf) u64 {
5530 const index = self.phdr_tls_index orelse return 0;5563 const index = self.phdr_tls_index orelse return 0;
5531 const phdr = self.phdrs.items[index];5564 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 {...@@ -300,7 +300,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void {
300 self.* = .{};300 self.* = .{};
301}301}
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 {
304 const shndx = self.relocsShndx() orelse return &[0]elf.Elf64_Rela{};304 const shndx = self.relocsShndx() orelse return &[0]elf.Elf64_Rela{};
305 return switch (self.file(elf_file).?) {305 return switch (self.file(elf_file).?) {
306 .zig_object => |x| x.relocs.items[shndx].items,306 .zig_object => |x| x.relocs.items[shndx].items,
...@@ -394,11 +394,62 @@ pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool {...@@ -394,11 +394,62 @@ pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool {
394 return false;394 return false;
395}395}
396396
397pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void {397pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) RelocError!void {
398 switch (elf_file.getTarget().cpu.arch) {398 const cpu_arch = elf_file.getTarget().cpu.arch;
399 .x86_64 => try x86_64.scanRelocs(self, elf_file, code, undefs),399 const file_ptr = self.file(elf_file).?;
400 else => return error.UnsupportedCpuArch,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 }
401 }451 }
452 if (has_reloc_errors) return error.RelocFailure;
402}453}
403454
404fn scanReloc(455fn scanReloc(
...@@ -407,7 +458,7 @@ fn scanReloc(...@@ -407,7 +458,7 @@ fn scanReloc(
407 rel: elf.Elf64_Rela,458 rel: elf.Elf64_Rela,
408 action: RelocAction,459 action: RelocAction,
409 elf_file: *Elf,460 elf_file: *Elf,
410) error{OutOfMemory}!void {461) RelocError!void {
411 const is_writeable = self.inputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0;462 const is_writeable = self.inputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0;
412 const num_dynrelocs = switch (self.file(elf_file).?) {463 const num_dynrelocs = switch (self.file(elf_file).?) {
413 .linker_defined => unreachable,464 .linker_defined => unreachable,
...@@ -554,7 +605,7 @@ fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 {...@@ -554,7 +605,7 @@ fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 {
554 return 3;605 return 3;
555}606}
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 {
558 var err = try elf_file.addErrorWithNotes(1);609 var err = try elf_file.addErrorWithNotes(1);
559 try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {} at offset 0x{x}", .{610 try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {} at offset 0x{x}", .{
560 relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),611 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...@@ -564,6 +615,7 @@ fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) er
564 self.file(elf_file).?.fmtPath(),615 self.file(elf_file).?.fmtPath(),
565 self.name(elf_file),616 self.name(elf_file),
566 });617 });
618 return error.RelocFailure;
567}619}
568620
569fn reportTextRelocError(621fn reportTextRelocError(
...@@ -571,7 +623,7 @@ fn reportTextRelocError(...@@ -571,7 +623,7 @@ fn reportTextRelocError(
571 symbol: *const Symbol,623 symbol: *const Symbol,
572 rel: elf.Elf64_Rela,624 rel: elf.Elf64_Rela,
573 elf_file: *Elf,625 elf_file: *Elf,
574) error{OutOfMemory}!void {626) RelocError!void {
575 var err = try elf_file.addErrorWithNotes(1);627 var err = try elf_file.addErrorWithNotes(1);
576 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{628 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
577 rel.r_offset,629 rel.r_offset,
...@@ -581,6 +633,7 @@ fn reportTextRelocError(...@@ -581,6 +633,7 @@ fn reportTextRelocError(
581 self.file(elf_file).?.fmtPath(),633 self.file(elf_file).?.fmtPath(),
582 self.name(elf_file),634 self.name(elf_file),
583 });635 });
636 return error.RelocFailure;
584}637}
585638
586fn reportPicError(639fn reportPicError(
...@@ -588,7 +641,7 @@ fn reportPicError(...@@ -588,7 +641,7 @@ fn reportPicError(
588 symbol: *const Symbol,641 symbol: *const Symbol,
589 rel: elf.Elf64_Rela,642 rel: elf.Elf64_Rela,
590 elf_file: *Elf,643 elf_file: *Elf,
591) error{OutOfMemory}!void {644) RelocError!void {
592 var err = try elf_file.addErrorWithNotes(2);645 var err = try elf_file.addErrorWithNotes(2);
593 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{646 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
594 rel.r_offset,647 rel.r_offset,
...@@ -599,6 +652,7 @@ fn reportPicError(...@@ -599,6 +652,7 @@ fn reportPicError(
599 self.name(elf_file),652 self.name(elf_file),
600 });653 });
601 try err.addNote(elf_file, "recompile with -fPIC", .{});654 try err.addNote(elf_file, "recompile with -fPIC", .{});
655 return error.RelocFailure;
602}656}
603657
604fn reportNoPicError(658fn reportNoPicError(
...@@ -606,7 +660,7 @@ fn reportNoPicError(...@@ -606,7 +660,7 @@ fn reportNoPicError(
606 symbol: *const Symbol,660 symbol: *const Symbol,
607 rel: elf.Elf64_Rela,661 rel: elf.Elf64_Rela,
608 elf_file: *Elf,662 elf_file: *Elf,
609) error{OutOfMemory}!void {663) RelocError!void {
610 var err = try elf_file.addErrorWithNotes(2);664 var err = try elf_file.addErrorWithNotes(2);
611 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{665 try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
612 rel.r_offset,666 rel.r_offset,
...@@ -617,6 +671,7 @@ fn reportNoPicError(...@@ -617,6 +671,7 @@ fn reportNoPicError(
617 self.name(elf_file),671 self.name(elf_file),
618 });672 });
619 try err.addNote(elf_file, "recompile with -fno-PIC", .{});673 try err.addNote(elf_file, "recompile with -fno-PIC", .{});
674 return error.RelocFailure;
620}675}
621676
622// This function will report any undefined non-weak symbols that are not imports.677// This function will report any undefined non-weak symbols that are not imports.
...@@ -627,7 +682,7 @@ fn reportUndefined(...@@ -627,7 +682,7 @@ fn reportUndefined(
627 sym_index: Symbol.Index,682 sym_index: Symbol.Index,
628 rel: elf.Elf64_Rela,683 rel: elf.Elf64_Rela,
629 undefs: anytype,684 undefs: anytype,
630) !void {685) !bool {
631 const comp = elf_file.base.comp;686 const comp = elf_file.base.comp;
632 const gpa = comp.gpa;687 const gpa = comp.gpa;
633 const rel_esym = switch (self.file(elf_file).?) {688 const rel_esym = switch (self.file(elf_file).?) {
...@@ -647,16 +702,95 @@ fn reportUndefined(...@@ -647,16 +702,95 @@ fn reportUndefined(
647 gop.value_ptr.* = std.ArrayList(Atom.Index).init(gpa);702 gop.value_ptr.* = std.ArrayList(Atom.Index).init(gpa);
648 }703 }
649 try gop.value_ptr.append(self.atom_index);704 try gop.value_ptr.append(self.atom_index);
705 return true;
650 }706 }
707
708 return false;
651}709}
652710
653pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {711pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) RelocError!void {
654 relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) });712 relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) });
655713
656 switch (elf_file.getTarget().cpu.arch) {714 const cpu_arch = elf_file.getTarget().cpu.arch;
657 .x86_64 => try x86_64.resolveRelocsAlloc(self, elf_file, code),715 const file_ptr = self.file(elf_file).?;
658 else => return error.UnsupportedCpuArch,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 }
659 }791 }
792
793 if (has_reloc_errors) return error.RelaxFailure;
660}794}
661795
662fn resolveDynAbsReloc(796fn resolveDynAbsReloc(
...@@ -761,10 +895,83 @@ fn applyDynamicReloc(value: i64, elf_file: *Elf, writer: anytype) !void {...@@ -761,10 +895,83 @@ fn applyDynamicReloc(value: i64, elf_file: *Elf, writer: anytype) !void {
761pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void {895pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void {
762 relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) });896 relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) });
763897
764 switch (elf_file.getTarget().cpu.arch) {898 const cpu_arch = elf_file.getTarget().cpu.arch;
765 .x86_64 => try x86_64.resolveRelocsNonAlloc(self, elf_file, code, undefs),899 const file_ptr = self.file(elf_file).?;
766 else => return error.UnsupportedCpuArch,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 }
767 }972 }
973
974 if (has_reloc_errors) return error.RelocFailure;
768}975}
769976
770pub fn format(977pub fn format(
...@@ -831,429 +1038,313 @@ pub const Flags = packed struct {...@@ -831,429 +1038,313 @@ pub const Flags = packed struct {
831};1038};
8321039
833const x86_64 = struct {1040const 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 {
835 const is_static = elf_file.base.isStatic();1049 const is_static = elf_file.base.isStatic();
836 const is_dyn_lib = elf_file.base.isDynLib();1050 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.1052 const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());
867 try atom.reportUndefined(elf_file, symbol, symbol_index, rel, undefs);1053 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
8681054
869 if (symbol.isIFunc(elf_file)) {1055 switch (r_type) {
870 symbol.flags.needs_got = true;1056 .@"64" => {
871 symbol.flags.needs_plt = true;1057 try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);
872 }1058 },
8731059
874 // While traversing relocations, mark symbols that require special handling such as1060 .@"32",
875 // pointer indirection via GOT, or a stub trampoline via PLT.1061 .@"32S",
876 switch (r_type) {1062 => {
877 .@"64" => {1063 try atom.scanReloc(symbol, rel, absRelocAction(symbol, elf_file), elf_file);
878 try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file);1064 },
879 },
8801065
881 .@"32",1066 .GOT32,
882 .@"32S",1067 .GOTPC32,
883 => {1068 .GOTPC64,
884 try atom.scanReloc(symbol, rel, absRelocAction(symbol, elf_file), elf_file);1069 .GOTPCREL,
885 },1070 .GOTPCREL64,
1071 .GOTPCRELX,
1072 .REX_GOTPCRELX,
1073 => {
1074 symbol.flags.needs_got = true;
1075 },
8861076
887 .GOT32,1077 .PLT32,
888 .GOTPC32,1078 .PLTOFF64,
889 .GOTPC64,1079 => {
890 .GOTPCREL,1080 if (symbol.flags.import) {
891 .GOTPCREL64,1081 symbol.flags.needs_plt = true;
892 .GOTPCRELX,1082 }
893 .REX_GOTPCRELX,1083 },
894 => {
895 symbol.flags.needs_got = true;
896 },
8971084
898 .PLT32,1085 .PC32 => {
899 .PLTOFF64,1086 try atom.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file);
900 => {1087 },
901 if (symbol.flags.import) {
902 symbol.flags.needs_plt = true;
903 }
904 },
9051088
906 .PC32 => {1089 .TLSGD => {
907 try atom.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file);1090 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr
908 },
9091091
910 .TLSGD => {1092 if (is_static or (!symbol.flags.import and !is_dyn_lib)) {
911 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr1093 // 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)) {1104 .TLSLD => {
914 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a1105 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr
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 },
9241106
925 .TLSLD => {1107 if (is_static or !is_dyn_lib) {
926 // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr1108 // 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) {1116 .GOTTPOFF => {
929 // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a1117 const should_relax = blk: {
930 // We skip the next relocation.1118 if (is_dyn_lib or symbol.flags.import) break :blk false;
931 i += 1;1119 if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false;
932 } else {1120 break :blk true;
933 elf_file.got.flags.needs_tlsld = true;1121 };
934 }1122 if (!should_relax) {
935 },1123 symbol.flags.needs_gottp = true;
1124 }
1125 },
9361126
937 .GOTTPOFF => {1127 .GOTPC32_TLSDESC => {
938 const should_relax = blk: {1128 const should_relax = is_static or (!is_dyn_lib and !symbol.flags.import);
939 if (is_dyn_lib or symbol.flags.import) break :blk false;1129 if (!should_relax) {
940 if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false;1130 symbol.flags.needs_tlsdesc = true;
941 break :blk true;1131 }
942 };1132 },
943 if (!should_relax) {
944 symbol.flags.needs_gottp = true;
945 }
946 },
9471133
948 .GOTPC32_TLSDESC => {1134 .TPOFF32,
949 const should_relax = is_static or (!is_dyn_lib and !symbol.flags.import);1135 .TPOFF64,
950 if (!should_relax) {1136 => {
951 symbol.flags.needs_tlsdesc = true;1137 if (is_dyn_lib) try atom.reportPicError(symbol, rel, elf_file);
952 }1138 },
953 },
9541139
955 .TPOFF32,1140 .GOTOFF64,
956 .TPOFF64,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,
957 => {1152 => {
958 if (is_dyn_lib) try atom.reportPicError(symbol, rel, elf_file);1153 assert(symbol.flags.has_zig_got);
959 },1154 },
9601155
961 .GOTOFF64,1156 else => try atom.reportUnhandledRelocError(rel, elf_file),
962 .DTPOFF32,1157 },
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 }
980 }1158 }
981 }1159 }
9821160
983 fn resolveRelocsAlloc(atom: Atom, elf_file: *Elf, code: []u8) !void {1161 fn resolveRelocAlloc(
984 const file_ptr = atom.file(elf_file).?;1162 atom: Atom,
985 var stream = std.io.fixedBufferStream(code);1163 elf_file: *Elf,
986 const cwriter = stream.writer();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);1174 const cwriter = stream.writer();
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 });
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) {1178 switch (r_type) {
1043 .NONE => unreachable,1179 .NONE => unreachable,
10441180
1045 .@"64" => {1181 .@"64" => {
1046 try atom.resolveDynAbsReloc(1182 try atom.resolveDynAbsReloc(
1047 target,1183 target,
1048 rel,1184 rel,
1049 dynAbsRelocAction(target, elf_file),1185 dynAbsRelocAction(target, elf_file),
1050 elf_file,1186 elf_file,
1051 cwriter,1187 cwriter,
1052 );1188 );
1053 },1189 },
10541190
1055 .PLT32,1191 .PLT32,
1056 .PC32,1192 .PC32,
1057 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little),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),1195 .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),1196 .GOTPC32 => try cwriter.writeInt(i32, @as(i32, @intCast(GOT + A - P)), .little),
1061 .GOTPC64 => try cwriter.writeInt(i64, GOT + A - P, .little),1197 .GOTPC64 => try cwriter.writeInt(i64, GOT + A - P, .little),
10621198
1063 .GOTPCRELX => {1199 .GOTPCRELX => {
1064 if (!target.flags.import and !target.isIFunc(elf_file) and !target.isAbs(elf_file)) blk: {1200 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;1201 x86_64.relaxGotpcrelx(code[r_offset - 2 ..]) catch break :blk;
1066 try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little);1202 try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little);
1067 continue;1203 return;
1068 }1204 }
1069 try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little);1205 try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little);
1070 },1206 },
10711207
1072 .REX_GOTPCRELX => {1208 .REX_GOTPCRELX => {
1073 if (!target.flags.import and !target.isIFunc(elf_file) and !target.isAbs(elf_file)) blk: {1209 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;1210 x86_64.relaxRexGotpcrelx(code[r_offset - 3 ..]) catch break :blk;
1075 try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little);1211 try cwriter.writeInt(i32, @as(i32, @intCast(S + A - P)), .little);
1076 continue;1212 return;
1077 }1213 }
1078 try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little);1214 try cwriter.writeInt(i32, @as(i32, @intCast(G + GOT + A - P)), .little);
1079 },1215 },
10801216
1081 .@"32" => try cwriter.writeInt(u32, @as(u32, @truncate(@as(u64, @intCast(S + A)))), .little),1217 .@"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),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),1220 .TPOFF32 => try cwriter.writeInt(i32, @as(i32, @truncate(S + A - TP)), .little),
1085 .TPOFF64 => try cwriter.writeInt(i64, 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),1223 .DTPOFF32 => try cwriter.writeInt(i32, @as(i32, @truncate(S + A - DTP)), .little),
1088 .DTPOFF64 => try cwriter.writeInt(i64, S + A - DTP, .little),1224 .DTPOFF64 => try cwriter.writeInt(i64, S + A - DTP, .little),
10891225
1090 .TLSGD => {1226 .TLSGD => {
1091 if (target.flags.has_tlsgd) {1227 if (target.flags.has_tlsgd) {
1092 const S_ = @as(i64, @intCast(target.tlsGdAddress(elf_file)));1228 const S_ = @as(i64, @intCast(target.tlsGdAddress(elf_file)));
1093 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);1229 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
1094 } else if (target.flags.has_gottp) {1230 } else if (target.flags.has_gottp) {
1095 const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));1231 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);1232 try x86_64.relaxTlsGdToIe(atom, &.{ rel, it.next().? }, @intCast(S_ - P), elf_file, stream);
1097 i += 1;1233 } else {
1098 } else {1234 try x86_64.relaxTlsGdToLe(
1099 try x86_64.relaxTlsGdToLe(1235 atom,
1100 atom,1236 &.{ rel, it.next().? },
1101 rels[i .. i + 2],1237 @as(i32, @intCast(S - TP)),
1102 @as(i32, @intCast(S - TP)),1238 elf_file,
1103 elf_file,1239 stream,
1104 &stream,1240 );
1105 );1241 }
1106 i += 1;1242 },
1107 }
1108 },
11091243
1110 .TLSLD => {1244 .TLSLD => {
1111 if (elf_file.got.tlsld_index) |entry_index| {1245 if (elf_file.got.tlsld_index) |entry_index| {
1112 const tlsld_entry = elf_file.got.entries.items[entry_index];1246 const tlsld_entry = elf_file.got.entries.items[entry_index];
1113 const S_ = @as(i64, @intCast(tlsld_entry.address(elf_file)));1247 const S_ = @as(i64, @intCast(tlsld_entry.address(elf_file)));
1114 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);1248 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
1115 } else {1249 } else {
1116 try x86_64.relaxTlsLdToLe(1250 try x86_64.relaxTlsLdToLe(
1117 atom,1251 atom,
1118 rels[i .. i + 2],1252 &.{ rel, it.next().? },
1119 @as(i32, @intCast(TP - @as(i64, @intCast(elf_file.tlsAddress())))),1253 @as(i32, @intCast(TP - @as(i64, @intCast(elf_file.tlsAddress())))),
1120 elf_file,1254 elf_file,
1121 &stream,1255 stream,
1122 );1256 );
1123 i += 1;1257 }
1124 }1258 },
1125 },
11261259
1127 .GOTPC32_TLSDESC => {1260 .GOTPC32_TLSDESC => {
1128 if (target.flags.has_tlsdesc) {1261 if (target.flags.has_tlsdesc) {
1129 const S_ = @as(i64, @intCast(target.tlsDescAddress(elf_file)));1262 const S_ = @as(i64, @intCast(target.tlsDescAddress(elf_file)));
1130 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);1263 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
1131 } else {1264 } else {
1132 try x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]);1265 x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch {
1133 try cwriter.writeInt(i32, @as(i32, @intCast(S - TP)), .little);1266 var err = try elf_file.addErrorWithNotes(1);
1134 }1267 try err.addMsg(elf_file, "could not relax {s}", .{@tagName(r_type)});
1135 },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) {1279 .TLSDESC_CALL => if (!target.flags.has_tlsdesc) {
1138 // call -> nop1280 // call -> nop
1139 try cwriter.writeAll(&.{ 0x66, 0x90 });1281 try cwriter.writeAll(&.{ 0x66, 0x90 });
1140 },1282 },
11411283
1142 .GOTTPOFF => {1284 .GOTTPOFF => {
1143 if (target.flags.has_gottp) {1285 if (target.flags.has_gottp) {
1144 const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));1286 const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file)));
1145 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);1287 try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
1146 } else {1288 } else {
1147 x86_64.relaxGotTpOff(code[r_offset - 3 ..]) catch unreachable;1289 x86_64.relaxGotTpOff(code[r_offset - 3 ..]);
1148 try cwriter.writeInt(i32, @as(i32, @intCast(S - TP)), .little);1290 try cwriter.writeInt(i32, @as(i32, @intCast(S - TP)), .little);
1149 }1291 }
1150 },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)) {1296 else => |x| switch (@intFromEnum(x)) {
1155 // Zig custom relocations1297 // Zig custom relocations
1156 Elf.R_ZIG_GOT32 => try cwriter.writeInt(u32, @as(u32, @intCast(ZIG_GOT + A)), .little),1298 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),1299 Elf.R_ZIG_GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(ZIG_GOT + A - P)), .little),
11581300
1159 else => {},1301 else => try atom.reportUnhandledRelocError(rel, elf_file),
1160 },1302 },
1161 }
1162 }1303 }
1163 }1304 }
11641305
1165 fn resolveRelocsNonAlloc(atom: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void {1306 fn resolveRelocNonAlloc(
1166 const file_ptr = atom.file(elf_file).?;1307 atom: Atom,
1167 var stream = std.io.fixedBufferStream(code);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());
1168 const cwriter = stream.writer();1319 const cwriter = stream.writer();
11691320
1170 const rels = atom.relocs(elf_file);1321 _, const A, const S, const GOT, _, _, const DTP, _ = args;
1171 var i: usize = 0;1322
1172 while (i < rels.len) : (i += 1) {1323 switch (r_type) {
1173 const rel = rels[i];1324 .NONE => unreachable,
1174 const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());1325 .@"8" => try cwriter.writeInt(u8, @as(u8, @bitCast(@as(i8, @intCast(S + A)))), .little),
1175 if (r_type == .NONE) continue;1326 .@"16" => try cwriter.writeInt(u16, @as(u16, @bitCast(@as(i16, @intCast(S + A)))), .little),
11761327 .@"32" => try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(S + A)))), .little),
1177 const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;1328 .@"32S" => try cwriter.writeInt(i32, @as(i32, @intCast(S + A)), .little),
11781329 .@"64" => try cwriter.writeInt(i64, S + A, .little),
1179 const target_index = switch (file_ptr) {1330 .DTPOFF32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - DTP)), .little),
1180 .zig_object => |x| x.symbol(rel.r_sym()),1331 .DTPOFF64 => try cwriter.writeInt(i64, S + A - DTP, .little),
1181 .object => |x| x.symbols.items[rel.r_sym()],1332 .GOTOFF64 => try cwriter.writeInt(i64, S + A - GOT, .little),
1182 else => unreachable,1333 .GOTPC64 => try cwriter.writeInt(i64, GOT + A, .little),
1183 };1334 .SIZE32 => {
1184 const target = elf_file.symbol(target_index);1335 const size = @as(i64, @intCast(target.elfSym(elf_file).st_size));
11851336 try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(size + A)))), .little);
1186 // Check for violation of One Definition Rule for COMDATs.1337 },
1187 if (target.file(elf_file) == null) {1338 .SIZE64 => {
1188 // TODO convert into an error1339 const size = @as(i64, @intCast(target.elfSym(elf_file).st_size));
1189 log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{1340 try cwriter.writeInt(i64, @as(i64, @intCast(size + A)), .little);
1190 file_ptr.fmtPath(),1341 },
1191 atom.name(elf_file),1342 else => try atom.reportUnhandledRelocError(rel, 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 }
1252 }1343 }
1253 }1344 }
12541345
1255 fn relaxGotpcrelx(code: []u8) !void {1346 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;
1257 const inst = switch (old_inst.encoding.mnemonic) {1348 const inst = switch (old_inst.encoding.mnemonic) {
1258 .call => try Instruction.new(old_inst.prefix, .call, &.{1349 .call => try Instruction.new(old_inst.prefix, .call, &.{
1259 // TODO: hack to force imm32s in the assembler1350 // TODO: hack to force imm32s in the assembler
...@@ -1263,28 +1354,28 @@ const x86_64 = struct {...@@ -1263,28 +1354,28 @@ const x86_64 = struct {
1263 // TODO: hack to force imm32s in the assembler1354 // TODO: hack to force imm32s in the assembler
1264 .{ .imm = Immediate.s(-129) },1355 .{ .imm = Immediate.s(-129) },
1265 }),1356 }),
1266 else => return error.RelaxFail,1357 else => return error.RelaxFailure,
1267 };1358 };
1268 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });1359 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
1269 const nop = try Instruction.new(.none, .nop, &.{});1360 const nop = try Instruction.new(.none, .nop, &.{});
1270 encode(&.{ nop, inst }, code) catch return error.RelaxFail;1361 try encode(&.{ nop, inst }, code);
1271 }1362 }
12721363
1273 fn relaxRexGotpcrelx(code: []u8) !void {1364 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;
1275 switch (old_inst.encoding.mnemonic) {1366 switch (old_inst.encoding.mnemonic) {
1276 .mov => {1367 .mov => {
1277 const inst = try Instruction.new(old_inst.prefix, .lea, &old_inst.ops);1368 const inst = try Instruction.new(old_inst.prefix, .lea, &old_inst.ops);
1278 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });1369 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
1279 encode(&.{inst}, code) catch return error.RelaxFail;1370 try encode(&.{inst}, code);
1280 },1371 },
1281 else => return error.RelaxFail,1372 else => return error.RelaxFailure,
1282 }1373 }
1283 }1374 }
12841375
1285 fn relaxTlsGdToIe(1376 fn relaxTlsGdToIe(
1286 self: Atom,1377 self: Atom,
1287 rels: []align(1) const elf.Elf64_Rela,1378 rels: []const elf.Elf64_Rela,
1288 value: i32,1379 value: i32,
1289 elf_file: *Elf,1380 elf_file: *Elf,
1290 stream: anytype,1381 stream: anytype,
...@@ -1307,7 +1398,7 @@ const x86_64 = struct {...@@ -1307,7 +1398,7 @@ const x86_64 = struct {
13071398
1308 else => {1399 else => {
1309 var err = try elf_file.addErrorWithNotes(1);1400 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 {}", .{
1311 relocation.fmtRelocType(rels[0].r_type(), .x86_64),1402 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
1312 relocation.fmtRelocType(rels[1].r_type(), .x86_64),1403 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
1313 });1404 });
...@@ -1316,13 +1407,14 @@ const x86_64 = struct {...@@ -1316,13 +1407,14 @@ const x86_64 = struct {
1316 self.name(elf_file),1407 self.name(elf_file),
1317 rels[0].r_offset,1408 rels[0].r_offset,
1318 });1409 });
1410 return error.RelaxFailure;
1319 },1411 },
1320 }1412 }
1321 }1413 }
13221414
1323 fn relaxTlsLdToLe(1415 fn relaxTlsLdToLe(
1324 self: Atom,1416 self: Atom,
1325 rels: []align(1) const elf.Elf64_Rela,1417 rels: []const elf.Elf64_Rela,
1326 value: i32,1418 value: i32,
1327 elf_file: *Elf,1419 elf_file: *Elf,
1328 stream: anytype,1420 stream: anytype,
...@@ -1360,7 +1452,7 @@ const x86_64 = struct {...@@ -1360,7 +1452,7 @@ const x86_64 = struct {
13601452
1361 else => {1453 else => {
1362 var err = try elf_file.addErrorWithNotes(1);1454 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 {}", .{
1364 relocation.fmtRelocType(rels[0].r_type(), .x86_64),1456 relocation.fmtRelocType(rels[0].r_type(), .x86_64),
1365 relocation.fmtRelocType(rels[1].r_type(), .x86_64),1457 relocation.fmtRelocType(rels[1].r_type(), .x86_64),
1366 });1458 });
...@@ -1369,6 +1461,7 @@ const x86_64 = struct {...@@ -1369,6 +1461,7 @@ const x86_64 = struct {
1369 self.name(elf_file),1461 self.name(elf_file),
1370 rels[0].r_offset,1462 rels[0].r_offset,
1371 });1463 });
1464 return error.RelaxFailure;
1372 },1465 },
1373 }1466 }
1374 }1467 }
...@@ -1388,24 +1481,24 @@ const x86_64 = struct {...@@ -1388,24 +1481,24 @@ const x86_64 = struct {
1388 }1481 }
1389 }1482 }
13901483
1391 fn relaxGotTpOff(code: []u8) !void {1484 fn relaxGotTpOff(code: []u8) void {
1392 const old_inst = disassemble(code) orelse return error.RelaxFail;1485 const old_inst = disassemble(code) orelse unreachable;
1393 switch (old_inst.encoding.mnemonic) {1486 switch (old_inst.encoding.mnemonic) {
1394 .mov => {1487 .mov => {
1395 const inst = try Instruction.new(old_inst.prefix, .mov, &.{1488 const inst = Instruction.new(old_inst.prefix, .mov, &.{
1396 old_inst.ops[0],1489 old_inst.ops[0],
1397 // TODO: hack to force imm32s in the assembler1490 // TODO: hack to force imm32s in the assembler
1398 .{ .imm = Immediate.s(-129) },1491 .{ .imm = Immediate.s(-129) },
1399 });1492 }) catch unreachable;
1400 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });1493 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
1401 encode(&.{inst}, code) catch return error.RelaxFail;1494 encode(&.{inst}, code) catch unreachable;
1402 },1495 },
1403 else => return error.RelaxFail,1496 else => unreachable,
1404 }1497 }
1405 }1498 }
14061499
1407 fn relaxGotPcTlsDesc(code: []u8) !void {1500 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;
1409 switch (old_inst.encoding.mnemonic) {1502 switch (old_inst.encoding.mnemonic) {
1410 .lea => {1503 .lea => {
1411 const inst = try Instruction.new(old_inst.prefix, .mov, &.{1504 const inst = try Instruction.new(old_inst.prefix, .mov, &.{
...@@ -1414,15 +1507,15 @@ const x86_64 = struct {...@@ -1414,15 +1507,15 @@ const x86_64 = struct {
1414 .{ .imm = Immediate.s(-129) },1507 .{ .imm = Immediate.s(-129) },
1415 });1508 });
1416 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });1509 relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding });
1417 encode(&.{inst}, code) catch return error.RelaxFail;1510 try encode(&.{inst}, code);
1418 },1511 },
1419 else => return error.RelaxFail,1512 else => return error.RelaxFailure,
1420 }1513 }
1421 }1514 }
14221515
1423 fn relaxTlsGdToLe(1516 fn relaxTlsGdToLe(
1424 self: Atom,1517 self: Atom,
1425 rels: []align(1) const elf.Elf64_Rela,1518 rels: []const elf.Elf64_Rela,
1426 value: i32,1519 value: i32,
1427 elf_file: *Elf,1520 elf_file: *Elf,
1428 stream: anytype,1521 stream: anytype,
...@@ -1460,6 +1553,7 @@ const x86_64 = struct {...@@ -1460,6 +1553,7 @@ const x86_64 = struct {
1460 self.name(elf_file),1553 self.name(elf_file),
1461 rels[0].r_offset,1554 rels[0].r_offset,
1462 });1555 });
1556 return error.RelaxFailure;
1463 },1557 },
1464 }1558 }
1465 }1559 }
...@@ -1485,15 +1579,446 @@ const x86_64 = struct {...@@ -1485,15 +1579,446 @@ const x86_64 = struct {
1485 const Instruction = encoder.Instruction;1579 const Instruction = encoder.Instruction;
1486};1580};
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
1488const std = @import("std");2011const std = @import("std");
1489const assert = std.debug.assert;2012const assert = std.debug.assert;
1490const elf = std.elf;2013const elf = std.elf;
1491const eh_frame = @import("eh_frame.zig");2014const eh_frame = @import("eh_frame.zig");
1492const log = std.log.scoped(.link);2015const log = std.log.scoped(.link);
2016const math = std.math;
2017const mem = std.mem;
1493const relocs_log = std.log.scoped(.link_relocs);2018const relocs_log = std.log.scoped(.link_relocs);
1494const relocation = @import("relocation.zig");2019const relocation = @import("relocation.zig");
14952020
1496const Allocator = std.mem.Allocator;2021const Allocator = mem.Allocator;
1497const Atom = @This();2022const Atom = @This();
1498const Elf = @import("../Elf.zig");2023const Elf = @import("../Elf.zig");
1499const Fde = eh_frame.Fde;2024const 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:...@@ -245,6 +245,9 @@ fn initAtoms(self: *Object, allocator: Allocator, handle: std.fs.File, elf_file:
245 atom.rel_index = @intCast(self.relocs.items.len);245 atom.rel_index = @intCast(self.relocs.items.len);
246 atom.rel_num = @intCast(relocs.len);246 atom.rel_num = @intCast(relocs.len);
247 try self.relocs.appendUnalignedSlice(allocator, relocs);247 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 }
248 }251 }
249 },252 },
250 else => {},253 else => {},
...@@ -333,6 +336,7 @@ fn skipShdr(self: *Object, index: u32, elf_file: *Elf) bool {...@@ -333,6 +336,7 @@ fn skipShdr(self: *Object, index: u32, elf_file: *Elf) bool {
333 if (mem.startsWith(u8, name, ".note")) break :blk true;336 if (mem.startsWith(u8, name, ".note")) break :blk true;
334 if (mem.startsWith(u8, name, ".comment")) break :blk true;337 if (mem.startsWith(u8, name, ".comment")) break :blk true;
335 if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true;338 if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true;
339 if (mem.startsWith(u8, name, ".riscv.attributes")) break :blk true; // TODO: riscv attributes
336 if (comp.config.debug_format == .strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and340 if (comp.config.debug_format == .strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and
337 mem.startsWith(u8, name, ".debug")) break :blk true;341 mem.startsWith(u8, name, ".debug")) break :blk true;
338 break :blk false;342 break :blk false;
...@@ -381,12 +385,15 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:...@@ -381,12 +385,15 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:
381 defer allocator.free(relocs);385 defer allocator.free(relocs);
382 const rel_start = @as(u32, @intCast(self.relocs.items.len));386 const rel_start = @as(u32, @intCast(self.relocs.items.len));
383 try self.relocs.appendUnalignedSlice(allocator, relocs);387 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 }
384 const fdes_start = self.fdes.items.len;391 const fdes_start = self.fdes.items.len;
385 const cies_start = self.cies.items.len;392 const cies_start = self.cies.items.len;
386393
387 var it = eh_frame.Iterator{ .data = raw };394 var it = eh_frame.Iterator{ .data = raw };
388 while (try it.next()) |rec| {395 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);
390 switch (rec.tag) {397 switch (rec.tag) {
391 .cie => try self.cies.append(allocator, .{398 .cie => try self.cies.append(allocator, .{
392 .offset = data_start + rec.offset,399 .offset = data_start + rec.offset,
...@@ -449,8 +456,18 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:...@@ -449,8 +456,18 @@ fn parseEhFrame(self: *Object, allocator: Allocator, handle: std.fs.File, shndx:
449 }456 }
450}457}
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
452fn filterRelocs(469fn filterRelocs(
453 relocs: []align(1) const elf.Elf64_Rela,470 relocs: []const elf.Elf64_Rela,
454 start: u64,471 start: u64,
455 len: u64,472 len: u64,
456) struct { start: u64, len: u64 } {473) struct { start: u64, len: u64 } {
...@@ -832,7 +849,8 @@ pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void {...@@ -832,7 +849,8 @@ pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void {
832 if (local.atom(elf_file)) |atom| if (!atom.flags.alive) continue;849 if (local.atom(elf_file)) |atom| if (!atom.flags.alive) continue;
833 const esym = local.elfSym(elf_file);850 const esym = local.elfSym(elf_file);
834 switch (esym.st_type()) {851 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,
836 else => {},854 else => {},
837 }855 }
838 local.flags.output_symtab = true;856 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:...@@ -317,7 +317,9 @@ fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file:
317 });317 });
318318
319 switch (cpu_arch) {319 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..]),
321 else => return error.UnsupportedCpuArch,323 else => return error.UnsupportedCpuArch,
322 }324 }
323}325}
...@@ -325,6 +327,8 @@ fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file:...@@ -325,6 +327,8 @@ fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file:
325pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {327pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
326 relocs_log.debug("{x}: .eh_frame", .{elf_file.shdrs.items[elf_file.eh_frame_section_index.?].sh_addr});328 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
328 for (elf_file.objects.items) |index| {332 for (elf_file.objects.items) |index| {
329 const object = elf_file.file(index).?.object;333 const object = elf_file.file(index).?.object;
330334
...@@ -335,7 +339,10 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {...@@ -335,7 +339,10 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
335339
336 for (cie.relocs(elf_file)) |rel| {340 for (cie.relocs(elf_file)) |rel| {
337 const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);341 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 };
339 }346 }
340347
341 try writer.writeAll(contents);348 try writer.writeAll(contents);
...@@ -359,7 +366,10 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {...@@ -359,7 +366,10 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
359366
360 for (fde.relocs(elf_file)) |rel| {367 for (fde.relocs(elf_file)) |rel| {
361 const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);368 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 };
363 }373 }
364374
365 try writer.writeAll(contents);375 try writer.writeAll(contents);
...@@ -367,6 +377,8 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {...@@ -367,6 +377,8 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
367 }377 }
368378
369 try writer.writeInt(u32, 0, .little);379 try writer.writeInt(u32, 0, .little);
380
381 if (has_reloc_errors) return error.RelocFailure;
370}382}
371383
372pub fn writeEhFrameObject(elf_file: *Elf, writer: anytype) !void {384pub fn writeEhFrameObject(elf_file: *Elf, writer: anytype) !void {
...@@ -540,18 +552,53 @@ const EH_PE = struct {...@@ -540,18 +552,53 @@ const EH_PE = struct {
540};552};
541553
542const x86_64 = struct {554const 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 {
544 const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());556 const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type());
545 switch (r_type) {557 switch (r_type) {
558 .NONE => {},
546 .@"32" => std.mem.writeInt(i32, data[0..4], @as(i32, @truncate(target)), .little),559 .@"32" => std.mem.writeInt(i32, data[0..4], @as(i32, @truncate(target)), .little),
547 .@"64" => std.mem.writeInt(i64, data[0..8], target, .little),560 .@"64" => std.mem.writeInt(i64, data[0..8], target, .little),
548 .PC32 => std.mem.writeInt(i32, data[0..4], @as(i32, @intCast(target - source)), .little),561 .PC32 => std.mem.writeInt(i32, data[0..4], @as(i32, @intCast(target - source)), .little),
549 .PC64 => std.mem.writeInt(i64, data[0..8], target - source, .little),562 .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),
551 }577 }
552 }578 }
553};579};
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
555const std = @import("std");602const std = @import("std");
556const assert = std.debug.assert;603const assert = std.debug.assert;
557const elf = std.elf;604const elf = std.elf;
src/link/Elf/relocation.zig+11-6
...@@ -1,4 +1,6 @@...@@ -1,4 +1,6 @@
1pub const Kind = enum {1pub const Kind = enum {
2 none,
3 other,
2 abs,4 abs,
3 copy,5 copy,
4 rel,6 rel,
...@@ -13,23 +15,24 @@ pub const Kind = enum {...@@ -13,23 +15,24 @@ pub const Kind = enum {
1315
14fn Table(comptime len: comptime_int, comptime RelType: type, comptime mapping: [len]struct { Kind, RelType }) type {16fn Table(comptime len: comptime_int, comptime RelType: type, comptime mapping: [len]struct { Kind, RelType }) type {
15 return struct {17 return struct {
16 fn decode(r_type: u32) ?Kind {18 fn decode(r_type: u32) Kind {
17 inline for (mapping) |entry| {19 inline for (mapping) |entry| {
18 if (@intFromEnum(entry[1]) == r_type) return entry[0];20 if (@intFromEnum(entry[1]) == r_type) return entry[0];
19 }21 }
20 return null;22 return .other;
21 }23 }
2224
23 fn encode(comptime kind: Kind) u32 {25 fn encode(comptime kind: Kind) u32 {
24 inline for (mapping) |entry| {26 inline for (mapping) |entry| {
25 if (entry[0] == kind) return @intFromEnum(entry[1]);27 if (entry[0] == kind) return @intFromEnum(entry[1]);
26 }28 }
27 unreachable;29 @panic("encoding .other is ambiguous");
28 }30 }
29 };31 };
30}32}
3133
32const x86_64_relocs = Table(10, elf.R_X86_64, .{34const x86_64_relocs = Table(11, elf.R_X86_64, .{
35 .{ .none, .NONE },
33 .{ .abs, .@"64" },36 .{ .abs, .@"64" },
34 .{ .copy, .COPY },37 .{ .copy, .COPY },
35 .{ .rel, .RELATIVE },38 .{ .rel, .RELATIVE },
...@@ -42,7 +45,8 @@ const x86_64_relocs = Table(10, elf.R_X86_64, .{...@@ -42,7 +45,8 @@ const x86_64_relocs = Table(10, elf.R_X86_64, .{
42 .{ .tlsdesc, .TLSDESC },45 .{ .tlsdesc, .TLSDESC },
43});46});
4447
45const aarch64_relocs = Table(10, elf.R_AARCH64, .{48const aarch64_relocs = Table(11, elf.R_AARCH64, .{
49 .{ .none, .NONE },
46 .{ .abs, .ABS64 },50 .{ .abs, .ABS64 },
47 .{ .copy, .COPY },51 .{ .copy, .COPY },
48 .{ .rel, .RELATIVE },52 .{ .rel, .RELATIVE },
...@@ -55,7 +59,8 @@ const aarch64_relocs = Table(10, elf.R_AARCH64, .{...@@ -55,7 +59,8 @@ const aarch64_relocs = Table(10, elf.R_AARCH64, .{
55 .{ .tlsdesc, .TLSDESC },59 .{ .tlsdesc, .TLSDESC },
56});60});
5761
58const riscv64_relocs = Table(10, elf.R_RISCV, .{62const riscv64_relocs = Table(11, elf.R_RISCV, .{
63 .{ .none, .NONE },
59 .{ .abs, .@"64" },64 .{ .abs, .@"64" },
60 .{ .copy, .COPY },65 .{ .copy, .COPY },
61 .{ .rel, .RELATIVE },66 .{ .rel, .RELATIVE },
src/link/MachO/Atom.zig+10-60
...@@ -699,14 +699,7 @@ fn resolveRelocInner(...@@ -699,14 +699,7 @@ fn resolveRelocInner(
699 const S_: i64 = @intCast(thunk.getTargetAddress(rel.target, macho_file));699 const S_: i64 = @intCast(thunk.getTargetAddress(rel.target, macho_file));
700 break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow;700 break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow;
701 };701 };
702 var inst = aarch64.Instruction{702 try aarch64.writeBranchImm(disp, code[rel_offset..][0..4]);
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);
710 },703 },
711 else => unreachable,704 else => unreachable,
712 }705 }
...@@ -776,16 +769,8 @@ fn resolveRelocInner(...@@ -776,16 +769,8 @@ fn resolveRelocInner(
776 };769 };
777 break :target math.cast(u64, target) orelse return error.Overflow;770 break :target math.cast(u64, target) orelse return error.Overflow;
778 };771 };
779 const pages = @as(u21, @bitCast(try Relocation.calcNumberOfPages(source, target)));772 const pages = @as(u21, @bitCast(try aarch64.calcNumberOfPages(source, target)));
780 var inst = aarch64.Instruction{773 try aarch64.writePages(pages, code[rel_offset..][0..4]);
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);
789 },774 },
790775
791 .pageoff => {776 .pageoff => {
...@@ -794,35 +779,8 @@ fn resolveRelocInner(...@@ -794,35 +779,8 @@ fn resolveRelocInner(
794 assert(!rel.meta.pcrel);779 assert(!rel.meta.pcrel);
795 const target = math.cast(u64, S + A) orelse return error.Overflow;780 const target = math.cast(u64, S + A) orelse return error.Overflow;
796 const inst_code = code[rel_offset..][0..4];781 const inst_code = code[rel_offset..][0..4];
797 if (Relocation.isArithmeticOp(inst_code)) {782 const kind = aarch64.classifyInst(inst_code);
798 const off = try Relocation.calcPageOffset(target, .arithmetic);783 try aarch64.writePageOffset(kind, target, inst_code);
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 }
826 },784 },
827785
828 .got_load_pageoff => {786 .got_load_pageoff => {
...@@ -830,15 +788,7 @@ fn resolveRelocInner(...@@ -830,15 +788,7 @@ fn resolveRelocInner(
830 assert(rel.meta.length == 2);788 assert(rel.meta.length == 2);
831 assert(!rel.meta.pcrel);789 assert(!rel.meta.pcrel);
832 const target = math.cast(u64, G + A) orelse return error.Overflow;790 const target = math.cast(u64, G + A) orelse return error.Overflow;
833 const off = try Relocation.calcPageOffset(target, .load_store_64);791 try aarch64.writePageOffset(.load_store_64, target, code[rel_offset..][0..4]);
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);
842 },792 },
843793
844 .tlvp_pageoff => {794 .tlvp_pageoff => {
...@@ -863,7 +813,7 @@ fn resolveRelocInner(...@@ -863,7 +813,7 @@ fn resolveRelocInner(
863813
864 const inst_code = code[rel_offset..][0..4];814 const inst_code = code[rel_offset..][0..4];
865 const reg_info: RegInfo = blk: {815 const reg_info: RegInfo = blk: {
866 if (Relocation.isArithmeticOp(inst_code)) {816 if (aarch64.isArithmeticOp(inst_code)) {
867 const inst = mem.bytesToValue(std.meta.TagPayload(817 const inst = mem.bytesToValue(std.meta.TagPayload(
868 aarch64.Instruction,818 aarch64.Instruction,
869 aarch64.Instruction.add_subtract_immediate,819 aarch64.Instruction.add_subtract_immediate,
...@@ -890,7 +840,7 @@ fn resolveRelocInner(...@@ -890,7 +840,7 @@ fn resolveRelocInner(
890 .load_store_register = .{840 .load_store_register = .{
891 .rt = reg_info.rd,841 .rt = reg_info.rd,
892 .rn = reg_info.rn,842 .rn = reg_info.rn,
893 .offset = try Relocation.calcPageOffset(target, .load_store_64),843 .offset = try aarch64.calcPageOffset(.load_store_64, target),
894 .opc = 0b01,844 .opc = 0b01,
895 .op1 = 0b01,845 .op1 = 0b01,
896 .v = 0,846 .v = 0,
...@@ -900,7 +850,7 @@ fn resolveRelocInner(...@@ -900,7 +850,7 @@ fn resolveRelocInner(
900 .add_subtract_immediate = .{850 .add_subtract_immediate = .{
901 .rd = reg_info.rd,851 .rd = reg_info.rd,
902 .rn = reg_info.rn,852 .rn = reg_info.rn,
903 .imm12 = try Relocation.calcPageOffset(target, .arithmetic),853 .imm12 = try aarch64.calcPageOffset(.arithmetic, target),
904 .sh = 0,854 .sh = 0,
905 .s = 0,855 .s = 0,
906 .op = 0,856 .op = 0,
...@@ -1183,7 +1133,7 @@ pub const Loc = struct {...@@ -1183,7 +1133,7 @@ pub const Loc = struct {
11831133
1184pub const Alignment = @import("../../InternPool.zig").Alignment;1134pub const Alignment = @import("../../InternPool.zig").Alignment;
11851135
1186const aarch64 = @import("../../arch/aarch64/bits.zig");1136const aarch64 = @import("../aarch64.zig");
1187const assert = std.debug.assert;1137const assert = std.debug.assert;
1188const bind = @import("dyld_info/bind.zig");1138const bind = @import("dyld_info/bind.zig");
1189const macho = std.macho;1139const macho = std.macho;
src/link/MachO/Relocation.zig-32
...@@ -60,38 +60,6 @@ pub fn lessThan(ctx: void, lhs: Relocation, rhs: Relocation) bool {...@@ -60,38 +60,6 @@ pub fn lessThan(ctx: void, lhs: Relocation, rhs: Relocation) bool {
60 return lhs.offset < rhs.offset;60 return lhs.offset < rhs.offset;
61}61}
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
95pub const Type = enum {63pub const Type = enum {
96 // x86_6464 // x86_64
97 /// RIP-relative displacement (X86_64_RELOC_SIGNED)65 /// RIP-relative displacement (X86_64_RELOC_SIGNED)
src/link/MachO/synthetic.zig+11-12
...@@ -267,9 +267,9 @@ pub const StubsSection = struct {...@@ -267,9 +267,9 @@ pub const StubsSection = struct {
267 },267 },
268 .aarch64 => {268 .aarch64 => {
269 // TODO relax if possible269 // TODO relax if possible
270 const pages = try Relocation.calcNumberOfPages(source, target);270 const pages = try aarch64.calcNumberOfPages(source, target);
271 try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);271 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);
273 try writer.writeInt(273 try writer.writeInt(
274 u32,274 u32,
275 aarch64.Instruction.ldr(.x16, .x16, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(),275 aarch64.Instruction.ldr(.x16, .x16, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(),
...@@ -411,9 +411,9 @@ pub const StubsHelperSection = struct {...@@ -411,9 +411,9 @@ pub const StubsHelperSection = struct {
411 .aarch64 => {411 .aarch64 => {
412 {412 {
413 // TODO relax if possible413 // 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);
415 try writer.writeInt(u32, aarch64.Instruction.adrp(.x17, pages).toU32(), .little);415 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);
417 try writer.writeInt(u32, aarch64.Instruction.add(.x17, .x17, off, false).toU32(), .little);417 try writer.writeInt(u32, aarch64.Instruction.add(.x17, .x17, off, false).toU32(), .little);
418 }418 }
419 try writer.writeInt(u32, aarch64.Instruction.stp(419 try writer.writeInt(u32, aarch64.Instruction.stp(
...@@ -424,9 +424,9 @@ pub const StubsHelperSection = struct {...@@ -424,9 +424,9 @@ pub const StubsHelperSection = struct {
424 ).toU32(), .little);424 ).toU32(), .little);
425 {425 {
426 // TODO relax if possible426 // 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);
428 try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);428 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);
430 try writer.writeInt(u32, aarch64.Instruction.ldr(430 try writer.writeInt(u32, aarch64.Instruction.ldr(
431 .x16,431 .x16,
432 .x16,432 .x16,
...@@ -679,9 +679,9 @@ pub const ObjcStubsSection = struct {...@@ -679,9 +679,9 @@ pub const ObjcStubsSection = struct {
679 {679 {
680 const target = sym.getObjcSelrefsAddress(macho_file);680 const target = sym.getObjcSelrefsAddress(macho_file);
681 const source = addr;681 const source = addr;
682 const pages = try Relocation.calcNumberOfPages(source, target);682 const pages = try aarch64.calcNumberOfPages(source, target);
683 try writer.writeInt(u32, aarch64.Instruction.adrp(.x1, pages).toU32(), .little);683 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);
685 try writer.writeInt(685 try writer.writeInt(
686 u32,686 u32,
687 aarch64.Instruction.ldr(.x1, .x1, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(),687 aarch64.Instruction.ldr(.x1, .x1, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(),
...@@ -692,9 +692,9 @@ pub const ObjcStubsSection = struct {...@@ -692,9 +692,9 @@ pub const ObjcStubsSection = struct {
692 const target_sym = macho_file.getSymbol(macho_file.objc_msg_send_index.?);692 const target_sym = macho_file.getSymbol(macho_file.objc_msg_send_index.?);
693 const target = target_sym.getGotAddress(macho_file);693 const target = target_sym.getGotAddress(macho_file);
694 const source = addr + 2 * @sizeOf(u32);694 const source = addr + 2 * @sizeOf(u32);
695 const pages = try Relocation.calcNumberOfPages(source, target);695 const pages = try aarch64.calcNumberOfPages(source, target);
696 try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);696 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);
698 try writer.writeInt(698 try writer.writeInt(
699 u32,699 u32,
700 aarch64.Instruction.ldr(.x16, .x16, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(),700 aarch64.Instruction.ldr(.x16, .x16, aarch64.Instruction.LoadStoreOffset.imm(off)).toU32(),
...@@ -778,7 +778,7 @@ pub const WeakBindSection = bind.WeakBind;...@@ -778,7 +778,7 @@ pub const WeakBindSection = bind.WeakBind;
778pub const LazyBindSection = bind.LazyBind;778pub const LazyBindSection = bind.LazyBind;
779pub const ExportTrieSection = Trie;779pub const ExportTrieSection = Trie;
780780
781const aarch64 = @import("../../arch/aarch64/bits.zig");781const aarch64 = @import("../aarch64.zig");
782const assert = std.debug.assert;782const assert = std.debug.assert;
783const bind = @import("dyld_info/bind.zig");783const bind = @import("dyld_info/bind.zig");
784const math = std.math;784const math = std.math;
...@@ -788,6 +788,5 @@ const trace = @import("../../tracy.zig").trace;...@@ -788,6 +788,5 @@ const trace = @import("../../tracy.zig").trace;
788const Allocator = std.mem.Allocator;788const Allocator = std.mem.Allocator;
789const MachO = @import("../MachO.zig");789const MachO = @import("../MachO.zig");
790const Rebase = @import("dyld_info/Rebase.zig");790const Rebase = @import("dyld_info/Rebase.zig");
791const Relocation = @import("Relocation.zig");
792const Symbol = @import("Symbol.zig");791const Symbol = @import("Symbol.zig");
793const Trie = @import("dyld_info/Trie.zig");792const Trie = @import("dyld_info/Trie.zig");
src/link/MachO/thunks.zig+3-3
...@@ -99,9 +99,9 @@ pub const Thunk = struct {...@@ -99,9 +99,9 @@ pub const Thunk = struct {
99 const sym = macho_file.getSymbol(sym_index);99 const sym = macho_file.getSymbol(sym_index);
100 const saddr = thunk.getAddress(macho_file) + i * trampoline_size;100 const saddr = thunk.getAddress(macho_file) + i * trampoline_size;
101 const taddr = sym.getAddress(.{}, macho_file);101 const taddr = sym.getAddress(.{}, macho_file);
102 const pages = try Relocation.calcNumberOfPages(saddr, taddr);102 const pages = try aarch64.calcNumberOfPages(saddr, taddr);
103 try writer.writeInt(u32, aarch64.Instruction.adrp(.x16, pages).toU32(), .little);103 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);
105 try writer.writeInt(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32(), .little);105 try writer.writeInt(u32, aarch64.Instruction.add(.x16, .x16, off, false).toU32(), .little);
106 try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little);106 try writer.writeInt(u32, aarch64.Instruction.br(.x16).toU32(), .little);
107 }107 }
...@@ -164,7 +164,7 @@ const max_distance = (1 << (jump_bits - 1));...@@ -164,7 +164,7 @@ const max_distance = (1 << (jump_bits - 1));
164/// and assume margin to be 5MiB.164/// and assume margin to be 5MiB.
165const max_allowed_distance = max_distance - 0x500_000;165const max_allowed_distance = max_distance - 0x500_000;
166166
167const aarch64 = @import("../../arch/aarch64/bits.zig");167const aarch64 = @import("../aarch64.zig");
168const assert = std.debug.assert;168const assert = std.debug.assert;
169const log = std.log.scoped(.link);169const log = std.log.scoped(.link);
170const macho = std.macho;170const 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 {...@@ -10,124 +10,141 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
10 .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs10 .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs
11 .os_tag = .linux,11 .os_tag = .linux,
12 });12 });
13 const musl_target = b.resolveTargetQuery(.{13 const x86_64_musl = b.resolveTargetQuery(.{
14 .cpu_arch = .x86_64,14 .cpu_arch = .x86_64,
15 .os_tag = .linux,15 .os_tag = .linux,
16 .abi = .musl,16 .abi = .musl,
17 });17 });
18 const glibc_target = b.resolveTargetQuery(.{18 const x86_64_gnu = b.resolveTargetQuery(.{
19 .cpu_arch = .x86_64,19 .cpu_arch = .x86_64,
20 .os_tag = .linux,20 .os_tag = .linux,
21 .abi = .gnu,21 .abi = .gnu,
22 });22 });
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
24 // Exercise linker in -r mode35 // Exercise linker in -r mode
25 elf_step.dependOn(testEmitRelocatable(b, .{ .use_llvm = false, .target = musl_target }));36 elf_step.dependOn(testEmitRelocatable(b, .{ .use_llvm = false, .target = x86_64_musl }));
26 elf_step.dependOn(testEmitRelocatable(b, .{ .target = musl_target }));37 elf_step.dependOn(testEmitRelocatable(b, .{ .target = x86_64_musl }));
27 elf_step.dependOn(testRelocatableArchive(b, .{ .target = musl_target }));38 elf_step.dependOn(testRelocatableArchive(b, .{ .target = x86_64_musl }));
28 elf_step.dependOn(testRelocatableEhFrame(b, .{ .target = musl_target }));39 elf_step.dependOn(testRelocatableEhFrame(b, .{ .target = x86_64_musl }));
29 elf_step.dependOn(testRelocatableNoEhFrame(b, .{ .target = musl_target }));40 elf_step.dependOn(testRelocatableNoEhFrame(b, .{ .target = x86_64_musl }));
3041
31 // Exercise linker in ar mode42 // Exercise linker in ar mode
32 elf_step.dependOn(testEmitStaticLib(b, .{ .target = musl_target }));43 elf_step.dependOn(testEmitStaticLib(b, .{ .target = x86_64_musl }));
33 elf_step.dependOn(testEmitStaticLibZig(b, .{ .use_llvm = false, .target = musl_target }));44 elf_step.dependOn(testEmitStaticLibZig(b, .{ .use_llvm = false, .target = x86_64_musl }));
3445
35 // Exercise linker with self-hosted backend (no LLVM)46 // Exercise linker with self-hosted backend (no LLVM)
36 elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target }));47 elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target }));
37 elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target }));48 elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target }));
38 elf_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = default_target }));49 elf_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = default_target }));
39 elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target }));50 elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target }));
40 elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = glibc_target }));51 elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = x86_64_gnu }));
41 elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = musl_target }));52 elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = x86_64_musl }));
4253
43 // Exercise linker with LLVM backend54 // Exercise linker with LLVM backend
44 // musl tests55 // musl tests
45 elf_step.dependOn(testAbsSymbols(b, .{ .target = musl_target }));56 elf_step.dependOn(testAbsSymbols(b, .{ .target = x86_64_musl }));
46 elf_step.dependOn(testCommonSymbols(b, .{ .target = musl_target }));57 elf_step.dependOn(testCommonSymbols(b, .{ .target = x86_64_musl }));
47 elf_step.dependOn(testCommonSymbolsInArchive(b, .{ .target = musl_target }));58 elf_step.dependOn(testCommonSymbolsInArchive(b, .{ .target = x86_64_musl }));
48 elf_step.dependOn(testEmptyObject(b, .{ .target = musl_target }));59 elf_step.dependOn(testEmptyObject(b, .{ .target = x86_64_musl }));
49 elf_step.dependOn(testEntryPoint(b, .{ .target = musl_target }));60 elf_step.dependOn(testEntryPoint(b, .{ .target = x86_64_musl }));
50 elf_step.dependOn(testGcSections(b, .{ .target = musl_target }));61 elf_step.dependOn(testGcSections(b, .{ .target = x86_64_musl }));
51 elf_step.dependOn(testImageBase(b, .{ .target = musl_target }));62 elf_step.dependOn(testImageBase(b, .{ .target = x86_64_musl }));
52 elf_step.dependOn(testInitArrayOrder(b, .{ .target = musl_target }));63 elf_step.dependOn(testInitArrayOrder(b, .{ .target = x86_64_musl }));
53 elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = musl_target }));64 elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = x86_64_musl }));
54 // https://github.com/ziglang/zig/issues/1744965 // https://github.com/ziglang/zig/issues/17449
55 // elf_step.dependOn(testLargeBss(b, .{ .target = musl_target }));66 // elf_step.dependOn(testLargeBss(b, .{ .target = x86_64_musl }));
56 elf_step.dependOn(testLinkingC(b, .{ .target = musl_target }));67 elf_step.dependOn(testLinkingC(b, .{ .target = x86_64_musl }));
57 elf_step.dependOn(testLinkingCpp(b, .{ .target = musl_target }));68 elf_step.dependOn(testLinkingCpp(b, .{ .target = x86_64_musl }));
58 elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target }));69 elf_step.dependOn(testLinkingZig(b, .{ .target = x86_64_musl }));
59 // https://github.com/ziglang/zig/issues/1745170 // https://github.com/ziglang/zig/issues/17451
60 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = musl_target }));71 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = x86_64_musl }));
61 elf_step.dependOn(testTlsStatic(b, .{ .target = musl_target }));72 elf_step.dependOn(testTlsStatic(b, .{ .target = x86_64_musl }));
62 elf_step.dependOn(testStrip(b, .{ .target = musl_target }));73 elf_step.dependOn(testStrip(b, .{ .target = x86_64_musl }));
6374
64 // glibc tests75 // glibc tests
65 elf_step.dependOn(testAsNeeded(b, .{ .target = glibc_target }));76 elf_step.dependOn(testAsNeeded(b, .{ .target = x86_64_gnu }));
66 // https://github.com/ziglang/zig/issues/1743077 // https://github.com/ziglang/zig/issues/17430
67 // elf_step.dependOn(testCanonicalPlt(b, .{ .target = glibc_target }));78 // elf_step.dependOn(testCanonicalPlt(b, .{ .target = x86_64_gnu }));
68 elf_step.dependOn(testCopyrel(b, .{ .target = glibc_target }));79 elf_step.dependOn(testCopyrel(b, .{ .target = x86_64_gnu }));
69 // https://github.com/ziglang/zig/issues/1743080 // 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 }));
71 // https://github.com/ziglang/zig/issues/1743082 // https://github.com/ziglang/zig/issues/17430
72 // elf_step.dependOn(testCopyrelAlignment(b, .{ .target = glibc_target }));83 // elf_step.dependOn(testCopyrelAlignment(b, .{ .target = x86_64_gnu }));
73 elf_step.dependOn(testDsoPlt(b, .{ .target = glibc_target }));84 elf_step.dependOn(testDsoPlt(b, .{ .target = x86_64_gnu }));
74 elf_step.dependOn(testDsoUndef(b, .{ .target = glibc_target }));85 elf_step.dependOn(testDsoUndef(b, .{ .target = x86_64_gnu }));
75 elf_step.dependOn(testExportDynamic(b, .{ .target = glibc_target }));86 elf_step.dependOn(testExportDynamic(b, .{ .target = x86_64_gnu }));
76 elf_step.dependOn(testExportSymbolsFromExe(b, .{ .target = glibc_target }));87 elf_step.dependOn(testExportSymbolsFromExe(b, .{ .target = x86_64_gnu }));
77 // https://github.com/ziglang/zig/issues/1743088 // https://github.com/ziglang/zig/issues/17430
78 // elf_step.dependOn(testFuncAddress(b, .{ .target = glibc_target }));89 // elf_step.dependOn(testFuncAddress(b, .{ .target = x86_64_gnu }));
79 elf_step.dependOn(testHiddenWeakUndef(b, .{ .target = glibc_target }));90 elf_step.dependOn(testHiddenWeakUndef(b, .{ .target = x86_64_gnu }));
80 elf_step.dependOn(testIFuncAlias(b, .{ .target = glibc_target }));91 elf_step.dependOn(testIFuncAlias(b, .{ .target = x86_64_gnu }));
81 // https://github.com/ziglang/zig/issues/1743092 // https://github.com/ziglang/zig/issues/17430
82 // elf_step.dependOn(testIFuncDlopen(b, .{ .target = glibc_target }));93 // elf_step.dependOn(testIFuncDlopen(b, .{ .target = x86_64_gnu }));
83 elf_step.dependOn(testIFuncDso(b, .{ .target = glibc_target }));94 elf_step.dependOn(testIFuncDso(b, .{ .target = x86_64_gnu }));
84 elf_step.dependOn(testIFuncDynamic(b, .{ .target = glibc_target }));95 elf_step.dependOn(testIFuncDynamic(b, .{ .target = x86_64_gnu }));
85 elf_step.dependOn(testIFuncExport(b, .{ .target = glibc_target }));96 elf_step.dependOn(testIFuncExport(b, .{ .target = x86_64_gnu }));
86 elf_step.dependOn(testIFuncFuncPtr(b, .{ .target = glibc_target }));97 elf_step.dependOn(testIFuncFuncPtr(b, .{ .target = x86_64_gnu }));
87 elf_step.dependOn(testIFuncNoPlt(b, .{ .target = glibc_target }));98 elf_step.dependOn(testIFuncNoPlt(b, .{ .target = x86_64_gnu }));
88 // https://github.com/ziglang/zig/issues/17430 ??99 // https://github.com/ziglang/zig/issues/17430 ??
89 // elf_step.dependOn(testIFuncStatic(b, .{ .target = glibc_target }));100 // elf_step.dependOn(testIFuncStatic(b, .{ .target = x86_64_gnu }));
90 // elf_step.dependOn(testIFuncStaticPie(b, .{ .target = glibc_target }));101 // elf_step.dependOn(testIFuncStaticPie(b, .{ .target = x86_64_gnu }));
91 elf_step.dependOn(testInitArrayOrder(b, .{ .target = glibc_target }));102 elf_step.dependOn(testInitArrayOrder(b, .{ .target = x86_64_gnu }));
92 elf_step.dependOn(testLargeAlignmentDso(b, .{ .target = glibc_target }));103 elf_step.dependOn(testLargeAlignmentDso(b, .{ .target = x86_64_gnu }));
93 elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = glibc_target }));104 elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = x86_64_gnu }));
94 elf_step.dependOn(testLargeBss(b, .{ .target = glibc_target }));105 elf_step.dependOn(testLargeBss(b, .{ .target = x86_64_gnu }));
95 elf_step.dependOn(testLinkOrder(b, .{ .target = glibc_target }));106 elf_step.dependOn(testLinkOrder(b, .{ .target = x86_64_gnu }));
96 elf_step.dependOn(testLdScript(b, .{ .target = glibc_target }));107 elf_step.dependOn(testLdScript(b, .{ .target = x86_64_gnu }));
97 elf_step.dependOn(testLdScriptPathError(b, .{ .target = glibc_target }));108 elf_step.dependOn(testLdScriptPathError(b, .{ .target = x86_64_gnu }));
98 elf_step.dependOn(testLdScriptAllowUndefinedVersion(b, .{ .target = glibc_target, .use_lld = true }));109 elf_step.dependOn(testLdScriptAllowUndefinedVersion(b, .{ .target = x86_64_gnu, .use_lld = true }));
99 elf_step.dependOn(testLdScriptDisallowUndefinedVersion(b, .{ .target = glibc_target, .use_lld = true }));110 elf_step.dependOn(testLdScriptDisallowUndefinedVersion(b, .{ .target = x86_64_gnu, .use_lld = true }));
100 elf_step.dependOn(testMismatchedCpuArchitectureError(b, .{ .target = glibc_target }));111 elf_step.dependOn(testMismatchedCpuArchitectureError(b, .{ .target = x86_64_gnu }));
101 // https://github.com/ziglang/zig/issues/17451112 // https://github.com/ziglang/zig/issues/17451
102 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = glibc_target }));113 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = x86_64_gnu }));
103 elf_step.dependOn(testPie(b, .{ .target = glibc_target }));114 elf_step.dependOn(testPie(b, .{ .target = x86_64_gnu }));
104 elf_step.dependOn(testPltGot(b, .{ .target = glibc_target }));115 elf_step.dependOn(testPltGot(b, .{ .target = x86_64_gnu }));
105 elf_step.dependOn(testPreinitArray(b, .{ .target = glibc_target }));116 elf_step.dependOn(testPreinitArray(b, .{ .target = x86_64_gnu }));
106 elf_step.dependOn(testSharedAbsSymbol(b, .{ .target = glibc_target }));117 elf_step.dependOn(testSharedAbsSymbol(b, .{ .target = x86_64_gnu }));
107 elf_step.dependOn(testTlsDfStaticTls(b, .{ .target = glibc_target }));118 elf_step.dependOn(testTlsDfStaticTls(b, .{ .target = x86_64_gnu }));
108 elf_step.dependOn(testTlsDso(b, .{ .target = glibc_target }));119 elf_step.dependOn(testTlsDso(b, .{ .target = x86_64_gnu }));
109 elf_step.dependOn(testTlsGd(b, .{ .target = glibc_target }));120 elf_step.dependOn(testTlsGd(b, .{ .target = x86_64_gnu }));
110 elf_step.dependOn(testTlsGdNoPlt(b, .{ .target = glibc_target }));121 elf_step.dependOn(testTlsGdNoPlt(b, .{ .target = x86_64_gnu }));
111 elf_step.dependOn(testTlsGdToIe(b, .{ .target = glibc_target }));122 elf_step.dependOn(testTlsGdToIe(b, .{ .target = x86_64_gnu }));
112 elf_step.dependOn(testTlsIe(b, .{ .target = glibc_target }));123 elf_step.dependOn(testTlsIe(b, .{ .target = x86_64_gnu }));
113 elf_step.dependOn(testTlsLargeAlignment(b, .{ .target = glibc_target }));124 elf_step.dependOn(testTlsLargeAlignment(b, .{ .target = x86_64_gnu }));
114 elf_step.dependOn(testTlsLargeTbss(b, .{ .target = glibc_target }));125 elf_step.dependOn(testTlsLargeTbss(b, .{ .target = x86_64_gnu }));
115 elf_step.dependOn(testTlsLargeStaticImage(b, .{ .target = glibc_target }));126 elf_step.dependOn(testTlsLargeStaticImage(b, .{ .target = x86_64_gnu }));
116 elf_step.dependOn(testTlsLd(b, .{ .target = glibc_target }));127 elf_step.dependOn(testTlsLd(b, .{ .target = x86_64_gnu }));
117 elf_step.dependOn(testTlsLdDso(b, .{ .target = glibc_target }));128 elf_step.dependOn(testTlsLdDso(b, .{ .target = x86_64_gnu }));
118 elf_step.dependOn(testTlsLdNoPlt(b, .{ .target = glibc_target }));129 elf_step.dependOn(testTlsLdNoPlt(b, .{ .target = x86_64_gnu }));
119 // https://github.com/ziglang/zig/issues/17430130 // https://github.com/ziglang/zig/issues/17430
120 // elf_step.dependOn(testTlsNoPic(b, .{ .target = glibc_target }));131 // elf_step.dependOn(testTlsNoPic(b, .{ .target = x86_64_gnu }));
121 elf_step.dependOn(testTlsOffsetAlignment(b, .{ .target = glibc_target }));132 elf_step.dependOn(testTlsOffsetAlignment(b, .{ .target = x86_64_gnu }));
122 elf_step.dependOn(testTlsPic(b, .{ .target = glibc_target }));133 elf_step.dependOn(testTlsPic(b, .{ .target = x86_64_gnu }));
123 elf_step.dependOn(testTlsSmallAlignment(b, .{ .target = glibc_target }));134 elf_step.dependOn(testTlsSmallAlignment(b, .{ .target = x86_64_gnu }));
124 elf_step.dependOn(testUnknownFileTypeError(b, .{ .target = glibc_target }));135 elf_step.dependOn(testUnknownFileTypeError(b, .{ .target = x86_64_gnu }));
125 elf_step.dependOn(testUnresolvedError(b, .{ .target = glibc_target }));136 elf_step.dependOn(testUnresolvedError(b, .{ .target = x86_64_gnu }));
126 elf_step.dependOn(testWeakExports(b, .{ .target = glibc_target }));137 elf_step.dependOn(testWeakExports(b, .{ .target = x86_64_gnu }));
127 elf_step.dependOn(testWeakUndefsDso(b, .{ .target = glibc_target }));138 elf_step.dependOn(testWeakUndefsDso(b, .{ .target = x86_64_gnu }));
128 elf_step.dependOn(testZNow(b, .{ .target = glibc_target }));139 elf_step.dependOn(testZNow(b, .{ .target = x86_64_gnu }));
129 elf_step.dependOn(testZStackSize(b, .{ .target = glibc_target }));140 elf_step.dependOn(testZStackSize(b, .{ .target = x86_64_gnu }));
130 elf_step.dependOn(testZText(b, .{ .target = glibc_target }));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
132 return elf_step;149 return elf_step;
133}150}