(self, insn)
Emulate instruction, create cross-references, plan to analyze
        subsequent instructions, modify flags etc. Upon entrance to this function
        all information about the instruction is in 'insn' structure.
        If zero is returned, the kernel will delete the instruction.

        adding xrefs is fairly straightforward, except for one hiccup:
        we'd like xrefs to flow from trailing END instructions,
         rather than getting orphaned in their own basic block.

        for example, consider the following:

            br $block0
            end

        if we place the code flow xref on the BR,
         then there is no flow to the END instruction,
         and the graph will look like:

            +------------+     +-----+
            |     ...    |     | end |
            | br $block0 |     +-----+
            +------------+
                   |
                  ...

        instead, we want the code flow xref to flow from the END,
         deferred from the BR, so the graph looks like this:

            +------------+
            |     ...    |
            | br $block0 |
            | end        |
            +------------+
                   |
                  ...

        to do this, at branching instruction,
         we detect if the following instruction is an END.
        if so, we flow through to the END,
         and queue the xrefs to be added when the END is processed.

        this assumes that the branching instructions are always analyzed before the END instructions.

        unfortunately, adding xrefs on subsequent instructions doesn't work (the node doesn't exist, or something).
        so, we have to used this "deferred" approach.
Emulate instruction, create cross-references, plan to analyze
        subsequent instructions, modify flags etc. Upon entrance to this function
        all information about the instruction is in 'insn' structure.
        If zero is returned, the kernel will delete the instruction.
Emulate instruction create cross - references plan to analyze subsequent instructions modify flags etc . Upon entrance to this function all information about the instruction is in insn structure . If zero is returned the kernel will delete the instruction .
def notify_emu(self, insn):
        '''
        Emulate instruction, create cross-references, plan to analyze
        subsequent instructions, modify flags etc. Upon entrance to this function
        all information about the instruction is in 'insn' structure.
        If zero is returned, the kernel will delete the instruction.

        adding xrefs is fairly straightforward, except for one hiccup:
        we'd like xrefs to flow from trailing END instructions,
         rather than getting orphaned in their own basic block.

        for example, consider the following:

            br $block0
            end

        if we place the code flow xref on the BR,
         then there is no flow to the END instruction,
         and the graph will look like:

            +------------+     +-----+
            |     ...    |     | end |
            | br $block0 |     +-----+
            +------------+
                   |
                  ...

        instead, we want the code flow xref to flow from the END,
         deferred from the BR, so the graph looks like this:

            +------------+
            |     ...    |
            | br $block0 |
            | end        |
            +------------+
                   |
                  ...

        to do this, at branching instruction,
         we detect if the following instruction is an END.
        if so, we flow through to the END,
         and queue the xrefs to be added when the END is processed.

        this assumes that the branching instructions are always analyzed before the END instructions.

        unfortunately, adding xrefs on subsequent instructions doesn't work (the node doesn't exist, or something).
        so, we have to used this "deferred" approach.
        '''

        # note: `next` may be None if invalid.
        next = idautils.DecodeInstruction(insn.ea + insn.size)

        # add drefs to globals
        for op in insn.ops:
            if not (op.type == idaapi.o_imm and op.specval == WASM_GLOBAL):
                continue

            if op.value not in self.globals:
                logger.debug('missing global: %d', op.value)
                continue

            global_va = self.globals[op.value]['offset']
            if insn.itype == self.itype_SET_GLOBAL:
                idc.add_dref(insn.ea, global_va, idc.dr_W)
            elif insn.itype == self.itype_GET_GLOBAL:
                idc.add_dref(insn.ea, global_va, idc.dr_R)
            else:
                raise RuntimeError('unexpected instruction referencing global: ' + str(insn))

        # TODO: add drefs to memory, but need example of this first.

        # handle cases like:
        #
        #     block
        #     ...
        #     br $foo
        #     end
        #
        # we want the cref to flow from the instruction `end`, not `br $foo`.
        if (insn.itype in {self.itype_BR,
                           self.itype_BR_IF,
                           self.itype_BR_TABLE}
              and next is not None                # NOQA: E127 continuation line over-indented for visual indent
              and next.itype == self.itype_END):  # NOQA: E127

            if insn.itype == self.itype_BR:
                return self.notify_emu_BR_END(insn, next)

            elif insn.itype == self.itype_BR_IF:
                return self.notify_emu_BR_IF_END(insn, next)

            elif insn.itype in (self.itype_BR_TABLE, ):
                raise NotImplementedError('br table')

        # handle cases like:
        #
        #     ...
        #     return
        #     end
        #
        # we want return to flow into the return, which should then not flow.
        elif (insn.itype == self.itype_RETURN
              and next is not None
              and next.itype == self.itype_END):
            return self.notify_emu_RETURN_END(insn, next)

        # handle other RETURN and UNREACHABLE instructions.
        # tbh, not sure how we'd encounter another RETURN, but we'll be safe.
        elif insn.get_canon_feature() & wasm.opcodes.INSN_NO_FLOW:
            return 1

        # handle an unconditional branch not at the end of a black.
        elif insn.itype == self.itype_BR:
            return self.notify_emu_BR(insn)

        elif insn.itype == self.itype_BR_TABLE:
            # haven't seen one of these yet, so don't know to handle exactly.
            raise NotImplementedError('br table')

        # handle a conditional branch not at the end of a block.
        elif insn.itype == self.itype_BR_IF:
            return self.notify_emu_BR_IF(insn)

        # add flows deferred from a prior branch, eg.
        #
        #     br $foo
        #     end
        #
        # flows deferred from the BR to the END insn.
        elif insn.itype == self.itype_END:
            return self.notify_emu_END(insn)

        # default behavior: fallthrough
        else:
            idaapi.add_cref(insn.ea, insn.ea + insn.size, idaapi.fl_F)
