Commit 2c2bc66ce1
Changed files (4)
src
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(" : [*]");
}
}
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;
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);
}
}
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));