diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2023-10-03 22:06:30 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2023-10-16 19:33:04 +0200 |
| commit | 2c2bc66ce160989734ba6772fa75a870795d9356 (patch) | |
| tree | e108d6fa5998cd9041dd53f154faf2c5c3574ea9 /src | |
| parent | 9ccd94d56037e05e87755887e334aa6a1a096ec5 (diff) | |
| download | zig-2c2bc66ce160989734ba6772fa75a870795d9356.tar.gz zig-2c2bc66ce160989734ba6772fa75a870795d9356.zip | |
elf: handle .eh_frame and non-alloc sections
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/Elf.zig | 32 | ||||
| -rw-r--r-- | src/link/Elf/Atom.zig | 133 | ||||
| -rw-r--r-- | src/link/Elf/Object.zig | 9 | ||||
| -rw-r--r-- | src/link/Elf/eh_frame.zig | 6 |
4 files changed, 155 insertions, 25 deletions
diff --git a/src/link/Elf.zig b/src/link/Elf.zig index dd8784027e..4b63efe2c9 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1316,7 +1316,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node const code = try zig_module.codeAlloc(self, atom_index); defer gpa.free(code); const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr; - try atom_ptr.resolveRelocs(self, code); + try atom_ptr.resolveRelocsAlloc(self, code); try self.base.file.?.pwriteAll(code, file_offset); } @@ -3912,6 +3912,16 @@ fn allocateAtoms(self: *Elf) void { fn writeAtoms(self: *Elf) !void { const gpa = self.base.allocator; + + var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Atom.Index)).init(gpa); + defer { + var it = undefs.iterator(); + while (it.next()) |entry| { + entry.value_ptr.deinit(); + } + undefs.deinit(); + } + for (self.shdrs.items, 0..) |shdr, shndx| { if (shdr.sh_type == elf.SHT_NULL) continue; if (shdr.sh_type == elf.SHT_NOBITS) continue; @@ -3928,11 +3938,13 @@ fn writeAtoms(self: *Elf) !void { @memset(buffer, padding_byte); for (self.objects.items) |index| { - try self.file(index).?.object.writeAtoms(self, @intCast(shndx), buffer); + try self.file(index).?.object.writeAtoms(self, @intCast(shndx), buffer, &undefs); } try self.base.file.?.pwriteAll(buffer, shdr.sh_offset); } + + try self.reportUndefined(&undefs); } fn updateSymtabSize(self: *Elf) !void { @@ -3983,6 +3995,22 @@ fn updateSymtabSize(self: *Elf) !void { fn writeSyntheticSections(self: *Elf) !void { const gpa = self.base.allocator; + if (self.eh_frame_section_index) |shndx| { + const shdr = self.shdrs.items[shndx]; + var buffer = try std.ArrayList(u8).initCapacity(gpa, shdr.sh_size); + defer buffer.deinit(); + try eh_frame.writeEhFrame(self, buffer.writer()); + try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); + } + + if (self.eh_frame_hdr_section_index) |shndx| { + const shdr = self.shdrs.items[shndx]; + var buffer = try std.ArrayList(u8).initCapacity(gpa, shdr.sh_size); + defer buffer.deinit(); + try eh_frame.writeEhFrameHdr(self, buffer.writer()); + try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); + } + if (self.got_section_index) |index| { const shdr = self.shdrs.items[index]; var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got.size(self)); diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 294486f043..802b1dc1b7 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -460,10 +460,7 @@ fn reportUndefined( } } -/// TODO mark relocs dirty -pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { - relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); - +pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void { const file_ptr = self.file(elf_file).?; var stream = std.io.fixedBufferStream(code); const cwriter = stream.writer(); @@ -505,8 +502,9 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { const G = @as(i64, @intCast(target.gotAddress(elf_file))) - GOT; // // Address of the thread pointer. const TP = @as(i64, @intCast(elf_file.tpAddress())); - // // Address of the dynamic thread pointer. - // const DTP = @as(i64, @intCast(elf_file.dtpAddress())); + // Address of the dynamic thread pointer. + const DTP = @as(i64, @intCast(elf_file.dtpAddress())); + _ = DTP; relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ({s})", .{ fmtRelocType(r_type), @@ -597,6 +595,108 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { } } +pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void { + relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); + + const file_ptr = self.file(elf_file).?; + var stream = std.io.fixedBufferStream(code); + const cwriter = stream.writer(); + + const rels = self.relocs(elf_file); + var i: usize = 0; + while (i < rels.len) : (i += 1) { + const rel = rels[i]; + const r_type = rel.r_type(); + if (r_type == elf.R_X86_64_NONE) continue; + + const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; + + const target_index = switch (file_ptr) { + .zig_module => |x| x.symbol(rel.r_sym()), + .object => |x| x.symbols.items[rel.r_sym()], + else => unreachable, + }; + const target = elf_file.symbol(target_index); + + // Check for violation of One Definition Rule for COMDATs. + if (target.file(elf_file) == null) { + // TODO convert into an error + log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{ + file_ptr.fmtPath(), + self.name(elf_file), + target.name(elf_file), + }); + continue; + } + + // Report an undefined symbol. + try self.reportUndefined(elf_file, target, target_index, rel, undefs); + + // We will use equation format to resolve relocations: + // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ + // + const P = @as(i64, @intCast(self.value + rel.r_offset)); + // Addend from the relocation. + const A = rel.r_addend; + // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub. + const S = @as(i64, @intCast(target.address(.{}, elf_file))); + // Address of the global offset table. + const GOT = blk: { + const shndx = if (elf_file.got_plt_section_index) |shndx| + shndx + else if (elf_file.got_section_index) |shndx| + shndx + else + null; + break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0; + }; + // Address of the dynamic thread pointer. + const DTP = @as(i64, @intCast(elf_file.dtpAddress())); + + relocs_log.debug(" {s}: {x}: [{x} => {x}] ({s})", .{ + fmtRelocType(r_type), + rel.r_offset, + P, + S + A, + target.name(elf_file), + }); + + try stream.seekTo(r_offset); + + switch (r_type) { + elf.R_X86_64_NONE => unreachable, + elf.R_X86_64_8 => try cwriter.writeIntLittle(u8, @as(u8, @bitCast(@as(i8, @intCast(S + A))))), + elf.R_X86_64_16 => try cwriter.writeIntLittle(u16, @as(u16, @bitCast(@as(i16, @intCast(S + A))))), + elf.R_X86_64_32 => try cwriter.writeIntLittle(u32, @as(u32, @bitCast(@as(i32, @intCast(S + A))))), + elf.R_X86_64_32S => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A))), + elf.R_X86_64_64 => try cwriter.writeIntLittle(i64, S + A), + elf.R_X86_64_DTPOFF32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - DTP))), + elf.R_X86_64_DTPOFF64 => try cwriter.writeIntLittle(i64, S + A - DTP), + elf.R_X86_64_GOTOFF64 => try cwriter.writeIntLittle(i64, S + A - GOT), + elf.R_X86_64_GOTPC64 => try cwriter.writeIntLittle(i64, GOT + A), + elf.R_X86_64_SIZE32 => { + const size = @as(i64, @intCast(target.elfSym(elf_file).st_size)); + try cwriter.writeIntLittle(u32, @as(u32, @bitCast(@as(i32, @intCast(size + A))))); + }, + elf.R_X86_64_SIZE64 => { + const size = @as(i64, @intCast(target.elfSym(elf_file).st_size)); + try cwriter.writeIntLittle(i64, @as(i64, @intCast(size + A))); + }, + else => { + var err = try elf_file.addErrorWithNotes(1); + try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {}", .{ + fmtRelocType(r_type), + }); + try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{ + self.file(elf_file).?.fmtPath(), + self.name(elf_file), + r_offset, + }); + }, + } + } +} + pub fn fmtRelocType(r_type: u32) std.fmt.Formatter(formatRelocType) { return .{ .data = r_type }; } @@ -696,17 +796,16 @@ fn format2( atom.atom_index, atom.name(elf_file), atom.value, atom.output_section_index, atom.alignment, atom.size, }); - // if (atom.fde_start != atom.fde_end) { - // try writer.writeAll(" : fdes{ "); - // for (atom.getFdes(elf_file), atom.fde_start..) |fde, i| { - // try writer.print("{d}", .{i}); - // if (!fde.alive) try writer.writeAll("([*])"); - // if (i < atom.fde_end - 1) try writer.writeAll(", "); - // } - // try writer.writeAll(" }"); - // } - const gc_sections = if (elf_file.base.options.gc_sections) |gc_sections| gc_sections else false; - if (gc_sections and !atom.flags.alive) { + if (atom.fde_start != atom.fde_end) { + try writer.writeAll(" : fdes{ "); + for (atom.fdes(elf_file), atom.fde_start..) |fde, i| { + try writer.print("{d}", .{i}); + if (!fde.alive) try writer.writeAll("([*])"); + if (i < atom.fde_end - 1) try writer.writeAll(", "); + } + try writer.writeAll(" }"); + } + if (!atom.flags.alive) { try writer.writeAll(" : [*]"); } } diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 8fb44e0faa..723b76469f 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -255,7 +255,6 @@ fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool { if (mem.startsWith(u8, name, ".note")) break :blk true; if (mem.startsWith(u8, name, ".comment")) break :blk true; if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true; - if (mem.startsWith(u8, name, ".eh_frame")) break :blk true; if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and mem.startsWith(u8, name, ".debug")) break :blk true; break :blk false; @@ -681,7 +680,7 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void { } } -pub fn writeAtoms(self: Object, elf_file: *Elf, output_section_index: u16, buffer: []u8) !void { +pub fn writeAtoms(self: Object, elf_file: *Elf, output_section_index: u16, buffer: []u8, undefs: anytype) !void { const gpa = elf_file.base.allocator; const atom_list = self.output_sections.get(output_section_index) orelse return; 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 const in_code = try self.codeDecompressAlloc(elf_file, atom_index); defer gpa.free(in_code); @memcpy(out_code, in_code); - try atom.resolveRelocs(elf_file, out_code); + + if (shdr.sh_flags & elf.SHF_ALLOC == 0) + try atom.resolveRelocsNonAlloc(elf_file, out_code, undefs) + else + try atom.resolveRelocsAlloc(elf_file, out_code); } } diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index d2f525f321..01ca6ccb1a 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -321,7 +321,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { defer gpa.free(contents); for (cie.relocs(elf_file)) |rel| { - const sym = object.symbol(rel.r_sym(), elf_file); + const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); try resolveReloc(cie, sym, rel, elf_file, contents); } @@ -345,7 +345,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { ); for (fde.relocs(elf_file)) |rel| { - const sym = object.symbol(rel.r_sym(), elf_file); + const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); try resolveReloc(fde, sym, rel, elf_file, contents); } @@ -396,7 +396,7 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void { const relocs = fde.relocs(elf_file); assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips... const rel = relocs[0]; - const sym = object.symbol(rel.r_sym(), elf_file); + const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]); const P = @as(i64, @intCast(fde.address(elf_file))); const S = @as(i64, @intCast(sym.address(.{}, elf_file))); const A = rel.r_addend; |
