程序设计方案基础

上传人:夏** 文档编号:592685479 上传时间:2024-09-22 格式:PPT 页数:200 大小:654KB
返回 下载 相关 举报
程序设计方案基础_第1页
第1页 / 共200页
程序设计方案基础_第2页
第2页 / 共200页
程序设计方案基础_第3页
第3页 / 共200页
程序设计方案基础_第4页
第4页 / 共200页
程序设计方案基础_第5页
第5页 / 共200页
点击查看更多>>
资源描述

《程序设计方案基础》由会员分享,可在线阅读,更多相关《程序设计方案基础(200页珍藏版)》请在金锄头文库上搜索。

1、第第 3 章章 程序设计基础程序设计基础GNU集成编译环境集成编译环境GCC(GNU Compiler Collection)是一种面向嵌入式领域、支持多种是一种面向嵌入式领域、支持多种编程语言、支持多种编程语言、支持多种CPU的交叉编译工具。的交叉编译工具。本章主要介绍:本章主要介绍:GCC编译过程编译过程C/C+交叉编译器交叉编译器arm-elf-gcc交叉汇编器交叉汇编器 arm-elf-as 交叉连接器交叉连接器arm-elf-l d工程管理器工程管理器make汇编语言编程汇编语言编程混合编程混合编程3.1 GNU GCC简介简介GNU GCC是一套面向嵌入式领是一套面向嵌入式领域的交

2、叉编译工具,支持多种编程语域的交叉编译工具,支持多种编程语言、多种优化选项并且能够支持分步言、多种优化选项并且能够支持分步编译、支持多种反汇编方式、支持多编译、支持多种反汇编方式、支持多种调试信息格式种调试信息格式,目前支持目前支持X86、ARM7、StrongARM、PPC4XX、MPC8XX、MIPS R3000等多种等多种CPU。 GNU GCC的基本功能包括:的基本功能包括:输出预处理后的输出预处理后的C/C+源程序(展开头源程序(展开头文件和替换宏)文件和替换宏)输出输出C/C+源程序的汇编代码源程序的汇编代码输出二进制目标文件输出二进制目标文件生成静态库生成静态库生成可执行程序生成

3、可执行程序转换文件格式转换文件格式3.1.1 GCC 组成1. C/C+交叉编译器交叉编译器arm-elf-gccarm-elf-gcc是编译的前端程序,它通过调是编译的前端程序,它通过调用其他程序来实现将程序源文件编译成目标文件用其他程序来实现将程序源文件编译成目标文件的功能。的功能。编译时,它首先调用预处理程序编译时,它首先调用预处理程序(cpp)对输对输入的源程序进行处理,然后调用入的源程序进行处理,然后调用 cc1 将预处理后将预处理后的程序编译成汇编代码,最后由的程序编译成汇编代码,最后由arm-elf-as将汇将汇编代码编译成目标代码。编代码编译成目标代码。arm-elf-gcc具

4、有丰富的命令选项,可以控具有丰富的命令选项,可以控制编译的各个阶段,满足用户的各种编译需求。制编译的各个阶段,满足用户的各种编译需求。2. 汇编器 arm-elf-asarm-elf-as将汇编语言程序转换为将汇编语言程序转换为ELF (Executable and Linking Format,执行时链接文件格式执行时链接文件格式)格式的可重定位目格式的可重定位目标代码,这些目标代码同其它目标模块或标代码,这些目标代码同其它目标模块或函数库易于定位和链接。函数库易于定位和链接。arm-elf-as产生一个交叉参考表和一产生一个交叉参考表和一个标准的符号表,产生的代码和数据能够个标准的符号表,

5、产生的代码和数据能够放在多个区放在多个区 (Section)中。中。3. 连接器连接器arm-elf-ldarm-elf-ld根据链接定位文件根据链接定位文件Linkcmds中的代码区、数据区、中的代码区、数据区、BSS区和栈区等定位区和栈区等定位信息,将可重定位的目标模块链接成一个单信息,将可重定位的目标模块链接成一个单一的、绝对定位的目标程序。一的、绝对定位的目标程序。该目标程序是该目标程序是ELF格式,并且可以包含格式,并且可以包含调试信息。调试信息。arm-elf-ld会产生一个内存映象文件会产生一个内存映象文件,该该文件显示所有目标模块、区和符号的绝对定文件显示所有目标模块、区和符号

6、的绝对定位地址。位地址。它也产生交叉参考列表,显示参考每个它也产生交叉参考列表,显示参考每个全局符号的目标模块。全局符号的目标模块。arm-elf-ld支持将多个目标模块链接成一支持将多个目标模块链接成一个单一的、绝对定位的目标程序,也能够依此个单一的、绝对定位的目标程序,也能够依此对目标模块进行链接,这个特性称为对目标模块进行链接,这个特性称为增量链接增量链接(Incremental Linking)。假如输入文件是一个函数库,假如输入文件是一个函数库,arm-elf-ld会自动从函数库装载被其它目标模块参考的函会自动从函数库装载被其它目标模块参考的函数模块。数模块。arm-elf-ld与其

7、它链接程序与其它链接程序相比相比,能提供,能提供更有帮助的诊断信息。许多链接器遇到第一个更有帮助的诊断信息。许多链接器遇到第一个错误即放弃链接,而错误即放弃链接,而arm-elf-ld只要有可能都只要有可能都继续执行,帮助用户识别其它错误,有时甚至继续执行,帮助用户识别其它错误,有时甚至能获得输出代码。能获得输出代码。 4. 库管理器库管理器arm-elf-ararm-elf-ar将多个可重定位的目标模块将多个可重定位的目标模块归档为一个函数库文件。归档为一个函数库文件。采用函数库文件,应用程序能够从该文采用函数库文件,应用程序能够从该文件中自动装载要参考的函数模块,同时将应件中自动装载要参考

8、的函数模块,同时将应用程序中频繁调用的函数放入函数库文件中,用程序中频繁调用的函数放入函数库文件中,易于应用程序的开发管理。易于应用程序的开发管理。arm-elf-ar支持支持ELF格式的函数库文件格式的函数库文件.5. 工程管理器工程管理器MAKEMake是用于自动编译、链接程序的实用是用于自动编译、链接程序的实用工具工具,使用使用make后就不需要手工的编译每个程后就不需要手工的编译每个程序文件。要使用序文件。要使用make,首先要编写首先要编写makefile。Makefile描述程序文件之间的依赖关系,描述程序文件之间的依赖关系,并提供更新文件的命令。在一个程序中,可执并提供更新文件的

9、命令。在一个程序中,可执行文件依赖于目标文件行文件依赖于目标文件,而目标文件依赖于源而目标文件依赖于源文件。如果文件。如果makefile文件存在文件存在,每次修改完源每次修改完源程序后,用户通常所需要做的事情就是在命令程序后,用户通常所需要做的事情就是在命令行敲入行敲入“make”,然后所有的事情都由然后所有的事情都由make来来完成。完成。6. 其他实用程序其他实用程序目标文件格式转换工具目标文件格式转换工具arm-elf-objcopy支持的文件格式有支持的文件格式有H-record、S-record、ABS、BIN、COFF、ELF。例如,它能够将例如,它能够将ELF格式文件转换为其格

10、式文件转换为其它格式的文件,如它格式的文件,如intel H-record格式、格式、Motorola S-record等。等。arm-elf-nm程程序用于显示文件中的符号信息。序用于显示文件中的符号信息。 3.1.2 GCC编译程序的基本过程编译程序的基本过程GCC编译程序的编译程序的基本过程基本过程如下:如下:arm-elf-gcc根据输入文件的后缀来确定根据输入文件的后缀来确定文件的类型,然后根据用户的编译选项(包括文件的类型,然后根据用户的编译选项(包括优化选项、调试信息选项等)将其编译成相应优化选项、调试信息选项等)将其编译成相应的汇编临时文件(后缀为的汇编临时文件(后缀为.s);

11、);arm-elf-as将该汇编文件编译成目标文件将该汇编文件编译成目标文件(后缀为(后缀为.o););arm-elf-ld 根据用户的链接选项(包括指根据用户的链接选项(包括指定链接命令文件等)将目标文件和各种库链接定链接命令文件等)将目标文件和各种库链接起来生成可执行文件。起来生成可执行文件。图图3-1展示了该编译过程:展示了该编译过程: 3.2 C/C+交叉编译器交叉编译器arm-elf-gcc3.2.1 3.2.1 概述概述概述概述arm-elf-gccarm-elf-gcc是编译的前端程序,它通过调用其是编译的前端程序,它通过调用其是编译的前端程序,它通过调用其是编译的前端程序,它通

12、过调用其他程序来实现将程序源文件编译成目标文件。他程序来实现将程序源文件编译成目标文件。他程序来实现将程序源文件编译成目标文件。他程序来实现将程序源文件编译成目标文件。编译时它首先调用预处理程序编译时它首先调用预处理程序编译时它首先调用预处理程序编译时它首先调用预处理程序(cpp)(cpp)对输入的源对输入的源对输入的源对输入的源程序进行处理程序进行处理程序进行处理程序进行处理; ;然后调用然后调用然后调用然后调用cc1cc1将预处理后的程序编译成汇编代码将预处理后的程序编译成汇编代码将预处理后的程序编译成汇编代码将预处理后的程序编译成汇编代码; ;最后由最后由最后由最后由arm-elf-as

13、arm-elf-as将汇编代码编译成目标代码。将汇编代码编译成目标代码。将汇编代码编译成目标代码。将汇编代码编译成目标代码。arm-elf-gccarm-elf-gcc具有丰富的命令选项,控制编译的具有丰富的命令选项,控制编译的具有丰富的命令选项,控制编译的具有丰富的命令选项,控制编译的各个阶段,满足用户的各种编译需求各个阶段,满足用户的各种编译需求各个阶段,满足用户的各种编译需求各个阶段,满足用户的各种编译需求 1.命令格式命令格式arm-elf-gcc options file在命令在命令arm-elf-gcc后面跟一个或后面跟一个或多个选项,选项间用空格隔开,然后跟多个选项,选项间用空格

14、隔开,然后跟一个或多个目标文件。一个或多个目标文件。例如,将例如,将 编译成目标文件编译成目标文件 并且生并且生成调试信息:成调试信息:arm-elf-gcc g c o 2.命令选项列表命令选项列表输出控制选项:输出控制选项:-c 将输入的源文件编译成目标文件将输入的源文件编译成目标文件-S 将将C/C+文件生成汇编文件文件生成汇编文件-o file 将输出内容存于文件将输出内容存于文件file-pipe 在编译的不同阶段之间采用管道通讯方式在编译的不同阶段之间采用管道通讯方式在编译的不同阶段之间采用管道通讯方式在编译的不同阶段之间采用管道通讯方式-v 打印出编译过程中执行的命令打印出编译过

15、程中执行的命令-x language 说明文件的输入类型为说明文件的输入类型为languageC语言选项:语言选项:-ansi 支持所有支持所有ANSI C程序程序警告选项:警告选项:-w 关闭所有警告关闭所有警告-Wall 打开所有警告打开所有警告-Wimplicit 如果有隐含申明,显示警告信息如果有隐含申明,显示警告信息-Wno-implicit 不显示对隐含申明的警告不显示对隐含申明的警告调试选项:调试选项:-g 在文件中产生调试信息在文件中产生调试信息(调试信息的文调试信息的文件格式有件格式有stabs、COFF、XCOFF、DWARF)优化选项:优化选项:-O0 不优化不优化-O1

16、 一级优化一级优化-O2 二级优化二级优化-O3 三级优化三级优化预处理选项:预处理选项:-E 运行运行C的预处理器的预处理器-C 在运用在运用-E进行预处理时不去掉注释进行预处理时不去掉注释-D macro 定义宏定义宏macro为为1-D macro=defn 定义宏定义宏macro为为defn汇编选项:汇编选项:-Wa,option 将选项将选项option传递传递 给汇编器给汇编器搜索路径选项:搜索路径选项:-I dir 设置搜索路径为设置搜索路径为dir-I- 指定只对指定只对 #include file,有有效的头文件搜索目录效的头文件搜索目录 3. 源文件类型的识别源文件类型的识

17、别arm-elf-gccarm-elf-gcc能够自动根据文件名后缀识别文件类型能够自动根据文件名后缀识别文件类型能够自动根据文件名后缀识别文件类型能够自动根据文件名后缀识别文件类型. .文件名后缀文件名后缀文件名后缀文件名后缀和和和和文件类型文件类型文件类型文件类型的对应关系如下:的对应关系如下:的对应关系如下:的对应关系如下:*.c *.c CC源文件源文件源文件源文件*.i *.i 经过预处理后的经过预处理后的经过预处理后的经过预处理后的C C源文件源文件源文件源文件*.h *.h CC头文件头文件头文件头文件*.ii *.ii 经过预处理后的经过预处理后的经过预处理后的经过预处理后的C

18、+C+源文件源文件源文件源文件*.cc *.cc C+C+源文件源文件源文件源文件*.cxx *.cxx C+C+源文件源文件源文件源文件*.cpp *.cpp C+C+源文件源文件源文件源文件*.C*.CC+C+源文件源文件源文件源文件*.s *.s 不需要预处理的汇编文件不需要预处理的汇编文件不需要预处理的汇编文件不需要预处理的汇编文件*.S*.S需要预处理的汇编文件需要预处理的汇编文件需要预处理的汇编文件需要预处理的汇编文件此外,用户可通过此外,用户可通过此外,用户可通过此外,用户可通过-x language-x language说明文件的输入类说明文件的输入类说明文件的输入类说明文件的

19、输入类型,此时可以不用以上的后缀规则。型,此时可以不用以上的后缀规则。型,此时可以不用以上的后缀规则。型,此时可以不用以上的后缀规则。-x language-x language其中的其中的其中的其中的languagelanguage可为:可为:可为:可为:c c CC源文件源文件源文件源文件c+ c+ C+C+源文件源文件源文件源文件c-header c-header CC头文件头文件头文件头文件cpp-output cpp-output 经过预处理后的经过预处理后的经过预处理后的经过预处理后的C C源文件源文件源文件源文件c+-cpp-output c+-cpp-output 经过预处理后

20、的经过预处理后的经过预处理后的经过预处理后的C+C+源文件源文件源文件源文件assembler assembler 不需要预处理的汇编文件不需要预处理的汇编文件不需要预处理的汇编文件不需要预处理的汇编文件assembler-with-cpp assembler-with-cpp 需要预处理的汇编文件需要预处理的汇编文件需要预处理的汇编文件需要预处理的汇编文件例如,编译一个不需要预处理的例如,编译一个不需要预处理的例如,编译一个不需要预处理的例如,编译一个不需要预处理的C C程序:程序:程序:程序:arm-elf-gcc c g xarm-elf-gcc c g xcpp-output cpp-

21、output -x none-x none如果如果如果如果-x-x后面未跟任何参数,则按照文件的后缀名做后面未跟任何参数,则按照文件的后缀名做后面未跟任何参数,则按照文件的后缀名做后面未跟任何参数,则按照文件的后缀名做相应处理。相应处理。相应处理。相应处理。 3.2.2 命令使用命令使用1.输出文件名的指定输出文件名的指定-o file将输出内容存于文件将输出内容存于文件file,仅适用于,仅适用于只有一个输出文件时。只有一个输出文件时。例如,将编译成汇编程序并存放于文例如,将编译成汇编程序并存放于文件:件:arm-elf-gcc S o 2.目标文件的生成目标文件的生成 -c将输入的源文件编