def notify_emu ( self , insn ) : # note: `next` may be None if invalid. next = idautils . DecodeInstruction ( insn . ea + insn . size ) # add drefs to globals for op in insn . ops : if not ( op . type == idaapi . o_imm and op . specval == WASM_GLOBAL ) : continue if op . value not in self . globals : logger . debug ( 'missing global: %d' , op . value ) continue global_va = self . globals [ op . value ] [ 'offset' ] if insn . itype == self . itype_SET_GLOBAL : idc . add_dref ( insn . ea , global_va , idc . dr_W ) elif insn . itype == self . itype_GET_GLOBAL : idc . add_dref ( insn . ea , global_va , idc . dr_R ) else : raise RuntimeError ( 'unexpected instruction referencing global: ' + str ( insn ) ) # TODO: add drefs to memory, but need example of this first. # handle cases like: # #     block #     ... #     br $foo #     end # # we want the cref to flow from the instruction `end`, not `br $foo`. if ( insn . itype in { self . itype_BR , self . itype_BR_IF , self . itype_BR_TABLE } and next is not None # NOQA: E127 continuation line over-indented for visual indent and next . itype == self . itype_END ) : # NOQA: E127 if insn . itype == self . itype_BR : return self . notify_emu_BR_END ( insn , next ) elif insn . itype == self . itype_BR_IF : return self . notify_emu_BR_IF_END ( insn , next ) elif insn . itype in ( self . itype_BR_TABLE , ) : raise NotImplementedError ( 'br table' ) # handle cases like: # #     ... #     return #     end # # we want return to flow into the return, which should then not flow. elif ( insn . itype == self . itype_RETURN and next is not None and next . itype == self . itype_END ) : return self . notify_emu_RETURN_END ( insn , next ) # handle other RETURN and UNREACHABLE instructions. # tbh, not sure how we'd encounter another RETURN, but we'll be safe. elif insn . get_canon_feature ( ) & wasm . opcodes . INSN_NO_FLOW : return 1 # handle an unconditional branch not at the end of a black. elif insn . itype == self . itype_BR : return self . notify_emu_BR ( insn ) elif insn . itype == self . itype_BR_TABLE : # haven't seen one of these yet, so don't know to handle exactly. raise NotImplementedError ( 'br table' ) # handle a conditional branch not at the end of a block. elif insn . itype == self . itype_BR_IF : return self . notify_emu_BR_IF ( insn ) # add flows deferred from a prior branch, eg. # #     br $foo #     end # # flows deferred from the BR to the END insn. elif insn . itype == self . itype_END : return self . notify_emu_END ( insn ) # default behavior: fallthrough else : idaapi . add_cref ( insn . ea , insn . ea + insn . size , idaapi . fl_F )