曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 11234|回复: 7
打印 上一主题 下一主题

Linux下ARM汇编

[复制链接]

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
跳转到指定楼层
楼主
发表于 2009-11-19 22:29:13 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
以下文章皆为转载
第一部分 Linux下ARM汇编语法
尽管在Linux下使用C或C++编写程序很方便,但汇编源程序用于系统最基本的初始化,如初始化堆栈指针、设置页表、操作ARM的协处理器等。初始化完成后就可以跳转到C代码执行。需要注意的是,GNU的汇编器遵循AT&T的汇编语法,可以从GNU的站点(www.gnu.org)上下载有关规范。
一. Linux汇编行结构
任何汇编行都是如下结构:
[:] [} @ comment
[:] [} @ 注释
Linux ARM 汇编中,任何以冒号结尾的标识符都被认为是一个标号,而不一定非要在一行的开始。
【例1】定义一个"add"的函数,返回两个参数的和。
.section .text, “x”
.global add @ give the symbol add external linkage
add:
ADD r0, r0, r1  @ add input arguments
MOV pc, lr @ return from subroutine
@ end of program
二. Linux 汇编程序中的标号
标号只能由a~z,A~Z,0~9,“.”,_等字符组成。当标号为0~9的数字时为局部标号,局部标号可以重复出现,使用方法如下:
 标号f: 在引用的地方向前的标号
 标号b: 在引用的地方向后的标号
【例2】使用局部符号的例子,一段循环程序
1:
    subs r0,r0,#1        @每次循环使r0=r0-1
    bne 1f         @跳转到1标号去执行
局部标号代表它所在的地址,因此也可以当作变量或者函数来使用。
三. Linux汇编程序中的分段
(1).section伪操作
用户可以通过.section伪操作来自定义一个段,格式如下:
 .section section_name [, "flags"[, %type[,flag_specific_arguments]]]
每一个段以段名为开始, 以下一个段名或者文件结尾为结束。这些段都有缺省的标志(flags),连接器可以识别这些标志。(与armasm中的AREA相同)。
下面是ELF格式允许的段标志
<标志> 含义
a 允许段
w 可写段
x 执行段
【例3】定义段
 .section .mysection @自定义数据段,段名为 “.mysection”
 .align  2
 strtemp:
 .ascii  "Temp string \n\0"

(2)汇编系统预定义的段名
.text  @代码段
.data  @初始化数据段
.bss  @未初始化数据段
.sdata @
.sbss  @
需要注意的是,源程序中.bss段应该在.text之前。
四. 定义入口点
汇编程序的缺省入口是 start标号,用户也可以在连接脚本文件中用ENTRY标志指明其它入口点。
【例4】定义入口点
.section.data
< initialized data here>
.section .bss
< uninitialized data here>
.section .text
.globl _start
_start:
<instruction code goes here>
五. Linux汇编程序中的宏定义
格式如下:
 .macro 宏名 参数名列表   @伪指令.macro定义一个宏
   宏体
 .endm  @.endm表示宏结束
如果宏使用参数,那么在宏体中使用该参数时添加前缀“\”。宏定义时的参数还可以使用默认值。
可以使用.exitm伪指令来退出宏。
【例5】宏定义
.macro SHIFTLEFT a, b
.if \b < 0
MOV \a, \a, ASR #-\b
.exitm
.endif
MOV \a, \a, LSL #\b
.endm
六. Linux汇编程序中的常数
(1)十进制数以非0数字开头,如:123和9876;
(2)二进制数以0b开头,其中字母也可以为大写;
(3)八进制数以0开始,如:0456,0123;
(4)十六进制数以0x开头,如:0xabcd,0X123f;
(5)字符串常量需要用引号括起来,中间也可以使用转义字符,如: “You are welcome!\n”;
(6)当前地址以“.”表示,在汇编程序中可以使用这个符号代表当前指令的地址;
(7)表达式:在汇编程序中的表达式可以使用常数或者数值, “-”表示取负数,“~”表示取补,“<>”表示不相等,其他的符号如:+、-、*、/、%、<、<<、>、>>、|、&、^、!、==、>=、<=、&&、||跟C语言中的用法相似。
七. Linux下ARM汇编的常用伪操作
在前面已经提到过了一些为操作,还有下面一些为操作:
 数据定义伪操作: .byte,.short,.long,.quad,.float,.string/.asciz/.ascii,重复定义伪操作.rept,赋值语句.equ/.set ;
 函数的定义 ;
 对齐方式伪操作 .align;
 源文件结束伪操作.end;
 .include伪操作;
 if伪操作;
 .global/ .globl 伪操作 ;
 .type伪操作 ;
 列表控制语句 ;
 区别于gas汇编的通用伪操作,下面是ARM特有的伪操作 :.reg ,.unreq ,.code ,.thumb ,.thumb_func ,.thumb_set, .ltorg ,.pool
1. 数据定义伪操作
(1) .byte:单字节定义,如:.byte 1,2,0b01,0x34,072,'s' ;
(2) .short:定义双字节数据,如:.short 0x1234,60000 ;
(3) .long:定义4字节数据,如:.long 0x12345678,23876565
(4) .quad:定义8字节,如:.quad 0x1234567890abcd
(5) .float:定义浮点数,如:
  .float 0f-314159265358979323846264338327\
    95028841971.693993751E-40                 @ - pi
(6) .string/.asciz/.ascii:定义多个字符串,如:
   .string "abcd", "efgh", "hello!"
   .asciz "qwer", "sun", "world!"
   .ascii "welcome\0"
需要注意的是:.ascii伪操作定义的字符串需要自行添加结尾字符'\0'。
(7) .rept:重复定义伪操作, 格式如下:
               .rept 重复次数
               数据定义
               .endr  @结束重复定义
     例如:
                .rept 3
                .byte 0x23
                .endr
(8) .equ/.set: 赋值语句, 格式如下:
                .equ(.set) 变量名,表达式
     例如:
                .equ abc 3  @让abc=3
2.函数的定义伪操作
(1)函数的定义,格式如下:
         函数名:
         函数体
         返回语句
一般的,函数如果需要在其他文件中调用, 需要用到.global伪操作将函数声明为全局函数。为了不至于在其他程序在调用某个C函数时发生混乱,对寄存器的使用我们需要遵循APCS准则。函数编译器将处理为函数代码为一段.global的汇编码。
(2)函数的编写应当遵循如下规则:
 a1-a4寄存器(参数、结果或暂存寄存器,r0到r3 的同义字)以及浮点寄存器f0-f3(如果存在浮点协处理器)在函数中是不必保存的;
 如果函数返回一个不大于一个字大小的值,则在函数结束时应该把这个值送到 r0 中;
 如果函数返回一个浮点数,则在函数结束时把它放入浮点寄存器f0中;
 如果函数的过程改动了sp(堆栈指针,r13)、fp(框架指针,r11)、sl(堆栈限制,r10)、lr(连接寄存器,r14)、v1-v8(变量寄存器,r4 到 r11)和 f4-f7,那么函数结束时这些寄存器应当被恢复为包含在进入函数时它所持有的值。
3. .align .end .include .incbin伪操作
(1).align:用来指定数据的对齐方式,格式如下:
                .align [absexpr1, absexpr2]
     以某种对齐方式,在未使用的存储区域填充值. 第一个值表示对齐方式,4, 8,16或     32. 第二个表达式值表示填充的值。
(2).end:表明源文件的结束。
(3).include:可以将指定的文件在使用.include 的地方展开,一般是头文件,例如:
                .include “myarmasm.h”
