'At the beginning of the basic block, we check if the first instruction stores the LR register onto the stack.
If it does, we calculate the offset of that store, and record the offset in function.info.
For instance, here is the disassembly of a THUMB mode function:
000007E4  STR.W           LR, [SP,#var_4]!
000007E8  MOV             R2, R1
000007EA  SUB             SP, SP, #0xC
000007EC  MOVS            R1, #0
00000800  ADD             SP, SP, #0xC
00000802  LDR.W           PC, [SP+4+var_4],#4
The very last basic block has a jumpkind of Ijk_Boring, which is because VEX cannot do such complicated analysis
to determine the real jumpkind.
As we can see, instruction 7e4h stores LR at [sp-4], and at the end of this function, instruction 802 loads LR
from [sp], then increments sp by 4. We execute the first instruction, and track the following things:
- if the value from register LR is stored onto the stack.
- the difference between the offset of the LR store on stack, and the SP after the store.
If at the end of the function, the LR is read out from the stack at the exact same stack offset, we will change
the jumpkind of the final IRSB to Ijk_Ret.
This method can be enabled by setting "ret_jumpkind_heuristics", which is an architecture-specific option on
ARM, to True.
:param int addr: Address of the basic block.
:param pyvex.IRSB irsb: The basic block object.
:param Function function: The function instance.
:return: None'

def _arm_track_lr_on_stack(self, addr, irsb, function):

    if ('lr_saved_on_stack' in function.info):
        return
    lr_offset = self.project.arch.registers['lr'][0]
    sp_offset = self.project.arch.sp_offset
    initial_sp = 2147418112
    initial_lr = 11259375
    tmps = {}
    for stmt in irsb.statements:
        if isinstance(stmt, pyvex.IRStmt.IMark):
            if ((stmt.addr + stmt.delta) != addr):
                break
        elif isinstance(stmt, pyvex.IRStmt.WrTmp):
            data = stmt.data
            if isinstance(data, pyvex.IRExpr.Get):
                if (data.offset == sp_offset):
                    tmps[stmt.tmp] = initial_sp
                elif (data.offset == lr_offset):
                    tmps[stmt.tmp] = initial_lr
            elif isinstance(data, pyvex.IRExpr.Binop):
                if (data.op == 'Iop_Sub32'):
                    (arg0, arg1) = data.args
                    if (isinstance(arg0, pyvex.IRExpr.RdTmp) and isinstance(arg1, pyvex.IRExpr.Const)):
                        if (arg0.tmp in tmps):
                            tmps[stmt.tmp] = (tmps[arg0.tmp] - arg1.con.value)
        elif isinstance(stmt, (pyvex.IRStmt.Store, pyvex.IRStmt.StoreG)):
            data = stmt.data
            storing_lr = False
            if isinstance(data, pyvex.IRExpr.RdTmp):
                if (data.tmp in tmps):
                    val = tmps[data.tmp]
                    if (val == initial_lr):
                        storing_lr = True
            if storing_lr:
                if isinstance(stmt.addr, pyvex.IRExpr.RdTmp):
                    if (stmt.addr.tmp in tmps):
                        storing_addr = tmps[stmt.addr.tmp]
                        function.info['lr_saved_on_stack'] = True
                        function.info['lr_on_stack_offset'] = (storing_addr - initial_sp)
                        break
    if ('lr_saved_on_stack' not in function.info):
        function.info['lr_saved_on_stack'] = False
