ARM GCC中的Link File

所謂的Link File是指在GCC鏈接階段用到的一個(gè)用于表示MCU內(nèi)存分布的文件,這個(gè)文件是GCC連接器的一個(gè)輸入文件,不同的鏈接文件會(huì)有不同的結(jié)果,比如我們常見(jiàn)的下載到RAM或者Flash中,這兩種不同的地址就對(duì)應(yīng)不同的連接文件。本篇文章我們就對(duì)鏈接文件做一個(gè)簡(jiǎn)單的描述。

連接文件

連接文件的基本概念

在GCC中,連接文件擴(kuò)展名一般是.ld, 是編譯器鏈接階段的輸入文件,他主要用來(lái)表示我們編譯的程序在目標(biāo)系統(tǒng)里面的存儲(chǔ)方式。在解釋這個(gè)概念之前我們有必要在回顧一下程序編譯的具體過(guò)程。

很早之前我們就知道程序編譯要經(jīng)過(guò)編譯和鏈接兩個(gè)階段,但是現(xiàn)在各種GUI工具的盛行,很多時(shí)候我們都是直接點(diǎn)一個(gè)按鈕就搞定了,工具實(shí)際在后臺(tái)做了什么操作我們并不是特別清楚。實(shí)際上編譯器的編譯主要經(jīng)過(guò)的兩個(gè)階段主要做的事情如下:

  • 預(yù)處理階段:這個(gè)階段編譯器會(huì)對(duì)我們的代碼進(jìn)行預(yù)處理,主要就是進(jìn)行宏的替換和注釋的刪除,這樣我們就會(huì)得到比較干脆的代碼,很多編譯器不在單獨(dú)列出這個(gè)預(yù)處理階段,而是直接和編譯過(guò)程綜合在一起。

  • 編譯階段:這個(gè)階段主要將我們的C語(yǔ)言編譯成機(jī)器語(yǔ)言,生成的目標(biāo)文件是.o的object文件和.lst的連接文件,這個(gè)連接文件和我們今天要講的連接文件有區(qū)別,他類(lèi)似于機(jī)器語(yǔ)言,但是里面的地址信息都是符號(hào)和相對(duì)地址,而不是真正的地址。編譯階段是針對(duì)源代碼中的C文件的,每個(gè)C文件都會(huì)單獨(dú)編譯,所以編譯器也沒(méi)法獲得絕對(duì)地址。

  • 連接階段:這個(gè)階段生成最終的可執(zhí)行程序,這個(gè)階段的輸入文件主要有我們今天要講的ld鏈接文件和編譯階段生成的所有.o文件,連接過(guò)程因?yàn)榈玫搅怂械某绦蛐畔?,所以它?huì)根據(jù)ld文件中的地址信息安排代碼在處理器中的存放方式,比如代碼放到什么位置,變量放到什么位置等等。

連接文件中比較重要的幾個(gè)部分

連接文件中內(nèi)存的分布是按照region來(lái)分布的:

MEMORY
{
  VECTORS (rwx)      : ORIGIN = 0x0,         LENGTH = 0x0410
  ITCM (rwx)         : ORIGIN = 0x00000410,  LENGTH = 128K- 0x410
  DTCM (rwx)         : ORIGIN = 0x20000000,  LENGTH = 128K
}
ENTRY(_reset_init)
Heap_Size = 4K;
Stack_Size =41K;

