2

I want write a routine for cortex-m0 in GCC C where mix C and assembly code. I am trying something like this

__attribute__((naked)) uint8_t dummy(uint8_t value)
{
    asm volatile (
            "push   {r1-r7, lr}\n\t"
             // R0 contains input value
             //here i do some operation on registers r0..r7         
             //.....
             //I guess result will be in the R0 register
            "MOV R0, R3\n\t"

            "pop    {r1-r7, pc}\n\t"
        );
}

but unfortunately i have problems:

  • my program crashes because of this.
  • compiler claims that my "dummy" routine doesn't have return value

EDIT, WORKING CODE:I moved the code to the separate .s file and final solution looks like this

    .syntax unified
    .arch armv6-m
    .text
    .thumb
    .thumb_func
    .align 1
    .globl    dummymethod
    .type    dummymethod, %function
dummymethod:
    .fnstart
            PUSH    {r1-r7, lr}
            // here some operation on registers R0..R7

            MOV R0, R3 //result is in R0

            pop {r1-r7, pc}

    .pool
    .cantunwind
    .fnend
    .size   dummymethod,.-dummymethod
  • 1
    It is generally a good idea to avoid inline assembly for that. Put your routine into an assembly file and assemble it into an object file on its own. – fuz May 07 '20 at 14:59
  • 1
    For what you shoud the code should work fine. It compiles fine on my machine. If there's a problem it's probably somewhere else. – Marco Bonelli May 07 '20 at 15:10
  • Also, add the output of `gcc -v`. – Marco Bonelli May 07 '20 at 15:20
  • you dont have a return value that is why it is complaining, the inline assembly functionality including return is not recognized by the compiler so it doesnt see a return value (in C language). add any return value return 0; and that should go away – old_timer May 07 '20 at 15:32

2 Answers2

3

use real assembly

.cpu cortex-m4
.thumb
.globl dummy
.thumb_func
dummy:
    mov r0,r3
    bx lr

the calling convention allows for modification of r0-r3, return in r0, you are not touching r4,r5,r6,r7,lr so no need to push those.

ugly but you can assemble it with gcc, gnu assembler, as, is preferred (arm-whatever-as)

old_timer
  • 69,149
  • 8
  • 89
  • 168
1

Falling off the end of a naked function is not actually an error. Warning in that case was a GCC bug that was fixed somewhere before GCC 5.4. (But code-gen is the same; older GCC versions that warn don't break your code.)

There are only a few minor benefits to writing a naked function in C + asm:

  • Having the compiler emit thumb vs. ARM directives if you have code that can compile for either mode. (Cortex-M0 is thumb-only so no benefit for your case.)
  • debug info, dynamic library function size, and other metadata stuff like that.
  • get the compiler to do C++ name mangling for you instead of declaring it extern "C", if you were writing C++.
  • You can maintain it next to some actual C code it interacts with.

Otherwise you might as well just write a separate asm file. It still can't inline like a non-naked wrapper function around a GNU C Extended asm statement could.

I'm not an ARM expert, but there's nothing wrong with the way you were using inline asm / naked function. The body of a naked is basically the only sane use-case for GNU C "Basic" asm (no constraints).

If you compile this with the right GCC options, GCC should emit the same directives you were using in stand-along asm. (Although not .cantunwind in case that matters.)


From Godbolt, gcc5.4.1 -O2 -Wall -mcpu=cortex-m0 -mthumb -fverbose-asm compiles it cleanly (no warnings). This is the asm output, with directives unfiltered, but some manual editing to remove directives and labels I think are just clutter (including debug info):

        .syntax unified
        .cpu cortex-m0
        .fpu softvfp

        .text
        .global dummy
        .code   16
        .thumb_func
        .type   dummy, %function
dummy:
        .syntax divided
        push   {r1-r7, lr}
        MOV R0, R3
        pop    {r1-r7, pc}

        .thumb
        .syntax unified
        .cfi_endproc
        .size   dummy, .-dummy

Since you're on a thumb-only CPU you don't need thumb interworking via bx lr, but as @old timer points out: if you can leave LR untouched during your function then you can just return with bx lr instead of popping it back into PC.

Also note that the first arg is passed in R0, so reading R3 is reading a register that your prototype says is not an input.


Moving this to a stand-alone asm file is totally fine, just as valid, and probably a better choice. But since you asked about doing it with a naked function, that should also work.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 1
    thank you for your answer, it is very useful for me. I asked the question because i was not aware about calling convention, and if i can return value through R0 register. And i was thinking that this might be the reason of the crash. Reading the answers i found that my original code was more or less fine and working. And then i found the problem in another part of my code. The code is actually doing some SPI bitbanging and it crashed because i violated some timing. – Vladimir Tsykunov May 09 '20 at 11:20