關於ro(rw)的問答

http://bbs.edw.com.cn/dispbbs.asp?boardid=20&replyid=249879&id=46592&page=1&skin=0&Star=1

請再解釋一個關於ADS中ro/rw base的疑惑(已查閱資料)!

首先說一下,我認爲我還是看了不少的資料,然後再來問這個問題的,看起來下面我寫的很多,其實我只是想把問題描述清楚一點,我認爲我已經寫的比較詳細了,所以應該不會耽誤各位大俠多少時間,所以希望能耐心看完,謝謝 !

我的CPU是4510,我在各種資料上都看過相關的解釋:
-ro-base address(即設定Image$$RO$$Base--我的理解)
這個選項將包含有RO(Read-Only屬性)輸出段的加載地址和運行地址設置爲address,
該地址必須是字對齊的,如果沒有指定這個選項,則默認的RO基地址值爲0x8000。
-rw-base address
這個選項設置包含RW(Read/Write屬性)輸出段的域的運行時地址,該地址必須是字對
齊的。

Image$$RO$$Base is the address of the read-only execution region (usually
contains code and read-only data).--ro運行時地址

Image$$RW$$Base is the address of the read-write execution region (usually
contains data).--rw運行時地址

所以大致意思,我是知道的,不過在還是在有幾個問題想不明白:

    問題1、就是這個設定的Image$$RO$$Base與我們復位時CPU讀取第一條指令的 0x0 地址有什麼關係?這個“Image$$RO$$Base”到底又設定了那些源代碼的運行起始地址?比如在一個Bootloader中,往往包含同時包含彙編代碼和C語言代碼兩部分,這個“Image$$RO$$Base”只設定了C語言中RO段的運行地址 ?(拷貝RO RW段代碼到Image$$RO$$Base,Image$$RW$$Base標識的位置是我的bootloader 彙編代碼進入C前做的最後一件事)
    如果說也設定了彙編中ro部分代碼的運行地址,那問題2又怎麼解釋 ?

    問題2、有人說“請注意,將工程編譯爲燒入Flash的二進制代碼時,需要將鏈接器重新設置,將調試時程序空間定位地址(RO Base地址)改爲0x00000000”,但我明明剛剛編譯的Bootloader就將ro base設爲了0xf00000,現在我已將其燒寫入Flash中測試過,Bootloader運行良好,而且在上電時Flash首先被映射到0地址開始的地方,這與設定的0xf00000差的很遠,當然在程序將進入C語言前,會remap SDRAM到0~16M空間,並將RO RW段拷貝到Image$$RO$$Base,Image$$RW$$Base標識的位置,問題是這個拷貝之前我的所有代碼(比如SDRAM初始化,remap,堆棧初始化等)都運行的很正常,這又怎麼解釋呢?
    這樣一來我到有個奇怪的想法就是,如果我的程序沒有C語言代碼,豈不是就可以不用管這個ro base的設置?因爲從一開機到拷貝ro/rw段前,我所有的彙編代碼執行良好。

    問題3、我的vector.S中復位入口“ResetEntry”的部分代碼如下,用AXD載入ADS編譯的這個.bin文件的時候可以看到,這個“ResetEntry”標號開始的代碼被編譯到了.bin文件的最前面,因爲我在鏈接設置裏面把reset section放在了img的最前面。我有點不明白就是這個“ResetEntry”標號代表着的一個地址與Image$$RO$$Base又有什麼關係 ?與復位時CPU讀取第一條指令的 0x0 地址又有什麼關係?因爲我的Bootloader在拷貝ro段代碼時用到了這個標號,同時我的Bootloader代碼也並不是真正放在Flash的起始部分(即開機的0地址處),只是在0地址處放了一條跳轉到Flash的高端,再執行哪裏存放的Bootloader代碼。

            AREA reset, CODE, READONLY
ENTRY
           ;復位和上電啓動的入口
ResetEntry
           b SYS_RST_HANDLER
           b UDF_INS_HANDLER
.........................................


    4、我在AXD調試程序時還發現,調試過程中我可以把ADS生成的.bin文件載入SDRAM中任何位置,然後修改pc值執行之,不論你的Image$$RO$$Base,Image$$ROW$$Base設定在什麼地方,當然我的這個調試的程序肯定沒有對ro/rw段做任何的拷貝操作。這讓我更對RW段運行時地址和Image$$ROW$$Base的關係感到疑惑,因爲rw段中不是保存有程序需要的變量或數據麼?Image$$ROW$$Base設定了rw段的運行地址,那豈不是也就設定了運行時變量或數據在內存中的存在位置?那我用AXD加載的這個程序又在哪裏獲得的程序運行需要的變量和數據呢 ?

    上面的問題其實一直就困擾了我好久,不過因爲項目方面比較急,所以也就一直放在哪裏沒有對其深究,現在稍微有點空,才放到網上來,請各位解惑,非常感謝 !
    我的表達能力也不怎麼好,上面的問題也許有點羅嗦,如果還有什麼沒有說清楚的也希望能提出來,3KS  ag !


