应用程序命令行宏定义方式实现
在u-boot开发过程中,发现u-boot命令添加非常方便,在任何编译的.c文件里面增加u_boot_CMD类似的一个命令定义,并实现命令函数,就能在命令行执行该命令。对于这点一直觉得很神奇,也按照这个格式增加了很多命令,但不知道怎么实现的。
现在想自己写个控制台程序,也想使用这种方式方便增加自己的命令,于是参照u-boot研究了一下。发现u-boot宏定义了一个“.u_boot_cmd”的Section,在command.h文件:
extern cmd_tbl_t __u_boot_cmd_start;
extern cmd_tbl_t __u_boot_cmd_end;
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
在u-boot.lds文件又有这段描述:
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
看起来,__u_boot_cmd_start和__u_boot_cmd_end是在lds文件里面定义出来的,所以命令结构通过预处理,存放到了“.u_boot_cmd”的Section里面。
那么在应用程序中,如何通过lds文件实现上述功能?
步骤:
1) 生成lds文件,方法:
ld --verbose得到默认的ld script
去掉头部和尾部,这里保存为cmd.ld文件。注意:如果arm交叉编译的话,ld前面要加 arm-linux-ld --verbose > cmd.lds
生成的lds文件有头,不符合lds文件语法,需要屏蔽掉。
2) 在Makefile中通过:
$(GCC) -T$(LDSCRIPT) -o $@ $^
指定链接时使用的lds文件,如果不指定则用默认的lds文件;
(此步骤,验证生成的lds文件是合法的,起到作用的)
3)在SECTIONS增加新的端定义,添加位置在
__bss_start = .;
__bss_start__ = .;之前
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .; -T指定连接文件,这样“u_boot_cmd”Section就生效了
gcc等编译器内置有缺省的连接脚本。如果采用缺省脚本,则生成的目标代码需要操作系统才能加载运行。为了能在嵌入式系统上直接运行,需要编写自己的连接脚本文件。编写连接脚本,首先要对目标文件的格式有一定了解。GNU编译器生成的目标文件缺省为elf格式。elf文件由若干段(section)组成,如不特殊指明,由C源程序生成的目标代码中包含如下段:.text(正文段)包含程序的指令代码;.data(数据段)包含固定的数据,如常量、字符串;.bss(未初始化数据段)包含未初始化的变量、数组等。C++源程序生成的目标代码中还包括.fini(析构函数代码)和.init(构造函数代码)等。有关elf文件格式,读者可自行参考相关资料。连接器的任务就是将多个目标文件的.text、.data和.bss等段连接在一起,而连接脚本文件是告诉连接器从什么地址开始放置这些段。例如笔者的引导程序连接文件link.lds为:
ENTRY(begin)
SECTION
{ .=0x00300000;
.text : { *(.text) }
.data: { *(.data) }
.bss: { *(.bss) }
}
其中,ENTRY(begin)指明程序的入口点为begin标号;.=0x00300000指明目标代码的起始地址为0x00300000,这一段地址为MX1的片内RAM;.text : { *(.text) }表示从0x00300000开始放置所有目标文件的代码段,随后的.data: { *(.data) }表示数据段从代码段的末尾开始,再后是.bss段。
注意makefile:
all:后面跟的是要生成的目标文件。all可以有一个或者多个目标文件。
all: $(TARGET)
%.o:%.c
$(CC) -c $(CFLAGS) $< -o $@
$(TARGET): main.o console.o command.o cmd_date.o
# $(GCC) $(CFLAGS) -o $@ $^
$(GCC) -T$(LDSCRIPT) -o $@ $^
all是个伪目标,是所有目标的目标,其功能是编译所有的目标。
如果makefile中不写all的话,则默认遇到makefile中第一个目标为默认目标。
C/C++ code.PHONY:all
all:prog1 prog2 prog3 prog4
要编译prog1 prog2 prog3 prog4 四个目标,我们可以使用 make all 命令来编译所有的目标。
也可以 make prog3单独编译 prog3 目标。