ARM assembly call from C, GCC (bare metal)

I am trying to do some kind of open source programming in ARM with GCC and testing on QEMU. Whenever I call on an ARM label with C, my program freezes. I have a simple code example that shows the problem in https://gist.github.com/1654392 - when I call activate () in this code, it freezes.

I observed with objdump that when I do bl from assembly to C code (like from _start), it generates a small shell that switches to thumb commands. It seems that the C code is generated in the thumb instructions, but my whole assembly is generated in the ARM instructions (32-bit). I can’t understand why this is or how to fix it.

+7
source share
4 answers

To call the ARM mode function defined in the assembly from the THUMB mode function defined in C, you need to define the symbol in the assembly as a function, and the tools (Linaro gcc) will produce a blx instruction instead of of bl .

Example:

 @ Here, we suppose that this part of code is inside of .code 32 .type fn, %function fn: mov pc, lr 
+5
source

see the http://github.com/dwelch67/yagbat qemu directory.

Here are some examples of calling a hand or finger from a hand

 start_vector: mov sp,#0x20000 ;@ call an arm function from arm bl notmain ;@ call a thumb function frm arm ldr r0,=0xAABBAABB bl hexstring_trampoline ;@ call a thumb function frm arm ldr r0,=0x12341234 ldr r1,hexstring_addr mov lr,pc bx r1 ;@ call a thumb function frm arm ldr r0,=0x12312344 bl hexstring_trampoline hang: b hang hexstring_trampoline: ldr r1,hexstring_addr bx r1 hexstring_addr: .word hexstring 

If you look at the instruction set instructions, you will see that you need to use BX or BLX to switch between hand and thumb states. BLX is not as widely supported as BX.

In terms of definition, program counter, pc is two forward instructions during command execution. for the thumb, which is 4 bytes, for the hand is 8 bytes. In any case, two instructions. To simulate bl that cannot be used to change state, you need to load the link registry with the return address and use bx to switch to the function change state depending on the lsbit address. so

 mov lr,pc bx r1 here: 

mov lr, pc above loads the address here: which is our return address, bx r1 in a state-independent way calls the function. lsbit addresses lr indicates return mode and you need to always use bx to return

 pre_thumb: ldr pc,lr thumb_capable: bx lr 

The compiler allocates the bl command for function calls, the linker fills the rest later, if it is too far from the target, then it needs a trampoline function, which the linker adds itself. Similarly, if you need to change modes, bl calls the trampoline function, which does this. I modeled that in one of the above, to emulate this, you can see that it is a little wasteful, I hope that my explanation of the compiler allocating space for bl makes it clearer, more wasteful to always plan for a mode change and should insert nops for most function calls in code.

The code also includes a thumb call in assembler:

 .thumb .thumb_func .globl XPUT32 XPUT32: push {lr} ;@ call an arm function from thumb asm ldr r2,=PUT32 mov lr,pc bx r2 pop {r2} bx r2 

basically the same thing, except that you can’t insert into lr in thumb mode, you can jump out to the computer, but I don’t think that these are switching modes, so you cannot use it, you need a spare register again . You, of course, need to know the calling conventions in order to know which registers you can use, or you can wrap another set of taps and tabs to save everything except lr

  push {r2,lr} ;@ call an arm function from thumb asm ldr r2,=PUT32 mov lr,pc bx r2 pop {r2} mov lr,r2 pop {r2} bx lr 

A heavy finger or hand to arm you, just use bl if you can. ldr pc, address if you can not.

+3
source

If you compiled your asm code as Thumb, you need to mark the function as a Thumb function, so the linker uses the correct instruction when branching to it (for example, BLX or BX to an address with a low bit set). This is done using the .thumb_func directive:

 .global activate .thumb_func activate: b test 

Another option is to explicitly ask the assembler to generate the ARM code:

 .code 32 .global activate activate: b test 

Check out this article , though remember that current processors don't need many of the workarounds that were needed in ARMv4, so you probably shouldn't blindly follow it.

0
source

To eliminate the confusion:

The problem was that the Ubuntu GCC cross-compiler for ARM generates large (16-bit) instructions by default. As the other answers show here, a call between them is possible, but although the GNU assembler found that the C code generates instructions for the thumb and so happily generated pads using bx to correctly configure the mode for calling in C, I can’t control what GCC itself generates to call the function, and it only called them with bl, which broke, because my build code must be ARM instructions (32-bit).

The solution (which is poorly documented) is to send gcc -marm, which will at least make all the code the same.

If there is a switch to get gcc to generate bx calls for functions, this will probably work.

0
source

All Articles