********************************************************************************************

四個問題其實就是一個,即鏈接器到底指定了什麼東西。簡單地說,linker把對函數的調用轉成向某一地址的跳轉,把對變量的讀寫轉成對某一地址的訪問。

但是,一段正在執行的代碼本身,從本質上來說,並不需要知道自己運行在什麼地址。只要PC指針有一個初值(比如0x0),然後每次加4之後,能取到下一條指令就行了。與此相關的另一個概念就是PIC(Position Independent Code,與位置無關的代碼)。

只有當代碼中調用了函數,向某個symbol跳轉或訪問了變量,才需要知道真實的地址。所以Image$$RO$$Base是否起作用,並不取決於C代碼和彙編代碼的區別,而在於PIC與PDC的區別。
********************************************************************************************

版主和TALISKER已經回答了樓主的問題。我也說說自己的看法,希望對你有幫助。
1. Image$$RO$$Base is the address of the read-only execution region
   你仔細琢磨RO BASE的意思,RO BASE指定的只是RO段的起始地址,注意:並沒
   有說始第一條指令的地址。所以,如果你的程序包括多個段的時候,有可能別
   的段放到了RO BASE指定的地址上,而並不是期望執行的第一條指令。在調試
   的時候,這不會造成什麼問題,因爲DEBUGGER會把PC指向程序的第一條指令。
   如果你把程序燒寫到FLASH裏面後,這可能會造成問題,因爲CPU在RESET後只
   從地址0x0處開始取指令。所以,如果你寫一個程序,期望程序從地址0x0開始
   執行,要滿足兩個條件:1-RO BASE要爲0x0,2-告訴linker,把包括你希望
   執行的第一天指令SECTION鏈接到RO段的最前面。
  
2. 對於你說到的第二個問題,爲什麼你把RO BASE設置爲0xF0000000還能從地址
   0x0開始執行?這是因爲4510的地址繞卷問題造成的,其實地址0xF0000000就是
   地址0x0,不信你可以自己測試一下,從地址0xF00000讀出來的指令和地址0x0
   的指令一樣。所以,你的第二個問題不是問題,這和RO BASE沒什麼關係。
  
3. 前面說過了,一個程序包括了RO段,但程序可能包括了很多個SECTION,在編譯
   的時候,各個section的RO段都是被鏈接到你指定的RO BASE開始的地方的,這
   就涉及到另外一個問題:在RO段裏面,你怎麼安排各個sedtion在RO段的順序?
   如果你寫的是BOOTLOADER的話,你必須要把包括你要執行的第一條指令的段放
   在最前面,這樣才能保證板子上電後能正常運行。
  
4. 我不知道你的程序裏面是否有RW段?程序分爲RO段和RW段,但並不是每個程序
   都一定有RW段,如果你的程序裏面沒有全局變量等,那編譯後,肯定是沒有RW
   段的,在這種情況下,你不進行RW段的拷貝,程序也能正常運行的。OK,在來
   看看你的問題,你說在編譯生成bin文件後,在bin文件裏如何定位RW段?答案
   是:單單依賴bin文件是無法定位RW段的。所以 - 如果你的程序有RW段的話,
   你必須在自己程序裏面處理RW段的拷貝。
********************************************************************************************

首先謝謝各位兄弟、大哥的回覆,非常感謝 !

其次針對各位的回覆,再綜合我所知道的別人給的解釋,我想再談一下我自己現在的想法:
to Talisker:我不知道你想設定ro base在0x04的意思是什麼?

to Twentyone:
針對你的回答1:
“在調試的時候,這不會造成什麼問題,因爲DEBUGGER會把PC指向程序的第一條指令。”---我調試的時候採用的是ADW載入ADS編譯的二進制bin文件的方式,所以pc值或bin文件載入地址都是由我指定的,但都與ro base設定的值不同。
針對你的回答2:“對於你說到的第二個問題,爲什麼你把RO BASE設置爲0xF0000000還能從地址0x0開始執行?這是因爲4510的地址繞卷問題造成的,其實地址0xF0000000就是地址0x0”---你說的沒錯,0xF0000000地址最終會對應到0地址,不過我是將ro base設在了0xf00000,即15M內存的地方,而調試使用的obey xxx.ini命令將0~16M的內存都分配給了SDRAM,所以將ro base 設在0xf00000,還沒有到地址卷繞的地方。
針對你的回答4:
我調試的程序首先是比較簡單,那是不錯,不過變量肯定是有的,不論全局、局部變量,都有 !是一個同時包含串口和IO口簡單測試的程序。

