authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-03 22:06:30+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-16 19:33:04+02:00
log2c2bc66ce160989734ba6772fa75a870795d9356
treee108d6fa5998cd9041dd53f154faf2c5c3574ea9
parent9ccd94d56037e05e87755887e334aa6a1a096ec5

elf: handle .eh_frame and non-alloc sections


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
13161316 const code = try zig_module.codeAlloc(self, atom_index);
13171317 defer gpa.free(code);
13181318 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);
13201320 try self.base.file.?.pwriteAll(code, file_offset);
13211321 }
13221322
......@@ -3912,6 +3912,16 @@ fn allocateAtoms(self: *Elf) void {
39123912
39133913fn writeAtoms(self: *Elf) !void {
39143914 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
39153925 for (self.shdrs.items, 0..) |shdr, shndx| {
39163926 if (shdr.sh_type == elf.SHT_NULL) continue;
39173927 if (shdr.sh_type == elf.SHT_NOBITS) continue;
......@@ -3928,11 +3938,13 @@ fn writeAtoms(self: *Elf) !void {
39283938 @memset(buffer, padding_byte);
39293939
39303940 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);
39323942 }
39333943
39343944 try self.base.file.?.pwriteAll(buffer, shdr.sh_offset);
39353945 }
3946
3947 try self.reportUndefined(&undefs);
39363948}
39373949
39383950fn updateSymtabSize(self: *Elf) !void {
......@@ -3983,6 +3995,22 @@ fn updateSymtabSize(self: *Elf) !void {
39833995fn writeSyntheticSections(self: *Elf) !void {
39843996 const gpa = self.base.allocator;
39853997
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
39864014 if (self.got_section_index) |index| {
39874015 const shdr = self.shdrs.items[index];
39884016 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(
460460 }
461461}
462462
463/// TODO mark relocs dirty
464pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
465 relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) });
466
463pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
467464 const file_ptr = self.file(elf_file).?;
468465 var stream = std.io.fixedBufferStream(code);
469466 const cwriter = stream.writer();
......@@ -505,8 +502,9 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
505502 const G = @as(i64, @intCast(target.gotAddress(elf_file))) - GOT;
506503 // // Address of the thread pointer.
507504 const TP = @as(i64, @intCast(elf_file.tpAddress()));
508 // // Address of the dynamic thread pointer.
509 // const DTP = @as(i64, @intCast(elf_file.dtpAddress()));
505 // Address of the dynamic thread pointer.
506 const DTP = @as(i64, @intCast(elf_file.dtpAddress()));
507 _ = DTP;
510508
511509 relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ({s})", .{
512510 fmtRelocType(r_type),
......@@ -597,6 +595,108 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
597595 }
598596}
599597
598pub 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
600700pub fn fmtRelocType(r_type: u32) std.fmt.Formatter(formatRelocType) {
601701 return .{ .data = r_type };
602702}
......@@ -696,17 +796,16 @@ fn format2(
696796 atom.atom_index, atom.name(elf_file), atom.value,
697797 atom.output_section_index, atom.alignment, atom.size,
698798 });
699 // if (atom.fde_start != atom.fde_end) {
700 // try writer.writeAll(" : fdes{ ");
701 // for (atom.getFdes(elf_file), atom.fde_start..) |fde, i| {
702 // try writer.print("{d}", .{i});
703 // if (!fde.alive) try writer.writeAll("([*])");
704 // if (i < atom.fde_end - 1) try writer.writeAll(", ");
705 // }
706 // try writer.writeAll(" }");
707 // }
708 const gc_sections = if (elf_file.base.options.gc_sections) |gc_sections| gc_sections else false;
709 if (gc_sections and !atom.flags.alive) {
799 if (atom.fde_start != atom.fde_end) {
800 try writer.writeAll(" : fdes{ ");
801 for (atom.fdes(elf_file), atom.fde_start..) |fde, i| {
802 try writer.print("{d}", .{i});
803 if (!fde.alive) try writer.writeAll("([*])");
804 if (i < atom.fde_end - 1) try writer.writeAll(", ");
805 }
806 try writer.writeAll(" }");
807 }
808 if (!atom.flags.alive) {
710809 try writer.writeAll(" : [*]");
711810 }
712811}
src/link/Elf/Object.zig+6-3
......@@ -255,7 +255,6 @@ fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool {
255255 if (mem.startsWith(u8, name, ".note")) break :blk true;
256256 if (mem.startsWith(u8, name, ".comment")) break :blk true;
257257 if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true;
258 if (mem.startsWith(u8, name, ".eh_frame")) break :blk true;
259258 if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and
260259 mem.startsWith(u8, name, ".debug")) break :blk true;
261260 break :blk false;
......@@ -681,7 +680,7 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void {
681680 }
682681}
683682
684pub fn writeAtoms(self: Object, elf_file: *Elf, output_section_index: u16, buffer: []u8) !void {
683pub fn writeAtoms(self: Object, elf_file: *Elf, output_section_index: u16, buffer: []u8, undefs: anytype) !void {
685684 const gpa = elf_file.base.allocator;
686685 const atom_list = self.output_sections.get(output_section_index) orelse return;
687686 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
695694 const in_code = try self.codeDecompressAlloc(elf_file, atom_index);
696695 defer gpa.free(in_code);
697696 @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);
699702 }
700703}
701704
src/link/Elf/eh_frame.zig+3-3
......@@ -321,7 +321,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
321321 defer gpa.free(contents);
322322
323323 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()]);
325325 try resolveReloc(cie, sym, rel, elf_file, contents);
326326 }
327327
......@@ -345,7 +345,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
345345 );
346346
347347 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()]);
349349 try resolveReloc(fde, sym, rel, elf_file, contents);
350350 }
351351
......@@ -396,7 +396,7 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {
396396 const relocs = fde.relocs(elf_file);
397397 assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips...
398398 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()]);
400400 const P = @as(i64, @intCast(fde.address(elf_file)));
401401 const S = @as(i64, @intCast(sym.address(.{}, elf_file)));
402402 const A = rel.r_addend;