I'm facing a weird issue, somewhat similar to this. I have a Windows Phone 8 native DLL project, mostly C++ but with an ARM assembly source in it. The source is in ARM mode (i. e. not Thumb). C++ is compiled to Thumb.
The app crashes when C++ tries to call into an assembly routine. The call command in the disassembly is BLX with an immediate offset - it's supposed to switch mode back to ARM, unconditionally, but somehow it doesn't.
I have the details of the exception. The exception code is 0xc000001d (invalid operation), and the value of the PC in the crash context struct is 0x696d5985. That's impossible in either mode - it's misaligned, bit zero is one. The BLX instruction goes
1b f0 0c eb - if you decipher, that's a two-part Thumb-style BLX all right, with a 4-aligned displacement. The T flag in the crash context is SET (CPSR=0x60000010).
I don't have a device, but the crash log from a beta tester is pretty conclusive. I have a debug log record right before the call into assembly. Then the crash.
EDIT: related . They claim, however, that the assembler itself (
armasm) translates ARM to Thumb. That's not the case for me - at least not statically. The DLL contains proper ARM code, as specified in the assembly source (
EDIT: tried with a slightly different jump sequence:
ldr r12, target and r12, r12, #0xfffffffe ; To be sure bx r12 ;BX to a register with a cleared 0th bit. Doesn't get any more explicit than that.
Same result. Looks like there's either some weird code morphing taking place somewhere in the Store, or the OS itself catches mode switches and prevents them.
Code morphing can probably be detected by dumping portions of the executable into the crash log along with rest of the crash data. But what can I do with OS interference, short of converting to whole codebase to Thumb? It doesn't just recompile.
EDIT for dwelch: the calling sequence in compiled C code goes like this:
.text:1000A35E MOV R2, #g_Host ;Three parameters .text:1000A366 MOV R1, R5 .text:1000A368 MOV R0, R6 .text:1000A36A BLX Func ; Code bytes 1B F0 0C EB
BLX to an immediate address HAS to switch mode. It's not conditional, like
bx register. The call target is a thunk:
.text:10025984 B Func_Impl
And the crash address is this thunk plus one: 5985.
This is a disassembly of a compiled DLL, but I have no guarantee that this is exactly what's executing on a device. The user in a linked MSDN thread claimed that they looked at the disassembly in the debugger and saw Thumb where ARM should've been. Microsoft, IIRC, holds a patent for modifying app code en route from publisher to device; that could be the reason.
A gentleman called Michael Schnell suggested elsewhere that the interrupt handler in Windows Phone 8 doesn't restore the Thumb flag, instead hard-codes it to 1. Testing seems to confirm that theory. The following snippet:
THUMB ASMTest mov r12, lr blx a mov lr, r12 bx lr ALIGN 4 ARM a bx lr
consistently crashes under a debugger, but runs as expected when debuggerless (i. e. no interrupts while in ARM mode). When I inserted an empty loop with 0x10000 iterations in ARM mode, it ran on a few tries, then crashed.
I have firsthand knowledge of this; I was the reverse engineer who figured out the cause in Windows RT's kernel. Specifically,
KeContextFromKframes in the Windows NT kernel (
ntoskrnl.exe) is setting the
T bit when freezing a thread's state for a task switch. This means that yes, upon resuming after an interrupt, you will crash.
This annoyed us jailbreakers for RT/WinPhone, because we couldn't directly port Chrome's JITter without breaking Microsoft's PatchGuard. We could load a kernel driver to patch this out of
KeContextFromKframes, but then PatchGuard would later cause a crash.
you cant get an unaligned arm address when using BLX from thumb to arm. The lower two bits are anded by the second instruction. Read the arm docs, you basically have two instructions the first one is:
0xF01B if H == 10 then LR = PC + (SignExtend(offset_11) << 12)
basically the first instruction results in a modification of lr where pc is the address if this instruction plus 4 (two instructions ahead).
LR = PC + 0x1B000
The second is
0xEB0C if H == 01 then PC = (LR + (offset_11 << 1)) AND 0xFFFFFFFC LR = (address of next instruction) | 1 CPSR T bit = 0
the end result is
PC = (address of next instruction + 0x1B000 + 0x318) AND 0xFFFFFFFC PC = (address of next instruction + 0x1B318) AND 0xFFFFFFFC LR = address of next instruction | 1 CPSR T bit = 0, arm mode.
I think your crash is somewhere else.
You should post the disassembly of the code in question, addresses of instructions and such.
as far as your BX attempt, you are walking a slippery slope...
ARM/Thumb state transfers If Rm[1:0] == 0b10, the result is UNPREDICTABLE, as branches to non word-aligned addresses are impossible in ARM state.
by anding with 1110 you are clearing the lsbit, but also potentially allowing an unaligned address. If you have not properly computed the destination address in r12 and if that is not arm code, then it wont work. Please post the disassembly of this as well as that will clearly show what is going on, also post the first instruction or few of the destination address.
It looks like your exception code is telling you that your exception is in thumb mode at a thumb address. Please post the disassembly for the code at/around that address.
User contributions licensed under CC BY-SA 3.0