SECTIONS
{
    /* The startup code goes first into INTERNAL_FLASH */
    .isr_vector :
    {
        __vector_table = .;
        . = ALIGN(4);
        KEEP(*(.isr_vector))
        . = ALIGN(4);
    } > VECTORS


    .text :
    {
        *(.text)                 /* .text sections (code) */
        *(.text*)                /* .text* sections (code) */
        *(.rodata)               /* .rodata sections (constants, strings, etc.) */
        *(.rodata*)              /* .rodata* sections (constants, strings, etc.) */
        *(.srodata .srodata.*)
        *(.glue_7)               /* glue arm to thumb code */
        *(.glue_7t)              /* glue thumb to arm code */
        *(.eh_frame)
        *(.init)
        *(.fini)
    } > ITCM


    .data :
    {
        . = ALIGN(4);
        __DATA_RAM = .;
        __data_start__ = .;      /* create a global symbol at data start */
        __etext = .;
        *(.data)                 /* .data sections */
        *(.data*)                /* .data* sections */
        *(.sdata .sdata.*)
        *(.heapsram*)            /* This is only for the pulpino official test code. */
        __noncachedata_start__ = .;   /* create a global symbol at ncache data start */
        *(NonCacheable)
        __noncachedata_end__ = .;     /* define a global symbol at ncache data end */
        KEEP(*(.jcr*))
        . = ALIGN(4);
        __data_end__ = .;        /* define a global symbol at data end */
    } > DTCM

    .bss :
    {
        __bss_start__ = .;
        *(.bss*)
        *(COMMON)
        __bss_end__ = .;
    } > DTCM

    .heap :
    {
        __end__ = .;
        end = __end__;
        __heap_start = .;
        . = + Heap_Size;
        . = ALIGN(4);
        __heap_end = .;
    } > DTCM

    /* Set stack top to end of RAM */
    __StackTop = ORIGIN(DTCM) + LENGTH(DTCM) - 8;
    __StackLimit = __StackTop - Stack_Size;
    PROVIDE(__stack = __StackTop);

    /* Check if data + heap + stack exceeds RAM limit */
    ASSERT(__StackLimit >= __heap_end, "region RAM overflowed with stack")

}

上面是一個(gè)簡(jiǎn)單的RAM中分布的連接文件,可以看到我們主要將內(nèi)存分成了VECTORS, ITCM, DTCM三個(gè)region,這三個(gè)部分中分別存放了不同的內(nèi)容,比如*(.text*)就是代碼段, *(.data*)就是內(nèi)存中的變量,這些變量一般是需要初始化的全局變量,如果程序不是直接下載到RAM中而是Flash中,我們還需要通過(guò)AT關(guān)鍵字進(jìn)行地址重定義。*(.bss*)是為初始化的全局變量,這些我們一般會(huì)在程序初始化的時(shí)候后將他們初始化為全零。

堆和棧的定義是直接采用定義地址偏移和定義符號(hào)來(lái)實(shí)現(xiàn)的,我們直接在用到的DATA段和BSS段之后指定了Heap_Size大小的空間作為堆空間,這里可以通過(guò)ALIGN關(guān)鍵字進(jìn)行地址對(duì)齊。棧則是直接將后面的剩余空間直接利用,最后會(huì)有一個(gè)判斷,用來(lái)計(jì)算最終地址是否溢出。

地址重定義

連接文件中還有一個(gè)比較關(guān)鍵的AT關(guān)鍵字這里沒(méi)有體現(xiàn),這個(gè)關(guān)鍵字主要是實(shí)現(xiàn)一個(gè)地址映射的功能,我們知道變量我們一般是放到RAM中去的,對(duì)于一些有初始值的變量我們不能簡(jiǎn)單的放到RAM中,因?yàn)閷?duì)于MCU而言,我們不可能每次都去下載程序之后在運(yùn)行,當(dāng)程序從flash直接啟動(dòng)的時(shí)候,RAM數(shù)據(jù)并不會(huì)自動(dòng)初始化,常用的方法是將這些RAM變量的初始值放到flash中去,在程序啟動(dòng)的時(shí)候手動(dòng)將這些值初始化到RAM中,AT關(guān)鍵字就是實(shí)現(xiàn)這個(gè)功能的,它的作用就是告訴連接器某個(gè)段連接的時(shí)候放到哪個(gè)地址,運(yùn)行的時(shí)候在哪個(gè)地址運(yùn)行。

好了就醬吧,該去吃早飯了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容