(4).incbin伪操作可以将原封不动的一个二进制文件编译到当前文件中,使用方法如下:
            .incbin "file"[,skip[,count]]
     skip表明是从文件开始跳过skip个字节开始读取文件,count是读取的字数.
4. .if伪操作
根据一个表达式的值来决定是否要编译下面的代码, 用.endif伪操作来表示条件判断的结束, 中间可以使用.else来决定.if的条件不满足的情况下应该编译哪一部分代码。
.if有多个变种:
 .ifdef symbol           @判断symbol是否定义
 .ifc string1,string2   @字符串string1和string2是否相等,字符串可以用单引号括起来
 .ifeq expression       @判断expression的值是否为0
.ifeqs string1,string2  @判断string1和string2是否相等,字符 串必须用双引号括起来
.ifge expression         @判断expression的值是否大于等于0
.ifgt absolute expression  @判断expression的值是否大于0
.ifle expression         @判断expression的值是否小于等于0
.iflt absolute expression  @判断expression的值是否小于0
.ifnc string1,string2     @判断string1和string2是否不相等, 其用法跟.ifc恰好相反。
.ifndef symbol, .ifnotdef symbol  @判断是否没有定义symbol,  跟.ifdef恰好相反
.ifne expression          @如果expression的值不是0, 那么编译器将编译下面的代码
.ifnes string1,string2    @如果字符串string1和string2不相 等, 那么编译器将编译下面的代码.
5. .global  .type   .title   .list
(1).global/ .globl :用来定义一个全局的符号,格式如下:
       .global symbol  或者  .globl symbol
(2).type:用来指定一个符号的类型是函数类型或者是对象类型, 对象类型一般是数据, 格式如下:
            .type 符号, 类型描述
【例6】
.globl a
.data
.align 4
.type a, @object
.size a, 4
a:
.long 10
【例7】
.section .text
.type asmfunc, @function
.globl asmfunc
asmfunc:
mov pc, lr
(3)列表控制语句:
.title:用来指定汇编列表的标题,例如:
            .title “my program”
.list:用来输出列表文件.
6. ARM特有的伪操作
(1) .reg: 用来给寄存器赋予别名,格式如下:
                   别名 .req 寄存器名
(2) .unreq: 用来取消一个寄存器的别名,格式如下:
       .unreq 寄存器别名
  注意被取消的别名必须事先定义过,否则编译器就会报错,这个伪操作也可以用来取消系统预制的别名, 例如r0, 但如果没有必要的话不推荐那样做。
(3) .code伪操作用来选择ARM或者Thumb指令集,格式如下:
           .code 表达式
  如果表达式的值为16则表明下面的指令为Thumb指令,如果表达式的值为32则表明下面的指令为ARM指令.
(4) .thumb伪操作等同于.code 16, 表明使用Thumb指令, 类似的.arm等同于.code 32
(5) .force_thumb伪操作用来强制目标处理器选择thumb的指令集而不管处理器是否支持
(6) .thumb_func伪操作用来指明一个函数是thumb指令集的函数
(7) .thumb_set伪操作的作用类似于.set, 可以用来给一个标志起一个别名, 比.set功能增加的一点是可以把一个标志标记为thumb函数的入口, 这点功能等同于.thumb_func
(8) .ltorg用于声明一个数据缓冲池(literal pool)的开始,它可以分配很大的空间。
(9) .pool的作用等同.ltorg
(9).space <number_of_bytes> {,<fill_byte>}
分配number_of_bytes字节的数据空间,并填充其值为fill_byte,若未指定该值,缺省填充0。(与armasm中的SPACE功能相同)
(10).word <word1> {,<word2>} …
插入一个32-bit的数据队列。(与armasm中的DCD功能相同)
可以使用.word把标识符作为常量使用
 例如:
  Start:
  valueOfStart:
   .word Start
 这样程序的开头Start便被存入了内存变量valueOfStart中。
(11).hword <short1> {,<short2>} …
插入一个16-bit的数据队列。(与armasm中的DCW相同)
八. GNU ARM汇编特殊字符和语法
代码行中的注释符号: [email=%E2%80%98@%E2%80%99]‘@’[/email]
整行注释符号: ‘#’
语句分离符号: ‘;’
直接操作数前缀: ‘#’ 或 ‘$’
第二部分 GNU的编译器和调试工具
一. 编译工具
1.编辑工具介绍
GNU提供的编译工具包括汇编器as、C编译器gcc、C++编译器g++、连接器ld和二进制转换工具objcopy。基于ARM平台的工具分别为arm-linux-as、arm-linux-gcc、arm-linux-g++、arm-linux-ld和arm-linux-objcopy。GNU的编译器功能非常强大,共有上百个操作选项,这也是这类工具让初学者头痛的原因。不过,实际开发中只需要用到有限的几个,大部分可以采用缺省选项。GNU工具的开发流程如下:编写C、C++语言或汇编源程序,用gcc或g++生成目标文件,编写连接脚本文件,用连接器生成最终目标文件(elf格式),用二进制转换工具生成可下载的二进制代码。
(1)编写C、C++语言或汇编源程序
通常汇编源程序用于系统最基本的初始化,如初始化堆栈指针、设置页表、操作ARM的协处理器等。初始化完成后就可以跳转到C代码执行。需要注意的是,GNU的汇编器遵循AT&T的汇编语法,读者可以从GNU的站点(www.gnu.org)上下载有关规范。汇编程序的缺省入口是 start标号,用户也可以在连接脚本文件中用ENTRY标志指明其它入口点(见下文关于连接脚本的说明)。
(2)用gcc或g++生成目标文件
如果应用程序包括多个文件,就需要进行分别编译,最后用连接器连接起来。如笔者的引导程序包括3个文件:init.s(汇编代码、初始化硬件)xmrecever.c(通信模块,采用Xmode协议)和flash.c(Flash擦写模块)。
分别用如下命令生成目标文件: arm-linux-gcc-c-O2-oinit.oinit.sarm-linux-gcc-c-O2-oxmrecever.oxmrecever.carm-linux-gcc-c-O2-oflash.oflash.c其中-c命令表示只生成目标代码,不进行连接;-o命令指明目标文件的名称;-O2表示采用二级优化,采用优化后可使生成的代码更短,运行速度更快。如果项目包含很多文件,则需要编写makefile文件。关于makefile的内容,请感兴趣的读者参考相关资料。
(3)编写连接脚本文件
gcc等编译器内置有缺省的连接脚本。如果采用缺省脚本,则生成的目标代码需要操作系统才能加载运行。为了能在嵌入式系统上直接运行,需要编写自己的连接脚本文件。编写连接脚本,首先要对目标文件的格式有一定了解。GNU编译器生成的目标文件缺省为elf格式。elf文件由若干段(section)组成,如不特殊指明,由C源程序生成的目标代码中包含如下段:.text(正文段)包含程序的指令代码;.data(数据段)包含固定的数据,如常量、字符串;.bss(未初始化数据段)包含未初始化的变量、数组等。C++源程序生成的目标代码中还包括.fini(析构函数代码)和.init(构造函数代码)等。连接器的任务就是将多个目标文件的.text、.data和.bss等段连接在一起,而连接脚本文件是告诉连接器从什么地址开始放置这些段。例如连接文件link.lds为:
ENTRY(begin)
SECTION
{
.=0x30000000;
.text:{*(.text)}
.data:{*(.data)}
.bss:{*(.bss)}
}
其中,ENTRY(begin)指明程序的入口点为begin标号;.=0x00300000指明目标代码的起始地址为0x30000000,这一段地址为MX1的片内RAM;.text:{*(.text)}表示从0x30000000开始放置所有目标文件的代码段,随后的.data:{*(.data)}表示数据段从代码段的末尾开始,再后是.bss段。
(4)用连接器生成最终目标文件
有了连接脚本文件,如下命令可生成最终的目标文件:
arm-linux-ld –no stadlib –o bootstrap.elf -Tlink.lds init.o xmrecever.o flash.o
其中,ostadlib表示不连接系统的运行库,而是直接从begin入口;-o指明目标文件的名称;-T指明采用的连接脚本文件(也可以使用-Ttext address,address表示执行区地址);最后是需要连接的目标文件列表。
(5)生成二进制代码
连接生成的elf文件还不能直接下载执行,通过objcopy工具可生成最终的二进制文件:
arm-linux-objcopy –O binary bootstrap.elf bootstrap.bin
其中-O binary指定生成为二进制格式文件。Objcopy还可以生成S格式的文件,只需将参数换成-O srec。还可以使用-S选项,移除所有的符号信息及重定位信息。如果想将生成的目标代码反汇编,还可以用objdump工具:
 arm-linux-objdump -D bootstrap.elf
