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 参数,学习下连接脚本吧,它能给你的编译带来很专业的体验。

相关文章
相关标签/搜索