| ... | ... | @@ -577,9 +577,12 @@ const MachODumper = struct { |
| 577 | 577 | const writer = output.writer(); |
| 578 | 578 | |
| 579 | 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 | 582 | var sections = std.ArrayList(macho.section_64).init(gpa); |
| 583 | defer sections.deinit(); |
| 582 | 584 | var imports = std.ArrayList([]const u8).init(gpa); |
| 585 | defer imports.deinit(); |
| 583 | 586 | var text_seg: ?u8 = null; |
| 584 | 587 | var dyld_info_lc: ?macho.dyld_info_command = null; |
| 585 | 588 | |
| ... | ... | @@ -636,10 +639,27 @@ const MachODumper = struct { |
| 636 | 639 | try writer.writeAll(dyld_info_label ++ "\n"); |
| 637 | 640 | if (lc.rebase_size > 0) { |
| 638 | 641 | const data = bytes[lc.rebase_off..][0..lc.rebase_size]; |
| 642 | try writer.writeAll("rebase info\n"); |
| 639 | 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 | 660 | if (lc.export_size > 0) { |
| 642 | 661 | const data = bytes[lc.export_off..][0..lc.export_size]; |
| 662 | try writer.writeAll("exports\n"); |
| 643 | 663 | try dumpExportsTrie(gpa, data, segments.items[text_seg.?], writer); |
| 644 | 664 | } |
| 645 | 665 | } |
| ... | ... | @@ -1011,7 +1031,6 @@ const MachODumper = struct { |
| 1011 | 1031 | defer rebases.deinit(); |
| 1012 | 1032 | try parseRebaseInfo(data, segments, &rebases); |
| 1013 | 1033 | mem.sort(u64, rebases.items, {}, std.sort.asc(u64)); |
| 1014 | | try writer.writeAll("rebase info\n"); |
| 1015 | 1034 | for (rebases.items) |addr| { |
| 1016 | 1035 | try writer.print("0x{x}\n", .{addr}); |
| 1017 | 1036 | } |
| ... | ... | @@ -1048,7 +1067,7 @@ const MachODumper = struct { |
| 1048 | 1067 | }, |
| 1049 | 1068 | macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB => { |
| 1050 | 1069 | const addend = try std.leb.readULEB128(u64, reader); |
| 1051 | | const seg = segments.items[seg_id.?]; |
| 1070 | const seg = segments[seg_id.?]; |
| 1052 | 1071 | const addr = seg.vmaddr + offset; |
| 1053 | 1072 | try rebases.append(addr); |
| 1054 | 1073 | offset += addend + @sizeOf(u64); |
| ... | ... | @@ -1072,7 +1091,7 @@ const MachODumper = struct { |
| 1072 | 1091 | }, |
| 1073 | 1092 | else => unreachable, |
| 1074 | 1093 | } |
| 1075 | | const seg = segments.items[seg_id.?]; |
| 1094 | const seg = segments[seg_id.?]; |
| 1076 | 1095 | const base_addr = seg.vmaddr; |
| 1077 | 1096 | var count: usize = 0; |
| 1078 | 1097 | while (count < ntimes) : (count += 1) { |
| ... | ... | @@ -1086,6 +1105,143 @@ const MachODumper = struct { |
| 1086 | 1105 | } |
| 1087 | 1106 | } |
| 1088 | 1107 | |
| 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 | 1245 | fn dumpExportsTrie( |
| 1090 | 1246 | gpa: Allocator, |
| 1091 | 1247 | data: []const u8, |
| ... | ... | @@ -1101,7 +1257,6 @@ const MachODumper = struct { |
| 1101 | 1257 | |
| 1102 | 1258 | mem.sort(Export, exports.items, {}, Export.lessThan); |
| 1103 | 1259 | |
| 1104 | | try writer.writeAll("exports\n"); |
| 1105 | 1260 | for (exports.items) |exp| { |
| 1106 | 1261 | switch (exp.tag) { |
| 1107 | 1262 | .@"export" => { |