Question

I'm building a Windows Phone project with some bits of it in assembly. My assembly file is in ARM mode (CODE32), and it tries to jump to a C function that I know is compiled to Thumb. The code goes like this:

    ldr r12, [pFunc]
    mov pc, r12
pFunc
    dcd My_C_Function

Here's the weird thing. The value at pFunc in the snippet is a pointer at a function thunk plus one. That is, the 0th bit is set, as if the jump target is meant to be Thumb and the jump instruction is meant to be BX. But the thunk is clearly ARM! The thunk loads the address of the function body plus one and executes a BX to it, properly switching modes.

Trying to BX to that address would probably crash, because that would switch modes and trying to execute ARM code in Thumb mode is not a good idea. Trying to simply jump to that address (as the current code does) would probably crash too, because PC would end up unaligned.

I could, in theory, manually clean up the 0th bit and then jump, but there's gotta be some error to my thinking. The thunk is generated by the C compiler - right? The C compiler knows that the thunk is ARM code. The address under pFunc is generated by the linker, since it's a cross-module call. So the low bit is placed there by the linker; why doesn't the linker know that those thunks are ARM?

Any explanation, please?

I don't have a WP8 device now, so I can't try it in real hardware. Staring hard at the generated code is the only debugging technique that I have :(

EDIT: but what if those thunks are not ARM, but Thumb-2? Thumb-2 supports some 32-bit command IIRC. Is their encoding the same as in ARM mode? How does Thumb-2 decode commands, anyway?

Was it helpful?

Solution 3

The disassembler (IDA Demo, specifically) misled me by uniting several commands into one line. It made a single mov line out of a mov-orr-orr sequence that was meant to assign a full 32-bit constant to a register. The thunks were Thumb after all. The linker works as designed.

IDA is otherwise great. I knew in the back of my mind about this particular behavior regarding ARM, but this time it slipped.

My bad, thanks and upvotes to everyone who tried to help.

OTHER TIPS

The details you want are specified in section "A2.3.2 Pseudocode details of operations on ARM core registers" of "ARM Architecture Reference Manual, ARMv7-A and ARMv7-R edition". Here is the relevant pseudocode (from the above manual) about writes to PC register:

BXWritePC(bits(32) address)
    if CurrentInstrSet() == InstrSet_ThumbEE then
        if address<0> == '1' then
            BranchTo(address<31:1>:'0');  // Remaining in ThumbEE state
        else
            UNPREDICTABLE;
    else
        if address<0> == '1' then
            SelectInstrSet(InstrSet_Thumb);
            BranchTo(address<31:1>:'0');
        elsif address<1> == '0' then
            SelectInstrSet(InstrSet_ARM);
            BranchTo(address);
        else // address<1:0> == '10'
            UNPREDICTABLE;

If the low bit of the address (bit 0) is set, the processor will clear this bit, switch to Thumb mode, and perform a jump to the new address.

This behaviour is correct for ARMv7 and later (i.e. applies to all Windows Phone devices, but not all Android/iOS devices).

You might be well have a valid / real problem on your hand. Afaik Windows Phone environment is required to be Thumb-2 only, so linker you are using might not be able to handle calls to ARM mode. See the same link for some considerations when you are mixing assembly and C.

If this was a Linux / ELF question, I would have answered differently;

The address under pFunc is generated by the linker, since it's a cross-module call. So the low bit is placed there by the linker.

pFunc is generated by compiler and a linker will fix it when you are building a loadable image which will already be fully resolved for static relocation of calls. All portable object files should contain a table about function modes so later linkers can process them and do relocation and updating call sequences accordingly.

See ELF for the ARM Architecture - 4.6 Relocation for how this is done via ELF files.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top