Rizin
unix-like reverse engineering framework and cli tools
il_validate.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2022 Florian Märkl <info@florianmaerkl.de>
2 // SPDX-License-Identifier: LGPL-3.0-only
3 
4 #include <rz_il/rz_il_validate.h>
5 #include <ht_uu.h>
6 
8 // ---------------------- context -----------------------
9 
14  HtPP /*<const char *, RzILSortPure *>*/ *global_vars;
15  HtUU /*<RzILMemIndex, ut32:ut32>*/ *mems;
17 }; /* RzILValidateGlobalContext */
18 
19 static void var_kv_free(HtPPKv *kv) {
20  free(kv->key);
21  free(kv->value);
22 }
23 
24 static void var_kv_unown_free(HtPPKv *kv) {
25  free(kv->key);
26 }
27 
33  rz_return_val_if_fail(pc_len, NULL);
35  if (!ctx) {
36  return NULL;
37  }
38  ctx->pc_len = pc_len;
39  ctx->global_vars = ht_pp_new(NULL, var_kv_free, NULL);
40  if (!ctx->global_vars) {
41  free(ctx);
42  return NULL;
43  }
44  ctx->mems = ht_uu_new0();
45  if (!ctx->mems) {
46  ht_pp_free(ctx->global_vars);
47  free(ctx);
48  return NULL;
49  }
50  return ctx;
51 }
52 
59  if (!hts) {
60  return;
61  }
62  *hts = sort;
63  ht_pp_update(ctx->global_vars, name, hts);
64 }
65 
70  rz_return_if_fail(ctx && key_len && val_len);
71  ht_uu_update(ctx->mems, idx, ((ut64)key_len << 32) | (ut64)val_len);
72 }
73 
81  if (vars) {
82  void **it;
83  rz_pvector_foreach (vars, it) {
84  RzILVar *var = *it;
86  }
87  rz_pvector_free(vars);
88  }
89  for (size_t i = 0; i < rz_pvector_len(&vm->vm_memory); i++) {
90  RzILMem *mem = rz_pvector_at(&vm->vm_memory, i);
91  if (!mem) {
92  continue;
93  }
95  }
96  return ctx;
97 }
98 
100  if (!ctx) {
101  return;
102  }
103  ht_pp_free(ctx->global_vars);
104  ht_uu_free(ctx->mems);
105  free(ctx);
106 }
107 
108 typedef struct {
110 
117  HtPP /*<const char *, RzILSortPure *>*/ *local_vars_known;
118 
119  HtPP /*<const char *, RzILSortPure *>*/ *local_vars_available;
120 } LocalContext;
121 
123  ctx->global_ctx = global_ctx;
124  ctx->local_vars_known = ht_pp_new(NULL, var_kv_free, NULL);
125  if (!ctx->local_vars_known) {
126  return false;
127  }
128  ctx->local_vars_available = ht_pp_new(NULL, var_kv_unown_free, NULL);
129  if (!ctx->local_vars_available) {
130  ht_pp_free(ctx->local_vars_known);
131  return false;
132  }
133  return true;
134 }
135 
137  ht_pp_free(ctx->local_vars_known);
138  ht_pp_free(ctx->local_vars_available);
139 }
140 
141 static bool local_var_copy_known_cb(RZ_NONNULL void *user, const void *k, const void *v) {
142  LocalContext *dst = user;
144  if (!sort) {
145  return false;
146  }
147  *sort = *(RzILSortPure *)v;
148  ht_pp_update(dst->local_vars_known, k, sort);
149  return true;
150 }
151 
152 static bool local_var_copy_avail_cb(RZ_NONNULL void *user, const void *k, const void *v) {
153  LocalContext *dst = user;
154  RzILSortPure *sort = ht_pp_find(dst->local_vars_known, k, NULL);
155  // known is superset of avail, so we can assert this:
156  rz_return_val_if_fail(sort && rz_il_sort_pure_eq(*sort, *(RzILSortPure *)v), false);
157  ht_pp_update(dst->local_vars_available, k, sort);
158  return true;
159 }
160 
162  if (!local_context_init(dst, src->global_ctx)) {
163  return false;
164  }
165  ht_pp_foreach(src->local_vars_known, local_var_copy_known_cb, dst);
166  ht_pp_foreach(src->local_vars_available, local_var_copy_avail_cb, dst);
167  return true;
168 }
169 
170 typedef struct {
171  bool failed;
173  const char *op_name;
177 
178 // called on src, take the union of the known types or fail if they don't agree
179 static bool local_var_meet_known_cb(RZ_NONNULL void *user, const void *k, const void *v) {
180  LocalContextMeet *meet = user;
181  RzILSortPure src_sort = *(RzILSortPure *)v;
182  RzILSortPure *dst_sort = ht_pp_find(meet->dst->local_vars_known, k, NULL);
183  if (dst_sort && !rz_il_sort_pure_eq(src_sort, *dst_sort)) {
184  char *src_sort_s = rz_il_sort_pure_stringify(src_sort);
185  char *dst_sort_s = rz_il_sort_pure_stringify(*dst_sort);
186  rz_strbuf_appendf(meet->report_builder, "Control flow paths from %s op do not agree on the type of local variable \"%s\": %s vs. %s.\n",
187  meet->op_name, (const char *)k, src_sort_s, dst_sort_s);
188  free(src_sort_s);
189  free(dst_sort_s);
190  meet->failed = true;
191  return false;
192  }
193  if (!dst_sort) {
194  dst_sort = RZ_NEW(RzILSortPure);
195  if (!dst_sort) {
196  meet->failed = true;
197  return false;
198  }
199  *dst_sort = src_sort;
200  ht_pp_update(meet->dst->local_vars_known, k, dst_sort);
201  }
202  return true;
203 }
204 
205 // called on dst, remove all vars from dst that do not appear in src (intersection)
206 static bool local_var_meet_avail_cb(RZ_NONNULL void *user, const void *k, const void *v) {
207  LocalContextMeet *meet = user;
208  RzILSortPure *src_sort = ht_pp_find(meet->src->local_vars_available, k, NULL);
209  if (!src_sort) {
210  ht_pp_delete(meet->dst->local_vars_available, k);
211  }
212  return true;
213 }
214 
228 static bool local_context_meet(RZ_INOUT LocalContext *a, RZ_IN LocalContext *b, RzStrBuf *report_builder, const char *op_name) {
229  LocalContextMeet meet = {
230  .failed = false,
231  .report_builder = report_builder,
232  .op_name = op_name,
233  .dst = a,
234  .src = b
235  };
236  ht_pp_foreach(b->local_vars_known, local_var_meet_known_cb, &meet);
237  if (meet.failed) {
238  return false;
239  }
240  ht_pp_foreach(a->local_vars_available, local_var_meet_avail_cb, &meet);
241  return true;
242 }
243 
245 
256 typedef struct local_pure_var_t {
257  const char *name;
261 
262 // clang-format off
263 #define VALIDATOR_PURE_ARGS \
264  RZ_NULLABLE RzILOpPure *op, \
265  RZ_NONNULL RzILSortPure *sort_out, \
266  RZ_NONNULL RzStrBuf *report_builder, \
267  RZ_NONNULL const LocalContext *ctx, \
268  RZ_NULLABLE LocalPureVar *local_pure_var_stack
269 // clang-format on
270 
273 
274 #define VALIDATOR_PURE_NAME(op) validate_pure_##op
275 #define VALIDATOR_PURE(op) static bool VALIDATOR_PURE_NAME(op)(VALIDATOR_PURE_ARGS)
276 #define VALIDATOR_ASSERT(condition, ...) \
277  do { \
278  if (!(condition)) { \
279  rz_strbuf_appendf(report_builder, __VA_ARGS__); \
280  return false; \
281  } \
282  } while (0)
283 #define VALIDATOR_DESCEND(op, sort) \
284  do { \
285  if (!validate_pure(op, sort, report_builder, ctx, local_pure_var_stack)) { \
286  return false; \
287  } \
288  } while (0)
289 
291  RzILOpArgsVar *args = &op->op.var;
292  VALIDATOR_ASSERT(args->v, "Var name of var op is NULL.\n");
293  switch (args->kind) {
294  case RZ_IL_VAR_KIND_GLOBAL: {
295  RzILSortPure *sort = ht_pp_find(ctx->global_ctx->global_vars, args->v, NULL);
296  VALIDATOR_ASSERT(sort, "Global variable \"%s\" referenced by var op does not exist.\n", args->v);
297  *sort_out = *sort;
298  return true;
299  }
300  case RZ_IL_VAR_KIND_LOCAL: {
301  RzILSortPure *sort = ht_pp_find(ctx->local_vars_available, args->v, NULL);
302  VALIDATOR_ASSERT(sort, "Local variable \"%s\" is not available at var op.\n", args->v);
303  *sort_out = *sort;
304  return true;
305  }
307  for (LocalPureVar *loc = local_pure_var_stack; loc; loc = loc->next) {
308  if (!strcmp(loc->name, args->v)) {
309  *sort_out = loc->sort;
310  return true;
311  }
312  }
313  VALIDATOR_ASSERT(false, "Local pure variable \"%s\" unbound at var op.\n", args->v);
314  return false;
315  }
316  default:
317  VALIDATOR_ASSERT(false, "Var op has invalid kind.\n");
318  }
319  return true;
320 }
321 
322 VALIDATOR_PURE(bool_const) {
323  *sort_out = rz_il_sort_pure_bool();
324  return true;
325 }
326 
328  RzBitVector *bv = op->op.bitv.value;
329  if (!bv) {
330  rz_strbuf_appendf(report_builder, "Bitvector in constant bitvector op is NULL.\n");
331  return false;
332  }
333  *sort_out = rz_il_sort_pure_bv(rz_bv_len(bv));
334  return true;
335 }
336 
341 VALIDATOR_PURE(bitv_binop) {
342  RzILOpPure *x = op->op.add.x; // just add is fine, all ops in here use the same struct
343  RzILOpPure *y = op->op.add.y;
344  RzILSortPure sx;
345  VALIDATOR_DESCEND(x, &sx);
346  VALIDATOR_ASSERT(sx.type == RZ_IL_TYPE_PURE_BITVECTOR, "Left operand of %s op is not a bitvector.\n", rz_il_op_pure_code_stringify(op->code));
347  RzILSortPure sy;
348  VALIDATOR_DESCEND(y, &sy);
349  VALIDATOR_ASSERT(sy.type == RZ_IL_TYPE_PURE_BITVECTOR, "Right operand of %s op is not a bitvector.\n", rz_il_op_pure_code_stringify(op->code));
350  VALIDATOR_ASSERT(sx.props.bv.length == sy.props.bv.length, "Operand sizes of %s op do not agree: %u vs. %u.\n",
351  rz_il_op_pure_code_stringify(op->code), (unsigned int)sx.props.bv.length, (unsigned int)sy.props.bv.length);
352  *sort_out = sx;
353  return true;
354 }
355 
357  RzILOpArgsIte *args = &op->op.ite;
359  VALIDATOR_DESCEND(args->condition, &sc);
360  VALIDATOR_ASSERT(sc.type == RZ_IL_TYPE_PURE_BOOL, "Condition of ite op is not boolean.\n");
361  RzILSortPure sx;
362  VALIDATOR_DESCEND(args->x, &sx);
363  RzILSortPure sy;
364  VALIDATOR_DESCEND(args->y, &sy);
365  if (!rz_il_sort_pure_eq(sx, sy)) {
366  char *sxs = rz_il_sort_pure_stringify(sx);
367  char *sys = rz_il_sort_pure_stringify(sy);
368  rz_strbuf_appendf(report_builder, "Types of ite branches do not agree: %s vs. %s.\n",
369  rz_str_get_null(sxs), rz_str_get_null(sys));
370  free(sxs);
371  free(sys);
372  return false;
373  }
374  *sort_out = sx;
375  return true;
376 }
377 
379  RzILOpArgsLet *args = &op->op.let;
380  VALIDATOR_ASSERT(args->name, "Var name of let op is NULL.\n");
381  VALIDATOR_ASSERT(args->exp, "Expression of let op is NULL.\n");
382  VALIDATOR_ASSERT(args->body, "Body of let op is NULL.\n");
384  VALIDATOR_DESCEND(args->exp, &sort);
385  LocalPureVar var = {
386  .name = args->name,
387  .sort = sort,
388  .next = local_pure_var_stack
389  };
390  return validate_pure(args->body, sort_out, report_builder, ctx, &var);
391 }
392 
394  RzILOpArgsBoolInv *args = &op->op.boolinv;
397  VALIDATOR_ASSERT(sort.type == RZ_IL_TYPE_PURE_BOOL, "Operand of boolean inv op is not boolean.\n");
398  *sort_out = rz_il_sort_pure_bool();
399  return true;
400 }
401 
406 VALIDATOR_PURE(bool_binop) {
407  RzILOpPure *x = op->op.booland.x; // just booland is fine, all ops in here use the same struct
408  RzILOpPure *y = op->op.booland.y;
409  RzILSortPure sx;
410  VALIDATOR_DESCEND(x, &sx);
411  VALIDATOR_ASSERT(sx.type == RZ_IL_TYPE_PURE_BOOL, "Left operand of %s op is not bool.\n", rz_il_op_pure_code_stringify(op->code));
412  RzILSortPure sy;
413  VALIDATOR_DESCEND(y, &sy);
414  VALIDATOR_ASSERT(sy.type == RZ_IL_TYPE_PURE_BOOL, "Right operand of %s op is not bool.\n", rz_il_op_pure_code_stringify(op->code));
415  *sort_out = rz_il_sort_pure_bool();
416  return true;
417 }
418 
423 VALIDATOR_PURE(bitv_bool_unop) {
424  RzILOpPure *x = op->op.msb.bv; // just msb is fine, all ops in here use the same struct
425  RzILSortPure sx;
426  VALIDATOR_DESCEND(x, &sx);
427  VALIDATOR_ASSERT(sx.type == RZ_IL_TYPE_PURE_BITVECTOR, "Operand of %s op is not a bitvector.\n", rz_il_op_pure_code_stringify(op->code));
428  *sort_out = rz_il_sort_pure_bool();
429  return true;
430 }
431 
436 VALIDATOR_PURE(bitv_unop) {
437  RzILOpPure *x = op->op.lognot.bv; // just lognot is fine, all ops in here use the same struct
438  RzILSortPure sx;
439  VALIDATOR_DESCEND(x, &sx);
440  VALIDATOR_ASSERT(sx.type == RZ_IL_TYPE_PURE_BITVECTOR, "Operand of %s op is not a bitvector.\n", rz_il_op_pure_code_stringify(op->code));
441  *sort_out = sx;
442  return true;
443 }
444 
446  RzILOpArgsShiftLeft *args = &op->op.shiftl;
447  RzILSortPure sf;
448  VALIDATOR_DESCEND(args->fill_bit, &sf);
449  VALIDATOR_ASSERT(sf.type == RZ_IL_TYPE_PURE_BOOL, "Fill operand of %s op is not bool.\n", rz_il_op_pure_code_stringify(op->code));
450  RzILSortPure sx;
451  VALIDATOR_DESCEND(args->x, &sx);
452  VALIDATOR_ASSERT(sx.type == RZ_IL_TYPE_PURE_BITVECTOR, "Value operand of %s op is not a bitvector.\n", rz_il_op_pure_code_stringify(op->code));
453  RzILSortPure sy;
454  VALIDATOR_DESCEND(args->y, &sy);
455  VALIDATOR_ASSERT(sy.type == RZ_IL_TYPE_PURE_BITVECTOR, "Distance operand of %s op is not a bitvector.\n", rz_il_op_pure_code_stringify(op->code));
456  *sort_out = sx;
457  return true;
458 }
459 
461  RzILOpArgsEq *args = &op->op.eq;
462  RzILSortPure sx;
463  VALIDATOR_DESCEND(args->x, &sx);
464  VALIDATOR_ASSERT(sx.type == RZ_IL_TYPE_PURE_BITVECTOR, "Left operand of %s op is not a bitvector.\n", rz_il_op_pure_code_stringify(op->code));
465  RzILSortPure sy;
466  VALIDATOR_DESCEND(args->y, &sy);
467  VALIDATOR_ASSERT(sy.type == RZ_IL_TYPE_PURE_BITVECTOR, "Right operand of %s op is not a bitvector.\n", rz_il_op_pure_code_stringify(op->code));
468  VALIDATOR_ASSERT(sx.props.bv.length == sy.props.bv.length, "Operand sizes of %s op do not agree: %u vs. %u.\n",
469  rz_il_op_pure_code_stringify(op->code), (unsigned int)sx.props.bv.length, (unsigned int)sy.props.bv.length);
470  *sort_out = rz_il_sort_pure_bool();
471  return true;
472 }
473 
475  RzILOpArgsCast *args = &op->op.cast;
476  VALIDATOR_ASSERT(args->length, "Length of cast op is 0.\n");
477  RzILSortPure sf;
478  VALIDATOR_DESCEND(args->fill, &sf);
479  VALIDATOR_ASSERT(sf.type == RZ_IL_TYPE_PURE_BOOL, "Fill operand of cast op is not bool.\n");
480  RzILSortPure sx;
481  VALIDATOR_DESCEND(args->val, &sx);
482  VALIDATOR_ASSERT(sx.type == RZ_IL_TYPE_PURE_BITVECTOR, "Value operand of %s op is not a bitvector.\n", rz_il_op_pure_code_stringify(op->code));
483  *sort_out = rz_il_sort_pure_bv(args->length);
484  return true;
485 }
486 
488  RzILOpPure *x = op->op.append.high;
489  RzILOpPure *y = op->op.append.low;
490  RzILSortPure sx;
491  VALIDATOR_DESCEND(x, &sx);
492  VALIDATOR_ASSERT(sx.type == RZ_IL_TYPE_PURE_BITVECTOR, "High operand of append op is not a bitvector.\n");
493  RzILSortPure sy;
494  VALIDATOR_DESCEND(y, &sy);
495  VALIDATOR_ASSERT(sy.type == RZ_IL_TYPE_PURE_BITVECTOR, "Low operand of append op is not a bitvector.\n");
496  *sort_out = rz_il_sort_pure_bv(sx.props.bv.length + sy.props.bv.length);
497  return true;
498 }
499 
501  RzILOpArgsLoad *args = &op->op.load;
502  bool found = false;
503  ut64 htm = ht_uu_find(ctx->global_ctx->mems, args->mem, &found);
504  VALIDATOR_ASSERT(found, "Mem %u referenced by load op does not exist.\n", (unsigned int)args->mem);
505  ut32 key_len = htm >> 32;
506  ut32 val_len = htm & UT32_MAX;
507  RzILSortPure sk;
508  VALIDATOR_DESCEND(args->key, &sk);
509  VALIDATOR_ASSERT(sk.type == RZ_IL_TYPE_PURE_BITVECTOR, "Key operand of load op is not a bitvector.\n");
510  VALIDATOR_ASSERT(sk.props.bv.length == key_len, "Length of key operand (%u) of load op is not equal to key length %u of mem %u.\n",
511  (unsigned int)sk.props.bv.length, (unsigned int)key_len, (unsigned int)args->mem);
512  *sort_out = rz_il_sort_pure_bv(val_len);
513  return true;
514 }
515 
517  RzILOpArgsLoadW *args = &op->op.loadw;
518  VALIDATOR_ASSERT(args->n_bits, "Length of loadw op is 0.\n");
519  bool found = false;
520  ut64 htm = ht_uu_find(ctx->global_ctx->mems, args->mem, &found);
521  VALIDATOR_ASSERT(found, "Mem %u referenced by loadw op does not exist.\n", (unsigned int)args->mem);
522  ut32 key_len = htm >> 32;
523  RzILSortPure sk;
524  VALIDATOR_DESCEND(args->key, &sk);
525  VALIDATOR_ASSERT(sk.type == RZ_IL_TYPE_PURE_BITVECTOR, "Key operand of loadw op is not a bitvector.\n");
526  VALIDATOR_ASSERT(sk.props.bv.length == key_len, "Length of key operand (%u) of loadw op is not equal to key length %u of mem %u.\n",
527  (unsigned int)sk.props.bv.length, (unsigned int)key_len, (unsigned int)args->mem);
528  *sort_out = rz_il_sort_pure_bv(args->n_bits);
529  return true;
530 }
531 
536  [RZ_IL_OP_B0] = VALIDATOR_PURE_NAME(bool_const),
537  [RZ_IL_OP_B1] = VALIDATOR_PURE_NAME(bool_const),
539  [RZ_IL_OP_AND] = VALIDATOR_PURE_NAME(bool_binop),
540  [RZ_IL_OP_OR] = VALIDATOR_PURE_NAME(bool_binop),
541  [RZ_IL_OP_XOR] = VALIDATOR_PURE_NAME(bool_binop),
543  [RZ_IL_OP_MSB] = VALIDATOR_PURE_NAME(bitv_bool_unop),
544  [RZ_IL_OP_LSB] = VALIDATOR_PURE_NAME(bitv_bool_unop),
545  [RZ_IL_OP_IS_ZERO] = VALIDATOR_PURE_NAME(bitv_bool_unop),
546  [RZ_IL_OP_NEG] = VALIDATOR_PURE_NAME(bitv_unop),
547  [RZ_IL_OP_LOGNOT] = VALIDATOR_PURE_NAME(bitv_unop),
548  [RZ_IL_OP_ADD] = VALIDATOR_PURE_NAME(bitv_binop),
549  [RZ_IL_OP_SUB] = VALIDATOR_PURE_NAME(bitv_binop),
550  [RZ_IL_OP_MUL] = VALIDATOR_PURE_NAME(bitv_binop),
551  [RZ_IL_OP_DIV] = VALIDATOR_PURE_NAME(bitv_binop),
552  [RZ_IL_OP_MOD] = VALIDATOR_PURE_NAME(bitv_binop),
553  [RZ_IL_OP_SDIV] = VALIDATOR_PURE_NAME(bitv_binop),
554  [RZ_IL_OP_SMOD] = VALIDATOR_PURE_NAME(bitv_binop),
555  [RZ_IL_OP_LOGAND] = VALIDATOR_PURE_NAME(bitv_binop),
556  [RZ_IL_OP_LOGOR] = VALIDATOR_PURE_NAME(bitv_binop),
557  [RZ_IL_OP_LOGXOR] = VALIDATOR_PURE_NAME(bitv_binop),
567 };
568 
570  VALIDATOR_ASSERT(op, "Encountered NULL for pure op.\n");
572  rz_return_val_if_fail(validator, false);
573  return validator(op, sort_out, report_builder, ctx, local_pure_var_stack);
574 }
575 
586  LocalContext local_ctx;
587  if (!local_context_init(&local_ctx, ctx)) {
588  if (report_out) {
589  *report_out = NULL;
590  }
591  return false;
592  }
593  RzStrBuf report_builder;
594  rz_strbuf_init(&report_builder);
595  RzILSortPure sort = { 0 };
596  bool valid = validate_pure(op, &sort, &report_builder, &local_ctx, NULL);
597  local_context_fini(&local_ctx);
598  if (sort_out) {
599  *sort_out = sort;
600  }
601  if (report_out) {
602  *report_out = rz_strbuf_is_empty(&report_builder) ? NULL : rz_str_trim_tail(rz_strbuf_drain_nofree(&report_builder));
603  }
604  rz_strbuf_fini(&report_builder);
605  return valid;
606 }
607 
609 
611 
616 #undef VALIDATOR_PURE_ARGS
617 #undef VALIDATOR_PURE_NAME
618 #undef VALIDATOR_PURE
619 #undef VALIDATOR_DESCEND
620 
621 // clang-format off
622 #define VALIDATOR_EFFECT_ARGS \
623  RZ_NULLABLE RzILOpEffect *op, \
624  RZ_NONNULL RzILTypeEffect *type_out, \
625  RZ_NONNULL RzStrBuf *report_builder, \
626  RZ_NONNULL LocalContext *ctx
627 // clang-format on
628 
631 
632 #define VALIDATOR_EFFECT_NAME(op) validate_effect_##op
633 #define VALIDATOR_EFFECT(op) static bool VALIDATOR_EFFECT_NAME(op)(VALIDATOR_EFFECT_ARGS)
634 #define VALIDATOR_DESCEND_PURE(op, sort) \
635  do { \
636  if (!validate_pure(op, sort, report_builder, ctx, NULL)) { \
637  return false; \
638  } \
639  } while (0)
640 #define VALIDATOR_DESCEND_EFFECT(op, etype, ectx, cleanup) \
641  do { \
642  if (!validate_effect(op, etype, report_builder, ectx)) { \
643  cleanup return false; \
644  } \
645  } while (0)
646 
648  *type_out = RZ_IL_TYPE_EFFECT_NONE;
649  return true;
650 }
651 
653  *type_out = RZ_IL_TYPE_EFFECT_NONE;
654  return true;
655 }
656 
658  RzILOpArgsStore *args = &op->op.store;
659  bool found = false;
660  ut64 htm = ht_uu_find(ctx->global_ctx->mems, args->mem, &found);
661  VALIDATOR_ASSERT(found, "Mem %u referenced by store op does not exist.\n", (unsigned int)args->mem);
662  ut32 key_len = htm >> 32;
663  ut32 val_len = htm & UT32_MAX;
664  RzILSortPure sk;
665  VALIDATOR_DESCEND_PURE(args->key, &sk);
666  VALIDATOR_ASSERT(sk.type == RZ_IL_TYPE_PURE_BITVECTOR, "Key operand of store op is not a bitvector.\n");
667  VALIDATOR_ASSERT(sk.props.bv.length == key_len, "Length of key operand (%u) of store op is not equal to key length %u of mem %u.\n",
668  (unsigned int)sk.props.bv.length, (unsigned int)key_len, (unsigned int)args->mem);
669  RzILSortPure sv;
670  VALIDATOR_DESCEND_PURE(args->value, &sv);
671  VALIDATOR_ASSERT(sv.type == RZ_IL_TYPE_PURE_BITVECTOR, "Value operand of store op is not a bitvector.\n");
672  VALIDATOR_ASSERT(sv.props.bv.length == val_len, "Length of value operand (%u) of store op is not equal to value length %u of mem %u.\n",
673  (unsigned int)sv.props.bv.length, (unsigned int)val_len, (unsigned int)args->mem);
674  *type_out = RZ_IL_TYPE_EFFECT_DATA;
675  return true;
676 }
677 
679  RzILOpArgsStoreW *args = &op->op.storew;
680  bool found = false;
681  ut64 htm = ht_uu_find(ctx->global_ctx->mems, args->mem, &found);
682  VALIDATOR_ASSERT(found, "Mem %u referenced by storew op does not exist.\n", (unsigned int)args->mem);
683  ut32 key_len = htm >> 32;
684  RzILSortPure sk;
685  VALIDATOR_DESCEND_PURE(args->key, &sk);
686  VALIDATOR_ASSERT(sk.type == RZ_IL_TYPE_PURE_BITVECTOR, "Key operand of storew op is not a bitvector.\n");
687  VALIDATOR_ASSERT(sk.props.bv.length == key_len, "Length of key operand (%u) of storew op is not equal to key length %u of mem %u.\n",
688  (unsigned int)sk.props.bv.length, (unsigned int)key_len, (unsigned int)args->mem);
689  RzILSortPure sv;
690  VALIDATOR_DESCEND_PURE(args->value, &sv);
691  VALIDATOR_ASSERT(sv.type == RZ_IL_TYPE_PURE_BITVECTOR, "Value operand of storew op is not a bitvector.\n");
692  *type_out = RZ_IL_TYPE_EFFECT_DATA;
693  return true;
694 }
695 
697  RzILOpArgsSet *args = &op->op.set;
698  VALIDATOR_ASSERT(args->v, "Var name of set op is NULL.\n");
699  RzILSortPure sx;
700  VALIDATOR_DESCEND_PURE(args->x, &sx);
701  RzILSortPure *sort = ht_pp_find(
702  args->is_local ? ctx->local_vars_known : ctx->global_ctx->global_vars, args->v, NULL);
703  VALIDATOR_ASSERT(args->is_local || sort, "Global variable \"%s\" referenced by set op does not exist.\n", args->v);
704  if (sort && !rz_il_sort_pure_eq(*sort, sx)) {
705  char *svs = rz_il_sort_pure_stringify(*sort);
706  char *sxs = rz_il_sort_pure_stringify(sx);
707  rz_strbuf_appendf(report_builder, "Types of %sal variable \"%s\" and set op do not agree: %s vs. %s.\n",
708  args->is_local ? "loc" : "glob",
709  args->v, rz_str_get_null(svs), rz_str_get_null(sxs));
710  free(svs);
711  free(sxs);
712  return false;
713  }
714  if (args->is_local) {
715  if (!sort) {
717  if (!sort) {
718  return false;
719  }
720  *sort = sx;
721  ht_pp_update(ctx->local_vars_known, args->v, sort);
722  }
723  ht_pp_update(ctx->local_vars_available, args->v, sort);
724  }
725  *type_out = RZ_IL_TYPE_EFFECT_DATA;
726  return true;
727 }
728 
730  RzILOpArgsJmp *args = &op->op.jmp;
731  RzILSortPure sd;
732  VALIDATOR_DESCEND_PURE(args->dst, &sd);
733  VALIDATOR_ASSERT(sd.type == RZ_IL_TYPE_PURE_BITVECTOR, "Dst operand of jmp op is not a bitvector.\n");
734  VALIDATOR_ASSERT(sd.props.bv.length == ctx->global_ctx->pc_len,
735  "Length of dst operand (%u) of jmp op is not equal to pc length %u.\n",
736  (unsigned int)sd.props.bv.length, (unsigned int)ctx->global_ctx->pc_len);
737  *type_out = RZ_IL_TYPE_EFFECT_CTRL;
738  return true;
739 }
740 
742  RzILOpArgsGoto *args = &op->op.goto_;
743  VALIDATOR_ASSERT(args->lbl, "Label of goto op is NULL.\n");
744  // So far, no restrictions on goto because labels are dynamically created. This might change in the future.
745  *type_out = RZ_IL_TYPE_EFFECT_CTRL;
746  return true;
747 }
748 
750  RzILOpArgsSeq *args = &op->op.seq;
751  RzILTypeEffect tx;
752  VALIDATOR_DESCEND_EFFECT(args->x, &tx, ctx, {});
753  RzILTypeEffect ty;
754  VALIDATOR_DESCEND_EFFECT(args->y, &ty, ctx, {});
755  // Code after a jmp/goto makes no sense because the jmp naturally jumps somewhere else already.
756  // Intuitively, this could be considered just dead code and valid, but because it is not practically useful,
757  // we reject such code completely for now, which gives us more freedom if in the future we do want to define
758  // semantics for code after ctrl in some way.
759  VALIDATOR_ASSERT(!(tx & RZ_IL_TYPE_EFFECT_CTRL) || !ty, "Encountered further effects after a ctrl effect in seq op.");
760  *type_out = tx | ty;
761  return true;
762 }
763 
765  RzILOpArgsBlk *args = &op->op.blk;
766  // Semantics of blk are still somewhat undefined in RzIL
767  RzILTypeEffect td;
768  VALIDATOR_DESCEND_EFFECT(args->data_eff, &td, ctx, {});
769  VALIDATOR_ASSERT((td | RZ_IL_TYPE_EFFECT_DATA) == RZ_IL_TYPE_EFFECT_DATA, "Data effect operand of blk op does not only perform data effects.");
770  RzILTypeEffect tc;
771  VALIDATOR_DESCEND_EFFECT(args->ctrl_eff, &tc, ctx, {});
772  VALIDATOR_ASSERT((tc | RZ_IL_TYPE_EFFECT_CTRL) == RZ_IL_TYPE_EFFECT_CTRL, "Control effect operand of blk op does not only perform control effects.");
773  *type_out = td | tc;
774  return true;
775 }
776 
778  RzILOpArgsRepeat *args = &op->op.repeat;
780  VALIDATOR_DESCEND_PURE(args->condition, &sc);
781  VALIDATOR_ASSERT(sc.type == RZ_IL_TYPE_PURE_BOOL, "Condition of repeat op is not boolean.\n");
782  LocalContext loop_ctx;
783  if (!local_context_copy(&loop_ctx, ctx)) {
784  return false;
785  }
786  RzILTypeEffect t;
787  VALIDATOR_DESCEND_EFFECT(args->data_eff, &t, ctx, { local_context_fini(&loop_ctx); });
788  // Enforce (by overapproximation) that there are no effects after a ctrl effect, like in seq.
789  // In a loop, we just reject ctrl completely. This also matches BAP's `repeat : bool -> data eff -> data eff`.
790  VALIDATOR_ASSERT((t | RZ_IL_TYPE_EFFECT_DATA) == RZ_IL_TYPE_EFFECT_DATA, "Body operand of repeat op does not only perform data effects.");
791  bool val = local_context_meet(ctx, &loop_ctx, report_builder, "repeat");
792  local_context_fini(&loop_ctx);
793  *type_out = t;
794  return val;
795 }
796 
798  RzILOpArgsBranch *args = &op->op.branch;
800  VALIDATOR_DESCEND_PURE(args->condition, &sc);
801  VALIDATOR_ASSERT(sc.type == RZ_IL_TYPE_PURE_BOOL, "Condition of branch op is not boolean.\n");
802  LocalContext false_ctx;
803  if (!local_context_copy(&false_ctx, ctx)) {
804  return false;
805  }
806  RzILTypeEffect tt;
807  VALIDATOR_DESCEND_EFFECT(args->true_eff, &tt, ctx, { local_context_fini(&false_ctx); });
808  RzILTypeEffect tf;
809  VALIDATOR_DESCEND_EFFECT(args->false_eff, &tf, &false_ctx, { local_context_fini(&false_ctx); });
810  bool val = local_context_meet(ctx, &false_ctx, report_builder, "branch");
811  local_context_fini(&false_ctx);
812  *type_out = tt | tf;
813  return val;
814 }
815 
828 };
829 
831  VALIDATOR_ASSERT(op, "Encountered NULL for effect op.\n");
833  rz_return_val_if_fail(validator, false);
834  return validator(op, type_out, report_builder, ctx);
835 }
836 
847  RZ_NULLABLE RZ_OUT HtPP /*<const char *, RzILSortPure *>*/ **local_var_sorts_out,
849  RZ_NULLABLE RZ_OUT RzILValidateReport *report_out) {
850  LocalContext local_ctx;
851  if (!local_context_init(&local_ctx, ctx)) {
852  if (report_out) {
853  *report_out = NULL;
854  }
855  return false;
856  }
858  RzStrBuf report_builder;
859  rz_strbuf_init(&report_builder);
860  bool valid = validate_effect(op, &type, &report_builder, &local_ctx);
861  if (valid && local_var_sorts_out) {
862  *local_var_sorts_out = local_ctx.local_vars_known;
863  local_ctx.local_vars_known = NULL;
864  }
865  local_context_fini(&local_ctx);
866  if (type_out) {
867  *type_out = type;
868  }
869  if (report_out) {
870  *report_out = rz_strbuf_is_empty(&report_builder) ? NULL : rz_str_trim_tail(rz_strbuf_drain_nofree(&report_builder));
871  }
872  rz_strbuf_fini(&report_builder);
873  return valid;
874 }
875 
#define jmp
lzma_index ** i
Definition: index.h:629
lzma_index * src
Definition: index.h:567
#define cast(x, y)
Definition: arch_53.h:166
static RzILOpEffect * cmp(cs_insn *insn, bool is_thumb)
Definition: arm_il32.c:942
static RZ_NULLABLE RzILOpBitVector * shift(RzILOpBitVector *val, RZ_NULLABLE RzILOpBool **carry_out, arm_shifter type, RZ_OWN RzILOpBitVector *dist)
Definition: arm_il32.c:190
ut16 val
Definition: armass64_const.h:6
#define append(x, y)
Definition: cmd_print.c:1740
#define RZ_API
#define NULL
Definition: cris-opc.c:27
uint32_t ut32
const char * k
Definition: dsignal.c:11
const char * v
Definition: dsignal.c:12
static char sc[]
Definition: egg_cb.c:6
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
RZ_API const KEY_TYPE bool * found
Definition: ht_inc.h:130
RZ_API ut32 rz_il_mem_key_len(RzILMem *mem)
Get the bit-size of a key (address) into the memory.
Definition: mem.c:49
RZ_API ut32 rz_il_mem_value_len(RzILMem *mem)
Get the bit-size of a value in the memory.
Definition: mem.c:60
RZ_API RZ_NONNULL const char * rz_il_op_pure_code_stringify(RzILOpPureCode code)
Definition: il_export.c:872
RZ_API RZ_OWN char * rz_il_sort_pure_stringify(RzILSortPure sort)
Definition: il_export.c:952
#define VALIDATOR_EFFECT_NAME(op)
Definition: il_validate.c:632
#define VALIDATOR_PURE_ARGS
Definition: il_validate.c:263
RZ_API void rz_il_validate_global_context_free(RzILValidateGlobalContext *ctx)
Definition: il_validate.c:99
#define VALIDATOR_ASSERT(condition,...)
Definition: il_validate.c:276
static void local_context_fini(LocalContext *ctx)
Definition: il_validate.c:136
static bool local_context_meet(RZ_INOUT LocalContext *a, RZ_IN LocalContext *b, RzStrBuf *report_builder, const char *op_name)
Definition: il_validate.c:228
static bool local_var_meet_known_cb(RZ_NONNULL void *user, const void *k, const void *v)
Definition: il_validate.c:179
RZ_API bool rz_il_validate_pure(RZ_NULLABLE RzILOpPure *op, RZ_NONNULL RzILValidateGlobalContext *ctx, RZ_NULLABLE RZ_OUT RzILSortPure *sort_out, RZ_NULLABLE RZ_OUT RzILValidateReport *report_out)
Definition: il_validate.c:584
#define VALIDATOR_PURE(op)
Definition: il_validate.c:275
RZ_API void rz_il_validate_global_context_add_mem(RzILValidateGlobalContext *ctx, RzILMemIndex idx, ut32 key_len, ut32 val_len)
Definition: il_validate.c:69
RZ_API void rz_il_validate_global_context_add_var(RzILValidateGlobalContext *ctx, RZ_NONNULL const char *name, RzILSortPure sort)
Definition: il_validate.c:56
#define VALIDATOR_EFFECT_ARGS
Definition: il_validate.c:622
static void var_kv_free(HtPPKv *kv)
Definition: il_validate.c:19
static bool local_context_copy(LocalContext *dst, LocalContext *src)
Definition: il_validate.c:161
#define VALIDATOR_DESCEND_EFFECT(op, etype, ectx, cleanup)
Definition: il_validate.c:640
#define VALIDATOR_DESCEND_PURE(op, sort)
Definition: il_validate.c:634
static bool local_var_meet_avail_cb(RZ_NONNULL void *user, const void *k, const void *v)
Definition: il_validate.c:206
static ValidatePureFn validate_pure_table[RZ_IL_OP_PURE_MAX]
Definition: il_validate.c:532
#define VALIDATOR_PURE_NAME(op)
Definition: il_validate.c:274
static bool local_var_copy_avail_cb(RZ_NONNULL void *user, const void *k, const void *v)
Definition: il_validate.c:152
static bool validate_effect(VALIDATOR_EFFECT_ARGS)
Definition: il_validate.c:830
bool(* ValidatePureFn)(VALIDATOR_PURE_ARGS)
Definition: il_validate.c:272
static bool local_context_init(LocalContext *ctx, const RzILValidateGlobalContext *global_ctx)
Definition: il_validate.c:122
RZ_API RzILValidateGlobalContext * rz_il_validate_global_context_new_from_vm(RZ_NONNULL RzILVM *vm)
Definition: il_validate.c:77
#define VALIDATOR_EFFECT(op)
Definition: il_validate.c:633
static bool local_var_copy_known_cb(RZ_NONNULL void *user, const void *k, const void *v)
Definition: il_validate.c:141
RZ_API RzILValidateGlobalContext * rz_il_validate_global_context_new_empty(ut32 pc_len)
Definition: il_validate.c:32
RZ_API bool rz_il_validate_effect(RZ_NULLABLE RzILOpEffect *op, RZ_NONNULL RzILValidateGlobalContext *ctx, RZ_NULLABLE RZ_OUT HtPP **local_var_sorts_out, RZ_NULLABLE RZ_OUT RzILTypeEffect *type_out, RZ_NULLABLE RZ_OUT RzILValidateReport *report_out)
Definition: il_validate.c:846
struct local_pure_var_t LocalPureVar
static ValidateEffectFn validate_effect_table[RZ_IL_OP_EFFECT_MAX]
Definition: il_validate.c:816
static bool validate_pure(VALIDATOR_PURE_ARGS)
Definition: il_validate.c:569
#define VALIDATOR_DESCEND(op, sort)
Definition: il_validate.c:283
bool(* ValidateEffectFn)(VALIDATOR_EFFECT_ARGS)
Definition: il_validate.c:630
static void var_kv_unown_free(HtPPKv *kv)
Definition: il_validate.c:24
RZ_API RZ_OWN RzPVector * rz_il_vm_get_all_vars(RZ_NONNULL RzILVM *vm, RzILVarKind kind)
Definition: il_vm.c:256
RZ_API ut32 rz_il_vm_get_pc_len(RzILVM *vm)
Definition: il_vm.c:145
void * mem
Definition: libc.cpp:91
char * dst
Definition: lz4.h:724
ut32 RzILMemIndex
Definition: mem.h:14
int args
Definition: mipsasm.c:18
int x
Definition: mipsasm.c:20
int type
Definition: mipsasm.c:17
int idx
Definition: setup.py:197
void * load(const char *name, size_t *len)
Definition: pufftest.c:60
static void repeat(struct parse *, sopno, int, int)
Definition: regcomp.c:1155
#define rz_return_if_fail(expr)
Definition: rz_assert.h:100
#define rz_return_val_if_fail(expr, val)
Definition: rz_assert.h:108
RZ_API ut32 rz_bv_len(RZ_NONNULL const RzBitVector *bv)
Definition: bitvector.c:1140
@ RZ_IL_OP_SHIFTR
@ RZ_IL_OP_ULE
@ RZ_IL_OP_SUB
@ RZ_IL_OP_LOGNOT
@ RZ_IL_OP_MSB
@ RZ_IL_OP_ITE
@ RZ_IL_OP_PURE_MAX
@ RZ_IL_OP_SLE
@ RZ_IL_OP_LSB
@ RZ_IL_OP_OR
@ RZ_IL_OP_IS_ZERO
@ RZ_IL_OP_NEG
@ RZ_IL_OP_ADD
@ RZ_IL_OP_B0
@ RZ_IL_OP_LET
@ RZ_IL_OP_CAST
@ RZ_IL_OP_MOD
@ RZ_IL_OP_LOGOR
@ RZ_IL_OP_VAR
@ RZ_IL_OP_INV
@ RZ_IL_OP_APPEND
@ RZ_IL_OP_DIV
@ RZ_IL_OP_SHIFTL
@ RZ_IL_OP_EQ
@ RZ_IL_OP_MUL
@ RZ_IL_OP_BITV
@ RZ_IL_OP_B1
@ RZ_IL_OP_XOR
@ RZ_IL_OP_LOAD
@ RZ_IL_OP_AND
@ RZ_IL_OP_SMOD
@ RZ_IL_OP_LOGXOR
@ RZ_IL_OP_SDIV
@ RZ_IL_OP_LOADW
@ RZ_IL_OP_LOGAND
@ RZ_IL_OP_BLK
@ RZ_IL_OP_GOTO
@ RZ_IL_OP_SEQ
@ RZ_IL_OP_EFFECT_MAX
@ RZ_IL_OP_NOP
@ RZ_IL_OP_JMP
@ RZ_IL_OP_EMPTY
@ RZ_IL_OP_STORE
@ RZ_IL_OP_STOREW
@ RZ_IL_OP_REPEAT
@ RZ_IL_OP_BRANCH
@ RZ_IL_OP_SET
Validation/Type Checking of RzIL Code.
char * RzILValidateReport
RZ_API RZ_BORROW char * rz_str_trim_tail(RZ_NONNULL char *str)
Removes whitespace characters (space, tab, newline etc.) from the end of a string and replaces them w...
Definition: str_trim.c:125
static const char * rz_str_get_null(const char *str)
Definition: rz_str.h:190
RZ_API RZ_OWN char * rz_strbuf_drain_nofree(RzStrBuf *sb)
Definition: strbuf.c:349
RZ_API void rz_strbuf_fini(RzStrBuf *sb)
Definition: strbuf.c:365
RZ_API bool rz_strbuf_appendf(RzStrBuf *sb, const char *fmt,...) RZ_PRINTF_CHECK(2
RZ_API void rz_strbuf_init(RzStrBuf *sb)
Definition: strbuf.c:33
RZ_API bool rz_strbuf_is_empty(RzStrBuf *sb)
Definition: strbuf.c:24
#define RZ_IN
Definition: rz_types.h:50
#define RZ_NULLABLE
Definition: rz_types.h:65
#define RZ_NEW0(x)
Definition: rz_types.h:284
#define RZ_OUT
Definition: rz_types.h:51
#define RZ_NONNULL
Definition: rz_types.h:64
#define RZ_NEW(x)
Definition: rz_types.h:285
#define RZ_INOUT
Definition: rz_types.h:52
#define UT32_MAX
Definition: rz_types_base.h:99
static size_t rz_pvector_len(const RzPVector *vec)
Definition: rz_vector.h:231
RZ_API void rz_pvector_free(RzPVector *vec)
Definition: vector.c:336
static void * rz_pvector_at(const RzPVector *vec, size_t index)
Definition: rz_vector.h:236
#define rz_pvector_foreach(vec, it)
Definition: rz_vector.h:334
#define b(i)
Definition: sha256.c:42
#define a(i)
Definition: sha256.c:41
static bool rz_il_sort_pure_eq(RzILSortPure a, RzILSortPure b)
Definition: sort.h:37
RzILTypeEffect
Definition: sort.h:66
@ RZ_IL_TYPE_EFFECT_DATA
mutating mems, vars, etc.
Definition: sort.h:68
@ RZ_IL_TYPE_EFFECT_CTRL
jmp/goto
Definition: sort.h:69
@ RZ_IL_TYPE_EFFECT_NONE
nop
Definition: sort.h:67
static RzILSortPure rz_il_sort_pure_bool()
Definition: sort.h:47
@ RZ_IL_TYPE_PURE_BOOL
Definition: sort.h:24
@ RZ_IL_TYPE_PURE_BITVECTOR
Definition: sort.h:25
static RzILSortPure rz_il_sort_pure_bv(ut32 length)
Definition: sort.h:55
LocalContext * dst
Definition: il_validate.c:174
const char * op_name
Definition: il_validate.c:173
RzStrBuf * report_builder
Definition: il_validate.c:172
LocalContext * src
Definition: il_validate.c:175
HtPP * local_vars_available
vars that can be accessed right now
Definition: il_validate.c:119
HtPP * local_vars_known
Definition: il_validate.c:117
const RzILValidateGlobalContext * global_ctx
Definition: il_validate.c:109
structure for bitvector
Definition: rz_bitvector.h:19
RzILSortPure sort
Definition: il_validate.c:258
const char * name
Definition: il_validate.c:257
struct local_pure_var_t * next
Definition: il_validate.c:259
Definition: z80asm.h:102
A single memory as part of the RzIL VM.
Definition: mem.h:26
op structure for inv (!bool -> bool)
op structure for branch (bool -> 'a eff -> 'a eff -> 'a eff)
op structure for casting bitv
op structure for binary comparison ops ('a bitv -> 'a bitv -> bool)
op structure for goto (label -> ctrl eff)
op structure for ite (bool -> 'a pure -> 'a pure -> 'a pure)
op structure for jmp (_ bitv -> ctrl eff)
op structure for let_ : 'a var -> 'a pure -> 'b pure -> 'b pure
op structure for load (('a, 'b) mem -> 'a bitv -> 'b bitv)
Load an entire word of arbitrary bit size from a memory.
op structure for Seq ('a eff -> 'a eff -> 'a eff)
op structure for set ('a var -> 'a pure -> data eff)
op structure for lshift and rshift (bool -> 's bitv -> 'b bitv -> 's bitv)
op structure for store (('a, 'b) mem -> 'a bitv -> 'b bitv -> ('a, 'b) mem)
Store an entire word of arbitrary bit size into a memory.
op structure for var ('a var -> 'a pure)
An IL op performing a pure computation, 'a pure.
struct rz_il_sort_pure_t::@283::@284 bv
union rz_il_sort_pure_t::@283 props
RzILTypePure type
Definition: sort.h:29
Definition of a variable inside the vm.
Definition: variable.h:19
RzILSortPure sort
"type" of the variable
Definition: variable.h:21
char * name
Definition: variable.h:20
Low-level VM to execute raw IL code.
Definition: rz_il_vm.h:37
op structure for blk (label -> data eff -> ctrl eff -> unit eff)
op structure for repeat (bool -> data eff -> data eff)
bool valid
Definition: core.c:77
#define bool
Definition: sysdefs.h:146
Definition: dis.c:32
struct op_code code
Definition: dis.c:33
@ RZ_IL_VAR_KIND_GLOBAL
global var, usually bound to a physical representation like a register.
Definition: variable.h:47
@ RZ_IL_VAR_KIND_LOCAL
local var, defined and assigned by set ops, mutable and useable across effects.
Definition: variable.h:48
@ RZ_IL_VAR_KIND_LOCAL_PURE
local pure var, bound only by let expressions, scope is limited to the let's pure body,...
Definition: variable.h:49
ut64(WINAPI *w32_GetEnabledXStateFeatures)()