mini2440 連接腳本

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 參數,學習下連接腳本吧,它能給你的編譯帶來很專業的體驗。

相關文章
相關標籤/搜索