首页嵌入式 › 裸板编程基础知识

裸板编程基础知识

一、交叉编译工具选项说明
1、arm-linux-gcc
我们知道一个c/c++文件要经过预处理、编译、汇编、连接四个步骤才能编程可执行文件
预处理:主要讲include文件插入到源文件中、将宏定义展开、根据条件编译命令选择要使用的代码,它用到arm-linux-ccp工具。

编译:将c/c++翻译成汇编,用到ccl工具
汇编:将汇编翻译成一定格式机器代码,用到的是arm-linux-as工具
连接:连接就是讲上步生成的目标文件盒系统库的目标文件、库文件连接起来,最终生成可以在特定平台上运行的可执行文件,用到arm-linux-ld工具
我们通过例子来说明:
arm-linux-gcc -o hello hello.c  :将hello.c预处理、编译、编译、连接称为文件hello,-o后跟目标
arm-linux-gcc -v -o hello hello.c :-v表示显示编译的细节
arm-linux-gcc -c -o hello hello.c: -c表示不进行连接,这个经常使用,因为我们需要对连接做一些配置,所以经常需要单独连接
arm-linux-gcc -S -o hello hello.c:-S表示编译后就停下来,不进行汇编和连接
arm-linux-gcc -E -o hello hello.c:-E表示预处理后即停下来,不进行编译、汇编和连接
arm-linux-gcc -g -o hello hello.c:-g表示产生调试信息
arm-linux-gcc -static -o hello hello.c:-static表示静态连接
2、arm-linux-ld
-Ttext startaddr:指定代码段
-Tdata startaddr:指定数据段
-Tbbs startaddr:指定bbs段
还是来看例子:
arm-linux-ld -Ttext 0x00000000 -g led_on.o -o led_on_elf:将led_on.o的代码段的地址设置为0x000000000,可执行文件名为led_on_elf。
但其实我们并不会单独对每一个段都进行连接,我们一般会写一个连接脚本,如leds.lds,这连接脚本里面我们定义好了各个段的连接地址,一般只需要定义代码段的起始地址,其它段依次排下去,比如:
SECTIONS {
. = 0x00;
  .text          :   { *(.text) }
.rodata ALIGN(4) : {*(.rodata)} 
  .data ALIGN(4) : { *(.data) }
  .bss ALIGN(4)  : { *(.bss)  *(COMMON) }
}
这个脚本就定义了最终编译出来的代码的组织形式,这里我们代码段的起始地址定义为0x00,其它段一次排下去。但这里有一个问题,其它段没问题,但是代码段的顺序很重要啊,我们需要指定,这就是我们要额外做的了:
arm-linux-ld -Tleds.lds  crt0.o leds.o -o leds_elf
它的意思就是根据连接脚本leds.lds来连接,指定连接顺序是先crt0.o,然后是leds.o
3、arm-linux-objcopy
用来复制一个目标文件的内容到另一个文件中,可以使用不同于源文件的格式来输出目的文件,即可以进行格式转换。我们常用arm-linux-objcopy来将ELF格式的可执行文件转换为二进制文件。我们来看一下一些选项:
-l bfdname:用来指明源文件的格式
-O用来指定输出文件的格式
-F bfdname:同时指明源文件和目的文件的格式。将源文件的内容复制到目的文件的过程中只进行复制不做格式的转换,源目标文件是什么格式,目的文件就是什么格式
-R sectionname:从输出文件中删掉所有名为sectionname的段
-g:不从源文件中复制调试符号到目标文件中去
-S表示不从源文件中复制从定位信息和符号信息到目标文件中去
如:arm-linux-objcopy -O binary -S leds_elf leds.bin:-O用来指定输出文件的格式,-S表示不从源文件中复制从定位信息和符号信息到目标文件中去。
4、arm-linux-objdump
用于显示二进制文件信息,可用于查看反汇编代码
-b bfdname:指定目标码格式
-d:反汇编可执行段
-D:反汇编所有段
-EB:指定字节序
-f:显示文件的整体头部摘要信息
-h:显示目标文件各个段的头部摘要信息
-i:显示支持的目标文件格式和cpu构建,在-b,-m中用到
-j name:仅显示指定段的信息
-m machine:指定反汇编目标文件时使用的构架
如:arm-linux-objdump -D -m arm  leds_elf > leds.dis
二、Makefile介绍
1、赋值方法
immediate = deferred   :定义延时变量,使用时才展开
immediate?=deferred   :定义延时变量,使用时才展开,?=仅仅在变量还没有定义的情况下生效
immediate:=immediate :定义立即变量,立即展开
immediate+=deferred or immediate :如果右边变量是立即变量,它也是定义立即变量。否则是定义延时变量
define immediate  :以下三行定义延时变量
deferred
endef
2、常用函数
函数调用格式如下:$(function argument1,argument2,……)
(1)字符串替换和分析函数
<1>$(subst from,to,text):在文本text中,用to替换每一处的from
如:$(subst,ee,EE,feet on the street),结果为:fEEt on the strEE
<2>$(patsubst pattern,replacement,text):寻找text中符合格式pattern的字,用replacement来替换它们。pattern和replacement可以使用通配符
如:$(patsubst %.c,%.o,x.c bar.c),结果为:x.o bar.o
<3>$(strip string):去掉前导和结尾空格,并将中间的多个空格压缩为单个空格
如:$(strip a    b c),结果为:”a b c”
<4>$(findstring find,in):在字符串”in“中搜寻”find“,如果找到返回值为”find”,否则返回值为空
如:$(findstring a,a b c)结果为:返回值为”a“
       $(findstring a,b c)结果为:返回值为空