22、译成目标文件。将输入的源文件编译成目标文件。例如,将编译成:例如,将编译成:arm-elf-gcc c o 3将将C/C+文件生成汇编文件文件生成汇编文件 -S将将C/C+文件生成汇编文件。文件生成汇编文件。例如例如:将编译生成汇编文件将编译生成汇编文件:arm-elf-gcc S o 4.预处理文件的生成预处理文件的生成-E只对源文件进行预处理并且缺省输出到标准只对源文件进行预处理并且缺省输出到标准输出。输出。例如,对进行预处理并将结果输出到屏幕:例如,对进行预处理并将结果输出到屏幕:arm-elf-gcc E 例如,对进行预处理并将结果输出到文件:例如,对进行预处理并将结果输出到文件:ar

23、m-elf-gcc E o 5设置头文件搜索路径设置头文件搜索路径头文件的头文件的引用引用有两种形式:有两种形式:一种是一种是# include“filename”,一种是一种是# include 。 前一种形式的前一种形式的路径搜索顺序路径搜索顺序是:当前目录、是:当前目录、指定的搜索路径;指定的搜索路径;后一种形式只搜索指定路径。后一种形式只搜索指定路径。 -I dir将目录将目录dir添加到头文件搜索目录列表的第添加到头文件搜索目录列表的第一项。一项。通过此选项可以使用户头文件先于系统头文通过此选项可以使用户头文件先于系统头文件被搜索到。件被搜索到。如果同时用如果同时用-I选项添加几个目

24、录,目录被搜选项添加几个目录,目录被搜索时的优先级顺序为从左到右。索时的优先级顺序为从左到右。例如,编译,在当前目录和例如,编译,在当前目录和/include中搜索中搜索所包含的头文件:所包含的头文件:arm-elf-gcc I ./ I/include c -I-I-以前以前用用-I指定的头文件搜索目录只指定的头文件搜索目录只对对 # include“file” 有效,对有效,对 # include 无效;无效;-I-以后以后指定的头文件搜索目录对以上指定的头文件搜索目录对以上两种形式的头文件都有效。两种形式的头文件都有效。此外,此外,-I-会禁止对当前目录的隐含搜会禁止对当前目录的隐含搜索

25、,不过用户可以通过使用索,不过用户可以通过使用“-I.”使能对当使能对当前目录的搜索。前目录的搜索。 例如例如:在需要编译的文件对头文件的引用有:在需要编译的文件对头文件的引用有:# include# include“file2.h”# include“file3.h”其中,在目录其中,在目录 /include/test下,在下,在/include下,在当前目录下。下,在当前目录下。在以下命令行中,只能搜索到,而不在以下命令行中,只能搜索到,而不 能搜索到:能搜索到:arm-elf-gcc I./include/test I I./include c arm-elf-gcc I./includ

26、e/test I I./include c 而在以下命令行中而在以下命令行中,可以搜索到需要的两个头可以搜索到需要的两个头文件和文件和file2.h: arm-elf-gcc I I./include I./include/test c arm-elf-gcc I I./include I./include/test c 如果要搜索到,必须要添加对当前目录的搜索如果要搜索到,必须要添加对当前目录的搜索如果要搜索到,必须要添加对当前目录的搜索如果要搜索到,必须要添加对当前目录的搜索: : arm-elf-gcc I I. I./include I./include/test c arm-elf-

27、gcc I I. I./include I./include/test c 实质上,上述编译命令等价于实质上,上述编译命令等价于实质上,上述编译命令等价于实质上,上述编译命令等价于: : arm-elf-gcc I. I./include I./include/test c arm-elf-gcc I. I./include I./include/test c 与与与与 arm-elf-gcc I./include I./include/test c arm-elf-gcc I./include I./include/test c 6控制警告产生控制警告产生用户可以使用以用户可以使用以用户可以

28、使用以用户可以使用以-W-W开头的不同选项对特定警告进行开头的不同选项对特定警告进行开头的不同选项对特定警告进行开头的不同选项对特定警告进行设定。设定。设定。设定。对于每种警告类型都有相应以对于每种警告类型都有相应以对于每种警告类型都有相应以对于每种警告类型都有相应以-Wno-Wno-开始的选项关开始的选项关开始的选项关开始的选项关闭警告。闭警告。闭警告。闭警告。例如例如例如例如: :如果有隐含申明,显示警告信息:如果有隐含申明,显示警告信息:如果有隐含申明,显示警告信息:如果有隐含申明,显示警告信息:arm-elf-gcc c Wimplicit arm-elf-gcc c Wimplici

29、t 不显示对隐含申明的警告:不显示对隐含申明的警告:不显示对隐含申明的警告:不显示对隐含申明的警告:arm-elf-gcc c Wnoimplicit arm-elf-gcc c Wnoimplicit 常用的警告选项有:常用的警告选项有:常用的警告选项有:常用的警告选项有:-w-w 关闭所有警告信息。关闭所有警告信息。关闭所有警告信息。关闭所有警告信息。-Wall-Wall 打开所有警告信息。打开所有警告信息。打开所有警告信息。打开所有警告信息。7实现优化实现优化优化的优化的主要目的主要目的是使编译生成的代码的是使编译生成的代码的尺寸更小、运行速度更快。尺寸更小、运行速度更快。但是在编译过程

30、中随着优化级别的升高,但是在编译过程中随着优化级别的升高,编译器会相应消耗更多时间和内存,而且优编译器会相应消耗更多时间和内存,而且优化生成代码的执行顺序和源代码有一定出入,化生成代码的执行顺序和源代码有一定出入,因此优化选项更多地用于生成固化代码,而因此优化选项更多地用于生成固化代码,而不用于生成调试代码。不用于生成调试代码。arm-elf-gcc支持多种优化选项,总体上划支持多种优化选项,总体上划分为分为三级优化三级优化:1.1.-O1 可以部分减小代码尺寸,对运行速可以部分减小代码尺寸,对运行速度有一定的提高。较多地使用了寄存器变量,提度有一定的提高。较多地使用了寄存器变量,提高指令的并

31、行度。高指令的并行度。2.2.-O2 除了解循环、函数插装和静态变量优除了解循环、函数插装和静态变量优化,几乎包含化,几乎包含arm-elf-gcc所有优化选项。一般所有优化选项。一般在生成固化代码时使用该选项较为适宜。在生成固化代码时使用该选项较为适宜。3.3.-O3 包含包含-O2的所有优化,并且还包含了的所有优化,并且还包含了解循环、函数插装和静态变量优化。通常情况下解循环、函数插装和静态变量优化。通常情况下,该级优化生成的代码执行速度最快,但是代码,该级优化生成的代码执行速度最快,但是代码尺寸比尺寸比-O2大一些。大一些。8在命令行定义宏在命令行定义宏-D macro定义宏定义宏mac

32、ro为为1。-D macro=defn 定义宏定义宏macro为为defn。例如例如:编译并且预定义宏编译并且预定义宏 RUN_CACHE 值为值为1: arm-elf-gcc c D RUN_CACHE 编译并且预定义宏编译并且预定义宏 RUN_CACHE 值为值为0: arm-elf-gcc c D RUN_CACHE=0 3.3 交叉连接器交叉连接器arm-elf-ld3.3.1 概述概述arm-elf-ld根据链接定位文件根据链接定位文件Linkcmds中代码段、数据段、中代码段、数据段、BSS段和堆栈段等定位段和堆栈段等定位信息,将可重定位的目标模块链接成一个单信息,将可重定位的目标

33、模块链接成一个单一的、绝对定位的目标程序,该目标程序是一的、绝对定位的目标程序,该目标程序是ELF格式,并且可以包含调试信息。格式,并且可以包含调试信息。arm-elf-ld可以输出一个内存映象文件,可以输出一个内存映象文件,该文件显示所有目标模块、段和符号的绝对该文件显示所有目标模块、段和符号的绝对定位地址,它也产生目标模块对全局符号引定位地址,它也产生目标模块对全局符号引用的交叉参考列表。用的交叉参考列表。arm-elf-ld支持将多个目标模块支持将多个目标模块链接成一个单一的、绝对定位的目链接成一个单一的、绝对定位的目标程序,也能够依次对目标模块进标程序,也能够依次对目标模块进行链接,这

34、个特性称为行链接,这个特性称为增量链接增量链接(Incremental Linking)。)。 arm-elf-ld会自动从库中装载被会自动从库中装载被调用函数所在的模块。调用函数所在的模块。1命令格式命令格式arm-elf-ld option file命令行后跟选项和可重定位的目标文件名。命令行后跟选项和可重定位的目标文件名。例如例如:链接的输入文件为,输出文件为,链接的库链接的输入文件为,输出文件为,链接的库为,生成内存映象文件,链接定位文件为为,生成内存映象文件,链接定位文件为linkcmds,则命令如下:,则命令如下:arm-elf-ld -Map -T linkcmds -L./li

35、b o lxxx2命令选项列表命令选项列表-e entry指定程序入口指定程序入口-M输出链接信息输出链接信息-lar指定链接库指定链接库-L dir添加搜索路径添加搜索路径-o设置输出文件名设置输出文件名-Tcommandfile指定链接命令文件指定链接命令文件-v显示版本信息显示版本信息-Map制定输出映像文件制定输出映像文件3.3.2 命令使用命令使用1 1程序入口地址程序入口地址程序入口地址程序入口地址-e entry-e entry以符号以符号以符号以符号entryentry作为程序执行的入口地址,而不从默认作为程序执行的入口地址,而不从默认作为程序执行的入口地址,而不从默认作为程序

36、执行的入口地址,而不从默认的入口地址开始。默认入口地址的指定方式和其他指定的入口地址开始。默认入口地址的指定方式和其他指定的入口地址开始。默认入口地址的指定方式和其他指定的入口地址开始。默认入口地址的指定方式和其他指定方式的描述,参见节。方式的描述,参见节。方式的描述,参见节。方式的描述,参见节。例如,链接的输入文件为,输出文件为,链接定位例如,链接的输入文件为,输出文件为,链接定位例如,链接的输入文件为,输出文件为,链接定位例如,链接的输入文件为,输出文件为,链接定位文件为文件为文件为文件为linkcmdslinkcmds,将入口地址设为,将入口地址设为,将入口地址设为,将入口地址设为_st

37、art_start,命令如下:,命令如下:,命令如下:,命令如下:arm-elf-ld T linkcmds e _start o arm-elf-ld T linkcmds e _start o 2输出链接信息输出链接信息-M在标准端口打印出符号映象表和内存分布信在标准端口打印出符号映象表和内存分布信息。息。例如:例如:链接的输入文件为链接的输入文件为,输出文件为输出文件为,在标准端口在标准端口打印出符号映象表和内存分布信息,命令如下:打印出符号映象表和内存分布信息,命令如下:arm-elf-ld M o 如果标准输出设置为显示器,运行命令后将如果标准输出设置为显示器,运行命令后将在显示器上

38、显示内存映象信息和符号映象表。在显示器上显示内存映象信息和符号映象表。 -Map mapfile将链接的符号映象表和内存分布信将链接的符号映象表和内存分布信息输出到文件息输出到文件mapfile里。里。例如例如:链接的输入文件为,输出文件为,链接的输入文件为,输出文件为,将链接的符号映象表和内存分布信息输将链接的符号映象表和内存分布信息输出到文件里,命令如下:出到文件里,命令如下:arm-elf-ld Map o 3指定链接的库指定链接的库-lar指定库文件为链接的库。指定库文件为链接的库。可以重复使用可以重复使用-l来指定多个链接的库。来指定多个链接的库。例如例如:链接的输入文件为,指定为链

39、接的库,输出链接的输入文件为,指定为链接的库,输出文件为,命令如下:文件为,命令如下:arm-elf-ld o lxxx注意:库的命名规则为,在注意:库的命名规则为,在-l指定库名时使指定库名时使用的格式为用的格式为-lxxx。4添加库和脚本文件的搜索路径添加库和脚本文件的搜索路径-Ldir将将dir添加到搜索路径。添加到搜索路径。搜索顺序按照命令行中输入的顺序,并且搜索顺序按照命令行中输入的顺序,并且优先于默认的搜索路径。优先于默认的搜索路径。所有在所有在-L添加的目录中找到的添加的目录中找到的-l指定的库都指定的库都有效。有效。例如例如:链接的输入文件为,输出文件为,将链接的输入文件为,输

40、出文件为,将/lib添加到库的搜索路径,命令如下:添加到库的搜索路径,命令如下:arm-elf-ld -L./lib o 5设置输出文件的名字设置输出文件的名字-o output将输出文件名字设定为将输出文件名字设定为output。如果。如果不指定输出文件名,不指定输出文件名,arm-elf-ld生成文件生成文件名默认为。名默认为。例如例如:链接的输入文件为,输出文件为,命链接的输入文件为,输出文件为,命令如下:令如下:arm-elf-ld o 3.3.3 linkcmds连接命令文件连接命令文件 arm-elf-ld的命令语言是一种描述性的脚本的命令语言是一种描述性的脚本语言,它主要应用于控

41、制语言,它主要应用于控制:有哪些输入文件、文有哪些输入文件、文件的格式怎样、输出文件中的模块怎样布局、件的格式怎样、输出文件中的模块怎样布局、分段的地址空间怎样分布、以及未初始化的数分段的地址空间怎样分布、以及未初始化的数据段怎样处理等。据段怎样处理等。用命令语言写成的文件用命令语言写成的文件(通常称为通常称为linkcmds)具有可重用性具有可重用性,不必每次在命令行输不必每次在命令行输入一大堆命令选项入一大堆命令选项.并且对于不同的应用并且对于不同的应用,只需只需对对linkcmds进行简单的修改就可以使用。进行简单的修改就可以使用。1调用调用linkcmds首先写一个链接命令文件首先写一

42、个链接命令文件linkcmds,然后,然后在在arm-elf-ld的命令中使用的命令中使用-T linkcmds参参数数,就能在链接时自动调用就能在链接时自动调用linkcmds文件文件例如例如: 链接的输入文件为,输出文件为,链链接的输入文件为,输出文件为,链接定位文件为接定位文件为linkcmds,则命令如下,则命令如下:arm-elf-ld T linkcmds o 2编写编写linkcmds(1)arm-elf-ld命令语言命令语言arm-elf-ld命令语言是一系列语句的集命令语言是一系列语句的集合,包括用简单的关键字设定选项、描述合,包括用简单的关键字设定选项、描述输入文件及其格式

43、、命名输出文件。输入文件及其格式、命名输出文件。其中有两种语句对于链接过程起重要其中有两种语句对于链接过程起重要作用:作用:SECTIONS语句和语句和MEMORY语句。语句。SECTIONS语句用于描述输出文件中的模语句用于描述输出文件中的模块怎样布局,块怎样布局,MEMORY语句描述目标机中语句描述目标机中可以用的存储单元。可以用的存储单元。(2)表达式)表达式在在linkcmds中的表达式与中的表达式与C语言中的表达语言中的表达式类似,它们具有如下的特征:式类似,它们具有如下的特征:表达式的值都是表达式的值都是“unsigned long”或者或者“long”类型类型常数都是整数常数都是

44、整数支持支持C语言中的操作符语言中的操作符可以引用或者定义全局变量可以引用或者定义全局变量可以使用内建的函数可以使用内建的函数 整数整数八进制数以八进制数以0开头,例如:开头,例如:0234;十进制数以非十进制数以非0的数字开头,例如:的数字开头,例如:567;十六进制数以十六进制数以0x或或0X开头,例如:开头,例如:0x16;负数以运算符负数以运算符-开头,例如:开头,例如:-102;以以K,M为后缀分别表示以为后缀分别表示以1024,1024*1024为单位,例如:为单位,例如:var1 = 1K和和var1 = 1024相等,相等,var2 = 1M和和var2 = 1024*1024

