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...@@ -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 }
13221322
...@@ -3912,6 +3912,16 @@ fn allocateAtoms(self: *Elf) void {...@@ -3912,6 +3912,16 @@ fn allocateAtoms(self: *Elf) void {
39123912
3913fn writeAtoms(self: *Elf) !void {3913fn 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);
39293939
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 }
39333943
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}
39373949
3938fn updateSymtabSize(self: *Elf) !void {3950fn updateSymtabSize(self: *Elf) !void {
...@@ -3983,6 +3995,22 @@ fn updateSymtabSize(self: *Elf) !void {...@@ -3983,6 +3995,22 @@ fn updateSymtabSize(self: *Elf) !void {
3983fn writeSyntheticSections(self: *Elf) !void {3995fn writeSyntheticSections(self: *Elf) !void {
3984 const gpa = self.base.allocator;3996 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
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}
462462
463/// TODO mark relocs dirty463pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
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
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;
510508
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}
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
600pub fn fmtRelocType(r_type: u32) std.fmt.Formatter(formatRelocType) {700pub 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 and258 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}
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 {
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}
701704
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);
322322
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 }
327327
...@@ -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 );
346346
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 }
351351
...@@ -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;