簡體   English   中英

MCU/嵌入式:與位置無關的代碼,.got 部分的最大大小?

[英]MCU/embedded: Position independent code, max size for .got section?

我試圖讓我的項目“獨立於職位”,但它不會給...

一些背景:

  • 恩智浦 imx rt 1024 evk 板
  • c++項目
  • 使用 -fPIC、-msingle-pic-base -mno-pic-data-is-text-relative 編譯 C 和 C++ 文件
  • 一個工作原型,我可以在其中運行一個小型演示 c++ 程序運行,它啟動一些 freertos 任務並創建一些靜態 c++ 對象(具有繼承,具有純虛擬類,以進行測試)
  • 強烈希望擁有 1 個二進制文件,我們可以通過讓客戶引導加載程序跳轉到 app1 或 app2 來“無線”(OTA)更新。

當我將更改應用到我的“真實”項目時,只要我注釋掉絕大多數 c++ 靜態構造函數,它就可以正常工作。

在此處輸入圖像描述

當我在 main.cpp 中包含一個(任何)更多構造函數時,將發生以下情況:

  • 我的引導加載程序將向量表從閃存(app1 或 app2)復制到 sram = OK
  • 我的引導加載程序跳轉到 0x202000004(復位處理程序 ISR 所在的 OC sram)= OK
  • ResetHandler將開始設置 R9(用於 .got 的寄存器)= OK
  • ResetHandler將跳轉到Startup = hard faults,檢查 CPU 中的寄存器,我可以看到 LR(鏈接寄存器)有一個虛假值(0xfffffff9),很明顯出了點問題。

我驗證了:

  • 反匯編的向量表,與 OC sram 中的向量表一對一匹配
  • 反匯編的 .got 部分與 DTC sram 中的 .got 一對一匹配。
  • 跳轉實際完成之前的Startup函數的地址。 它與 .got 部分中的條目匹配。

當我通過注釋掉一些東西來減少代碼量時,除了硬故障和 LR 中的損壞值之外,一切的行為都完全相同。

是否有一些(官方?!)文檔確認在為 ARM(Cortex m7)進行交叉編譯時對 .got 部分存在硬性限制?

有沒有人可以通過給出可能的提示來以任何方式做出貢獻?

作為參考,當 .got 大小達到“一些奇怪的閾值”時啟動代碼會彈出(我的假設當然可能是錯誤的)。

extern void Startup(unsigned int flash_start, unsigned int flash_end, unsigned int lma_offset);

extern unsigned int __flash_start__;
extern unsigned int __flash_end__;

extern unsigned int __global_offset_table_flash_start__;
extern unsigned int __global_offset_table_sram_start__;
extern unsigned int __global_offset_table_sram_end__;

//*****************************************************************************
// Reset entry point for your code.
// Sets up a simple runtime environment and initializes the C/C++
// library.
//*****************************************************************************
__attribute__ ((naked))
void ResetISR(void)
{
    __asm ("MOV R11, #1");

    // Disable interrupts
    __asm volatile ("cpsid i");

    unsigned int lma_offset;
    unsigned int *global_offset_table_flash_start;

    // Before doing anything else related to variables in sram, setup r9 for position independent code first.
    // And correct the firmware offset which is stored in r10 (add it to r9)
    // Finally grab the updated global offset table address from r9
    __asm volatile ("LDR r9, = __global_offset_table_flash_start__");
    __asm volatile ("ADD r9, r9, r10");

    __asm ("MOV %[result], R9"
        : [result] "=r" (global_offset_table_flash_start) );

    // Grab the lma offset defined in bootloader from r10
    __asm ("MOV %[result], R10"
        : [result] "=r" (lma_offset) );

    unsigned int flash_start = reinterpret_cast<unsigned int>(&__flash_start__);
    unsigned int flash_end = reinterpret_cast<unsigned int>(&__flash_end__);

    unsigned int *flash;
    unsigned int *sram;
    unsigned int *sram_end;

    __asm ("MOV R11, #2");

    //
    // Copy global offset table to sram
    //
    flash = const_cast<unsigned int*>(global_offset_table_flash_start);
    sram = const_cast<unsigned int*>(&__global_offset_table_sram_start__);
    sram_end = const_cast<unsigned int*>(&__global_offset_table_sram_end__);

    for (int i = 0u; i < (sram_end - sram); ++i)
    {
        sram[i] = flash[i];
        if (sram[i] >= flash_start && sram[i] <= flash_end)
        {
            sram[i] += lma_offset;
        }
    }

    // Update R9, as of now, all functions should be resolvable through the got
    __asm volatile ("LDR r9, = __global_offset_table_sram_start__");

    __asm ("MOV R11, #3");


    unsigned int address = reinterpret_cast<unsigned int>(&Startup);

    __asm__ volatile ("MOV R12, %[input]"
        : : [input] "r" (address)
          );

    // Jump to regular startup code
    Startup(flash_start, flash_end, lma_offset);
}