45、相等。相等。 变量名变量名以字母、下划线和点开头,可以包含任何字以字母、下划线和点开头,可以包含任何字母、下划线、数字、点和连接符。母、下划线、数字、点和连接符。变量名不能和关键字一样,如果变量名和关变量名不能和关键字一样,如果变量名和关键字一样,或者变量名中包含空格时,必须将变键字一样,或者变量名中包含空格时,必须将变量名包含在量名包含在“”中中.例如:例如:“SECTION”9;“with a space”“also with a space” + 10;在在arm-elf-ld命令语言中,空格用于界定相邻命令语言中,空格用于界定相邻符号,例如:符号,例如:A-B表示一个变量名,而表示一个

46、变量名,而A - B表示表示一个减法的表达式。一个减法的表达式。 地址记数器点号地址记数器点号“. ”“.”是一个包含当前输出地址的计数器。是一个包含当前输出地址的计数器。因为因为“.”总是表示某个输出段的地址,总是表示某个输出段的地址,所以总是和表达式一起在所以总是和表达式一起在SECTIONS命令中命令中出现。出现。“.”可以在任何一般符号出现的地方出可以在任何一般符号出现的地方出现,对现,对“.”的赋值将引起计数器所指位置的的赋值将引起计数器所指位置的移动,而计数器位置不能反向移动。移动,而计数器位置不能反向移动。例如:例如:SECTIONSoutput:file1(.text). =

47、. + 1000;file2(.text). += 1000;file3(.text) = 0x1234;在左面的例子中,在左面的例子中,在在file1(.text)与与file2(.text)之间被空出了之间被空出了1000个字节的空间,个字节的空间,file2(.text)与)与file3(.text)之)之间也被空出了间也被空出了1000个字节的空间,而个字节的空间,而0x1234为该分段的为该分段的空间空隙的填充值。空间空隙的填充值。 可以将可以将“.”赋给变量赋给变量;也可以对也可以对“.”赋值。赋值。例如:例如:data_start = . ;.= . + 2000;(3)link

48、cmds的结构的结构linkcmds文件主要由四个部分文件主要由四个部分组成组成:1.1.程序入口说明:用于指定程序运行时所需程序入口说明:用于指定程序运行时所需要执行的第一条指令的地址。要执行的第一条指令的地址。2.2.程序头说明:生成目标文件类型为程序头说明:生成目标文件类型为ELF,可,可以指定详细的程序头信息。以指定详细的程序头信息。3.3.内存布局的说明:用于规划内存的布局,内存布局的说明:用于规划内存的布局,将内存空间划分为不同的部分。将内存空间划分为不同的部分。4.4.分段的分步说明:详细指明各个分段的构分段的分步说明:详细指明各个分段的构成以及分段的定位地址和装载地址。成以及分

49、段的定位地址和装载地址。 其中其中和和的部分不能被省略。的部分不能被省略。 (4)对程序入口的说明)对程序入口的说明arm-elf-ld命令语言有一条特定命令用于指定命令语言有一条特定命令用于指定输出文件中第一条可执行指令输出文件中第一条可执行指令,即程序的入口点。即程序的入口点。该命令格式如下:该命令格式如下:ENTRY(symbol)其中其中ENTRY是保留字,是保留字,symbol表示程序的表示程序的入口地址,通常是用一个全局地址标号(入口地址,通常是用一个全局地址标号(label)来表示入口地址。来表示入口地址。例如,在程序中的开始地方有一标号:例如,在程序中的开始地方有一标号:.gl

50、obal demo_startdemo_start:movl $_stack_top,%esp那么在那么在linkcmds中可以用下面的方式说明中可以用下面的方式说明程序的入口:程序的入口:ENTRY(demo_start);该命令可以作为单独的一条命令放在该命令可以作为单独的一条命令放在linkcmds的任何位置,也可以放在的任何位置,也可以放在SECTIONS内关内关于段的定义部分,都对布局起作用。于段的定义部分,都对布局起作用。指定入口地址的方式还有很多,现在按优先指定入口地址的方式还有很多,现在按优先级递减的顺序描述如下:级递减的顺序描述如下:用用-e选项指定入口地址选项指定入口地址在

51、在linkcmds里用里用ENTRY(symbol)指令)指令变量变量start的值,如果有变量的值,如果有变量start.text段第一字节的地址,如果存在段第一字节的地址,如果存在0地址地址(5)对程序头的说明)对程序头的说明ELF格式文件需要使用程序头,它用格式文件需要使用程序头,它用于描述程序应该怎么被装入内存。于描述程序应该怎么被装入内存。在默认情况下,在默认情况下,arm-elf-ld可以自己可以自己生成一个程序头,用户也可以用生成一个程序头,用户也可以用PHDRS自己编写程序头,当运用该命令时,自己编写程序头,当运用该命令时,arm-elf-ld不会生成默认的程序头。不会生成默认

52、的程序头。注意:如果没有特殊要求,建议用户注意:如果没有特殊要求,建议用户不要不要自己写程序头。自己写程序头。PHDRSPHDRS name type FILEHDR PHDRS AT ( address ) name type FILEHDR PHDRS AT ( address ) FLAGS FLAGS(flagsflags) ; 其中其中其中其中PHDRSPHDRS、FILEHDRFILEHDR、ATAT、FLAGSFLAGS都是关键字。都是关键字。都是关键字。都是关键字。namename表示段(表示段(表示段(表示段(SegmentSegment)的名字,而该段)的名字,而该段)的名

53、字,而该段)的名字,而该段(SegmentSegment)装入的内容由)装入的内容由)装入的内容由)装入的内容由SECTIONSSECTIONS中对分段中对分段中对分段中对分段(SectionSection)的描述决定,例如:)的描述决定,例如:)的描述决定,例如:)的描述决定,例如:SECTIONSSECTIONSsecname start BLOCK(align) (NOLOAD) : AT ( secname start BLOCK(align) (NOLOAD) : AT ( ldadr )ldadr ) contents region :phdr =fill contents reg

54、ion :phdr =fill 因此因此因此因此PHDRSPHDRS中的中的中的中的namename应该和应该和应该和应该和SECTIONSSECTIONS中的中的中的中的phdrphdr保持一致。保持一致。保持一致。保持一致。typetype表示段的类型,可以为下面描述的任意表示段的类型,可以为下面描述的任意表示段的类型,可以为下面描述的任意表示段的类型,可以为下面描述的任意一种类型(括号内表示关键字的值):一种类型(括号内表示关键字的值):一种类型(括号内表示关键字的值):一种类型(括号内表示关键字的值):PT_NULL (0) PT_NULL (0) 空程序头空程序头空程序头空程序头PT

55、_LOAD (1) PT_LOAD (1) 描述一个可装入的段描述一个可装入的段描述一个可装入的段描述一个可装入的段PT_DYNAMIC (2)PT_DYNAMIC (2)表示包含动态链接信息的段表示包含动态链接信息的段表示包含动态链接信息的段表示包含动态链接信息的段PT_INTERP (3) PT_INTERP (3) 表示该段包含程序解释器的表示该段包含程序解释器的表示该段包含程序解释器的表示该段包含程序解释器的名字名字名字名字PT_NOTE (4) PT_NOTE (4) 表示包含注释信息的段表示包含注释信息的段表示包含注释信息的段表示包含注释信息的段PT_SHLIB (5) PT_SH

56、LIB (5) 一个保留的程序头一个保留的程序头一个保留的程序头一个保留的程序头PT_PHDR (6) PT_PHDR (6) 表示该段可能包含程序头的描述表示该段可能包含程序头的描述表示该段可能包含程序头的描述表示该段可能包含程序头的描述信息信息信息信息expression expression 用数值表示一个程序头的类型,用数值表示一个程序头的类型,用数值表示一个程序头的类型,用数值表示一个程序头的类型,该类型没有对应的关键字该类型没有对应的关键字该类型没有对应的关键字该类型没有对应的关键字FILEHDRFILEHDR表示在段中包含表示在段中包含表示在段中包含表示在段中包含ELFELF文件

57、头的信息。文件头的信息。文件头的信息。文件头的信息。PHDRSPHDRS表示在段中还要包含程序头本身的信息。表示在段中还要包含程序头本身的信息。表示在段中还要包含程序头本身的信息。表示在段中还要包含程序头本身的信息。AT (address)AT (address)表示该段的起始地址,若在表示该段的起始地址,若在表示该段的起始地址,若在表示该段的起始地址,若在SECTIONSSECTIONS中也有中也有中也有中也有ATAT时,程序头中定义的时,程序头中定义的时,程序头中定义的时,程序头中定义的ATAT优先。优先。优先。优先。例如:例如:例如:例如:PHDRSPHDRS headers PT_PH

58、DR PHDRS ;headers PT_PHDR PHDRS ;interp PT_INTERP ;interp PT_INTERP ;text PT_LOAD FILEHDR PHDRS ;text PT_LOAD FILEHDR PHDRS ;data PT_LOAD ;data PT_LOAD ;dynamic PT_DYNAMIC ;dynamic PT_DYNAMIC ; SECTIONSSECTIONS . = SIZEOF_HEADERS;. = SIZEOF_HEADERS;.interp : *(.interp) :text :interp.interp : *(.inte

59、rp) :text :interp.text : *(.text) :text.text : *(.text) :text.rodata : *(.rodata) /* defaults to :text */.rodata : *(.rodata) /* defaults to :text */. = . + 0x1000; /* move to a new page in memory */. = . + 0x1000; /* move to a new page in memory */.data : *(.data) :data.data : *(.data) :data.dynami

60、c : *(.dynamic) :data :dynamic.dynamic : *(.dynamic) :data :dynamic. 在上面的例子中,定义了在上面的例子中,定义了5个段:个段:headers申明一个程序头段;申明一个程序头段;interp申明一个段,段中包含了程申明一个段,段中包含了程序解释器的名字;序解释器的名字;text申明一个可被下载的段,并且申明一个可被下载的段,并且包含了文件的头信息和各段的信息;包含了文件的头信息和各段的信息;data申明一个可被下载的段;申明一个可被下载的段;dynamic申明一个包含动态链接信申明一个包含动态链接信息的段;息的段; 在在SEC

61、TIONS中可以看到,有的分中可以看到,有的分段同时属于两个段,实质上是这两段同时属于两个段,实质上是这两个段占用同一空间。个段占用同一空间。.rodata也属于也属于.text段是由于它的段是由于它的上一个分段属于上一个分段属于.text段,而段,而.rodata又没有指明归属段。又没有指明归属段。注意:如果没有特殊要求,建议用注意:如果没有特殊要求,建议用户不要自己写程序头。户不要自己写程序头。 (6)对内存布局的说明)对内存布局的说明arm-elf-ld的默认配置允许将输出程的默认配置允许将输出程序定位到任何可用内存。序定位到任何可用内存。用户也可以用用户也可以用MEMORY命令对内存命

62、令对内存进行配置。进行配置。MEMORY命令可以定义目标机内存命令可以定义目标机内存段的位置和大小,当装载的程序块大小段的位置和大小,当装载的程序块大小超出指定的内存段大小时,超出指定的内存段大小时,arm-elf-ld会会提示出错,而不会自动寻找可用的内存提示出错,而不会自动寻找可用的内存段,这样可以避免内存分配错误。段,这样可以避免内存分配错误。 定义内存段的方式:定义内存段的方式:MEMORYname (attr) :ORIGIN = origin, LENGTH = lenname表示内存段的名字,可以使用任何变量表示内存段的名字,可以使用任何变量名,但是不能和已有变量名、文件名和分段

63、名名,但是不能和已有变量名、文件名和分段名(section name)冲突。)冲突。attr没有实际的用途,可省略。没有实际的用途,可省略。origin(可简写为:(可简写为:org或者或者o)表示内存段的)表示内存段的起始位置。起始位置。Length(可简写为可简写为:len或者或者l)表示内存段的长表示内存段的长度度. MEMORYrom : ORIGIN = 0x3f80000, LENGTH = 512Kram : org = 0, l = 1M 表示定义了两个内存段:表示定义了两个内存段:rom内存段,起始地址为内存段,起始地址为0x3f80000,长,长度为度为512K;ram内存

64、段,起始地址为内存段,起始地址为0,长度为,长度为1M。(7)对分段的说明)对分段的说明SECTIONS命令控制如何正确地命令控制如何正确地将输将输入分段定位到输出分段入分段定位到输出分段,包括在输出文件中包括在输出文件中的顺序的顺序,和输入分段在输出分段中的定位。和输入分段在输出分段中的定位。如果不用如果不用SECTIONS命令,命令,arm-elf-ld将对每个输入分段生成相同名字的输出分将对每个输入分段生成相同名字的输出分段,分段的顺序由输入文件中遇到的分段段,分段的顺序由输入文件中遇到的分段的先后顺序决定。的先后顺序决定。 SECTIONS命令的格式:命令的格式:SECTIONS.se

65、cname start BLOCK(align) (NOLOAD) : AT ( ldadr )secname start BLOCK(align) (NOLOAD) : AT ( ldadr ) contents region :phdr =fill.其中其中secname和和contents都是必须有的。都是必须有的。secname表示输出分段的名字,受输出格式的限表示输出分段的名字,受输出格式的限制,一些输出格式对段名有限制,制,一些输出格式对段名有限制,例如只允许例如只允许.text,.data或或.bss的分段名存在的分段名存在.另外另外arm-elf-ld不输出空的分段。不输出空的

66、分段。 startstart,BLOCKBLOCK(alignalign), ,(NOLOADNOLOAD),AT,AT(ldadrldadr), , regionregion,:phdr:phdr,=fill=fill都是可选项:都是可选项:Start 表示分段的起始地址表示分段的起始地址表示分段的起始地址表示分段的起始地址, ,该地址被称为重定位该地址被称为重定位该地址被称为重定位该地址被称为重定位地址地址地址地址; ;BLOCK(align)表示分段以表示分段以align对齐;对齐;(NOLOAD)表示该段不能被装载;表示该段不能被装载;AT (ldadr) 表示该分段装入的起始地址为表

67、示该分段装入的起始地址为表示该分段装入的起始地址为表示该分段装入的起始地址为ldadrldadr,当没有该参数时分段的装入地址和重定位地址相同;,当没有该参数时分段的装入地址和重定位地址相同;,当没有该参数时分段的装入地址和重定位地址相同;,当没有该参数时分段的装入地址和重定位地址相同;region 表示该分段地址空间在表示该分段地址空间在region所定义的范围内所定义的范围内,region就是在就是在MEMORY命命令中定义的内存段名字;令中定义的内存段名字;:phdr 表示该分段的装载地址空间在表示该分段的装载地址空间在phdr定义的范围内,定义的范围内,phdr就是在程序头中就是在程序

68、头中定义的段名字;定义的段名字;=fill 表示在该分段的空间空隙的填充表示在该分段的空间空隙的填充值。值。 contents表示具体的分段内容,主要表示具体的分段内容,主要描述该输出分段中包含有输入文件中的哪描述该输出分段中包含有输入文件中的哪些分段。些分段。常见的分段名如下:常见的分段名如下:.text 表示代码段表示代码段.data 初始化了的数据段初始化了的数据段.bss 未初始化的数据段未初始化的数据段.rodata 不可写的数据段不可写的数据段COMMON 未初始化的数据段未初始化的数据段用户在汇编语言程序中可以自定义分用户在汇编语言程序中可以自定义分段名,如:段名,如:mycod

