authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-12-10 00:50:52+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-12-10 00:50:52+01:00
loga2d8e039312a1b5505a8e8d0e114da6abc3fc7e6
tree7a8030543412deda949f9f246e082e3eaab5ba68
parent5bda88f9a37604f95e66274362b98514c685c715

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


1 files changed, 160 insertions(+), 5 deletions(-)

lib/std/Build/Step/CheckObject.zig+160-5
...@@ -577,9 +577,12 @@ const MachODumper = struct {...@@ -577,9 +577,12 @@ const MachODumper = struct {
577 const writer = output.writer();577 const writer = output.writer();
578578
579 var symtab: ?Symtab = null;579 var symtab: ?Symtab = null;
580 var segments = std.ArrayList(macho.segment_command_u64);580 var segments = std.ArrayList(macho.segment_command_64).init(gpa);
581 defer segments.deinit();
581 var sections = std.ArrayList(macho.section_64).init(gpa);582 var sections = std.ArrayList(macho.section_64).init(gpa);
583 defer sections.deinit();
582 var imports = std.ArrayList([]const u8).init(gpa);584 var imports = std.ArrayList([]const u8).init(gpa);
585 defer imports.deinit();
583 var text_seg: ?u8 = null;586 var text_seg: ?u8 = null;
584 var dyld_info_lc: ?macho.dyld_info_command = null;587 var dyld_info_lc: ?macho.dyld_info_command = null;
585588
...@@ -636,10 +639,27 @@ const MachODumper = struct {...@@ -636,10 +639,27 @@ const MachODumper = struct {
636 try writer.writeAll(dyld_info_label ++ "\n");639 try writer.writeAll(dyld_info_label ++ "\n");
637 if (lc.rebase_size > 0) {640 if (lc.rebase_size > 0) {
638 const data = bytes[lc.rebase_off..][0..lc.rebase_size];641 const data = bytes[lc.rebase_off..][0..lc.rebase_size];
642 try writer.writeAll("rebase info\n");
639 try dumpRebaseInfo(gpa, data, segments.items, writer);643 try dumpRebaseInfo(gpa, data, segments.items, writer);
640 }644 }
645 if (lc.bind_size > 0) {
646 const data = bytes[lc.bind_off..][0..lc.bind_size];
647 try writer.writeAll("bind info\n");
648 try dumpBindInfo(gpa, data, segments.items, imports.items, false, writer);
649 }
650 if (lc.weak_bind_size > 0) {
651 const data = bytes[lc.weak_bind_off..][0..lc.weak_bind_size];
652 try writer.writeAll("weak bind info\n");
653 try dumpBindInfo(gpa, data, segments.items, imports.items, false, writer);
654 }
655 if (lc.lazy_bind_size > 0) {
656 const data = bytes[lc.lazy_bind_off..][0..lc.lazy_bind_size];
657 try writer.writeAll("lazy bind info\n");
658 try dumpBindInfo(gpa, data, segments.items, imports.items, true, writer);
659 }
641 if (lc.export_size > 0) {660 if (lc.export_size > 0) {
642 const data = bytes[lc.export_off..][0..lc.export_size];661 const data = bytes[lc.export_off..][0..lc.export_size];
662 try writer.writeAll("exports\n");
643 try dumpExportsTrie(gpa, data, segments.items[text_seg.?], writer);663 try dumpExportsTrie(gpa, data, segments.items[text_seg.?], writer);
644 }664 }
645 }665 }
...@@ -1011,7 +1031,6 @@ const MachODumper = struct {...@@ -1011,7 +1031,6 @@ const MachODumper = struct {
1011 defer rebases.deinit();1031 defer rebases.deinit();
1012 try parseRebaseInfo(data, segments, &rebases);1032 try parseRebaseInfo(data, segments, &rebases);
1013 mem.sort(u64, rebases.items, {}, std.sort.asc(u64));1033 mem.sort(u64, rebases.items, {}, std.sort.asc(u64));
1014 try writer.writeAll("rebase info\n");
1015 for (rebases.items) |addr| {1034 for (rebases.items) |addr| {
1016 try writer.print("0x{x}\n", .{addr});1035 try writer.print("0x{x}\n", .{addr});
1017 }1036 }
...@@ -1048,7 +1067,7 @@ const MachODumper = struct {...@@ -1048,7 +1067,7 @@ const MachODumper = struct {
1048 },1067 },
1049 macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB => {1068 macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB => {
1050 const addend = try std.leb.readULEB128(u64, reader);1069 const addend = try std.leb.readULEB128(u64, reader);
1051 const seg = segments.items[seg_id.?];1070 const seg = segments[seg_id.?];
1052 const addr = seg.vmaddr + offset;1071 const addr = seg.vmaddr + offset;
1053 try rebases.append(addr);1072 try rebases.append(addr);
1054 offset += addend + @sizeOf(u64);1073 offset += addend + @sizeOf(u64);
...@@ -1072,7 +1091,7 @@ const MachODumper = struct {...@@ -1072,7 +1091,7 @@ const MachODumper = struct {
1072 },1091 },
1073 else => unreachable,1092 else => unreachable,
1074 }1093 }
1075 const seg = segments.items[seg_id.?];1094 const seg = segments[seg_id.?];
1076 const base_addr = seg.vmaddr;1095 const base_addr = seg.vmaddr;
1077 var count: usize = 0;1096 var count: usize = 0;
1078 while (count < ntimes) : (count += 1) {1097 while (count < ntimes) : (count += 1) {
...@@ -1086,6 +1105,143 @@ const MachODumper = struct {...@@ -1086,6 +1105,143 @@ const MachODumper = struct {
1086 }1105 }
1087 }1106 }
10881107
1108 const Binding = struct {
1109 address: u64,
1110 addend: i64,
1111 ordinal: ?u16,
1112 name: []const u8,
1113
1114 fn deinit(binding: *Binding, gpa: Allocator) void {
1115 gpa.free(binding.name);
1116 }
1117
1118 fn lessThan(ctx: void, lhs: Binding, rhs: Binding) bool {
1119 _ = ctx;
1120 return lhs.address < rhs.address;
1121 }
1122 };
1123
1124 fn dumpBindInfo(
1125 gpa: Allocator,
1126 data: []const u8,
1127 segments: []const macho.segment_command_64,
1128 dylibs: []const []const u8,
1129 is_lazy: bool,
1130 writer: anytype,
1131 ) !void {
1132 var bindings = std.ArrayList(Binding).init(gpa);
1133 defer {
1134 for (bindings.items) |*b| {
1135 b.deinit(gpa);
1136 }
1137 bindings.deinit();
1138 }
1139 try parseBindInfo(gpa, data, segments, &bindings, is_lazy);
1140 mem.sort(Binding, bindings.items, {}, Binding.lessThan);
1141 for (bindings.items) |binding| {
1142 try writer.print("0x{x} [addend: {d}]", .{ binding.address, binding.addend });
1143 if (binding.ordinal) |ord| {
1144 try writer.print(" ({s})", .{std.fs.path.basename(dylibs[ord - 1])});
1145 }
1146 try writer.print(" {s}\n", .{binding.name});
1147 }
1148 }
1149
1150 fn parseBindInfo(
1151 gpa: Allocator,
1152 data: []const u8,
1153 segments: []const macho.segment_command_64,
1154 bindings: *std.ArrayList(Binding),
1155 lazy_ops: bool,
1156 ) !void {
1157 var stream = std.io.fixedBufferStream(data);
1158 var creader = std.io.countingReader(stream.reader());
1159 const reader = creader.reader();
1160
1161 var seg_id: ?u8 = null;
1162 var dylib_id: ?u16 = null;
1163 var offset: u64 = 0;
1164 var addend: i64 = 0;
1165
1166 var name_buf = std.ArrayList(u8).init(gpa);
1167 defer name_buf.deinit();
1168
1169 while (true) {
1170 const byte = reader.readByte() catch break;
1171 const opc = byte & macho.BIND_OPCODE_MASK;
1172 const imm = byte & macho.BIND_IMMEDIATE_MASK;
1173 switch (opc) {
1174 macho.BIND_OPCODE_DONE => {
1175 if (!lazy_ops) break;
1176 },
1177 macho.BIND_OPCODE_SET_TYPE_IMM => {
1178 if (lazy_ops) break;
1179 },
1180 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
1181 dylib_id = imm;
1182 },
1183 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
1184 seg_id = imm;
1185 offset = try std.leb.readULEB128(u64, reader);
1186 },
1187 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
1188 name_buf.clearRetainingCapacity();
1189 try reader.readUntilDelimiterArrayList(&name_buf, 0, std.math.maxInt(u32));
1190 try name_buf.append(0);
1191 },
1192 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
1193 addend = try std.leb.readILEB128(i64, reader);
1194 },
1195 macho.BIND_OPCODE_ADD_ADDR_ULEB => {
1196 if (lazy_ops) break;
1197 const x = try std.leb.readULEB128(u64, reader);
1198 offset = @intCast(@as(i64, @intCast(offset)) + @as(i64, @bitCast(x)));
1199 },
1200 macho.BIND_OPCODE_DO_BIND,
1201 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB,
1202 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED,
1203 macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB,
1204 => {
1205 var add_addr: u64 = 0;
1206 var count: u64 = 1;
1207 var skip: u64 = 0;
1208
1209 switch (opc) {
1210 macho.BIND_OPCODE_DO_BIND => {},
1211 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB => {
1212 if (lazy_ops) break;
1213 add_addr = try std.leb.readULEB128(u64, reader);
1214 },
1215 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED => {
1216 if (lazy_ops) break;
1217 add_addr = imm * @sizeOf(u64);
1218 },
1219 macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB => {
1220 if (lazy_ops) break;
1221 count = try std.leb.readULEB128(u64, reader);
1222 skip = try std.leb.readULEB128(u64, reader);
1223 },
1224 else => unreachable,
1225 }
1226
1227 const seg = segments[seg_id.?];
1228 var i: u64 = 0;
1229 while (i < count) : (i += 1) {
1230 const addr: u64 = @intCast(@as(i64, @intCast(seg.vmaddr + offset)));
1231 try bindings.append(.{
1232 .address = addr,
1233 .addend = addend,
1234 .ordinal = dylib_id,
1235 .name = try gpa.dupe(u8, name_buf.items),
1236 });
1237 offset += skip + @sizeOf(u64) + add_addr;
1238 }
1239 },
1240 else => break,
1241 }
1242 }
1243 }
1244
1089 fn dumpExportsTrie(1245 fn dumpExportsTrie(
1090 gpa: Allocator,1246 gpa: Allocator,
1091 data: []const u8,1247 data: []const u8,
...@@ -1101,7 +1257,6 @@ const MachODumper = struct {...@@ -1101,7 +1257,6 @@ const MachODumper = struct {
11011257
1102 mem.sort(Export, exports.items, {}, Export.lessThan);1258 mem.sort(Export, exports.items, {}, Export.lessThan);
11031259
1104 try writer.writeAll("exports\n");
1105 for (exports.items) |exp| {1260 for (exports.items) |exp| {
1106 switch (exp.tag) {1261 switch (exp.tag) {
1107 .@"export" => {1262 .@"export" => {