def get_constant(self, name):
        """Gets a symbolic constant.

        If 'name' is not the exact name of a known constant, a number of
        variations will be tried in an attempt to find a match.

        This method should only be used in interactive mode; it's risky to
        call it as part of an analysis. Use get_constant_strict instead.

        Return value is a ConstSym object (which may be None).
        An AmbiguousSymbolError exception will be raised if the supplied
        name can't be matched to a single constant.

        Args:
            name
        """

        # todo add search for downloadable capabilities
        matches = self._search_by_name("constants", "search_matches", name)

        if matches == []:
            result = None
        else:
            # We may have found multiple matches for the symbol name.
            # If all the matches are aliases for each other, we can return
            # that value. if they're different, admit our mistake.
            val = self.get_constant_strict(matches[0]["name"],
                                           matches[0]["elf_id"])
            # The first is always the same with the first.
            success = True
            # Skip the first which is used to check against.
            for match in matches[1:]:
                try:
                    constant = self.get_constant_strict(
                        match["name"], match["elf_id"])
                    if (val != constant):
                        success = False
                        break
                # Todo remevoe this if B-242063 is corrected.
                except BaseException:
                    success = False
                    break

            if success:
                result = val
            else:
                apology = ("Multiple potential matches found for "
                           "constant '%s': " % name)
                raise AmbiguousSymbolError(apology, matches)

        return result
    def get_var(self, identifier, elf_id=None):
        """Searches variables for the supplied identifier (name or address).

        If a supplied name is not the exact name of a known variable, a
        number of variations will be tried in an attempt to find a match.

        If a supplied address is not the start of a known variable, the
        containing variable (if any) will be returned.

        For example, get_var(0x600) and get_var(0x604) will both
        return the variable $stack.buffer, starting at address 0x600.

        This method should only be used in interactive mode; it's risky
        to call it as part of an analysis. Use get_var_strict instead.

        Args:
            identifier: Name or address of a variable. If the type is
                integer then it's an address. If the type is string it's
                a name.
            elf_id (int, optional): The bundle elf id if the variable is
                in a downloadable capability.

        Returns:
            value is a Variable object (which may be None).

        Raises:
            AmbiguousSymbolError: When the supplied name can't be matched
                to a single variable.
        """

        if isinstance(identifier, numbers.Integral):
            return self._search_var_by_address(identifier)

        # First search for strict matches
        matches = self._search_by_name("var_by_name", "strict_search_matches",
                                       identifier, elf_id)

        if matches == []:
            # No strict matches: search if there is any similarly named
            # variables
            matches = self._search_by_name("var_by_name", "search_matches",
                                           identifier, elf_id)

        if matches == []:
            result = None
        else:
            # We found one or more matches for the symbol name.  If all
            # the matches are aliases for each other, just return that
            # value.  if they're different, admit our mistake.
            val = self.get_var_strict(matches[0]["name"], matches[0]["elf_id"])
            # The first is always the same with the first.
            success = True
            # Skip the first which is used to check against.
            for match in matches[1:]:
                try:
                    variable = self.get_var_strict(match["name"],
                                                   match["elf_id"])
                    if (val != variable):
                        success = False
                        break
                # Todo remevoe this if B-242063 is corrected.
                except BaseException:
                    success = False
                    break

            if success:
                result = val
            else:
                apology = ("Multiple potential matches found for "
                           "variable '%s': " % identifier)
                raise AmbiguousSymbolError(apology, matches)

        return result
    def get_reg(self, identifier):
        """Get register.

        Like Analysis.get_reg_strict(), except it's not strict (!)
        'identifier' can be a register name, or address. If it's a name,
        we attempt to find the closest match in our list of registers.

        Args:
            identifier

        Returns:
            a DataSym instance.

        Raises:
            AmbiguousSymbolError: If more than one match is found.
        """

        reg = None  # Will be a ConstSym.

        # If the user supplied an address, and it smells like a register,
        # attempt to look it up.
        if isinstance(identifier, numbers.Integral):
            if Arch.get_dm_region(identifier) == "MMR":
                # Look for constants that have a value of the supplied
                # address.  Inherently risky, since any random constant
                # could have a value that looks like a register address.
                # Since we only do this to set the name, it should be ok.
                possible_regs = [
                    item[0] for item in list(self.debuginfo.constants.items())
                    if item[1] == identifier
                ]
                if possible_regs:
                    reg_name = " or ".join(possible_regs)
                    reg = ct.ConstSym(reg_name, identifier)
        else:
            # Look up the supplied name in our list of constants. If the
            # name is found, reg.value is actually going to be the address
            # of the register.

            # get_constant might throw an AmbiguousSymbolError exception
            # here; in this case we want to catch it, and weed out any
            # matches that aren't register names.
            try:
                if 'regfile' in identifier:
                    return self.chipdata.get_reg_strict(identifier)

                return self.chipdata.get_reg_strict('regfile_' + identifier)
            except KeyError:
                pass
            except BaseException:
                # This shoud be on UnknownRegister but is too hard to import
                pass

            try:
                reg = self.debuginfo.get_constant(identifier)
            except AmbiguousSymbolError as ambs:
                # We helpfully store the ambiguous matches in the exception
                # args
                ambiguous_matches = ambs.args[1]

                actual_ambiguous_matches = []
                for match in ambiguous_matches:
                    amconst = self.debuginfo.get_constant_strict(
                        match["name"], match["elf_id"])
                    if Arch.get_dm_region(amconst.value, False) == "MMR":
                        actual_ambiguous_matches.append(match)

                if not actual_ambiguous_matches:
                    # We actually ended up finding no real registers
                    reg = None
                else:
                    # If all the matches are aliases for each other, we can
                    # return that value. if they're different,
                    # admit our mistake.
                    val = self.debuginfo.get_constant_strict(
                        actual_ambiguous_matches[0]["name"],
                        actual_ambiguous_matches[0]["elf_id"])
                    # The first is always the same with the first.
                    success = True
                    # Skip the first which is used to check against.
                    for match in actual_ambiguous_matches[1:]:
                        try:
                            variable = self.debuginfo.get_constant_strict(
                                match["name"], match["elf_id"])
                            if val != variable:
                                success = False
                                break
                        # Todo remevoe this if B-242063 is corrected.
                        except BaseException:
                            success = False
                            break

                    if success:
                        # We actually got only one register match - work with
                        # it.
                        reg = self.debuginfo.get_constant_strict(
                            actual_ambiguous_matches[0]["name"])
                    else:
                        apology = "Multiple potential matches found " + \
                            "for register name '" + identifier + "': "
                        raise AmbiguousSymbolError(apology,
                                                   actual_ambiguous_matches)
            try:
                if reg and (Arch.get_dm_region(reg.value) != "MMR"):
                    # Reg.value wasn't the address of a memory-mapped
                    # register; it was probably a random symbolic
                    # constant. Oh well.
                    return None
            except Arch.NotDmRegion:
                if reg.value == 0xfffffff:
                    # For Crescendo, it has been noticed that the register are
                    # being treated as constants with the value 0xfffffff.
                    # Furthermore, must strip the C and asm specific symbols
                    # for get_reg_strict().
                    try:
                        if '$_' in reg.name:
                            reg_name = reg.name[2:]
                            return self.chipdata.get_reg_strict(reg_name)
                        elif '$' in reg.name:
                            reg_name = reg.name[1:]
                            return self.chipdata.get_reg_strict(reg_name)
                    except BaseException:
                        return self.chipdata.get_reg_strict(reg.name)

        if reg is None:
            return None

        # If we get here, we found something.
        # There's a small chance we've got an unrelated constant, if its
        # value looks sufficiently like the address of a memory-mapped
        # register (e.g. $codec.stream_decode.FAST_AVERAGE_SHIFT_CONST).
        # Can't do much about that.

        # Look up the register contents.
        try:
            regcontents = self.chipdata.get_data(reg.value)
            fullreg = ct.DataSym(reg.name, reg.value, regcontents)
        except KeyError:
            # Reg.value wasn't the address of a register, for some reason.
            fullreg = None

        return fullreg
    def get_var(self, identifier, elf_id=None, datalen=None):
        """Get a variable.

        Like Analysis.get_var_strict(), except it's not strict (!)
        'identifier' can be a variable name, or address. If it's a name,
        we attempt to find the closest match in our list of variables.

        In this case the user can also provide a data length; if it is set,
        we return a slice of data, 'datalen' addressable units long starting at
        the address specified by 'identifier'.

        Args:
            identifier: Could be name or address.
            elf_id (int, optional): The bundle elf id if the variable is
                in a downloadable capability.
            datalen: If the identifier is an address the data length is
                specified by this input.

        Returns:
            A Variable.

        Raises:
            AmbiguousSymbolError: If more than one match is found.
        """
        # For Crescendo, data can only be fetched as words. Since it is
        # octet-addressed, the addresses must be divisible with the number of
        # addresses per word (32 bit words - 4 octets, therefore addresses must
        # be divisible with 4).
        if isinstance(identifier, numbers.Integral):
            identifier = cu.get_correct_addr(identifier, Arch.addr_per_word)

        # Same as above. The lengths are measured in addressable units.
        if datalen is not None:
            datalen = cu.convert_byte_len_word(datalen, Arch.addr_per_word)
        # The following is necessary since we can't rely on variable
        # sizes. If a (say) register address was passed in here we will likely
        # match a variable entry for $flash.data24.__Limit.
        if isinstance(identifier, numbers.Integral) and \
                Arch.get_dm_region(identifier) == "MMR":
            return None

        # First, look up the variable in the debug information.
        # Even if the user supplied an address rather than a name, it's nice
        # if we can tell them which variable it might be part of.

        # Might throw an AmbiguousSymbolError exception here; can't get that
        # with an address but can with a variable name.
        var = None
        try:
            var = self.debuginfo.get_var(identifier, elf_id)
        except AmbiguousSymbolError as amb:
            # Filter out matches of struct/array members, where their parent is
            # also in the list of matches.
            matches = amb.args[1]
            quarantine_list = []
            for match in matches:
                try:
                    mvar = self.debuginfo.get_var_strict(
                        match["name"], match["elf_id"])
                    if mvar.parent is not None and mvar.parent.name in matches:
                        # This is a struct/array member
                        quarantine_list.append(match)
                    else:
                        possible_parent = mvar
                except ValueError:
                    # Out of memory can be seen for asm memory reagions. Ignore
                    # them.
                    quarantine_list.append(match)

            # If the number of things in the quarantine list is EXACTLY
            # ONE MORE than the number of things in the matches list, then
            # we probably have found a single variable and all its
            # members.
            if len(matches) == len(quarantine_list) + 1:
                var = possible_parent
            else:
                # Give up
                raise AmbiguousSymbolError(amb.args[0], amb.args[1])

        if var is None:
            return None

        # Don't necessarily want to modify the actual variable entry below*,
        # so maybe create a copy here.
        # * Why? Well var is just a reference to the original variable in the
        # debuginfo class - we ought not to change it frivolously, since it
        # could break some other analysis.
        # In this case, we don't want to permanently munge the name
        # just because we're doing a slice this time.
        ret_var = var
        if datalen:
            if isinstance(identifier, numbers.Integral):
                if var.address == identifier and var.size <= datalen:
                    ret_var = copy.deepcopy(var)
                    ret_var.name = "User-defined slice, part of: " + \
                        var.name + " ???"
                    # We want to get a slice of data, not just the variable
                    # entry.
                    ret_var.size = datalen
                    # If the identifier is a variable name, don't include any
                    # members we might have already inspected.
                    ret_var.members = None
                else:
                    ret_var = ct.Variable("???", identifier, datalen)
        else:
            # Mitigation: we can't rely on 'var' actually containing the
            # supplied address, due to the lack of size information (see
            # KerDebugInfo.py). So work around it here.
            if (isinstance(identifier, numbers.Integral) and
                    identifier >= var.address + Arch.addr_per_word * var.size):
                # Just return the value at the given address.
                ret_var = ct.Variable(var.name + " ???", identifier, 1)

        # Now get the data value(s) from chipdata. Look in DM first, only
        # try const if we run out of options.
        try:
            ret_var.value = self.chipdata.get_data(ret_var.address,
                                                   ret_var.size)
        except InvalidDmLengthError as oor:
            # Address was valid, but size was not.
            # oor.args[1] contains the last valid address in the supplied
            # range.
            valid_size = (oor.max_length - ret_var.address) + 1
            ret_var.value = self.chipdata.get_data(ret_var.address, valid_size)
            ret_var.size = valid_size
        except InvalidDmAddressError:
            # The address wasn't valid. Could be that this variable is
            # actually in dm const.
            try:
                ret_var.value = self.debuginfo.get_dm_const(
                    ret_var.address, ret_var.size)
            except InvalidDmConstLengthError as oor:
                # Address was valid, but size was not.
                valid_size = oor.max_length - ret_var.address
                ret_var.value = self.debuginfo.get_dm_const(
                    ret_var.address, valid_size)
            except InvalidDmConstAddressError:
                # Ok we really are stuck. Return variable with a value of None.
                debug_info = self.debuginfo.get_kymera_debuginfo()
                ret_var.value = debug_info.debug_strings[ret_var.address]
                return ret_var

        # Need a way to work out whether we've already inspected this
        # variable, so we can avoid doing it more than once.
        # An inspection *should* result in a non-empty type_name string.
        # Also, don't inspect the slices. It would be bad.
        ret_var.members = []
        var_elf_id = self.debuginfo.table.get_elf_id_from_address(
            ret_var.address)
        if not var_elf_id:
            var_elf_id = self.debuginfo.get_kymera_debuginfo().elf_id

        self.debuginfo.inspect_var(ret_var, var_elf_id)
        return ret_var
    def get(self, identifier, elf_id=None, datalen=None):
        """
        Get a variable, register or range of addresses.

        'identifier' can be an address, or the name of a register or variable
        (note constant names are not handled). If it's a name, we attempt to
        find the closest match in our lists of registers and variables.
        However, please note that there is a 'search' function to conduct
        searches, i.e. search('*[IDENTIFIER]*'), and it's advised to use that
        instead of 'get'.

        If the identifier is ambiguous, an AmbiguousSymbolError exception may
        be thrown.

        Like `get_var`, it's also possible to pass in a data length to request
        a slice of data (this only makes sense for things in DM).

        Obviously there are things that could go wrong here, especially if
        the user passes in a random number and we try to guess what it points
        to. That's why we only provide this function in Interactive mode!

        Args:
            identifier: Could be name or address
            elf_id: The bundle elf id if the variable is in a downloadable
                capability.
            datalen: If the identifier is an address the data length is
                specified by this input.

        Returns:
            a DataSym (which may be a Variable) or a SourceInfo (if a
            code address was supplied).
        """
        pm_addr = None
        reg = None
        var = None
        apology = ""
        hit_flag = False
        # For Crescendo, data can only be fetched as words. Since it is
        # octet-addressed, the addresses must be divisible by the number of
        # addresses per word (32 bit words - 4 octets, therefore addresses must
        # be divisible by 4).
        if isinstance(identifier, numbers.Integral):
            identifier = cu.get_correct_addr(identifier, Arch.addr_per_word)

        # Same as above. The lengths are measured in addressable units.
        if datalen is not None:
            datalen = cu.convert_byte_len_word(datalen, Arch.addr_per_word)

        # Look to see whether it's an address in PM.
        # Since the PM range can be huge (and may overlap with DM), perform a
        # sanity check to avoid false positives.
        if (isinstance(identifier, numbers.Integral)
                and Arch.get_pm_region(identifier, False) in ("PMROM", "PMRAM")
                and datalen is None):
            # Is it the address of a valid instruction?
            try:
                self.debuginfo.get_instruction(identifier)
            except KeyError:
                pass
            else:
                pm_addr = self.debuginfo.get_source_info(identifier)

        # Look to see whether it's the name of a module in PM. This needs to
        # include all the fluff at the start, (i.e. '$M.foo.bar' rather than
        # just 'foo.bar' or 'bar') so as to avoid clashes with names in DM
        # space (which are more likely to be what's wanted).
        if isinstance(identifier, numbers.Integral):
            try:
                pm_addr = self.debuginfo.get_source_info(identifier)
                hit_flag = True
            except KeyError:
                pass
            except AmbiguousSymbolError as amb_pm:
                apology += str(amb_pm) + "\n"

        # Look to see whether it's a register.
        try:
            reg = self.get_reg(identifier)
            hit_flag = True
        except AmbiguousSymbolError as amb_reg:
            logger.warning(
                "Multiple registers found for {}. Use `search('*{}*')` to see "
                "the results clearer.\n".format(str(identifier),
                                                str(identifier)))
            apology += str(amb_reg) + "\n"

        # Also look to see whether it's a variable name/address.
        try:
            var = self.get_var(identifier, elf_id, datalen)
            hit_flag = True
        except AmbiguousSymbolError as amb_var:
            logger.warning(
                "Multiple variables found for {}. Use `search('*{}*')` to see "
                "the results clearer.\n".format(str(identifier),
                                                str(identifier)))
            apology += str(amb_var) + "\n"

        # We got at least one hit
        if hit_flag:
            if not pm_addr and not reg and not var:
                # We didn't find anything at all
                logger.warning(
                    "Nothing was found! You can also use `search('*%s*')`.",
                    identifier)
                return None
            elif pm_addr and not reg and not var:
                return pm_addr
            elif reg and not var and not pm_addr:
                return reg
            elif var and not reg and not pm_addr:
                return var
            else:
                # We got unique, valid hits for one or more of the above.
                apology = (
                    "Multiple matches found for {}. Use `search('*{}*')` to "
                    "see the results clearer.\n".format(
                        str(identifier), str(identifier)))

        # If we get here then we couldn't resolve ambiguous symbols in one or
        #  more cases. Note any genuine match of the other types as well.
        if pm_addr:
            apology += " code address in module " + pm_addr.module_name + "\n"
        if var:
            apology += " variable " + var.name + "\n"
        if reg:
            apology += " register " + reg.name + "\n"
        raise AmbiguousSymbolError(apology)