1import logging 2 3l = logging.getLogger("archinfo.arch_arm") 4 5try: 6 import capstone as _capstone 7except ImportError: 8 _capstone = None 9 10try: 11 import keystone as _keystone 12except ImportError: 13 _keystone = None 14 15try: 16 import unicorn as _unicorn 17except ImportError: 18 _unicorn = None 19 20from .arch import Arch, register_arch, Endness, Register 21from .tls import TLSArchInfo 22 23# TODO: determine proper base register (if it exists) 24# TODO: handle multiple return registers? 25# TODO: which endianness should be default? 26 27def is_arm_arch(a): 28 return a.name.startswith('ARM') or a.name.startswith("AArch") 29 30def get_real_address_if_arm(arch, addr): 31 """ 32 Obtain the real address of an instruction. ARM architectures are supported. 33 34 :param Arch arch: The Arch object. 35 :param int addr: The instruction address. 36 :return: The real address of an instruction. 37 :rtype: int 38 """ 39 40 return ((addr >> 1) << 1) if is_arm_arch(arch) else addr 41 42class ArchARM(Arch): 43 def __init__(self, endness=Endness.LE): 44 45 instruction_endness = None 46 if endness == Endness.LE: 47 instruction_endness = Endness.LE 48 49 super(ArchARM, self).__init__(endness, 50 instruction_endness=instruction_endness 51 ) 52 if endness == Endness.BE: 53 self.function_prologs = { 54 br"\xe9\x2d[\x00-\xff][\x00-\xff]", # stmfd sp!, {xxxxx} 55 br"\xe5\x2d\xe0\x04", # push {lr} 56 br"\xe1\xa0\xc0\x0c\xe5\x2d\xe0\x04" 57 } 58 self.thumb_prologs = { 59 br"\xb5[\x00-\xff]\xb0[\x80-\xff]", # push {??, ??, ..., ??, lr}; sub sp, sp, #?? 60 br"\xb4\x80\xb0[\x80-\xff]", # push {r7}; sub sp, sp, #?? 61 br"\xb4[\x00-\xff]\xb5\x00\xb0[\x80-\xff]", # push {r?, r?}; push {lr}; sub sp, sp, #?? 62 br"\xb0[\x80-\xff]\x90[\x00-\xff]", # sub sp, sp, #??; str r0, [sp, ?] 63 br"\xb5[\x00-\xff]\x4c[\x00-\xff]\x44\xa5", # push {??, ..., ??, lr}; ldr r4, [pc, #??]; add sp, r4 64 } 65 self.function_epilogs = { 66 br"\xe8\xbd[\x00-\xff]{2}\xe1\x2f\xff\x1e" # pop {xxx}; bx lr 67 br"\xe4\x9d\xe0\x04\xe1\x2f\xff\x1e" # pop {xxx}; bx lr 68 } 69 70 # ArchARM will match with any ARM, but ArchARMEL/ArchARMHF is a mismatch 71 def __eq__(self, other): 72 # pylint: disable=unidiomatic-typecheck 73 if not isinstance(other, ArchARM): 74 return False 75 if self.memory_endness != other.memory_endness or self.bits != other.bits: 76 return False 77 if type(self) is type(other): 78 return True 79 if type(self) is ArchARM or type(other) is ArchARM: 80 return True 81 return False 82 83 def __getstate__(self): 84 self._cs = None 85 self._cs_thumb = None 86 self._ks = None 87 self._ks_thumb = None 88 return self.__dict__ 89 90 def __setstate__(self, data): 91 self.__dict__.update(data) 92 93 @property 94 def capstone_thumb(self): 95 if _capstone is None: 96 l.warning("Capstone is not installed!") 97 return None 98 if self._cs_thumb is None: 99 self._cs_thumb = _capstone.Cs(self.cs_arch, self.cs_mode + _capstone.CS_MODE_THUMB) 100 self._cs_thumb.detail = True 101 return self._cs_thumb 102 103 @property 104 def keystone_thumb(self): 105 if _keystone is None: 106 l.warning("Keystone is not installed!") 107 return None 108 if self._ks_thumb is None: 109 self._ks_thumb = _keystone.Ks(self.ks_arch, _keystone.KS_MODE_THUMB) 110 return self._ks_thumb 111 112 @property 113 def unicorn_thumb(self): 114 if _unicorn is None: 115 l.warning("Unicorn is not installed!") 116 return None 117 return _unicorn.Uc(self.uc_arch, self.uc_mode + _unicorn.UC_MODE_THUMB) 118 119 def m_addr(self, addr, *args, **kwargs): 120 """ 121 Given the address of some code block, convert it to the address where this block 122 is stored in memory. The memory address can also be referred to as the "real" address. 123 124 For ARM-architecture, the "real" address is always even (has its lowest bit clear). 125 126 :param addr: The address to convert. 127 :return: The "real" address in memory. 128 :rtype: int 129 """ 130 return addr & ~1 131 132 # pylint: disable=keyword-arg-before-vararg, arguments-differ 133 def x_addr(self, addr, thumb=None, *args, **kwargs): 134 """ 135 Given the address of some code block, convert it to the value that should be assigned 136 to the instruction pointer register in order to execute the code in that block. 137 138 :param addr: The address to convert. 139 :param thumb: Set this parameter to True if you want to convert the address into the THUMB form. 140 Set this parameter to False if you want to convert the address into the ARM form. 141 Set this parameter to None (default) if you want to keep the address as is. 142 :return: The "execution" address. 143 :rtype: int 144 """ 145 if thumb is None: 146 return addr 147 elif not thumb: 148 return addr & ~1 149 else: # thumb 150 return addr | 1 151 152 def is_thumb(self, addr): # pylint:disable=unused-argument 153 """ 154 Return True, if the address is the THUMB address. False otherwise. 155 156 :param addr: The address to check. 157 :return: Whether the given address is the THUMB address. 158 """ 159 return bool(addr & 1) 160 161 bits = 32 162 vex_arch = "VexArchARM" 163 name = "ARMEL" 164 qemu_name = 'arm' 165 ida_processor = 'armb' 166 linux_name = 'arm' 167 triplet = 'arm-linux-gnueabihf' 168 max_inst_bytes = 4 169 ret_offset = 8 170 vex_conditional_helpers = True 171 syscall_num_offset = 36 172 call_pushes_ret = False 173 stack_change = -4 174 memory_endness = Endness.LE 175 register_endness = Endness.LE 176 sizeof = {'short': 16, 'int': 32, 'long': 32, 'long long': 64} 177 if _capstone: 178 cs_arch = _capstone.CS_ARCH_ARM 179 cs_mode = _capstone.CS_MODE_LITTLE_ENDIAN 180 _cs_thumb = None 181 if _keystone: 182 ks_arch = _keystone.KS_ARCH_ARM 183 ks_mode = _keystone.KS_MODE_ARM + _keystone.KS_MODE_LITTLE_ENDIAN 184 _ks_thumb = None 185 uc_arch = _unicorn.UC_ARCH_ARM if _unicorn else None 186 uc_mode = _unicorn.UC_MODE_LITTLE_ENDIAN if _unicorn else None 187 uc_mode_thumb = _unicorn.UC_MODE_LITTLE_ENDIAN + _unicorn.UC_MODE_THUMB if _unicorn else None 188 uc_const = _unicorn.arm_const if _unicorn else None 189 uc_prefix = "UC_ARM_" if _unicorn else None 190 #self.ret_instruction = b"\x0E\xF0\xA0\xE1" # this is mov pc, lr 191 ret_instruction = b"\x1E\xFF\x2F\xE1" # this is bx lr 192 nop_instruction = b"\x00\x00\x00\x00" 193 function_prologs = { 194 br"[\x00-\xff][\x00-\xff]\x2d\xe9", # stmfd sp!, {xxxxx} 195 br"\x04\xe0\x2d\xe5", # push {lr} 196 br"\r\xc0\xa0\xe1[\x00-\xff][\x00-\xff]\x2d\xe9", # mov r12, sp; stmfd sp!, {xxxxx} 197 br"\r\xc0\xa0\xe1\x04\xe0\x2d\xe5", # mov r12, sp; push {lr} 198 } 199 thumb_prologs = { 200 br"[\x00-\xff]\xb5[\x80-\xff]\xb0", # push {??, ??, ..., ??, lr}; sub sp, sp, #?? 201 br"\x80\xb4[\x80-\xff]\xb0", # push {r7}; sub sp, sp, #?? 202 br"[\x00-\xff]\xb4\x00\xb5[\x80-\xff]\xb0", # push {r?, r?}; push {lr}; sub sp, sp, #?? 203 br"[\x80-\xff]\xb0[\x00-\xff]\x90", # sub sp, sp, #??; str r0, [sp, ?] 204 br"[\x00-\xff]\xb5[\x00-\xff]\x4c\xa5\x44", # push {??, ..., ??, lr}; ldr r4, [pc, #??]; add sp, r4 205 } 206 function_epilogs = { 207 br"[\x00-\xff]{2}\xbd\xe8\x1e\xff\x2f\xe1" # pop {xxx}; bx lr 208 br"\x04\xe0\x9d\xe4\x1e\xff\x2f\xe1" # pop {xxx}; bx lr 209 } 210 instruction_alignment = 2 # cuz there is also thumb mode 211 register_list = [ 212 Register(name='r0', size=4, alias_names=('a1',), 213 general_purpose=True, argument=True, linux_entry_value='ld_destructor'), 214 Register(name='r1', size=4, alias_names=('a2',), 215 general_purpose=True, argument=True), 216 Register(name='r2', size=4, alias_names=('a3',), 217 general_purpose=True, argument=True), 218 Register(name='r3', size=4, alias_names=('a4',), 219 general_purpose=True, argument=True), 220 Register(name='r4', size=4, alias_names=('v1',), 221 general_purpose=True), 222 Register(name='r5', size=4, alias_names=('v2',), 223 general_purpose=True), 224 Register(name='r6', size=4, alias_names=('v3',), 225 general_purpose=True), 226 Register(name='r7', size=4, alias_names=('v4',), 227 general_purpose=True), 228 Register(name='r8', size=4, alias_names=('v5',), 229 general_purpose=True), 230 Register(name='r9', size=4, alias_names=('v6', 'sb'), 231 general_purpose=True), 232 Register(name='r10', size=4, alias_names=('v7', 'sl'), 233 general_purpose=True), 234 Register(name='r11', size=4, alias_names=('v8', 'fp', 'bp'), 235 general_purpose=True), 236 Register(name='r12', size=4, general_purpose=True), 237 # r12 is sometimes known as "ip" (intraprocedural call scratch) but we can't have that... 238 Register(name='sp', size=4, alias_names=('r13',), 239 general_purpose=True, default_value=(Arch.initial_sp, True, 'global')), 240 Register(name='lr', size=4, alias_names=('r14',), 241 general_purpose=True, concretize_unique=True), 242 Register(name='pc', size=4, vex_name='r15t', alias_names=('r15', 'ip')), 243 Register(name='cc_op', size=4, default_value=(0, False, None), artificial=True, concrete=False), 244 Register(name='cc_dep1', size=4, default_value=(0, False, None), artificial=True, concrete=False), 245 Register(name='cc_dep2', size=4, default_value=(0, False, None), artificial=True, concrete=False), 246 Register(name='cc_ndep', size=4, default_value=(0, False, None), artificial=True, concrete=False), 247 Register(name='qflag32', size=4, default_value=(0, False, None), artificial=True, concrete=False), 248 Register(name='geflag0', size=4, vector=True, default_value=(0, False, None), artificial=True, concrete=False), 249 Register(name='geflag1', size=4, vector=True, default_value=(0, False, None), artificial=True, concrete=False), 250 Register(name='geflag2', size=4, vector=True, default_value=(0, False, None), artificial=True, concrete=False), 251 Register(name='geflag3', size=4, vector=True, default_value=(0, False, None), artificial=True, concrete=False), 252 Register(name='emnote', size=4, vector=True, default_value=(0, False, None), artificial=True, concrete=False), 253 Register(name='cmstart', size=4, artificial=True, vector=True, default_value=(0, False, None), concrete=False), 254 Register(name='cmlen', size=4, artificial=True, default_value=(0, False, None), concrete=False), 255 Register(name='nraddr', size=4, artificial=True, default_value=(0, False, None), concrete=False), 256 Register(name='ip_at_syscall', size=4, artificial=True, concrete=False), 257 Register(name='d0', size=8, floating_point=True, vector=True), 258 Register(name='d1', size=8, floating_point=True, vector=True), 259 Register(name='d2', size=8, floating_point=True, vector=True), 260 Register(name='d3', size=8, floating_point=True, vector=True), 261 Register(name='d4', size=8, floating_point=True, vector=True), 262 Register(name='d5', size=8, floating_point=True, vector=True), 263 Register(name='d6', size=8, floating_point=True, vector=True), 264 Register(name='d7', size=8, floating_point=True, vector=True), 265 Register(name='d8', size=8, floating_point=True, vector=True), 266 Register(name='d9', size=8, floating_point=True, vector=True), 267 Register(name='d10', size=8, floating_point=True, vector=True), 268 Register(name='d11', size=8, floating_point=True, vector=True), 269 Register(name='d12', size=8, floating_point=True, vector=True), 270 Register(name='d13', size=8, floating_point=True, vector=True), 271 Register(name='d14', size=8, floating_point=True, vector=True), 272 Register(name='d15', size=8, floating_point=True, vector=True), 273 Register(name='d16', size=8, floating_point=True, vector=True), 274 Register(name='d17', size=8, floating_point=True, vector=True), 275 Register(name='d18', size=8, floating_point=True, vector=True), 276 Register(name='d19', size=8, floating_point=True, vector=True), 277 Register(name='d20', size=8, floating_point=True, vector=True), 278 Register(name='d21', size=8, floating_point=True, vector=True), 279 Register(name='d22', size=8, floating_point=True, vector=True), 280 Register(name='d23', size=8, floating_point=True, vector=True), 281 Register(name='d24', size=8, floating_point=True, vector=True), 282 Register(name='d25', size=8, floating_point=True, vector=True), 283 Register(name='d26', size=8, floating_point=True, vector=True), 284 Register(name='d27', size=8, floating_point=True, vector=True), 285 Register(name='d28', size=8, floating_point=True, vector=True), 286 Register(name='d29', size=8, floating_point=True, vector=True), 287 Register(name='d30', size=8, floating_point=True, vector=True), 288 Register(name='d31', size=8, floating_point=True, vector=True), 289 Register(name='fpscr', size=4, floating_point=True, artificial=True, concrete=False), 290 Register(name='tpidruro', size=4, artificial=True, concrete=False), 291 Register(name='itstate', size=4, artificial=True, default_value=(0, False, None), concrete=False), 292 ] 293 294 got_section_name = '.got' 295 ld_linux_name = 'ld-linux.so.3' 296 elf_tls = TLSArchInfo(1, 8, [], [0], [], 0, 0) 297 #elf_tls = TLSArchInfo(1, 32, [], [0], [], 0, 0) 298 # that line was lying in the original CLE code and I have no clue why it's different 299 300class ArchARMHF(ArchARM): 301 name = 'ARMHF' 302 triplet = 'arm-linux-gnueabihf' 303 ld_linux_name = 'ld-linux-armhf.so.3' 304 305class ArchARMEL(ArchARM): 306 name = 'ARMEL' 307 triplet = 'arm-linux-gnueabi' 308 ld_linux_name = 'ld-linux.so.3' 309 elf_tls = TLSArchInfo(1, 8, [], [0], [], 0, 0) 310 311class ArchARMCortexM(ArchARMEL): 312 """ 313 This is an architecture description for ARM Cortex-M microcontroller-class CPUs. 314 315 These CPUs have the following unusual / annoying distinctions from their relatives: 316 - Explicitly only support the Thumb-2 instruction set. Executing with the T-bit off causes the processor to fault 317 instantly 318 - Always little-endian 319 - Coprocessors? Nope, none of that rubbish 320 - Well-known standard memory map across all devices 321 - Rarely use an MPU, even though this does exist on some devices 322 - A built-in "NVIC" (Nested Vectored Interrupt Controller) as part of the standard. 323 - Standardized "blob format" including the IVT, with initial SP and entry prepended 324 - Usually don't run an OS (SimLinux? No thanks) 325 - As part of the above, handle syscalls (SVC) instructions through an interrupt (now called PendSV) 326 Uses its own fancy stack layout for this, which (UGH) varies by sub-sub-architecture 327 - Some fancy instructions normally never seen in other uses of Thumb (CPSID, CPSIE, WFI, MRS.W, MSR.W) 328 - New registers, namely: 329 * FAULTMASK 330 * PRIMASK 331 * BASEPRI 332 * CONTROL 333 * SP, banked as PSP or MSP 334 * PSR, now just one PSR, with a few meta-registers APSR, IPSR, and EPSR which take a chunk of that each 335 336 """ 337 338 name = "ARMCortexM" 339 triplet = 'arm-none-eabi' # ARM's own CM compilers use this triplet 340 341 # These are the standard THUMB prologs. We leave these off for other ARMs due to their length 342 # For CM, we assume the FPs are OK, as they are almost guaranteed to appear all over the place 343 function_prologs = {} 344 345 thumb_prologs = { 346 br"[\x00-\xff]\xb5", # push {xxx,lr} 347 br"\x2d\xe9[\x00-\xff][\x00-\xff]" # push.w {xxx, lr} 348 } 349 function_epilogs = { 350 br"[\x00-\xff]\xbd" # pop {xxx, pc} 351 # TODO: POP.W 352 } 353 354 register_list = [ 355 Register(name='r0', size=4, alias_names=('a1',), 356 general_purpose=True, argument=True, linux_entry_value='ld_destructor'), 357 Register(name='r1', size=4, alias_names=('a2',), 358 general_purpose=True, argument=True), 359 Register(name='r2', size=4, alias_names=('a3',), 360 general_purpose=True, argument=True), 361 Register(name='r3', size=4, alias_names=('a4',), 362 general_purpose=True, argument=True), 363 Register(name='r4', size=4, alias_names=('v1',), 364 general_purpose=True), 365 Register(name='r5', size=4, alias_names=('v2',), 366 general_purpose=True), 367 Register(name='r6', size=4, alias_names=('v3',), 368 general_purpose=True), 369 Register(name='r7', size=4, alias_names=('v4',), 370 general_purpose=True), 371 Register(name='r8', size=4, alias_names=('v5',), 372 general_purpose=True), 373 Register(name='r9', size=4, alias_names=('v6', 'sb'), 374 general_purpose=True), 375 Register(name='r10', size=4, alias_names=('v7', 'sl'), 376 general_purpose=True), 377 Register(name='r11', size=4, alias_names=('v8', 'fp', 'bp'), 378 general_purpose=True), 379 Register(name='r12', size=4, general_purpose=True), 380 # r12 is sometimes known as "ip" (intraprocedural call scratch) but we can't have that... 381 Register(name='sp', size=4, alias_names=('r13',), 382 general_purpose=True, default_value=(Arch.initial_sp, True, 'global')), 383 Register(name='lr', size=4, alias_names=('r14',), 384 general_purpose=True, concretize_unique=True), 385 Register(name='pc', size=4, vex_name='r15t', alias_names=('r15', 'ip')), 386 Register(name='cc_op', size=4, default_value=(0, False, None), artificial=True, concrete=False), 387 Register(name='cc_dep1', size=4, default_value=(0, False, None), artificial=True, concrete=False), 388 Register(name='cc_dep2', size=4, default_value=(0, False, None), artificial=True, concrete=False), 389 Register(name='cc_ndep', size=4, default_value=(0, False, None), artificial=True, concrete=False), 390 Register(name='qflag32', size=4, default_value=(0, False, None), artificial=True, concrete=False), 391 Register(name='ip_at_syscall', size=4, artificial=True, concrete=False), 392 # Cortex-M Has a different FPU from all other ARMs. 393 Register(name='d0', size=8, subregisters=[('s0', 0, 2), ('s1', 2, 2)], floating_point=True), 394 Register(name='d1', size=8, subregisters=[('s2', 0, 2), ('s3', 2, 2)], floating_point=True), 395 Register(name='d2', size=8, subregisters=[('s4', 0, 2), ('s5', 2, 2)], floating_point=True), 396 Register(name='d3', size=8, subregisters=[('s6', 0, 2), ('s7', 2, 2)], floating_point=True), 397 Register(name='d4', size=8, subregisters=[('s8', 0, 2), ('s9', 2, 2)], floating_point=True), 398 Register(name='d5', size=8, subregisters=[('s10', 0, 2), ('s11', 2, 2)], floating_point=True), 399 Register(name='d6', size=8, subregisters=[('s12', 0, 2), ('s13', 2, 2)], floating_point=True), 400 Register(name='d7', size=8, subregisters=[('s14', 0, 2), ('s15', 2, 2)], floating_point=True), 401 Register(name='d8', size=8, subregisters=[('s16', 0, 2), ('s17', 2, 2)], floating_point=True), 402 Register(name='d9', size=8, subregisters=[('s18', 0, 2), ('s19', 2, 2)], floating_point=True), 403 Register(name='d10', size=8, subregisters=[('s20', 0, 2), ('s21', 2, 2)], floating_point=True), 404 Register(name='d11', size=8, subregisters=[('s22', 0, 2), ('s23', 2, 2)], floating_point=True), 405 Register(name='d12', size=8, subregisters=[('s24', 0, 2), ('s25', 2, 2)], floating_point=True), 406 Register(name='d13', size=8, subregisters=[('s26', 0, 2), ('s27', 2, 2)], floating_point=True), 407 Register(name='d14', size=8, subregisters=[('s28', 0, 2), ('s29', 2, 2)], floating_point=True), 408 Register(name='d15', size=8, subregisters=[('s30', 0, 2), ('s31', 2, 2)], floating_point=True), 409 # TODO: NOTE: This is technically part of the EPSR, not its own register 410 Register(name='fpscr', size=4, floating_point=True), 411 # TODO: NOTE: This is also part of EPSR 412 Register(name='itstate', size=4, artificial=True, default_value=(0, False, None), concrete=False), 413 # Whether exceptions are masked or not. (e.g., everything but NMI) 414 Register(name='faultmask', size=4, default_value=(0, False, None)), 415 # The one-byte priority, above which interrupts will not be handled if PRIMASK is 1. 416 # Yes, you can implement an RTOS scheduler in hardware with this and the NVIC, you monster! 417 Register(name='basepri', size=4, default_value=(0, False, None)), 418 # Only the bottom bit of PRIMASK is relevant, even though the docs say its 32bit. 419 # Configures whether interrupt priorities are respected or not. 420 Register(name='primask', size=4, default_value=(0, False, None)), 421 # NOTE: We specifically declare IEPSR here. Not PSR, .... variants.for 422 # VEX already handles the content of APSR, and half of EPSR (as ITSTATE) above. 423 # We only keep here the data not computed via CCalls 424 # The default is to have the T bit on. 425 Register(name='iepsr', size=4, default_value=(0x01000000, False, None)), 426 # CONTROL: 427 # Bit 2: Whether the FPU is active or not 428 # Bit 1: Whether we use MSP (0) or PSP (1) 429 # Bit 0: Thread mode privilege level. 0 for privileged, 1 for unprivileged. 430 Register(name='control', size=4, default_value=(0, False, None)) 431 ] 432 433 # Special handling of CM mode in *stone 434 if _capstone: 435 cs_arch = _capstone.CS_ARCH_ARM 436 cs_mode = _capstone.CS_MODE_LITTLE_ENDIAN + _capstone.CS_MODE_THUMB + _capstone.CS_MODE_MCLASS 437 _cs_thumb = None 438 if _keystone: 439 ks_arch = _keystone.KS_ARCH_ARM 440 ks_mode = _keystone.KS_MODE_THUMB + _keystone.KS_MODE_LITTLE_ENDIAN 441 _ks_thumb = None 442 uc_arch = _unicorn.UC_ARCH_ARM if _unicorn else None 443 uc_mode = _unicorn.UC_MODE_THUMB + _unicorn.UC_MODE_LITTLE_ENDIAN if _unicorn else None 444 uc_mode_thumb = _unicorn.UC_MODE_THUMB + _unicorn.UC_MODE_LITTLE_ENDIAN if _unicorn else None 445 446 @property 447 def capstone_thumb(self): 448 return self.capstone 449 450 @property 451 def keystone_thumb(self): 452 return self.keystone 453 454 def __init__(self, *args, **kwargs): 455 super(ArchARMCortexM, self).__init__(*args, **kwargs) 456 457 # TODO: Make arm_spotter use these 458 # TODO: Make SimOS use these. 459 # TODO: Add.... the NVIC? to SimOS 460 461 462register_arch([r'.*cortexm|.*cortex\-m.*|.*v7\-m.*'], 32, 'any', ArchARMCortexM) 463register_arch([r'.*armhf.*'], 32, 'any', ArchARMHF) 464register_arch([r'.*armeb|.*armbe'], 32, Endness.BE, ArchARM) 465register_arch([r'.*armel|arm.*'], 32, Endness.LE, ArchARMEL) 466register_arch([r'.*arm.*|.*thumb.*'], 32, 'any', ArchARM) 467