69、e、mydata之类。之类。 在在C语言文件编译成目标文件后语言文件编译成目标文件后,通常通常包含有包含有.text、.data、COMMON、.rodata段。段。其中其中.rodata表示不可写的数据段,通表示不可写的数据段,通常包含在常包含在C语言程序中定义的一些常量,语言程序中定义的一些常量,如如const char version_string =“Lambda x86/fpm”之类。之类。 contents可用格式:filenamefilename(section)filename(section,section,)filename(section section )和针对所有文件

70、的:和针对所有文件的:*(section)*(section,section,)*(section section )如果在用如果在用“*”指定所有文件时,以指定所有文件时,以前已经使用前已经使用filename指定过一些文件,指定过一些文件,那么那么“*”表示剩下的文件:表示剩下的文件:filename(COMMON)*(COMMON)指定输入文件中名为指定输入文件中名为COMMON的的分段里未初始化的数据在输出分段中的分段里未初始化的数据在输出分段中的位置。位置。下面举例说明下面举例说明contents中的具体内中的具体内容及编写方法。容及编写方法。 下面举例说明下面举例说明下面举例说明下

71、面举例说明contentscontents中的具体内容及编写方法。中的具体内容及编写方法。中的具体内容及编写方法。中的具体内容及编写方法。例如:例如:例如:例如:.text 0 :.text 0 : file1.o file2.o file3.o file1.o file2.o file3.o 表示将、中的所有分段都放在输出文件的表示将、中的所有分段都放在输出文件的表示将、中的所有分段都放在输出文件的表示将、中的所有分段都放在输出文件的.text.text段中。段中。段中。段中。例如:例如:例如:例如:.text 0 :.text 0 : *(.text);*(.text); 表示将所有输入文

72、件中的表示将所有输入文件中的表示将所有输入文件中的表示将所有输入文件中的.text.text分段都放在输分段都放在输分段都放在输分段都放在输出文件的出文件的出文件的出文件的.text.text段中。段中。段中。段中。 例如:例如:.text 0 :file1.o (.text);file2.o (.text);file3.o (.text); 表示将、中的表示将、中的.text段都放在输出文件的段都放在输出文件的.text段中。段中。 例如:例如:text1 :file1.o (.text);text2 :*(.text);表示将中的表示将中的.text段放在输出文件的段放在输出文件的text

73、1段中,而其他输入文件的段中,而其他输入文件的.text段都放段都放在输出文件的在输出文件的text2段。段。 (8)注释)注释arm-elf-ld语言中的注释和语言中的注释和C语语言一样。言一样。 例如:例如:/* comments */3.4 工程管理器工程管理器 MAKE3.4.1 概述概述make是用于自动编译、链接程是用于自动编译、链接程序的实用工具。序的实用工具。使用使用make后就不需要手工编译后就不需要手工编译每个程序文件每个程序文件。要使用要使用make,首先要编写,首先要编写makefile ,makefile描述程序文件之间的依赖关系以及描述程序文件之间的依赖关系以及提供

74、更新文件的命令。提供更新文件的命令。典型地,在一个程序中,可执行文件依典型地,在一个程序中,可执行文件依赖于目标文件,而目标文件依赖于源文件。赖于目标文件,而目标文件依赖于源文件。 如果如果makefile文件存在,每次修改完源文件存在,每次修改完源程序后,用户通常所需要做的事情就是在命程序后,用户通常所需要做的事情就是在命令行敲入令行敲入“make”,然后所有的事情都由,然后所有的事情都由make来完成。来完成。 1命令格式命令格式make -f makefile option target make命令后跟命令后跟-f选项,指定选项,指定makefile的名的名字为字为makefile;o

75、ption表示表示make的一些选项的一些选项;target是是make指定的目标,在将详细说指定的目标,在将详细说明。明。例如例如:makefile的名字是的名字是my_hello_make:make f my_hello_makemake f my_hello_make2命令选项列表命令选项列表-f指定指定makefile-e使环境变量优先于使环境变量优先于makefile的变量的变量-I dir设定搜索目录设定搜索目录-i忽略忽略make过程中所有错误过程中所有错误-n只显示执行过程,而不真正执行只显示执行过程,而不真正执行-r使隐含规则无效使隐含规则无效-w显示工作目录显示工作目录-C

76、 dir读取读取makefile设置的工作目录设置的工作目录-s不显示执行的命令不显示执行的命令 命令使用命令使用 makefile文件用来告诉文件用来告诉make需要做的事情,需要做的事情,通常指怎样编译、怎样链接一个程序。通常指怎样编译、怎样链接一个程序。以以C语言程序为例语言程序为例:在用在用make重新编译的时候,如果一个头文重新编译的时候,如果一个头文件已被修改,则包含这个头文件的所有件已被修改,则包含这个头文件的所有C源代码源代码文件都必须被重新编译。文件都必须被重新编译。而每个目标文件都与而每个目标文件都与C的源代码文件有关,的源代码文件有关,如果有源代码文件被修改过,则所有目标

77、文件都如果有源代码文件被修改过,则所有目标文件都必须被重新链接生成最后的结果。必须被重新链接生成最后的结果。编写一个编写一个makefile将在节详细介绍。将在节详细介绍。1指定指定makefile-f makefile用该选项指定用该选项指定makefile的名字为的名字为makefile。如果如果make中多次使用中多次使用-f指定多个指定多个makefile,则所有,则所有makefile将链接起来作为将链接起来作为最后的最后的makefile。如果不指定如果不指定makefile,make默认的默认的makefile依次为依次为“makefile”、“Makefile”。例如:例如:m

78、ake f my_hello_make2使环境变量优先于使环境变量优先于makefile文件中的变量文件中的变量-e使环境变量优先于使环境变量优先于makefile文件中的变量。文件中的变量。例如:例如: make e3指定包含文件的搜索路径指定包含文件的搜索路径-I dir指定在解析指定在解析makefile文件中的文件中的.include时的搜索路径为时的搜索路径为dir。如果有多个路径,将按输入顺如果有多个路径,将按输入顺序依次查找。序依次查找。例如:例如:make I/include/mk4忽略错误忽略错误-i忽略忽略make执行过程中的所有错误。执行过程中的所有错误。例如:例如:ma

79、ke i 5显示命令的执行过程显示命令的执行过程-n只显示命令的执行过程而不真正执行。只显示命令的执行过程而不真正执行。例如:例如:make n 6使隐含规则无效使隐含规则无效-r使使make的隐含规则无效,清除后缀名规的隐含规则无效,清除后缀名规则中默认的后缀清单。则中默认的后缀清单。例如:例如:make r7显示执行过程中的工作目录显示执行过程中的工作目录-w显示显示make执行过程中的工作目录。执行过程中的工作目录。例如:例如:make w8读取读取makefile文件前设置工作目录文件前设置工作目录-C dir在读取在读取makefile文件以前将工作目录改文件以前将工作目录改变为变为

80、dir,完成,完成make后改回原来的目录。后改回原来的目录。如果在一次如果在一次make中使用多个中使用多个-C选项,选项,每个选项都和前面一个有关系。每个选项都和前面一个有关系。“-C dir0 / -C dir1 ”与与“-C dir0 / dir1”等价等价.例如:例如:make C bsp9不显示所执行的命令不显示所执行的命令-s运行运行make时用选项时用选项-s可以不显示执可以不显示执行的命令,只显示生成的结果文件。行的命令,只显示生成的结果文件。例如:例如:make s3.4.3 编写一个编写一个makefile1. makefile的结构的结构makefile文件包含:文件包

81、含:显式规则显式规则隐含规则隐含规则变量定义变量定义指令指令注释注释2. 编写编写makefile中的规则中的规则makefilemakefile中规则的格式如下:中规则的格式如下:中规则的格式如下:中规则的格式如下:targets targets :dependenciesdependenciescommandcommand或者或者或者或者targets targets :dependencies dependencies ;commandcommandcommandcommandtargets targets 指定目标名,通常是一个程序产生的目标文件名,指定目标名,通常是一个程序产生的目标

82、文件名,指定目标名,通常是一个程序产生的目标文件名,指定目标名,通常是一个程序产生的目标文件名,也可能是执行一个动作的名字,名字之间用空格隔开。也可能是执行一个动作的名字,名字之间用空格隔开。也可能是执行一个动作的名字,名字之间用空格隔开。也可能是执行一个动作的名字,名字之间用空格隔开。dependencydependency 描述产生描述产生描述产生描述产生targettarget所需的文件,一个所需的文件,一个所需的文件,一个所需的文件,一个targettarget通常通常通常通常依赖于多个依赖于多个依赖于多个依赖于多个dependencydependency。command comman

83、d 用于指定该规则的命令。用于指定该规则的命令。用于指定该规则的命令。用于指定该规则的命令。注意:注意:注意:注意:commandcommand必须以必须以必须以必须以TABTAB键开头。如果某一行过长可以键开头。如果某一行过长可以键开头。如果某一行过长可以键开头。如果某一行过长可以分作两行,用分作两行,用分作两行,用分作两行,用连接。连接。连接。连接。例如:例如:例如:例如:smcinitsmcinit: arm-elf-ar ruvs o arm-elf-ar ruvs o : arm-elf-gcc c o arm-elf-gcc c o : arm-elf-gcc c o arm-el

84、f-gcc c o cleanclean:rm *.orm *.o表示目标名的有表示目标名的有表示目标名的有表示目标名的有smcinitsmcinit、。、。、。、。smcinitsmcinit依赖于和,依赖于和,依赖于和,依赖于和,而又依赖于和依赖于和而又依赖于和依赖于和而又依赖于和依赖于和而又依赖于和依赖于和. .各目标分别由命令各目标分别由命令各目标分别由命令各目标分别由命令arm-elf-ar ruvs o arm-elf-ar ruvs o ;arm-elf-arm-elf-gcc c o gcc c o ;arm-elf-gcc c o arm-elf-gcc c o 来生成。来生

85、成。来生成。来生成。cleanclean为一动作名,删除所有后缀为为一动作名,删除所有后缀为为一动作名,删除所有后缀为为一动作名,删除所有后缀为.o.o的文件。的文件。的文件。的文件。 3. make调用调用makefile中的规则中的规则在默认情况下,在默认情况下,在默认情况下,在默认情况下,makemake运行不是以运行不是以运行不是以运行不是以“.”“.”开头的第一开头的第一开头的第一开头的第一条规则。在上面的例子中,条规则。在上面的例子中,条规则。在上面的例子中,条规则。在上面的例子中,makemake默认执行的是规则默认执行的是规则默认执行的是规则默认执行的是规则smcinitsmc

86、init,此时只需要输入命令:,此时只需要输入命令:,此时只需要输入命令:,此时只需要输入命令: makemakemakemake将读入将读入将读入将读入makefilemakefile,然后执行第一条规则,例,然后执行第一条规则,例,然后执行第一条规则,例,然后执行第一条规则,例子中该规则是链接目标文件生成库,因此必须执行规则子中该规则是链接目标文件生成库,因此必须执行规则子中该规则是链接目标文件生成库,因此必须执行规则子中该规则是链接目标文件生成库,因此必须执行规则smcinitsmcinit依赖的规则和。在执行过程中将自动更新他们依赖的规则和。在执行过程中将自动更新他们依赖的规则和。在执

87、行过程中将自动更新他们依赖的规则和。在执行过程中将自动更新他们所依赖的文件。所依赖的文件。所依赖的文件。所依赖的文件。有些规则不是被依赖的规则,需要有些规则不是被依赖的规则,需要有些规则不是被依赖的规则,需要有些规则不是被依赖的规则,需要makemake指定才能指定才能指定才能指定才能被运行,如上面的例子中的被运行,如上面的例子中的被运行,如上面的例子中的被运行,如上面的例子中的cleanclean规则可以这样执行:规则可以这样执行:规则可以这样执行:规则可以这样执行:make cleanmake clean这两种方式的结果一样。只是第一种方式没指明目这两种方式的结果一样。只是第一种方式没指明

88、目这两种方式的结果一样。只是第一种方式没指明目这两种方式的结果一样。只是第一种方式没指明目标名,第二种方式指明了目标名。标名,第二种方式指明了目标名。标名,第二种方式指明了目标名。标名,第二种方式指明了目标名。4.设置设置makefile中文件的搜索路径中文件的搜索路径在在在在makefilemakefile中,可以通过给中,可以通过给中,可以通过给中,可以通过给VPATHVPATH赋值来设置规则中目标文赋值来设置规则中目标文赋值来设置规则中目标文赋值来设置规则中目标文件和依赖文件的搜索目录。件和依赖文件的搜索目录。件和依赖文件的搜索目录。件和依赖文件的搜索目录。makemake首先搜索当前目

89、录,如果未找到首先搜索当前目录,如果未找到首先搜索当前目录,如果未找到首先搜索当前目录,如果未找到依赖的文件,依赖的文件,依赖的文件,依赖的文件,makemake将按照将按照将按照将按照VPATHVPATH中给的目录依次搜索。中给的目录依次搜索。中给的目录依次搜索。中给的目录依次搜索。VPATHVPATH对对对对makefilemakefile中所有文件都有效。中所有文件都有效。中所有文件都有效。中所有文件都有效。例如:例如:例如:例如: 在目录在目录在目录在目录/c/demo/c/demo/中,在目录中,在目录中,在目录中,在目录/c/demo/head/c/demo/head/中,则可以给

90、中,则可以给中,则可以给中,则可以给VPATHVPATH变量赋值:变量赋值:变量赋值:变量赋值:VPATH VPATH := /c/demo /c/demo/head= /c/demo /c/demo/head或者或者或者或者VPATH VPATH := /c/demo= /c/demo:/c/demo/head /c/demo/head 也可以使用指令也可以使用指令也可以使用指令也可以使用指令vpathvpath,与,与,与,与VAPTHVAPTH在使用上的在使用上的在使用上的在使用上的区别区别区别区别是是是是:vpath:vpath可以给不同类文件指定不同的搜索目录。可以给不同类文件指定不

91、同的搜索目录。可以给不同类文件指定不同的搜索目录。可以给不同类文件指定不同的搜索目录。%.o%.o表示所有以表示所有以表示所有以表示所有以 .o .o结尾的结尾的结尾的结尾的子串。子串。子串。子串。 vpath %.c /c/demovpath %.h /c/demo/headvpath %.c 表示清除所有表示清除所有vpath对对%.c设置的搜索目录设置的搜索目录vpath 表示清除所有以前用表示清除所有以前用vpath设置的搜索目录设置的搜索目录这两种方式的效果是一样的,但是后一这两种方式的效果是一样的,但是后一种要明确一些。这样种要明确一些。这样make就会根据就会根据VPATH或者或

92、者vpath来搜索相应的依赖文件。来搜索相应的依赖文件。 5. 如何定义变量如何定义变量为了简化为了简化makefile以及减少不必要的错以及减少不必要的错误,可以用变量的形式来代表目标文件名或误,可以用变量的形式来代表目标文件名或字符串,在需要使用时直接调用变量。字符串,在需要使用时直接调用变量。在在makefile中变量可以被这样定义:中变量可以被这样定义:CC = arm-elf-gccAS := arm-elf -asAR = arm-elf -arLIBPATH := ./lib从上面的定义中可以看出,有从上面的定义中可以看出,有两种定义变量的形式:两种定义变量的形式:1.变量名变量

