mirror of
https://github.com/ziglang/zig.git
synced 2025-12-27 08:33:15 +00:00
653 lines
26 KiB
Zig
653 lines
26 KiB
Zig
const std = @import("std");
|
|
const assert = std.debug.assert;
|
|
const macho = std.macho;
|
|
const math = std.math;
|
|
const mem = std.mem;
|
|
const leb = std.leb;
|
|
const log = std.log.scoped(.eh_frame);
|
|
|
|
const Allocator = mem.Allocator;
|
|
const Atom = @import("Atom.zig");
|
|
const MachO = @import("../MachO.zig");
|
|
const Relocation = @import("Relocation.zig");
|
|
const SymbolWithLoc = MachO.SymbolWithLoc;
|
|
const UnwindInfo = @import("UnwindInfo.zig");
|
|
const Zld = @import("zld.zig").Zld;
|
|
|
|
pub fn scanRelocs(zld: *Zld) !void {
|
|
const gpa = zld.gpa;
|
|
|
|
for (zld.objects.items, 0..) |*object, object_id| {
|
|
var cies = std.AutoHashMap(u32, void).init(gpa);
|
|
defer cies.deinit();
|
|
|
|
var it = object.getEhFrameRecordsIterator();
|
|
|
|
for (object.exec_atoms.items) |atom_index| {
|
|
var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
|
|
while (inner_syms_it.next()) |sym| {
|
|
const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
|
|
if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
|
|
it.seekTo(fde_offset);
|
|
const fde = (try it.next()).?;
|
|
|
|
const cie_ptr = fde.getCiePointerSource(@intCast(object_id), zld, fde_offset);
|
|
const cie_offset = fde_offset + 4 - cie_ptr;
|
|
|
|
if (!cies.contains(cie_offset)) {
|
|
try cies.putNoClobber(cie_offset, {});
|
|
it.seekTo(cie_offset);
|
|
const cie = (try it.next()).?;
|
|
try cie.scanRelocs(zld, @as(u32, @intCast(object_id)), cie_offset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
|
|
const sect_id = zld.eh_frame_section_index orelse return;
|
|
const sect = &zld.sections.items(.header)[sect_id];
|
|
sect.@"align" = 3;
|
|
sect.size = 0;
|
|
|
|
const cpu_arch = zld.options.target.cpu.arch;
|
|
const gpa = zld.gpa;
|
|
var size: u32 = 0;
|
|
|
|
for (zld.objects.items, 0..) |*object, object_id| {
|
|
var cies = std.AutoHashMap(u32, u32).init(gpa);
|
|
defer cies.deinit();
|
|
|
|
var eh_it = object.getEhFrameRecordsIterator();
|
|
|
|
for (object.exec_atoms.items) |atom_index| {
|
|
var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
|
|
while (inner_syms_it.next()) |sym| {
|
|
const fde_record_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
|
|
if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
|
|
|
|
const record_id = unwind_info.records_lookup.get(sym) orelse continue;
|
|
const record = unwind_info.records.items[record_id];
|
|
|
|
// TODO skip this check if no __compact_unwind is present
|
|
const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
|
|
if (!is_dwarf) continue;
|
|
|
|
eh_it.seekTo(fde_record_offset);
|
|
const source_fde_record = (try eh_it.next()).?;
|
|
|
|
const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
|
|
const cie_offset = fde_record_offset + 4 - cie_ptr;
|
|
|
|
const gop = try cies.getOrPut(cie_offset);
|
|
if (!gop.found_existing) {
|
|
eh_it.seekTo(cie_offset);
|
|
const source_cie_record = (try eh_it.next()).?;
|
|
gop.value_ptr.* = size;
|
|
size += source_cie_record.getSize();
|
|
}
|
|
|
|
size += source_fde_record.getSize();
|
|
}
|
|
}
|
|
|
|
sect.size = size;
|
|
}
|
|
}
|
|
|
|
pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
|
|
const sect_id = zld.eh_frame_section_index orelse return;
|
|
const sect = zld.sections.items(.header)[sect_id];
|
|
const seg_id = zld.sections.items(.segment_index)[sect_id];
|
|
const seg = zld.segments.items[seg_id];
|
|
|
|
const cpu_arch = zld.options.target.cpu.arch;
|
|
const gpa = zld.gpa;
|
|
|
|
var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa);
|
|
defer {
|
|
for (eh_records.values()) |*rec| {
|
|
rec.deinit(gpa);
|
|
}
|
|
eh_records.deinit();
|
|
}
|
|
|
|
var eh_frame_offset: u32 = 0;
|
|
|
|
for (zld.objects.items, 0..) |*object, object_id| {
|
|
try eh_records.ensureUnusedCapacity(2 * @as(u32, @intCast(object.exec_atoms.items.len)));
|
|
|
|
var cies = std.AutoHashMap(u32, u32).init(gpa);
|
|
defer cies.deinit();
|
|
|
|
var eh_it = object.getEhFrameRecordsIterator();
|
|
|
|
for (object.exec_atoms.items) |atom_index| {
|
|
var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
|
|
while (inner_syms_it.next()) |target| {
|
|
const fde_record_offset = object.eh_frame_records_lookup.get(target) orelse continue;
|
|
if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
|
|
|
|
const record_id = unwind_info.records_lookup.get(target) orelse continue;
|
|
const record = &unwind_info.records.items[record_id];
|
|
|
|
// TODO skip this check if no __compact_unwind is present
|
|
const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
|
|
if (!is_dwarf) continue;
|
|
|
|
eh_it.seekTo(fde_record_offset);
|
|
const source_fde_record = (try eh_it.next()).?;
|
|
|
|
const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
|
|
const cie_offset = fde_record_offset + 4 - cie_ptr;
|
|
|
|
const gop = try cies.getOrPut(cie_offset);
|
|
if (!gop.found_existing) {
|
|
eh_it.seekTo(cie_offset);
|
|
const source_cie_record = (try eh_it.next()).?;
|
|
var cie_record = try source_cie_record.toOwned(gpa);
|
|
try cie_record.relocate(zld, @as(u32, @intCast(object_id)), .{
|
|
.source_offset = cie_offset,
|
|
.out_offset = eh_frame_offset,
|
|
.sect_addr = sect.addr,
|
|
});
|
|
eh_records.putAssumeCapacityNoClobber(eh_frame_offset, cie_record);
|
|
gop.value_ptr.* = eh_frame_offset;
|
|
eh_frame_offset += cie_record.getSize();
|
|
}
|
|
|
|
var fde_record = try source_fde_record.toOwned(gpa);
|
|
try fde_record.relocate(zld, @as(u32, @intCast(object_id)), .{
|
|
.source_offset = fde_record_offset,
|
|
.out_offset = eh_frame_offset,
|
|
.sect_addr = sect.addr,
|
|
});
|
|
fde_record.setCiePointer(eh_frame_offset + 4 - gop.value_ptr.*);
|
|
|
|
switch (cpu_arch) {
|
|
.aarch64 => {}, // relocs take care of LSDA pointers
|
|
.x86_64 => {
|
|
// We need to relocate target symbol address ourselves.
|
|
const atom_sym = zld.getSymbol(target);
|
|
try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{
|
|
.base_addr = sect.addr,
|
|
.base_offset = eh_frame_offset,
|
|
});
|
|
|
|
// We need to parse LSDA pointer and relocate ourselves.
|
|
const cie_record = eh_records.get(
|
|
eh_frame_offset + 4 - fde_record.getCiePointer(),
|
|
).?;
|
|
const eh_frame_sect = object.getSourceSection(object.eh_frame_sect_id.?);
|
|
const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
|
|
.base_addr = eh_frame_sect.addr,
|
|
.base_offset = fde_record_offset,
|
|
});
|
|
if (source_lsda_ptr) |ptr| {
|
|
const sym_index = object.getSymbolByAddress(ptr, null);
|
|
const sym = object.symtab[sym_index];
|
|
try fde_record.setLsdaPointer(cie_record, sym.n_value, .{
|
|
.base_addr = sect.addr,
|
|
.base_offset = eh_frame_offset,
|
|
});
|
|
}
|
|
},
|
|
else => unreachable,
|
|
}
|
|
|
|
eh_records.putAssumeCapacityNoClobber(eh_frame_offset, fde_record);
|
|
|
|
UnwindInfo.UnwindEncoding.setDwarfSectionOffset(
|
|
&record.compactUnwindEncoding,
|
|
cpu_arch,
|
|
@as(u24, @intCast(eh_frame_offset)),
|
|
);
|
|
|
|
const cie_record = eh_records.get(
|
|
eh_frame_offset + 4 - fde_record.getCiePointer(),
|
|
).?;
|
|
const lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
|
|
.base_addr = sect.addr,
|
|
.base_offset = eh_frame_offset,
|
|
});
|
|
if (lsda_ptr) |ptr| {
|
|
record.lsda = ptr - seg.vmaddr;
|
|
}
|
|
|
|
eh_frame_offset += fde_record.getSize();
|
|
}
|
|
}
|
|
}
|
|
|
|
var buffer = std.ArrayList(u8).init(gpa);
|
|
defer buffer.deinit();
|
|
const writer = buffer.writer();
|
|
|
|
for (eh_records.values()) |record| {
|
|
try writer.writeIntLittle(u32, record.size);
|
|
try buffer.appendSlice(record.data);
|
|
}
|
|
|
|
try zld.file.pwriteAll(buffer.items, sect.offset);
|
|
}
|
|
const EhFrameRecordTag = enum { cie, fde };
|
|
|
|
pub fn EhFrameRecord(comptime is_mutable: bool) type {
|
|
return struct {
|
|
tag: EhFrameRecordTag,
|
|
size: u32,
|
|
data: if (is_mutable) []u8 else []const u8,
|
|
|
|
const Record = @This();
|
|
|
|
pub fn deinit(rec: *Record, gpa: Allocator) void {
|
|
comptime assert(is_mutable);
|
|
gpa.free(rec.data);
|
|
}
|
|
|
|
pub fn toOwned(rec: Record, gpa: Allocator) Allocator.Error!EhFrameRecord(true) {
|
|
const data = try gpa.dupe(u8, rec.data);
|
|
return EhFrameRecord(true){
|
|
.tag = rec.tag,
|
|
.size = rec.size,
|
|
.data = data,
|
|
};
|
|
}
|
|
|
|
pub inline fn getSize(rec: Record) u32 {
|
|
return 4 + rec.size;
|
|
}
|
|
|
|
pub fn scanRelocs(
|
|
rec: Record,
|
|
zld: *Zld,
|
|
object_id: u32,
|
|
source_offset: u32,
|
|
) !void {
|
|
if (rec.getPersonalityPointerReloc(zld, object_id, source_offset)) |target| {
|
|
try zld.addGotEntry(target);
|
|
}
|
|
}
|
|
|
|
pub fn getTargetSymbolAddress(rec: Record, ctx: struct {
|
|
base_addr: u64,
|
|
base_offset: u64,
|
|
}) u64 {
|
|
assert(rec.tag == .fde);
|
|
const addend = mem.readIntLittle(i64, rec.data[4..][0..8]);
|
|
return @as(u64, @intCast(@as(i64, @intCast(ctx.base_addr + ctx.base_offset + 8)) + addend));
|
|
}
|
|
|
|
pub fn setTargetSymbolAddress(rec: *Record, value: u64, ctx: struct {
|
|
base_addr: u64,
|
|
base_offset: u64,
|
|
}) !void {
|
|
assert(rec.tag == .fde);
|
|
const addend = @as(i64, @intCast(value)) - @as(i64, @intCast(ctx.base_addr + ctx.base_offset + 8));
|
|
mem.writeIntLittle(i64, rec.data[4..][0..8], addend);
|
|
}
|
|
|
|
pub fn getPersonalityPointerReloc(
|
|
rec: Record,
|
|
zld: *Zld,
|
|
object_id: u32,
|
|
source_offset: u32,
|
|
) ?SymbolWithLoc {
|
|
const cpu_arch = zld.options.target.cpu.arch;
|
|
const relocs = getRelocs(zld, object_id, source_offset);
|
|
for (relocs) |rel| {
|
|
switch (cpu_arch) {
|
|
.aarch64 => {
|
|
const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
|
|
switch (rel_type) {
|
|
.ARM64_RELOC_SUBTRACTOR,
|
|
.ARM64_RELOC_UNSIGNED,
|
|
=> continue,
|
|
.ARM64_RELOC_POINTER_TO_GOT => {},
|
|
else => unreachable,
|
|
}
|
|
},
|
|
.x86_64 => {
|
|
const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
|
|
switch (rel_type) {
|
|
.X86_64_RELOC_GOT => {},
|
|
else => unreachable,
|
|
}
|
|
},
|
|
else => unreachable,
|
|
}
|
|
const target = Atom.parseRelocTarget(zld, .{
|
|
.object_id = object_id,
|
|
.rel = rel,
|
|
.code = rec.data,
|
|
.base_offset = @as(i32, @intCast(source_offset)) + 4,
|
|
});
|
|
return target;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
pub fn relocate(rec: *Record, zld: *Zld, object_id: u32, ctx: struct {
|
|
source_offset: u32,
|
|
out_offset: u32,
|
|
sect_addr: u64,
|
|
}) !void {
|
|
comptime assert(is_mutable);
|
|
|
|
const cpu_arch = zld.options.target.cpu.arch;
|
|
const relocs = getRelocs(zld, object_id, ctx.source_offset);
|
|
|
|
for (relocs) |rel| {
|
|
const target = Atom.parseRelocTarget(zld, .{
|
|
.object_id = object_id,
|
|
.rel = rel,
|
|
.code = rec.data,
|
|
.base_offset = @as(i32, @intCast(ctx.source_offset)) + 4,
|
|
});
|
|
const rel_offset = @as(u32, @intCast(rel.r_address - @as(i32, @intCast(ctx.source_offset)) - 4));
|
|
const source_addr = ctx.sect_addr + rel_offset + ctx.out_offset + 4;
|
|
|
|
switch (cpu_arch) {
|
|
.aarch64 => {
|
|
const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
|
|
switch (rel_type) {
|
|
.ARM64_RELOC_SUBTRACTOR => {
|
|
// Address of the __eh_frame in the source object file
|
|
},
|
|
.ARM64_RELOC_POINTER_TO_GOT => {
|
|
const target_addr = zld.getGotEntryAddress(target).?;
|
|
const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse
|
|
return error.Overflow;
|
|
mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], result);
|
|
},
|
|
.ARM64_RELOC_UNSIGNED => {
|
|
assert(rel.r_extern == 1);
|
|
const target_addr = try Atom.getRelocTargetAddress(zld, target, false);
|
|
const result = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr));
|
|
mem.writeIntLittle(i64, rec.data[rel_offset..][0..8], @as(i64, @intCast(result)));
|
|
},
|
|
else => unreachable,
|
|
}
|
|
},
|
|
.x86_64 => {
|
|
const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
|
|
switch (rel_type) {
|
|
.X86_64_RELOC_GOT => {
|
|
const target_addr = zld.getGotEntryAddress(target).?;
|
|
const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);
|
|
const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
|
|
const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
|
|
mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], disp);
|
|
},
|
|
else => unreachable,
|
|
}
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn getCiePointerSource(rec: Record, object_id: u32, zld: *Zld, offset: u32) u32 {
|
|
assert(rec.tag == .fde);
|
|
const cpu_arch = zld.options.target.cpu.arch;
|
|
const addend = mem.readIntLittle(u32, rec.data[0..4]);
|
|
switch (cpu_arch) {
|
|
.aarch64 => {
|
|
const relocs = getRelocs(zld, object_id, offset);
|
|
const maybe_rel = for (relocs) |rel| {
|
|
if (rel.r_address - @as(i32, @intCast(offset)) == 4 and
|
|
@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_SUBTRACTOR)
|
|
break rel;
|
|
} else null;
|
|
const rel = maybe_rel orelse return addend;
|
|
const object = &zld.objects.items[object_id];
|
|
const target_addr = object.in_symtab.?[rel.r_symbolnum].n_value;
|
|
const sect = object.getSourceSection(object.eh_frame_sect_id.?);
|
|
return @intCast(sect.addr + offset - target_addr + addend);
|
|
},
|
|
.x86_64 => return addend,
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
pub fn getCiePointer(rec: Record) u32 {
|
|
assert(rec.tag == .fde);
|
|
return mem.readIntLittle(u32, rec.data[0..4]);
|
|
}
|
|
|
|
pub fn setCiePointer(rec: *Record, ptr: u32) void {
|
|
assert(rec.tag == .fde);
|
|
mem.writeIntLittle(u32, rec.data[0..4], ptr);
|
|
}
|
|
|
|
pub fn getAugmentationString(rec: Record) []const u8 {
|
|
assert(rec.tag == .cie);
|
|
return mem.sliceTo(@as([*:0]const u8, @ptrCast(rec.data.ptr + 5)), 0);
|
|
}
|
|
|
|
pub fn getPersonalityPointer(rec: Record, ctx: struct {
|
|
base_addr: u64,
|
|
base_offset: u64,
|
|
}) !?u64 {
|
|
assert(rec.tag == .cie);
|
|
const aug_str = rec.getAugmentationString();
|
|
|
|
var stream = std.io.fixedBufferStream(rec.data[9 + aug_str.len ..]);
|
|
var creader = std.io.countingReader(stream.reader());
|
|
const reader = creader.reader();
|
|
|
|
for (aug_str, 0..) |ch, i| switch (ch) {
|
|
'z' => if (i > 0) {
|
|
return error.BadDwarfCfi;
|
|
} else {
|
|
_ = try leb.readULEB128(u64, reader);
|
|
},
|
|
'R' => {
|
|
_ = try reader.readByte();
|
|
},
|
|
'P' => {
|
|
const enc = try reader.readByte();
|
|
const offset = ctx.base_offset + 13 + aug_str.len + creader.bytes_read;
|
|
const ptr = try getEncodedPointer(enc, @as(i64, @intCast(ctx.base_addr + offset)), reader);
|
|
return ptr;
|
|
},
|
|
'L' => {
|
|
_ = try reader.readByte();
|
|
},
|
|
'S', 'B', 'G' => {},
|
|
else => return error.BadDwarfCfi,
|
|
};
|
|
|
|
return null;
|
|
}
|
|
|
|
pub fn getLsdaPointer(rec: Record, cie: Record, ctx: struct {
|
|
base_addr: u64,
|
|
base_offset: u64,
|
|
}) !?u64 {
|
|
assert(rec.tag == .fde);
|
|
const enc = (try cie.getLsdaEncoding()) orelse return null;
|
|
var stream = std.io.fixedBufferStream(rec.data[20..]);
|
|
const reader = stream.reader();
|
|
_ = try reader.readByte();
|
|
const offset = ctx.base_offset + 25;
|
|
const ptr = try getEncodedPointer(enc, @as(i64, @intCast(ctx.base_addr + offset)), reader);
|
|
return ptr;
|
|
}
|
|
|
|
pub fn setLsdaPointer(rec: *Record, cie: Record, value: u64, ctx: struct {
|
|
base_addr: u64,
|
|
base_offset: u64,
|
|
}) !void {
|
|
assert(rec.tag == .fde);
|
|
const enc = (try cie.getLsdaEncoding()) orelse unreachable;
|
|
var stream = std.io.fixedBufferStream(rec.data[21..]);
|
|
const writer = stream.writer();
|
|
const offset = ctx.base_offset + 25;
|
|
try setEncodedPointer(enc, @as(i64, @intCast(ctx.base_addr + offset)), value, writer);
|
|
}
|
|
|
|
fn getLsdaEncoding(rec: Record) !?u8 {
|
|
assert(rec.tag == .cie);
|
|
const aug_str = rec.getAugmentationString();
|
|
|
|
const base_offset = 9 + aug_str.len;
|
|
var stream = std.io.fixedBufferStream(rec.data[base_offset..]);
|
|
var creader = std.io.countingReader(stream.reader());
|
|
const reader = creader.reader();
|
|
|
|
for (aug_str, 0..) |ch, i| switch (ch) {
|
|
'z' => if (i > 0) {
|
|
return error.BadDwarfCfi;
|
|
} else {
|
|
_ = try leb.readULEB128(u64, reader);
|
|
},
|
|
'R' => {
|
|
_ = try reader.readByte();
|
|
},
|
|
'P' => {
|
|
const enc = try reader.readByte();
|
|
_ = try getEncodedPointer(enc, 0, reader);
|
|
},
|
|
'L' => {
|
|
const enc = try reader.readByte();
|
|
return enc;
|
|
},
|
|
'S', 'B', 'G' => {},
|
|
else => return error.BadDwarfCfi,
|
|
};
|
|
|
|
return null;
|
|
}
|
|
|
|
fn getEncodedPointer(enc: u8, pcrel_offset: i64, reader: anytype) !?u64 {
|
|
if (enc == EH_PE.omit) return null;
|
|
|
|
var ptr: i64 = switch (enc & 0x0F) {
|
|
EH_PE.absptr => @as(i64, @bitCast(try reader.readIntLittle(u64))),
|
|
EH_PE.udata2 => @as(i16, @bitCast(try reader.readIntLittle(u16))),
|
|
EH_PE.udata4 => @as(i32, @bitCast(try reader.readIntLittle(u32))),
|
|
EH_PE.udata8 => @as(i64, @bitCast(try reader.readIntLittle(u64))),
|
|
EH_PE.uleb128 => @as(i64, @bitCast(try leb.readULEB128(u64, reader))),
|
|
EH_PE.sdata2 => try reader.readIntLittle(i16),
|
|
EH_PE.sdata4 => try reader.readIntLittle(i32),
|
|
EH_PE.sdata8 => try reader.readIntLittle(i64),
|
|
EH_PE.sleb128 => try leb.readILEB128(i64, reader),
|
|
else => return null,
|
|
};
|
|
|
|
switch (enc & 0x70) {
|
|
EH_PE.absptr => {},
|
|
EH_PE.pcrel => ptr += pcrel_offset,
|
|
EH_PE.datarel,
|
|
EH_PE.textrel,
|
|
EH_PE.funcrel,
|
|
EH_PE.aligned,
|
|
=> return null,
|
|
else => return null,
|
|
}
|
|
|
|
return @as(u64, @bitCast(ptr));
|
|
}
|
|
|
|
fn setEncodedPointer(enc: u8, pcrel_offset: i64, value: u64, writer: anytype) !void {
|
|
if (enc == EH_PE.omit) return;
|
|
|
|
var actual = @as(i64, @intCast(value));
|
|
|
|
switch (enc & 0x70) {
|
|
EH_PE.absptr => {},
|
|
EH_PE.pcrel => actual -= pcrel_offset,
|
|
EH_PE.datarel,
|
|
EH_PE.textrel,
|
|
EH_PE.funcrel,
|
|
EH_PE.aligned,
|
|
=> unreachable,
|
|
else => unreachable,
|
|
}
|
|
|
|
switch (enc & 0x0F) {
|
|
EH_PE.absptr => try writer.writeIntLittle(u64, @as(u64, @bitCast(actual))),
|
|
EH_PE.udata2 => try writer.writeIntLittle(u16, @as(u16, @bitCast(@as(i16, @intCast(actual))))),
|
|
EH_PE.udata4 => try writer.writeIntLittle(u32, @as(u32, @bitCast(@as(i32, @intCast(actual))))),
|
|
EH_PE.udata8 => try writer.writeIntLittle(u64, @as(u64, @bitCast(actual))),
|
|
EH_PE.uleb128 => try leb.writeULEB128(writer, @as(u64, @bitCast(actual))),
|
|
EH_PE.sdata2 => try writer.writeIntLittle(i16, @as(i16, @intCast(actual))),
|
|
EH_PE.sdata4 => try writer.writeIntLittle(i32, @as(i32, @intCast(actual))),
|
|
EH_PE.sdata8 => try writer.writeIntLittle(i64, actual),
|
|
EH_PE.sleb128 => try leb.writeILEB128(writer, actual),
|
|
else => unreachable,
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
pub fn getRelocs(zld: *Zld, object_id: u32, source_offset: u32) []const macho.relocation_info {
|
|
const object = &zld.objects.items[object_id];
|
|
assert(object.hasEhFrameRecords());
|
|
const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse
|
|
return &[0]macho.relocation_info{};
|
|
const all_relocs = object.getRelocs(object.eh_frame_sect_id.?);
|
|
return all_relocs[urel.reloc.start..][0..urel.reloc.len];
|
|
}
|
|
|
|
pub const Iterator = struct {
|
|
data: []const u8,
|
|
pos: u32 = 0,
|
|
|
|
pub fn next(it: *Iterator) !?EhFrameRecord(false) {
|
|
if (it.pos >= it.data.len) return null;
|
|
|
|
var stream = std.io.fixedBufferStream(it.data[it.pos..]);
|
|
const reader = stream.reader();
|
|
|
|
var size = try reader.readIntLittle(u32);
|
|
if (size == 0xFFFFFFFF) {
|
|
log.err("MachO doesn't support 64bit DWARF CFI __eh_frame records", .{});
|
|
return error.BadDwarfCfi;
|
|
}
|
|
|
|
const id = try reader.readIntLittle(u32);
|
|
const tag: EhFrameRecordTag = if (id == 0) .cie else .fde;
|
|
const offset: u32 = 4;
|
|
const record = EhFrameRecord(false){
|
|
.tag = tag,
|
|
.size = size,
|
|
.data = it.data[it.pos + offset ..][0..size],
|
|
};
|
|
|
|
it.pos += size + offset;
|
|
|
|
return record;
|
|
}
|
|
|
|
pub fn reset(it: *Iterator) void {
|
|
it.pos = 0;
|
|
}
|
|
|
|
pub fn seekTo(it: *Iterator, pos: u32) void {
|
|
assert(pos >= 0 and pos < it.data.len);
|
|
it.pos = pos;
|
|
}
|
|
};
|
|
|
|
pub const EH_PE = struct {
|
|
pub const absptr = 0x00;
|
|
pub const uleb128 = 0x01;
|
|
pub const udata2 = 0x02;
|
|
pub const udata4 = 0x03;
|
|
pub const udata8 = 0x04;
|
|
pub const sleb128 = 0x09;
|
|
pub const sdata2 = 0x0A;
|
|
pub const sdata4 = 0x0B;
|
|
pub const sdata8 = 0x0C;
|
|
pub const pcrel = 0x10;
|
|
pub const textrel = 0x20;
|
|
pub const datarel = 0x30;
|
|
pub const funcrel = 0x40;
|
|
pub const aligned = 0x50;
|
|
pub const indirect = 0x80;
|
|
pub const omit = 0xFF;
|
|
};
|