/*
         * Usually it expects a pointer type for a memory access.
         * Convert to a real type it points to.  But global variables
-        * are accessed directly without a pointer.
+        * and local variables are accessed directly without a pointer.
         */
        if (is_pointer) {
                if ((dwarf_tag(type_die) != DW_TAG_pointer_type &&
        int reg, offset;
        int ret = -1;
        int i, nr_scopes;
+       int fbreg = -1;
+       bool is_fbreg = false;
+       int fb_offset = 0;
 
        /* Get a compile_unit for this address */
        if (!find_cu_die(di, pc, &cu_die)) {
        /* Get a list of nested scopes - i.e. (inlined) functions and blocks. */
        nr_scopes = die_get_scopes(&cu_die, pc, &scopes);
 
+       if (reg != DWARF_REG_PC && dwarf_hasattr(&scopes[0], DW_AT_frame_base)) {
+               Dwarf_Attribute attr;
+               Dwarf_Block block;
+
+               /* Check if the 'reg' is assigned as frame base register */
+               if (dwarf_attr(&scopes[0], DW_AT_frame_base, &attr) != NULL &&
+                   dwarf_formblock(&attr, &block) == 0 && block.length == 1) {
+                       switch (*block.data) {
+                       case DW_OP_reg0 ... DW_OP_reg31:
+                               fbreg = *block.data - DW_OP_reg0;
+                               break;
+                       case DW_OP_call_frame_cfa:
+                               if (die_get_cfa(di->dbg, pc, &fbreg,
+                                               &fb_offset) < 0)
+                                       fbreg = -1;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       }
+
 retry:
+       is_fbreg = (reg == fbreg);
+       if (is_fbreg)
+               offset = loc->offset - fb_offset;
+
        /* Search from the inner-most scope to the outer */
        for (i = nr_scopes - 1; i >= 0; i--) {
                if (reg == DWARF_REG_PC) {
                } else {
                        /* Look up variables/parameters in this scope */
                        if (!die_find_variable_by_reg(&scopes[i], pc, reg,
-                                                     &var_die))
+                                                     &offset, is_fbreg, &var_die))
                                continue;
                }
 
                /* Found a variable, see if it's correct */
                ret = check_variable(&var_die, type_die, offset,
-                                    reg != DWARF_REG_PC);
+                                    reg != DWARF_REG_PC && !is_fbreg);
                loc->offset = offset;
                goto out;
        }
 
        unsigned reg;
        /* Access offset, set for global data */
        int offset;
+       /* True if the current register is the frame base */
+       bool is_fbreg;
 };
 
 /* Max number of registers DW_OP_regN supports */
 #define DWARF_OP_DIRECT_REGS  32
 
+static bool match_var_offset(Dwarf_Die *die_mem, struct find_var_data *data,
+                            u64 addr_offset, u64 addr_type)
+{
+       Dwarf_Die type_die;
+       Dwarf_Word size;
+
+       if (addr_offset == addr_type) {
+               /* Update offset relative to the start of the variable */
+               data->offset = 0;
+               return true;
+       }
+
+       if (die_get_real_type(die_mem, &type_die) == NULL)
+               return false;
+
+       if (dwarf_aggregate_size(&type_die, &size) < 0)
+               return false;
+
+       if (addr_offset >= addr_type + size)
+               return false;
+
+       /* Update offset relative to the start of the variable */
+       data->offset = addr_offset - addr_type;
+       return true;
+}
+
 /* Only checks direct child DIEs in the given scope. */
 static int __die_find_var_reg_cb(Dwarf_Die *die_mem, void *arg)
 {
                if (start > data->pc)
                        break;
 
+               /* Local variables accessed using frame base register */
+               if (data->is_fbreg && ops->atom == DW_OP_fbreg &&
+                   data->offset >= (int)ops->number &&
+                   match_var_offset(die_mem, data, data->offset, ops->number))
+                       return DIE_FIND_CB_END;
+
                /* Only match with a simple case */
                if (data->reg < DWARF_OP_DIRECT_REGS) {
                        if (ops->atom == (DW_OP_reg0 + data->reg) && nops == 1)
                                return DIE_FIND_CB_END;
+
+                       /* Local variables accessed by a register + offset */
+                       if (ops->atom == (DW_OP_breg0 + data->reg) &&
+                           match_var_offset(die_mem, data, data->offset, ops->number))
+                               return DIE_FIND_CB_END;
                } else {
                        if (ops->atom == DW_OP_regx && ops->number == data->reg &&
                            nops == 1)
                                return DIE_FIND_CB_END;
+
+                       /* Local variables accessed by a register + offset */
+                       if (ops->atom == DW_OP_bregx && data->reg == ops->number &&
+                           match_var_offset(die_mem, data, data->offset, ops->number2))
+                               return DIE_FIND_CB_END;
                }
        }
        return DIE_FIND_CB_SIBLING;
  * @sc_die: a scope DIE
  * @pc: the program address to find
  * @reg: the register number to find
+ * @poffset: pointer to offset, will be updated for fbreg case
+ * @is_fbreg: boolean value if the current register is the frame base
  * @die_mem: a buffer to save the resulting DIE
  *
- * Find the variable DIE accessed by the given register.
+ * Find the variable DIE accessed by the given register.  It'll update the @offset
+ * when the variable is in the stack.
  */
 Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg,
+                                   int *poffset, bool is_fbreg,
                                    Dwarf_Die *die_mem)
 {
        struct find_var_data data = {
                .pc = pc,
                .reg = reg,
+               .offset = *poffset,
+               .is_fbreg = is_fbreg,
        };
-       return die_find_child(sc_die, __die_find_var_reg_cb, &data, die_mem);
+       Dwarf_Die *result;
+
+       result = die_find_child(sc_die, __die_find_var_reg_cb, &data, die_mem);
+       if (result)
+               *poffset = data.offset;
+       return result;
 }
 
 /* Only checks direct child DIEs in the given scope */
        ptrdiff_t off = 0;
        Dwarf_Attribute attr;
        Dwarf_Addr base, start, end;
-       Dwarf_Word size;
-       Dwarf_Die type_die;
        Dwarf_Op *ops;
        size_t nops;
 
                if (data->addr < ops->number)
                        continue;
 
-               if (data->addr == ops->number) {
-                       /* Update offset relative to the start of the variable */
-                       data->offset = 0;
+               if (match_var_offset(die_mem, data, data->addr, ops->number))
                        return DIE_FIND_CB_END;
-               }
-
-               if (die_get_real_type(die_mem, &type_die) == NULL)
-                       continue;
-
-               if (dwarf_aggregate_size(&type_die, &size) < 0)
-                       continue;
-
-               if (data->addr >= ops->number + size)
-                       continue;
-
-               /* Update offset relative to the start of the variable */
-               data->offset = data->addr - ops->number;
-               return DIE_FIND_CB_END;
        }
        return DIE_FIND_CB_SIBLING;
 }