| author | |
| committer | |
| log | 7230b68b350b16c637e84f3ff224be24d23214ce |
| tree | 442e6a70d71db0787c993723a39379ab9562851a |
| parent | 256c5934bfc19d3b8a1cf01bc07c9ad86a6c6524 |
| parent | f8989a9c6948f6317799defd6de8bb7e2833f8cb |
| signature |
elf: add basic aarch64 and riscv64 support14 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" |
| 12 | PREFIX="$HOME/deps/$CACHE_BASENAME" | 12 | PREFIX="$HOME/deps/$CACHE_BASENAME" |
| 13 | ZIG="$PREFIX/bin/zig" | 13 | ZIG="$PREFIX/bin/zig" |
| 14 | 14 | ||
| 15 | export PATH="$HOME/deps/wasmtime-v10.0.2-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-6.1.0.1/bin:$PATH" | 15 | export PATH="$HOME/deps/wasmtime-v10.0.2-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-8.2.1/bin:$PATH" |
| 16 | 16 | ||
| 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" |
| 12 | PREFIX="$HOME/deps/$CACHE_BASENAME" | 12 | PREFIX="$HOME/deps/$CACHE_BASENAME" |
| 13 | ZIG="$PREFIX/bin/zig" | 13 | ZIG="$PREFIX/bin/zig" |
| 14 | 14 | ||
| 15 | export PATH="$HOME/deps/wasmtime-v10.0.2-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-6.1.0.1/bin:$PATH" | 15 | export PATH="$HOME/deps/wasmtime-v10.0.2-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-8.2.1/bin:$PATH" |
| 16 | 16 | ||
| 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, |
| 189 | dso_handle_index: ?Symbol.Index = null, | 189 | dso_handle_index: ?Symbol.Index = null, |
| 190 | rela_iplt_start_index: ?Symbol.Index = null, | 190 | rela_iplt_start_index: ?Symbol.Index = null, |
| 191 | rela_iplt_end_index: ?Symbol.Index = null, | 191 | rela_iplt_end_index: ?Symbol.Index = null, |
| 192 | global_pointer_index: ?Symbol.Index = null, | ||
| 192 | start_stop_indexes: std.ArrayListUnmanaged(u32) = .{}, | 193 | start_stop_indexes: std.ArrayListUnmanaged(u32) = .{}, |
| 193 | 194 | ||
| 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 resolve | 1344 | // 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 | // TODO | 1358 | 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 | } |
| 1369 | 1370 | ||
| 1370 | try self.writePhdrTable(); | 1371 | try self.writePhdrTable(); |
| 1371 | try self.writeShdrTable(); | 1372 | try self.writeShdrTable(); |
| 1372 | try self.writeAtoms(); | 1373 | try self.writeAtoms(); |
| 1373 | 1374 | 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 | }; | ||
| 1381 | 1375 | ||
| 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); |
| 2050 | 2044 | ||
| 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 | } |
| 2060 | 2057 | ||
| 2061 | try self.reportUndefinedSymbols(&undefs); | 2058 | try self.reportUndefinedSymbols(&undefs); |
| 2062 | 2059 | ||
| 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 | } |
| 3097 | 3096 | ||
| 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 | } |
| 3100 | 3103 | ||
| ... | @@ -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 | } |
| 3226 | 3242 | ||
| 3227 | fn checkDuplicates(self: *Elf) !void { | 3243 | fn 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 | } |
| 4433 | 4449 | ||
| 4450 | var has_reloc_errors = false; | ||
| 4451 | |||
| 4434 | // TODO iterate over `output_sections` directly | 4452 | // 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 | else | 4511 | 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 | } |
| 4510 | 4525 | ||
| 4511 | try self.reportUndefinedSymbols(&undefs); | 4526 | try self.reportUndefinedSymbols(&undefs); |
| 4527 | |||
| 4528 | if (has_reloc_errors) return error.FlushFailure; | ||
| 4512 | } | 4529 | } |
| 4513 | 4530 | ||
| 4514 | pub fn updateSymtabSize(self: *Elf) !void { | 4531 | pub 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 | } |
| 4671 | 4695 | ||
| ... | @@ -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 | } |
| 5528 | 5552 | ||
| 5553 | pub 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 | |||
| 5529 | pub fn tpAddress(self: *Elf) u64 { | 5562 | pub 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 | } |
| 302 | 302 | ||
| 303 | pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela { | 303 | pub 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 | } |
| 396 | 396 | ||
| 397 | pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void { | 397 | pub 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 | } |
| 403 | 454 | ||
| 404 | fn scanReloc( | 455 | fn 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 | } |
| 556 | 607 | ||
| 557 | fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) error{OutOfMemory}!void { | 608 | fn 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 | } |
| 568 | 620 | ||
| 569 | fn reportTextRelocError( | 621 | fn 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 | } |
| 585 | 638 | ||
| 586 | fn reportPicError( | 639 | fn 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 | } |
| 603 | 657 | ||
| 604 | fn reportNoPicError( | 658 | fn 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 | } |
| 621 | 676 | ||
| 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 | } |
| 652 | 710 | ||
| 653 | pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void { | 711 | pub 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) }); |
| 655 | 713 | ||
| 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 | } |
| 661 | 795 | ||
| 662 | fn resolveDynAbsReloc( | 796 | fn 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 { |
| 761 | pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void { | 895 | pub 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) }); |
| 763 | 897 | ||
| 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 | } |
| 769 | 976 | ||
| 770 | pub fn format( | 977 | pub fn format( |
| ... | @@ -831,429 +1038,313 @@ pub const Flags = packed struct { | ... | @@ -831,429 +1038,313 @@ pub const Flags = packed struct { |
| 831 | }; | 1038 | }; |
| 832 | 1039 | ||
| 833 | const x86_64 = struct { | 1040 | const 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 | } | ||
| 865 | 1051 | ||
| 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; |
| 868 | 1054 | ||
| 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 | }, |
| 873 | 1059 | ||
| 874 | // While traversing relocations, mark symbols that require special handling such as | 1060 | .@"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 | }, | ||
| 880 | 1065 | ||
| 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 | }, | ||
| 886 | 1076 | ||
| 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 | }, | ||
| 897 | 1084 | ||
| 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 | }, | ||
| 905 | 1088 | ||
| 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 | }, | ||
| 909 | 1091 | ||
| 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_addr | 1093 | // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a |
| 1094 | // We skip the next relocation. | ||
| 1095 | it.skip(1); | ||
| 1096 | } else if (!symbol.flags.import and is_dyn_lib) { | ||
| 1097 | symbol.flags.needs_gottp = true; | ||
| 1098 | it.skip(1); | ||
| 1099 | } else { | ||
| 1100 | symbol.flags.needs_tlsgd = true; | ||
| 1101 | } | ||
| 1102 | }, | ||
| 912 | 1103 | ||
| 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.a | 1105 | // 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 | }, | ||
| 924 | 1106 | ||
| 925 | .TLSLD => { | 1107 | if (is_static or !is_dyn_lib) { |
| 926 | // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr | 1108 | // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a |
| 1109 | // We skip the next relocation. | ||
| 1110 | it.skip(1); | ||
| 1111 | } else { | ||
| 1112 | elf_file.got.flags.needs_tlsld = true; | ||
| 1113 | } | ||
| 1114 | }, | ||
| 927 | 1115 | ||
| 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.a | 1117 | 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 | }, | ||
| 936 | 1126 | ||
| 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 | }, | ||
| 947 | 1133 | ||
| 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 | }, | ||
| 954 | 1139 | ||
| 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 | }, |
| 960 | 1155 | ||
| 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 | } |
| 982 | 1160 | ||
| 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; | ||
| 987 | 1173 | ||
| 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 | }); | ||
| 1039 | 1175 | ||
| 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; |
| 1041 | 1177 | ||
| 1042 | switch (r_type) { | 1178 | switch (r_type) { |
| 1043 | .NONE => unreachable, | 1179 | .NONE => unreachable, |
| 1044 | 1180 | ||
| 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 | }, |
| 1054 | 1190 | ||
| 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), |
| 1058 | 1194 | ||
| 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), |
| 1062 | 1198 | ||
| 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 | }, |
| 1071 | 1207 | ||
| 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 | }, |
| 1080 | 1216 | ||
| 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), |
| 1083 | 1219 | ||
| 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), |
| 1086 | 1222 | ||
| 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), |
| 1089 | 1225 | ||
| 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 | }, | ||
| 1109 | 1243 | ||
| 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 | }, | ||
| 1126 | 1259 | ||
| 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 | }, | ||
| 1136 | 1278 | ||
| 1137 | .TLSDESC_CALL => if (!target.flags.has_tlsdesc) { | 1279 | .TLSDESC_CALL => if (!target.flags.has_tlsdesc) { |
| 1138 | // call -> nop | 1280 | // call -> nop |
| 1139 | try cwriter.writeAll(&.{ 0x66, 0x90 }); | 1281 | try cwriter.writeAll(&.{ 0x66, 0x90 }); |
| 1140 | }, | 1282 | }, |
| 1141 | 1283 | ||
| 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 | }, |
| 1151 | 1293 | ||
| 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), |
| 1153 | 1295 | ||
| 1154 | else => |x| switch (@intFromEnum(x)) { | 1296 | else => |x| switch (@intFromEnum(x)) { |
| 1155 | // Zig custom relocations | 1297 | // 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), |
| 1158 | 1300 | ||
| 1159 | else => {}, | 1301 | else => try atom.reportUnhandledRelocError(rel, elf_file), |
| 1160 | }, | 1302 | }, |
| 1161 | } | ||
| 1162 | } | 1303 | } |
| 1163 | } | 1304 | } |
| 1164 | 1305 | ||
| 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(); |
| 1169 | 1320 | ||
| 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), |
| 1176 | 1327 | .@"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), |
| 1178 | 1329 | .@"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)); |
| 1185 | 1336 | 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 error | 1339 | 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 | } |
| 1254 | 1345 | ||
| 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 assembler | 1350 | // 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 assembler | 1354 | // 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 | } |
| 1272 | 1363 | ||
| 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 | } |
| 1284 | 1375 | ||
| 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 { |
| 1307 | 1398 | ||
| 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 | } |
| 1322 | 1414 | ||
| 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 { |
| 1360 | 1452 | ||
| 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 | } |
| 1390 | 1483 | ||
| 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 assembler | 1490 | // 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 | } |
| 1406 | 1499 | ||
| 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 | } |
| 1422 | 1515 | ||
| 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 | }; |
| 1487 | 1581 | ||
| 1582 | const 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 | |||
| 1773 | const 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 | |||
| 1977 | const ResolveArgs = struct { i64, i64, i64, i64, i64, i64, i64, i64 }; | ||
| 1978 | |||
| 1979 | const RelocError = error{ | ||
| 1980 | Overflow, | ||
| 1981 | OutOfMemory, | ||
| 1982 | NoSpaceLeft, | ||
| 1983 | RelocFailure, | ||
| 1984 | RelaxFailure, | ||
| 1985 | UnsupportedCpuArch, | ||
| 1986 | }; | ||
| 1987 | |||
| 1988 | const 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 | |||
| 1488 | const std = @import("std"); | 2011 | const std = @import("std"); |
| 1489 | const assert = std.debug.assert; | 2012 | const assert = std.debug.assert; |
| 1490 | const elf = std.elf; | 2013 | const elf = std.elf; |
| 1491 | const eh_frame = @import("eh_frame.zig"); | 2014 | const eh_frame = @import("eh_frame.zig"); |
| 1492 | const log = std.log.scoped(.link); | 2015 | const log = std.log.scoped(.link); |
| 2016 | const math = std.math; | ||
| 2017 | const mem = std.mem; | ||
| 1493 | const relocs_log = std.log.scoped(.link_relocs); | 2018 | const relocs_log = std.log.scoped(.link_relocs); |
| 1494 | const relocation = @import("relocation.zig"); | 2019 | const relocation = @import("relocation.zig"); |
| 1495 | 2020 | ||
| 1496 | const Allocator = std.mem.Allocator; | 2021 | const Allocator = mem.Allocator; |
| 1497 | const Atom = @This(); | 2022 | const Atom = @This(); |
| 1498 | const Elf = @import("../Elf.zig"); | 2023 | const Elf = @import("../Elf.zig"); |
| 1499 | const Fde = eh_frame.Fde; | 2024 | const 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 and | 340 | 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; |
| 386 | 393 | ||
| 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 | } |
| 451 | 458 | ||
| 459 | fn 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 | |||
| 452 | fn filterRelocs( | 469 | fn 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 | }); |
| 318 | 318 | ||
| 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: |
| 325 | pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { | 327 | pub 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}); |
| 327 | 329 | ||
| 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; |
| 330 | 334 | ||
| ... | @@ -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 { |
| 335 | 339 | ||
| 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 | } |
| 340 | 347 | ||
| 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 { |
| 359 | 366 | ||
| 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 | } |
| 364 | 374 | ||
| 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 | } |
| 368 | 378 | ||
| 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 | } |
| 371 | 383 | ||
| 372 | pub fn writeEhFrameObject(elf_file: *Elf, writer: anytype) !void { | 384 | pub 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 | }; |
| 541 | 553 | ||
| 542 | const x86_64 = struct { | 554 | const 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 | |||
| 568 | const 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 | }; |
| 554 | 580 | ||
| 581 | const 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 | |||
| 592 | fn 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 | |||
| 555 | const std = @import("std"); | 602 | const std = @import("std"); |
| 556 | const assert = std.debug.assert; | 603 | const assert = std.debug.assert; |
| 557 | const elf = std.elf; | 604 | const elf = std.elf; |
src/link/Elf/relocation.zig+11-6| ... | @@ -1,4 +1,6 @@ | ... | @@ -1,4 +1,6 @@ |
| 1 | pub const Kind = enum { | 1 | pub 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 { |
| 13 | 15 | ||
| 14 | fn Table(comptime len: comptime_int, comptime RelType: type, comptime mapping: [len]struct { Kind, RelType }) type { | 16 | fn 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 | } |
| 22 | 24 | ||
| 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 | } |
| 31 | 33 | ||
| 32 | const x86_64_relocs = Table(10, elf.R_X86_64, .{ | 34 | const 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 | }); |
| 44 | 47 | ||
| 45 | const aarch64_relocs = Table(10, elf.R_AARCH64, .{ | 48 | const 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 | }); |
| 57 | 61 | ||
| 58 | const riscv64_relocs = Table(10, elf.R_RISCV, .{ | 62 | const 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 | }, |
| 790 | 775 | ||
| 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 | }, |
| 827 | 785 | ||
| 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 | }, |
| 843 | 793 | ||
| 844 | .tlvp_pageoff => { | 794 | .tlvp_pageoff => { |
| ... | @@ -863,7 +813,7 @@ fn resolveRelocInner( | ... | @@ -863,7 +813,7 @@ fn resolveRelocInner( |
| 863 | 813 | ||
| 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 { |
| 1183 | 1133 | ||
| 1184 | pub const Alignment = @import("../../InternPool.zig").Alignment; | 1134 | pub const Alignment = @import("../../InternPool.zig").Alignment; |
| 1185 | 1135 | ||
| 1186 | const aarch64 = @import("../../arch/aarch64/bits.zig"); | 1136 | const aarch64 = @import("../aarch64.zig"); |
| 1187 | const assert = std.debug.assert; | 1137 | const assert = std.debug.assert; |
| 1188 | const bind = @import("dyld_info/bind.zig"); | 1138 | const bind = @import("dyld_info/bind.zig"); |
| 1189 | const macho = std.macho; | 1139 | const 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 | } |
| 62 | 62 | ||
| 63 | pub 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 | |||
| 70 | pub 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 | |||
| 79 | pub 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 | |||
| 90 | pub 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 | |||
| 95 | pub const Type = enum { | 63 | pub const Type = enum { |
| 96 | // x86_64 | 64 | // 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 possible | 269 | // 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 possible | 413 | // 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 possible | 426 | // 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; |
| 778 | pub const LazyBindSection = bind.LazyBind; | 778 | pub const LazyBindSection = bind.LazyBind; |
| 779 | pub const ExportTrieSection = Trie; | 779 | pub const ExportTrieSection = Trie; |
| 780 | 780 | ||
| 781 | const aarch64 = @import("../../arch/aarch64/bits.zig"); | 781 | const aarch64 = @import("../aarch64.zig"); |
| 782 | const assert = std.debug.assert; | 782 | const assert = std.debug.assert; |
| 783 | const bind = @import("dyld_info/bind.zig"); | 783 | const bind = @import("dyld_info/bind.zig"); |
| 784 | const math = std.math; | 784 | const math = std.math; |
| ... | @@ -788,6 +788,5 @@ const trace = @import("../../tracy.zig").trace; | ... | @@ -788,6 +788,5 @@ const trace = @import("../../tracy.zig").trace; |
| 788 | const Allocator = std.mem.Allocator; | 788 | const Allocator = std.mem.Allocator; |
| 789 | const MachO = @import("../MachO.zig"); | 789 | const MachO = @import("../MachO.zig"); |
| 790 | const Rebase = @import("dyld_info/Rebase.zig"); | 790 | const Rebase = @import("dyld_info/Rebase.zig"); |
| 791 | const Relocation = @import("Relocation.zig"); | ||
| 792 | const Symbol = @import("Symbol.zig"); | 791 | const Symbol = @import("Symbol.zig"); |
| 793 | const Trie = @import("dyld_info/Trie.zig"); | 792 | const 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. |
| 165 | const max_allowed_distance = max_distance - 0x500_000; | 165 | const max_allowed_distance = max_distance - 0x500_000; |
| 166 | 166 | ||
| 167 | const aarch64 = @import("../../arch/aarch64/bits.zig"); | 167 | const aarch64 = @import("../aarch64.zig"); |
| 168 | const assert = std.debug.assert; | 168 | const assert = std.debug.assert; |
| 169 | const log = std.log.scoped(.link); | 169 | const log = std.log.scoped(.link); |
| 170 | const macho = std.macho; | 170 | const macho = std.macho; |
src/link/aarch64.zig created+106| ... | @@ -0,0 +1,106 @@ | ||
| 1 | pub 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 | |||
| 6 | pub 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 | |||
| 15 | pub 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 | |||
| 31 | pub 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 | |||
| 42 | pub 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 | |||
| 68 | pub 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 | |||
| 75 | pub 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 | |||
| 87 | pub 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 | |||
| 98 | const assert = std.debug.assert; | ||
| 99 | const bits = @import("../arch/aarch64/bits.zig"); | ||
| 100 | const builtin = @import("builtin"); | ||
| 101 | const math = std.math; | ||
| 102 | const mem = std.mem; | ||
| 103 | const std = @import("std"); | ||
| 104 | |||
| 105 | pub const Instruction = bits.Instruction; | ||
| 106 | pub const Register = bits.Register; | ||
src/link/riscv.zig created+74| ... | @@ -0,0 +1,74 @@ | ||
| 1 | pub 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 | |||
| 12 | pub 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 | |||
| 27 | pub 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 | |||
| 39 | pub 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 | |||
| 50 | pub 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 | |||
| 62 | fn 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 | |||
| 70 | const bits = @import("../arch/riscv64/bits.zig"); | ||
| 71 | const mem = std.mem; | ||
| 72 | const std = @import("std"); | ||
| 73 | |||
| 74 | pub 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 archs | 10 | .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 | }); | ||
| 23 | 33 | ||
| 34 | // x86_64 tests | ||
| 24 | // Exercise linker in -r mode | 35 | // 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 })); |
| 30 | 41 | ||
| 31 | // Exercise linker in ar mode | 42 | // 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 })); |
| 34 | 45 | ||
| 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 })); |
| 42 | 53 | ||
| 43 | // Exercise linker with LLVM backend | 54 | // Exercise linker with LLVM backend |
| 44 | // musl tests | 55 | // 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/17449 | 65 | // 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/17451 | 70 | // 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 })); |
| 63 | 74 | ||
| 64 | // glibc tests | 75 | // 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/17430 | 77 | // 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/17430 | 80 | // 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/17430 | 82 | // 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/17430 | 88 | // 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/17430 | 92 | // 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/17451 | 112 | // 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/17430 | 130 | // 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 })); | ||
| 131 | 148 | ||
| 132 | return elf_step; | 149 | return elf_step; |
| 133 | } | 150 | } |