| 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,7 +24,7 @@ pub const TranslateCStep = @import("build/TranslateCStep.zig"); |
| 24 | pub const WriteFileStep = @import("build/WriteFileStep.zig"); | 24 | pub const WriteFileStep = @import("build/WriteFileStep.zig"); |
| 25 | pub const RunStep = @import("build/RunStep.zig"); | 25 | pub const RunStep = @import("build/RunStep.zig"); |
| 26 | pub const CheckFileStep = @import("build/CheckFileStep.zig"); | 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 | pub const InstallRawStep = @import("build/InstallRawStep.zig"); | 28 | pub const InstallRawStep = @import("build/InstallRawStep.zig"); |
| 29 | pub const OptionsStep = @import("build/OptionsStep.zig"); | 29 | pub const OptionsStep = @import("build/OptionsStep.zig"); |
| 30 | 30 | ||
| ... | @@ -1865,8 +1865,8 @@ pub const LibExeObjStep = struct { | ... | @@ -1865,8 +1865,8 @@ pub const LibExeObjStep = struct { |
| 1865 | return run_step; | 1865 | return run_step; |
| 1866 | } | 1866 | } |
| 1867 | 1867 | ||
| 1868 | pub fn checkMachO(self: *LibExeObjStep) *CheckMachOStep { | 1868 | pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { |
| 1869 | return CheckMachOStep.create(self.builder, self.getOutputSource()); | 1869 | return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format); |
| 1870 | } | 1870 | } |
| 1871 | 1871 | ||
| 1872 | pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void { | 1872 | pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void { |
| ... | @@ -3455,7 +3455,7 @@ pub const Step = struct { | ... | @@ -3455,7 +3455,7 @@ pub const Step = struct { |
| 3455 | write_file, | 3455 | write_file, |
| 3456 | run, | 3456 | run, |
| 3457 | check_file, | 3457 | check_file, |
| 3458 | check_macho, | 3458 | check_object, |
| 3459 | install_raw, | 3459 | install_raw, |
| 3460 | options, | 3460 | options, |
| 3461 | custom, | 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,7 +28,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 28 | }); | 28 | }); |
| 29 | 29 | ||
| 30 | if (builtin.os.tag == .macos) { | 30 | if (builtin.os.tag == .macos) { |
| 31 | cases.addBuildFile("test/link/entry/build.zig", .{ | 31 | cases.addBuildFile("test/link/macho/entry/build.zig", .{ |
| 32 | .build_modes = true, | 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,7 +13,7 @@ pub fn build(b: *Builder) void { |
| 13 | dylib.linkLibC(); | 13 | dylib.linkLibC(); |
| 14 | dylib.install(); | 14 | dylib.install(); |
| 15 | 15 | ||
| 16 | const check_dylib = dylib.checkMachO(); | 16 | const check_dylib = dylib.checkObject(.macho); |
| 17 | check_dylib.check("cmd ID_DYLIB"); | 17 | check_dylib.check("cmd ID_DYLIB"); |
| 18 | check_dylib.checkNext("path @rpath/liba.dylib"); | 18 | check_dylib.checkNext("path @rpath/liba.dylib"); |
| 19 | check_dylib.checkNext("timestamp 2"); | 19 | check_dylib.checkNext("timestamp 2"); |
| ... | @@ -30,7 +30,7 @@ pub fn build(b: *Builder) void { | ... | @@ -30,7 +30,7 @@ pub fn build(b: *Builder) void { |
| 30 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib/")); | 30 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib/")); |
| 31 | exe.addRPath(b.pathFromRoot("zig-out/lib")); | 31 | exe.addRPath(b.pathFromRoot("zig-out/lib")); |
| 32 | 32 | ||
| 33 | const check_exe = exe.checkMachO(); | 33 | const check_exe = exe.checkObject(.macho); |
| 34 | check_exe.check("cmd LOAD_DYLIB"); | 34 | check_exe.check("cmd LOAD_DYLIB"); |
| 35 | check_exe.checkNext("path @rpath/liba.dylib"); | 35 | check_exe.checkNext("path @rpath/liba.dylib"); |
| 36 | check_exe.checkNext("timestamp 2"); | 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,7 +13,7 @@ pub fn build(b: *Builder) void { |
| 13 | exe.linkLibC(); | 13 | exe.linkLibC(); |
| 14 | exe.entry_symbol_name = "_non_main"; | 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 | check_exe.check("segname __TEXT"); | 18 | check_exe.check("segname __TEXT"); |
| 19 | check_exe.checkNextExtract("vmaddr {vmaddr}"); | 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,7 +14,7 @@ pub fn build(b: *Builder) void { |
| 14 | exe.linkLibC(); | 14 | exe.linkLibC(); |
| 15 | exe.pagezero_size = 0x4000; | 15 | exe.pagezero_size = 0x4000; |
| 16 | 16 | ||
| 17 | const check = exe.checkMachO(); | 17 | const check = exe.checkObject(.macho); |
| 18 | check.check("LC 0"); | 18 | check.check("LC 0"); |
| 19 | check.checkNext("segname __PAGEZERO"); | 19 | check.checkNext("segname __PAGEZERO"); |
| 20 | check.checkNext("vmaddr 0"); | 20 | check.checkNext("vmaddr 0"); |
| ... | @@ -33,7 +33,7 @@ pub fn build(b: *Builder) void { | ... | @@ -33,7 +33,7 @@ pub fn build(b: *Builder) void { |
| 33 | exe.linkLibC(); | 33 | exe.linkLibC(); |
| 34 | exe.pagezero_size = 0; | 34 | exe.pagezero_size = 0; |
| 35 | 35 | ||
| 36 | const check = exe.checkMachO(); | 36 | const check = exe.checkObject(.macho); |
| 37 | check.check("LC 0"); | 37 | check.check("LC 0"); |
| 38 | check.checkNext("segname __TEXT"); | 38 | check.checkNext("segname __TEXT"); |
| 39 | check.checkNext("vmaddr 0"); | 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,7 +13,7 @@ pub fn build(b: *Builder) void { |
| 13 | exe.linkLibC(); | 13 | exe.linkLibC(); |
| 14 | exe.stack_size = 0x100000000; | 14 | exe.stack_size = 0x100000000; |
| 15 | 15 | ||
| 16 | const check_exe = exe.checkMachO(); | 16 | const check_exe = exe.checkObject(.macho); |
| 17 | check_exe.check("cmd MAIN"); | 17 | check_exe.check("cmd MAIN"); |
| 18 | check_exe.checkNext("stacksize 100000000"); | 18 | check_exe.checkNext("stacksize 100000000"); |
| 19 | 19 |