+static int resolve_origin(int i, const struct parsed_opr *opr,
+ int magic, int *op_i, int *is_caller);
+
+static void eliminate_seh(int opcnt)
+{
+ int i, j, k, ret;
+
+ for (i = 0; i < opcnt; i++) {
+ if (ops[i].op != OP_MOV)
+ continue;
+ if (ops[i].operand[0].segment != SEG_FS)
+ continue;
+ if (!IS(opr_name(&ops[i], 0), "0"))
+ continue;
+
+ ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+ if (ops[i].operand[1].reg == xSP) {
+ for (j = i - 1; j >= 0; j--) {
+ if (ops[j].op != OP_PUSH)
+ continue;
+ ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+ g_seh_size += 4;
+ if (ops[j].operand[0].val == ~0)
+ break;
+ if (ops[j].operand[0].type == OPT_REG) {
+ k = -1;
+ ret = resolve_origin(j, &ops[j].operand[0],
+ j + opcnt * 22, &k, NULL);
+ if (ret == 1)
+ ops[k].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+ }
+ }
+ if (j < 0)
+ ferr(ops, "missing seh terminator\n");
+ }
+ else {
+ k = -1;
+ ret = resolve_origin(i, &ops[i].operand[1],
+ i + opcnt * 23, &k, NULL);
+ if (ret == 1)
+ ops[k].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+ }
+ }
+
+ // assume all sf writes above g_seh_size to be seh related
+ // (probably unsafe but oh well)
+ for (i = 0; i < opcnt; i++) {
+ const struct parsed_opr *opr;
+ char ofs_reg[16];
+ int offset = 0;
+
+ if (ops[i].op != OP_MOV)
+ continue;
+ opr = &ops[i].operand[0];
+ if (opr->type != OPT_REGMEM)
+ continue;
+ if (!is_stack_access(&ops[i], opr))
+ continue;
+
+ parse_stack_access(&ops[i], opr->name, ofs_reg, &offset,
+ NULL, NULL, 0);
+ if (offset < 0 && offset >= -g_seh_size)
+ ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+ }
+}
+