| ... | @@ -41,9 +41,22 @@ const Action = union(enum) { | ... | @@ -41,9 +41,22 @@ const Action = union(enum) { |
| 41 | compute_eq: ComputeEqAction, | 41 | compute_eq: ComputeEqAction, |
| 42 | }; | 42 | }; |
| 43 | | 43 | |
| | 44 | /// MatchAction is the main building block of standard matchers with optional eat-all token `{*}` |
| | 45 | /// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature |
| | 46 | /// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use |
| | 47 | /// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc. |
| | 48 | /// it should be plenty useful in its current form. |
| 44 | const MatchAction = struct { | 49 | const MatchAction = struct { |
| 45 | needle: []const u8, | 50 | needle: []const u8, |
| 46 | | 51 | |
| | 52 | /// Will return true if the `needle` was found in the `haystack`. |
| | 53 | /// Some examples include: |
| | 54 | /// |
| | 55 | /// LC 0 => will match in its entirety |
| | 56 | /// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64 |
| | 57 | /// and save under `vmaddr` global name (see `global_vars` param) |
| | 58 | /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib` |
| | 59 | /// in that order with other letters in between |
| 47 | fn match(act: MatchAction, haystack: []const u8, global_vars: anytype) !bool { | 60 | fn match(act: MatchAction, haystack: []const u8, global_vars: anytype) !bool { |
| 48 | var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " "); | 61 | var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " "); |
| 49 | var needle_it = mem.tokenize(u8, mem.trim(u8, act.needle, " "), " "); | 62 | var needle_it = mem.tokenize(u8, mem.trim(u8, act.needle, " "), " "); |
| ... | @@ -81,6 +94,12 @@ const MatchAction = struct { | ... | @@ -81,6 +94,12 @@ const MatchAction = struct { |
| 81 | } | 94 | } |
| 82 | }; | 95 | }; |
| 83 | | 96 | |
| | 97 | /// ComputeEqAction can be used to perform an operation on the extracted global variables |
| | 98 | /// using the MatchAction. It currently only supports an addition. The operation is required |
| | 99 | /// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well, |
| | 100 | /// to avoid any parsing really). |
| | 101 | /// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively |
| | 102 | /// they could then be added with this simple program `vmaddr entryoff +`. |
| 84 | const ComputeEqAction = struct { | 103 | const ComputeEqAction = struct { |
| 85 | expected: []const u8, | 104 | expected: []const u8, |
| 86 | var_stack: std.ArrayList([]const u8), | 105 | var_stack: std.ArrayList([]const u8), |
| ... | @@ -115,23 +134,32 @@ const Check = struct { | ... | @@ -115,23 +134,32 @@ const Check = struct { |
| 115 | } | 134 | } |
| 116 | }; | 135 | }; |
| 117 | | 136 | |
| | 137 | /// Creates a new sequence of actions with `phrase` as the first anchor searched phrase. |
| 118 | pub fn check(self: *CheckObjectStep, phrase: []const u8) void { | 138 | pub fn check(self: *CheckObjectStep, phrase: []const u8) void { |
| 119 | var new_check = Check.create(self.builder); | 139 | var new_check = Check.create(self.builder); |
| 120 | new_check.match(phrase); | 140 | new_check.match(phrase); |
| 121 | self.checks.append(new_check) catch unreachable; | 141 | self.checks.append(new_check) catch unreachable; |
| 122 | } | 142 | } |
| 123 | | 143 | |
| | 144 | /// Adds another searched phrase to the latest created Check with `CheckObjectStep.check(...)`. |
| | 145 | /// Asserts at least one check already exists. |
| 124 | pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void { | 146 | pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void { |
| 125 | assert(self.checks.items.len > 0); | 147 | assert(self.checks.items.len > 0); |
| 126 | const last = &self.checks.items[self.checks.items.len - 1]; | 148 | const last = &self.checks.items[self.checks.items.len - 1]; |
| 127 | last.match(phrase); | 149 | last.match(phrase); |
| 128 | } | 150 | } |
| 129 | | 151 | |
| | 152 | /// Creates a new check checking specifically symbol table parsed and dumped from the object |
| | 153 | /// file. |
| | 154 | /// Issuing this check will force parsing and dumping of the symbol table. |
| 130 | pub fn checkInSymtab(self: *CheckObjectStep) void { | 155 | pub fn checkInSymtab(self: *CheckObjectStep) void { |
| 131 | self.dump_symtab = true; | 156 | self.dump_symtab = true; |
| 132 | self.check("symtab"); | 157 | self.check("symtab"); |
| 133 | } | 158 | } |
| 134 | | 159 | |
| | 160 | /// Creates a new standalone, singular check which allows running simple binary operations |
| | 161 | /// on the extracted variables. It will then compare the reduced program with the value of |
| | 162 | /// the expected variable. |
| 135 | pub fn checkComputeEq(self: *CheckObjectStep, program: []const u8, expected: []const u8) void { | 163 | pub fn checkComputeEq(self: *CheckObjectStep, program: []const u8, expected: []const u8) void { |
| 136 | const gpa = self.builder.allocator; | 164 | const gpa = self.builder.allocator; |
| 137 | var ca = ComputeEqAction{ | 165 | var ca = ComputeEqAction{ |