93、名 = 值值2.变量名:变量名:= 值值两者的两者的不同点不同点在于,前者定义在于,前者定义的变量是在被用到时才取它的值,的变量是在被用到时才取它的值,而后者则是在定义变量或者给它赋而后者则是在定义变量或者给它赋值时就确定了它的值。值时就确定了它的值。例如:例如:例如:例如:var1 = hello firstvar1 = hello firstvar2 var2 = = $var1 $var1var1 = hello secondvar1 = hello secondtest_echotest_echo:echo $var2echo $var2执行的结果是显示:执行的结果是显示:执行的结果是

94、显示:执行的结果是显示:hello secondhello secondvar1 = hello firstvar1 = hello firstvar2 var2 :=:= $var1 $var1var1 = hello secondvar1 = hello secondtest_echotest_echo:echo $var2echo $var2执行的结果是显示:执行的结果是显示:执行的结果是显示:执行的结果是显示:hello firsthello first例如:例如:var1 = hello firstvar1 = $var1 and secondecho_test:echo $var1

95、会陷入死循环中。会陷入死循环中。var1 := hello firstvar1 := $var1 and secondecho_test:echo $var1会显示:会显示:hello first and second6. 引用变量引用变量有两种方式:有两种方式:1.$VarName2.$(VarName) 两种方式的效果一样。两种方式的效果一样。VarName表示变量名。表示变量名。7. make提供的常用变量提供的常用变量$表示目标名表示目标名$ 表示所有的依赖文件表示所有的依赖文件$ 第一个依赖文件第一个依赖文件例如:例如: : $CC $CFLAGS $ -o $*.o的的隐含规则隐含

96、规则为:为:%.o:%.c$CC $CFLAGS $*.o的的隐含规则隐含规则为:为: %.o:%.s$AS $ASFLAGS $4设置目标文件名字设置目标文件名字-o filename每次运行每次运行arm-elf-as只输出一个目标文件,默只输出一个目标文件,默认输出文件为。认输出文件为。可以通过可以通过-o选项指定输出文件名字,通常都选项指定输出文件名字,通常都以以.o为后缀。为后缀。如果指定输出文件的名字和现有某个文件重名,如果指定输出文件的名字和现有某个文件重名,生成的文件将直接覆盖已有的文件。生成的文件将直接覆盖已有的文件。例如例如:编译输出目标文件:编译输出目标文件:arm-el

97、f-as I/include o 5如何取消警告信息如何取消警告信息-W加选项加选项-W以后,运行以后,运行arm-elf-as就不就不输出警告信息。输出警告信息。例如例如:编译输出目标文件,不输出警告信息:编译输出目标文件,不输出警告信息:arm-elf-as W o 6设置是否进行预处理设置是否进行预处理arm-elf-as内部的预处理程序,完成以内部的预处理程序,完成以下工作:调整并删除多余空格,删除注下工作:调整并删除多余空格,删除注释,将字符常量改成对应的数值。释,将字符常量改成对应的数值。arm-elf-as不执行不执行arm-elf-gcc预处理程预处理程序能完成的部分,如宏预处

98、理和包含文序能完成的部分,如宏预处理和包含文件预处理。件预处理。可以通过可以通过.include “file”对指定文件对指定文件进行预处理。进行预处理。arm-elf-gcc可以对后缀为可以对后缀为.S汇编程序进汇编程序进行其他形式的预处理。行其他形式的预处理。 如果源文件第一行是如果源文件第一行是NO_APP或者或者编译时使用选项编译时使用选项-f将不进行预处理。将不进行预处理。如果要保留空格或注释,可以在需要如果要保留空格或注释,可以在需要保留部分开始加入保留部分开始加入APP,结束的地方,结束的地方加加NO_APP。例如例如:编译输出目标文件,并且编译时不编译输出目标文件,并且编译时不

99、进行预处理,则命令如下:进行预处理,则命令如下:arm-elf-as f o 3.6 汇编语言编程汇编语言编程在在ARM汇编语言程序里汇编语言程序里,有一些特殊指令助记有一些特殊指令助记符符,这些助记符与指令系统的助记符不同这些助记符与指令系统的助记符不同,没有相对没有相对应的操作码应的操作码,通常称这些特殊指令助记符为通常称这些特殊指令助记符为伪指令伪指令,它们所完成的操作称为它们所完成的操作称为伪操作伪操作。伪指令在源程序中的作用是为完成汇编程序作伪指令在源程序中的作用是为完成汇编程序作各种准备工作的,这些伪指令仅在汇编过程中起作各种准备工作的,这些伪指令仅在汇编过程中起作用,一旦汇编结束

100、,伪指令的使命就完成了。用,一旦汇编结束,伪指令的使命就完成了。在在ARM的汇编程序中,有如下几种伪指令:符的汇编程序中,有如下几种伪指令:符号定义伪指令、数据定义伪指令、汇编控制伪指令、号定义伪指令、数据定义伪指令、汇编控制伪指令、宏指令以及其他伪指令。宏指令以及其他伪指令。3.6.1 汇编语言汇编语言1. 基本元素基本元素(1) 字符集字符集汇编中使用下列字符组成源程序汇编中使用下列字符组成源程序的各种语法元素:大写字母的各种语法元素:大写字母 A Z;小写字母小写字母 a z;数字;数字 0 9;符号;符号 + - * / = ( ) ; , . : $ & # % _ “ - | ?

101、!。 其中大小写字母作用不同。其中大小写字母作用不同。(2) 约定的名字约定的名字包括寄存器名、指令名字和伪包括寄存器名、指令名字和伪操作符。每一个伪操作符表示一定操作符。每一个伪操作符表示一定功能的操作。功能的操作。伪操作的功能由汇编系统实现伪操作的功能由汇编系统实现,没有目标代码对应。这一点是伪操没有目标代码对应。这一点是伪操作符与操作符的作符与操作符的不同不同之处。伪操作之处。伪操作符是由汇编系统约定的名字,不用符是由汇编系统约定的名字,不用定义就能实现。定义就能实现。伪操作符可以分为六类:伪操作符可以分为六类: 1.数据定义伪操作符数据定义伪操作符2.符号定义伪操作符符号定义伪操作符3

102、.程序结构伪操作符程序结构伪操作符4.条件汇编伪操作符条件汇编伪操作符5.宏伪操作符宏伪操作符6.其他伪操作符其他伪操作符(3) 定义的名字定义的名字汇编程序中的标号、分段名、汇编程序中的标号、分段名、宏定义名都是用户可以定义的名字。宏定义名都是用户可以定义的名字。 标号标号标号只能由标号只能由a z 、A Z、0 9、_等字符组成,标号的长等字符组成,标号的长度不受限制度不受限制,大小写字母有区别。大小写字母有区别。当标号为当标号为0 9的单个数字时表示该标的单个数字时表示该标号为局部标号号为局部标号, 局部标号可以多次重复出现。局部标号可以多次重复出现。在引用时,使用方法如下(在引用时,使

103、用方法如下(N代表代表0 9的数字):的数字):Nf 在引用处的地方向前(程序地在引用处的地方向前(程序地址增长的方向)的址增长的方向)的N标号。标号。Nb 在引用处的地方向后(程序地在引用处的地方向后(程序地址增长的方向)的址增长的方向)的N标号。标号。标号在最终的绝对定位的代码中表示所标号在最终的绝对定位的代码中表示所在处的地址,因此在汇编中的标号可以在在处的地址,因此在汇编中的标号可以在C/C+程序中当作变量或者函数来使用。程序中当作变量或者函数来使用。 分段名分段名汇编系统中预定义的分段名有汇编系统中预定义的分段名有: .text .bss .data .sdata .sbss 等,但

104、是用户可等,但是用户可以自己定义段名,语法如下:以自己定义段名,语法如下:.section section_name attribute例如:定义一个可以执行的代码段例如:定义一个可以执行的代码段 .mytext.section .mytext,axmycode 宏定义名宏定义名宏定义的语法如下:宏定义的语法如下:.macro macro_name parm1 parmNmacro body.endm(4) 常数常数二进制数由二进制数由0b或者或者0B开头,如:开头,如:0b1000101、0B1001110;十六进制数以十六进制数以0x或者或者0X开头,如:开头,如:0x4567、0X100

105、89;八进制数由八进制数由0开头,如:开头,如:0345、09870;十进制数以非零数开头,如:十进制数以非零数开头,如:345、12980例如:例如:.byte 74, 0112, 092, 0x4A, 0X4a, J, J .ascii Ring the bell7(5) 当前地址数当前地址数当前的地址数用点号当前的地址数用点号“.”表示,在汇编程序中可表示,在汇编程序中可以直接使用该符号。以直接使用该符号。(6) 表达式表达式在汇编程序中可以使用表达式,在在汇编程序中可以使用表达式,在表达式中可以使用常数和数值。表达式中可以使用常数和数值。可以使用的运算符有:可以使用的运算符有: 前缀运

106、算符号前缀运算符号- 取负数取负数 取补数取补数 中缀运算符号中缀运算符号* / % | & !+ -(7) 注释符号注释符号不同芯片的汇编程序中,注释不同芯片的汇编程序中,注释的符号有所不同,以的符号有所不同,以“”开开头的程序行是注释行。头的程序行是注释行。2. 语句语句(1) 语句类型语句类型汇编语句按其作用和编译的情况分为两汇编语句按其作用和编译的情况分为两大类:大类:执行性语句执行性语句和和说明性语句说明性语句。执行性语句执行性语句是在编译后有目标程序与之是在编译后有目标程序与之对应,按其编译后目标程序的对应情况又可对应,按其编译后目标程序的对应情况又可以分为:以分为:一般执行性语句

107、一般执行性语句和和宏语句宏语句。一般执行性语句一般执行性语句与目标程序是一一对应与目标程序是一一对应的,即一个一般执行语句只产生一条目标代的,即一个一般执行语句只产生一条目标代码指令。码指令。宏语句宏语句由伪操作符定义,包括宏定义、由伪操作符定义,包括宏定义、宏调用及宏扩展语句。一个宏语句对应了一宏调用及宏扩展语句。一个宏语句对应了一组目标代码程序,可以看成是一般执行性语组目标代码程序,可以看成是一般执行性语句的扩展。句的扩展。 说明性语句说明性语句由伪操作符定义,它用于用户以源由伪操作符定义,它用于用户以源程序方式和汇编程序通信。程序方式和汇编程序通信。用户使用说明性语句表示源程序的终止说明

108、、用户使用说明性语句表示源程序的终止说明、分段定义、数据定义、内存结构等信息。分段定义、数据定义、内存结构等信息。数据定义语句用于描述数据和给数据赋初值数据定义语句用于描述数据和给数据赋初值.列表控制语句用于说明源程序的格式要求。列表控制语句用于说明源程序的格式要求。程序结构语句用于说明源程序的结构和目标程程序结构语句用于说明源程序的结构和目标程序的结构。序的结构。条件汇编语句用于说明汇编某部分语句时的条条件汇编语句用于说明汇编某部分语句时的条件,满足条件则编译件,满足条件则编译,否则跳过这部分不予编译否则跳过这部分不予编译.(2) 数据语句一字节数据定义语句一字节数据定义语句一字节数据定义语

109、句一字节数据定义语句语法:语法:语法:语法:.byte expressions.byte expressions例子:例子:例子:例子:.byte 0x89 ,0x45, 56, K , M , 023, 0B101011.byte 0x89 ,0x45, 56, K , M , 023, 0B101011两字节数据定义语句两字节数据定义语句两字节数据定义语句两字节数据定义语句语法:语法:语法:语法: .short expressions .short expressions例子:例子:例子:例子: .short 0x6789 ,0b101110111 .short 0x6789 ,0b101

110、110111 四字节数据定义语句四字节数据定义语句四字节数据定义语句四字节数据定义语句语法:语法:语法:语法:.long expressions.long expressions例子:例子:例子:例子:.long 0x78896676 , 02356243563456 .long 0x78896676 , 02356243563456 八字节数据定义八字节数据定义八字节数据定义八字节数据定义语法:语法:语法:语法: .quad expressions .quad expressions例子:例子:例子:例子: 单个字串定义单个字串定义单个字串定义单个字串定义语法:语法:语法:语法:.strin

111、g “ string”.string “ string”例子:例子:例子:例子:.string “this is an example”.string “this is an example”多个字串多个字串多个字串多个字串1 1语法:语法:语法:语法:.ascii “string”.ascii “string”.例子:例子:例子:例子:.ascii “string1” .ascii “string1” ,“string2”,“string3”,“string2”,“string3”string1,string2,string3string1,string2,string3字串间是连续的。字串

112、间是连续的。字串间是连续的。字串间是连续的。多个字串多个字串多个字串多个字串2 2语法:语法:语法:语法:.asciz “string”.asciz “string”.例子:例子:例子:例子:.asciz “string1” .asciz “string1” ,“string2”,“string3”,“string2”,“string3” 重复数据定义重复数据定义语法:语法:.rept count 数据定义数据定义 .endr例子:例子:.rept 等价于:等价于: .long 0x788 .byte 0x99.long 0x788 .long 0x788.byte 0x99 .byte 0x

113、99.endr各种数据在内存空间的对齐边界示按各种数据在内存空间的对齐边界示按数据本身的大小对齐的,数据本身的大小对齐的,byte以字节对以字节对齐,齐,short以字节对齐,以字节对齐,long以字节以字节对齐,对齐,例如例如:.byte 0x78.short0x66.long0x789一共占用个字节而不是个字节。一共占用个字节而不是个字节。(3) 列表控制语句列表控制语句.title “heading”在汇编列表中将在汇编列表中将“heading“作为作为标题。标题。.list系统遇此语句就输出列表文件。系统遇此语句就输出列表文件。(4)一般执行语句)一般执行语句不同的芯片有不同指令集,不

114、同的芯片有不同指令集,见相关的指令手册。见相关的指令手册。 3. 程序结构程序结构(1)程序结构语句)程序结构语句程序结构语句程序结构语句是伪操作符定义的说明是伪操作符定义的说明语句,用于说明程序段的开始、结束以及语句,用于说明程序段的开始、结束以及源程序的结束等。源程序的结束等。在汇编系统中有预定义的程序结构语在汇编系统中有预定义的程序结构语句,用户也可以自己定义一些程序结构段句,用户也可以自己定义一些程序结构段(详见中关于(详见中关于.section的说明)。的说明)。预定义的程序结构语句如下(以下用预定义的程序结构语句如下(以下用ARM的汇编指令举例说明):的汇编指令举例说明): 数据段

115、的开始数据段的开始.data 表示一个数据段的开始,其它段的结束:表示一个数据段的开始,其它段的结束:.data.long 0xFFFFFC04,0x0F0CFC04,0x0FFFF804,0x01BF70040xFFFFFC04,0x0F0CFC04,0x0FFFF804,0x01BF7004.long 0x0FFDD000,0x1FFFF447,0x0FFFFC04,0x1FFFFC070x0FFDD000,0x1FFFF447,0x0FFFFC04,0x1FFFFC07 .bss表示未初始化数据段的开始,其它段的结束:表示未初始化数据段的开始,其它段的结束:表示未初始化数据段的开始,其它

116、段的结束:表示未初始化数据段的开始,其它段的结束:.bss.long 0,0,0.long 0,0,0 源程序的结束.end 表示该源程序的结束,表示该源程序的结束,在在.end后面的程序不会被编译。后面的程序不会被编译。 (2) 过程(函数)的定义过程(函数)的定义过程的结构如下:过程的结构如下:过程名:过程名:过程体过程体返回语句返回语句 例如(用例如(用ARM的汇编指令举例的汇编指令举例说明):说明):.align 2.align 2.globl uart1_sendch.globl uart1_sendch.type uart1_sendch,function.type uart1_s

