zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob 36c8a794 (25795B) - Raw


      1 const std = @import("std");
      2 const assert = std.debug.assert;
      3 const macho = std.macho;
      4 const math = std.math;
      5 const mem = std.mem;
      6 const leb = std.leb;
      7 const log = std.log.scoped(.eh_frame);
      8 
      9 const Allocator = mem.Allocator;
     10 const AtomIndex = @import("zld.zig").AtomIndex;
     11 const Atom = @import("ZldAtom.zig");
     12 const Relocation = @import("Relocation.zig");
     13 const SymbolWithLoc = @import("zld.zig").SymbolWithLoc;
     14 const UnwindInfo = @import("UnwindInfo.zig");
     15 const Zld = @import("zld.zig").Zld;
     16 
     17 pub fn scanRelocs(zld: *Zld) !void {
     18     const gpa = zld.gpa;
     19 
     20     for (zld.objects.items, 0..) |*object, object_id| {
     21         var cies = std.AutoHashMap(u32, void).init(gpa);
     22         defer cies.deinit();
     23 
     24         var it = object.getEhFrameRecordsIterator();
     25 
     26         for (object.exec_atoms.items) |atom_index| {
     27             const fde_offset = object.eh_frame_records_lookup.get(atom_index) orelse continue;
     28             if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
     29             it.seekTo(fde_offset);
     30             const fde = (try it.next()).?;
     31 
     32             const cie_ptr = fde.getCiePointerSource(@intCast(object_id), zld, fde_offset);
     33             const cie_offset = fde_offset + 4 - cie_ptr;
     34 
     35             if (!cies.contains(cie_offset)) {
     36                 try cies.putNoClobber(cie_offset, {});
     37                 it.seekTo(cie_offset);
     38                 const cie = (try it.next()).?;
     39                 try cie.scanRelocs(zld, @as(u32, @intCast(object_id)), cie_offset);
     40             }
     41         }
     42     }
     43 }
     44 
     45 pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
     46     const sect_id = zld.getSectionByName("__TEXT", "__eh_frame") orelse return;
     47     const sect = &zld.sections.items(.header)[sect_id];
     48     sect.@"align" = 3;
     49     sect.size = 0;
     50 
     51     const cpu_arch = zld.options.target.cpu.arch;
     52     const gpa = zld.gpa;
     53     var size: u32 = 0;
     54 
     55     for (zld.objects.items, 0..) |*object, object_id| {
     56         var cies = std.AutoHashMap(u32, u32).init(gpa);
     57         defer cies.deinit();
     58 
     59         var eh_it = object.getEhFrameRecordsIterator();
     60 
     61         for (object.exec_atoms.items) |atom_index| {
     62             const fde_record_offset = object.eh_frame_records_lookup.get(atom_index) orelse continue;
     63             if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
     64 
     65             const record_id = unwind_info.records_lookup.get(atom_index) orelse continue;
     66             const record = unwind_info.records.items[record_id];
     67 
     68             // TODO skip this check if no __compact_unwind is present
     69             const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
     70             if (!is_dwarf) continue;
     71 
     72             eh_it.seekTo(fde_record_offset);
     73             const source_fde_record = (try eh_it.next()).?;
     74 
     75             const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
     76             const cie_offset = fde_record_offset + 4 - cie_ptr;
     77 
     78             const gop = try cies.getOrPut(cie_offset);
     79             if (!gop.found_existing) {
     80                 eh_it.seekTo(cie_offset);
     81                 const source_cie_record = (try eh_it.next()).?;
     82                 gop.value_ptr.* = size;
     83                 size += source_cie_record.getSize();
     84             }
     85 
     86             size += source_fde_record.getSize();
     87         }
     88     }
     89 
     90     sect.size = size;
     91 }
     92 
     93 pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
     94     const sect_id = zld.getSectionByName("__TEXT", "__eh_frame") orelse return;
     95     const sect = zld.sections.items(.header)[sect_id];
     96     const seg_id = zld.sections.items(.segment_index)[sect_id];
     97     const seg = zld.segments.items[seg_id];
     98 
     99     const cpu_arch = zld.options.target.cpu.arch;
    100     const gpa = zld.gpa;
    101 
    102     var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa);
    103     defer {
    104         for (eh_records.values()) |*rec| {
    105             rec.deinit(gpa);
    106         }
    107         eh_records.deinit();
    108     }
    109 
    110     var eh_frame_offset: u32 = 0;
    111 
    112     for (zld.objects.items, 0..) |*object, object_id| {
    113         try eh_records.ensureUnusedCapacity(2 * @as(u32, @intCast(object.exec_atoms.items.len)));
    114 
    115         var cies = std.AutoHashMap(u32, u32).init(gpa);
    116         defer cies.deinit();
    117 
    118         var eh_it = object.getEhFrameRecordsIterator();
    119 
    120         for (object.exec_atoms.items) |atom_index| {
    121             const fde_record_offset = object.eh_frame_records_lookup.get(atom_index) orelse continue;
    122             if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
    123 
    124             const record_id = unwind_info.records_lookup.get(atom_index) orelse continue;
    125             const record = &unwind_info.records.items[record_id];
    126 
    127             // TODO skip this check if no __compact_unwind is present
    128             const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
    129             if (!is_dwarf) continue;
    130 
    131             eh_it.seekTo(fde_record_offset);
    132             const source_fde_record = (try eh_it.next()).?;
    133 
    134             const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
    135             const cie_offset = fde_record_offset + 4 - cie_ptr;
    136 
    137             const gop = try cies.getOrPut(cie_offset);
    138             if (!gop.found_existing) {
    139                 eh_it.seekTo(cie_offset);
    140                 const source_cie_record = (try eh_it.next()).?;
    141                 var cie_record = try source_cie_record.toOwned(gpa);
    142                 try cie_record.relocate(zld, @as(u32, @intCast(object_id)), .{
    143                     .source_offset = cie_offset,
    144                     .out_offset = eh_frame_offset,
    145                     .sect_addr = sect.addr,
    146                 });
    147                 eh_records.putAssumeCapacityNoClobber(eh_frame_offset, cie_record);
    148                 gop.value_ptr.* = eh_frame_offset;
    149                 eh_frame_offset += cie_record.getSize();
    150             }
    151 
    152             var fde_record = try source_fde_record.toOwned(gpa);
    153             try fde_record.relocate(zld, @as(u32, @intCast(object_id)), .{
    154                 .source_offset = fde_record_offset,
    155                 .out_offset = eh_frame_offset,
    156                 .sect_addr = sect.addr,
    157             });
    158             fde_record.setCiePointer(eh_frame_offset + 4 - gop.value_ptr.*);
    159 
    160             switch (cpu_arch) {
    161                 .aarch64 => {}, // relocs take care of LSDA pointers
    162                 .x86_64 => {
    163                     // We need to relocate target symbol address ourselves.
    164                     const atom = zld.getAtom(atom_index);
    165                     const atom_sym = zld.getSymbol(atom.getSymbolWithLoc());
    166                     try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{
    167                         .base_addr = sect.addr,
    168                         .base_offset = eh_frame_offset,
    169                     });
    170 
    171                     // We need to parse LSDA pointer and relocate ourselves.
    172                     const cie_record = eh_records.get(
    173                         eh_frame_offset + 4 - fde_record.getCiePointer(),
    174                     ).?;
    175                     const eh_frame_sect = object.getSourceSection(object.eh_frame_sect_id.?);
    176                     const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
    177                         .base_addr = eh_frame_sect.addr,
    178                         .base_offset = fde_record_offset,
    179                     });
    180                     if (source_lsda_ptr) |ptr| {
    181                         const sym_index = object.getSymbolByAddress(ptr, null);
    182                         const sym = object.symtab[sym_index];
    183                         try fde_record.setLsdaPointer(cie_record, sym.n_value, .{
    184                             .base_addr = sect.addr,
    185                             .base_offset = eh_frame_offset,
    186                         });
    187                     }
    188                 },
    189                 else => unreachable,
    190             }
    191 
    192             eh_records.putAssumeCapacityNoClobber(eh_frame_offset, fde_record);
    193 
    194             UnwindInfo.UnwindEncoding.setDwarfSectionOffset(
    195                 &record.compactUnwindEncoding,
    196                 cpu_arch,
    197                 @as(u24, @intCast(eh_frame_offset)),
    198             );
    199 
    200             const cie_record = eh_records.get(
    201                 eh_frame_offset + 4 - fde_record.getCiePointer(),
    202             ).?;
    203             const lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
    204                 .base_addr = sect.addr,
    205                 .base_offset = eh_frame_offset,
    206             });
    207             if (lsda_ptr) |ptr| {
    208                 record.lsda = ptr - seg.vmaddr;
    209             }
    210 
    211             eh_frame_offset += fde_record.getSize();
    212         }
    213     }
    214 
    215     var buffer = std.ArrayList(u8).init(gpa);
    216     defer buffer.deinit();
    217     const writer = buffer.writer();
    218 
    219     for (eh_records.values()) |record| {
    220         try writer.writeIntLittle(u32, record.size);
    221         try buffer.appendSlice(record.data);
    222     }
    223 
    224     try zld.file.pwriteAll(buffer.items, sect.offset);
    225 }
    226 const EhFrameRecordTag = enum { cie, fde };
    227 
    228 pub fn EhFrameRecord(comptime is_mutable: bool) type {
    229     return struct {
    230         tag: EhFrameRecordTag,
    231         size: u32,
    232         data: if (is_mutable) []u8 else []const u8,
    233 
    234         const Record = @This();
    235 
    236         pub fn deinit(rec: *Record, gpa: Allocator) void {
    237             comptime assert(is_mutable);
    238             gpa.free(rec.data);
    239         }
    240 
    241         pub fn toOwned(rec: Record, gpa: Allocator) Allocator.Error!EhFrameRecord(true) {
    242             const data = try gpa.dupe(u8, rec.data);
    243             return EhFrameRecord(true){
    244                 .tag = rec.tag,
    245                 .size = rec.size,
    246                 .data = data,
    247             };
    248         }
    249 
    250         pub inline fn getSize(rec: Record) u32 {
    251             return 4 + rec.size;
    252         }
    253 
    254         pub fn scanRelocs(
    255             rec: Record,
    256             zld: *Zld,
    257             object_id: u32,
    258             source_offset: u32,
    259         ) !void {
    260             if (rec.getPersonalityPointerReloc(zld, object_id, source_offset)) |target| {
    261                 try Atom.addGotEntry(zld, target);
    262             }
    263         }
    264 
    265         pub fn getTargetSymbolAddress(rec: Record, ctx: struct {
    266             base_addr: u64,
    267             base_offset: u64,
    268         }) u64 {
    269             assert(rec.tag == .fde);
    270             const addend = mem.readIntLittle(i64, rec.data[4..][0..8]);
    271             return @as(u64, @intCast(@as(i64, @intCast(ctx.base_addr + ctx.base_offset + 8)) + addend));
    272         }
    273 
    274         pub fn setTargetSymbolAddress(rec: *Record, value: u64, ctx: struct {
    275             base_addr: u64,
    276             base_offset: u64,
    277         }) !void {
    278             assert(rec.tag == .fde);
    279             const addend = @as(i64, @intCast(value)) - @as(i64, @intCast(ctx.base_addr + ctx.base_offset + 8));
    280             mem.writeIntLittle(i64, rec.data[4..][0..8], addend);
    281         }
    282 
    283         pub fn getPersonalityPointerReloc(
    284             rec: Record,
    285             zld: *Zld,
    286             object_id: u32,
    287             source_offset: u32,
    288         ) ?SymbolWithLoc {
    289             const cpu_arch = zld.options.target.cpu.arch;
    290             const relocs = getRelocs(zld, object_id, source_offset);
    291             for (relocs) |rel| {
    292                 switch (cpu_arch) {
    293                     .aarch64 => {
    294                         const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
    295                         switch (rel_type) {
    296                             .ARM64_RELOC_SUBTRACTOR,
    297                             .ARM64_RELOC_UNSIGNED,
    298                             => continue,
    299                             .ARM64_RELOC_POINTER_TO_GOT => {},
    300                             else => unreachable,
    301                         }
    302                     },
    303                     .x86_64 => {
    304                         const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
    305                         switch (rel_type) {
    306                             .X86_64_RELOC_GOT => {},
    307                             else => unreachable,
    308                         }
    309                     },
    310                     else => unreachable,
    311                 }
    312                 const target = Atom.parseRelocTarget(zld, .{
    313                     .object_id = object_id,
    314                     .rel = rel,
    315                     .code = rec.data,
    316                     .base_offset = @as(i32, @intCast(source_offset)) + 4,
    317                 });
    318                 return target;
    319             }
    320             return null;
    321         }
    322 
    323         pub fn relocate(rec: *Record, zld: *Zld, object_id: u32, ctx: struct {
    324             source_offset: u32,
    325             out_offset: u32,
    326             sect_addr: u64,
    327         }) !void {
    328             comptime assert(is_mutable);
    329 
    330             const cpu_arch = zld.options.target.cpu.arch;
    331             const relocs = getRelocs(zld, object_id, ctx.source_offset);
    332 
    333             for (relocs) |rel| {
    334                 const target = Atom.parseRelocTarget(zld, .{
    335                     .object_id = object_id,
    336                     .rel = rel,
    337                     .code = rec.data,
    338                     .base_offset = @as(i32, @intCast(ctx.source_offset)) + 4,
    339                 });
    340                 const rel_offset = @as(u32, @intCast(rel.r_address - @as(i32, @intCast(ctx.source_offset)) - 4));
    341                 const source_addr = ctx.sect_addr + rel_offset + ctx.out_offset + 4;
    342 
    343                 switch (cpu_arch) {
    344                     .aarch64 => {
    345                         const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
    346                         switch (rel_type) {
    347                             .ARM64_RELOC_SUBTRACTOR => {
    348                                 // Address of the __eh_frame in the source object file
    349                             },
    350                             .ARM64_RELOC_POINTER_TO_GOT => {
    351                                 const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);
    352                                 const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse
    353                                     return error.Overflow;
    354                                 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], result);
    355                             },
    356                             .ARM64_RELOC_UNSIGNED => {
    357                                 assert(rel.r_extern == 1);
    358                                 const target_addr = try Atom.getRelocTargetAddress(zld, target, false, false);
    359                                 const result = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr));
    360                                 mem.writeIntLittle(i64, rec.data[rel_offset..][0..8], @as(i64, @intCast(result)));
    361                             },
    362                             else => unreachable,
    363                         }
    364                     },
    365                     .x86_64 => {
    366                         const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
    367                         switch (rel_type) {
    368                             .X86_64_RELOC_GOT => {
    369                                 const target_addr = try Atom.getRelocTargetAddress(zld, target, true, false);
    370                                 const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);
    371                                 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
    372                                 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
    373                                 mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], disp);
    374                             },
    375                             else => unreachable,
    376                         }
    377                     },
    378                     else => unreachable,
    379                 }
    380             }
    381         }
    382 
    383         pub fn getCiePointerSource(rec: Record, object_id: u32, zld: *Zld, offset: u32) u32 {
    384             assert(rec.tag == .fde);
    385             const cpu_arch = zld.options.target.cpu.arch;
    386             const addend = mem.readIntLittle(u32, rec.data[0..4]);
    387             switch (cpu_arch) {
    388                 .aarch64 => {
    389                     const relocs = getRelocs(zld, object_id, offset);
    390                     const maybe_rel = for (relocs) |rel| {
    391                         if (rel.r_address - @as(i32, @intCast(offset)) == 4 and
    392                             @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_SUBTRACTOR)
    393                             break rel;
    394                     } else null;
    395                     const rel = maybe_rel orelse return addend;
    396                     const object = &zld.objects.items[object_id];
    397                     const target_addr = object.in_symtab.?[rel.r_symbolnum].n_value;
    398                     const sect = object.getSourceSection(object.eh_frame_sect_id.?);
    399                     return @intCast(sect.addr + offset - target_addr + addend);
    400                 },
    401                 .x86_64 => return addend,
    402                 else => unreachable,
    403             }
    404         }
    405 
    406         pub fn getCiePointer(rec: Record) u32 {
    407             assert(rec.tag == .fde);
    408             return mem.readIntLittle(u32, rec.data[0..4]);
    409         }
    410 
    411         pub fn setCiePointer(rec: *Record, ptr: u32) void {
    412             assert(rec.tag == .fde);
    413             mem.writeIntLittle(u32, rec.data[0..4], ptr);
    414         }
    415 
    416         pub fn getAugmentationString(rec: Record) []const u8 {
    417             assert(rec.tag == .cie);
    418             return mem.sliceTo(@as([*:0]const u8, @ptrCast(rec.data.ptr + 5)), 0);
    419         }
    420 
    421         pub fn getPersonalityPointer(rec: Record, ctx: struct {
    422             base_addr: u64,
    423             base_offset: u64,
    424         }) !?u64 {
    425             assert(rec.tag == .cie);
    426             const aug_str = rec.getAugmentationString();
    427 
    428             var stream = std.io.fixedBufferStream(rec.data[9 + aug_str.len ..]);
    429             var creader = std.io.countingReader(stream.reader());
    430             const reader = creader.reader();
    431 
    432             for (aug_str, 0..) |ch, i| switch (ch) {
    433                 'z' => if (i > 0) {
    434                     return error.BadDwarfCfi;
    435                 } else {
    436                     _ = try leb.readULEB128(u64, reader);
    437                 },
    438                 'R' => {
    439                     _ = try reader.readByte();
    440                 },
    441                 'P' => {
    442                     const enc = try reader.readByte();
    443                     const offset = ctx.base_offset + 13 + aug_str.len + creader.bytes_read;
    444                     const ptr = try getEncodedPointer(enc, @as(i64, @intCast(ctx.base_addr + offset)), reader);
    445                     return ptr;
    446                 },
    447                 'L' => {
    448                     _ = try reader.readByte();
    449                 },
    450                 'S', 'B', 'G' => {},
    451                 else => return error.BadDwarfCfi,
    452             };
    453 
    454             return null;
    455         }
    456 
    457         pub fn getLsdaPointer(rec: Record, cie: Record, ctx: struct {
    458             base_addr: u64,
    459             base_offset: u64,
    460         }) !?u64 {
    461             assert(rec.tag == .fde);
    462             const enc = (try cie.getLsdaEncoding()) orelse return null;
    463             var stream = std.io.fixedBufferStream(rec.data[20..]);
    464             const reader = stream.reader();
    465             _ = try reader.readByte();
    466             const offset = ctx.base_offset + 25;
    467             const ptr = try getEncodedPointer(enc, @as(i64, @intCast(ctx.base_addr + offset)), reader);
    468             return ptr;
    469         }
    470 
    471         pub fn setLsdaPointer(rec: *Record, cie: Record, value: u64, ctx: struct {
    472             base_addr: u64,
    473             base_offset: u64,
    474         }) !void {
    475             assert(rec.tag == .fde);
    476             const enc = (try cie.getLsdaEncoding()) orelse unreachable;
    477             var stream = std.io.fixedBufferStream(rec.data[21..]);
    478             const writer = stream.writer();
    479             const offset = ctx.base_offset + 25;
    480             try setEncodedPointer(enc, @as(i64, @intCast(ctx.base_addr + offset)), value, writer);
    481         }
    482 
    483         fn getLsdaEncoding(rec: Record) !?u8 {
    484             assert(rec.tag == .cie);
    485             const aug_str = rec.getAugmentationString();
    486 
    487             const base_offset = 9 + aug_str.len;
    488             var stream = std.io.fixedBufferStream(rec.data[base_offset..]);
    489             var creader = std.io.countingReader(stream.reader());
    490             const reader = creader.reader();
    491 
    492             for (aug_str, 0..) |ch, i| switch (ch) {
    493                 'z' => if (i > 0) {
    494                     return error.BadDwarfCfi;
    495                 } else {
    496                     _ = try leb.readULEB128(u64, reader);
    497                 },
    498                 'R' => {
    499                     _ = try reader.readByte();
    500                 },
    501                 'P' => {
    502                     const enc = try reader.readByte();
    503                     _ = try getEncodedPointer(enc, 0, reader);
    504                 },
    505                 'L' => {
    506                     const enc = try reader.readByte();
    507                     return enc;
    508                 },
    509                 'S', 'B', 'G' => {},
    510                 else => return error.BadDwarfCfi,
    511             };
    512 
    513             return null;
    514         }
    515 
    516         fn getEncodedPointer(enc: u8, pcrel_offset: i64, reader: anytype) !?u64 {
    517             if (enc == EH_PE.omit) return null;
    518 
    519             var ptr: i64 = switch (enc & 0x0F) {
    520                 EH_PE.absptr => @as(i64, @bitCast(try reader.readIntLittle(u64))),
    521                 EH_PE.udata2 => @as(i16, @bitCast(try reader.readIntLittle(u16))),
    522                 EH_PE.udata4 => @as(i32, @bitCast(try reader.readIntLittle(u32))),
    523                 EH_PE.udata8 => @as(i64, @bitCast(try reader.readIntLittle(u64))),
    524                 EH_PE.uleb128 => @as(i64, @bitCast(try leb.readULEB128(u64, reader))),
    525                 EH_PE.sdata2 => try reader.readIntLittle(i16),
    526                 EH_PE.sdata4 => try reader.readIntLittle(i32),
    527                 EH_PE.sdata8 => try reader.readIntLittle(i64),
    528                 EH_PE.sleb128 => try leb.readILEB128(i64, reader),
    529                 else => return null,
    530             };
    531 
    532             switch (enc & 0x70) {
    533                 EH_PE.absptr => {},
    534                 EH_PE.pcrel => ptr += pcrel_offset,
    535                 EH_PE.datarel,
    536                 EH_PE.textrel,
    537                 EH_PE.funcrel,
    538                 EH_PE.aligned,
    539                 => return null,
    540                 else => return null,
    541             }
    542 
    543             return @as(u64, @bitCast(ptr));
    544         }
    545 
    546         fn setEncodedPointer(enc: u8, pcrel_offset: i64, value: u64, writer: anytype) !void {
    547             if (enc == EH_PE.omit) return;
    548 
    549             var actual = @as(i64, @intCast(value));
    550 
    551             switch (enc & 0x70) {
    552                 EH_PE.absptr => {},
    553                 EH_PE.pcrel => actual -= pcrel_offset,
    554                 EH_PE.datarel,
    555                 EH_PE.textrel,
    556                 EH_PE.funcrel,
    557                 EH_PE.aligned,
    558                 => unreachable,
    559                 else => unreachable,
    560             }
    561 
    562             switch (enc & 0x0F) {
    563                 EH_PE.absptr => try writer.writeIntLittle(u64, @as(u64, @bitCast(actual))),
    564                 EH_PE.udata2 => try writer.writeIntLittle(u16, @as(u16, @bitCast(@as(i16, @intCast(actual))))),
    565                 EH_PE.udata4 => try writer.writeIntLittle(u32, @as(u32, @bitCast(@as(i32, @intCast(actual))))),
    566                 EH_PE.udata8 => try writer.writeIntLittle(u64, @as(u64, @bitCast(actual))),
    567                 EH_PE.uleb128 => try leb.writeULEB128(writer, @as(u64, @bitCast(actual))),
    568                 EH_PE.sdata2 => try writer.writeIntLittle(i16, @as(i16, @intCast(actual))),
    569                 EH_PE.sdata4 => try writer.writeIntLittle(i32, @as(i32, @intCast(actual))),
    570                 EH_PE.sdata8 => try writer.writeIntLittle(i64, actual),
    571                 EH_PE.sleb128 => try leb.writeILEB128(writer, actual),
    572                 else => unreachable,
    573             }
    574         }
    575     };
    576 }
    577 
    578 pub fn getRelocs(zld: *Zld, object_id: u32, source_offset: u32) []const macho.relocation_info {
    579     const object = &zld.objects.items[object_id];
    580     assert(object.hasEhFrameRecords());
    581     const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse
    582         return &[0]macho.relocation_info{};
    583     const all_relocs = object.getRelocs(object.eh_frame_sect_id.?);
    584     return all_relocs[urel.reloc.start..][0..urel.reloc.len];
    585 }
    586 
    587 pub const Iterator = struct {
    588     data: []const u8,
    589     pos: u32 = 0,
    590 
    591     pub fn next(it: *Iterator) !?EhFrameRecord(false) {
    592         if (it.pos >= it.data.len) return null;
    593 
    594         var stream = std.io.fixedBufferStream(it.data[it.pos..]);
    595         const reader = stream.reader();
    596 
    597         var size = try reader.readIntLittle(u32);
    598         if (size == 0xFFFFFFFF) {
    599             log.err("MachO doesn't support 64bit DWARF CFI __eh_frame records", .{});
    600             return error.BadDwarfCfi;
    601         }
    602 
    603         const id = try reader.readIntLittle(u32);
    604         const tag: EhFrameRecordTag = if (id == 0) .cie else .fde;
    605         const offset: u32 = 4;
    606         const record = EhFrameRecord(false){
    607             .tag = tag,
    608             .size = size,
    609             .data = it.data[it.pos + offset ..][0..size],
    610         };
    611 
    612         it.pos += size + offset;
    613 
    614         return record;
    615     }
    616 
    617     pub fn reset(it: *Iterator) void {
    618         it.pos = 0;
    619     }
    620 
    621     pub fn seekTo(it: *Iterator, pos: u32) void {
    622         assert(pos >= 0 and pos < it.data.len);
    623         it.pos = pos;
    624     }
    625 };
    626 
    627 pub const EH_PE = struct {
    628     pub const absptr = 0x00;
    629     pub const uleb128 = 0x01;
    630     pub const udata2 = 0x02;
    631     pub const udata4 = 0x03;
    632     pub const udata8 = 0x04;
    633     pub const sleb128 = 0x09;
    634     pub const sdata2 = 0x0A;
    635     pub const sdata4 = 0x0B;
    636     pub const sdata8 = 0x0C;
    637     pub const pcrel = 0x10;
    638     pub const textrel = 0x20;
    639     pub const datarel = 0x30;
    640     pub const funcrel = 0x40;
    641     pub const aligned = 0x50;
    642     pub const indirect = 0x80;
    643     pub const omit = 0xFF;
    644 };