authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-12-09 10:57:12+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-12-09 10:57:12+01:00
log40952b4cdbd81e10236ba4b6c126b46a8b87b60e
tree0611728a05e779b4751142a5de3e309d22a28e00
parenteb70c8801e62a7f81ffcea735cb628995ca35d42

lib/std/Build/CheckObject: dump Mach-O dyld_info_only exports data


1 files changed, 210 insertions(+), 0 deletions(-)

lib/std/Build/Step/CheckObject.zig+210
......@@ -381,6 +381,18 @@ pub fn checkInSymtab(self: *CheckObject) void {
381381 self.checkExact(label);
382382}
383383
384/// Creates a new check checking specifically dyld_info_only contents parsed and dumped
385/// from the object file.
386/// This check is target-dependent and applicable to MachO only.
387pub fn checkInDyldInfo(self: *CheckObject) void {
388 const label = switch (self.obj_format) {
389 .macho => MachODumper.dyld_info_label,
390 else => @panic("Unsupported target platform"),
391 };
392 self.checkStart();
393 self.checkExact(label);
394}
395
384396/// Creates a new check checking specifically dynamic symbol table parsed and dumped from the object
385397/// file.
386398/// This check is target-dependent and applicable to ELF only.
......@@ -543,6 +555,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
543555
544556const MachODumper = struct {
545557 const LoadCommandIterator = macho.LoadCommandIterator;
558 const dyld_info_label = "dyld info data";
546559 const symtab_label = "symbol table";
547560
548561 const Symtab = struct {
......@@ -566,6 +579,8 @@ const MachODumper = struct {
566579 var symtab: ?Symtab = null;
567580 var sections = std.ArrayList(macho.section_64).init(gpa);
568581 var imports = std.ArrayList([]const u8).init(gpa);
582 var text_seg: ?macho.segment_command_64 = null;
583 var dyld_info_lc: ?macho.dyld_info_command = null;
569584
570585 try dumpHeader(hdr, writer);
571586
......@@ -582,6 +597,9 @@ const MachODumper = struct {
582597 for (cmd.getSections()) |sect| {
583598 sections.appendAssumeCapacity(sect);
584599 }
600 if (mem.eql(u8, seg.segName(), "__TEXT")) {
601 text_seg = seg;
602 }
585603 },
586604 .SYMTAB => {
587605 const lc = cmd.cast(macho.symtab_command).?;
......@@ -595,6 +613,9 @@ const MachODumper = struct {
595613 => {
596614 try imports.append(cmd.getDylibPathName());
597615 },
616 .DYLD_INFO_ONLY => {
617 dyld_info_lc = cmd.cast(macho.dyld_info_command).?;
618 },
598619 else => {},
599620 }
600621
......@@ -608,6 +629,14 @@ const MachODumper = struct {
608629 try dumpSymtab(sections.items, imports.items, stab, writer);
609630 }
610631
632 if (dyld_info_lc) |lc| {
633 try writer.writeAll(dyld_info_label ++ "\n");
634 if (lc.export_size > 0) {
635 const data = bytes[lc.export_off..][0..lc.export_size];
636 try dumpExportsTrie(gpa, data, text_seg.?, writer);
637 }
638 }
639
611640 return output.toOwnedSlice();
612641 }
613642
......@@ -964,6 +993,187 @@ const MachODumper = struct {
964993 }
965994 }
966995 }
996
997 fn dumpExportsTrie(
998 gpa: Allocator,
999 data: []const u8,
1000 seg: macho.segment_command_64,
1001 writer: anytype,
1002 ) !void {
1003 var arena = std.heap.ArenaAllocator.init(gpa);
1004 defer arena.deinit();
1005
1006 var exports = std.ArrayList(Export).init(arena.allocator());
1007 var it = TrieIterator{ .data = data };
1008 try parseTrieNode(arena.allocator(), &it, "", &exports);
1009
1010 mem.sort(Export, exports.items, {}, Export.lessThan);
1011
1012 try writer.writeAll("exports\n");
1013 for (exports.items) |exp| {
1014 switch (exp.tag) {
1015 .@"export" => {
1016 const info = exp.data.@"export";
1017 if (info.kind != .regular or info.weak) {
1018 try writer.writeByte('[');
1019 }
1020 switch (info.kind) {
1021 .regular => {},
1022 .absolute => try writer.writeAll("ABS, "),
1023 .tlv => try writer.writeAll("THREAD_LOCAL, "),
1024 }
1025 if (info.weak) try writer.writeAll("WEAK");
1026 if (info.kind != .regular or info.weak) {
1027 try writer.writeAll("] ");
1028 }
1029 try writer.print("{x} ", .{seg.vmaddr + info.vmoffset});
1030 },
1031 else => {},
1032 }
1033
1034 try writer.print("{s}\n", .{exp.name});
1035 }
1036 }
1037
1038 const TrieIterator = struct {
1039 data: []const u8,
1040 pos: usize = 0,
1041
1042 fn getStream(it: *TrieIterator) std.io.FixedBufferStream([]const u8) {
1043 return std.io.fixedBufferStream(it.data[it.pos..]);
1044 }
1045
1046 fn readULEB128(it: *TrieIterator) !u64 {
1047 var stream = it.getStream();
1048 var creader = std.io.countingReader(stream.reader());
1049 const reader = creader.reader();
1050 const value = try std.leb.readULEB128(u64, reader);
1051 it.pos += creader.bytes_read;
1052 return value;
1053 }
1054
1055 fn readString(it: *TrieIterator) ![:0]const u8 {
1056 var stream = it.getStream();
1057 const reader = stream.reader();
1058
1059 var count: usize = 0;
1060 while (true) : (count += 1) {
1061 const byte = try reader.readByte();
1062 if (byte == 0) break;
1063 }
1064
1065 const str = @as([*:0]const u8, @ptrCast(it.data.ptr + it.pos))[0..count :0];
1066 it.pos += count + 1;
1067 return str;
1068 }
1069
1070 fn readByte(it: *TrieIterator) !u8 {
1071 var stream = it.getStream();
1072 const value = try stream.reader().readByte();
1073 it.pos += 1;
1074 return value;
1075 }
1076 };
1077
1078 const Export = struct {
1079 name: []const u8,
1080 tag: enum { @"export", reexport, stub_resolver },
1081 data: union {
1082 @"export": struct {
1083 kind: enum { regular, absolute, tlv },
1084 weak: bool = false,
1085 vmoffset: u64,
1086 },
1087 reexport: u64,
1088 stub_resolver: struct {
1089 stub_offset: u64,
1090 resolver_offset: u64,
1091 },
1092 },
1093
1094 inline fn rankByTag(self: Export) u3 {
1095 return switch (self.tag) {
1096 .@"export" => 1,
1097 .reexport => 2,
1098 .stub_resolver => 3,
1099 };
1100 }
1101
1102 fn lessThan(ctx: void, lhs: Export, rhs: Export) bool {
1103 _ = ctx;
1104 if (lhs.rankByTag() == rhs.rankByTag()) {
1105 return switch (lhs.tag) {
1106 .@"export" => lhs.data.@"export".vmoffset < rhs.data.@"export".vmoffset,
1107 .reexport => lhs.data.reexport < rhs.data.reexport,
1108 .stub_resolver => lhs.data.stub_resolver.stub_offset < rhs.data.stub_resolver.stub_offset,
1109 };
1110 }
1111 return lhs.rankByTag() < rhs.rankByTag();
1112 }
1113 };
1114
1115 fn parseTrieNode(
1116 arena: Allocator,
1117 it: *TrieIterator,
1118 prefix: []const u8,
1119 exports: *std.ArrayList(Export),
1120 ) !void {
1121 const size = try it.readULEB128();
1122 if (size > 0) {
1123 const flags = try it.readULEB128();
1124 switch (flags) {
1125 macho.EXPORT_SYMBOL_FLAGS_REEXPORT => {
1126 const ord = try it.readULEB128();
1127 const name = try arena.dupe(u8, try it.readString());
1128 try exports.append(.{
1129 .name = if (name.len > 0) name else prefix,
1130 .tag = .reexport,
1131 .data = .{ .reexport = ord },
1132 });
1133 },
1134 macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => {
1135 const stub_offset = try it.readULEB128();
1136 const resolver_offset = try it.readULEB128();
1137 try exports.append(.{
1138 .name = prefix,
1139 .tag = .stub_resolver,
1140 .data = .{ .stub_resolver = .{
1141 .stub_offset = stub_offset,
1142 .resolver_offset = resolver_offset,
1143 } },
1144 });
1145 },
1146 else => {
1147 const vmoff = try it.readULEB128();
1148 try exports.append(.{
1149 .name = prefix,
1150 .tag = .@"export",
1151 .data = .{ .@"export" = .{
1152 .kind = switch (flags & macho.EXPORT_SYMBOL_FLAGS_KIND_MASK) {
1153 macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR => .regular,
1154 macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE => .absolute,
1155 macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL => .tlv,
1156 else => unreachable,
1157 },
1158 .weak = flags & macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION != 0,
1159 .vmoffset = vmoff,
1160 } },
1161 });
1162 },
1163 }
1164 }
1165
1166 const nedges = try it.readByte();
1167 for (0..nedges) |_| {
1168 const label = try it.readString();
1169 const off = try it.readULEB128();
1170 const prefix_label = try std.fmt.allocPrint(arena, "{s}{s}", .{ prefix, label });
1171 const curr = it.pos;
1172 it.pos = off;
1173 try parseTrieNode(arena, it, prefix_label, exports);
1174 it.pos = curr;
1175 }
1176 }
9671177};
9681178
9691179const ElfDumper = struct {