lib/std/Build/CheckObject: dump Mach-O dyld_info_only bind, weak-bind and lazy-bind data

This commit is contained in:
Jakub Konka 2023-12-10 00:50:52 +01:00
parent 5bda88f9a3
commit a2d8e03931

View File

@ -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" => {