在 mini2440 使用sdram 中和以前同樣簡單的使用 -T0x30000000 控制連接生成的ELF文件中 .text 段加載位置。在稍複雜的項目中常會涉及哪一個對象文件中的哪一個section是否出如今最終文件中,若是出現,其加載位置和在elf中的存儲位置分別在何處,是否要有對齊限制等。這些若是都以參數傳給 ld 會累垮程序猿,因而連接腳本出現了。這裏不許備詳細描述其規則,僅舉一例:網上常有人問《嵌入式Linux應用開發徹底手冊》第7章的MMU例子編譯時出現
linux
ordered `.ARM.exidx' and unordered `.ARM.extab'
相似字樣怎麼解,提到的解法大體有換用低版本編譯器、使用 -nostdlib 參數。c++
.ARM.exidx 和 .ARM.extab 這兩個段是在編譯 c++ 時出現的,並且看起來只有 4.1 以上版本的 arm-linux-gcc 編譯器纔會生成。這能夠用 arm-linux-readelf -S 來驗證一下。在用配套的 arm-linux-ld 連接時是不容許把 .ARM.exidx 和 .ARM.extab 放在同一個段裏的。若是項目文件有head.s 和 sdram.cpp:shell
.text .global _start _start: bl kill_dog bl control_mem bl copy2sdram ldr pc, =sdram sdram: mov sp, #0x34000000 ldr r4, =main mov lr, pc bx r4 _end: b _end kill_dog: mov r0, #0x53000000 mov r1, #0 str r1, [r0] mov pc, lr control_mem: mov r0, #0x48000000 ldr r1, =0x22111112 str r1, [r0], #4 mov r1, #0x00000700 str r1, [r0], #4 mov r1, #0x00000700 str r1, [r0], #4 mov r1, #0x00000700 str r1, [r0], #4 mov r1, #0x00000700 str r1, [r0], #4 mov r1, #0x00000700 str r1, [r0], #4 mov r1, #0x00000700 str r1, [r0], #4 ldr r1, =0x00018009 str r1, [r0], #4 ldr r1, =0x00018009 str r1, [r0], #4 ldr r1, =0x008e04eb str r1, [r0], #4 mov r1, #0x000000b2 str r1, [r0], #4 mov r1, #0x00000030 str r1, [r0], #4 mov r1, #0x00000030 str r1, [r0], #4 mov r1, #0x00000000 str r1, [r0], #4 mov r1, #0x00000000 str r1, [r0], #4 mov pc, lr copy2sdram: mov r0, #0x400 mov r1, #0x30000000 mov r2, #0x1000 loop: ldr r3, [r0], #4 str r3, [r1], #4 cmp r0, r2 bne loop mov pc, lr
class CNumberedMusicalNotation { public: CNumberedMusicalNotation( void ); ~CNumberedMusicalNotation( void ); void dao( void ); void rai( void ); void mi( void ); void fa( void ); void suo( void ); void la( void ); void xi( void ); private: void latency( void ); unsigned long* data; }; CNumberedMusicalNotation::CNumberedMusicalNotation() { unsigned long* gpbcon = reinterpret_cast<unsigned long*>(0x56000010); *gpbcon = 0x15400; // enable GPB output data = reinterpret_cast<unsigned long*>(0x56000014); *data = ~0; } CNumberedMusicalNotation::~CNumberedMusicalNotation() { *data = ~0; latency(); } void CNumberedMusicalNotation::dao() { // led1 - GPB5 *data = ~(1<<5); latency(); } void CNumberedMusicalNotation::rai() { // led2 - GPB6 *data = ~(1<<6); latency(); } void CNumberedMusicalNotation::mi() { // led3 - GPB7 *data = ~(1<<7); latency(); } void CNumberedMusicalNotation::fa() { // led4 - GPB8 *data = ~(1<<8); latency(); } void CNumberedMusicalNotation::suo() { // led1+3 - GPB5+7 *data = ~(1<<5 | 1<<7); latency(); } void CNumberedMusicalNotation::la() { // led2+4 - GPB6+8 *data = ~(1<<6 | 1<<8); latency(); } void CNumberedMusicalNotation::xi() { // led1+4 - GPB5+8 *data = ~(1<<5 | 1<<8); latency(); } void CNumberedMusicalNotation::latency() { volatile int i; for ( i = 0; i < 10000; i++ ); } int __attribute__((__long_call__)) main() { CNumberedMusicalNotation n; n.dao(); n.rai(); n.mi(); n.suo(); n.suo(); n.la(); n.suo(); n.mi(); n.dao(); n.rai(); n.mi(); n.mi(); n.rai(); n.dao(); n.rai(); n.dao(); n.rai(); n.mi(); n.suo(); n.suo(); n.la(); n.suo(); n.mi(); n.dao(); n.rai(); n.mi(); n.mi(); n.rai(); n.rai(); n.dao(); return 0; }
注意在 head.s 裏要求oop
copy2sdram: mov r0, #0x400 mov r1, #0x30000000 mov r2, #0x1000
這是對nand flash啓動設置的,要求拷貝範圍是片內的0x400(1024)到0x1000(4096),目標是0x30000000。這要求1024~4096中包含sdram.cpp 全部代碼。而加載位置在 gdb 做 load 時就肯定了,所以要求在連接腳本里把存儲位置肯定好,存儲位置到文件頭要等於片內加載位置到片ram基址。佈局
Makefile學習
all: clean sdram.elf sdram.elf : arm-linux-gcc -c -O2 -o head.o head.s arm-linux-g++ -c -O2 -o sdram.o sdram.cpp arm-linux-ld -Tsdram.lds -o sdram.elf head.o sdram.o arm-linux-objcopy -O binary -S sdram.elf sdram.bin arm-linux-objdump -D -m arm sdram.elf > sdram.dis clean: rm -f *.o *.elf *.dis *.bin
連接腳本sdram.lds.net
ENTRY(_start) SECTIONS { . = 0x00000000; loader : { head.o } . = 0x30000000; .ARM.extab ALIGN(4) : AT(1024) { sdram.o(.ARM.extab*) } .ARM.exidx ALIGN(4) : AT(1024) { sdram.o(.ARM.exidx*) } runner ALIGN(4) : AT(1128) { sdram.o } }
用ENTRY肯定程序入口,用 . = 肯定當前虛擬內存(lma)位置,用 AT 定位存儲位置,用ALIGN保證32位對齊。
腳本說明,程序入口爲_start 標籤處起始加載地址爲0,head.o 中全部段將被放入一個名爲 loader 的段中,從0處計算相對位置。
以後改變加載地址爲0x30000000,把.ARM.extab 和 .ARM.exidx 從 sdram.o 中分離出來(該版本 arm-linux-ld的要求),而後把 sdram.o 中其餘段併入一個叫 runner 的段中,這 3 個段將從 0x30000000 計算相對位置。這裏爲什麼AT參數分別取 1024 1024 和 1128 是以前用 arm-linux-readelf -S sdram.o 獲得的:.ARM.extab 長度爲0,.ARM.exidx長度爲104,要把sdram.o中代碼存到文件 1024 開始出故有上述佈局。code
能夠試着把 sdram.lds 中的 AT 去掉,看看你的 sdram.bin 會有多大。
對象
這裏還有一個要注意的,和mini2440 使用sdram中跳到main的方式不一樣,那時是全部段加載位置在一塊兒,跳轉距離小於32M(thumb模式下只有4M),用一句 bl main 就能搞定。如今從bank0 調到 bank6,距離有6*128M,要用 bx 來實現,語句多了幾條:blog
ldr r4, =main mov lr, pc bx r4
先用能長距離加載 ldr 把地址讀入寄存器,再準備好 lr,最後纔是 bx 跳轉。
總之,無須換用低版編譯器也不用 -nostdlib 參數,學習下連接腳本吧,它能給你的編譯帶來很專業的體驗。