diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2023-12-10 00:50:52 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2023-12-10 00:50:52 +0100 |
| commit | a2d8e039312a1b5505a8e8d0e114da6abc3fc7e6 (patch) | |
| tree | 7a8030543412deda949f9f246e082e3eaab5ba68 /lib/std/Build/Step/CheckObject.zig | |
| parent | 5bda88f9a37604f95e66274362b98514c685c715 (diff) | |
| download | zig-a2d8e039312a1b5505a8e8d0e114da6abc3fc7e6.tar.gz zig-a2d8e039312a1b5505a8e8d0e114da6abc3fc7e6.zip | |
lib/std/Build/CheckObject: dump Mach-O dyld_info_only bind, weak-bind and lazy-bind data
Diffstat (limited to 'lib/std/Build/Step/CheckObject.zig')
| -rw-r--r-- | lib/std/Build/Step/CheckObject.zig | 165 |
1 files changed, 160 insertions, 5 deletions
diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index cea95595cd..5251cf15c5 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -577,9 +577,12 @@ const MachODumper = struct { const writer = output.writer(); var symtab: ?Symtab = null; - var segments = std.ArrayList(macho.segment_command_u64); + var segments = std.ArrayList(macho.segment_command_64).init(gpa); + defer segments.deinit(); var sections = std.ArrayList(macho.section_64).init(gpa); + defer sections.deinit(); var imports = std.ArrayList([]const u8).init(gpa); + defer imports.deinit(); var text_seg: ?u8 = null; var dyld_info_lc: ?macho.dyld_info_command = null; @@ -636,10 +639,27 @@ const MachODumper = struct { try writer.writeAll(dyld_info_label ++ "\n"); if (lc.rebase_size > 0) { const data = bytes[lc.rebase_off..][0..lc.rebase_size]; + try writer.writeAll("rebase info\n"); try dumpRebaseInfo(gpa, data, segments.items, writer); } + if (lc.bind_size > 0) { + const data = bytes[lc.bind_off..][0..lc.bind_size]; + try writer.writeAll("bind info\n"); + try dumpBindInfo(gpa, data, segments.items, imports.items, false, writer); + } + if (lc.weak_bind_size > 0) { + const data = bytes[lc.weak_bind_off..][0..lc.weak_bind_size]; + try writer.writeAll("weak bind info\n"); + try dumpBindInfo(gpa, data, segments.items, imports.items, false, writer); + } + if (lc.lazy_bind_size > 0) { + const data = bytes[lc.lazy_bind_off..][0..lc.lazy_bind_size]; + try writer.writeAll("lazy bind info\n"); + try dumpBindInfo(gpa, data, segments.items, imports.items, true, writer); + } if (lc.export_size > 0) { const data = bytes[lc.export_off..][0..lc.export_size]; + try writer.writeAll("exports\n"); try dumpExportsTrie(gpa, data, segments.items[text_seg.?], writer); } } @@ -1011,7 +1031,6 @@ const MachODumper = struct { defer rebases.deinit(); try parseRebaseInfo(data, segments, &rebases); mem.sort(u64, rebases.items, {}, std.sort.asc(u64)); - try writer.writeAll("rebase info\n"); for (rebases.items) |addr| { try writer.print("0x{x}\n", .{addr}); } @@ -1048,7 +1067,7 @@ const MachODumper = struct { }, macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB => { const addend = try std.leb.readULEB128(u64, reader); - const seg = segments.items[seg_id.?]; + const seg = segments[seg_id.?]; const addr = seg.vmaddr + offset; try rebases.append(addr); offset += addend + @sizeOf(u64); @@ -1072,7 +1091,7 @@ const MachODumper = struct { }, else => unreachable, } - const seg = segments.items[seg_id.?]; + const seg = segments[seg_id.?]; const base_addr = seg.vmaddr; var count: usize = 0; while (count < ntimes) : (count += 1) { @@ -1086,6 +1105,143 @@ const MachODumper = struct { } } + const Binding = struct { + address: u64, + addend: i64, + ordinal: ?u16, + name: []const u8, + + fn deinit(binding: *Binding, gpa: Allocator) void { + gpa.free(binding.name); + } + + fn lessThan(ctx: void, lhs: Binding, rhs: Binding) bool { + _ = ctx; + return lhs.address < rhs.address; + } + }; + + fn dumpBindInfo( + gpa: Allocator, + data: []const u8, + segments: []const macho.segment_command_64, + dylibs: []const []const u8, + is_lazy: bool, + writer: anytype, + ) !void { + var bindings = std.ArrayList(Binding).init(gpa); + defer { + for (bindings.items) |*b| { + b.deinit(gpa); + } + bindings.deinit(); + } + try parseBindInfo(gpa, data, segments, &bindings, is_lazy); + mem.sort(Binding, bindings.items, {}, Binding.lessThan); + for (bindings.items) |binding| { + try writer.print("0x{x} [addend: {d}]", .{ binding.address, binding.addend }); + if (binding.ordinal) |ord| { + try writer.print(" ({s})", .{std.fs.path.basename(dylibs[ord - 1])}); + } + try writer.print(" {s}\n", .{binding.name}); + } + } + + fn parseBindInfo( + gpa: Allocator, + data: []const u8, + segments: []const macho.segment_command_64, + bindings: *std.ArrayList(Binding), + lazy_ops: bool, + ) !void { + var stream = std.io.fixedBufferStream(data); + var creader = std.io.countingReader(stream.reader()); + const reader = creader.reader(); + + var seg_id: ?u8 = null; + var dylib_id: ?u16 = null; + var offset: u64 = 0; + var addend: i64 = 0; + + var name_buf = std.ArrayList(u8).init(gpa); + defer name_buf.deinit(); + + while (true) { + const byte = reader.readByte() catch break; + const opc = byte & macho.BIND_OPCODE_MASK; + const imm = byte & macho.BIND_IMMEDIATE_MASK; + switch (opc) { + macho.BIND_OPCODE_DONE => { + if (!lazy_ops) break; + }, + macho.BIND_OPCODE_SET_TYPE_IMM => { + if (lazy_ops) break; + }, + macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => { + dylib_id = imm; + }, + macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { + seg_id = imm; + offset = try std.leb.readULEB128(u64, reader); + }, + macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { + name_buf.clearRetainingCapacity(); + try reader.readUntilDelimiterArrayList(&name_buf, 0, std.math.maxInt(u32)); + try name_buf.append(0); + }, + macho.BIND_OPCODE_SET_ADDEND_SLEB => { + addend = try std.leb.readILEB128(i64, reader); + }, + macho.BIND_OPCODE_ADD_ADDR_ULEB => { + if (lazy_ops) break; + const x = try std.leb.readULEB128(u64, reader); + offset = @intCast(@as(i64, @intCast(offset)) + @as(i64, @bitCast(x))); + }, + macho.BIND_OPCODE_DO_BIND, + macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB, + macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED, + macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB, + => { + var add_addr: u64 = 0; + var count: u64 = 1; + var skip: u64 = 0; + + switch (opc) { + macho.BIND_OPCODE_DO_BIND => {}, + macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB => { + if (lazy_ops) break; + add_addr = try std.leb.readULEB128(u64, reader); + }, + macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED => { + if (lazy_ops) break; + add_addr = imm * @sizeOf(u64); + }, + macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB => { + if (lazy_ops) break; + count = try std.leb.readULEB128(u64, reader); + skip = try std.leb.readULEB128(u64, reader); + }, + else => unreachable, + } + + const seg = segments[seg_id.?]; + var i: u64 = 0; + while (i < count) : (i += 1) { + const addr: u64 = @intCast(@as(i64, @intCast(seg.vmaddr + offset))); + try bindings.append(.{ + .address = addr, + .addend = addend, + .ordinal = dylib_id, + .name = try gpa.dupe(u8, name_buf.items), + }); + offset += skip + @sizeOf(u64) + add_addr; + } + }, + else => break, + } + } + } + fn dumpExportsTrie( gpa: Allocator, data: []const u8, @@ -1101,7 +1257,6 @@ const MachODumper = struct { mem.sort(Export, exports.items, {}, Export.lessThan); - try writer.writeAll("exports\n"); for (exports.items) |exp| { switch (exp.tag) { .@"export" => { |
