MCUxpresso imr rt - 位置独立代码导致免费 rtos 任务激活时出现内存故障

MCUxpresso imr rt - Position Independent Code result in mem fault on free rtos task activation

我有一块来自 nxp 的开发板 (imx rt 1024),我在上面编写了使用 MCUxpresso 的软件(nxp 的 IDE)。对于我的项目,我被要求引入位置无关代码 (PIC),长话短说,它可以在无线安装固件更新时为我们节省几秒钟的停机时间。

我首先创建了一个新的标准项目,亲自动手并更好地了解 PIC 的工作原理。因此,在对链接器脚本进行了一些更改(将 .got 部分放在闪存 (LMA) 中的某个位置并将其定义为在 SRAM (VMA) 中)并在启动代码中进行了一些修改之后,我终于完成了第一步工作:我运行 可以将我的 MCU 上的代码编译为“位置无关代码”。

显然,我还没有将它移动到“任何”位置。但作为第一步,我很高兴。

我的下一步是引入一些响应硬件中断的代码。我不知道为什么,但我觉得这可能很棘手。或者,也许我只是想看到它也起作用。对我来说不幸的是,它失败了。很难。

简单阐述一下:

  1. 所以我有一个项目,它切换我的开发板上的一个引脚,它切换一个 led。为像我这样简单的人提供简单的反馈。这行得通。印象还不是很深刻。

  2. 接下来,我用 -fPIC 编译了同一个项目,并且它也能正常工作。

  3. 接下来,我引入了 freertos,创建了一个简单的任务,为我完成“闪烁”,然后进入睡眠状态。同样,开始 而没有 使用 -fPIC 进行编译。这也行。同样,不令人印象深刻,简单的小步骤。

做闪烁的任务供参考

static void main_task(void *params)
{
    while (1)
    {
        GPIO1->DR ^= (1<<24);
        vTaskDelay(100);
    }
}
  1. 下一步很明显,让我们再次进行 freertos 测试,但现在使用 -fPIC 进行编译。一旦 FreeRTOS 尝试启动其调度程序,程序就会因内存错误而崩溃。

现在我尝试了很多事情来解决这个问题,一个线索,一个关于导致这个问题的模糊想法,但我一无所获。将 .got 部分从 ITC sram、DTC sram 移动,根本不要将其移动到 SRAM,而是将其保留在闪存中(因为我还没有在闪存中移动代码)。似乎没有什么能改变这种行为。一旦我使用 -fPIC 编译我的 freertos 测试项目,它就会在任务计划程序启动时崩溃。

深入了解一些细节

供参考,链接器脚本修改:

 .got : ALIGN(4)
{
    __global_offset_table_flash_start__ = LOADADDR(.got) ;
    __global_offset_table_itc_start__ = ADDR(.got) ;
    *(.got* .got.*)
    __global_offset_table_flash_end__ = . ;
} >SRAM_DTC AT>PROGRAM_FLASH

SIDE 注意:这被复制到 SRAM_DTC(DTC 是针对数据优化的内存,ITC 是针对指令(/aka 函数)的优化内存)。我 认为 .got 部分包含指向数据的指针,而 .got.plt 包含共享库的函数(我不使用共享库,所以我没有 .got.plt 部分)。我试过了ITC 和 DTC sram 中的 .got 部分。两者都以同样的方式失败。对我来说,将 .got 存储在 DTC ram 中是最有意义的。所以我坚持这样做,直到我了解到其中的一些假设错了。

也供参考,ResetHandler的调整部分。在启动代码中,我设置了用于 PIC 的 r9 寄存器。我还将 .got 部分复制到 SRAM_DTC

// Ignore the volatile stuff, it makes debugging easier, if I don't, 
// I see 'optimized out' in mcuxpresso which is making my life harder
// other than that, it serves no functional purpose
// volatile is casted away with const_cast<...>

volatile extern unsigned int __global_offset_table_flash_start__;
volatile extern unsigned int __global_offset_table_itc_start__;
volatile extern unsigned int __global_offset_table_flash_end__;

volatile unsigned int size;
unsigned int index;

unsigned int *global_offset_table_flash;
unsigned int *global_offset_table_itc;
unsigned int *global_offset_table_end_itc;
unsigned int global_offset_table_size;


