昨天和大家分享了一些裸板程序开发的流程,今天小伙伴可以根据我写的内容简单的写一下,你人生的第一个裸板程序(其中有一些具体的源代码以个人爱好进行写,有小伙伴需要的可以私信)
1.上位机linux系统编辑LED裸板程序代码,具体实施步骤如下:
sudo chown tarena /opt -R //将/opt目录的root用户权限修改为tarena用户
sudo chgrp tarena /opt -R //将/opt目录的root组权限修改为tarena组
结果是tarena普通用户也可以访问/opt目录了,后面嵌入式的代码都放在/opt目录中
mkdir -p /opt/arm/day01/1.0
cd /opt/arm/day01/1.0
vim led.h //各种声明
vim led.c //各种定义
vim main.c //负责调用
2.上位机安装交叉编译器(针对于ARM架构的gcc)
获取交叉编译器:porting_resource.rar/交叉编译器/arm-cortex_a9-eabi-4.7-eglibc-2.18.tar.gz
cp arm-cortex_a9-eabi-4.7-eglibc-2.18.tar.gz /opt/
cd /opt/
tar -xvf arm-cortex_a9-eabi-4.7-eglibc-2.18.tar.gz
mv arm-cortex_a9-eabi-4.7-eglibc-2.18 toolchains //对生成的目录重命名为toolchains
sudo vim /etc/environment
打开此文件在PATH环境变量中添加:/opt/toolchains/bin
结果如下:PATH="/opt/toolchains/bin:........" //注意:.......是省略了,你们千万别省略----设置PATH环境变量是为了让系统知道在哪里可以找到可执行文件,从而在命令行中可以直接使用命令名来执行这些文件,而不需要指定完整路径。不用写 ------./
保存退出
重启上位机linux系统
验证交叉编译器是否生效,执行以下命令:
arm-cortex_a9-linux-gnueabi-gcc -v //查看交叉编译器的版本,如果能够看到说明安装成功
3.上位机交叉编译LED裸板程序,具体实施步骤如下:
cd /opt/arm/day01/1.0
arm-cortex_a9-linux-gnueabi-gcc -nostdlib -c -o led.o led.c
arm-cortex_a9-linux-gnueabi-gcc -nostdlib -c -o main.o main.c
说明:
arm...gcc:C语言程序的交叉编译器
-nostdlib:告诉编译器一律不允许使用标准C库,此程序乃裸板程序
-c:只编译不链接
arm-cortex_a9-linux-gnueabi-ld -nostartfiles -nostdlib -Ttext=0x48000000 -emain -o led.elf main.o led.o
说明:
arm...ld:链接器,将所有的.o文件组合在一起
-nostartfiles:告诉链接器,由于是裸板程序,无需添加相关的启动代码(就是程序的真正入口_start->main)
-Ttext=0x48000000:指定程序代码段的起始地址为0x48000000
-emain:指定程序的入口函数为main
-o led.elf:生成elf格式的可执行文件
注意:elf格式的可执行文件只能运行在操作系统下,此文件的格式:文件信息头+纯正的二进制代码+文件信息尾
在操作系统下,操作系统会帮你提取纯正的二进制代码最终运行
arm-cortex_a9-linux-gnueabi-objcopy -O binary led.elf led.bin
说明:
arm...objcopy:二进制文件处理功能,提取纯正的二进制代码
类似去皮的工具,led.elf:类似带皮的核桃,led.bin:类似核桃里面的果肉,不带皮
led.elf:带皮的核桃,操作系统会去皮,也可以手动利用arm...objcopy去皮
led.bin:果肉,不带皮,是真正要运行的代码文件
cp /opt/arm/day01/1.0/led.bin /tftpboot //拷贝led.bin到tftp网络下载服务的共享目录中
4.下位机测试
重启下位机,进入uboot命令行模式执行:
print //查看serverip和ipaddr环境变量对应的地址是否正确
ping 192.168.1.8
tftp 0x48000000 led.bin
go 0x48000000
5.掌握反汇编工具(objdump),具体实验步骤如下:
a)上位机执行:
cd /opt/arm/day01/1.0
arm-cortex_a9-linux-gnueabi-gcc -nostdlib -c -o led.o led.c //只编译不链接,不允许用标准C库的代码
arm-cortex_a9-linux-gnueabi-gcc -nostdlib -c -o main.o main.c
arm-cortex_a9-linux-gnueabi-ld -nostartfiles -nostdlib -Ttext=0x48000000 -emain -o led.elf main.o led.o //链接
arm-cortex_a9-linux-gnueabi-objcopy -O binary led.elf led.bin //提取纯正的二进制代码
arm-cortex_a9-linux-gnueabi-objdump -D led.elf > led.dis
说明:
arm...objdump:反汇编工具,将二进制可执行文件翻译生成汇编文件
gcc正向编译:.c->.i->.s->.o->可执行程序
反汇编/逆向编译:可执行文件或者.o->.s
led.dis:就是生成的反汇编文件,里面就是汇编代码
vim led.dis //打开反汇编文件,得到:
led.elf: 文件格式 elf32-littlearm //ELF格式,32位,小端模式,arm模式 ,后续详解
Disassembly of section .text: //以下就是代码段的内容
48000000 <main>: //说明main函数的内存起始地址就是0x48000000
//也就是说内存的0x48000000这个地址存放的是main函数
将来在下位机执行go 0x48000000时,实际是让cpu直接运行main函数
至于其他的信息后续课程详解!
b)下位机测试:
重启下位机,进入uboot命令行模式执行:
print //查看serverip和ipaddr环境变量对应的地址是否正确
ping 192.168.1.8 //先验证网络是否联通
tftp 0x48000000 led.bin //下载内容到下位机内存0x48000000
go 0x48000000 ->运行0x48000000地址上的程序(led.bin)
6.案例:继续玩转反汇编工具
a)上位机实施步骤:
cp /opt/arm/day01/1.0 /opt/arm/day01/2.0 -fr
cd /opt/arm/day01/2.0
vim led.c
将delay函数或者led_init,除了入口函数main其余的任意函数定义的代码放到入口函数main的前面
保存退出
arm-cortex_a9-linux-gnueabi-gcc -nostdlib -c -o led.o led.c //只编译不链接,不允许用标准C库的代码
arm-cortex_a9-linux-gnueabi-gcc -nostdlib -c -o main.o main.c
arm-cortex_a9-linux-gnueabi-ld -nostartfiles -nostdlib -Ttext=0x48000000 -emain -o led.elf main.o led.o//链接
arm-cortex_a9-linux-gnueabi-objcopy -O binary led.elf led.bin //提取纯正的二进制代码
拷贝到windows的fastboot目录中
arm-cortex_a9-linux-gnueabi-objdump -D led.elf > led.dis
vim led.dis 得到:
48000000 <led_init>: //此时下位机内存的0x48000000地址放的不再是入口函数main,而出第一个函数led_init
将来在下位机执行go 0x48000000肯定无法再循环开关灯了,因为cpu执行led_init
并不会执行main函数
b)下位机测试:
重启下位机,进入uboot命令行模式执行:
print //查看serverip和ipaddr环境变量对应的地址是否正确
ping 192.168.1.8 //先验证网络是否联通
tftp 0x48000000 led.bin //下载内容到下位机内存0x48000000
go 0x48000000 ->运行0x48000000地址上的程序(led.bin)//实际是让cpu直接去执行led_init函数,无法再开关灯了
c)上位机实施步骤:
cp /opt/arm/day01/1.0 /opt/arm/day01/3.0 -fr
arm-cortex_a9-linux-gnueabi-gcc -nostdlib -c -o led.o led.c //只编译不链接,不允许用标准C库的代码
arm-cortex_a9-linux-gnueabi-gcc -nostdlib -c -o main.o main.c
arm-cortex_a9-linux-gnueabi-ld -nostartfiles -nostdlib -Ttext=0x48000000 -emain -o led.elf led.o main.o //链接
arm-cortex_a9-linux-gnueabi-objcopy -O binary led.elf led.bin //提取纯正的二进制代码
拷贝到windows的fastboot目录中
arm-cortex_a9-linux-gnueabi-objdump -D led.elf > led.dis
vim led.dis 得到:
go 0x48000000 <led_init>: //此时下位机内存的0x48000000地址放的不再是入口函数main,而出第一个函数led_init
将来在下位机执行go 0x48000000肯定无法再循环开关灯了,因为cpu执行led_init
并不会执行main函数
b)下位机测试:
重启下位机,进入uboot命令行模式执行:
print //查看serverip和ipaddr环境变量对应的地址是否正确
ping 192.168.1.8 //先验证网络是否联通
tftp 0x48000000 led.bin //下载内容到下位机内存0x48000000
go 0x48000000 ->运行0x48000000地址上的程序(led.bin)//实际是让cpu直接去执行led_init函数,无法再开关灯了
c)切记:裸板程序在链接时(arm...ld)是从文件的开头依次向下开始一个函数一个函数的链接
所以就需要将入口函数放到文件的最开头,如果是多个文件(.o)
则将入口函数所在的.o文件放到第一个去连接:
例如:arm...ld .... -o xxx.elf A.o B.o C.o ... //A.o文件中的第一个函数就是入口函数,所以A.o放到最开头