<5>$(filter pattern…,text):返回在”text”中由空格隔开且匹配格式”pattern…”的字,去除不符合格式”pattern..“的字。
如:$(filter %.c %.s,foo.c bar.c baz.s ugh.h)结果为:foo.c bar.c baz.s
<6>$(filter-out pattern…,text): 返回在”text”中由空格隔开且不匹配格式”pattern…”的字,去除符合格式”pattern..“的字。它是filter的反函数
如:$(filter-out %.c %.s,foo.c bar.c baz.s ugh.h)结果为:ugh.h

<7>$(sort list):将”list”中的字按字母顺序排序,并去掉重复的字。输出由单个空格隔开的字的列表

如:$(sort foo bar lose):结果为:返回”bar foo lose”
(2)文件名函数
<1>$(dir names…):抽取”names…”中每一个文件名的路径部分,文件名的路径部分包括从文件名的首字符到最后一个斜杠(含斜杠)之前的一切字符
如:$(dir src/foo.c hacks):结果为”src/ ./”
<2>$(notdir names…):抽取”name…”中每一个文件名中除路径外的一切字符(真正的文件名)
如:$(notdir src/foo.c hacks):结果为”foo.c hacks“
<3>$(suffix names…):抽取”name…”中每一个文件名的后缀
如:$(suffix src/foo.c src-1.0/bar.c hacks):结果为”.c .c“
<4>$(basename names…):抽取”names…”中每一个文件名中除后缀外的一切字符
如:$(basename src/foo.c src-1.0/bar.c hacks):结果为”src/foo src-1.0/bar hacks“
<5>$(addsuffix suffix,names…):”names”为文件列表,suffix为后缀,这个函数就是将后缀添加给文件
如:$(addsuffix .c,foo bar):结果为”foo.c bar.c“
<6>$(addprefix prefix,names…):”names”为文件列表,prefix为前缀,这个函数就是将前缀加给文件
如:$(addprefix src/,foo bar):结果为”src/foo src/bar”
<7>$(wildcard pattern):这个函数的结果是一列和格式匹配且真实存在的文件的名称,文件名之间用一个空格隔开
如:若当前目录下有文件1.c 2.c 1.h 2.h,则:
c_src:=$(wildcard *.c):结果是”1.c 2.c“
(3)其它函数
<1>$(foreach var,list,text):首先将list扩展所得的每个字都赋给”var”变量,然后”text”引用该变量进行扩展,因此每次扩展都不相同
如:dirs := a b c d
       files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))
       结果是:$(wildcard a/*)
                      $(wildcard b/*)
                      $(wildcard c/*)
                      $(wildcard d/*)
<2>$(if condition,then-part[,else-part]):首先把condition的前导空格、结尾空格去掉,然后扩展。如果扩展为非空字符串,则条件condition为真,如果为空字符串,则条件condition为假。若condition为真,则计算then-part的值,并将该值作为整个函数if的值。若condition为假,并且第三个参数存在,则计算第三个参数else-part的值,并将该值作为整个函数if的值,如果第三个参数不存在,函数if将什么也不计算,返回空值。
<3>$(origin variable)
三、常用的ARM汇编指令
1、相对跳转指令:
b和bl,bl除跳转之外还将返回地址保存在lr寄存器中。这两条指令的跳转范围是当前指令的前后32Mb,而且他们是位置无关的指令
2、数据传送指令:
mov r1,r2               @r1=r2
mov r1,#4096        @r1=4096
3、地址读取伪指令:
ldr r1,=4097    @r1=4097
ldr r,=labe1     @获得指令的绝对地址
labe1:
……………..
4、内存访问指令:ldr、str、ldm、stm
ldr:从内存读取数据到寄存器,ldr r1,[r2,#4]:将地址为r2+4的内存单元的数据读到寄存器r1
str:把寄存器数据存储到内存中,str r1,[r2]:将r1的数据保存到地址为r2的内存单元中
ldm:将内存块的数据写到寄存器列表中
str:将寄存器列表的数据写到内存中
5、加减指令:add、sub
add:加
sub:减
6、程序状态寄存器访问指令:msr、mrs
msr cpsr,r0  :复制r0到cpsr中
mrs r0,cpsr  :复制cpsr到r0中
7、其它伪指令,例:
.extern main
.text
.global _start
_start:
.extern:定义一个外部符号(可以是变量也可以是函数),上面的代码表示本文件中引用的main是一个外部函数
.text:表示下面的语句都属于代码段
.global:将本文件中的某个程序标号定义为全局的,比如上面的代码表示_start是个全局函数

发表评论