FreeRTOS/Source/portable/GCC/ARM_CM3/port.c inline assembly error


I’m porting FreeRTOS to an STM32 device. I am in an unusual scenario since I need to perform some modifications to the code adding some assembly for data and control flow integrity to the entire codebase.
When adding my modifications, I have the following error:

<inline asm>:1:3: error: out of range pc-relative fixup value
         ldr r0, =0xE000ED08    
error: cannot compile inline asm

It is caused by the code of the function prvPortStartFirstTask( void ) in FreeRTOS/Source/portable/GCC/ARM_CM3/port.c.

How can I fix this out of range error?

I omit it right now for brevity, but obviously I can provide further information about the nature of the transformation I need to perform.

Hi @davidebaro,

You shouldn’t have to modify the CM3 port for an STM32. Can you share what you are trying to accomplish, and also share your modifications?

Hi @jefftenney, thank you for your response.

I’m writing an LLVM pass that adds some code for fault tolerance. Right now it consists in adding some instructions for integrity checks at every basic block of the program.

Basically I have some static signatures at compilation time, when the code is executed I need to update the signature and check it against the static one to see if any faults occurred.

I am not very familiar with ARM instructions but, as far as I understood, that piece of code (the one generating my error) should load the address 0xE000ED08, that corresponds with the NVIC offset register. The problem is that perhaps the size of the code increases too much due to my transformation, therefore the offset between the PC and the address I am looking for is too big. Is there any workaround for this?

So you want your code image to have static signatures sprinkled throughout the image? As an alternative, you could consider that the entire image is already full of static signatures. These signatures change when the code image changes. But so will the signatures you are proposing, because their addresses will change, right?

You can fix the issue by eliminating the “shorthand” code. See vPortSVCHandler() for example. The shorthand in vPortSVCHandler() would have been
ldr r3, =pxCurrentTCB
So you can see how to eliminate the shorthand.

Cortex M doesn’t directly support just any 32-bit immediate data. Instead you have to load the 32-bit data from a “literal pool” nearby, and you can address the literal pool via PC-relative address.

Note as an alternative, you can also just eliminate the first four instructions. Those instructions are merely setting the MSP back to its reset value. That’s not important to do if you have plenty of main stack. So you can eliminate these:

        " ldr r0, =0xE000ED08 	\n"/* Use the NVIC offset register to locate the stack. */
        " ldr r0, [r0] 			\n"
        " ldr r0, [r0] 			\n"
        " msr msp, r0			\n"/* Set the msp back to the start of the stack. */

Does the following definition work for you?

static void prvPortStartFirstTask( void )
    __asm volatile (
        " ldr r0, xNVICConst            \n"/* Use the NVIC offset register to locate the stack. */
        " ldr r0, [r0]                  \n"
        " ldr r0, [r0]                  \n"
        " msr msp, r0                   \n"/* Set the msp back to the start of the stack. */
        " cpsie i                       \n"/* Globally enable interrupts. */
        " cpsie f                       \n"
        " dsb                           \n"
        " isb                           \n"
        " svc 0                         \n"/* System call to start first task. */
        " nop                           \n"
        " .ltorg                        \n"
        " .align 4                      \n"
        " xNVICConst: .word 0xE000ED08  \n"

I got a bit lost @jefftenney .

My code is in an LLVM pass. This means that it works on the intermediate representation (IR) of the program, it is the one where most of the platform-independent compiler optimizations are performed. My goal is to build an LLVM pass that works in “most cases”, that is, applies a fault detection mechanism on top of virtually any architecture.

@aggarg Thanks for the response, I tried to substitute the function but it does not work after I run my transformation. I don’t have the error anymore by the way, so maybe there’s something wrong on my end.

That’s because I was completely lost re LLVM. :slight_smile:

Anyway I think @aggarg eliminated the shorthand properly. You could also try eliminating the .ltorg directive since it is no longer relevant (and apparently doesn’t really work in inline assembly anyway). But that is not likely to fix the new issue.

Does it work without your transformation?

Yes, without the transformation it works. But it was working also earlier without your fix. :confused:

The fix is just to address the linker error you are getting. The reason I asked if it works without your transformation is to ensure that the change I suggested is correct.

I see. I did some testing trying to apply my transformations only to smaller parts of the code such as the main() and main.c files and apparently there are some issues on my end, this is the first non-toy project that I am trying to transform.

Thank y’all for your feedback and responses :smiley: