| author | |
| committer | |
| log | 2c2bc66ce160989734ba6772fa75a870795d9356 |
| tree | e108d6fa5998cd9041dd53f154faf2c5c3574ea9 |
| parent | 9ccd94d56037e05e87755887e334aa6a1a096ec5 |
4 files changed, 155 insertions(+), 25 deletions(-)
src/link/Elf.zig+30-2| ... | @@ -1316,7 +1316,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1316,7 +1316,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1316 | const code = try zig_module.codeAlloc(self, atom_index); | 1316 | const code = try zig_module.codeAlloc(self, atom_index); |
| 1317 | defer gpa.free(code); | 1317 | defer gpa.free(code); |
| 1318 | const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr; | 1318 | const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr; |
| 1319 | try atom_ptr.resolveRelocs(self, code); | 1319 | try atom_ptr.resolveRelocsAlloc(self, code); |
| 1320 | try self.base.file.?.pwriteAll(code, file_offset); | 1320 | try self.base.file.?.pwriteAll(code, file_offset); |
| 1321 | } | 1321 | } |
| 1322 | 1322 | ||
| ... | @@ -3912,6 +3912,16 @@ fn allocateAtoms(self: *Elf) void { | ... | @@ -3912,6 +3912,16 @@ fn allocateAtoms(self: *Elf) void { |
| 3912 | 3912 | ||
| 3913 | fn writeAtoms(self: *Elf) !void { | 3913 | fn writeAtoms(self: *Elf) !void { |
| 3914 | const gpa = self.base.allocator; | 3914 | const gpa = self.base.allocator; |
| 3915 | |||
| 3916 | var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Atom.Index)).init(gpa); | ||
| 3917 | defer { | ||
| 3918 | var it = undefs.iterator(); | ||
| 3919 | while (it.next()) |entry| { | ||
| 3920 | entry.value_ptr.deinit(); | ||
| 3921 | } | ||
| 3922 | undefs.deinit(); | ||
| 3923 | } | ||
| 3924 | |||
| 3915 | for (self.shdrs.items, 0..) |shdr, shndx| { | 3925 | for (self.shdrs.items, 0..) |shdr, shndx| { |
| 3916 | if (shdr.sh_type == elf.SHT_NULL) continue; | 3926 | if (shdr.sh_type == elf.SHT_NULL) continue; |
| 3917 | if (shdr.sh_type == elf.SHT_NOBITS) continue; | 3927 | if (shdr.sh_type == elf.SHT_NOBITS) continue; |
| ... | @@ -3928,11 +3938,13 @@ fn writeAtoms(self: *Elf) !void { | ... | @@ -3928,11 +3938,13 @@ fn writeAtoms(self: *Elf) !void { |
| 3928 | @memset(buffer, padding_byte); | 3938 | @memset(buffer, padding_byte); |
| 3929 | 3939 | ||
| 3930 | for (self.objects.items) |index| { | 3940 | for (self.objects.items) |index| { |
| 3931 | try self.file(index).?.object.writeAtoms(self, @intCast(shndx), buffer); | 3941 | try self.file(index).?.object.writeAtoms(self, @intCast(shndx), buffer, &undefs); |
| 3932 | } | 3942 | } |
| 3933 | 3943 | ||
| 3934 | try self.base.file.?.pwriteAll(buffer, shdr.sh_offset); | 3944 | try self.base.file.?.pwriteAll(buffer, shdr.sh_offset); |
| 3935 | } | 3945 | } |
| 3946 | |||
| 3947 | try self.reportUndefined(&undefs); | ||
| 3936 | } | 3948 | } |
| 3937 | 3949 | ||
| 3938 | fn updateSymtabSize(self: *Elf) !void { | 3950 | fn updateSymtabSize(self: *Elf) !void { |
| ... | @@ -3983,6 +3995,22 @@ fn updateSymtabSize(self: *Elf) !void { | ... | @@ -3983,6 +3995,22 @@ fn updateSymtabSize(self: *Elf) !void { |
| 3983 | fn writeSyntheticSections(self: *Elf) !void { | 3995 | fn writeSyntheticSections(self: *Elf) !void { |
| 3984 | const gpa = self.base.allocator; | 3996 | const gpa = self.base.allocator; |
| 3985 | 3997 | ||
| 3998 | if (self.eh_frame_section_index) |shndx| { | ||
| 3999 | const shdr = self.shdrs.items[shndx]; | ||
| 4000 | var buffer = try std.ArrayList(u8).initCapacity(gpa, shdr.sh_size); | ||
| 4001 | defer buffer.deinit(); | ||
| 4002 | try eh_frame.writeEhFrame(self, buffer.writer()); | ||
| 4003 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | ||
| 4004 | } | ||
| 4005 | |||
| 4006 | if (self.eh_frame_hdr_section_index) |shndx| { | ||
| 4007 | const shdr = self.shdrs.items[shndx]; | ||
| 4008 | var buffer = try std.ArrayList(u8).initCapacity(gpa, shdr.sh_size); | ||
| 4009 | defer buffer.deinit(); | ||
| 4010 | try eh_frame.writeEhFrameHdr(self, buffer.writer()); | ||
| 4011 | try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); | ||
| 4012 | } | ||
| 4013 | |||
| 3986 | if (self.got_section_index) |index| { | 4014 | if (self.got_section_index) |index| { |
| 3987 | const shdr = self.shdrs.items[index]; | 4015 | const shdr = self.shdrs.items[index]; |
| 3988 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got.size(self)); | 4016 | var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got.size(self)); |
src/link/Elf/Atom.zig+116-17| ... | @@ -460,10 +460,7 @@ fn reportUndefined( | ... | @@ -460,10 +460,7 @@ fn reportUndefined( |
| 460 | } | 460 | } |
| 461 | } | 461 | } |
| 462 | 462 | ||
| 463 | /// TODO mark relocs dirty | 463 | pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void { |
| 464 | pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { | ||
| 465 | relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); | ||
| 466 | |||
| 467 | const file_ptr = self.file(elf_file).?; | 464 | const file_ptr = self.file(elf_file).?; |
| 468 | var stream = std.io.fixedBufferStream(code); | 465 | var stream = std.io.fixedBufferStream(code); |
| 469 | const cwriter = stream.writer(); | 466 | const cwriter = stream.writer(); |
| ... | @@ -505,8 +502,9 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { | ... | @@ -505,8 +502,9 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { |
| 505 | const G = @as(i64, @intCast(target.gotAddress(elf_file))) - GOT; | 502 | const G = @as(i64, @intCast(target.gotAddress(elf_file))) - GOT; |
| 506 | // // Address of the thread pointer. | 503 | // // Address of the thread pointer. |
| 507 | const TP = @as(i64, @intCast(elf_file.tpAddress())); | 504 | const TP = @as(i64, @intCast(elf_file.tpAddress())); |
| 508 | // // Address of the dynamic thread pointer. | 505 | // Address of the dynamic thread pointer. |
| 509 | // const DTP = @as(i64, @intCast(elf_file.dtpAddress())); | 506 | const DTP = @as(i64, @intCast(elf_file.dtpAddress())); |
| 507 | _ = DTP; | ||
| 510 | 508 | ||
| 511 | relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ({s})", .{ | 509 | relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ({s})", .{ |
| 512 | fmtRelocType(r_type), | 510 | fmtRelocType(r_type), |
| ... | @@ -597,6 +595,108 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { | ... | @@ -597,6 +595,108 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { |
| 597 | } | 595 | } |
| 598 | } | 596 | } |
| 599 | 597 | ||
| 598 | pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void { | ||
| 599 | relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); | ||
| 600 | |||
| 601 | const file_ptr = self.file(elf_file).?; | ||
| 602 | var stream = std.io.fixedBufferStream(code); | ||
| 603 | const cwriter = stream.writer(); | ||
| 604 | |||
| 605 | const rels = self.relocs(elf_file); | ||
| 606 | var i: usize = 0; | ||
| 607 | while (i < rels.len) : (i += 1) { | ||
| 608 | const rel = rels[i]; | ||
| 609 | const r_type = rel.r_type(); | ||
| 610 | if (r_type == elf.R_X86_64_NONE) continue; | ||
| 611 | |||
| 612 | const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; | ||
| 613 | |||
| 614 | const target_index = switch (file_ptr) { | ||
| 615 | .zig_module => |x| x.symbol(rel.r_sym()), | ||
| 616 | .object => |x| x.symbols.items[rel.r_sym()], | ||
| 617 | else => unreachable, | ||
| 618 | }; | ||
| 619 | const target = elf_file.symbol(target_index); | ||
| 620 | |||
| 621 | // Check for violation of One Definition Rule for COMDATs. | ||
| 622 | if (target.file(elf_file) == null) { | ||
| 623 | // TODO convert into an error | ||
| 624 | log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{ | ||
| 625 | file_ptr.fmtPath(), | ||
| 626 | self.name(elf_file), | ||
| 627 | target.name(elf_file), | ||
| 628 | }); | ||
| 629 | continue; | ||
| 630 | } | ||
| 631 | |||
| 632 | // Report an undefined symbol. | ||
| 633 | try self.reportUndefined(elf_file, target, target_index, rel, undefs); | ||
| 634 | |||
| 635 | // We will use equation format to resolve relocations: | ||
| 636 | // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ | ||
| 637 | // | ||
| 638 | const P = @as(i64, @intCast(self.value + rel.r_offset)); | ||
| 639 | // Addend from the relocation. | ||
| 640 | const A = rel.r_addend; | ||
| 641 | // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub. | ||
| 642 | const S = @as(i64, @intCast(target.address(.{}, elf_file))); | ||
| 643 | // Address of the global offset table. | ||
| 644 | const GOT = blk: { | ||
| 645 | const shndx = if (elf_file.got_plt_section_index) |shndx| | ||
| 646 | shndx | ||
| 647 | else if (elf_file.got_section_index) |shndx| | ||
| 648 | shndx | ||
| 649 | else | ||
| 650 | null; | ||
| 651 | break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0; | ||
| 652 | }; | ||
| 653 | // Address of the dynamic thread pointer. | ||
| 654 | const DTP = @as(i64, @intCast(elf_file.dtpAddress())); | ||
| 655 | |||
| 656 | relocs_log.debug(" {s}: {x}: [{x} => {x}] ({s})", .{ | ||
| 657 | fmtRelocType(r_type), | ||
| 658 | rel.r_offset, | ||
| 659 | P, | ||
| 660 | S + A, | ||
| 661 | target.name(elf_file), | ||
| 662 | }); | ||
| 663 | |||
| 664 | try stream.seekTo(r_offset); | ||
| 665 | |||
| 666 | switch (r_type) { | ||
| 667 | elf.R_X86_64_NONE => unreachable, | ||
| 668 | elf.R_X86_64_8 => try cwriter.writeIntLittle(u8, @as(u8, @bitCast(@as(i8, @intCast(S + A))))), | ||
| 669 | elf.R_X86_64_16 => try cwriter.writeIntLittle(u16, @as(u16, @bitCast(@as(i16, @intCast(S + A))))), | ||
| 670 | elf.R_X86_64_32 => try cwriter.writeIntLittle(u32, @as(u32, @bitCast(@as(i32, @intCast(S + A))))), | ||
| 671 | elf.R_X86_64_32S => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A))), | ||
| 672 | elf.R_X86_64_64 => try cwriter.writeIntLittle(i64, S + A), | ||
| 673 | elf.R_X86_64_DTPOFF32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - DTP))), | ||
| 674 | elf.R_X86_64_DTPOFF64 => try cwriter.writeIntLittle(i64, S + A - DTP), | ||
| 675 | elf.R_X86_64_GOTOFF64 => try cwriter.writeIntLittle(i64, S + A - GOT), | ||
| 676 | elf.R_X86_64_GOTPC64 => try cwriter.writeIntLittle(i64, GOT + A), | ||
| 677 | elf.R_X86_64_SIZE32 => { | ||
| 678 | const size = @as(i64, @intCast(target.elfSym(elf_file).st_size)); | ||
| 679 | try cwriter.writeIntLittle(u32, @as(u32, @bitCast(@as(i32, @intCast(size + A))))); | ||
| 680 | }, | ||
| 681 | elf.R_X86_64_SIZE64 => { | ||
| 682 | const size = @as(i64, @intCast(target.elfSym(elf_file).st_size)); | ||
| 683 | try cwriter.writeIntLittle(i64, @as(i64, @intCast(size + A))); | ||
| 684 | }, | ||
| 685 | else => { | ||
| 686 | var err = try elf_file.addErrorWithNotes(1); | ||
| 687 | try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {}", .{ | ||
| 688 | fmtRelocType(r_type), | ||
| 689 | }); | ||
| 690 | try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{ | ||
| 691 | self.file(elf_file).?.fmtPath(), | ||
| 692 | self.name(elf_file), | ||
| 693 | r_offset, | ||
| 694 | }); | ||
| 695 | }, | ||
| 696 | } | ||
| 697 | } | ||
| 698 | } | ||
| 699 | |||
| 600 | pub fn fmtRelocType(r_type: u32) std.fmt.Formatter(formatRelocType) { | 700 | pub fn fmtRelocType(r_type: u32) std.fmt.Formatter(formatRelocType) { |
| 601 | return .{ .data = r_type }; | 701 | return .{ .data = r_type }; |
| 602 | } | 702 | } |
| ... | @@ -696,17 +796,16 @@ fn format2( | ... | @@ -696,17 +796,16 @@ fn format2( |
| 696 | atom.atom_index, atom.name(elf_file), atom.value, | 796 | atom.atom_index, atom.name(elf_file), atom.value, |
| 697 | atom.output_section_index, atom.alignment, atom.size, | 797 | atom.output_section_index, atom.alignment, atom.size, |
| 698 | }); | 798 | }); |
| 699 | // if (atom.fde_start != atom.fde_end) { | 799 | if (atom.fde_start != atom.fde_end) { |
| 700 | // try writer.writeAll(" : fdes{ "); | 800 | try writer.writeAll(" : fdes{ "); |
| 701 | // for (atom.getFdes(elf_file), atom.fde_start..) |fde, i| { | 801 | for (atom.fdes(elf_file), atom.fde_start..) |fde, i| { |
| 702 | // try writer.print("{d}", .{i}); | 802 | try writer.print("{d}", .{i}); |
| 703 | // if (!fde.alive) try writer.writeAll("([*])"); | 803 | if (!fde.alive) try writer.writeAll("([*])"); |
| 704 | // if (i < atom.fde_end - 1) try writer.writeAll(", "); | 804 | if (i < atom.fde_end - 1) try writer.writeAll(", "); |
| 705 | // } | 805 | } |
| 706 | // try writer.writeAll(" }"); | 806 | try writer.writeAll(" }"); |
| 707 | // } | 807 | } |
| 708 | const gc_sections = if (elf_file.base.options.gc_sections) |gc_sections| gc_sections else false; | 808 | if (!atom.flags.alive) { |
| 709 | if (gc_sections and !atom.flags.alive) { | ||
| 710 | try writer.writeAll(" : [*]"); | 809 | try writer.writeAll(" : [*]"); |
| 711 | } | 810 | } |
| 712 | } | 811 | } |
src/link/Elf/Object.zig+6-3| ... | @@ -255,7 +255,6 @@ fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool { | ... | @@ -255,7 +255,6 @@ fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool { |
| 255 | if (mem.startsWith(u8, name, ".note")) break :blk true; | 255 | if (mem.startsWith(u8, name, ".note")) break :blk true; |
| 256 | if (mem.startsWith(u8, name, ".comment")) break :blk true; | 256 | if (mem.startsWith(u8, name, ".comment")) break :blk true; |
| 257 | if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true; | 257 | if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true; |
| 258 | if (mem.startsWith(u8, name, ".eh_frame")) break :blk true; | ||
| 259 | if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and | 258 | if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and |
| 260 | mem.startsWith(u8, name, ".debug")) break :blk true; | 259 | mem.startsWith(u8, name, ".debug")) break :blk true; |
| 261 | break :blk false; | 260 | break :blk false; |
| ... | @@ -681,7 +680,7 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void { | ... | @@ -681,7 +680,7 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void { |
| 681 | } | 680 | } |
| 682 | } | 681 | } |
| 683 | 682 | ||
| 684 | pub fn writeAtoms(self: Object, elf_file: *Elf, output_section_index: u16, buffer: []u8) !void { | 683 | pub fn writeAtoms(self: Object, elf_file: *Elf, output_section_index: u16, buffer: []u8, undefs: anytype) !void { |
| 685 | const gpa = elf_file.base.allocator; | 684 | const gpa = elf_file.base.allocator; |
| 686 | const atom_list = self.output_sections.get(output_section_index) orelse return; | 685 | const atom_list = self.output_sections.get(output_section_index) orelse return; |
| 687 | const shdr = elf_file.shdrs.items[output_section_index]; | 686 | const shdr = elf_file.shdrs.items[output_section_index]; |
| ... | @@ -695,7 +694,11 @@ pub fn writeAtoms(self: Object, elf_file: *Elf, output_section_index: u16, buffe | ... | @@ -695,7 +694,11 @@ pub fn writeAtoms(self: Object, elf_file: *Elf, output_section_index: u16, buffe |
| 695 | const in_code = try self.codeDecompressAlloc(elf_file, atom_index); | 694 | const in_code = try self.codeDecompressAlloc(elf_file, atom_index); |
| 696 | defer gpa.free(in_code); | 695 | defer gpa.free(in_code); |
| 697 | @memcpy(out_code, in_code); | 696 | @memcpy(out_code, in_code); |
| 698 | try atom.resolveRelocs(elf_file, out_code); | 697 | |
| 698 | if (shdr.sh_flags & elf.SHF_ALLOC == 0) | ||
| 699 | try atom.resolveRelocsNonAlloc(elf_file, out_code, undefs) | ||
| 700 | else | ||
| 701 | try atom.resolveRelocsAlloc(elf_file, out_code); | ||
| 699 | } | 702 | } |
| 700 | } | 703 | } |
| 701 | 704 |
src/link/Elf/eh_frame.zig+3-3| ... | @@ -321,7 +321,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { | ... | @@ -321,7 +321,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { |
| 321 | defer gpa.free(contents); | 321 | defer gpa.free(contents); |
| 322 | 322 | ||
| 323 | for (cie.relocs(elf_file)) |rel| { | 323 | for (cie.relocs(elf_file)) |rel| { |
| 324 | const sym = object.symbol(rel.r_sym(), elf_file); | 324 | const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); |
| 325 | try resolveReloc(cie, sym, rel, elf_file, contents); | 325 | try resolveReloc(cie, sym, rel, elf_file, contents); |
| 326 | } | 326 | } |
| 327 | 327 | ||
| ... | @@ -345,7 +345,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { | ... | @@ -345,7 +345,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { |
| 345 | ); | 345 | ); |
| 346 | 346 | ||
| 347 | for (fde.relocs(elf_file)) |rel| { | 347 | for (fde.relocs(elf_file)) |rel| { |
| 348 | const sym = object.symbol(rel.r_sym(), elf_file); | 348 | const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); |
| 349 | try resolveReloc(fde, sym, rel, elf_file, contents); | 349 | try resolveReloc(fde, sym, rel, elf_file, contents); |
| 350 | } | 350 | } |
| 351 | 351 | ||
| ... | @@ -396,7 +396,7 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void { | ... | @@ -396,7 +396,7 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void { |
| 396 | const relocs = fde.relocs(elf_file); | 396 | const relocs = fde.relocs(elf_file); |
| 397 | assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips... | 397 | assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips... |
| 398 | const rel = relocs[0]; | 398 | const rel = relocs[0]; |
| 399 | const sym = object.symbol(rel.r_sym(), elf_file); | 399 | const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); |
| 400 | const P = @as(i64, @intCast(fde.address(elf_file))); | 400 | const P = @as(i64, @intCast(fde.address(elf_file))); |
| 401 | const S = @as(i64, @intCast(sym.address(.{}, elf_file))); | 401 | const S = @as(i64, @intCast(sym.address(.{}, elf_file))); |
| 402 | const A = rel.r_addend; | 402 | const A = rel.r_addend; |