117、endch,functionuart1_sendch:uart1_sendch:ldrldrr2,=SYSFLGr2,=SYSFLG1:1:ldrldrr1,r2r1,r2tsttstr1,#UTXFF1r1,#UTXFF1bnebne1b1bldrldrr2,=UARTDR1r2,=UARTDR1strbstrb r0,r2r0,r2movmov pc,lrpc,lr一般情况下,一般情况下,一般情况下,一般情况下,.type .type 和和和和.align.align声明可以缺省。声明可以缺省。声明可以缺省。声明可以缺省。 3.6.2 宏语句与条件汇编宏语句与条件汇编1. 等价语句等价语句

118、(1) .equ语句语句语法:语法: .equ symbol,expression例子:例子: .equ PPC_PC,32*4应用:应用: stwr4,PPC_PC(r1) (2) .set语句语句与与.equ的功能相同的功能相同。 2. 宏定义与宏调用宏定义与宏调用宏定义:宏定义:.macro macro_name param1 ,param2,.paramN .macro body.endm例如:(使用例如:(使用ARM的汇编指令集说明)的汇编指令集说明).macro ROMSEC_patovaTTPA,pa_start,va_start,tmp,ic ldrtmp,=APFIELD_R

119、OMadd TTPA,TTPA,va_start,LSR #18add tmp,tmp,pa_start20:strtmp,TTPA,#4add tmp,tmp,#0x10000subsic,ic,#1bne 20b .endm例如:例如:.macro rept3.long 0x9000000.long 0x9000000.long 0x9000000.endm应用:应用:.datarept3等价于等价于.data.long 0x9000000.long 0x9000000.long 0x9000000 当宏定义有参数时,在参数前面添加前当宏定义有参数时,在参数前面添加前缀缀“” 。如果要提前

120、退出宏可以使用。如果要提前退出宏可以使用.exitm 。宏定义中的参数还可以有缺省值,如:。宏定义中的参数还可以有缺省值,如:.macro test p1=0x100 p2 .long p1.long p2.endm应用:应用:test , 2 等价于等价于.long 0x100.long 23. 3. 重复块和源文件的嵌入重复块和源文件的嵌入重复块和源文件的嵌入重复块和源文件的嵌入(1) (1) 重复块重复块重复块重复块定义:定义:定义:定义:.rept count.rept countcontentscontents.endr.endr例如:例如:例如:例如:.rept 2.rept 2.

121、long 0x12908.long 0x12908.endr.endr等价于:等价于:等价于:等价于:.long 0x12908.long 0x12908.long 0x12908.long 0x12908(2) 源文件的嵌入源文件的嵌入在一个汇编文件中可以嵌入其在一个汇编文件中可以嵌入其它汇编文件,例如汇编头文件等。它汇编文件,例如汇编头文件等。方法如下:方法如下:.include “filename”4 条件汇编条件汇编.if expression .if expression 表达式为非零则编译后面的语句,否则后面的语表达式为非零则编译后面的语句,否则后面的语表达式为非零则编译后面的语句

122、,否则后面的语表达式为非零则编译后面的语句,否则后面的语句被忽略。句被忽略。句被忽略。句被忽略。.ifdef symbol.ifdef symbol如果符号被定义则编译后面的语句,否则后面的如果符号被定义则编译后面的语句,否则后面的如果符号被定义则编译后面的语句,否则后面的如果符号被定义则编译后面的语句,否则后面的语句被忽略。语句被忽略。语句被忽略。语句被忽略。.ifndef symbol.ifndef symbol如果符号未被定义则编译后面的语句,否则后面如果符号未被定义则编译后面的语句,否则后面如果符号未被定义则编译后面的语句,否则后面如果符号未被定义则编译后面的语句,否则后面的语句被忽略

123、。的语句被忽略。的语句被忽略。的语句被忽略。.else.else表示与前面的表示与前面的表示与前面的表示与前面的if if语句的条件相反。语句的条件相反。语句的条件相反。语句的条件相反。.endif.endif表示条件判断结束。表示条件判断结束。表示条件判断结束。表示条件判断结束。例如:例如:例如:例如:.macro sum from=0, to=5.macro sum from=0, to=5 .long from .long from .if to - from .if to - from sum “(from+1)”,to sum “(from+1)”,to .endif .endif.

124、endm.endm应用:应用:应用:应用:sumsum,5 5等价于等价于等价于等价于.long 0.long 0 .long 5 .long 5条件判断可以嵌套使用,条件判断可以嵌套使用,条件判断可以嵌套使用,条件判断可以嵌套使用,if-else-endifif-else-endif遵循最遵循最遵循最遵循最近匹配的原则。近匹配的原则。近匹配的原则。近匹配的原则。3.6.3 模块化程序设计模块化程序设计 模块化程序设计汇编语言程序可模块化程序设计汇编语言程序可以先按模块独立汇编,然后和应用以先按模块独立汇编,然后和应用的其他模块链接形成一个可执行的的其他模块链接形成一个可执行的程序程序 1全局

125、符号全局符号在模块中定义的、要被别的模块使在模块中定义的、要被别的模块使用的符号(包括变量名和函数名)都必用的符号(包括变量名和函数名)都必须被声明为全局符号。须被声明为全局符号。方法如下:方法如下:.global symbol在本模块中要使用其他模块中的全在本模块中要使用其他模块中的全局符号,可以用局符号,可以用.extern symbol的方式的方式声明,但也可以不用声明在汇编时自动声明,但也可以不用声明在汇编时自动认为它是其它模块中的全局符号。认为它是其它模块中的全局符号。2模块间的符号互用模块间的符号互用(1)汇编模块与汇编模块间的调用)汇编模块与汇编模块间的调用只要是全局符号在汇编模

126、块间就只要是全局符号在汇编模块间就可以直接使用。可以直接使用。(2)汇编模块调用)汇编模块调用C语言模块中的语言模块中的函数函数汇编模块调用汇编模块调用C语言模块时,不语言模块时,不同芯片传递参数的方式有差别,详同芯片传递参数的方式有差别,详细见节。细见节。(3)汇编模块使用)汇编模块使用C语言模块中语言模块中的变量的变量首先保证该变量在首先保证该变量在C语言中是全局语言中是全局变量,然后在汇编中直接使用变量名。变量,然后在汇编中直接使用变量名。注意:注意:C语言中的变量名在汇编中语言中的变量名在汇编中不用加下划线。另外该变量名不能用不用加下划线。另外该变量名不能用static修饰,否则该变量

127、只局限于所在修饰,否则该变量只局限于所在的模块有效。的模块有效。 (4)C语言模块调用汇编模块中的函数语言模块调用汇编模块中的函数该函数名在汇编程序中必须是全局该函数名在汇编程序中必须是全局的符号,即必须用的符号,即必须用.global声明,然后在声明,然后在C语言中申明该函数的原型,最后在使语言中申明该函数的原型,最后在使用时与一般的用时与一般的C函数一样。函数一样。(5)C语言模块使用汇编模块中的变量语言模块使用汇编模块中的变量该变量在汇编程序中必须是全局的该变量在汇编程序中必须是全局的符号,即必须用符号,即必须用.global声明,然后在声明,然后在C语言中申明该变量的原型,最后在使用语

128、言中申明该变量的原型,最后在使用时与一般的时与一般的C变量一样。变量一样。 3.6.4 内存模式内存模式在在uClinux环境下,内存模式为平环境下,内存模式为平模式,即整个内存空间最大为模式,即整个内存空间最大为4GB。所有任务共享这所有任务共享这4GB的空间,而不的空间,而不是每个任务有单独的是每个任务有单独的4G虚拟空间。虚拟空间。所有的寻址都是所有的寻址都是32位地址的方式,位地址的方式,因此程序模块间可以很容易的共享变量因此程序模块间可以很容易的共享变量和数据。和数据。3.6.5 StrongARM & ARM71.1.寄存器名字寄存器名字寄存器名字寄存器名字寄存器名字如表寄存器名字

129、如表寄存器名字如表寄存器名字如表3-13-1所示。所示。所示。所示。 类类型型型型说说 明明明明R0R0r14r14r14r14通用寄存器通用寄存器通用寄存器通用寄存器F0F0f7f7f7f7浮点寄存器浮点寄存器浮点寄存器浮点寄存器PcPc指令指指令指指令指指令指针针PsPs机器状机器状机器状机器状态态寄存器寄存器寄存器寄存器fpsfps浮点状浮点状浮点状浮点状态态寄存器寄存器寄存器寄存器2. 如何在汇编模块中调用如何在汇编模块中调用C语言模块语言模块中的函数中的函数在调用在调用C函数之前,必须在当前栈中空出至函数之前,必须在当前栈中空出至少少8个字节的空间,然后才调用个字节的空间,然后才调用

130、C函数。函数。C函数的第一个参数(最左边的参数)用函数的第一个参数(最左边的参数)用r0传递,后面的参数依次用传递,后面的参数依次用r1、r2等来传递。等来传递。例如:例如:假定假定C函数为函数为int get_sum (int var1,int var2),则在汇编程序中首先将参数送到则在汇编程序中首先将参数送到r0、r1中,中,然后将栈指针减然后将栈指针减8,最后调用,最后调用get_sum。注意注意:C函数名在汇编中使用时不用加下划线函数名在汇编中使用时不用加下划线3注释符号注释符号以以“”开头的程序行是注释行。开头的程序行是注释行。4一段程序一段程序在下面的程序中有在下面的程序中有.d

131、ata .bss .text等三个预定等三个预定义的段,在程序的后面定义了一个用户自己的段义的段,在程序的后面定义了一个用户自己的段.mytext,属性为可执行段。,属性为可执行段。在程序中还用在程序中还用.global 声明了几个本文件中的符声明了几个本文件中的符号为全局符号号为全局符号,在其他模块中可以使用这些符号在其他模块中可以使用这些符号(var1作为变量作为变量,u1b_set作为函数使用)作为函数使用).title “example”.data.global var1var1:.long 0x897678 ,0x2378789.byte 89 ,56, 23.string “ he

132、llo”.bss.global zero_varzero_var:.short 0,0,0.long 0,0,0,0.textUART1INIT_TEST:ldr r3,=SYSCON1ldr r0,r3tst r0,#UART1ENbeq 2f1:ldr r1,r3tst r1,#UTXFF1bne 1b2:bic r0,r0,#UART1ENstr r0,r3bic r0,r0,#SIRENstr r0,r3orr r0,r0,#UART1ENstr r0,r3ldr r3,=SYSFLG2ldr r0,r3and r0,r0,#0x40mov pc,lr.section “.mytext

133、” , “ax”.global u1b_setu1b_set:ldr r3,=UBLCR1str r0,r3mov pc,lr.end3.7 简单程序设计简单程序设计3.7.1 顺序程序设计顺序程序设计例例3-1 用用ARM指令实现的指令实现的C赋值语句:赋值语句:x=(a+b)-c可以用可以用r0表示表示a、rl表示表示b、r2表示表示c和和r3表示表示x,用,用r4作为间接寻址寄存器。作为间接寻址寄存器。在进行算术运算之前,代码必须先把在进行算术运算之前,代码必须先把a、b、c的值装入到寄存器,运算结束后,还要的值装入到寄存器,运算结束后,还要把把x的值存回存储器中。的值存回存储器中。这段

134、代码执行下面这些必须的步骤这段代码执行下面这些必须的步骤:ADR r4,a;读取变量;读取变量 a 的地址的地址LDR r0,r4;读;读a的内容到的内容到 r0ADR r4,b;读取变量;读取变量b的地址的地址LDR rl,r4 ;读;读b内容到内容到 r1ADD r3,r0,rl;a+b 的结果保存在的结果保存在r3ADR r4,c;读取变量;读取变量c的地址的地址LDR r2,r4;读;读c的内容到的内容到r2SUB r3,r3,r2 ;(a+b)-c结果保存到结果保存到r3ADR r4,x;读;读x的地址的地址STR r3,r4 ;保存变量;保存变量x例例例例3-2 3-2 用用用用A

135、RMARM指令实现的指令实现的指令实现的指令实现的C C赋值语句:赋值语句:赋值语句:赋值语句:z=(a2)|(b&15)z=(a2)|(b&15)可以使用可以使用可以使用可以使用r0r0表示表示表示表示a a和和和和z z,r1r1表示表示表示表示b b,r4r4表示地址进行表示地址进行表示地址进行表示地址进行编码,代码如下:编码,代码如下:编码,代码如下:编码,代码如下: ADR r4,a ADR r4,a ;读取变量;读取变量;读取变量;读取变量a a的地址到的地址到的地址到的地址到r4r4 LDR r0,r4 LDR r0,r4 ;读;读;读;读a a的内容到的内容到的内容到的内容到r

136、0r0 MOV r0,r0,LSL 2 MOV r0,r0,LSL 2 ;实现;实现;实现;实现a2 a2 操作,结果保存在操作,结果保存在操作,结果保存在操作,结果保存在r0r0 ADR r4,b ADR r4,b ;读取变量;读取变量;读取变量;读取变量b b的地址到的地址到的地址到的地址到r4r4 LDR rl,r4 LDR rl,r4 ;读;读;读;读b b的内容到的内容到的内容到的内容到r1r1 AND r1,r1,#15 AND r1,r1,#15 ;实现;实现;实现;实现b&15 b&15 操作,结果保存在操作,结果保存在操作,结果保存在操作,结果保存在r1r1中中中中 ORR

137、rl,r0,rl ORR rl,r0,rl ;计算;计算;计算;计算z z的结果的结果的结果的结果 ADR r4,z ADR r4,z ;读取变量;读取变量;读取变量;读取变量z z的地址到的地址到的地址到的地址到r4r4 STR rl,r4 STR rl,r4 ;保存变量;保存变量;保存变量;保存变量z z 3.7.2 分支程序设计分支程序设计我们用示例我们用示例我们用示例我们用示例3-33-3作为探讨条件执行的用法的方法。作为探讨条件执行的用法的方法。作为探讨条件执行的用法的方法。作为探讨条件执行的用法的方法。 例例例例3-3 3-3 在在在在ARMARM中实现下面中实现下面中实现下面中实

