elf: handle .eh_frame and non-alloc sections

This commit is contained in:
Jakub Konka
2023-10-03 22:06:30 +02:00
parent 9ccd94d560
commit 2c2bc66ce1
4 changed files with 155 additions and 25 deletions

View File

@@ -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));

View File

@@ -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(" : [*]");
}
}

View File

@@ -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);
}
}

View File

@@ -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;