第一段程序:
helloword.c
#define UFCON0 ((volatile unsigned int *)(0x50000020))
void helloword(void){
const char* p = "helloword\n";
while(*p){
*UFCON0 = *p++;
};
while(1);
}
UFCON0代表串口寄存器的地址,将数据写入这个寄存器,就会通过这个串口将数据发送出去
编译命令:
arm-elf-gcc -O2 -c helloword.c
-O是优化选项
-c编译成目标文件.o
此时是无法找到函数入口的,因为没有main函数同时也没有用连接脚本手动指向函数入口
因此手动指定函数入口以及链接生成可执行文件
arm-elf-ld -e helloword -Ttext 0x0 helloword.o -o helloword
-e helloworld是指定函数入口,这里边指定helloword函数是入口
-Ttext 0x0是指定函数运行的基地址,也就是说,链接工具将目标文件的helloworld函数链接到了内存的0地址上去执行
内存0地址:ARM体系结构中代码都是从0地址开始运行
-o生成可执行文件,此时helloworld是ELF格式的可执行文件,还是不能够直接加载到硬件中去执行
生成.bin文件命令:
arm-elf-objcopy -O binary helloword helloword.bin
arm-elf-objcopy命令是将ELF格式的文件中的二进制机器码抽离出来的命令
由于ELF格式的文件除了实际的代码之外,还包含ELF格式的文件头,段头或节头等附加信息
抽离出来的.bin文件除了运行时的代码和数据之外不包含其他任何信息
应用程序中不用显示指定链接脚本,否则可能会与操作系统不匹配
但是操作系统代码与底层代码需要显示的指定连接脚本
一个典型的连接脚本
SECTIONS
{
. = 0x1000;
.text : {*(.text)} //可执行文件中所有的代码段的集合的起始地址就是在0x1000处
. = 0x8000000;
.data : {*(.data)}
.bss : {*(.bss)}
}
SECTIONS,这个命令是用来描述输出文件的内存分布的
代码段分配在0x1000处,data段分配在0x8000000处,bss段紧跟在data段后
.号是可赋值的,. = 0x1000;意义就是0x1000是起始地址后边跟着的是描述.text是代码段
例子:
helloword.lds
ENTRY(helloword) //等价于-e选项,指定函数入口
SECTIONS
{
. = 0x00000000;
.text : {*(.text)}
. = ALIGN(32); //ALIGN(n),就是按照n字节对齐
.data : {*(.data)}
. = ALIGN(32);
.bss : {*(.bss)}
}
使用链接脚本:
arm-elf-ld -T helloword.lds helloworld.o -o helloword
-T选项让链接器从链接脚本读取并解析命令,然后按照命令的要求链接生成可执行文件