| ... | ... | @@ -3,6 +3,7 @@ const assert = std.debug.assert; |
| 3 | 3 | const build = std.build; |
| 4 | 4 | const fs = std.fs; |
| 5 | 5 | const macho = std.macho; |
| 6 | const math = std.math; |
| 6 | 7 | const mem = std.mem; |
| 7 | 8 | const testing = std.testing; |
| 8 | 9 | |
| ... | ... | @@ -36,20 +37,24 @@ pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Targe |
| 36 | 37 | return self; |
| 37 | 38 | } |
| 38 | 39 | |
| 39 | | const Action = union(enum) { |
| 40 | | match: MatchAction, |
| 41 | | compute_eq: ComputeEqAction, |
| 42 | | }; |
| 43 | | |
| 44 | | /// MatchAction is the main building block of standard matchers with optional eat-all token `{*}` |
| 40 | /// There two types of actions currently suported: |
| 41 | /// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}` |
| 45 | 42 | /// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature |
| 46 | 43 | /// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use |
| 47 | 44 | /// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc. |
| 48 | 45 | /// it should be plenty useful in its current form. |
| 49 | | const MatchAction = struct { |
| 50 | | needle: []const u8, |
| 46 | /// * `.compute_cmp` - can be used to perform an operation on the extracted global variables |
| 47 | /// using the MatchAction. It currently only supports an addition. The operation is required |
| 48 | /// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well, |
| 49 | /// to avoid any parsing really). |
| 50 | /// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively |
| 51 | /// they could then be added with this simple program `vmaddr entryoff +`. |
| 52 | const Action = struct { |
| 53 | tag: enum { match, compute_cmp }, |
| 54 | phrase: []const u8, |
| 55 | expected: ?ComputeCompareExpected = null, |
| 51 | 56 | |
| 52 | | /// Will return true if the `needle` was found in the `haystack`. |
| 57 | /// Will return true if the `phrase` was found in the `haystack`. |
| 53 | 58 | /// Some examples include: |
| 54 | 59 | /// |
| 55 | 60 | /// LC 0 => will match in its entirety |
| ... | ... | @@ -57,9 +62,11 @@ const MatchAction = struct { |
| 57 | 62 | /// and save under `vmaddr` global name (see `global_vars` param) |
| 58 | 63 | /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib` |
| 59 | 64 | /// in that order with other letters in between |
| 60 | | fn match(act: MatchAction, haystack: []const u8, global_vars: anytype) !bool { |
| 65 | fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool { |
| 66 | assert(act.tag == .match); |
| 67 | |
| 61 | 68 | var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " "); |
| 62 | | var needle_it = mem.tokenize(u8, mem.trim(u8, act.needle, " "), " "); |
| 69 | var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " "); |
| 63 | 70 | |
| 64 | 71 | while (needle_it.next()) |needle_tok| { |
| 65 | 72 | const hay_tok = hay_it.next() orelse return false; |
| ... | ... | @@ -93,22 +100,80 @@ const MatchAction = struct { |
| 93 | 100 | |
| 94 | 101 | return true; |
| 95 | 102 | } |
| 96 | | }; |
| 97 | 103 | |
| 98 | | /// ComputeEqAction can be used to perform an operation on the extracted global variables |
| 99 | | /// using the MatchAction. It currently only supports an addition. The operation is required |
| 100 | | /// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well, |
| 101 | | /// to avoid any parsing really). |
| 102 | | /// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively |
| 103 | | /// they could then be added with this simple program `vmaddr entryoff +`. |
| 104 | | const ComputeEqAction = struct { |
| 105 | | expected: []const u8, |
| 106 | | var_stack: std.ArrayList([]const u8), |
| 107 | | op_stack: std.ArrayList(Op), |
| 104 | /// Will return true if the `phrase` is correctly parsed into an RPN program and |
| 105 | /// its reduced, computed value compares using `op` with the expected value, either |
| 106 | /// a literal or another extracted variable. |
| 107 | fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool { |
| 108 | var op_stack = std.ArrayList(enum { add }).init(gpa); |
| 109 | var values = std.ArrayList(u64).init(gpa); |
| 110 | |
| 111 | var it = mem.tokenize(u8, act.phrase, " "); |
| 112 | while (it.next()) |next| { |
| 113 | if (mem.eql(u8, next, "+")) { |
| 114 | try op_stack.append(.add); |
| 115 | } else { |
| 116 | const val = global_vars.get(next) orelse { |
| 117 | std.debug.print( |
| 118 | \\ |
| 119 | \\========= Variable was not extracted: =========== |
| 120 | \\{s} |
| 121 | \\ |
| 122 | , .{next}); |
| 123 | return error.UnknownVariable; |
| 124 | }; |
| 125 | try values.append(val); |
| 126 | } |
| 127 | } |
| 108 | 128 | |
| 109 | | const Op = enum { |
| 110 | | add, |
| 111 | | }; |
| 129 | var op_i: usize = 1; |
| 130 | var reduced: u64 = values.items[0]; |
| 131 | for (op_stack.items) |op| { |
| 132 | const other = values.items[op_i]; |
| 133 | switch (op) { |
| 134 | .add => { |
| 135 | reduced += other; |
| 136 | }, |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | const exp_value = switch (act.expected.?.value) { |
| 141 | .variable => |name| global_vars.get(name) orelse { |
| 142 | std.debug.print( |
| 143 | \\ |
| 144 | \\========= Variable was not extracted: =========== |
| 145 | \\{s} |
| 146 | \\ |
| 147 | , .{name}); |
| 148 | return error.UnknownVariable; |
| 149 | }, |
| 150 | .literal => |x| x, |
| 151 | }; |
| 152 | return math.compare(reduced, act.expected.?.op, exp_value); |
| 153 | } |
| 154 | }; |
| 155 | |
| 156 | const ComputeCompareExpected = struct { |
| 157 | op: math.CompareOperator, |
| 158 | value: union(enum) { |
| 159 | variable: []const u8, |
| 160 | literal: u64, |
| 161 | }, |
| 162 | |
| 163 | pub fn format( |
| 164 | value: @This(), |
| 165 | comptime fmt: []const u8, |
| 166 | options: std.fmt.FormatOptions, |
| 167 | writer: anytype, |
| 168 | ) !void { |
| 169 | _ = fmt; |
| 170 | _ = options; |
| 171 | try writer.print("{s} ", .{@tagName(value.op)}); |
| 172 | switch (value.value) { |
| 173 | .variable => |name| try writer.writeAll(name), |
| 174 | .literal => |x| try writer.print("{x}", .{x}), |
| 175 | } |
| 176 | } |
| 112 | 177 | }; |
| 113 | 178 | |
| 114 | 179 | const Check = struct { |
| ... | ... | @@ -122,15 +187,18 @@ const Check = struct { |
| 122 | 187 | }; |
| 123 | 188 | } |
| 124 | 189 | |
| 125 | | fn match(self: *Check, needle: []const u8) void { |
| 190 | fn match(self: *Check, phrase: []const u8) void { |
| 126 | 191 | self.actions.append(.{ |
| 127 | | .match = .{ .needle = self.builder.dupe(needle) }, |
| 192 | .tag = .match, |
| 193 | .phrase = self.builder.dupe(phrase), |
| 128 | 194 | }) catch unreachable; |
| 129 | 195 | } |
| 130 | 196 | |
| 131 | | fn computeEq(self: *Check, act: ComputeEqAction) void { |
| 197 | fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void { |
| 132 | 198 | self.actions.append(.{ |
| 133 | | .compute_eq = act, |
| 199 | .tag = .compute_cmp, |
| 200 | .phrase = self.builder.dupe(phrase), |
| 201 | .expected = expected, |
| 134 | 202 | }) catch unreachable; |
| 135 | 203 | } |
| 136 | 204 | }; |
| ... | ... | @@ -165,25 +233,13 @@ pub fn checkInSymtab(self: *CheckObjectStep) void { |
| 165 | 233 | /// Creates a new standalone, singular check which allows running simple binary operations |
| 166 | 234 | /// on the extracted variables. It will then compare the reduced program with the value of |
| 167 | 235 | /// the expected variable. |
| 168 | | pub fn checkComputeEq(self: *CheckObjectStep, program: []const u8, expected: []const u8) void { |
| 169 | | const gpa = self.builder.allocator; |
| 170 | | var ca = ComputeEqAction{ |
| 171 | | .expected = expected, |
| 172 | | .var_stack = std.ArrayList([]const u8).init(gpa), |
| 173 | | .op_stack = std.ArrayList(ComputeEqAction.Op).init(gpa), |
| 174 | | }; |
| 175 | | |
| 176 | | var it = mem.tokenize(u8, program, " "); |
| 177 | | while (it.next()) |next| { |
| 178 | | if (mem.eql(u8, next, "+")) { |
| 179 | | ca.op_stack.append(.add) catch unreachable; |
| 180 | | } else { |
| 181 | | ca.var_stack.append(self.builder.dupe(next)) catch unreachable; |
| 182 | | } |
| 183 | | } |
| 184 | | |
| 236 | pub fn checkComputeCompare( |
| 237 | self: *CheckObjectStep, |
| 238 | program: []const u8, |
| 239 | expected: ComputeCompareExpected, |
| 240 | ) void { |
| 185 | 241 | var new_check = Check.create(self.builder); |
| 186 | | new_check.computeEq(ca); |
| 242 | new_check.computeCmp(program, expected); |
| 187 | 243 | self.checks.append(new_check) catch unreachable; |
| 188 | 244 | } |
| 189 | 245 | |
| ... | ... | @@ -210,10 +266,10 @@ fn make(step: *Step) !void { |
| 210 | 266 | for (self.checks.items) |chk| { |
| 211 | 267 | var it = mem.tokenize(u8, output, "\r\n"); |
| 212 | 268 | for (chk.actions.items) |act| { |
| 213 | | switch (act) { |
| 214 | | .match => |match_act| { |
| 269 | switch (act.tag) { |
| 270 | .match => { |
| 215 | 271 | while (it.next()) |line| { |
| 216 | | if (try match_act.match(line, &vars)) break; |
| 272 | if (try act.match(line, &vars)) break; |
| 217 | 273 | } else { |
| 218 | 274 | std.debug.print( |
| 219 | 275 | \\ |
| ... | ... | @@ -222,51 +278,33 @@ fn make(step: *Step) !void { |
| 222 | 278 | \\========= But parsed file does not contain it: ======= |
| 223 | 279 | \\{s} |
| 224 | 280 | \\ |
| 225 | | , .{ match_act.needle, output }); |
| 281 | , .{ act.phrase, output }); |
| 226 | 282 | return error.TestFailed; |
| 227 | 283 | } |
| 228 | 284 | }, |
| 229 | | .compute_eq => |c_eq| { |
| 230 | | var values = std.ArrayList(u64).init(gpa); |
| 231 | | try values.ensureTotalCapacity(c_eq.var_stack.items.len); |
| 232 | | for (c_eq.var_stack.items) |vv| { |
| 233 | | const val = vars.get(vv) orelse { |
| 285 | .compute_cmp => { |
| 286 | const res = act.computeCmp(gpa, vars) catch |err| switch (err) { |
| 287 | error.UnknownVariable => { |
| 234 | 288 | std.debug.print( |
| 235 | | \\ |
| 236 | | \\========= Variable was not extracted: =========== |
| 237 | | \\{s} |
| 238 | 289 | \\========= From parsed file: ===================== |
| 239 | 290 | \\{s} |
| 240 | 291 | \\ |
| 241 | | , .{ vv, output }); |
| 292 | , .{output}); |
| 242 | 293 | return error.TestFailed; |
| 243 | | }; |
| 244 | | values.appendAssumeCapacity(val); |
| 245 | | } |
| 246 | | |
| 247 | | var op_i: usize = 1; |
| 248 | | var reduced: u64 = values.items[0]; |
| 249 | | for (c_eq.op_stack.items) |op| { |
| 250 | | const other = values.items[op_i]; |
| 251 | | switch (op) { |
| 252 | | .add => { |
| 253 | | reduced += other; |
| 254 | | }, |
| 255 | | } |
| 256 | | } |
| 257 | | |
| 258 | | const expected = vars.get(c_eq.expected) orelse { |
| 294 | }, |
| 295 | else => |e| return e, |
| 296 | }; |
| 297 | if (!res) { |
| 259 | 298 | std.debug.print( |
| 260 | 299 | \\ |
| 261 | | \\========= Variable was not extracted: =========== |
| 262 | | \\{s} |
| 263 | | \\========= From parsed file: ===================== |
| 300 | \\========= Comparison failed for action: =========== |
| 301 | \\{s} {s} |
| 302 | \\========= From parsed file: ======================= |
| 264 | 303 | \\{s} |
| 265 | 304 | \\ |
| 266 | | , .{ c_eq.expected, output }); |
| 305 | , .{ act.phrase, act.expected.?, output }); |
| 267 | 306 | return error.TestFailed; |
| 268 | | }; |
| 269 | | try testing.expectEqual(reduced, expected); |
| 307 | } |
| 270 | 308 | }, |
| 271 | 309 | } |
| 272 | 310 | } |
| ... | ... | @@ -349,6 +387,23 @@ const MachODumper = struct { |
| 349 | 387 | seg.fileoff, |
| 350 | 388 | seg.filesize, |
| 351 | 389 | }); |
| 390 | |
| 391 | for (lc.segment.sections.items) |sect| { |
| 392 | try writer.writeByte('\n'); |
| 393 | try writer.print( |
| 394 | \\sectname {s} |
| 395 | \\addr {x} |
| 396 | \\size {x} |
| 397 | \\offset {x} |
| 398 | \\align {x} |
| 399 | , .{ |
| 400 | sect.sectName(), |
| 401 | sect.addr, |
| 402 | sect.size, |
| 403 | sect.offset, |
| 404 | sect.@"align", |
| 405 | }); |
| 406 | } |
| 352 | 407 | }, |
| 353 | 408 | |
| 354 | 409 | .ID_DYLIB, |