| author | |
| committer | |
| log | 23a63f4ce445d26d7fc577eecc6c3f5ca129a007 |
| tree | 2cf2f80e1eb9cd8e64165c133ab36493814eb037 |
| parent | b5601a2da60df2f8f2bc6ac1ef287d4733a47df2 |
8 files changed, 366 insertions(+), 342 deletions(-)
lib/std/build.zig+4-4| ... | ... | @@ -24,7 +24,7 @@ pub const TranslateCStep = @import("build/TranslateCStep.zig"); |
| 24 | 24 | pub const WriteFileStep = @import("build/WriteFileStep.zig"); |
| 25 | 25 | pub const RunStep = @import("build/RunStep.zig"); |
| 26 | 26 | pub const CheckFileStep = @import("build/CheckFileStep.zig"); |
| 27 | pub const CheckMachOStep = @import("build/CheckMachOStep.zig"); | |
| 27 | pub const CheckObjectStep = @import("build/CheckObjectStep.zig"); | |
| 28 | 28 | pub const InstallRawStep = @import("build/InstallRawStep.zig"); |
| 29 | 29 | pub const OptionsStep = @import("build/OptionsStep.zig"); |
| 30 | 30 | |
| ... | ... | @@ -1865,8 +1865,8 @@ pub const LibExeObjStep = struct { |
| 1865 | 1865 | return run_step; |
| 1866 | 1866 | } |
| 1867 | 1867 | |
| 1868 | pub fn checkMachO(self: *LibExeObjStep) *CheckMachOStep { | |
| 1869 | return CheckMachOStep.create(self.builder, self.getOutputSource()); | |
| 1868 | pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { | |
| 1869 | return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format); | |
| 1870 | 1870 | } |
| 1871 | 1871 | |
| 1872 | 1872 | pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void { |
| ... | ... | @@ -3455,7 +3455,7 @@ pub const Step = struct { |
| 3455 | 3455 | write_file, |
| 3456 | 3456 | run, |
| 3457 | 3457 | check_file, |
| 3458 | check_macho, | |
| 3458 | check_object, | |
| 3459 | 3459 | install_raw, |
| 3460 | 3460 | options, |
| 3461 | 3461 | custom, |
lib/std/build/CheckMachOStep.zig deleted-331| ... | ... | @@ -1,331 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const build = std.build; | |
| 4 | const fs = std.fs; | |
| 5 | const macho = std.macho; | |
| 6 | const mem = std.mem; | |
| 7 | ||
| 8 | const CheckMachOStep = @This(); | |
| 9 | ||
| 10 | const Allocator = mem.Allocator; | |
| 11 | const Builder = build.Builder; | |
| 12 | const Step = build.Step; | |
| 13 | ||
| 14 | pub const base_id = .check_macho; | |
| 15 | ||
| 16 | step: Step, | |
| 17 | builder: *Builder, | |
| 18 | source: build.FileSource, | |
| 19 | max_bytes: usize = 20 * 1024 * 1024, | |
| 20 | checks: std.ArrayList(Check), | |
| 21 | dump_symtab: bool = false, | |
| 22 | ||
| 23 | pub fn create(builder: *Builder, source: build.FileSource) *CheckMachOStep { | |
| 24 | const gpa = builder.allocator; | |
| 25 | const self = gpa.create(CheckMachOStep) catch unreachable; | |
| 26 | self.* = CheckMachOStep{ | |
| 27 | .builder = builder, | |
| 28 | .step = Step.init(.check_file, "CheckMachO", gpa, make), | |
| 29 | .source = source.dupe(builder), | |
| 30 | .checks = std.ArrayList(Check).init(gpa), | |
| 31 | }; | |
| 32 | self.source.addStepDependencies(&self.step); | |
| 33 | return self; | |
| 34 | } | |
| 35 | ||
| 36 | const Action = union(enum) { | |
| 37 | exact_match: []const u8, | |
| 38 | extract_var: struct { | |
| 39 | fuzzy_match: []const u8, | |
| 40 | var_name: []const u8, | |
| 41 | var_value: u64, | |
| 42 | }, | |
| 43 | compare: CompareAction, | |
| 44 | }; | |
| 45 | ||
| 46 | const CompareAction = struct { | |
| 47 | expected: union(enum) { | |
| 48 | literal: u64, | |
| 49 | varr: []const u8, | |
| 50 | }, | |
| 51 | var_stack: std.ArrayList([]const u8), | |
| 52 | op_stack: std.ArrayList(Op), | |
| 53 | ||
| 54 | const Op = enum { | |
| 55 | add, | |
| 56 | }; | |
| 57 | }; | |
| 58 | ||
| 59 | const Check = struct { | |
| 60 | builder: *Builder, | |
| 61 | actions: std.ArrayList(Action), | |
| 62 | ||
| 63 | fn create(b: *Builder) Check { | |
| 64 | return .{ | |
| 65 | .builder = b, | |
| 66 | .actions = std.ArrayList(Action).init(b.allocator), | |
| 67 | }; | |
| 68 | } | |
| 69 | ||
| 70 | fn exactMatch(self: *Check, phrase: []const u8) void { | |
| 71 | self.actions.append(.{ | |
| 72 | .exact_match = self.builder.dupe(phrase), | |
| 73 | }) catch unreachable; | |
| 74 | } | |
| 75 | ||
| 76 | fn extractVar(self: *Check, phrase: []const u8, var_name: []const u8) void { | |
| 77 | self.actions.append(.{ | |
| 78 | .extract_var = .{ | |
| 79 | .fuzzy_match = self.builder.dupe(phrase), | |
| 80 | .var_name = self.builder.dupe(var_name), | |
| 81 | .var_value = undefined, | |
| 82 | }, | |
| 83 | }) catch unreachable; | |
| 84 | } | |
| 85 | }; | |
| 86 | ||
| 87 | pub fn check(self: *CheckMachOStep, phrase: []const u8) void { | |
| 88 | var new_check = Check.create(self.builder); | |
| 89 | new_check.exactMatch(phrase); | |
| 90 | self.checks.append(new_check) catch unreachable; | |
| 91 | } | |
| 92 | ||
| 93 | pub fn checkNext(self: *CheckMachOStep, phrase: []const u8) void { | |
| 94 | assert(self.checks.items.len > 0); | |
| 95 | const last = &self.checks.items[self.checks.items.len - 1]; | |
| 96 | last.exactMatch(phrase); | |
| 97 | } | |
| 98 | ||
| 99 | pub fn checkNextExtract(self: *CheckMachOStep, comptime phrase: []const u8) void { | |
| 100 | assert(self.checks.items.len > 0); | |
| 101 | const matcher_start = comptime mem.indexOf(u8, phrase, "{") orelse | |
| 102 | @compileError("missing { } matcher"); | |
| 103 | const matcher_end = comptime mem.indexOf(u8, phrase, "}") orelse | |
| 104 | @compileError("missing { } matcher"); | |
| 105 | const last = &self.checks.items[self.checks.items.len - 1]; | |
| 106 | last.extractVar(phrase[0..matcher_start], phrase[matcher_start + 1 .. matcher_end]); | |
| 107 | } | |
| 108 | ||
| 109 | pub fn checkInSymtab(self: *CheckMachOStep) void { | |
| 110 | self.dump_symtab = true; | |
| 111 | self.check("symtab"); | |
| 112 | } | |
| 113 | ||
| 114 | pub fn checkCompare(self: *CheckMachOStep, comptime phrase: []const u8, expected: anytype) void { | |
| 115 | comptime assert(phrase[0] == '{'); | |
| 116 | comptime assert(phrase[phrase.len - 1] == '}'); | |
| 117 | ||
| 118 | const gpa = self.builder.allocator; | |
| 119 | var ca = CompareAction{ | |
| 120 | .expected = expected, | |
| 121 | .var_stack = std.ArrayList([]const u8).init(gpa), | |
| 122 | .op_stack = std.ArrayList(CompareAction.Op).init(gpa), | |
| 123 | }; | |
| 124 | ||
| 125 | var it = mem.tokenize(u8, phrase[1 .. phrase.len - 1], " "); | |
| 126 | while (it.next()) |next| { | |
| 127 | if (mem.eql(u8, next, "+")) { | |
| 128 | ca.op_stack.append(.add) catch unreachable; | |
| 129 | } else { | |
| 130 | ca.var_stack.append(self.builder.dupe(next)) catch unreachable; | |
| 131 | } | |
| 132 | } | |
| 133 | ||
| 134 | var new_check = Check.create(self.builder); | |
| 135 | new_check.actions.append(.{ .compare = ca }) catch unreachable; | |
| 136 | self.checks.append(new_check) catch unreachable; | |
| 137 | } | |
| 138 | ||
| 139 | fn make(step: *Step) !void { | |
| 140 | const self = @fieldParentPtr(CheckMachOStep, "step", step); | |
| 141 | ||
| 142 | const gpa = self.builder.allocator; | |
| 143 | const src_path = self.source.getPath(self.builder); | |
| 144 | const contents = try fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); | |
| 145 | ||
| 146 | // Parse the object file's header | |
| 147 | var stream = std.io.fixedBufferStream(contents); | |
| 148 | const reader = stream.reader(); | |
| 149 | ||
| 150 | const hdr = try reader.readStruct(macho.mach_header_64); | |
| 151 | if (hdr.magic != macho.MH_MAGIC_64) { | |
| 152 | return error.InvalidMagicNumber; | |
| 153 | } | |
| 154 | ||
| 155 | var metadata = std.ArrayList(u8).init(gpa); | |
| 156 | const writer = metadata.writer(); | |
| 157 | ||
| 158 | var symtab_cmd: ?macho.symtab_command = null; | |
| 159 | var i: u16 = 0; | |
| 160 | while (i < hdr.ncmds) : (i += 1) { | |
| 161 | var cmd = try macho.LoadCommand.read(gpa, reader); | |
| 162 | ||
| 163 | if (self.dump_symtab and cmd.cmd() == .SYMTAB) { | |
| 164 | symtab_cmd = cmd.symtab; | |
| 165 | } | |
| 166 | ||
| 167 | try dumpLoadCommand(cmd, i, writer); | |
| 168 | try writer.writeByte('\n'); | |
| 169 | } | |
| 170 | ||
| 171 | if (symtab_cmd) |cmd| { | |
| 172 | try writer.writeAll("symtab\n"); | |
| 173 | const strtab = contents[cmd.stroff..][0..cmd.strsize]; | |
| 174 | const symtab = @ptrCast( | |
| 175 | [*]const macho.nlist_64, | |
| 176 | @alignCast(@alignOf(macho.nlist_64), contents.ptr + cmd.symoff), | |
| 177 | )[0..cmd.nsyms]; | |
| 178 | ||
| 179 | for (symtab) |sym| { | |
| 180 | if (sym.stab()) continue; | |
| 181 | const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0); | |
| 182 | try writer.print("{s} {x}\n", .{ sym_name, sym.n_value }); | |
| 183 | } | |
| 184 | } | |
| 185 | ||
| 186 | var vars = std.StringHashMap(u64).init(gpa); | |
| 187 | ||
| 188 | for (self.checks.items) |chk| { | |
| 189 | const first_action = chk.actions.items[0]; | |
| 190 | ||
| 191 | switch (first_action) { | |
| 192 | .exact_match => |first| { | |
| 193 | if (mem.indexOf(u8, metadata.items, first)) |index| { | |
| 194 | // TODO backtrack to track current scope | |
| 195 | var it = std.mem.tokenize(u8, metadata.items[index..], "\r\n"); | |
| 196 | ||
| 197 | outer: for (chk.actions.items[1..]) |next_action| { | |
| 198 | switch (next_action) { | |
| 199 | .exact_match => |exact| { | |
| 200 | while (it.next()) |line| { | |
| 201 | if (mem.eql(u8, line, exact)) { | |
| 202 | std.debug.print("{s} == {s}\n", .{ line, exact }); | |
| 203 | continue :outer; | |
| 204 | } | |
| 205 | std.debug.print("{s} != {s}\n", .{ line, exact }); | |
| 206 | } else { | |
| 207 | return error.TestFailed; | |
| 208 | } | |
| 209 | }, | |
| 210 | .extract_var => |extract| { | |
| 211 | const phrase = extract.fuzzy_match; | |
| 212 | while (it.next()) |line| { | |
| 213 | if (mem.indexOf(u8, line, phrase)) |found| { | |
| 214 | std.debug.print("{s} in {s}\n", .{ phrase, line }); | |
| 215 | // Extract variable and save back in the action. | |
| 216 | const trimmed = mem.trim(u8, line[found + phrase.len ..], " "); | |
| 217 | const parsed = try std.fmt.parseInt(u64, trimmed, 16); | |
| 218 | try vars.putNoClobber(extract.var_name, parsed); | |
| 219 | continue :outer; | |
| 220 | } | |
| 221 | std.debug.print("{s} not in {s}\n", .{ extract.fuzzy_match, line }); | |
| 222 | } | |
| 223 | }, | |
| 224 | .compare => unreachable, | |
| 225 | } | |
| 226 | } | |
| 227 | } else { | |
| 228 | return error.TestFailed; | |
| 229 | } | |
| 230 | }, | |
| 231 | .compare => |act| { | |
| 232 | var values = std.ArrayList(u64).init(gpa); | |
| 233 | try values.ensureTotalCapacity(act.var_stack.items.len); | |
| 234 | for (act.var_stack.items) |vv| { | |
| 235 | const val = vars.get(vv) orelse return error.TestFailed; | |
| 236 | values.appendAssumeCapacity(val); | |
| 237 | } | |
| 238 | ||
| 239 | var op_i: usize = 1; | |
| 240 | var reduced: u64 = values.items[0]; | |
| 241 | for (act.op_stack.items) |op| { | |
| 242 | const other = values.items[op_i]; | |
| 243 | switch (op) { | |
| 244 | .add => { | |
| 245 | reduced += other; | |
| 246 | }, | |
| 247 | } | |
| 248 | } | |
| 249 | ||
| 250 | const expected = switch (act.expected) { | |
| 251 | .literal => |exp| exp, | |
| 252 | .varr => |vv| vars.get(vv) orelse return error.TestFailed, | |
| 253 | }; | |
| 254 | if (reduced != expected) return error.TestFailed; | |
| 255 | }, | |
| 256 | .extract_var => unreachable, | |
| 257 | } | |
| 258 | } | |
| 259 | ||
| 260 | var it = vars.iterator(); | |
| 261 | while (it.next()) |entry| { | |
| 262 | std.debug.print(" {s} => {x}", .{ entry.key_ptr.*, entry.value_ptr.* }); | |
| 263 | } | |
| 264 | } | |
| 265 | ||
| 266 | fn dumpLoadCommand(lc: macho.LoadCommand, index: u16, writer: anytype) !void { | |
| 267 | // print header first | |
| 268 | try writer.print( | |
| 269 | \\LC {d} | |
| 270 | \\cmd {s} | |
| 271 | \\cmdsize {d} | |
| 272 | , .{ index, @tagName(lc.cmd()), lc.cmdsize() }); | |
| 273 | ||
| 274 | switch (lc.cmd()) { | |
| 275 | .SEGMENT_64 => { | |
| 276 | // TODO dump section headers | |
| 277 | const seg = lc.segment.inner; | |
| 278 | try writer.writeByte('\n'); | |
| 279 | try writer.print( | |
| 280 | \\segname {s} | |
| 281 | \\vmaddr {x} | |
| 282 | \\vmsize {x} | |
| 283 | \\fileoff {x} | |
| 284 | \\filesz {x} | |
| 285 | , .{ | |
| 286 | seg.segName(), | |
| 287 | seg.vmaddr, | |
| 288 | seg.vmsize, | |
| 289 | seg.fileoff, | |
| 290 | seg.filesize, | |
| 291 | }); | |
| 292 | }, | |
| 293 | ||
| 294 | .ID_DYLIB, | |
| 295 | .LOAD_DYLIB, | |
| 296 | => { | |
| 297 | const dylib = lc.dylib.inner.dylib; | |
| 298 | try writer.writeByte('\n'); | |
| 299 | try writer.print( | |
| 300 | \\path {s} | |
| 301 | \\timestamp {d} | |
| 302 | \\current version {x} | |
| 303 | \\compatibility version {x} | |
| 304 | , .{ | |
| 305 | mem.sliceTo(lc.dylib.data, 0), | |
| 306 | dylib.timestamp, | |
| 307 | dylib.current_version, | |
| 308 | dylib.compatibility_version, | |
| 309 | }); | |
| 310 | }, | |
| 311 | ||
| 312 | .MAIN => { | |
| 313 | try writer.writeByte('\n'); | |
| 314 | try writer.print( | |
| 315 | \\entryoff {x} | |
| 316 | \\stacksize {x} | |
| 317 | , .{ lc.main.entryoff, lc.main.stacksize }); | |
| 318 | }, | |
| 319 | ||
| 320 | .RPATH => { | |
| 321 | try writer.writeByte('\n'); | |
| 322 | try writer.print( | |
| 323 | \\path {s} | |
| 324 | , .{ | |
| 325 | mem.sliceTo(lc.rpath.data, 0), | |
| 326 | }); | |
| 327 | }, | |
| 328 | ||
| 329 | else => {}, | |
| 330 | } | |
| 331 | } |
lib/std/build/CheckObjectStep.zig created+355| ... | ... | @@ -0,0 +1,355 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const build = std.build; | |
| 4 | const fs = std.fs; | |
| 5 | const macho = std.macho; | |
| 6 | const mem = std.mem; | |
| 7 | ||
| 8 | const CheckObjectStep = @This(); | |
| 9 | ||
| 10 | const Allocator = mem.Allocator; | |
| 11 | const Builder = build.Builder; | |
| 12 | const Step = build.Step; | |
| 13 | ||
| 14 | pub const base_id = .check_obj; | |
| 15 | ||
| 16 | step: Step, | |
| 17 | builder: *Builder, | |
| 18 | source: build.FileSource, | |
| 19 | max_bytes: usize = 20 * 1024 * 1024, | |
| 20 | checks: std.ArrayList(Check), | |
| 21 | dump_symtab: bool = false, | |
| 22 | obj_format: std.Target.ObjectFormat, | |
| 23 | ||
| 24 | pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep { | |
| 25 | const gpa = builder.allocator; | |
| 26 | const self = gpa.create(CheckObjectStep) catch unreachable; | |
| 27 | self.* = .{ | |
| 28 | .builder = builder, | |
| 29 | .step = Step.init(.check_file, "CheckObject", gpa, make), | |
| 30 | .source = source.dupe(builder), | |
| 31 | .checks = std.ArrayList(Check).init(gpa), | |
| 32 | .obj_format = obj_format, | |
| 33 | }; | |
| 34 | self.source.addStepDependencies(&self.step); | |
| 35 | return self; | |
| 36 | } | |
| 37 | ||
| 38 | const Action = union(enum) { | |
| 39 | exact_match: []const u8, | |
| 40 | extract_var: struct { | |
| 41 | fuzzy_match: []const u8, | |
| 42 | var_name: []const u8, | |
| 43 | var_value: u64, | |
| 44 | }, | |
| 45 | compare: CompareAction, | |
| 46 | }; | |
| 47 | ||
| 48 | const CompareAction = struct { | |
| 49 | expected: union(enum) { | |
| 50 | literal: u64, | |
| 51 | varr: []const u8, | |
| 52 | }, | |
| 53 | var_stack: std.ArrayList([]const u8), | |
| 54 | op_stack: std.ArrayList(Op), | |
| 55 | ||
| 56 | const Op = enum { | |
| 57 | add, | |
| 58 | }; | |
| 59 | }; | |
| 60 | ||
| 61 | const Check = struct { | |
| 62 | builder: *Builder, | |
| 63 | actions: std.ArrayList(Action), | |
| 64 | ||
| 65 | fn create(b: *Builder) Check { | |
| 66 | return .{ | |
| 67 | .builder = b, | |
| 68 | .actions = std.ArrayList(Action).init(b.allocator), | |
| 69 | }; | |
| 70 | } | |
| 71 | ||
| 72 | fn exactMatch(self: *Check, phrase: []const u8) void { | |
| 73 | self.actions.append(.{ | |
| 74 | .exact_match = self.builder.dupe(phrase), | |
| 75 | }) catch unreachable; | |
| 76 | } | |
| 77 | ||
| 78 | fn extractVar(self: *Check, phrase: []const u8, var_name: []const u8) void { | |
| 79 | self.actions.append(.{ | |
| 80 | .extract_var = .{ | |
| 81 | .fuzzy_match = self.builder.dupe(phrase), | |
| 82 | .var_name = self.builder.dupe(var_name), | |
| 83 | .var_value = undefined, | |
| 84 | }, | |
| 85 | }) catch unreachable; | |
| 86 | } | |
| 87 | }; | |
| 88 | ||
| 89 | pub fn check(self: *CheckObjectStep, phrase: []const u8) void { | |
| 90 | var new_check = Check.create(self.builder); | |
| 91 | new_check.exactMatch(phrase); | |
| 92 | self.checks.append(new_check) catch unreachable; | |
| 93 | } | |
| 94 | ||
| 95 | pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void { | |
| 96 | assert(self.checks.items.len > 0); | |
| 97 | const last = &self.checks.items[self.checks.items.len - 1]; | |
| 98 | last.exactMatch(phrase); | |
| 99 | } | |
| 100 | ||
| 101 | pub fn checkNextExtract(self: *CheckObjectStep, comptime phrase: []const u8) void { | |
| 102 | assert(self.checks.items.len > 0); | |
| 103 | const matcher_start = comptime mem.indexOf(u8, phrase, "{") orelse | |
| 104 | @compileError("missing { } matcher"); | |
| 105 | const matcher_end = comptime mem.indexOf(u8, phrase, "}") orelse | |
| 106 | @compileError("missing { } matcher"); | |
| 107 | const last = &self.checks.items[self.checks.items.len - 1]; | |
| 108 | last.extractVar(phrase[0..matcher_start], phrase[matcher_start + 1 .. matcher_end]); | |
| 109 | } | |
| 110 | ||
| 111 | pub fn checkInSymtab(self: *CheckObjectStep) void { | |
| 112 | self.dump_symtab = true; | |
| 113 | self.check("symtab"); | |
| 114 | } | |
| 115 | ||
| 116 | pub fn checkCompare(self: *CheckObjectStep, comptime phrase: []const u8, expected: anytype) void { | |
| 117 | comptime assert(phrase[0] == '{'); | |
| 118 | comptime assert(phrase[phrase.len - 1] == '}'); | |
| 119 | ||
| 120 | const gpa = self.builder.allocator; | |
| 121 | var ca = CompareAction{ | |
| 122 | .expected = expected, | |
| 123 | .var_stack = std.ArrayList([]const u8).init(gpa), | |
| 124 | .op_stack = std.ArrayList(CompareAction.Op).init(gpa), | |
| 125 | }; | |
| 126 | ||
| 127 | var it = mem.tokenize(u8, phrase[1 .. phrase.len - 1], " "); | |
| 128 | while (it.next()) |next| { | |
| 129 | if (mem.eql(u8, next, "+")) { | |
| 130 | ca.op_stack.append(.add) catch unreachable; | |
| 131 | } else { | |
| 132 | ca.var_stack.append(self.builder.dupe(next)) catch unreachable; | |
| 133 | } | |
| 134 | } | |
| 135 | ||
| 136 | var new_check = Check.create(self.builder); | |
| 137 | new_check.actions.append(.{ .compare = ca }) catch unreachable; | |
| 138 | self.checks.append(new_check) catch unreachable; | |
| 139 | } | |
| 140 | ||
| 141 | fn make(step: *Step) !void { | |
| 142 | const self = @fieldParentPtr(CheckObjectStep, "step", step); | |
| 143 | ||
| 144 | const gpa = self.builder.allocator; | |
| 145 | const src_path = self.source.getPath(self.builder); | |
| 146 | const contents = try fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); | |
| 147 | ||
| 148 | const output = switch (self.obj_format) { | |
| 149 | .macho => try MachODumper.parseAndDump(contents, .{ | |
| 150 | .gpa = gpa, | |
| 151 | .dump_symtab = self.dump_symtab, | |
| 152 | }), | |
| 153 | .elf => @panic("TODO elf parser"), | |
| 154 | .coff => @panic("TODO coff parser"), | |
| 155 | .wasm => @panic("TODO wasm parser"), | |
| 156 | else => unreachable, | |
| 157 | }; | |
| 158 | ||
| 159 | var vars = std.StringHashMap(u64).init(gpa); | |
| 160 | ||
| 161 | for (self.checks.items) |chk| { | |
| 162 | const first_action = chk.actions.items[0]; | |
| 163 | ||
| 164 | switch (first_action) { | |
| 165 | .exact_match => |first| { | |
| 166 | if (mem.indexOf(u8, output, first)) |index| { | |
| 167 | // TODO backtrack to track current scope | |
| 168 | var it = std.mem.tokenize(u8, output[index..], "\r\n"); | |
| 169 | ||
| 170 | outer: for (chk.actions.items[1..]) |next_action| { | |
| 171 | switch (next_action) { | |
| 172 | .exact_match => |exact| { | |
| 173 | while (it.next()) |line| { | |
| 174 | if (mem.eql(u8, line, exact)) { | |
| 175 | std.debug.print("{s} == {s}\n", .{ line, exact }); | |
| 176 | continue :outer; | |
| 177 | } | |
| 178 | std.debug.print("{s} != {s}\n", .{ line, exact }); | |
| 179 | } else { | |
| 180 | return error.TestFailed; | |
| 181 | } | |
| 182 | }, | |
| 183 | .extract_var => |extract| { | |
| 184 | const phrase = extract.fuzzy_match; | |
| 185 | while (it.next()) |line| { | |
| 186 | if (mem.indexOf(u8, line, phrase)) |found| { | |
| 187 | std.debug.print("{s} in {s}\n", .{ phrase, line }); | |
| 188 | // Extract variable and save back in the action. | |
| 189 | const trimmed = mem.trim(u8, line[found + phrase.len ..], " "); | |
| 190 | const parsed = try std.fmt.parseInt(u64, trimmed, 16); | |
| 191 | try vars.putNoClobber(extract.var_name, parsed); | |
| 192 | continue :outer; | |
| 193 | } | |
| 194 | std.debug.print("{s} not in {s}\n", .{ extract.fuzzy_match, line }); | |
| 195 | } | |
| 196 | }, | |
| 197 | .compare => unreachable, | |
| 198 | } | |
| 199 | } | |
| 200 | } else { | |
| 201 | return error.TestFailed; | |
| 202 | } | |
| 203 | }, | |
| 204 | .compare => |act| { | |
| 205 | var values = std.ArrayList(u64).init(gpa); | |
| 206 | try values.ensureTotalCapacity(act.var_stack.items.len); | |
| 207 | for (act.var_stack.items) |vv| { | |
| 208 | const val = vars.get(vv) orelse return error.TestFailed; | |
| 209 | values.appendAssumeCapacity(val); | |
| 210 | } | |
| 211 | ||
| 212 | var op_i: usize = 1; | |
| 213 | var reduced: u64 = values.items[0]; | |
| 214 | for (act.op_stack.items) |op| { | |
| 215 | const other = values.items[op_i]; | |
| 216 | switch (op) { | |
| 217 | .add => { | |
| 218 | reduced += other; | |
| 219 | }, | |
| 220 | } | |
| 221 | } | |
| 222 | ||
| 223 | const expected = switch (act.expected) { | |
| 224 | .literal => |exp| exp, | |
| 225 | .varr => |vv| vars.get(vv) orelse return error.TestFailed, | |
| 226 | }; | |
| 227 | if (reduced != expected) return error.TestFailed; | |
| 228 | }, | |
| 229 | .extract_var => unreachable, | |
| 230 | } | |
| 231 | } | |
| 232 | ||
| 233 | var it = vars.iterator(); | |
| 234 | while (it.next()) |entry| { | |
| 235 | std.debug.print(" {s} => {x}", .{ entry.key_ptr.*, entry.value_ptr.* }); | |
| 236 | } | |
| 237 | } | |
| 238 | ||
| 239 | const Opts = struct { | |
| 240 | gpa: ?Allocator = null, | |
| 241 | dump_symtab: bool = false, | |
| 242 | }; | |
| 243 | ||
| 244 | const MachODumper = struct { | |
| 245 | fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 { | |
| 246 | const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator | |
| 247 | var stream = std.io.fixedBufferStream(bytes); | |
| 248 | const reader = stream.reader(); | |
| 249 | ||
| 250 | const hdr = try reader.readStruct(macho.mach_header_64); | |
| 251 | if (hdr.magic != macho.MH_MAGIC_64) { | |
| 252 | return error.InvalidMagicNumber; | |
| 253 | } | |
| 254 | ||
| 255 | var output = std.ArrayList(u8).init(gpa); | |
| 256 | const writer = output.writer(); | |
| 257 | ||
| 258 | var symtab_cmd: ?macho.symtab_command = null; | |
| 259 | var i: u16 = 0; | |
| 260 | while (i < hdr.ncmds) : (i += 1) { | |
| 261 | var cmd = try macho.LoadCommand.read(gpa, reader); | |
| 262 | ||
| 263 | if (opts.dump_symtab and cmd.cmd() == .SYMTAB) { | |
| 264 | symtab_cmd = cmd.symtab; | |
| 265 | } | |
| 266 | ||
| 267 | try dumpLoadCommand(cmd, i, writer); | |
| 268 | try writer.writeByte('\n'); | |
| 269 | } | |
| 270 | ||
| 271 | if (symtab_cmd) |cmd| { | |
| 272 | try writer.writeAll("symtab\n"); | |
| 273 | const strtab = bytes[cmd.stroff..][0..cmd.strsize]; | |
| 274 | const symtab = @ptrCast( | |
| 275 | [*]const macho.nlist_64, | |
| 276 | @alignCast(@alignOf(macho.nlist_64), bytes.ptr + cmd.symoff), | |
| 277 | )[0..cmd.nsyms]; | |
| 278 | ||
| 279 | for (symtab) |sym| { | |
| 280 | if (sym.stab()) continue; | |
| 281 | const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0); | |
| 282 | try writer.print("{s} {x}\n", .{ sym_name, sym.n_value }); | |
| 283 | } | |
| 284 | } | |
| 285 | ||
| 286 | return output.toOwnedSlice(); | |
| 287 | } | |
| 288 | ||
| 289 | fn dumpLoadCommand(lc: macho.LoadCommand, index: u16, writer: anytype) !void { | |
| 290 | // print header first | |
| 291 | try writer.print( | |
| 292 | \\LC {d} | |
| 293 | \\cmd {s} | |
| 294 | \\cmdsize {d} | |
| 295 | , .{ index, @tagName(lc.cmd()), lc.cmdsize() }); | |
| 296 | ||
| 297 | switch (lc.cmd()) { | |
| 298 | .SEGMENT_64 => { | |
| 299 | // TODO dump section headers | |
| 300 | const seg = lc.segment.inner; | |
| 301 | try writer.writeByte('\n'); | |
| 302 | try writer.print( | |
| 303 | \\segname {s} | |
| 304 | \\vmaddr {x} | |
| 305 | \\vmsize {x} | |
| 306 | \\fileoff {x} | |
| 307 | \\filesz {x} | |
| 308 | , .{ | |
| 309 | seg.segName(), | |
| 310 | seg.vmaddr, | |
| 311 | seg.vmsize, | |
| 312 | seg.fileoff, | |
| 313 | seg.filesize, | |
| 314 | }); | |
| 315 | }, | |
| 316 | ||
| 317 | .ID_DYLIB, | |
| 318 | .LOAD_DYLIB, | |
| 319 | => { | |
| 320 | const dylib = lc.dylib.inner.dylib; | |
| 321 | try writer.writeByte('\n'); | |
| 322 | try writer.print( | |
| 323 | \\path {s} | |
| 324 | \\timestamp {d} | |
| 325 | \\current version {x} | |
| 326 | \\compatibility version {x} | |
| 327 | , .{ | |
| 328 | mem.sliceTo(lc.dylib.data, 0), | |
| 329 | dylib.timestamp, | |
| 330 | dylib.current_version, | |
| 331 | dylib.compatibility_version, | |
| 332 | }); | |
| 333 | }, | |
| 334 | ||
| 335 | .MAIN => { | |
| 336 | try writer.writeByte('\n'); | |
| 337 | try writer.print( | |
| 338 | \\entryoff {x} | |
| 339 | \\stacksize {x} | |
| 340 | , .{ lc.main.entryoff, lc.main.stacksize }); | |
| 341 | }, | |
| 342 | ||
| 343 | .RPATH => { | |
| 344 | try writer.writeByte('\n'); | |
| 345 | try writer.print( | |
| 346 | \\path {s} | |
| 347 | , .{ | |
| 348 | mem.sliceTo(lc.rpath.data, 0), | |
| 349 | }); | |
| 350 | }, | |
| 351 | ||
| 352 | else => {}, | |
| 353 | } | |
| 354 | } | |
| 355 | }; |
test/link.zig+1-1| ... | ... | @@ -28,7 +28,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 28 | 28 | }); |
| 29 | 29 | |
| 30 | 30 | if (builtin.os.tag == .macos) { |
| 31 | cases.addBuildFile("test/link/entry/build.zig", .{ | |
| 31 | cases.addBuildFile("test/link/macho/entry/build.zig", .{ | |
| 32 | 32 | .build_modes = true, |
| 33 | 33 | }); |
| 34 | 34 |
test/link/macho/dylib/build.zig+2-2| ... | ... | @@ -13,7 +13,7 @@ pub fn build(b: *Builder) void { |
| 13 | 13 | dylib.linkLibC(); |
| 14 | 14 | dylib.install(); |
| 15 | 15 | |
| 16 | const check_dylib = dylib.checkMachO(); | |
| 16 | const check_dylib = dylib.checkObject(.macho); | |
| 17 | 17 | check_dylib.check("cmd ID_DYLIB"); |
| 18 | 18 | check_dylib.checkNext("path @rpath/liba.dylib"); |
| 19 | 19 | check_dylib.checkNext("timestamp 2"); |
| ... | ... | @@ -30,7 +30,7 @@ pub fn build(b: *Builder) void { |
| 30 | 30 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib/")); |
| 31 | 31 | exe.addRPath(b.pathFromRoot("zig-out/lib")); |
| 32 | 32 | |
| 33 | const check_exe = exe.checkMachO(); | |
| 33 | const check_exe = exe.checkObject(.macho); | |
| 34 | 34 | check_exe.check("cmd LOAD_DYLIB"); |
| 35 | 35 | check_exe.checkNext("path @rpath/liba.dylib"); |
| 36 | 36 | check_exe.checkNext("timestamp 2"); |
test/link/macho/entry/build.zig+1-1| ... | ... | @@ -13,7 +13,7 @@ pub fn build(b: *Builder) void { |
| 13 | 13 | exe.linkLibC(); |
| 14 | 14 | exe.entry_symbol_name = "_non_main"; |
| 15 | 15 | |
| 16 | const check_exe = exe.checkMachO(); | |
| 16 | const check_exe = exe.checkObject(.macho); | |
| 17 | 17 | |
| 18 | 18 | check_exe.check("segname __TEXT"); |
| 19 | 19 | check_exe.checkNextExtract("vmaddr {vmaddr}"); |
test/link/macho/pagezero/build.zig+2-2| ... | ... | @@ -14,7 +14,7 @@ pub fn build(b: *Builder) void { |
| 14 | 14 | exe.linkLibC(); |
| 15 | 15 | exe.pagezero_size = 0x4000; |
| 16 | 16 | |
| 17 | const check = exe.checkMachO(); | |
| 17 | const check = exe.checkObject(.macho); | |
| 18 | 18 | check.check("LC 0"); |
| 19 | 19 | check.checkNext("segname __PAGEZERO"); |
| 20 | 20 | check.checkNext("vmaddr 0"); |
| ... | ... | @@ -33,7 +33,7 @@ pub fn build(b: *Builder) void { |
| 33 | 33 | exe.linkLibC(); |
| 34 | 34 | exe.pagezero_size = 0; |
| 35 | 35 | |
| 36 | const check = exe.checkMachO(); | |
| 36 | const check = exe.checkObject(.macho); | |
| 37 | 37 | check.check("LC 0"); |
| 38 | 38 | check.checkNext("segname __TEXT"); |
| 39 | 39 | check.checkNext("vmaddr 0"); |
test/link/macho/stack_size/build.zig+1-1| ... | ... | @@ -13,7 +13,7 @@ pub fn build(b: *Builder) void { |
| 13 | 13 | exe.linkLibC(); |
| 14 | 14 | exe.stack_size = 0x100000000; |
| 15 | 15 | |
| 16 | const check_exe = exe.checkMachO(); | |
| 16 | const check_exe = exe.checkObject(.macho); | |
| 17 | 17 | check_exe.check("cmd MAIN"); |
| 18 | 18 | check_exe.checkNext("stacksize 100000000"); |
| 19 | 19 |