另外有人回答說:

解答問題1&2:這說明你的bootloader是基於相對地址的,也就是說與地址無關,而這也是對bootloader的一個基本要求。一般的bootloader都設計成地址無關的。

解答問題4:這說明你的程序比較小,或者函數調用不復雜,堆棧操作不頻繁等。也就是說,不按照你的設置的ro和rw基址調入程序,有可能數據空間不夠,或者代碼和數據重疊,程序執行就可能出錯。

------我看這個回答好像是對的,不知道各位對此有什麼理解或看法 ?

********************************************************************************************
Twentyone:

文後附的是一個測試程序,這個程序可以驗證RW的影響。
測試的之前,你把你的板子的MEMORY給配置好,把SDRAM配置到地址0X0開始的地方。

測試1:程序的RO_BASE = 0x0,RW_BASE不設置,你用仿真器單步運行程序,然後觀察
內存0x800的內容變化,變化順序應該是0x0 -> 0x1 -> 0x2 ... -> 0x9

測試2:程序的R0_BASE不變,還是RO_BASE = 0x0,RW_BASE設置爲0x400,單步運行程
序,觀察內存0x800的內容變化,看看結果和第一次測試的是否一樣

我:

我試過了,也基本上清楚了,按照你的實驗設定測試2會看到0x800地址的數據不規則的變化,我又看了一下對應的彙編代碼(我是指編譯後的彙編代碼),發現程序在讀取array數組值時,是從rw base開始的地方讀取數據的,因此如果一開始沒有將rw段放到rw base開始的地方,則程序執行後讀取的數據肯定是不對的,這也符合rw base本身的意思,現在我應該說是徹底的清楚了,謝謝 !
    我以前的問題是:我在程序中使用變量前首先就先改變了變量的值,因此RW段首先就被我程序先修改了,也就是說雖然我沒有搬移RW 段的操作,但是我重新修改了rw base開始的數據的值,對應到你的這個測試程序,也就是說本來我沒做搬移RW段的操作,則0x400地址開始的數據(在你這個測試程序中存放的是array數組的值)是不確定的,但後來程序一開始我就修改了array數組的數據,也即修改了0x400開始的數據,因此RW段雖然我沒有搬移操作,但其實也就被我重新設定正確了,然後再讀取使用,當然執行起來,當然就沒有什麼問題了,不過這裏有一點:就是我的程序本來就比較小,RW段也很小,所以沒有什麼問題,但如果程序大一點,RW段太大,我不能夠一一重新指定,那程序就會出錯了,問題解決,非常感謝 !
    不過關於RO段的問題:RO base 設定值與bin程序載入值不同,但程序仍能正確執行的問題 。
    我想可能只能用我得代碼都是“地址無關”的這個來解釋了,對吧 ?
    這東西有點絞,不知道我上面的表達清楚沒? 呵呵,如果沒什麼問題,我想就把上面這段話轉到電子產品世界去了,然後我那個帖子也就可以方封貼了,呵呵,不過真的非常感謝你的幫助,謝謝 !

Twentyone:

不用客氣!

你說的很清楚,你的理解是正確的,關於RO_BASE的問題,如果程序完全運行正確的話
,只能用與地址無關來解釋。

如果你想做一個RO_BASE的測試的話,你可以寫一個測試程序,在程序裏面包括一些絕
對尋址的指令,你就會發現程序運行時用不同的RO_BASE的話,運行會有問題的(可能
不影響結果,但中間肯定是有地方和你期望的不一樣的)。


ft,忘了貼代碼了,Twentyone大俠的測試程序如下:

init.s:

AREA    Init, CODE, READONLY
CODE32
ENTRY
ARM
LDR SP, =0xC00
IMPORT CTEST
B    CTEST
END


test.c:

unsigned int array[] = {0,1,2,3,4,5,6,7,8,9};

int CTEST(void)
{
int i;
unsigned int base; 
i = 0;
base = 0x800;
for(i = 0; i < 10; i++){
  *((unsigned int*)(base)) = array[i];
}
return 0;
}
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章