PS:我知道 -fPIC 在 linux 中被廣泛使用。 那里不存在這樣的限制。 也許這是 ARM 特定的東西,甚至是 CPU(皮質 m7)特定的東西)。 也許一些 Linux -fPIC 大師可能有一些想法可以幫助我……

PPS:如果我需要分享其他任何內容,請說...

我會將它保持打開狀態,作為為同樣事情苦苦掙扎的人們提供參考。 沒有依賴性。 沒有問題,除了你真正介紹的那些:我自己。

對我來說,主要問題是在重新定位時無法調試我的應用程序。 這可以通過發出 GDB 命令add-symbol-file <path-to-elf-file> <address-to-text-section>

例如:

  • 我的應用程序已編譯並鏈接到 0x60020000
  • 我的應用程序在閃存中上傳到 0x60030000(因此偏移量為 0x10000)
  • 當使用arm-none-eabi-readelf -WS myapp.axf讀取 elf 文件時,我可以讀到文本部分的偏移量為 0x2120。

當我在調試器中啟動引導加載程序時,在跳轉到重新定位的應用程序之前,我發出命令:

add-symbol-file myapp.axf 0x60032120

這會加載符號,gbd 會將 0x2120 的偏移量添加到 .text 部分中的所有符號。 這樣我就可以調試了。

一旦我的調試器運行起來,我就可以看到幾個編程錯誤。 最關鍵的是在 sram 中使用 .got 部分的基礎設置 r9讀取鏈接器符號。 我仍然在這些鏈接器符號中添加了 LMA 偏移量,而這會在幕后“自動”發生。 因此,在某些情況下,我正在讀取垃圾內存,並將其存儲在要由libc_init_array初始化的部分中。

修復這些后,我遇到了另一個奇怪的問題。 一個 nxp 驅動程序聲明了一個包含所有指向GPIO的指針的static const array 當我編譯源文件並通過arm-none-eabi-objdump將其拉出時,我可以看到 .text 中的數組,完美地設置了 GPIO1..GPIO5 的地址。 但是,在通過 objdump 再次鏈接和轉儲內容之后,我注意到同一個數組被更改了。 對 GPIO5 外設的引用以某種方式設置為 0x0。

現在,我不知道為什么會這樣,但我想如果我刪除const部分,那么數組將被映射到 sram,也許我可以擺脫這個問題。 我有一次很幸運,它解決了這個問題。 並不是一個完美的解決方案,因為現在我必須非常厭倦聲明static const內容的代碼。 我稍后會調查它,但現在,我很高興這個故事結束了。 我用 -fPIC 編譯了我的 c++ 應用程序,我可以在閃存中的任何位置(4 字節對齊)上運行它,最重要的是,我還可以通過它進行調試。

所以對於下一個在這個“位置無關代碼”旅程中發瘋的人:不要放棄,痛苦已經結束了;-)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM