山高疑日近,海阔觉天低

链接地址-裸机与uBoot开发

要区分“存储地址”和“运行地址”这两个概念,“存储地址”就是可执行文件存储在哪里,可执行文件的存储地址可以随意选择。“运行地址”就是代码运行的时候所处的地址,这个我们在链接的时候就已经确定好了,代码要运行,那就必须处于运行地址处,否则代码肯定运行出错。比如 I.MX6U 支持 SD 卡、EMMC、NAND 启动,因此代码可以存储到 SD 卡、EMMC 或者 NAND 中,但是要运行的话就必须将代码从 SD 卡、EMMC 或者NAND 中拷贝到其运行地址(链接地址)处,“存储地址”和“运行地址”可以一样,比如STM32 的存储起始地址和运行起始地址都是 0X08000000。本教程所有的裸机例程都是烧写到 SD 卡中,上电以后 I.MX6U 的内部 boot rom 程序会将可执行文件拷贝到链接地址处,这个链接地址可以在 I.MX6U 的内部 128KB RAM 中(0X900000~0X91FFFF),也可以在外部的 DDR 中。本教程所有裸机例程的链接地址都在 DDR中,链接起始地址为0X87800000。I.MX6U-ALPHA 开发板的 DDR 容量有两种:512MB 和256MB,起始地址都为 0X80000000,只不过 512MB 的终止地址为 0X9FFFFFFF,而 256MB 容量的终止地址为 0X8FFFFFFF。之所以选择 0X87800000 这个地址是因为后面要讲的 Uboot 其链接地址就是 0X87800000,这样我们统一使用 0X87800000 这个链接地址,不容易记混。

下面是正点原子裸机led实例的Makefile:

led.bin:led.s
 arm-linux-gnueabihf-gcc -g -c led.s -o led.o
 arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
 arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
 arm-linux-gnueabihf-objdump -D led.elf > led.dis
clean:
 rm -rf *.o led.bin led.elf led.dis

1、arm-linux-gnueabihf-gcc 编译文件

arm-linux-gnueabihf-gcc -g -c led.s -o led.o

上述命令就是将 led.s 编译为 led.o,其中“-g”选项是产生调试信息,GDB 能够使用这些
调试信息进行代码调试。“-c”选项是编译源文件,但是不链接。“-o”选项是指定编译产生的文
件名字,这里我们指定 led.s 编译完成以后的文件名字为 led.o。执行上述命令以后就会编译生
成一个 led.o 文件,如图 8.4.1.1 所示:

2、arm-linux-gnueabihf-ld 链接文件

arm-linux-gnueabihf-ld 用来将众多的.o 文件链接到一个指定的链接位置,STM32程序也有链接只不过用户不知道,查看工程的*.map文件如下:

stm32程序所有源文件编译成.o文件,这些.o 文件就是从0x08000000这个链接地址开始依次存放,最终生成一个可以下载的 hex 或者 bin 文件

arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf

上述命令中-Ttext 就是指定链接地址,“-o”选项指定链接生成的 elf 文件名,这里我们命名为 led.elf。

3、arm-linux-gnueabihf-objcopy 格式转换
arm-linux-gnueabihf-objcopy 更像一个格式转换工具,我们需要用它将 led.elf 文件转换为led.bin 文件,命令如下:

arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin

4、arm-linux-gnueabihf-objdump 反汇编
大多数情况下我们都是用 C 语言写试验例程的,有时候需要查看其汇编代码来调试代码,因此就需要进行反汇编,一般可以将 elf 文件反汇编,比如如下命令:

arm-linux-gnueabihf-objdump -D led.elf > led.dis

从反汇编结果:

led.elf:     文件格式 elf32-littlearm
Disassembly of section .text:

87800000 <_start>:
87800000:	e59f0080 	ldr	r0, [pc, #128]	; 87800088 <loop+0x4>
87800004:	e3e01000 	mvn	r1, #0
87800008:	e5801000 	str	r1, [r0]
8780000c:	e59f0078 	ldr	r0, [pc, #120]	; 8780008c <loop+0x8>
87800010:	e5801000 	str	r1, [r0]
87800014:	e59f0074 	ldr	r0, [pc, #116]	; 87800090 <loop+0xc>
87800018:	e5801000 	str	r1, [r0]
8780001c:	e59f0070 	ldr	r0, [pc, #112]	; 87800094 <loop+0x10>
87800020:	e5801000 	str	r1, [r0]
87800024:	e59f006c 	ldr	r0, [pc, #108]	; 87800098 <loop+0x14>
87800028:	e5801000 	str	r1, [r0]
8780002c:	e59f0068 	ldr	r0, [pc, #104]	; 8780009c <loop+0x18>

可以看出 led.dis 里面是汇编代码,而且还可以看到内存分配情况。在0X87800000 处就是全局标号_start,也就是程序开始的地方。通过 led.dis 这个反汇编文件可以明显的看出我们的代码已经链接到了以 0X87800000 为起始地址的区域
代码烧写

我们在调试裸机和 Uboot 的时候是将代码下载到 SD 中,因为方便嘛,当调试完成以后量产的时候要将裸机或者 Uboot 烧写到 SPI NOR Flash、EMMC、NAND 等这些存储介质中的。

使用 imxdownload 向 SD 卡烧写 led.bin 文件,命令格式如下: ./imxdownload <.bin file>  <SD device>

./imxdownload led.bin /dev/sdd //不能烧写到/dev/sda 或 sda1 设备里面!那是系统磁盘

烧写完成以后会在当前工程目录下生成一个 load.imx 的文件,load.imx 这个文件就是软件 imxdownload 根据 NXP 官方启动方式介绍的内容,在 led.bin 文件前面添加了一些数据头以后生成的。最终烧写到 SD 卡里面的就是这个 load.imx 文件,而非led.bin。至于具体添加了些什么内容,我们会在下一章讲解

程序的段定义:

汇编系统预定义了一些段名:
.text 表示代码段。
.data 初始化的数据段。
.bss 未初始化的数据段。
.rodata 只读数据段。

#用file命令查看gcc编译文件属性:
file  main.o  ledc.elf 
main.o:   ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), not stripped
ledc.elf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, not stripped
#用size命令查看gcc编译文件信息:
$size  main.o start.o  ledc.elf 
   text    data     bss     dec     hex filename
    460       0       0     460     1cc main.o
     28       0       0      28      1c start.o
    680       0       0     680     2a8 ledc.elf

下面是正点原子裸机led实例C语言版本的Makefile:

objs := start.o main.o
ledc.bin:$(objs)
	arm-linux-gnueabihf-ld -Ttext 0X87800000 -o ledc.elf $^
	arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@
	arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis
%.o:%.s
	arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
%.o:%.S
	arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
%.o:%.c
	arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
clean:
	rm -rf *.o ledc.bin ledc.elf ledc.dis

下面是正点原子裸机led实例C语言版本的start.s:

.global _start  /* 全局标号 */

/*
 * 描述:_start函数,程序从此函数开始执行,此函数主要功能是设置C 运行环境。
 */
_start:
	/* 进入SVC模式 */
	mrs r0, cpsr
	bic r0, r0, #0x1f 	/* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
	orr r0, r0, #0x13 	/* r0或上0x13,表示使用SVC模式*/
	msr cpsr, r0		/* 将r0 的数据写入到cpsr_c中 */

	ldr sp, =0X80200000	/* 设置栈指针 */
	b main			/* 跳转到main函数  */

注意“-nostdlib”编译选项,他的意思时不用C、C++标准库,标准库里面有”_start”汇编标号,不用C库那么就要自己写一个汇编文件 初始化栈的指针并跳转到main函数,本例就是start.s。在连接的时候如果用户不指定链接地址,编译器会自动分配链接地址

赞(0) 打赏
未经允许不得转载:Mr.Zhang » 链接地址-裸机与uBoot开发

你的打赏是我的动力

登录

找回密码

注册