138、现下面if if语句:语句:语句:语句: if(ab) if(a=b, a=b,跳转到跳转到跳转到跳转到 fblock fblock子程序执行子程序执行子程序执行子程序执行 MOV r0,#5 MOV r0,#5 ;令;令;令;令x = 5x = 5 ADR r4,x ADR r4,x ;读取;读取;读取;读取x x的地址到的地址到的地址到的地址到r4r4 STR r0,r4 STR r0,r4 ;保存变量;保存变量;保存变量;保存变量x x ADR r4,c ADR r4,c ;读取变量;读取变量;读取变量;读取变量c c的地址到的地址到的地址到的地址到r4r4 LDR r0,r4 LDR

139、r0,r4 ;读;读;读;读c c 的内容到的内容到的内容到的内容到r0r0 ADR r4,d ADR r4,d ;读取变量;读取变量;读取变量;读取变量d d的地址到的地址到的地址到的地址到r4 r4 LDR rl,r4 ;读取;读取d的内容到的内容到r1 ADD r0,r0,rl ;计算;计算a+b,结果保存在,结果保存在r0 ADR r4,y ;读取变量;读取变量y的地址的地址 STR r0,r4 ;结果保存在;结果保存在y中中 B after ;程序跳转到;程序跳转到after 子块子块fblock: ADR r4,c ;读取变量;读取变量c的地址的地址 LDR r0,r4 ;读;读c

140、的内容到的内容到r0 ADR r4,d ;读取变量;读取变量d的地址到的地址到r4 LDR rl,r4 ;读变量;读变量d的内容到的内容到r1 SUB r0,r0,rl ;计算;计算a b 结果保存在结果保存在r0 ADR r4,x ;读取变量;读取变量x的地址的地址 STR r0,r4 ;结果保存在;结果保存在 x中中after: 例例3-4 在在ARM中实现中实现C的的switch语句语句 C中的中的switch语句采用下列形式:语句采用下列形式: switch(test) case 0:break; case 1:break; 上述语句也可以像上述语句也可以像if语句那样编码,首语句那样

141、编码,首先测试先测试test=A,然后测试,然后测试test=B,依此类推,依此类推. 用基址加偏移量寻址并建立分支表的方用基址加偏移量寻址并建立分支表的方法执行起来会更有效地实现:法执行起来会更有效地实现: ADR r2,test ;读取变量读取变量test的地址的地址 LDR r0,r2 ;读读test的内容到的内容到r0 ADR rl,switchtab ;读取读取switchtab 的地址到的地址到r1 LDR rl5,rl,r0,LSL #2 switchtab: .word case 0 .word case l case0: code for case 0casel: code

142、for case 1这种实现方法使用这种实现方法使用这种实现方法使用这种实现方法使用testtest值作为一个表的偏移量,其中值作为一个表的偏移量,其中值作为一个表的偏移量,其中值作为一个表的偏移量,其中该表保存了实现各种情况的代码段的地址。该表保存了实现各种情况的代码段的地址。该表保存了实现各种情况的代码段的地址。该表保存了实现各种情况的代码段的地址。这段代码的核心就是这段代码的核心就是这段代码的核心就是这段代码的核心就是LDRLDR指令,它把多种功能集中在指令,它把多种功能集中在指令,它把多种功能集中在指令,它把多种功能集中在一个简单的指令里:一个简单的指令里:一个简单的指令里:一个简单的

143、指令里:它将它将它将它将r0r0的值左移两位,把偏移量转化为字地址。的值左移两位,把偏移量转化为字地址。的值左移两位,把偏移量转化为字地址。的值左移两位,把偏移量转化为字地址。它用基址加偏移量寻址的方法把左移了的它用基址加偏移量寻址的方法把左移了的它用基址加偏移量寻址的方法把左移了的它用基址加偏移量寻址的方法把左移了的testtest的值的值的值的值( (存放在存放在存放在存放在r0r0中中中中) )加到保存在加到保存在加到保存在加到保存在r1r1中的表的基址中。中的表的基址中。中的表的基址中。中的表的基址中。它将该指令计算出的新地址置为程序计数器它将该指令计算出的新地址置为程序计数器它将该指

144、令计算出的新地址置为程序计数器它将该指令计算出的新地址置为程序计数器(r15)(r15)的的的的值。值。值。值。每一个每一个每一个每一个casecase都由存放在存储器某处的一段代码实现。都由存放在存储器某处的一段代码实现。都由存放在存储器某处的一段代码实现。都由存放在存储器某处的一段代码实现。分支表从名为分支表从名为分支表从名为分支表从名为switchtabswitchtab的单元开始。的单元开始。的单元开始。的单元开始。wordword语句是在该处装入一个语句是在该处装入一个语句是在该处装入一个语句是在该处装入一个3232位地址到存储器的一位地址到存储器的一位地址到存储器的一位地址到存储器

145、的一种方法,因此分支表包含了对应于各个种方法,因此分支表包含了对应于各个种方法,因此分支表包含了对应于各个种方法,因此分支表包含了对应于各个casecase的代码段起的代码段起的代码段起的代码段起点的地址。点的地址。点的地址。点的地址。循环程序设计循环程序设计 循环是非常通用的循环是非常通用的循环是非常通用的循环是非常通用的C C语句。语句。语句。语句。循环能用条件分支自然地实现,因为循环总是循环能用条件分支自然地实现,因为循环总是循环能用条件分支自然地实现,因为循环总是循环能用条件分支自然地实现,因为循环总是对存在数组中的值进行操作,循环也是对基址加偏对存在数组中的值进行操作,循环也是对基址

146、加偏对存在数组中的值进行操作,循环也是对基址加偏对存在数组中的值进行操作,循环也是对基址加偏移量寻址模式的另一种用法较好的说明。移量寻址模式的另一种用法较好的说明。移量寻址模式的另一种用法较好的说明。移量寻址模式的另一种用法较好的说明。例例例例3-4 3-4 用用用用ARMARM指令实现指令实现指令实现指令实现FIRFIR过滤器过滤器过滤器过滤器FIR(finite impulser response)FIR(finite impulser response)过滤器是一种过滤器是一种过滤器是一种过滤器是一种处理信号的常用方法;处理信号的常用方法;处理信号的常用方法;处理信号的常用方法;FIRF

147、IR过滤器是简单的对积求和:过滤器是简单的对积求和:过滤器是简单的对积求和:过滤器是简单的对积求和: cixi cixi 1in 1in作为过滤器使用时,作为过滤器使用时,作为过滤器使用时,作为过滤器使用时,xixi假定为周期性采集的数假定为周期性采集的数假定为周期性采集的数假定为周期性采集的数据样品,据样品,据样品,据样品,cici是系数。是系数。是系数。是系数。这个计算总是按如下方式进行:这个计算总是按如下方式进行:这个计算总是按如下方式进行:这个计算总是按如下方式进行: 这种表示假定样品是周期性采集而来的,每次一个新的样这种表示假定样品是周期性采集而来的,每次一个新的样品到来都要重新计算

148、一次品到来都要重新计算一次FIR过滤器的输出。过滤器的输出。方框表示存储刚刚到来的样品产生方框表示存储刚刚到来的样品产生xi时延元素。时延元素。延迟的样品分别单独与延迟的样品分别单独与c相乘相乘,然后求和得到过滤器的输出然后求和得到过滤器的输出fc1c2c3c4x1x2x3x4FIR过滤器的过滤器的C语言代码如下:语言代码如下: for(i=0,f=0;in;i+) f=f+ci*xi; 我们可以根据基址加偏移量寻我们可以根据基址加偏移量寻址法对数组址法对数组c和和x进行编址。将每个进行编址。将每个数组的第零元素的地址数组的第零元素的地址,装入一个寄装入一个寄存器存器,存放存放i的寄存器则用作

149、偏移量。的寄存器则用作偏移量。下面就是该循环的代码:下面就是该循环的代码: MOV r0,#0 ;使用;使用r0作为计数器作为计数器i,置初值,置初值 ;为;为0 MOV r8,#0 ;使用;使用r8作为字节偏移量,置作为字节偏移量,置;初值为;初值为0 ADR r2,n ;读取;读取n的地址到的地址到r2 LDR rl,r2;读;读n的值到的值到r1 MOV r2,#0 ;使用;使用r2作为作为 f,置初值为,置初值为 0 ADR r3,c;读取;读取c的地址到的地址到r3 作为作为ci数数;组的首地址;组的首地址 ADR r5,x;读取;读取x的地址到的地址到r5,作为,作为xi;数组的首

150、地址;数组的首地址 l oopl oop: LDR r4 LDR r4,r3,r8 r3,r8 ;读取;读取;读取;读取 ci ci 的值到的值到的值到的值到r4 r4 LDR r6,r5,r8LDR r6,r5,r8;读取;读取;读取;读取xi xi 的值到的值到的值到的值到r6r6 MUL r4,r4,r6MUL r4,r4,r6;计算;计算;计算;计算 ci*si ci*si,;结果保存到;结果保存到;结果保存到;结果保存到r4r4 ADD r2,r2,r4ADD r2,r2,r4 ;求和送给;求和送给;求和送给;求和送给f f ;修改循环计数器和数组下标;修改循环计数器和数组下标;修改

151、循环计数器和数组下标;修改循环计数器和数组下标 ADD r8,r8,#4ADD r8,r8,#4;偏移量增加;偏移量增加;偏移量增加;偏移量增加3232位位位位 ADD r0,r0,#1ADD r0,r0,#1;i+i+ ;测试推出循环条件;测试推出循环条件;测试推出循环条件;测试推出循环条件 CMP r0,rlCMP r0,rl BLT LoopBLT Loop;if iNif iN,继续循环,继续循环,继续循环,继续循环 loop looploop endloop end不论该代码是用不论该代码是用C语言还是用汇编语言编语言还是用汇编语言编写,我们都要注意代码中的数值精确度。写,我们都要注

152、意代码中的数值精确度。 32位位X 32位的乘法得到位的乘法得到64位的结果。位的结果。ARM的的MUL指令把结果的低指令把结果的低32位保存到目的位保存到目的寄存器中。只要结果不超过寄存器中。只要结果不超过32位,就能得到位,就能得到所需结果。所需结果。如果输入值正是可能有时超过如果输入值正是可能有时超过32位,我位,我们就要重新设计代码来计算高分辨率的值。们就要重新设计代码来计算高分辨率的值。子程序设计子程序设计 C C语言的另一重要类是函数。语言的另一重要类是函数。语言的另一重要类是函数。语言的另一重要类是函数。每个每个每个每个C C函数返回一个值函数返回一个值函数返回一个值函数返回一个

153、值( (除非它的返回类型是除非它的返回类型是除非它的返回类型是除非它的返回类型是void)void);一般把不返回值的结构称为;一般把不返回值的结构称为;一般把不返回值的结构称为;一般把不返回值的结构称为子例程子例程子例程子例程或或或或过程过程过程过程。考虑下面这个考虑下面这个考虑下面这个考虑下面这个C C函数的简单用法:函数的简单用法:函数的简单用法:函数的简单用法: x=a+bx=a+b; foo(x)foo(x); y=c-dy=c-d;当函数被调用后马上返回到调用代码中,在上当函数被调用后马上返回到调用代码中,在上当函数被调用后马上返回到调用代码中,在上当函数被调用后马上返回到调用代码

154、中,在上例中就是返回到对例中就是返回到对例中就是返回到对例中就是返回到对y y赋值的语句。一个简单的分支是赋值的语句。一个简单的分支是赋值的语句。一个简单的分支是赋值的语句。一个简单的分支是不够的,因为我们不知要返回到哪儿。要想正确返不够的,因为我们不知要返回到哪儿。要想正确返不够的,因为我们不知要返回到哪儿。要想正确返不够的,因为我们不知要返回到哪儿。要想正确返回,就要在调用函数或过程时保存回,就要在调用函数或过程时保存回,就要在调用函数或过程时保存回,就要在调用函数或过程时保存PCPC的值,当调用的值,当调用的值,当调用的值,当调用过程结束时,将过程结束时,将过程结束时,将过程结束时,将P

155、CPC设置到下条指令的地址。设置到下条指令的地址。设置到下条指令的地址。设置到下条指令的地址。分支链接指令在分支链接指令在分支链接指令在分支链接指令在ARMARM中用于过程调用。中用于过程调用。中用于过程调用。中用于过程调用。这样,例如:这样,例如:这样,例如:这样,例如: BL fooBL foo将执行一个分支并链接到从定位点将执行一个分支并链接到从定位点将执行一个分支并链接到从定位点将执行一个分支并链接到从定位点foofoo开始的代码开始的代码开始的代码开始的代码( (用相对用相对用相对用相对PCPC寻址的方式寻址的方式寻址的方式寻址的方式) )。分支链接其实和分支很相似,。分支链接其实和

156、分支很相似,。分支链接其实和分支很相似,。分支链接其实和分支很相似,只不过是在分支前将当前只不过是在分支前将当前只不过是在分支前将当前只不过是在分支前将当前PCPC的值存在的值存在的值存在的值存在r14r14中。过程返回中。过程返回中。过程返回中。过程返回时,将时,将时,将时,将r14r14中的值移入中的值移入中的值移入中的值移入r15r15中即可:中即可:中即可:中即可:MOV r15MOV r15,r14r14当然在调用过程中不能覆盖保存在当然在调用过程中不能覆盖保存在当然在调用过程中不能覆盖保存在当然在调用过程中不能覆盖保存在r14r14中的中的中的中的PCPC值。值。值。值。但是这种机

157、制只能调用一层过程。但是这种机制只能调用一层过程。但是这种机制只能调用一层过程。但是这种机制只能调用一层过程。例如例如例如例如: :如果我们在另一个如果我们在另一个如果我们在另一个如果我们在另一个C C函数中调用一个函数中调用一个函数中调用一个函数中调用一个C C函数,第二函数,第二函数,第二函数,第二个函数调用将会覆盖个函数调用将会覆盖个函数调用将会覆盖个函数调用将会覆盖r14r14,破坏第一个调用函数的返回,破坏第一个调用函数的返回,破坏第一个调用函数的返回,破坏第一个调用函数的返回地址。地址。地址。地址。允许嵌套过程调用允许嵌套过程调用允许嵌套过程调用允许嵌套过程调用( (包括递归调用包

158、括递归调用包括递归调用包括递归调用) )的标准过程将建的标准过程将建的标准过程将建的标准过程将建立一个栈完成。立一个栈完成。立一个栈完成。立一个栈完成。 例例3-5 ARM中的过程调用。中的过程调用。C语言描述如下:语言描述如下:void f1(int a)f2(a);ARM的的C编译程序常规是用编译程序常规是用r13指指向栈顶。假定参数向栈顶。假定参数a已经传人栈中的已经传人栈中的f1(),并且假设我们必须在调用,并且假设我们必须在调用f2()前将前将f2的的参数参数(碰巧是同一个值碰巧是同一个值)入栈。入栈。下面是包含对下面是包含对下面是包含对下面是包含对f2()f2()调用的调用的调用的

159、调用的f1()f1()的手写代码:的手写代码:的手写代码:的手写代码: f1 f1:LDR r0LDR r0,r13r13;读取栈顶单元的内容到;读取栈顶单元的内容到;读取栈顶单元的内容到;读取栈顶单元的内容到r0r0 ;调用;调用;调用;调用f2()f2() STMFA rl3! STMFA rl3!,rl4 rl4 ;将;将;将;将f1f1返回地址存储到栈中返回地址存储到栈中返回地址存储到栈中返回地址存储到栈中 STMFA rl3! STMFA rl3!,r0 r0 ;将;将;将;将 f2 f2 的参数保存到栈顶的参数保存到栈顶的参数保存到栈顶的参数保存到栈顶 BL f2 BL f2;跳转

160、到;跳转到;跳转到;跳转到 f2 f2执行执行执行执行 ;返回到;返回到;返回到;返回到f1()f1() SUB r13 SUB r13,#4 #4 ; f2 f2的参数出栈的参数出栈的参数出栈的参数出栈 LDR r13! LDR r13!,r15 r15 ;返回到;返回到;返回到;返回到R15R15我们用基址加偏移量寻址法将传入我们用基址加偏移量寻址法将传入f1()的参数值装入的参数值装入r0中。调用中。调用f2()时,先将时,先将f1()的返回地址入栈,该地址在执行进入的返回地址入栈,该地址在执行进入f1()的的分支链接指令时保存在分支链接指令时保存在r14中,然后将中,然后将f2()的参

161、数入栈。的参数入栈。这两种情况下,我们都是使用自动增这两种情况下,我们都是使用自动增长的地址来入栈和调节栈指针的。长的地址来入栈和调节栈指针的。要返回,我们首先要调整栈,将掩盖要返回,我们首先要调整栈,将掩盖了了f1()返回地址的返回地址的f2()的参数去掉;然后用的参数去掉;然后用自动增长寻址弹出自动增长寻址弹出f1()的返回地址,放入的返回地址,放入PC(r15)中。中。 3.8 混合语言编程混合语言编程在应用系统的程序设计中,若所有在应用系统的程序设计中,若所有的编程任务均用汇编语言来完成,其工的编程任务均用汇编语言来完成,其工作量是可想而知的,同时,不利于系统作量是可想而知的,同时,不

162、利于系统升级或应用软件移植。升级或应用软件移植。事实上,事实上,ARM体系结构支持体系结构支持C/C+以及与汇编语言的混合编程,在一个完以及与汇编语言的混合编程,在一个完整的程序设计中,除了初始化部分用汇整的程序设计中,除了初始化部分用汇编语言完成以外,其主要的编程任务一编语言完成以外,其主要的编程任务一般都用般都用C/C+完成。完成。汇编语言与汇编语言与C/C+的混合编程通常的混合编程通常有以下几种方式:有以下几种方式:1.在在C/C+代码中嵌入汇编指令;代码中嵌入汇编指令;2.在汇编程序和在汇编程序和C/C+的程序之间进的程序之间进行变量的互访;行变量的互访;3.汇编程序、汇编程序、C/C

163、+程序间的相互调程序间的相互调用。用。在实际的编程应用中,使用较多的方在实际的编程应用中,使用较多的方式是:式是:程序的程序的初始化部分初始化部分用用汇编语言汇编语言完成,完成,然后用然后用C/C+完成主要的完成主要的编程任务编程任务;程序在执行时首先完成初始化过程,程序在执行时首先完成初始化过程,然后跳转到然后跳转到C/C+程序代码中,汇编程序程序代码中,汇编程序和和C/C+程序之间一般没有参数的传递,程序之间一般没有参数的传递,也没有频繁的相互调用,因此,整个程序也没有频繁的相互调用,因此,整个程序的结构显得相对简单,容易理解,以下进的结构显得相对简单,容易理解,以下进行详细介绍。行详细介

164、绍。如何在如何在C语言内嵌汇编语言语言内嵌汇编语言在在在在C C程序中嵌入汇编程序,可以实现一些高级程序中嵌入汇编程序,可以实现一些高级程序中嵌入汇编程序,可以实现一些高级程序中嵌入汇编程序,可以实现一些高级语言所没有的功能,提高程序执行效率。语言所没有的功能,提高程序执行效率。语言所没有的功能,提高程序执行效率。语言所没有的功能,提高程序执行效率。armccarmcc编译器的内嵌汇编器支持编译器的内嵌汇编器支持编译器的内嵌汇编器支持编译器的内嵌汇编器支持ARMARM指令集,指令集,指令集,指令集,tcctcc编译器的内嵌汇编器支持编译器的内嵌汇编器支持编译器的内嵌汇编器支持编译器的内嵌汇编器

165、支持ThumbThumb指令集。指令集。指令集。指令集。1. 1. 内嵌汇编的语法内嵌汇编的语法内嵌汇编的语法内嵌汇编的语法_asm _asm 指令指令指令指令; ;指令指令指令指令 * *注释注释注释注释* * 指令指令指令指令 嵌入汇编程序如例嵌入汇编程序如例嵌入汇编程序如例嵌入汇编程序如例3-63-6所示,给出了所示,给出了所示,给出了所示,给出了IRQIRQ中断使中断使中断使中断使能能能能/ /关闭函数关闭函数关闭函数关闭函数enable_IRQenable_IRQ和和和和 disable_IRQ disable_IRQ。 例例3-6 使能禁能使能禁能IRQ中断中断 _inline v

166、oid enable_IRQ(void) int tmp; _asm /嵌入汇编代码嵌入汇编代码 MRS tmp,CPSR /读取读取CPSR的值的值 BIC tmp,tmp,#0x80 MSR CPSR_c,tmp _inline void disable_IRQ(vold) int tmp;_asm MRS tmp,CPSR ORR tmp,tmp,#0x80 MSR CPSR_c,tmp 2. 内嵌汇编的指令用法内嵌汇编的指令用法 (1) 操作数操作数内嵌的汇编指令中作为操作数的寄存器和内嵌的汇编指令中作为操作数的寄存器和常量可以是常量可以是C表达式。表达式。这些表达式可以是这些表达式可

167、以是char、short或或int类型,类型,而且这些表达式都是作为无符号数进行操作。而且这些表达式都是作为无符号数进行操作。若需要有符号数,用户需要自己处理与符若需要有符号数,用户需要自己处理与符号有关的操作。号有关的操作。编译器将会计算这些表达式的值,并为其编译器将会计算这些表达式的值,并为其分配寄存器。分配寄存器。 (2) (2) 物理寄存器物理寄存器物理寄存器物理寄存器内嵌汇编中使用物理寄存器有以下内嵌汇编中使用物理寄存器有以下内嵌汇编中使用物理寄存器有以下内嵌汇编中使用物理寄存器有以下限制限制限制限制: 1.1.不能直接向不能直接向不能直接向不能直接向PCPC寄存器赋值,程序跳转只能

168、使用寄存器赋值,程序跳转只能使用寄存器赋值,程序跳转只能使用寄存器赋值,程序跳转只能使用B B或或或或BLBL指令实现。指令实现。指令实现。指令实现。2.2.使用物理寄存器的指令中,不要使用过于复杂的使用物理寄存器的指令中,不要使用过于复杂的使用物理寄存器的指令中,不要使用过于复杂的使用物理寄存器的指令中,不要使用过于复杂的C C表达式。因为表达式过于复杂时,将会需要较多的表达式。因为表达式过于复杂时,将会需要较多的表达式。因为表达式过于复杂时,将会需要较多的表达式。因为表达式过于复杂时,将会需要较多的物理寄存器。这些寄存器可能与指令中的物理寄存物理寄存器。这些寄存器可能与指令中的物理寄存物理

169、寄存器。这些寄存器可能与指令中的物理寄存物理寄存器。这些寄存器可能与指令中的物理寄存器在使用时发生冲突。器在使用时发生冲突。器在使用时发生冲突。器在使用时发生冲突。3.3.编译器可能会使用编译器可能会使用编译器可能会使用编译器可能会使用R12R12或或或或R13R13存放编译的中间结果。存放编译的中间结果。存放编译的中间结果。存放编译的中间结果。在计算表达式的值时可能会将寄存器在计算表达式的值时可能会将寄存器在计算表达式的值时可能会将寄存器在计算表达式的值时可能会将寄存器R0R3R0R3、R12R12和和和和R14R14用于子程序调用。因此,在内嵌的汇编指令用于子程序调用。因此,在内嵌的汇编指

170、令用于子程序调用。因此,在内嵌的汇编指令用于子程序调用。因此,在内嵌的汇编指令中,不要将这些寄存器同时指定为指令中的物理存中,不要将这些寄存器同时指定为指令中的物理存中,不要将这些寄存器同时指定为指令中的物理存中,不要将这些寄存器同时指定为指令中的物理存储器。储器。储器。储器。4.4.通常内嵌的汇编指令中不要指定物理寄存器,因为通常内嵌的汇编指令中不要指定物理寄存器,因为通常内嵌的汇编指令中不要指定物理寄存器,因为通常内嵌的汇编指令中不要指定物理寄存器,因为这可能会影响编译器分配寄存器,进而影响代码的这可能会影响编译器分配寄存器,进而影响代码的这可能会影响编译器分配寄存器,进而影响代码的这可能

171、会影响编译器分配寄存器,进而影响代码的效率。效率。效率。效率。(3) 常量。在内嵌汇编指令中,常量前面的常量。在内嵌汇编指令中,常量前面的“#”可以省略。可以省略。(4) 指令展开。内嵌的汇编指令中,如果包含指令展开。内嵌的汇编指令中,如果包含常量操作数,则该指令有可能被内嵌汇编器常量操作数,则该指令有可能被内嵌汇编器展开成几条指令。展开成几条指令。(5) 标号。标号。C程序中的标号可以被内嵌的汇编程序中的标号可以被内嵌的汇编指令使用。但是只有指令指令使用。但是只有指令B可以使用可以使用C程序中程序中的标号,而指令的标号,而指令BL则不能使用。则不能使用。 (6) 内存单元的分配。所有的内存分

172、配均由内存单元的分配。所有的内存分配均由C编译器完成,分配的内存单元通过变量供内编译器完成,分配的内存单元通过变量供内嵌汇编器使用。内嵌汇编器不支持内嵌汇编嵌汇编器使用。内嵌汇编器不支持内嵌汇编程序中用于内存分配的伪指令。程序中用于内存分配的伪指令。 (7) SWI和和BL指令。指令。在内嵌的在内嵌的SWI和和BL指令中,除了正常的操作指令中,除了正常的操作数域外,还必须增加以下数域外,还必须增加以下3个可选的寄存器列个可选的寄存器列表:表: 1.1.第第1个寄存器列表中的寄存器用于输入个寄存器列表中的寄存器用于输入的参数。的参数。2.2.第第2个寄存器列表中的寄存器用于存储返个寄存器列表中的

173、寄存器用于存储返回的结果。回的结果。3.3.第第3个寄存器列表中的寄存器的内容可能个寄存器列表中的寄存器的内容可能被被调用的子程序破坏,即这些寄存器是供被被调用的子程序破坏,即这些寄存器是供被调用的子程序作为工作寄存器。被调用的子程序作为工作寄存器。 3. 3. 内嵌汇编器内嵌汇编器内嵌汇编器内嵌汇编器与与与与armasmarmasm汇编器汇编器汇编器汇编器的差异的差异的差异的差异内嵌汇编器不支持通过内嵌汇编器不支持通过内嵌汇编器不支持通过内嵌汇编器不支持通过“.”“.”指示符或指示符或指示符或指示符或PCPC获取当前获取当前获取当前获取当前指令地址;指令地址;指令地址;指令地址;不支持不支持

174、不支持不支持“LDR Rn“LDR Rn,expr”expr”伪指令,而使用伪指令,而使用伪指令,而使用伪指令,而使用“MOV “MOV RnRn,expr”expr”指令向寄存器赋值;指令向寄存器赋值;指令向寄存器赋值;指令向寄存器赋值;不支持标号表达式;不支持不支持标号表达式;不支持不支持标号表达式;不支持不支持标号表达式;不支持ADRADR和和和和ADRLADRL伪指令;伪指令;伪指令;伪指令;不支持不支持不支持不支持BXBX指令;指令;指令;指令;不能向不能向不能向不能向PCPC赋值。赋值。赋值。赋值。使用使用使用使用0x0x前缀代替前缀代替前缀代替前缀代替“&”“&”,表示十六进制数

175、。,表示十六进制数。,表示十六进制数。,表示十六进制数。当使用当使用当使用当使用8 8位移位常数导致位移位常数导致位移位常数导致位移位常数导致CPSRCPSR的的的的ALUALU标志更新时,标志更新时,标志更新时,标志更新时,N N、Z Z、C C和和和和V V标志中的标志中的标志中的标志中的C C不具有真实意义。不具有真实意义。不具有真实意义。不具有真实意义。 4. 内嵌汇编注意事项内嵌汇编注意事项(1)(1)必须小心使用物理寄存器必须小心使用物理寄存器必须小心使用物理寄存器必须小心使用物理寄存器. .如如如如R0R3R0R3、PCPC、LRLR和和和和CPSRCPSR中的中的中的中的N N

176、、Z Z、C C和和和和V V标标标标志位志位志位志位, ,因为计算汇编代码中的因为计算汇编代码中的因为计算汇编代码中的因为计算汇编代码中的C C表达式时表达式时表达式时表达式时, ,可能会使用这可能会使用这可能会使用这可能会使用这些物理寄存器些物理寄存器些物理寄存器些物理寄存器, ,并会修改并会修改并会修改并会修改N N、Z Z、C C和和和和V V 标志位。标志位。标志位。标志位。例如:例如:例如:例如: _asm _asm MOV var MOV var,x x ADD yADD y,varvar,x/yx/y 计算计算计算计算x/yx/y时时时时R0R0会被修改。内嵌汇编器探测到隐含会

177、被修改。内嵌汇编器探测到隐含会被修改。内嵌汇编器探测到隐含会被修改。内嵌汇编器探测到隐含的寄存器冲突就会报错。的寄存器冲突就会报错。的寄存器冲突就会报错。的寄存器冲突就会报错。(2)不要使用寄存器代替变量。)不要使用寄存器代替变量。尽管有时寄尽管有时寄存器明显对应某个变量,但也不能直接使用寄存器明显对应某个变量,但也不能直接使用寄存器代替变量。存器代替变量。例如:例如: int bad_f(int x) / x存放在存放在R0中中 _asm ADD R0,R0,#1 /发生寄存器冲发生寄存器冲突,实际上突,实际上x的值没有变化的值没有变化 return(x); 尽管根据编译器的编译规则似乎可以