__attribute__ ((naked, section(".after_vectors.reset")))
void ResetISR(void) 
{
    // Disable interrupts
    __asm volatile ("cpsid i");
    // Setup r9 used for PIC, and let it point to the location in flash first
    __asm volatile ("LDR r9, = __global_offset_table_flash_start__");
    // Set the stack pointer, AFTER we setup r9
    __asm volatile ("MSR MSP, %0" : : "r" (&_vStackTop) : );

    //
    // Copy global offset table to ram
    //
    global_offset_table_flash = const_cast<unsigned int*>(&__global_offset_table_flash_start__);
    global_offset_table_itc = const_cast<unsigned int*>(&__global_offset_table_itc_start__);
    global_offset_table_end_itc = const_cast<unsigned int*>(&__global_offset_table_flash_end__);

    size =
        reinterpret_cast<unsigned int>(&__global_offset_table_flash_end__) -
        reinterpret_cast<unsigned int>(&__global_offset_table_itc_start__);
    global_offset_table_size = static_cast<unsigned int>(&__global_offset_table_flash_end__ - &__global_offset_table_itc_start__);

    for (index = 0u; index < size/sizeof(unsigned int); ++index)
    {
        global_offset_table_itc[index] = global_offset_table_flash[index];
    }

    __asm volatile ("LDR r9, = __global_offset_table_itc_start__");

    // ... rest of startup code, initializes VTOR and some other nxp generated stuff
    // ... before it jumps to main()

我知道真正的男人会在集会上这样做。我还不足以进行简单的组装。我还可以想象铁杆汇编人员可能不太熟悉这个 c++。我非常有信心这段代码可以实现它声称的功能。我是一步一步调试的。 .got section 被 uint32 复制到 uint32 上并最终进入 SRAM。

跳转到main完成后。执行的代码很少。我配置我的 LED 引脚,创建一个具有大量堆栈的 freertos 任务,然后启动调度程序。

static void main_task(void *params);

int main(void) {

    /* Init board hardware. */
    BOARD_ConfigMPU();
    BOARD_InitBootPins();
    BOARD_InitBootClocks();
    BOARD_InitBootPeripherals();
#ifndef BOARD_INIT_DEBUG_CONSOLE_PERIPHERAL
    /* Init FSL debug console. */
    BOARD_InitDebugConsole();
#endif

    gpio_pin_config_t USER_LED_config = {
        .direction = kGPIO_DigitalOutput,
        .outputLogic = 0U,
        .interruptMode = kGPIO_NoIntmode
    };
    /* Initialize GPIO functionality on GPIO_AD_B1_08 (pin 82) */
    GPIO_PinInit(GPIO1, 24U, &USER_LED_config);


    xTaskCreate(
        main_task,
        "main",
        2000,
        nullptr,
        2,
        nullptr );

    vTaskStartScheduler();


    return 0 ;
}

启动调度程序时,代码会立即因内存错误而崩溃。

有没有人有使用位置独立代码的经验并认识到这种行为?有小费吗?一些排除问题的好建议?

我可以分享任何与代码相关的内容。它只是在 mcuxpresso 中创建的一个标准的新 c++ 项目。如果有帮助,我什至可以在 github 上分享整个项目。

Help/pointers/tips 不胜感激!

freertos community 的帮助下我解决了这个问题。

最后我所要做的就是修改初始化堆栈的 freertos 端口,以在创建的堆栈上恢复 r9。

StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
                                 TaskFunction_t pxCode,
                                 void * pvParameters )
{
    /* Simulate the stack frame as it would be created by a context switch
     * interrupt. */

    /* Offset added to account for the way the MCU uses the stack on entry/exit
     * of interrupts, and to ensure alignment. */
    pxTopOfStack--;

    *pxTopOfStack = portINITIAL_XPSR;                                    /* xPSR */
    pxTopOfStack--;
    *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS;             /* LR */

    /* Save code space by skipping register initialisation. */
    pxTopOfStack -= 5;                            /* R12, R3, R2 and R1. */
    *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */

    /* A save method is being used that requires each task to maintain its
     * own exec return value. */
    pxTopOfStack--;
    *pxTopOfStack = portINITIAL_EXC_RETURN;

    pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
    
    // 
    // I added this part, as suggested by freertos members
    // 

    // Patched freertos for supporting -fpic: Set the task's initial R9 value
    __asm ("MOV %[result], R9"
        : [result] "=r" (pxTopOfStack[9-4])
      );


    return pxTopOfStack;
}