程序編譯的幾個(gè)階段
一般而言,程序編譯經(jīng)歷下圖四個(gè)階段,鏈接是編譯的最后一步,無論是在PC上編譯代碼,還是在PC上使用嵌入式gcc工具交叉編譯嵌入式代碼,編譯過程都是如下幾步。深入理解鏈接過程是嵌入式工程師必要掌握的能力!
ld鏈接腳本的基礎(chǔ)概念
鏈接過程是將各式各樣的.o文件鏈接為一個(gè)文件的過程。鏈接腳本描述連接器如何將這些輸入文件(.o)文件映射為一個(gè)輸出文件的,并且定義了輸出文件的memory layout。幾乎所有的鏈接腳本都是在做這些事情。
下面給出一個(gè)簡單的鏈接腳本實(shí)例,每行腳本都有相應(yīng)的注解:
左右滑動(dòng)查看完整內(nèi)容
SECTIONS { . = 0x10000; .text : { *(.text) } . = 0x8000000; .bss : { *(.bss) } }
上面提到的定位計(jì)數(shù)器就是點(diǎn) ‘.’
這個(gè)鏈接腳本文件(Linker Scripty),用于告訴鏈接器如何將不同的代碼和數(shù)據(jù)段(sections)組合在一起形成可執(zhí)行文件。下面我會(huì)解釋其中的每一部分:
1. = 0x10000;
這行代碼重新設(shè)置了定位計(jì)數(shù)器(location counter)的值為0x10000,即地址0x10000。
它告訴鏈接器在此處開始分配.text段的地址空間。
2.text : { *(.text) }
這行代碼定義了一個(gè).text段,并告訴鏈接器將所有名為.text的數(shù)據(jù)節(jié)(section)放入這個(gè)段中。
*(.text)表示將所有輸入文件中的.text段合并到輸出文件的.text段中。
3 . = 0x8000000;
這行代碼重新設(shè)置了定位計(jì)數(shù)器的值為0x8000000,即地址0x8000000。
它告訴鏈接器在此處開始分配.data和.bss段的地址空間。
4 .data : { *(.data) }
這行代碼定義了一個(gè).data段,并告訴鏈接器將所有名為.data的數(shù)據(jù)節(jié)放入這個(gè)段中。
*(.data)表示將所有輸入文件中的.data段合并到輸出文件的.data段中。
5.bss : { *(.bss) }
這行代碼定義了一個(gè).bss段,并告訴鏈接器將所有名為.bss的數(shù)據(jù)節(jié)放入這個(gè)段中。
*(.bss)表示將所有輸入文件中的.bss段合并到輸出文件的.bss段中。
總體來說,這段鏈接腳本告訴鏈接器在特定的地址處分配.text、.data和.bss段,并將對應(yīng)的數(shù)據(jù)節(jié)合并到這些段中。
鏈接腳本相關(guān)的概念
內(nèi)存(Memory)
左右滑動(dòng)查看完整內(nèi)容
MEMORY { name [(attr)] : ORIGIN = origin, LENGTH = len … }
注解:這里的“attr”只能由以下特性組成:
‘R’ Read-only section
‘W’ -- Read/write section
‘X’ -- Executable section
‘A’ -- Allocatable section
‘I’ -- Initialized section
‘L’ -- Same as ‘I’
‘!’ -- Invert the sense of any of the attributes that follow
左右滑動(dòng)查看完整內(nèi)容
/* Memories definition */ MEMORY { RAM (xrw) : ORIGIN = 0x20000300, LENGTH = 36K FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K }
注解:
“xrw”表示“RAM”區(qū)是可讀、可寫和可執(zhí)行的,且RAM 的起始地址為“0x20000300”,長度為36K。
“rx”表示“FLASH”區(qū)是可讀和可執(zhí)行的,F(xiàn)LASH的起始地址為“0x08000000”,長度為128K。
段(Section)
Section有l(wèi)oadable(可加載)和allocatable(可分配)兩種類型。不可加載也不可分配的內(nèi)存段,通常包含某些調(diào)試信息。
loadable(可加載)是指:程序運(yùn)行時(shí),該段內(nèi)容應(yīng)該被加載到內(nèi)存中。
allocatable(可分配)是指:該段的內(nèi)容應(yīng)該被預(yù)留出,但不應(yīng)該加載任何別的內(nèi)容(某些情況下,這些內(nèi)存必須歸零)。
“可加載”和“可分配”的section都有兩個(gè)地址:“VMA”和“LMA”。
VMA(the virtual memory address):這是運(yùn)行輸出文件時(shí),該section的地址。VMA是可選項(xiàng),可以不設(shè)置。
LMA(load memory address):這是加載section時(shí)的地址。
在大多數(shù)情況下,這兩個(gè)地址是相同的。當(dāng)然也可以不相等,比如下面的例子就是LMA和VMA不同的案例:
數(shù)據(jù)段被加載到ROM中,然后在程序啟動(dòng)時(shí)復(fù)制到RAM中(通常用于初始化全局變量)。此時(shí)ROM地址就是LMA,RAM地址就是VMA。
語法:
左右滑動(dòng)查看完整內(nèi)容
SECTIONS { section [address] [(type)] : { [AT(lma)] [ALIGN(section_align) | ALIGN_WITH_INPUT] [SUBALIGN(subsection_align)] [constraint] { output-section-command output-section-command … } [>region] [AT>lma_region] [:phdr :phdr ...] [=fillexp] [,] ... }
大多數(shù)的段僅使用了上述的一部分屬性。
示例:
左右滑動(dòng)查看完整內(nèi)容
/* Sections */ SECTIONS { /* The startup code into "FLASH" Rom type memory */ .isr_vector : { . = ALIGN(4); KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(4); } >FLASH /* Initialized data sections into "RAM" Ram type memory */ .data: { . = ALIGN(4); _sdata = .; /* create a global symbol at data start */ *(.data) /* .data sections */ *(.data*) /* .data* sections */ . = ALIGN(4); _edata = .; /* define a global symbol at data end */ } >RAM AT> FLASH }
上述示例中“.isr_vector”的LMA與VMA是相等的?!?data”因?yàn)橛小?RAM AT> FLASH”的修飾,表示.data段的VMA為RAM,LMA為FLASH。即.data段的內(nèi)容會(huì)放在FLASH中,但是運(yùn)行時(shí),會(huì)加載到RAM中。
常用命令
ASSERT
語法:ASSERT(exp, message)
確保exp是非零值,如果為零,將以錯(cuò)誤碼的形式退出鏈接文件,并輸出message。在必要的位置添加斷言,可以清晰的定位問題。
左右滑動(dòng)查看完整內(nèi)容
/* The usage of ASSERT */ .test : { ASSERT ((_estack > (_Min_Stack_Size + _Min_Heap_Size)),"Error: There is an ERR occurred"); }
當(dāng)示例中的“_estack”大于“_Min_Stack_Size + _Min_Heap_Size”時(shí),就會(huì)打印“There is an ERR occurred”。
KEEP
用途:當(dāng)鏈接器使用('--gc-sections')進(jìn)行垃圾回收時(shí),KEEP()可以使得被標(biāo)記段的內(nèi)容不被清除。
左右滑動(dòng)查看完整內(nèi)容
/* The startup code into "FLASH" Rom type memory */ .isr_vector : { . = ALIGN(4); KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(4); } >FLASH
指定“變量”的輸出地址:
可以定義如下的memory,然后將“變量”存放于該memory,就能控制“變量”的輸出地址。
左右滑動(dòng)查看完整內(nèi)容
/* Memories definition */ MEMORY { FW_RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 0x300 /* 0x20000000 ~ 0x200002FF */ RAM (xrw) : ORIGIN = 0x20000300, LENGTH = 35K FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K }
同時(shí)在c文件中,在定義“變量”時(shí),添加如下對應(yīng)的屬性:
__attribute__((section(".FW_RAM"))) uint8_t key[8] = {0,1,2,3,4,5,6,7 };
變量將位于“0x20000000 ~ 0x200002FF”區(qū)域(如果僅僅只有key數(shù)組位于該區(qū)域,將從0x20000000開始存放,如果有多個(gè)變量存儲于該區(qū)域,將按照編譯的順序,從0x20000000依次存放)。
指定“函數(shù)”的輸出地址:
可以定義如下的memory和section,然后將“函數(shù)”存放于該section,就能控制“函數(shù)”的輸出地址。
左右滑動(dòng)查看完整內(nèi)容
/* Memories definition */ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 0x300 /* 0x08000000 ~ 0x080002FF */ CG_FLASH (rx) : ORIGIN = 0x08000300, LENGTH = 0x134 /* 0x08000300 ~ 0x08000433 */ RAM (xrw) : ORIGIN = 0x20000300, LENGTH = 0x900 /* 0x20000300 ~ 0x20001FFF */ } SECTIONS { … .SE_Call_Fun: { . = ALIGN(4); . = . + 0x4; *(.SE_Call_Fun) . = ALIGN(4); } > CG_FLASH … }
同時(shí)在c文件中, 在“函數(shù)”的實(shí)現(xiàn)部分,添加如下對應(yīng)的屬性:
__attribute__((section(".SE_Call_Fun"))) uint32_t call_fun(Callgate_Func_Type_t ftype, void *param)
函數(shù)“call_fun”將存放于0x08000304處(留意此處的位置計(jì)數(shù)器將產(chǎn)生0x04的內(nèi)存間隙)。
指定“文件”輸出地址:
可以定義如下的memory和section,然后將指定的文件存放于該section,就能控制“文件”的輸出地址。
左右滑動(dòng)查看完整內(nèi)容
/* Memories definition */ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 0x300 /* 0x08000000 ~ 0x080002FF */ FW_FLASH (rx) : ORIGIN = 0x08000434, LENGTH = 0x2BCC/* 0x08000434 ~ 0x08003000 */ RAM (xrw) : ORIGIN = 0x20000300, LENGTH = 0x900 /* 0x20000300 ~ 0x20001FFF */ } /* Sections */ SECTIONS { … .main_section : { . = ALIGN(4); Core/Src/main.o(.text*) . = ALIGN(4); } >FLASH … }
示例中將main.o指定到FLASH區(qū)域中;更改FLASH的地址或者main_section的LMA,就可以實(shí)現(xiàn)將特定文件指定到特定內(nèi)存區(qū)域。
案例:RZ/N2L把 .text, .data, .bss段從ATCM改到SYSTEM_RAM
這里描述的RZ/N2L的內(nèi)存分配:
左右滑動(dòng)查看完整內(nèi)容
長按可保存查看大圖
把.text段從ATCM改到SYSTEM_RAM:
左右滑動(dòng)查看完整內(nèi)容
長按可保存查看大圖
把.data段從ATCM改到SYSTEM_RAM:
左右滑動(dòng)查看完整內(nèi)容
長按可保存查看大圖
.bss段的改動(dòng)也是類似的:
左右滑動(dòng)查看完整內(nèi)容
長按可保存查看大圖
-
嵌入式
+關(guān)注
關(guān)注
5082文章
19126瀏覽量
305299 -
程序
+關(guān)注
關(guān)注
117文章
3787瀏覽量
81060 -
腳本
+關(guān)注
關(guān)注
1文章
389瀏覽量
14866
原文標(biāo)題:e2 studio中鏈接腳本的修改指導(dǎo)(通用)
文章出處:【微信號:瑞薩MCU小百科,微信公眾號:瑞薩MCU小百科】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論