178、确尽管根据编译器的编译规则似乎可以确定定R0对应对应x,但这样的代码会使内嵌汇编器,但这样的代码会使内嵌汇编器认为发生了寄存器冲突。认为发生了寄存器冲突。用其它寄存器代替用其它寄存器代替R0存放参数存放参数x,使得,使得该函数将该函数将x原封不动地返回。原封不动地返回。这段代码的正确写法如下:这段代码的正确写法如下: int bad_f(int x) _asm ADD x,x,#1 return(x); (3) (3) 使用内嵌式汇编无需保存和恢复寄存器。使用内嵌式汇编无需保存和恢复寄存器。使用内嵌式汇编无需保存和恢复寄存器。使用内嵌式汇编无需保存和恢复寄存器。事实上,除了事实上,除了事实上,

179、除了事实上,除了CPSRCPSR和和和和SPSRSPSR寄存器,对物理寄寄存器,对物理寄寄存器,对物理寄寄存器,对物理寄存器先读后写都会引起汇编器报错。存器先读后写都会引起汇编器报错。存器先读后写都会引起汇编器报错。存器先读后写都会引起汇编器报错。例如:例如:例如:例如: int f(int x)int f(int x) _asrn _asrn STMFD SP! STMFD SP!,R0 /R0 /保存保存保存保存R0R0。先读后写,汇编出错先读后写,汇编出错先读后写,汇编出错先读后写,汇编出错 ADD R0ADD R0,x x,l l EOR xEOR x,R0R0,x x LDMFD S

180、P!LDMFD SP!,R0R0 return(x)return(x); (4)LDM和和STM指令的寄存器列表中指令的寄存器列表中只允许使用物理寄存器。只允许使用物理寄存器。内嵌汇编可以修改处理器模式、协内嵌汇编可以修改处理器模式、协处理器模式以及处理器模式以及FP、SL、SB等等APCS寄存器。但是编译器在编译时并不了寄存器。但是编译器在编译时并不了解这些变化,因此必须保证在执行解这些变化,因此必须保证在执行C代代码前恢复相应被修改的处理器模式。码前恢复相应被修改的处理器模式。(5) 汇编语言中的汇编语言中的“,”号作为操作数分隔符。号作为操作数分隔符。如果有如果有C表达式作为操作数,若表达式中包含表达式作为操作数,若表达式中包含有有“,”,则必须使用符号,则必须使用符号“(”和和“)”将其归约将其归约为一个汇编操作数。为一个汇编操作数。例如:例如:_asm ADD x,y,(f(),z) /“f(),z”为一个带为一个带有有“,”的的C表达式表达式

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 高等教育 > 研究生课件

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号