嵌入式系統中代碼的執行方式主要有3 種:
1. 完全映射(fully shadowed)。
嵌入式系統程序運行時,將所有的代碼從非易失存儲器(Flash、ROM等)複製到RAM中運行。
例子: s3c2440的nandflash和i. mx6ull的sd卡
2. 按需分頁(demand paging)。
只複製部分代碼到RAM中。
這種方法對RAM中的頁進行導入導出管理,如果訪問位於虛存中但不在物理RAM中會產生頁錯誤,這時纔將代碼和數據映射到RAM中。
例子: linux系統對於可執行程序的加載
3. eXecute In Place ( XIP) 。
在系統啓動時,不將代碼複製到RAM,而是直接在非易失性存儲位置執行。
RAM中只存放需要不斷變化的數據部分
例子: stm32的主flash 及s3c2440的norflash
代碼中有變化部分和不變化部分, 所以代碼可以分割? 如果可以, 怎麼分割?
代碼中 分割成 . code . ro- data . data . bss . heap . stack
二進制文件中 分割 成 . code . ro- data . data
內存中 分割成 . code . ro- data . data . bss . heap . stack
分割出來的代碼怎麼實現完全映射?
. code . ro- data . data 複製到ram
. bss 清0
. stack 設置SP
. heap 設置
分割出來的代碼怎麼實現XIP?
XIP 指的是可以在 flash 取指令, 然後執行, 棧還是放在 ram 中的
pc 順序指向
調用函數時使用. stack
方便編碼
反彙編文件
87800000 < _start> :
87800000 : e10f0000 mrs r0, CPSR
87800004 : e3c0001f bic r0, r0, #31
87800008 : e3800013 orr r0, r0, #19
8780000 c: e129f000 msr CPSR_fc, r0
在 hex文件
: 020000048780F 3
: 10000000 00000F E1 1F 00 C0E3 130080E3 00F 029E1 CE
高字節 e1 放在了 高地址, 小端.
ARM默認設置爲小端格式
led 中的段位置的放置 是有道理的
SECTIONS {
. = 0X87800000 ;
. text :
{
start. o
* ( . text)
}
. rodata ALIGN ( 4 ) : { * ( . rodata) }
. data ALIGN ( 4 ) : { * ( . data) }
__bss_start= . ;
. bss ALIGN ( 4 ) : { * ( . bss) * ( COMMON) }
__bss_end= . ;
}
一開始必定是 . text 段( RO)
然後還是RO的 . rodata ( 由const 修飾的全局變量)
然後 . data ( 在二進制文件中)
然後是. bss ( . bss 不在二進制文件中, 但是佔用地址, 爲了不讓. bss佔二進制文件的空間, 所以將其放在最後面. 並省略這個段)
-- -- -- -- -- -- -- -- - 在二進制文件中
. text段 爲RO, . text 段有相對地址( 相對PC) , . text 段中有可能有絕對地址, 是因爲可能使用了地址相關彙編代碼( 不是C代碼)
. rodata 段 爲RO, 緊緊挨着 . text段. 在 . text段中如果有對 const 變量的引用, 引用的不是地址, 是 值
. data 段 爲RW, 緊緊挨着 . rodata段. 在 . text段中如果有對 data變量的引用, 引用的是地址
. bss 段 爲RW , 不存在於 二進制文件中. 在 . text段中如果有對 bss變量的引用, 引用的是地址, 但是可以確定. bss開始與結束的地址及每個bss變量的地址
. stack 段, 不存在於二進制文件中.
. heap段, 不存在於二進制文件中.
-- -- -- -- -- -- -- -- - 怎麼講二進制文件拷貝到內存中, 並做好內存環境配置
1. 對於. text 段. 一定要拷貝到對應的地址上去. 因爲. text中有可能使用了地址相關彙編代碼
2. 對於. rodata 段. 其實可以不用拷貝, 因爲. rodata 的值已經在 . text 中了.
3. 對於. data 段. 需要拷貝到對應的地址上去, 因爲 . text 中要引用 data變量的地址
4. 對於 bss 段, 因爲二進制文件中沒有, 但是. text中會對其變量地址引用 . 所以 每個變量的地址, 每個變量都要初始化爲0. 要在內存中, 從段的起始到結束, 設置爲0.
5. 對於stack 段, 因爲 代碼調用 實際上被編譯成了 push pop 指令, 所以函數實質就是 壓棧彈棧. 雖然PC一直指向. text段, 但是運行時的狀態是在. stack段. . 那麼棧空間肯定要設置. 棧空間的設置只需要初始化SP指針即可. 另外, 棧的類型( 先增, 後增, 先減, 後減) 由具體芯片定義.
6. 對於 heap 段, 一般用來存儲數據. 如果要使用動態內存, 則肯定要用初始化heap , 即, 找一塊內存, 將該內存用數據結構進行管理, malloc和free的時候其實本質也是對數據結構的管理
-- -- -- -- -- -- -- -- - 在內存中
. text段 爲RO, . text 段有相對地址( 相對PC) , . text 段中有可能有絕對地址, 是因爲可能使用了地址相關彙編碼( 不是C代碼)
. rodata 段 爲RO, 緊緊挨着 . text段. 在 . text段中如果有對 const 變量的引用, 引用的不是地址, 是 值
. data 段 爲RW, 緊緊挨着 . rodata段. 在 . text段中如果有對 data變量的引用, 引用的是地址
. bss 段 爲RW , 緊緊挨着 . data段. 在 . text段中如果有對 bss變量的引用, 引用的是地址
. stack 段一般 爲 高地址-> 低地址 發展, 往. heap發展, 不和. text . rodata . data . bss 段重複
. heap 段一般爲 低地址-> 高地址 發展, 往. stack發展, 不和. text . rodata . data . bss 段重複
段有哪些
$ readelf - S ledc. elf
There are 11 section headers, starting at offset 0x8320 :
Section Headers:
[ Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0 ] NULL 00000000 000000 000000 00 0 0 0
[ 1 ] . text PROGBITS 87800000 008000 000158 00 AX 0 0 4
[ 2 ] . text. startup PROGBITS 87800158 008158 0000f 8 00 AX 0 0 4
[ 3 ] . rodata PROGBITS 87800250 008250 000004 00 A 0 0 4
[ 4 ] . data PROGBITS 87800254 008254 000004 00 WA 0 0 4
[ 5 ] . bss NOBITS 87800258 008258 00000 c 00 WA 0 0 4
[ 6 ] . comment PROGBITS 00000000 008258 000046 01 MS 0 0 1
[ 7 ] . ARM. attributes ARM_ATTRIBUTES 00000000 00829 e 000027 00 0 0 1
[ 8 ] . shstrtab STRTAB 00000000 0082 c5 00005 b 00 0 0 1
[ 9 ] . symtab SYMTAB 00000000 0084 d8 000310 10 10 30 4
[ 10 ] . strtab STRTAB 00000000 0087e8 0000f 2 00 0 0 1
Key to Flags:
W ( write) , A ( alloc) , X ( execute) , M ( merge) , S ( strings)
I ( info) , L ( link order) , G ( group) , T ( TLS) , E ( exclude) , x ( unknown)
O ( extra OS processing required) o ( OS specific) , p ( processor specific)
. text
. text. startup
. rodata
. data
. bss
. comment
. ARM. attributes
bin 文件中 (最終只有bin文件被燒寫到flash中)
. text
. text. startup
. rodata
. data