@@ -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,30 +37,36 @@ 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
56
61
/// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64
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 ,
0 commit comments