交叉编译环境

嵌入式交叉编译环境简单使用教程。


GCC交叉编译工具链

安装

将光盘中toolchain工具链拷贝到linux中,选择一个目录安装,我安装在/usr/local/arm目录下,没有该目录可以自行创建

设置环境变量

设置环境变量,可以编辑家目录下的”.bashrc”文件,添加”export PATH=$PATH:/usr/local/arm/bin”,然后执行命令”source .bashrc”即可.

可以为每一个命令都配置一个软连接来简化命令记忆,并且可以达到上面的效果,但是我比较懒,就不想做了:( …

GCC基本入门

基本后缀选项

  • -o:指定输出文件

基本命令

  • gcc:编译指令
    • -c:只编译,不进行链接
  • ld:链接指令,得到elf可执行文件,linux中可以直接执行elf文件,但是在嵌入式裸机中需要把elf转换成可烧写的bin或者img文件
    • -Ttext:指定链接基地址,就是该程序要链接到的位置
  • objcopy:将可执行文件转换为可烧写的bin或者img文件
    • -O:指定文件格式”binary”二进制文件
  • objdump:将可执行文件反汇编得到一个dis反汇编文件
    • -D: 反汇编

objdump反汇编工具

GCC中的反汇编工具,将链接好的elf可执行程序进行反汇编,得到dis汇编源代码,常用格式:arm-linux-objdump -D led_usb.bin > led_usb.dis
- -D指明要进行反汇编
- >用于链接反汇编原文件和输出文件

反汇编可以帮助我们分析理解程序,尤其是链接脚本和链接地址,还可以帮助我们进行程序的调试,以下是led_usb.elf的反汇编代码:


led.elf:     file format elf32-littlearm    // 文件名:文件格式(大小端)


Disassembly of section .text:   // 代码段

00000000 <_start>:  // 地址 <标号>
   0:   e59f009c    ldr r0, [pc, #156]  ; a4 <delay_loop+0x10>       // 链接地址:机器码 汇编指令;
   4:   e59f109c    ldr r1, [pc, #156]  ; a8 <delay_loop+0x14>
   8:   e5810000    str r0, [r1]
   c:   e3a04010    mov r4, #16
  10:   e59f5094    ldr r5, [pc, #148]  ; ac <delay_loop+0x18>
  14:   e5854000    str r4, [r5]

00000018 <flash>:
  18:   e3a00002    mov r0, #2
  1c:   e59f108c    ldr r1, [pc, #140]  ; b0 <delay_loop+0x1c>
  20:   e5810000    str r0, [r1]
  24:   e3e00008    mvn r0, #8
  28:   e59f1084    ldr r1, [pc, #132]  ; b4 <delay_loop+0x20>
  2c:   e5810000    str r0, [r1]
  30:   eb000015    bl  8c <delay>
  34:   e3a00002    mov r0, #2
  38:   e59f1070    ldr r1, [pc, #112]  ; b0 <delay_loop+0x1c>
  3c:   e5810000    str r0, [r1]
  40:   e3e00010    mvn r0, #16
  44:   e59f1068    ldr r1, [pc, #104]  ; b4 <delay_loop+0x20>
  48:   e5810000    str r0, [r1]
  4c:   eb00000e    bl  8c <delay>
  50:   e3a00002    mov r0, #2
  54:   e59f1054    ldr r1, [pc, #84]   ; b0 <delay_loop+0x1c>
  58:   e5810000    str r0, [r1]
  5c:   e3e00020    mvn r0, #32
  60:   e59f104c    ldr r1, [pc, #76]   ; b4 <delay_loop+0x20>
  64:   e5810000    str r0, [r1]
  68:   eb000007    bl  8c <delay>
  6c:   e3a00000    mov r0, #0
  70:   e59f1038    ldr r1, [pc, #56]   ; b0 <delay_loop+0x1c>
  74:   e5810000    str r0, [r1]
  78:   e3a00038    mov r0, #56 ; 0x38
  7c:   e59f1030    ldr r1, [pc, #48]   ; b4 <delay_loop+0x20>
  80:   e5810000    str r0, [r1]
  84:   eb000000    bl  8c <delay>
  88:   eaffffe2    b   18 <flash>

0000008c <delay>:
  8c:   e59f2024    ldr r2, [pc, #36]   ; b8 <delay_loop+0x24>
  90:   e3a03000    mov r3, #0

00000094 <delay_loop>:
  94:   e2422001    sub r2, r2, #1
  98:   e1520003    cmp r2, r3
  9c:   1afffffc    bne 94 <delay_loop>
  a0:   e1a0f00e    mov pc, lr
  a4:   11111111    tstne   r1, r1, lsl r1
  a8:   e0200240    eor r0, r0, r0, asr #4
  ac:   e02000a0    eor r0, r0, r0, lsr #1
  b0:   e02000a4    eor r0, r0, r4, lsr #1
  b4:   e0200244    eor r0, r0, r4, asr #4
  b8:   0098967f    addseq  r9, r8, pc, ror r6

Disassembly of section .ARM.attributes:

00000000 <.ARM.attributes>:
   0:   00001341    andeq   r1, r0, r1, asr #6
   4:   61656100    cmnvs   r5, r0, lsl #2
   8:   01006962    tsteq   r0, r2, ror #18
   c:   00000009    andeq   r0, r0, r9
  10:   01080106    tsteq   r8, r6, lsl #2

烧录的bin文件其实最终都是将一条条指令写入,每条指令都有一个指令地址,这个指令地址由链接脚本决定,上边的程序在编译时使用使用:arm-none-eabi-ld -Ttext 0x0 -o led.elf $^语句指定了链接地址为0x0,所以可以看到程序的代码段是从00000000地址开始的

makeFile入门

使用makeFile以便于进行C语言工程的编译。

makeFile基本格式

makeFile基本格式如下:
目标: 依赖1 依赖2 ……
命令

例如:
helloworld: helloworld.c helloworld.h
gcc helloworld.c
则只要执行make,目标helloworld就会被编译出来

makeFile特点

  1. 目标不限于可执行的二进制程序,可以是任何使用命令可以生成的结果
  2. 依赖是用于生成目标的原材料,包括且不限于可执行程序,动态/静态库,源文件和头文件等
  3. 命令不限于linux中所有可以执行的命令
  4. 命令之前不能使用空格,只能是一个Tab
  5. linux中makeFile文件名一般命名为”makeFile”,也可以使用-f指定要使用的makeFile文件

makeFile规则

显示规则

  1. 格式:%.o:%.S/c:表示所有的以.o结尾的目标都是以.S/c为依赖编译而来的
  2. $@:自动变量,指代当前的目标
  3. $<:自动变量,指代当前所有的依赖

mkv210_image.c

该文件不是在嵌入式环境下执行的,是在宿主机上用于将用于USB启动使用的镜像转换为SD卡启动使用的镜像,由于是在宿主机上运行,所以编译该文件时不能使用arm-linux-gcc,而要使用宿主机上的gcc.
编译和使用步骤为:

  • gcc mkv210_image.c -o x210
  • ./x210 led_usb.bin led_sd.bin

SD卡启动和USB启动

启动区别

  1. 当SD卡启动时,BL0需要检查校验和,所以要在前16个字节上填充上校验和,而且需要烧写到0xd0020000位置上,才可以执行
  2. 当USB启动时BL0读取到BL1时不检查校验和,直接从BL1的实际内容开始执行,跳过了前面的16个字节,执行地址为0xd0020010,那我们的镜像在USB启动时前面不需要放置校验和,并直接将镜像下载到0xd0020010地址即可,

mkv210_image.c

作用

我们在编译链接得到led_usb.bin,是要执行的实际内容,没有添加校验和,所以只能在USB启动的时候烧写到0xd0020010执行,要使用SD卡启动,就需要添加校验和,mkv210_image.c就做了这个工作:

工作流分析

宏定义
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define BUFSIZE                 (16*1024)
#define IMG_SIZE                (16*1024)
#define SPL_HEADER_SIZE         16
//#define SPL_HEADER              "S5PC110 HEADER  "
#define SPL_HEADER              "****************"
部分1
FILE        *fp;
char        *Buf, *a;
int     BufLen;
int     nbytes, fileLen;
unsigned int    checksum, count;
int     i;

// 1. 3个参数
if (argc != 3)
{
    printf("Usage: %s <source file> <destination file>\n", argv[0]);
    return -1;
}

检查参数,由于我们需要传入两个文件名,所以参数个数必须是3.

部分2
// 2. 分配16K的buffer
BufLen = BUFSIZE;
Buf = (char *)malloc(BufLen);
if (!Buf)
{
    printf("Alloc buffer failed!\n");
    return -1;
}

memset(Buf, 0x00, BufLen);

使用malloc分配16K的内存区域,memset由于初始化该内存区域,全部写成0

部分3
// 3. 读源bin到buffer
// 3.1 打开源bin
fp = fopen(argv[1], "rb");
if( fp == NULL)
{
    printf("source file open error\n");
    free(Buf);
    return -1;
}
// 3.2 获取源bin长度
fseek(fp, 0L, SEEK_END);                                // 定位到文件尾
fileLen = ftell(fp);                                    // 得到文件长度
fseek(fp, 0L, SEEK_SET);                                // 再次定位到文件头
// 3.3 源bin长度不得超过16K-16byte
count = (fileLen < (IMG_SIZE - SPL_HEADER_SIZE))
    ? fileLen : (IMG_SIZE - SPL_HEADER_SIZE);
// 3.4 buffer[0~15]存放"S5PC110 HEADER  "
memcpy(&Buf[0], SPL_HEADER, SPL_HEADER_SIZE);
// 3.5 读源bin到buffer[16]
nbytes = fread(Buf + SPL_HEADER_SIZE, 1, count, fp);
if ( nbytes != count )
{
    printf("source file read error\n");
    free(Buf);
    fclose(fp);
    return -1;
}
fclose(fp);

读取源文件到分配的内存空间中,由于我们分配的内存空间为16K,后续还要加上16byte的头信息,所以源文件不能大于16K-16byte,然后跳过内存空间的前16byte,将源文件写入到后面的区域,

第四步
// 4. 计算校验和
    // 4.1 从第16byte开始统计buffer中共有几个1
// 4.1 从第16byte开始计算,把buffer中所有的字节数据加和起来得到的结果
a = Buf + SPL_HEADER_SIZE;
for(i = 0, checksum = 0; i < IMG_SIZE - SPL_HEADER_SIZE; i++)
    checksum += (0x000000FF) & *a++;
// 4.2 将校验和保存在buffer[8~15]
a = Buf + 8;                            // Buf是210.bin的起始地址,+8表示向后位移2个字,也就是说写入到第3个字
*( (unsigned int *)a ) = checksum;

计算源文件的校验和,写到内存空间8-15个字节中,0~7存放”********”,前面可以看到

部分5
// 5. 拷贝buffer中的内容到目的bin
// 5.1 打开目的bin
fp = fopen(argv[2], "wb");
if (fp == NULL)
{
    printf("destination file open error\n");
    free(Buf);
    return -1;
}
// 5.2 将16k的buffer拷贝到目的bin中
a = Buf;
nbytes  = fwrite( a, 1, BufLen, fp);
if ( nbytes != BufLen )
{
    printf("destination file write error\n");
    free(Buf);
    fclose(fp);
    return -1;
}

free(Buf);
fclose(fp);

return 0;

打开目的文件,并将内存空间中的数据写入到目的文件中,释放内存空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值