至此,所生成的目标文件就可以直接写入Flash中运行了。
2.Makefile实例
example: head.s  main.c
 arm-linux-gcc -c -o head.o head.s
 arm-linux-gcc -c -o main.o main.c
 arm-linux-ld -Tlink.lds head.o ain.o -o example.elf
 arm-linux-objcopy -O binary -S example_tmp.o example
 arm-linux-objdump -D -b binary -m arm  example >ttt.s
二. 调试工具
Linux下的GNU调试工具主要是gdb、gdbserver和kgdb。其中gdb和gdbserver可完成对目标板上Linux下应用程序的远程调试。gdbserver是一个很小的应用程序,运行于目标板上,可监控被调试进程的运行,并通过串口与上位机上的gdb通信。开发者可以通过上位机的gdb输入命令,控制目标板上进程的运行,查看内存和寄存器的内容。gdb5.1.1以后的版本加入了对ARM处理器的支持,在初始化时加入- target==arm参数可直接生成基于ARM平台的gdbserver。gdb工具可以从ftp://ftp.gnu.org/pub/gnu/gdb/上下载。
对于Linux内核的调试,可以采用kgdb工具,同样需要通过串口与上位机上的gdb通信,对目标板的Linux内核进行调试。可以从http://oss.sgi.com/projects/kgdb/上了解具体的使用方法。

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
沙发
 楼主| 发表于 2009-11-19 22:30:58 | 只看该作者

GNU ARM汇编语言

任何汇编行都是如下结构:
[<label>:] [<instruction or directive>} @ comment
[<标签>:] [<指令>} @ 注释

GNU ARM 汇编中,任何以冒号结尾的都被认为是一个标签,而不一定非要在一行的开始。下面是一个简单的例子,这段汇编程序定义了一个"add"的函数,该函数返回两个参数的和:

.section .text, “x”
.global add @ give the symbol add external linkage
add:
ADD r0, r0, r1 @ add input arguments
MOV pc, lr @ return from subroutine
@ end of program

GNU ARM汇编伪指令


下面列出了一些GNU ARM汇编伪指令,并给出了相应说明。


.ascii “<string>” 在汇编中定义字符串并为之分配存储空间(与armasm中的DCB功能类似)。
.asciz “<string>” 和.ascii类似, 但不分配存储空间。



.balign <power_of_2> {,<fill_value> {,<max_padding>} }
以某种排列方式在内存中填充数值。 (该指令与armasm中的ALIGN类似)。
power_of_2表示排列方式,其值可为4,8,16或32,单位是byte;
fill_value是要填充的值;
max_padding最大的填充界限,请求填充的bytes数超过该值,将被忽略。



.byte <byte1> {,<byte2>} … 定义一个或多个Byte,并为之分配空间(与armasm的DCB类似)。

.code <number_of_bits> 设定指令宽度,16表示Thumb,32表示ARM assembly
(和armasm中的CODE16,CODE32相同)。



.if
.else
.endif
预编译宏(与armasm中的IF ELSE ENDIF相同)。



.end 汇编文件结束标志,常常省略不用。


.endm 宏结束标志。
.exitm 宏跳出。
.macro <name> {<arg_1} {,<arg_2>} … {,<arg_N>}
定义一段名为name的宏,arg_xxx为参数。
必须有对应的.endm结尾。
可以使用.exitm从中间跳出宏。(与armasm中的MACRO, MEND, MEXIT相同)。
在使用宏参数时必须这样使用:“\\<arg>”。

例如:
[CODE].macro SHIFTLEFT a, b
.if \\b < 0
MOV \\a, \\a, ASR #-\\b
.exitm
.endif
MOV \\a, \\a, LSL #\\b
.endm


.rept <number_of_times> 循环执行.endr前的代码段number_of_times次。
(与armasm中的WEN相似)


.irp <param> {,<val_1>} {,<val_2>} …
循环执行.endr前的代码段,param依次取后面给出的值。
在循环执行的代码段中必须以“\\<param> ”表示参数。


.endr 结束循环(与armasm中的WEND相似).



.equ <symbol name>, <value> 为一个标号赋值,类似C中的#define。(与armasm中的EQU相同)



.err 编译错误报告,将引起编译的终止。



.global <symbol> 全局声明标志,这样声明的标号将可以被外部使用。(与armasm中的EXPORT相同)。



.hword <short1> {,<short2>} …
插入一个16-bit的数据队列。(与armasm中的DCW相同)



.ifdef <symbol> 如果 <symbol>被定义,该快代码将被编译。以 .endif结束。
.ifndef <symbol> 如果 <symbol>未被定义,该快代码将被编译。以 .endif结束。



