我使用友善之臂公司出品的smart210平台,cpu是s5pv210。在裸机开发时,用usb线烧写bin文件到内存中直接运行。
为了尽量排除其它因素的干扰,使用最简单的led跑马灯代码。该程序由两部分组成:start.S和led.c,代码如下:
start.S
.global _start
.global main
_start:
bl main
over:
b over
led.c
#define GPJ2CON (*(volatile unsigned int *)0xE0200280)
#define GPJ2DAT (*(volatile unsigned int *)0xE0200284)
void led_init(void)
{
GPJ2CON=0x1111;
GPJ2DAT=0xf;
}
void delay(unsigned int n)
{
while(n--);
}
int main(void)
{
led_init();
unsigned int i=0;
while(1)
{
GPJ2DAT=~(1<<(i%4));
i++;
delay(0x100000);
}
}
以下是我用的makefile文件,交叉编译工具用的是开发板自带的arm-linux-gcc。
name=led
obj=start.o led.o
$(name)new.bin:$(name).bin
gcc mkv210_image.c -o mkv
./mkv $^ $@
$(name).bin:$(name).elf
arm-linux-objcopy -O binary $^ $@
arm-linux-objdump -D $^ >$(name).dis
$(name).elf:$(obj)
arm-linux-ld -Ttext=0x20000000 $^ -o $@
.S.o:
arm-linux-gcc -c $<
.c.o:
arm-linux-gcc -c $<
clean:
rm *.o *.elf *.bin -f
查看反汇编代码前面几句如下:
20000000 <_start>:
20000000: eb000020 bl 20000088 <main>
20000004 <over>:
20000004: eafffffe b 20000004 <over>
20000008 <led_init>:
20000008: e52db004 push {fp} ; (str fp, [sp, #-4]!)
生成的bin文件前面部分如下
20 00 00 EB FE FF FF EA 04 B0 2D E5 00 B0 8D E2
可以看到bin文件里面是纯粹的语句,没有任何的地址信息。
烧写到内存后,cpu直接跳转到0x20000000的地址处运行,跑马灯是正常的。
start.S文件仅仅用来跳转,那就把它去掉吧。仅仅留c文件。
编译后的elf文件和bin文件分别如下:
Disassembly of section .text:
20000000 :
20000000: e52db004 push {fp} ; (str fp, [sp, #-4]!)
20000004: e28db000 add fp, sp, #0
20000008: e59f301c ldr r3, [pc, #28] ; 2000002c
04 B0 2D E5 00 B0 8D E2 1C 30 9F E5 1C 20 9F E5
烧写到板子,无法运行。
查看编译输出,有一条警告:
arm-linux-ld: warning: cannot find entry symbol _start; defaulting to 20000000
入口地址被设为20000000,而那里是led_init的函数,因此无法运行。
将 arm-linux-ld -Ttext=0x20000000 −o @加上 –entry=main
用readelf -a led.elf分析文件,结果如下
入口点地址: 0x20000080
程序头起点: 52 (bytes into file)
入口点已正确设置,再看bin文件:
04 B0 2D E5 00 B0 8D E2 1C 30 9F E5 1C 20 9F E5
没有任何改变。烧写后仍然不能成功运行。
看来是objcopy的问题了。
objcopy里有一个–set-start=val的选项,加上这个选项后,bin文件没有任何改变。查看选项说明:Not all object file formats support setting the start address. 好吧。看来是不支持。
烧写后仍无法运行。
将main函数放在最上部,将其它函数放在后面,这样main函数的地址就是0x20000000。编译烧写后运行成功。
开发板默认从bin文件的起始处开始运行,无法设定特定的入口地址,如果要将某个函数设为入口,必须重新排列顺序,将函数放在最开始的位置。