IMPORT |Image$$RO$$Limit|
IMPORT |Image$$RW$$Base|
IMPORT |Image$$ZI$$Base|
IMPORT |Image$$ZI$$Limit|
IMPORT |Image$$RW$$Limit|
從ADS1.2的幫助文檔可以看出以下幾個symbol的定義應該是:
|Image$$RO$$Base|
表示RO輸出段運行時起始地址,也可以說是程序代碼存放的起始地址,由-ro-base這個參數指定;
|Image$$RO$$Limit|
表示RO輸出段運行時存儲區域界限,其值可通過|Image$$RO$$Base|+Code sizes+RO Data sizes+4計算得出;
|Image$$RW$$Base|
表示RW輸出段運行時起始地址,記得是運行時的地址,而不一定是加載時的存放地址,因爲RW輸出段在加載時可能是在ROM中並緊跟着RO輸出段存放的,當程序運行時才移動(在有些書中說是移動,個人覺得應該只是複製,希望大家能幫我解釋一下,謝謝!)到RAM起始地址爲|Image$$RW$$Base|的區域,由-rw-base這個參數指定;未指定的話,默認緊跟RO輸出段,那麼|Image$$RW$$Base|=|Image$$RO$$Limit|;
|Image$$RW$$Limit|
表示RW輸出段運行時存儲區域界限,其值可通過|Image$$RW$$Base|+RW Data sizes+4計算得出;
|Image$$ZI$$Base|
表示ZI輸出段運行時起始地址,它是運行時在RAM中生成的,緊跟着RW輸出段存放,其值和|Image$$RW$$Limit|一樣;
|Image$$ZI$$Limit|
表示ZI輸出段運行時存儲區域界限,其值可通過|Image$$ZI$$Base|+ZI Data sizes+4計算得出。
RO和RW還有ZI代表什麼?
一般而言,一個程序包括只讀的代碼段和可讀寫的數據段。在ARM的集成開發環境中,只讀的代碼段和常量被稱作RO段(ReadOnly);可讀寫的全局變量和靜態變量被稱作RW段(ReadWrite);RW段中要被初始化爲零的變量被稱爲ZI段(ZeroInit)。對於嵌入式系統而言,程序映象都是存儲在Flash存儲器等一些非易失性器件中的,而在運行時,程序中的RW段必須重新裝載到可讀寫的RAM中。這就涉及到程序的加載時域和運行時域。簡單來說,程序的加載時域就是指程序燒入Flash中的狀態,運行時域是指程序執行時的狀態。對於比較簡單的情況,可以在ADS集成開發環境的ARM LINKER選項中指定RO BASE和RW BASE,告知連接器RO和RW的連接基地址。對於複雜情況,如RO段被分成幾部分並映射到存儲空間的多個地方時,需要創建一個稱爲“分佈裝載描述文件”的文本文件,通知連接器把程序的某一部分連接在存儲器的某個地址空間。需要指出的是,分佈裝載描述文件中的定義要按照系統重定向後的存儲器分佈情況進行。在引導程序完成初始化的任務後,應該把主程序轉移到RAM中去運行,以加快系統的運行速度。
什麼是arm的映像文件,arm映像文件其實就是可執行文件,包括bin或hex兩種格式,可以直接燒到rom裏執行。在axd調試過程中,我們調試的是axf文件,其實這也是一種映像文件,它只是在bin文件中加了一個文件頭和一些調試信息。映像文件一般由域組成,域最多由三個輸出段組成(RO,RW,ZI)組成,輸出段又由輸入段組成。所謂域,指的就是整個bin映像文件所處在的區域,它又分爲加載域和運行域。加載域就是映像文件被靜態存放的工作區域,一般來說flash裏的 整個bin文件所在的地址空間就是加載域,當然在程序一般都不會放在 flash裏執行,一般都會搬到sdram裏運行工作,它們在被搬到sdram裏工作所處的地址空間就是運行域。我們輸入的代碼,一般有代碼部分和數據部分,這就是所謂的輸入段,經過編譯後就變成了bin文件中ro段和rw段,還有所謂的zi段,這就是輸出段。對於加載域中的輸出段,一般來說ro段後面緊跟着rw段,rw段後面緊跟着zi段。在運行域中這些輸出段並不連續,但rw和zi一定是連着的。zi段和rw段中的數據其實可以是rw屬性。
| Image$$RO$$Base| |Image$$RO$$Limit| |Image$$RW$$Base| |Image$$ZI$$Base| |Image$$ZI$$Limit|這幾個變量是編譯器通知的,我們在 makefile文件中可以看到它們的值。它們指示了在運行域中各個輸出段所處的地址空間| Image$$RO$$Base| 就是ro段在運行域中的起始地址,|Image$$RO$$Limit| 是ro段在運行域中的截止地址。其它依次類推。我們可以在linker的output中指定,在 simple模式中,ro base對應的就是| Image$$RO$$Base|,rw base 對應的是|Image$$RW$$Base|,由於rw和zi相連,|Image$$ZI$$Base| 就等於|Image$$ZI$$limit| .其它的值都是編譯器自動計算出來的。
下面是2410啓動代碼的搬運部分,我給出註釋
BaseOfROM DCD |Image$$RO$$Base|
TopOfROM DCD |Image$$RO$$Limit|
BaseOfBSS DCD |Image$$RW$$Base|
BaseOfZero DCD |Image$$ZI$$Base|
EndOfBSS DCD |Image$$ZI$$Limit|
adr r0, ResetEntry; ResetEntry是復位運行時域的起始地址,在boot
nand中一般是0
ldr r2, BaseOfROM;
cmp r0, r2
ldreq r0, TopOfROM;TopOfROM=0x30001de0,代碼段地址的結束
beq InitRam
ldr r3, TopOfROM
;part 1,通過比較,將ro搬到sdram裏,搬到的目的地址從 | Image$$RO$$Base| 開始,到|Image$$RO$$Limit|結束
0
ldmia r0!, {r4-r7}
stmia r2!, {r4-r7}
cmp r2, r3
bcc %B0;
;part 2,搬rw段到sdram,目的地址從|Image$$RW$$Base| 開始,到|Image$$ZI$$Base|結束
sub r2, r2, r3;r2=0
sub r0, r0, r2
InitRam ;carry rw to baseofBSS
ldr r2, BaseOfBSS ;TopOfROM=0x30001de0,baseofrw
ldr r3, BaseOfZero ;BaseOfZero=0x30001de0
0
cmp r2, r3
ldrcc r1, [r0], #4
strcc r1, [r2], #4
bcc %B0
;part 3,將sdram zi初始化爲0,地址從|Image$$ZI$$Base|到|Image$$ZI$$Limit|
mov r0, #0;init 0
ldr r3, EndOfBSS;EndOfBSS=30001e40
1
cmp r2, r3
strcc r0, [r2], #4
bcc %B1
**************************************************************************************************
RO段、RW段和ZI段 --Image$$??$$Limit 含義
一直以來對於ARM體系中所描述的RO,RW和ZI數據存在似是而非的理解,這段時間對其仔細瞭解了一番,發現了一些規律,理解了一些以前書本上有的但是不理解的東西,我想應該有不少人也有和我同樣的困惑,因此將我的一些關於RO,RW和ZI的理解寫出來,希望能對大家有所幫助。
要了解RO,RW和ZI需要首先了解以下知識:
ARM程序的組成
此處所說的“ARM程序”是指在ARM系統中正在執行的程序,而非保存在ROM中的bin映像(image)文件,這一點清注意區別。
一個ARM程序包含3部分:RO,RW和ZI
RO是程序中的指令和常量
RW是程序中的已初始化變量
ZI是程序中的未初始化的變量
由以上3點說明可以理解爲:
RO就是readonly,
RW就是read/write,
ZI就是zero
ARM映像文件的組成
所謂ARM映像文件就是指燒錄到ROM中的bin文件,也成爲image文件。以下用Image文件來稱呼它。
Image文件包含了RO和RW數據。
之所以Image文件不包含ZI數據,是因爲ZI數據都是0,沒必要包含,只要程序運行之前將ZI數據所在的區域一律清零即可。包含進去反而浪費存儲空間。
Q:爲什麼Image中必須包含RO和RW?
A:因爲RO中的指令和常量以及RW中初始化過的變量是不能像ZI那樣“無中生有”的。
ARM程序的執行過程
從以上兩點可以知道,燒錄到ROM中的image文件與實際運行時的ARM程序之間並不是完全一樣的。因此就有必要了解ARM程序是如何從ROM中的image到達實際運行狀態的。
實際上,ROM中的指令至少應該有這樣的功能:
1. 將RW從ROM中搬到RAM中,因爲RW是變量,變量不能存在ROM中。
2. 將ZI所在的RAM區域全部清零,因爲ZI區域並不在Image中,所以需要程序根據編譯器給出的ZI地址及大小來將相應得RAM區域清零。ZI中也是變量,同理:變量不能存在ROM中
在程序運行的最初階段,RO中的指令完成了這兩項工作後C程序才能正常訪問變量。否則只能運行不含變量的代碼。
**************************************************************************************************
下面我將給出幾個例子,最直觀的來說明RO,RW,ZI在C中是什麼意思。
1; RO
看下面兩段程序,他們之間差了一條語句,這條語句就是聲明一個字符常量。因此按照我們之前說的,他們之間應該只會在RO數據中相差一個字節(字符常量爲1字節)。
Prog1:
#i nclude <stdio.h>
void main(void)
{
;
}
Prog2:
#i nclude <stdio.h>
const char a = 5;
void main(void)
{
;
}
Prog1編譯出來後的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
Prog2編譯出來後的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 61 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1009 ( 0.99kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)
================================================================================
以上兩個程序編譯出來後的信息可以看出:
Prog1和Prog2的RO包含了Code和RO Data兩類數據。他們的唯一區別就是Prog2的RO Data比Prog1多了1個字節。這正和之前的推測一致。
如果增加的是一條指令而不是一個常量,則結果應該是Code數據大小有差別。
2; RW
同樣再看兩個程序,他們之間只相差一個“已初始化的變量”,按照之前所講的,已初始化的變量應該是算在RW中的,所以兩個程序之間應該是RW大小有區別。
Prog3:
#i nclude <stdio.h>
void main(void)
{
;
}
Prog4:
#i nclude <stdio.h>
char a = 5;
void main(void)
{
;
}
Prog3編譯出來後的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
Prog4編譯出來後的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 1 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)
================================================================================
可以看出Prog3和Prog4之間確實只有RW Data之間相差了1個字節,這個字節正是被初始化過的一個字符型變量“a”所引起的。
3; ZI
再看兩個程序,他們之間的差別是一個未初始化的變量“a”,從之前的瞭解中,應該可以推測,這兩個程序之間應該只有ZI大小有差別。
Prog3:
#i nclude <stdio.h>
void main(void)
{
;
}
Prog4:
#i nclude <stdio.h>
char a;
void main(void)
{
;
}
Prog3編譯出來後的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
Prog4編譯出來後的信息如下:
================================================================================
Code RO Data RW Data ZI Data Debug
948 60 0 97 0 Grand Totals
================================================================================
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)
================================================================================
編譯的結果完全符合推測,只有ZI數據相差了1個字節。這個字節正是未初始化的一個字符型變量“a”所引起的。
注意:如果一個變量被初始化爲0,則該變量的處理方法與未初始化華變量一樣放在ZI區域。
即:ARM C程序中,所有的未初始化變量都會被自動初始化爲0。
總結:
1; C中的指令以及常量被編譯後是RO類型數據。
2; C中的未被初始化或初始化爲0的變量編譯後是ZI類型數據。
3; C中的已被初始化成非0值的變量編譯後市RW類型數據。
附:
程序的編譯命令(假定C程序名爲tst.c):
armcc -c -o tst.o tst.c
armlink -noremove -elf -nodebug -info totals -info sizes -map -list aa.map -o tst.elf tst.o
編譯後的信息就在aa.map文件中。
ROM主要指:NAND Flash,Nor Flash
RAM主要指:PSRAM,SDRAM,SRAM,DDRAM
**************************************************************************************************
Image$$??$$Limit 的含義
對於剛學習ARM的人來說,如果分析它的啓動代碼,往往不明白下面幾個變量的含義:|Image$$RO$$Limit|、|Image$$RW$$Base|、|Image$$ZI$$Base|。
當把程序編寫好以後,就要進行編譯和鏈接了,在ADS1.2中選擇MAKE按鈕,會出現一個Errors and Warnings 的對話框,在該欄中顯示編譯和鏈接的結果,如果沒有錯誤,在文件的最後應該能看到Image component sizes,後面緊跟的依次是Code,RO Data ,RW Data ,ZI Data ,Debug 各個項目的字節數,最後會有他們的一個統計數據:
Code 163632 ,RO Data 20939 ,RW Data 53 ,ZI Data 17028
Tatal RO size (Code+ RO Data) 184571 (180.25kB)
Tatal RW size(RW Data+ ZI Data) 17081(16.68 kB)
Tatal ROM size(Code+ RO Data+ RW Data) 184624(180.30 kB)
後面的字節數是根據用戶不同的程序而來的,下面就以上面的數據爲例來介紹那幾個變量的計算。
在ADS的Debug Settings中有一欄是Linker/ARM Linker,在output選項中有一個RO base選項,
假如
RO base設置爲0x0c100000,後面的RW base 設置爲0x0c200000,然後在Options選項中有Image entry point ,是一個初始程序的入口地址,設置爲0x0c100000 。
有了上面這些信息我們就可以完全知道這幾個變量是怎麼來的了:
|Image$$RO$$Base| = Image entry point =RO base =0x0c100000 ;表示程序代碼存放的起始地址
|Image$$RO$$Limit|=程序代碼起始地址+代碼長度+1=0x0c100000+Tatal RO size+1
= 0x0c100000 + 184571 + 1 = 0x0c100000 +0x2D0FB + 1
= 0x0c12d0fc
|Image$$RW$$Base| = 0x0c200000=RW base 地址指定
|Image$$RW$$Limit| =|Image$$RW$$Base|+ RW Data 53 = 0x0c200000+0x37(4的倍數,0到55,共56個單元)
=0x0c200037
|Image$$ZI$$Base| = |Image$$RW$$Limit| + 1 =0x0c200038
|Image$$ZI$$Limit| = |Image$$ZI$$Base| + ZI Data 17028
=0x0c200038 + 0x4284
=0x0c2042bc
也可以由此計算:
|Image$$ZI$$Limit| = |Image$$RW$$Base| +TatalRWsize(RWData+ZIData) 17081
=0x0c200000+0x42b9+3(要滿足4的倍數)
=0x0c2042bc
**************************************************************************************************
Part1 簡介
一 概述
Scatter file (分散加載描述文件)用於armlink的輸入參數,他指定映像文件內部各區域的download與運行時位置。Armlink將會根據scatter file生成一些區域相關的符號,他們是全局的供用戶建立運行時環境時使用。(注意:當使用了scatter file 時將不會生成以下符號 Image$$RW$$Base, Image$$RW$$Limit, Image$$RO$$Base, Image$$RO$$Limit, Image$$ZI$$Base, and Image$$ZI$$Limit)
二 什麼時候使用scatter file
當然首要的條件是你在利用ADS進行項目開發,下面我們看看更具體的一些情況。
1 存在複雜的地址映射:例如代碼和數據需要分開放在在多個區域。
2 存在多種存儲器類型:例如包含 Flash,ROM,SDRAM,快速SRAM。我們根據代碼與數據的特性把他們放在不同的存儲器中,比如中斷處理部分放在快速SRAM內部來提高響應速度,而把不常用到的代碼放到速度比較慢的Flash內。
3 函數的地址固定定位:可以利用Scatter file實現把某個函數放在固定地址,而不管其應用程序是否已經改變或重新編譯。
4 利用符號確定堆與堆棧:
5 內存映射的IO:採用scatter file可以實現把某個數據段放在精確的地指處。
因此對於嵌入式系統來說scatter file是必不可少的,因爲嵌入式系統採用了ROM,RAM,和內存映射的IO。
三 scatter file 實例
1 簡單的內存映射
LOAD_ROM 0x0000 0x8000
{
EXEC_ROM 0x0000 0x8000
{
*( RO)
}
RAM 0x10000 0x6000
{
*( RW, ZI)
}
}
LOAD_ROM(下載區域名稱) 0x0000(下載區域起始地址) 0x8000(下載區域最大字節數)
{
EXEC_ROM(第一執行區域名稱) 0x0000(第一執行區域起始地址) 0x8000(第一執行區域最大字節數)
{
*( RO(代碼與只讀數據))
}
RAM(第二執行區域名稱) 0x10000(第二執行區域起始地址) 0x6000(第二執行區域最大字節數)
{
*( RW(讀寫變量), ZI(未初始化變量))
}
}
2 複雜內存映射
LOAD_ROM_1 0x0000
{
EXEC_ROM_1 0x0000
{
program1.o( RO)
}
DRAM 0x18000 0x8000
{
program1.o ( RW, ZI)
}
}
LOAD_ROM_2 0x4000
{
EXEC_ROM_2 0x4000
{
program2.o( RO)
}
SRAM 0x8000 0x8000
{
program2.o ( RW, ZI)
}
}
LOAD_ROM_1 0x0000(下載區域一起始地址)
{
EXEC_ROM_1 0x0000(第一執行區域開始地址)
{
program1.o( RO) (program1.o內的Code與RO data 放在第一執行區域)
}
DRAM 0x18000(第二執行區域開始地址) 0x8000(第二執行區域最大字節數)
{
program1.o ( RW, ZI) (program1.o內的RW data與 ZI data 放在第二執行區域)
}
}
LOAD_ROM_2 0x4000(下載區域二起始地址)
{
EXEC_ROM_2 0x4000
{
program2.o( RO) (program2.o內的Code與RO data 放在第一執行區域)
}
SRAM 0x8000 0x8000
{
program2.o ( RW, ZI) (program2.o內的RW data與 ZI data 放在第二執行區域)
}
}
Part2 基本語法
2.1 BNF 符號與語法
" :由引號賴標示的符號保持其字面原意,如A” ”B標示A B。
A ::= B :定義A爲B。
[A] :標示可選部分,如A[B]C用來標示ABC或AC。
A :用來標示A可以重複任意次,如A 可標示A,AA,AAA, …
A* :同A 。
A | B :用來標示選擇其一,不能全選。如A|B用來標示A或者B。
(A B) :標示一個整體,當和|符號或複雜符號的多次重複一起使用時尤其強大,如(AB) (C|D)標示ABC,ABD,ABABC,ABABD, …
2.2 分散加載文件各部分描述
(2.1)
如圖2.1所示爲一個完整的分散加載腳本描述結構圖。下面我們對圖示中各個部分進行講述。
2.2.1 加載區描述
每個加載區有:
ó名稱:供連接器確定不同下載區域
ó基地址:相對或絕對地址
ó屬性:可選
ó最大字節數:可選
ó執行區域列:確定執行時各執行區域的類型與位置
load_region_name (base_address | (" " offset)) [attribute_list] [ max_size ]
"{"
execution_region_deion
"}"
load_region_name:下載區域名稱,最大有效字符數31。(並不像執行區域段名用於Load$$region_name,而是僅僅用於標示下載區域)。
base_address:本區域內部目標被連接到的地址(按字對齊)。
offset:相對前一個下載區域的偏移量(4的整數倍,如果爲第一個區域)。
2.2.2 執行區描述
每個執行區有:
ó名稱:供連接器確定不同下載區域
ó基地址:相對或絕對地址
ó屬性:確定執行區域的屬性
ó最大字節數:可選
ó輸入段:確定放在該執行區域的模塊
exec_region_name (base_address | " " offset) [attribute_list] [max_size]
"{"
input_section_deion
"}"
exec_region_name:執行區域名稱,最大有效字符數31。
base_address:本執行區域目標要被聯接到的位置,按字對齊。
offset:相對於前一個執行區域結束地址的偏移量,4的整數倍;如果沒有前繼之能夠行區域(本執行區域爲該下載區域的第一個執行區域),則該偏移量是相對於該下載區域的基址偏移量。
attribute_list:PI,OVERLAY,ABSOLUTE,FIXED,UNINIT。
PI: 位置獨立。
OVERLAY: 覆蓋。
ABSOLUTE: 絕對地址。
FIXED: 固定地址,下載地址與執行地址具有該地址指示確定。
UNINIT: 未初始化數據。
RELOC:無法明確指定執行區域具有該屬性,而只能通過繼承前一個執行區或父區域獲得。
對於PI,OVERLAY,ABSOLUTE,FIXED,我們只能選擇一個,缺省屬性爲ABSOLUTE。一個執行區域要麼直接繼承其前面的執行區域的屬性或者具有屬性爲ABSOLUTE。
具有PI,OVERLAY,RELOC屬性的執行區域允許其地址空間重疊,對於BSOLUTE,FIXED 屬性執行區域地址空間重疊Armlink會報錯。
max_size:可選,他用於指使Armlink在實際分配空間大於指定值時報錯。
input_section_deion:指示輸入段的內容。
2.2.3 輸入段描述
輸入段:
ó模塊名:目標文件名,庫成員名,庫文件名。名稱可以使用通配符。
ó輸入段名,或輸入段屬性(READ-ONLY,CODE)。
module_select_pattern
["("
(" " input_section_attr | input_section_pattern)
([","] " " input_section_attr | "," input_section_pattern))*
")"]
2.2.3.1
module_select_pattern:選擇的模塊名稱(目標文件,庫文件成員,庫文件),模塊名可以使用通配符(*匹配任意多個字符,?匹配任意一個字符),名稱不區分字母大小寫,它是供選擇的樣本。
例1:*libtx.a ( RO)
libtx.a爲threadX庫文件。
例2:tx_ill.o (INIT)
tx_ill.o爲threadX中斷向量目標文件。
2.2.3.2
input_section_attr:輸入段屬性選擇子,每個選擇子以” ”開頭,選擇子不區分大小寫字符。
選擇子可選RO-CODE,RO-DATA,RO( selects both RO-CODE and RO-DATA),RW-DATA,RW-CODE,RW( selects both RW-CODE and RW-DATA),ZI,ENTRY( that is a section containing an ENTRY point)。
以下同義詞可以選擇:CODE (for RO-CODE),CONST( for RO-DATA),TEXT (for RO),DATA (for RW),BSS (for ZI)。
還有兩個僞屬性:FIRST,LAST。如果各段的先後順序比較重要時,可以使用FIRST,LAST標示一個執行區域的第一個和最後一個段。
例1:os_main_init.o (INIT , FIRST)
FIRST表示放於本執行區域的開始處。
例2:*libtx.a ( RO)
RO 表示*libtx.a的只讀部分。
2.2.3.3
input_section_pattern:輸入段名。
例1:os_main_init.o (INIT , FIRST)
INIT 爲os_main_init.o的一個段。
例2:os_stackheap.o (heap)
heap 爲os_stackheap.o的一個段。
例3:os_stackheap.o (stack)
stack爲os_stackheap.o的一個段。
RO段、RW段和ZI段 --Image$$??$$Limit 含義---實例
//--------------------------------------------------------------------------------------------------------------------------
分散加載文件事例
ADS下的分散加載文件應用實例
load_region_name start_address | " "offset [attributes] [max_size]
{
execution_region_name start_address | " "offset [attributes][max_size]
{
module_select_pattern ["("
(" " input_section_attr | input_section_pattern)
([","] " " input_section_attr | "," input_section_pattern)) *
")"]
}
}
load_region: 加載區,用來保存永久性數據(程序和只讀變量)的區域;
execution_region: 執行區,程序執行時,從加載區域將數據複製到相應執行區後才能被正確執行;
load_region_name: 加載區域名,用於“Linker”區別不同的加載區域,最多31個字符;
start_address: 起始地址,指示區域的首地址;
offset: 前一個加載區域尾地址+offset 做爲當前的起始地址,且“offset”應爲“0”或“4”的倍數;
attributes: 區域屬性,可設置如下屬性:
PI 與地址無關方式存放;
RELOC 重新部署,保留定位信息,以便重新定位該段到新的執行區;
OVERLAY 覆蓋,允許多個可執行區域在同一個地址,ADS不支持;
ABSOLUTE 絕對地址(默認);
max_size: 該區域的大小;
execution_region_name:執行區域名;
start_address: 該執行區的首地址,必須字對齊;
offset: 同上;
attributes: 同上;
PI 與地址無關,該區域的代碼可任意移動後執行;
OVERLAY 覆蓋;
ABSOLUTE 絕對地址(默認);
FIXED 固定地址;
UNINIT 不用初始化該區域的ZI段;
module_select_pattern: 目標文件濾波器,支持通配符“*”和“?”;
*.o匹配所有目標,* (或“.ANY”)匹配所有目標文件和庫。
input_section_attr: 每個input_section_attr必須跟隨在“+”後;且大小寫不敏感;
RO-CODE 或 CODE
RO-DATA 或 CONST
RO或TEXT, selects both RO-CODE and RO-DATA
RW-DATA
RW-CODE
RW 或 DATA, selects both RW-CODE and RW-DATA
ZI 或 BSS
ENTRY, that is a section containing an ENTRY point.
FIRST,用於指定存放在一個執行區域的第一個或最後一個區域;
LAST,同上;
input_section_pattern: 段名;
彙編中指定段:
AREA vectors, CODE, READONLY
C中指定段:
#pragma arm section [sort_type[[=]"name"]] [,sort_type="name"]*
sort_type: code、rwdata、rodata、zidata
如果“sort_type”指定了但沒有指定“name”,那麼之前的修改的段名將被恢復成默認值。
#pragma arm section // 恢復所有段名爲默認設置。
應用:
#pragma arm section rwdata = "SRAM",zidata = "SRAM"
static OS_STK SecondTaskStk[256]; // “rwdata”“zidata”將定位在“sram”段中。
#pragma arm section // 恢復默認設置
分散加載文件中定義如下:
Exec_Sram 0x80000000 0x40000
{
* (sram)
}
“PI” 屬性使用示例:
LR_1 0x010000 PI ; The first load region is at 0x010000.
{
ER_RO 0 ; The PI attribute is inherited from parent.
; The default execution address is 0x010000, but the code can be moved.
{
*( RO) ; All the RO sections go here.
}
ER_RW 0 ABSOLUTE ; PI attribute is overridden by ABSOLUTE.
{
*( RW) ; The RW sections are placed next. They cannot be moved.
}
ER_ZI 0 ; ER_ZI region placed after ER_RW region.
{
*( ZI) ; All the ZI sections are placed consecutively here.
}
}
LR_1 0x010000 ; The first load region is at 0x010000.
{
ER_RO 0 ; Default ABSOLUTE attribute is inherited from parent. The execution address
; is 0x010000. The code and ro data cannot be moved.
{
*( RO) ; All the RO sections go here.
}
ER_RW 0x018000 PI ; PI attribute overrides ABSOLUTE
{
*( RW) ; The RW sections are placed at 0x018000 and they can be moved.
}
ER_ZI 0 ; ER_ZI region placed after ER_RW region.
{
*( ZI) ; All the ZI sections are placed consecutively here.
}
}
程序中對某區域地址等的引用方法:
Load$$region_name$$Base Load address of the region.
Image$$region_name$$Base Execution address of the region.
Image$$region_name$$Length Execution region length in bytes (multiple of 4).
Image$$region_name$$Limit Address of the byte beyond the end of the execution region.
Image$$region_name$$ZI$$Base Execution address of the ZI output section in this region.
Image$$region_name$$ZI$$Length Length of the ZI output section in bytes (multiple of 4).
Image$$region_name$$ZI$$Limit Address of the byte beyond the end of the ZI output sectionin the execution region.
SectionName$$Base Input Address of the start of the consolidated section called SectionName.
SectionName$$Limit Input Address of the byte beyond the end of the consolidated section called SectionName.
Load: 加載區,即存放地址;
Image: 執行區,即運行地址;
Base: 區首地址;
Limit: 區尾地址;
Length: 區長度;
region_name: RO、RW、ZI、load_region_name、execution_region_name;
例如:
“RAM1”區域的首地址: Image$$RAM1$$Base
上例中“sram”段首地址: sram$$Base
彙編引用示例:
IMPORT |Load$$Exec_RAM1$$Base| // Exec_RAM1 爲“RW”段
IMPORT |Image$$Exec_RAM1$$Base|
IMPORT |Image$$Exec_RAM1$$Length|
IMPORT |Image$$Exec_RAM1$$Limit|
LDR R0, =|Load$$Exec_RAM1$$Base|
LDR R1, =|Image$$Exec_RAM1$$Base|
LDR R2, =|Image$$Exec_RAM1$$Limit|
0
CMP R1, R2
LDRCC R3, [R0], #4
STRCC R3, [R1], #4
BCC %b0
C 引用:
extern unsigned char Load$$Exec_RAM1$$Base;
extern unsigned char Image$$Exec_RAM1$$Base;
extern unsigned char Image$$Exec_RAM1$$Length;
void MoveRO(void)
{
unsigned char * psrc, *pdst;
unsigned int count;
count = (unsigned int) &Image$$Exec_RAM1$$Length;
psrc = (unsigned char *)&Load$$Exec_RAM1$$Base;
pdst = (unsigned char *)&Image$$Exec_RAM1$$Base;
while (count--) {
*pdst = *psrc ;
}
}
加載文件示例一:
起始地址 大小
ROM: 0x00000000 256K ;0x1fc 保留爲加密字,程序在ROM中運行;
RAM 0x40000000 16K ;用於全局變量及任務堆棧;
SRAM 0x80000000 512K ;SRAM速度慢,主要用於存放大的數據表;
LOAD_ROM1 0x00000000 0x1f8 ; 指定該加載區域首地址、大小
{
EXEC_ROM1 0 0x1f8 ; 沒有前一加載區域,所以該執行區域首地址爲加載去首地址
; 並指定該區域長度
{
Startup.o (vectors, FIRST) ; 目標文件的“vectors”段放在該執行區域的第一段
irq.o ( RO) ; 目標文件的所有“RO”段放在該執行區域
}
}
LOAD_ROM2 0x00000200 ; 第二個加載區域
{
EXEC_ROM2 0 0x3e600
{
* ( RO) ; 所有目標文件和庫文件中的“RO”段存放在該區域
}
RAM1 0x40000000 0x4000
{
* ( RW, ZI) ; 所有目標文件和庫文件的“RW”和“ZI”段存放在該區域
}
SRAM2 0x80000000 0x80000
{
* (sram) ; 所有目標文件中的“sram”段存放在該區域
}
}
示例二:
“iap.o”定義在“Exec_RAM1”中運行,所以設置“PI”屬性;
在調用“iap.c”中函數之前應該將其從“Load$$Exec_IAP$$Base”複製到指定的“Exec_RAM1”區域;
Load_region1 0x00000000 0x1fc
{
EXEC_ROM1 0
{
Startup.o (vectors, FIRST)
irq.o ( RO)
}
}
Load_region2 0x00000200 0x3e600
{
EXEC_ROM2 0
{
* ( RO)
}
Exec_IAP 0 PI // 可能引起鏈接器未使用該屬性警告,忽略
{
iap.o ( RO)
}
Exec_RAM1 0x40000000 0x4000
{
* ( RW, ZI)
}
Exec_Sram 0x80000000 0x40000
{
* (SRAM)
}
}
// 移動“IAP.o”中的所有函數到“ImageExecIAPBase”加載區,並調用其中的函數
extern unsigned char Load$$Exec_IAP$$Base;
extern unsigned char Image$$Exec_IAP$$Length;
#define ImageExecIAPBase (0x40000000 0x1000) // 加載區首址
void MoveIAPRO(void)
{
unsigned char * psrc, *pdst;
unsigned int count;
count = (unsigned int) &Image$$Exec_IAP$$Length;
psrc = (unsigned char *)&Load$$Exec_IAP$$Base;
pdst = (unsigned char *)ImageExecIAPBase;
while (count--) {
*pdst = *psrc ;
}
}
// 調用“IAP.O”中的某函數
{
void (* pfnIAPWrite)(unsigned long, int);
pfnIAPWrite = (void (*)(unsigned long, int))
(ImageExecIAPBase
(unsigned int)IAPWrite - // 被調用函數名
(unsigned int)&Load$$Exec_IAP$$Base);
pfnIAPWrite((int)((CUPDATA *)CODESTARTADDR)->data,
((CUPDATA *)CODESTARTADDR)->length);
}
//————————————————————————————————————————————————————————————
ARM編譯程序參考
介紹ARM編譯程序的ARM特有方面,包括:
Pragmas 編譯指示
keywords 函數關鍵字
Variable declaration keywords 變量聲明關鍵字
Pragmas
ARM編譯程序可識別一下格式的編譯指示:
#pragma [no_] feature-name
編譯指示優於相關的命令行選項。
能識別的編譯選項如下:
Pragma name
Default
Reference
arm section
Off
Pragmas controlling code generation
check_printf_formats
Off
Pragmas controlling printf and scanf argument checking
check_scanf_formats
Off
Pragmas controlling printf and scanf argument checking
check_stack
On
Pragmas controlling code generation
debug
On
Pragmas controlling debugging
import
code generation
Ospace
optimization
Otime
optimization
Onum
optimization
softfp_linkage
Off
code generation
* check_printf_formats
該編譯指示標記類似於printf的函數,如果存在文字格式串,則對照進行類型檢查。
#pragma check_printf_formats
extern void myprintf(const char *format, …);
#pragma no_check_printf_formats
* check_scanf_formats
該編譯指示對聲明爲類似於scanf的函數做標記,以便對照文字格式串檢查自變量的格式。
#pragma check_scanf_formats
extern void myformat(const char *format, …);
#pragma no_check_scanf_formats
* debug 該編譯指示可打開或關閉調試表生成, 如果指定#pragma no_debug,則不會爲隨後的聲明和函數生成調試信息表條目,直到下一個#pragma debug出現。
* Pragmas controlling optimization
Ospace
Otime
Onum
* Pragmas controlling code generation
o check_stack 如果已經使用了#pragma no_check_stack和-apcs/swst命令行選項禁止棧檢查,則該編譯指示可使的檢查是否違反了棧限制的函數入口代碼的重新生成。
o once 同#ifndef …#endif效果相類似,用於頭文件。但一般推薦使用#ifndef…#define。
o softfp_linkage 該編譯指示指定了至下一個#pragma no_softfp_linkage之間的所有函數聲明描述了使用軟件浮點鏈接的函數。__softfp關鍵字與該編譯指示的效果相同
o import(symbol_name) 該編譯指示生成對symbol_name的導入引用。同如下彙編語言相同:IMPORT symbol_name。符號名作爲外部符號放在映像的符號表中。
o arm section section_sort_list This pragma specifies the code or data section name that used for subsequent or objects.This include definitions of anonymous objects the compiler creates for initializations.該編譯指示可指定代碼或數據段的名稱用於隨後的函數或對象。包括編譯程序爲初始化而創建的匿名對象的定義。該選項對一下情況沒有影響:
內聯函數(及其局部靜態變量)
模板實例(及其局部靜態變量)
消除未使用的變量和函數
將定義寫入目標文件中的順序
該編譯指示完整語法爲:
#pragma arm section [sort_type[[=]“name”]][,sort_type=
“name”]
此處name用於段名稱,sort_type可爲如下之一code, rwdata, rodata
和zidata。若指定sort_type,沒有指定name,則sort_type的段名被
重新設置爲默認值。單獨輸入#pragma arm section,則所以對象段的
恢復爲其默認值
int x1 = 5; // in .data (default)
int y1[100]; // in .bss (default)
int const z1[3] = {1,2,3}; // in .constdata (default)
#pragma arm section rwdata = "foo", rodata = "bar"
int x2 = 5; // in foo (data part of region)
int y2[100]; // in .bss
int const z2[3] ={1,2,3}; // in bar
char *s2 = "abc"; // s2 in foo, "abc" in bar
#pragma arm section rodata
int x3 = 5; // in foo
int y3[100]; // in .bss
int const z3[3] ={1,2,3}; // in .constdata
char *s3 = "abc"; // s3 in foo, "abc" in .constdata
#pragma arm section code = "foo"
int add1(int x) // in foo (code part of region)
{
return x 1;
}
#pragma arm section code
使用分散加載描述文件和鏈接程序,以控制將命名段放置在存儲器中
的特定地址。
· keywords
一些關鍵字指示編譯程序對其某個函數進行特殊處理。包括函數內的聲明,函數限定符及函數存儲類限定符。即Declarations inside , qualifiers and storage.
__asm{assembler-code} 指示編譯程序該語句是用彙編語言編寫的。
__irq This enables a C or C to be used as an interrupt routine called by the IRQ, or FIQ vectors. All corrupted registers except floating-point registers are preserved, not only those that are normally preserved under the ATPCS. The default ATPCS mode must be used. The exits by setting the pc to lr-4 and the CPSR to the value in SPSR. It is not available in tcc or tcpp. No arguments or return values can be used with __irq s.
__pure 指明函數聲明爲純的。純函數沒有了公共子表達式。默認情況下,函數假定是不純的(產生副作用)。純函數需要滿足:其結果僅取決於其自變量的值;沒有副作用,其不能調用非純函數。不能使用全局變量或廢棄指針,同一參數兩次調用純函數,返回應該相同
一般而言,一個程序包括只讀的代碼段和可讀寫的數據段。在ARM的集成開發環境中,只讀的代碼段和常量被稱作RO段(ReadOnly);可讀寫的全局變 量和靜態變量被稱作RW段(ReadWrite);RW段中要被初始化爲零的變量被稱爲ZI段(ZeroInit)。對於嵌入式系統而言,程序映象都是存 儲在Flash存儲器等一些非易失性器件中的,而在運行時,程序中的RW段必須重新裝載到可讀寫的RAM中。這就涉及到程序的加載時域和運行時域。簡單來 說,程序的加載時域就是指程序燒入Flash中的狀態,運行時域是指程序執行時的狀態。對於比較簡單的情況,可以在ADS集成開發環境的ARM LINKER選項中指定RO BASE和RW BASE,告知連接器RO和RW的連接基地址。對於複雜情況,如RO段被分成幾部分並映射到存儲空間的多個地方時,需要創建一個稱爲“分佈裝載描述文件” 的文本文件,通知連接器把程序的某一部分連接在存儲器的某個地址空間。需要指出的是,分佈裝載描述文件中的定義要按照系統重定向後的存儲器分佈情況進行。 在引導程序完成初始化的任務後,應該把主程序轉移到RAM中去運行,以加快系統的運行速度。
什麼是arm的映像文件,arm映像文件其實就是可執行文件,包括bin或hex兩種格式,可以直接燒到rom裏執行。在axd調試過程中,我們調試的是 axf文件,其實這也是一種映像文件,它只是在bin文件中加了一個文件頭和一些調試信息。映像文件一般由域組成,域最多由三個輸出段組成 (RO,RW,ZI)組成,輸出段又由輸入段組成。所謂域,指的就是整個bin映像文件所處在的區域,它又分爲加載域和運行域。加載域就是映像文件被靜態 存放的工作區域,一般來說flash裏的整個bin文件所在的地址空間就是加載域,當然在程序一般都不會放在 flash裏執行,一般都會搬到sdram裏運行工作,它們在被搬到sdram裏工作所處的地址空間就是運行域。我們輸入的代碼,一般有代碼部分和數據部 分,這就是所謂的輸入段,經過編譯後就變成了bin文件中ro段和rw段,還有所謂的zi段,這就是輸出段。對於加載域中的輸出段,一般來說ro段後面緊 跟着rw段,rw段後面緊跟着zi段。在運行域中這些輸出段並不連續,但rw和zi一定是連着的。zi段和rw段中的數據其實可以是rw屬性。
| Image$$RO$$Base| |Image$$RO$$Limit| |Image$$RW$$Base| |Image$$ZI$$Base| |Image$$ZI$$Limit|這幾個變量是編譯器通知的,我們在 makefile文件中可以看到它們的值。它們指示了在運行域中各個輸出段所處的地址空間| Image$$RO$$Base| 就是ro段在運行域中的起始地址,|Image$$RO$$Limit| 是ro段在運行域中的截止地址。其它依次類推。我們可以在linker的output中指定,在 simple模式中,ro base對應的就是| Image$$RO$$Base|,rw base 對應的是|Image$$RW$$Base|,由於rw和zi相連,|Image$$ZI$$Base| 就等於|Image$$ZI$$limit| .其它的值都是編譯器自動計算出來的。
下面是2410啓動代碼的搬運部分,我給出註釋
BaseOfROM DCD |Image$$RO$$Base|
TopOfROM DCD |Image$$RO$$Limit|
BaseOfBSS DCD |Image$$RW$$Base|
BaseOfZero DCD |Image$$ZI$$Base|
EndOfBSS DCD |Image$$ZI$$Limit|
adr r0, ResetEntry; ResetEntry是復位運行時域的起始地址,在boot
nand中一般是0
ldr r2, BaseOfROM;
cmp r0, r2
ldreq r0, TopOfROM;TopOfROM=0x30001de0,代碼段地址的結束
beq InitRam
ldr r3, TopOfROM
;part 1,通過比較,將ro搬到sdram裏,搬到的目的地址從 | Image$$RO$$Base| 開始,到|Image$$RO$$Limit|結束
0
ldmia r0!, {r4-r7}
stmia r2!, {r4-r7}
cmp r2, r3
bcc %B0;
;part 2,搬rw段到sdram,目的地址從|Image$$RW$$Base| 開始,到|Image$$ZI$$Base|結束
sub r2, r2, r3;r2=0
sub r0, r0, r2
InitRam ;carry rw to baseofBSS
ldr r2, BaseOfBSS ;TopOfROM=0x30001de0,baseofrw
ldr r3, BaseOfZero ;BaseOfZero=0x30001de0
0
cmp r2, r3
ldrcc r1, [r0], #4
strcc r1, [r2], #4
bcc %B0
;part 3,將sdram zi初始化爲0,地址從|Image$$ZI$$Base|到|Image$$ZI$$Limit|
mov r0, #0;init 0
ldr r3, EndOfBSS;EndOfBSS=30001e40
1
cmp r2, r3
strcc r0, [r2], #4
bcc %B1