.include “<filename>” 包含文件。(与armasm中的INCLUDE 或者C中的#i nclude一样)



<register_name> .req <register_name>
定义一个寄存器,.req的左边是定义的寄存器名,右边是使用的真正使用的寄存器。
(与armasm中的RN类似)
例如:acc .req r0



[CODE].section <section_name> {,”<flags>”}
开始一个新的代码或数据段。.text, 代码段;.data, 初始化数据段;.bss, 未初始化数据段。
这些段都有缺省的标志(flags),联接器可以识别这些标志。(与armasm中的AREA相同)。


下面是ELF格式允许的段标志
<标志> 含义
a 允许段
w 可写段
x 执行段



.set <variable_name>, <variable_value> 变量赋值。(与armasm中的SETA相同)



.space <number_of_bytes> {,<fill_byte>}
分配number_of_bytes字节的数据空间,并填充其值为fill_byte,若未指定该值,缺省填充0。
(与armasm中的SPACE功能相同)



.word <word1> {,<word2>} …
插入一个32-bit的数据队列。(与armasm中的DCD功能相同)


GNU ARM汇编特殊字符和语法



代码行中的注释符号: ‘@’
整行注释符号: ‘#’
语句分离符号: ‘;’
直接操作数前缀: ‘#’ 或 ‘$’

.arm 以arm格式编译,同code32
.thumb 以thumb格式编译,同code16
.code16 以thumb格式编译
.code32 以arm格式编译

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
板凳
 楼主| 发表于 2009-11-19 22:47:47 | 只看该作者

ARM GNU 汇编伪指令简介

1 ARM GNU 汇编伪指令简介
  (1)abort
       .abort 停止汇编
  (2)align
      .align absexpr1,absexpr2
     以某种对齐方式,在未使用的存储区域填充值. 第一个值表示对齐方式,4, 8,16或
     32. 第二个表达式值表示填充的值
  (3)if...else...endif
     .if
     .else
     .endif: 支持条件预编译
  (4)include
     .include "file": 包含指定的头文件, 可以把一个汇编常量定义放在头文件中
  (5)comm
     .comm  symbol, length:
     在bss段申请一段命名空间,该段空间的名称叫symbol, 长度为length. Ld连接器在连接
     会为它留出空间
  (6)data
     .data subsection: 说明接下来的定义归属于subsection数据段
  (7)equ
     .equ symbol, expression: 把某一个符号(symbol)定义成某一个值(expression).该
     指令并不分配空间
  (8)global
     .global symbol: 定义一个全局符号, 通常是为ld使用
  (9)ascii
     .ascii "string": 定义一个字符串并为之分配空间
  (10)byte
     .byte expressions: 定义一个字节, 并为之分配空间
  (11)short
     .short expressions: 定义一个短整型, 并为之分配空间
  (12)int
     .int expressions: 定义一个整型,并为之分配空间
  (13)long
     .long expressions: 定义一个长整型, 并为之分配空间
  (14)word
     .word expressions: 定义一个字,并为之分配空间, 4 bytes
  (15)macro/endm
     .macro: 定义一段宏代码, .macro表示代码的开始, .endm表示代码的结束, .exitm
     跳出宏, 示例如下:
     .macro SHIFTLEFT a, b
     .if \\b < 0
      mov \\a, \\a, ASR #-\\b
     .exitm
     .endif
      mov \\a, \\a, LSL #\\b
     .endm
  (16)req
      name .req register name: 为寄存器定义一个别名
  (17)code
     .code [16|32]: 指定指令代码产生的长度, 16表示Thumb指令, 32表示ARM指令
  (18)ltorg
     .ltorg: 表示当前往下的定义在归于当前段,并为之分配空间

2 ARM GNU专有符号
  (1)@ 表示注释从当前位置到行尾的字符.
  (2)# 注释掉一整行.
  (3); 新行分隔符.

3 操作码
  (1)NOP: nop 空操作, 相当于MOV r0, r0
  (2)LDR: ldr <register> , =<expression> 相当于PC寄存器或其它寄存器的长转移
  (3)ADR: adr <register> <label> 相于PC寄存器或其它寄存器的小范围转移
  (4)ADRL: adrl <register> <label> 相于PC寄存器或其寄存器的中范围转移

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
地板
 楼主| 发表于 2009-11-19 22:48:31 | 只看该作者

GNU汇编使用经验

1. mov pc #0x   指令可用来实现代码跳转,跳转时的立即数可以是32位的,但必须是一个8位的数移位偶数次的结果。
2. 在代码中使用.word定义变量时,一定要保证该地址不会被指令执行到,或者把变量的定义专门放到一个数据段里面。
3. 比较时使用cmp指令,后跟bne、bge、bgt、blt、ble跳转指令
4. 可以使用.word把标识符作为常量使用
例如:
  Start:
  valueOfStart:
   .word Start
这样程序的开头Start便被存入了内存变量valueOfStart中。
5. adr RN 0 ;表示adr以后可用来代替R0使用
6. 关于标签,标签存在于编译过程中,标签本身对应的值是绝对地址,例如:
.word label
定义的数值是label标签对应的绝对地址。
有些编译语句在使用时把它转变为绝对地址,例如:
ldr r0, label
相当于是ldr r0, [label-pc]
ldr和adr的区别:
adr 仅取标签的相对地址,取得的是label的相对地址,该代码可以在和标签相对位置不变的情况下移动;ldr r0, =_start取得的是_start的绝对地址,该代码可以在_start标签的绝对位置不变的情况下移动;而ldr r0, _start是根据_start对当前PC的相对位置读取其所在地址的值,故是一句位置无关代码,可以在和_start标签的相对位置不变的情况下移动。
    ldr     r0, _start
        adr     r0, _start
        ldr     r0, =_start
        nop
        mov     pc, lr
_start:
        nop
      
编译的时候设置 RO 为 0x0c008000
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
0c008000 <_start-0x14>:
c008000:       e59f000c        ldr     r0, [pc, #12]   ; c008014 <_start>
c008004:       e28f0008        add     r0, pc, #8      ; 0x8
c008008:       e59f0008        ldr     r0, [pc, #8]    ; c008018 <_start+0x4>
c00800c:       e1a00000        nop                     (mov r0,r0)
c008010:       e1a0f00e        mov     pc, lr
0c008014 <_start>:
c008014:       e1a00000        nop                     (mov r0,r0)
c008018:       0c008014        stceq   0, cr8, [r0], -#80
分析:
ldr     r0, _start
从内存地址 _start 的地方把值读入。执行这个后,r0 = 0xe1a00000
adr     r0, _start
取得 _start 的地址到 r0,但是请看反编译的结果,它是与位置无关的。其实取得的时相对的位置。例如这段代码在 0x0c008000 运行,那么 adr r0, _start 得到 r0 = 0x0c008014;如果在地址 0 运行,就是 0x00000014 了。
ldr     r0, =_start
这个取得标号 _start 的绝对地址。这个绝对地址是在 link 的时候确定的。看上去这只是一个指令,但是它要占用 2 个 32bit 的空间,一条是指令,另一条是 _start 的数据(因为在编译的时候不能确定 _start 的值,而且也不能用 mov 指令来给 r0 赋一个 32bit 的常量,所以需要多出一个空间存放 _start 的真正数据,在这里就是 0x0c008014)。
因此可以看出,这个是绝对的寻址,不管这段代码在什么地方运行,它的结果都是 r0 = 0x0c008014

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
5#
 楼主| 发表于 2009-11-19 22:49:12 | 只看该作者

GNU的编译器和开发工具

GNU提供的编译工具包括汇编器as、C编译器gcc、C++编译器g++、连接器ld和二进制转换工具objcopy。基于ARM平台的工具分别为 arm-linux-as、arm-linux-gcc、arm-linux-g++、arm -linux-ld 和arm-linux-objcopy。GNU的所有开发工具都可以从www.gnu.org上下载,基于ARM的工具可以从www.uclinux.org获得。GNU的编译器功能非常强大,共有上百个操作选项,这也是这类工具让初学者头痛的原因。不过,实际开发中只需要用到有限的几个,大部分可以采用缺省选项。GNU工具的开发流程如下:编写C、C++语言或汇编源程序,用gcc或g++生成目标文件,编写连接脚本文件,用连接器生成最终目标文件(elf格式),用二进制转换工具生成可下载的二进制代码。GNU工具都运行在Linux下,开发者需要1台运行Linux的PC作为上位机。由于篇幅所限,不能完整地介绍整个嵌入式操作系统的开发过程,将以第二节中提到的通过自举模式下载的引导程序为例,说明开发的过程。对于像Linux这样的大系统,基本的开发流程是一样的。
  引导程序将通过自举模式下载到MX1的片内RAM,从地址0x00300000开始并执行。完成串口和SDRAM的初始化后,引导程序将等待接收应用程序或操作系统内核,将接收到的数据放在SDRAM中。数据接收完毕后,引导程序将SDRAM中的数据写入Flash,下一次就可以从Flash中直接引导系统了。由于操作系统的内核比较大,如Linux有1 MB以上,下载过程必须考虑纠错。因此,接收部分采用Xmode协议,可以用Windows下超级终端的Xmode发送方式发送文件。

  (1)编写C、C++语言或汇编源程序

  通常汇编源程序用于系统最基本的初始化,如初始化堆栈指针、设置页表、操作ARM的协处理器等。初始化完成后就可以跳转到C代码执行。需要注意的是,GNU的汇编器遵循AT&T的汇编语法,读者可以从GNU的站点(www.gnu.org)上下载有关规范。汇编程序的缺省入口是start标号,用户也可以在连接脚本文件中用ENTRY标志指明其它入口点(见下文关于连接脚本的说明)。

  (2)用gcc或g++生成目标文件

  如果应用程序包括多个文件,就需要进行分别编译,最后用连接器连接起来。如笔者的引导程序包括3个文件:init.s(汇编代码、初始化硬件) xmrecever.c(通信模块,采用Xmode协议)和flash.c(Flash擦写模块)。

  分别用如下命令生成目标文件:

  arm-linux-gcc -c -O2 -o init.o init.s

  arm-linux-gcc -c -O2 -o xmrecever.o xmrecever.c

  arm-linux-gcc -c -O2 -o flash.o flash.c

  其中-c命令表示只生成目标代码,不进行连接;-o 命令指明目标文件的名称;-O2表示采用二级优化,采用优化后可使生成的代码更短,运行速度更快。如果项目包含很多文件,则需要编写makefile文件。关于makefile的内容,请感兴趣的读者参考相关资料。

  (3)编写连接脚本文件

  gcc等编译器内置有缺省的连接脚本。如果采用缺省脚本,则生成的目标代码需要操作系统才能加载运行。为了能在嵌入式系统上直接运行,需要编写自己的连接脚本文件。编写连接脚本,首先要对目标文件的格式有一定了解。GNU编译器生成的目标文件缺省为elf格式。elf文件由若干段(section)组成,如不特殊指明,由C源程序生成的目标代码中包含如下段:.text(正文段)包含程序的指令代码;.data(数据段)包含固定的数据,如常量、字符串;.bss(未初始化数据段)包含未初始化的变量、数组等。C++源程序生成的目标代码中还包括.fini(析构函数代码)和.init(构造函数代码)等。有关elf文件格式,读者可自行参考相关资料。连接器的任务就是将多个目标文件的.text、.data和.bss等段连接在一起,而连接脚本文件是告诉连接器从什么地址开始放置这些段。例如笔者的引导程序连接文件link.lds为:

ENTRY(begin)

SECTION

{ .=0x00300000;

.text : { *(.text) }

.data: { *(.data) }

.bss: { *(.bss) }

}

  其中,ENTRY(begin)指明程序的入口点为begin标号;.=0x00300000指明目标代码的起始地址为0x00300000,这一段地址为MX1的片内RAM;.text : { *(.text) }表示从0x00300000开始放置所有目标文件的代码段,随后的.data: { *(.data) }表示数据段从代码段的末尾开始,再后是.bss段。

  (4)用连接器生成最终目标文件

  有了连接脚本文件,如下命令可生成最终的目标文件:

  arm-linux-ld-nostadlib-o bootstrap.elf-T link.lds init.o xmrecever.o flash.o

  其中,ostadlib表示不连接系统的运行库,而是直接从begin入口;-o指明目标文件的名称;-T指明采用的连接脚本文件;最后是需要连接的目标文件列表。

  (5)生成二进制代码

  连接生成的elf文件还不能直接下载执行,通过objcopy工具可生成最终的二进制文件:

  arm-linux-objcopy-O binary bootstrap.elf bootstrap.bin

  其中-Obinary指定生成为二进制格式文件。Objcopy还可以生成S格式的文件,只需将参数换成-O srec。如果想将生成的目标代码反汇编,还可以用objdump工具:

  arm-linux-objdump-D bootstrap.elf

  至此,所生成的目标文件就可以直接写入Flash中运行了。如果要通过自举模式下载,还需要转换为自举模式的文件格式,相关转换工具可以在摩托罗拉的网站上找到。

  掌握了GNU工具后,开发者就可以开发或移植C或C++代码的程序。用户可以不需要操作系统,直接开发简单应用程序。但对于更复杂的应用来说,操作系统必不可少。目前流行的源代码公开的操作系统如Linux、μC/OS都可以用GNU工具编译。ARM的Linux已有很多成熟的版本,可以支持 ARM720、ARM920、ARM1020等多种处理器,读者可从www.uclinux.orgwww.armdevzone.com上获取最新信息。Linux移植过程中和处理器相关的代码都放在arch/arm目录下。对于内核,用户需要做的是设定自己系统的内存映像,RAM起始地址,I/O地址空间和虚拟I/O地址空间,参看arch/arm/mach-integrator/arch.c文件。除了内核外,用户还需要为自己的系统编制各种各样的驱动程序

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
6#
 楼主| 发表于 2009-11-19 22:50:03 | 只看该作者

用GNU工具开发基于ARM的嵌入式系统

摘要:介绍如何利用GNU的工具开发基于ARM的嵌入式系统,以及使用编译器、连接器和调试工具的具体方法,为广大嵌入式系统开发人员提供一种低成本的开发手段。    关键词:ARMGNUMC928MX1gccgdbgdbserver

   当前,ARM公司的32位RISC处理器,以其内核耗电少、成本低、功能强、特有16/32位双指令集,已成为移动通信、手持计算、多媒体数字消费等嵌入式解决方案的RISC标准,市场占有率超过了75%。多家公司都推出了自己的基于ARM内核的处理器产品,越来越多的开发人员开始了针对ARM平台的开发。通常开发人员需要购买芯片厂商或第三方提供的开发板,还需要购买开发软件,如C编译器或者集成了实时操作系统的开发环境。开发板的价格从数百到上千美元,而编译器、实时操作系统价格更是动辄数千到数万美元。这样,在开发初期,软硬件上的投资就需要上万美元,对于国内大多数开发人员来说,无疑是太贵了。

  庆幸的是,GNU所倡导的自由软件给开发者带来了福音。1984年,旨在开发一个类似Unix的,并且是完全免费的完整操作系统和配套工具: GNU系统(发音为"guh-NEW")。GNU的操作系统和开发工具都是免费的,遵循GNU通用公共许可证(GPL)协议,任何人都可以从网上获取全部的源代码。关于GNU和公共许可证协议的详细资料,读者可参看GNU网站的中文介绍:http://www.gnu.org/home.cn.html

  除了大家熟知的Linux操作系统外,GNU的软件还包括编译器(gcc,g++)、二进制转换工具(objdump,objcopy)、调试工具(gdb,gdbserver,kgdb)和基于不同硬件平台的开发库。GNU开发工具的主要缺点是采用命令行方式,用户掌握和使用比较困难,不如基于Windows系统的开发工具好用。但是,GNU工具的复杂性是由于它更贴近编译器和操作系统的底层,并提供了更大的灵活性。一旦学习和掌握了相关工具,也就了解了系统设计的基础知识,为今后的开发工作打下基础。GNU的开发工具都是免费的,遵循GPL协议,任何人都可以从网上获取。笔者参与了一个基于ARM平台的嵌入式Linux系统开发,采用的是摩托罗拉龙珠系列的MC928MX1。从测试代码、引导程序、嵌入式Linux移植、应用程序、图形界面都可以用GNU工具进行开发,不需要在开发工具上做额外的投入。本文所介绍的开发方法同样适用于其它公司的基于ARM的产品。

1硬件平台

  MC928MX1(以下简称MX1)是摩托罗拉公司基于ARM核心的第一款MCU,主要面向高端嵌入式应用。内部采用ARM920T内核,并集成了SDRAM/Flash、LCD、USB、蓝牙(bluetooth)、多媒体闪存卡(MMC)、CMOS摄像头等控制器。关于MX1的详细资料,感兴趣的读者可以参考http://www.motorola.com.cn/semiconductors/。作为应用开发的最小系统必须包括RAM(程序运行空间)、Flash(存放目标代码)和串行接口(用于调试和下载程序)。MX1提供了6个片选端(CS0~CS5),内置了SDRAM控制器,数据宽度32位。在笔者的系统中采用了2片8M×16位的SDRAM和2片4M×16位的同步Flash存储器,分别接入数据线的低16位和高16位,如图1 所示。

  图1中SDRAM接片选端CS2,Flash接片选端CS3,其余为SDRAM/Flash的控制信号。最小系统还包括至少1个串行接口,可以采用MX1内置的UART控制器,图略。

2自举模式

  目前,许多嵌入式处理器都提供了自举模式(Bootstrap),供用户写入引导代码。自举模式利用了固化在芯片内部的一段引导程序,当处理器复位时,如果在特定引脚上加信号,则处理器将在复位后执行固化ROM中的程序。例如,MX1提供了4条复位引脚,复位时引脚不同的电平组合可以从不同的片选端启动系统。自举ROM中的程序完成串口的初始化,然后等待用户从串口写入用户代码。自举模式所能接受的是一种专门格式的文本文件,包括数据和要写入/ 读出的地址。关于自举模式的代码格式,可参考相关芯片的手册。在摩托罗拉的网站还提供了许多小工具,帮助开发者将其它格式的文件转换成为自举模式格式。通过自举模式下载的通常是一段和上位机软件(如超级终端)通信的程序,完成接收数据并写入Flash的操作。写入的数据可以是用户自己的应用程序、数据或者操作系统的内核。通过自举模式下载的引导程序同样可以用GNU工具开发。

3GNU的编译器和开发工具

  GNU提供的编译工具包括汇编器as、C编译器gcc、C++编译器g++、连接器ld和二进制转换工具objcopy。基于ARM平台的工具分别为arm-linux-as、arm-linux-gcc、arm-linux-g++、arm-linux-ld和arm-linux- objcopy。GNU的所有开发工具都可以从www.gnu.org上下载,基于ARM的工具可以从www.uclinux.org获得。GNU的编译器功能非常强大,共有上百个操作选项,这也是这类工具让初学者头痛的原因。不过,实际开发中只需要用到有限的几个,大部分可以采用缺省选项。GNU工具的开发流程如下:编写C、C++语言或汇编源程序,用gcc或g++生成目标文件,编写连接脚本文件,用连接器生成最终目标文件(elf格式),用二进制转换工具生成可下载的二进制代码。GNU工具都运行在Linux下,开发者需要1台运行Linux的PC作为上位机。由于篇幅所限,不能完整地介绍整个嵌入式操作系统的开发过程,将以第二节中提到的通过自举模式下载的引导程序为例,说明开发的过程。对于像Linux这样的大系统,基本的开发流程是一样的。

  引导程序将通过自举模式下载到MX1的片内RAM,从地址0x00300000开始并执行。完成串口和SDRAM的初始化后,引导程序将等待接收应用程序或操作系统内核,将接收到的数据放在SDRAM中。数据接收完毕后,引导程序将SDRAM中的数据写入Flash,下一次就可以从Flash中直接引导系统了。由于操作系统的内核比较大,如Linux有1MB以上,下载过程必须考虑纠错。因此,接收部分采用Xmode协议,可以用Windows 下超级终端的Xmode发送方式发送文件。

   (1)编写C、C++语言或汇编源程序

  通常汇编源程序用于系统最基本的初始化,如初始化堆栈指针、设置页表、操作ARM的协处理器等。初始化完成后就可以跳转到C代码执行。需要注意的是,GNU的汇编器遵循AT&T的汇编语法,读者可以从GNU的站点(www.gnu.org)上下载有关规范。汇编程序的缺省入口是 start标号,用户也可以在连接脚本文件中用ENTRY标志指明其它入口点(见下文关于连接脚本的说明)。

   (2)用gcc或g++生成目标文件

  如果应用程序包括多个文件,就需要进行分别编译,最后用连接器连接起来。如笔者的引导程序包括3个文件:init.s(汇编代码、初始化硬件)xmrecever.c(通信模块,采用Xmode协议)和flash.c(Flash擦写模块)。
分别用如下命令生成目标文件: arm-linux-gcc-c-O2-oinit.oinit.s arm-linux-gcc-c-O2-oxmrecever.oxmrecever.c arm-linux-gcc-c-O2-oflash.oflash.c 其中-c命令表示只生成目标代码,不进行连接;-o命令指明目标文件的名称;-O2表示采用二级优化,采用优化后可使生成的代码更短,运行速度更快。如果项目包含很多文件,则需要编写makefile文件。关于makefile的内容,请感兴趣的读者参考相关资料。 (3)编写连接脚本文件

  gcc等编译器内置有缺省的连接脚本。如果采用缺省脚本,则生成的目标代码需要操作系统才能加载运行。为了能在嵌入式系统上直接运行,需要编写自己的连接脚本文件。编写连接脚本,首先要对目标文件的格式有一定了解。GNU编译器生成的目标文件缺省为elf格式。elf文件由若干段(section)组成,如不特殊指明,由C源程序生成的目标代码中包含如下段:.text(正文段)包含程序的指令代码;.data(数据段)包含固定的数据,如常量、字符串;.bss(未初始化数据段)包含未初始化的变量、数组等。C++源程序生成的目标代码中还包括.fini(析构函数代码)和. init(构造函数代码)等。有关elf文件格式,读者可自行参考相关资料。连接器的任务就是将多个目标文件的.text、.data和.bss等段连接在一起,而连接脚本文件是告诉连接器从什么地址开始放置这些段。例如笔者的引导程序连接文件link.lds为:
ENTRY(begin) SECTION {.=0x00300000; .text:{*(.text)} .data:{*(.data)} .bss:{*(.bss)} }

  其中,ENTRY(begin)指明程序的入口点为begin标号;.=0x00300000指明目标代码的起始地址为0x00300000,这一段地址为MX1的片内RAM;.text:{*(.text)}表示从0x00300000开始放置所有目标文件的代码段,随后的.data:{* (.data)}表示数据段从代码段的末尾开始,再后是.bss段。
(4)用连接器生成最终目标文件

  有了连接脚本文件,如下命令可生成最终的目标文件:

arm-linux-ld-nostadlib-obootstrap.elf-Tlink.ldsinit.oxmrecever.oflash.o

其中,ostadlib表示不连接系统的运行库,而是直接从begin入口;-o指明目标文件的名称;-T指明采用的连接脚本文件;最后是需要连接的目标文件列表。
(5)生成二进制代码

  连接生成的elf文件还不能直接下载执行,通过objcopy工具可生成最终的二进制文件:
arm-linux-objcopy-Obinarybootstrap.elfbootstrap.bin 其中-Obinary指定生成为二进制格式文件。Objcopy还可以生成S格式的文件,只需将参数换成-Osrec。如果想将生成的目标代码反汇编,还可以用objdump工具: arm-linux-objdump-Dbootstrap.elf

  至此,所生成的目标文件就可以直接写入Flash中运行了。如果要通过自举模式下载,还需要转换为自举模式的文件格式,相关转换工具可以在摩托罗拉的网站上找到。

  掌握了GNU工具后,开发者就可以开发或移植C或C++代码的程序。用户可以不需要操作系统,直接开发简单应用程序。但对于更复杂的应用来说,操作系统必不可少。目前流行的源代码公开的操作系统如Linux、μC/OS都可以用GNU工具编译。ARM的Linux已有很多成熟的版本,可以支持 ARM720、ARM920、ARM1020等多种处理器,读者可从www.uclinux.orgwww.armdevzone.com上获取最新信息。Linux移植过程中和处理器相关的代码都放在arch/arm目录下。对于内核,用户需要做的是设定自己系统的内存映像,RAM起始地址,I/O地址空间和虚拟I/O地址空间,参看arch/arm/mach-integrator/arch.c文件。除了内核外,用户还需要为自己的系统编制各种各样的驱动程序。

4调试工具

  Linux下的GNU调试工具主要是gdb、gdbserver和kgdb。其中gdb和gdbserver可完成对目标板上Linux下应用程序的远程调试。gdbserver是一个很小的应用程序,运行于目标板上,可监控被调试进程的运行,并通过串口与上位机上的gdb通信。开发者可以通过上位机的gdb输入命令,控制目标板上进程的运行,查看内存和寄存器的内容。gdb5.1.1以后的版本加入了对ARM处理器的支持,在初始化时加入- target==arm参数可直接生成基于ARM平台的gdbserver。gdb工具可以从ftp: //ftp.gnu.org/pub/gnu/gdb/上下载。

  对于Linux内核的调试,可以采用kgdb工具,同样需要通过串口与上位机上的gdb通信,对目标板的Linux内核进行调试。由于篇幅所限,感兴趣的读者可以从http://oss.sgi.com/projects/kgdb/上了解具体的使用方法。

结束语

  本文以一个具体的实例为例,对GNU工具中的常用功能作了介绍。其实GNU工具的功能还远不止这些,更进一步的操作有:针对不同处理器,不同算法的软件优化、高效的内嵌汇编、大型项目管理功能等。相信GNU能成为越来越多开发人员的选择。

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
7#
 楼主| 发表于 2009-11-19 23:04:39 | 只看该作者

两份 E文 资料下载

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
8#
 楼主| 发表于 2009-11-19 23:07:23 | 只看该作者

objcopy命令介绍

objcopy把一种目标文件中的内容复制到另一种类型的目标文件中.

(1)将图像编译到可执行文件内
Q: 如何将一个二进制文件,比如图片,词典一类的东西做为.o文件,直接链接到可执行文件内部呢?
A:
$ objcopy -I binary -O elf32-i386 -B i386 14_95_13.jpg image.o
$ gcc image.o tt.o -o tt
$ nm tt | grep 14_95
0805d6c7 D _binary_14_95_13_jpg_end
00014213 A _binary_14_95_13_jpg_size
080494b4 D _binary_14_95_13_jpg_start

(2)使用objcopy把不用的信息去掉:
$ objcopy -R .comment -R .note halo halo.min

(3)
$ objcopy -R .note -R .comment -S -O binary xyb xyb.bin
-R .note -R .comment 表示移掉 .note 与 .comment 段
-S 表示移出所有的标志及重定位信息
-O binary xyb xyb.bin 表示由xyb生成二进制文件xyb.bin
objcopy工具使用指南
objcopy Utility
objcopy [ -F bfdname | --target=bfdname ]
[ -I bfdname | --input-target=bfdname ]
[ -O bfdname | --output-target= bfdname ]
[ -S | --strip-all ] [ -g | --strip-debug ]
[ -K symbolname | --keep-symbol= symbolname ]
[ -N symbolname | --strip-symbol= symbolname ]
[ -L symbolname | --localize-symbol= symbolname ]
[ -W symbolname | --weaken-symbol= symbolname ]
[ -x | --discard-all ] [ -X | --discard-locals ]
[ -b byte | --byte= byte ]
[ -i interleave | --interleave= interleave ]
[ -R sectionname | --remove-section= sectionname ]
[ -p | --preserve-dates ] [ --debugging ]
[ --gap-fill= val ] [ --pad-to= address ]
[ --set-start= val ] [ --adjust-start= incr ]
[ --change-address= incr ]
[ --change-section-address= section{=,+,-} val ]
[ --change-warnings ] [ --no-change-warnings ]
[ --set-section-flags= section= flags ]
[ --add-section= sectionname= filename ]
[ --change-leading char ] [--remove-leading-char ]
[ --weaken ]
[ -v | --verbose ] [ -V | --version ] [ --help ]
input-file [ outfile ]
 
GNU实用工具程序objcopy的作用是拷贝一个目标文件的内容到另一个目标文件中。Objcopy使用GNUBFD库去读或写目标文件。Objcopy可以使用不同于源目标文件的格式来写目的目标文件(也即是说可以将一种格式的目标文件转换成另一种格式的目标文件)。通过以上命令行选项可以控制Objcopy的具体操作。
Objcopy在进行目标文件的转换时,将生成一个临时文件,转换完成后就将这个临时文件删掉。Objcopy使用BFD做转换工作。如果没有明确地格式要求,则Objcopy将访问所有在BFD库中已经描述了的并且它可以识别的格式,请参见《GNUpro Decelopment Tools》中“using ld”一章中“BFD库”部分和“BFD库中规范的目标文件格式”部分。
通过使用srec作为输出目标(使用命令行选项-o srec),Objcopy可以产生S记录格式文件。
通过使用binary作为输出目标(使用命令行选项-obinary),Objcopy可以产生原始的二进制文件。使用Objcopy产生一个原始的二进制文件,实质上是进行了一回输入目标文件内容的内存转储。所有的符号和重定位信息都将被丢弃。内存转储起始于输入目标文件中那些将要拷贝到输出目标文件去的部分的最小虚地址处。
使用Objcopy生成S记录格式文件或者原始的二进制文件的过程中,-S选项和-R选项可能会比较有用。-S选项是用来删掉包含调试信息的部分,-R选项是用来删掉包含了二进制文件不需要的内容的那些部分。
input-file
outfile
参数input-file和outfile分别表示输入目标文件(源目标文件)和输出目标文件(目的目标文件)。如果在命令行中没有明确地指定outfile,那么Objcopy将创建一个临时文件来存放目标结果,然后使用input-file的名字来重命名这个临时文件(这时候,原来的input-file将被覆盖)。
-I bfdname
--input-target=bfdname
明确告诉Objcopy,源文件的格式是什么,bfdname是BFD库中描述的标准格式名。这样做要比“让Objcopy自己去分析源文件的格式,然后去和BFD中描述的各种格式比较,通过而得知源文件的目标格式名”的方法要高效得多。
-O bfdname
--output-target= bfdname
使用指定的格式来写输出文件(即目标文件),bfdname是BFD库中描述的标准格式名。
-F bfdname
--target= bfdname
明确告诉Objcopy,源文件的格式是什么,同时也使用这个格式来写输出文件(即目标文件),也就是说将源目标文件中的内容拷贝到目的目标文件的过程中,只进行拷贝不做格式转换,源目标文件是什么格式,目的目标文件就是什么格式。
-R sectionname
--remove-section= sectionname
从输出文件中删掉所有名为sectionname的段。这个选项可以多次使用。
注意:不恰当地使用这个选项可能会导致输出文件不可用。
-S
--strip-all (strip 剥去、剥)
不从源文件中拷贝重定位信息和符号信息到输出文件(目的文件)中去。
-g
--strip-debug
不从源文件中拷贝调试符号到输出文件(目的文件)中去。
--strip-undeeded
剥去所有在重定位处理时所不需要的符号。
-K symbolname
--keep-symbol= symbolname
仅从源文件中拷贝名为symbolname的符号。这个选项可以多次使用。
-N symbolname
--strip-symbol= symbolname
不从源文件中拷贝名为symbolname的符号。这个选项可以多次使用。它可以和其他的strip选项联合起来使用(除了-K symbolname | --keep-symbol= symbolname外)。
-L symbolname
--localize-symbol= symbolname
使名为symbolname的符号在文件内局部化,以便该符号在该文件外部是不可见的。这个选项可以多次使用。
-W symbolname
-weaken-symbol= symbolname
弱化名为symbolname的符号。这个选项可以多次使用。
-x
--discard-all (discard 丢弃、抛弃)
不从源文件中拷贝非全局符号。
-X
--discard-locals
不从源文件中拷贝又编译器生成的局部符号(这些符号通常是L或 . 开头的)。
-b byte
--byte= byte
Keep only every byte of the input file (header data is not affected). byte can be
in the range from 0 to interleave-1, where interleave is given by the -i or
--interleave option, or the default of 4. This option is useful for creating files to
program ROM . It is typically used with an srec output target.
-i interleave
--interleave= interleave (interleave 隔行、交叉)
Only copy one out of every interleave bytes. Select which byte to copy with the
-b or --byte option. The default is 4. objcopy ignores this option if you do not
specify either -b or --byte.
-p
--preserve-dates (preserve 保存、保持)
设置输出文件的访问和修改日期和输入文件相同。
[ --debugging ]
如果可能的话,转换调试信息。因为只有特定的调试格式被支持,以及这个转换过程要耗费一定的时间,所以这个选项不是默认的。
--gap-fill= val
使用内容val填充段与段之间的空隙。通过增加段的大小,在地址较低的一段附加空间中填充内容val来完成这一选项的功能。
--pad-to= address
填充输出文件到虚拟地址address。通过增加输出文件中最后一个段的大小,在输出文件中最后一段的末尾和address之间的这段附加空间中,用--gap-fill=val选项中指定的内容val来填充(默认内容是0,即没有使用--gap-fill= val选项的情况下)。
--set-start= val
设置新文件(应该是输出文件吧?)的起始地址为val。不是所有的目标文件格式都支持设置起始地址。
--change-start = incr
--adjust-start= incr
通过增加值incr来改变起始地址。不是所有的目标文件格式都支持设置起始地址。
--change-addresses incr
--adjust-vma incr
Change the VMA and LMA addresses of all sections, section., as well as the
start address, by adding incr. Some object file formats do not permit section
addresses to be changed arbitrarily.
通过加上一个值incr,改变所有段的VMA(Virtual Memory Address运行时地址)和LMA(Load Memory Address装载地址),以及起始地址。某些目标文件格式不允许随便更改段的地址。
--change-section-address section{=,+,-} val
--adjust-section-vma section{=,+,-} val
设置或者改变名为section的段的VMA(Virtual Memory Address运行时地址)和LMA(Load MemoryAddress装载地址)。如果这个选项中使用的是“=”,那么名为section的段的VMA(Virtual MemoryAddress运行时地址)和LMA(Load MemoryAddress装载地址)将被设置成val;如果这个选项中使用的是“-”或者“+”,那么上述两个地址将被设置或者改变成这两个地址的当前值减去或加上val后的值。如果在输入文件中名为section的段不存在,那么Objcopy将发出一个警告,除非--no-change-warnings选项被使用。
这里的段地址设置和改变都是输出文件中的段相对于输入文件中的段而言的。例如:
(1)--change-section-address .text = 10000
这里是指将输入文件(即源文件)中名为.text的段拷贝到输出文件中后,输出文件中的.text段的VMA(Virtual Memory Address运行时地址)和LMA(Load Memory Address装载地址)将都被设置成10000。
(2)--change-section-address .text + 100
这里是指将输入文件(即源文件)中名为.text的段拷贝到输出文件中后,输出文件中的.text段的VMA(Virtual MemoryAddress运行时地址)和LMA(Load MemoryAddress装载地址)将都被设置成以前输入文件中.text段的地址(当前地址)加上100后的值。
--change-section-lma section{=,+,-} val
仅设置或者改变名为section的段的LMA(Load MemoryAddress装载地址)。一个段的LMA是程序被加载时,该段将被加载到的一段内存空间的首地址。通常LMA和VMA(Virtual MemoryAddress运行时地址)是相同的,但是在某些系统中,特别是在那些程序放在ROM的系统中,LMA和VMA是不相同的。如果这个选项中使用的是“=”,那么名为section的段的LMA(Load MemoryAddress装载地址)将被设置成val;如果这个选项中使用的是“-”或者“+”,那么LMA将被设置或者改变成这两个地址的当前值减去或加上val后的值。如果在输入文件中名为section的段不存在,那么Objcopy将发出一个警告,除非--no-change-warnings选项被使用。
--change-section-vma section{=,+,-} val
仅设置或者改变名为section的段的VMA(Load Memory Address装载地址)。一个段的VMA是程序运行时,该段的定位地址。通常VMA和LMA(VirtualMemoryAddress运行时地址)是相同的,但是在某些系统中,特别是在那些程序放在ROM的系统中,LMA和VMA是不相同的。如果这个选项中使用的是“=”,那么名为section的段的LMA(Load MemoryAddress装载地址)将被设置成val;如果这个选项中使用的是“-”或者“+”,那么LMA将被设置或者改变成这两个地址的当前值减去或加上val后的值。如果在输入文件中名为section的段不存在,那么Objcopy将发出一个警告,除非--no-change-warnings选项被使用。
--change-warnings
--adjust-warnings
如果命令行中使用了--change-section-address section{=,+,-} val或者--adjust-section-vma section{=,+,-}val,又或者--change-section-lma section{=,+,-} val,又或者--change-section-vmasection{=,+,-} val,并且输入文件中名为section的段不存在,则Objcopy发出警告。这是默认的选项。
--no-chagne-warnings
--no-adjust-warnings
如果命令行中使用了--change-section-address section{=,+,-} val或者--adjust-section-vma section{=,+,-}val,又或者--change-section-lma section{=,+,-} val,又或者--change-section-vmasection{=,+,-} val,即使输入文件中名为section的段不存在, Objcopy也不会发出警告。
--set-section-flags section=flags
为为section的段设置一个标识。这个flags变量的可以取逗号分隔的多个标识名字符串(这些标识名字符串是能够被Objcopy程序所识别的),合法的标识名有alloc,load,readonly,code,data和rom。
Youcan set the contents flag for a section which does not havecontents,but it is not meaningful to clear the contents flag of a section whichdoes have contents; just remove the section instead. Not all flags aremeaningful for all object file formats.
--add-section sectionname=filename
进行目标文件拷贝的过程中,在输出文件中增加一个名为sectionname的新段。这个新增加的段的内容从文件filename得到。这个新增加的段的大小就是这个文件filename的大小。只要输出文件的格式允许该文件的段可以有任意的段名(段名不是标准的,固定的),这个选项才能使用。
--change-leading-char
Some object file formats use special characters at the start of symbols. The most
common such character is underscore, which compilers often add before every
symbol. This option tells objcopy to change the leading character of every
symbol when it converts between object file formats. If the object file formats use
the same leading character, this option has no effect. Otherwise, it will add a
character, or remove a character, or change a character, as appropriate.
--remove-leading-char
If the first character of a global symbol is a special symbol leading character used
by the object file format, remove the character. The most common symbol leading
character is underscore. This option will remove a leading underscore from all
global symbols. This can be useful if you want to link together objects of different
file formats with different conventions for symbol names.
--weaken
Change all global symbols in the file to be weak. This can be useful when building
an object that will be linked against other objects using the -R option to the linker.
This option is only effective when using an object file format that supports weak
symbols.
-V
--version
Show the version number of objcopy.
-v
--verbose
Verbose output: list all object files modified. In the case of archives, objcopy -V
lists all members of the archive.
--help
Show a summary of the options to objcopy.
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-5-7 02:49 , Processed in 0.127069 second(s), 21 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表