1
www.cnasic.comwww.cnasic.com
第第 三三 讲讲
编译,汇编,链接编译,汇编,链接
凌 明
trio@seu.edu.cn
东南大学国家专用集成电路系统
工程
路基工程安全技术交底工程项目施工成本控制工程量增项单年度零星工程技术标正投影法基本原理
技术研究中心
www.cnasic.comwww.cnasic.com
目 录
编译、汇编与连接
项目中的文件组织
项目中文件的依赖关系
Make
为ARM编写高效的C语言代码
基本的C数据类型
C循环结构
寄存器分配
函数调用
结构体的安排
移植问题
2
www.cnasic.comwww.cnasic.com
编译、汇编与连接编译、汇编与连接
www.cnasic.comwww.cnasic.com
面向嵌入式系统的软件开发环境
(传统的开发环境)
编译器
汇编器
liber 连接器
编辑
环境
?
?
?
?
?
?
?
?
交叉开交叉开
发环境发环境
IDEIDE Debugger
(调试,跟踪)
转换程序 仿真器(ICE)
目标板
烧结程序
串口,并口,
以太网,USB
项目
管理
3
www.cnasic.comwww.cnasic.com
file.c file.s
C 源文件 汇编源文件
C 编译器
file.s
汇编器
file.o
Linker
Liber
file.lib OS lib ANSI lib
make
file
file.outfile.xrf
file.lst
errors
调试工具
目标系统
转换工具
二进制文件
烧结工具
操作系统库 标准C库
www.cnasic.comwww.cnasic.com
编译器和汇编器的作用
编译器将C文件转换成为汇编文件
汇编器将汇编文件转换成为二进制指令流
*.o文件(目标文件)
每个目标文件是独立编址的,也就是说每
个目标文件的第一条指令都从相同的地址
开始存放
4
www.cnasic.comwww.cnasic.com
目标文件的结构
TEXT
(CODE)
DATA
执行代码
有初值的全局变量
或静态变量
无初值的全局变量
或静态变量
RO
RW
ZI
低地址
高地址 ARM CPUARM CPU
www.cnasic.comwww.cnasic.com
连接器的作用
将多个目标文件或库文件按照各文件中段
进行统一编址
生成一个完整的统一的地址印象
嵌入式系统中一般生成一个绝对地址印象
(非PIC)
在有MMU的系统中可以为每个任务单独分
配一个地址空间
5
www.cnasic.comwww.cnasic.com
连接器的作用
ROM BasedROM Based
RAM BaseRAM Base
RO1
ZI 1
RO2
RW2
ZI2
RO3
RW3
ZI3
RO1
RO2
RO3
RW1
RW2
RW3
ZI1
ZI2
ZI3
file1.o file2.o file3.o
Link
file.out
RW1
www.cnasic.comwww.cnasic.com
项目中的文件组织项目中的文件组织
6
www.cnasic.comwww.cnasic.com
一个简单的项目-myproject
File1.C
File1.h
File2.C
File2.h
File3.C
File3.h
CALLCALL CALLCALL
IncludeInclude IncludeInclude
File1.oFile1.o File2.oFile2.o File3.oFile3.o
IncludeInclude
mylib.libmylib.lib
myproject.outmyproject.out
LinkLink
File1.oFile1.o依赖于依赖于
File1.cFile1.c
File1.hFile1.h
File2.hFile2.h
MyProject.outMyProject.out依赖于:依赖于:
File1.oFile1.o
File2.oFile2.o
File3.oFile3.o
Mylib.libMylib.lib
www.cnasic.comwww.cnasic.com
如何生成myproject?
编译File1.C->File1.o
编译File2.C->File2.o
编译File3.C->File3.o
连接File1.o+ File2.o+ File3.o+mylib.lib
问题:问题:
11、如果不仅仅是这几个文件,而是上百个文件怎么办?一个一个编译?、如果不仅仅是这几个文件,而是上百个文件怎么办?一个一个编译?
回答:回答:
只要编译修改过的文件就可以了。但是如果我们修改了只要编译修改过的文件就可以了。但是如果我们修改了File2.h, File2.h, 是不是要是不是要
重新编译重新编译File1.C? File1.C? 有没有办法解决这种复杂的相互依赖关系?有没有办法解决这种复杂的相互依赖关系?
回答:回答:
MAKEMAKE文件文件
IDEIDE中的项目管理中的项目管理
再问?如果我们修改了再问?如果我们修改了File2.C,File2.C,而没有修改而没有修改File2.h, File2.h, 是否要重新编译是否要重新编译File1.C?File1.C?
7
www.cnasic.comwww.cnasic.com
MAKE文件
MAKE实际上是一个批处理程序,该程序通过解
释特定格式的MAKE脚本(MAKE文件),完成
一个项目相关文件的编译,汇编与连接
MAKE脚本一般描述了整个项目中各个文件的相
互依赖关系,MAKE通过调用脚本中指定编译
器,汇编器和连接器,按照项目个文件的依赖关
系进行处理。
常见的MAKE程序:
MS nmake
Gcc make
www.cnasic.comwww.cnasic.com
MAKEMAKE文件阅读文件阅读
E:\ASIX OS\platform-
20030124\platform\build\drball\PDA.MAK
8
www.cnasic.comwww.cnasic.com
IDE 中的项目管理
现代的IDE环境一般不需要程序员自己编写
MAKE脚本,系统引入Project的概念,自
动维护文件之间的依赖关系,大大方便了
程序员的工作。
www.cnasic.comwww.cnasic.com
为为ARMARM编写高效编写高效
的的CC语言代码语言代码
9
www.cnasic.comwww.cnasic.com
编译器相关是一个问题!
不同的编译器对于数据类型的约定可能不同
Char, short, int, long 分别
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
示什么?
不同的编译器在处理函数调用的传参与返回值可能不同
不同的编译器在局部变量的处理上可能不同
不同的编译器在组织结构体的时候,存储器的布局可能不
同
不同的编译器在缺省状态下的优化选项可能不同
www.cnasic.comwww.cnasic.com
ARM体系结构中的LDR和STR指令
装载一个有/无符号64位数据LDRD
存储一个有/无符号16位数据STRH
V4
存储一个有/无符号64位数据STRD
装载一个有符号16位数据LDRSH
装载一个无符号16位数据LDRH
装载一个有符号8位数据LDRSB
V5
存储一个有/无符号32位数据STR
装载一个有/无符号32位数据LDR
存储一个有/无符号8位数据STRB
装载一个无符号8位数据LDRBPre V4
功能指令体系结构
装载装载88位或位或1616位数据必须做符号位扩展,这需要额外的时间位数据必须做符号位扩展,这需要额外的时间
10
www.cnasic.comwww.cnasic.com
ARM C编译器的数据类型映射
Char 无符号无符号88位位
Short 有符号16位
Int 有符号32位
Long 有符号32位
Long long 有符号64位
www.cnasic.comwww.cnasic.com
局部变量的数据类型
IntInt checksum(intchecksum(int *data)*data)
{{
char i;char i;
intint sum = 0;sum = 0;
for (i = 0; i < 64; i++)for (i = 0; i < 64; i++)
{{
sum += sum += data[idata[i];];
}}
return sum;return sum;
} }
这段代码的问题这段代码的问题::
1, 1, 所有的所有的ARM ARM 寄存器都是寄存器都是3232位位
2, 2, 所有的堆栈入口也是所有的堆栈入口也是3232位位
3,3,编译器必须显示地处理编译器必须显示地处理 i = 255i = 255
时的情况,对于时的情况,对于char char 而言而言 255255
加加1 1 的结果是的结果是 00!!
ChecksumChecksum
MOV r2, r0 ; r2 = dataMOV r2, r0 ; r2 = data
MOV r0, #0 ; sum = 0MOV r0, #0 ; sum = 0
MOV r1, #0 ; i = 0MOV r1, #0 ; i = 0
Checksum_loopChecksum_loop
LDR r3,[r2, r1, LSL #2] ; r3 = LDR r3,[r2, r1, LSL #2] ; r3 = data[idata[i]]
ADD r1, r1, #1 ; r1 = i + 1ADD r1, r1, #1 ; r1 = i + 1
AND r1,r1,#0xff ; i = (char)r1AND r1,r1,#0xff ; i = (char)r1
CMP r1,#0x40 ; I < 64 ?CMP r1,#0x40 ; I < 64 ?
ADD r0, r3, r0 ; sum += r3ADD r0, r3, r0 ; sum += r3
BCC BCC checksum_loopchecksum_loop
MOV pc, r14 ; return sum MOV pc, r14 ; return sum
11
www.cnasic.comwww.cnasic.com
局部变量的数据类型
short short checksum(shortchecksum(short *data)*data)
{{
unsigned unsigned intint i;i;
short sum = 0;short sum = 0;
for (i = 0; i < 64; i++)for (i = 0; i < 64; i++)
{{
sum = (short)( sum = (short)( sum+data[isum+data[i] );] );
}}
return sum;return sum;
} }
假设数据包中的数据是假设数据包中的数据是1616位位
注意注意: : 缺省情况下缺省情况下 sum + sum + data[idata[i]]的的
结构是结构是3232位整新,因此需要进行显位整新,因此需要进行显
示的数据类型的强制转换示的数据类型的强制转换
ChecksumChecksum
MOV r2, r0 ; r2 = dataMOV r2, r0 ; r2 = data
MOV r0, #0 ; sum = 0MOV r0, #0 ; sum = 0
MOV r1, #0 ; i = 0MOV r1, #0 ; i = 0
Checksum_loopChecksum_loop
ADD r3,r2,r1,LSL #1 ;r3 = &ADD r3,r2,r1,LSL #1 ;r3 = &data[idata[i]]
LDRH r3,[r3,#0] ;r3=LDRH r3,[r3,#0] ;r3=data[idata[i]]
ADD r1,r1,#1 ;i++ADD r1,r1,#1 ;i++
CMP r1,#0x40 ; I < 64 ?CMP r1,#0x40 ; I < 64 ?
ADD r0, r3, r0 ; sum += r3ADD r0, r3, r0 ; sum += r3
MOV r0,r0,LSL #16MOV r0,r0,LSL #16
MOV r0,r0,ASR #16 ;sum = (short)r0MOV r0,r0,ASR #16 ;sum = (short)r0
BCC BCC checksum_loopchecksum_loop
MOV pc, r14 ; return sum MOV pc, r14 ; return sum
short checksum(short *data)
{
unsigned int i;
int sum = 0;
for (i = 0; i < 64; i++)
{
sum += *( data++ );
}
//将数据类型转换放在循环外
return (short)sum;
}
short short checksum(shortchecksum(short *data)*data)
{{
unsigned unsigned intint i;i;
intint sum = 0;sum = 0;
for (i = 0; i < 64; i++)for (i = 0; i < 64; i++)
{{
sum += *( data++ );sum += *( data++ );
}}
////将数据类型转换放在循环外将数据类型转换放在循环外
return (return (short)sumshort)sum; ;
} }
www.cnasic.comwww.cnasic.com
函数的参数类型
IntInt wordincwordinc ((intint a)a)
{return a+1;{return a+1;
}}
IntInt shortinc(shortshortinc(short a)a)
{return a+1;{return a+1;
}}
IntInt charinc(charcharinc(char a)a)
{return a+1;{return a+1;
}}
wordincwordinc
ADD a1,a1,#1ADD a1,a1,#1
MOV PC,LRMOV PC,LR
wordincwordinc
ADD a1,a1,#1ADD a1,a1,#1
MOV a1,a1,LSL #16MOV a1,a1,LSL #16
MOV a1,a1,ASR #16MOV a1,a1,ASR #16
MOV PC,LRMOV PC,LR
wordincwordinc
ADD a1,a1,#1ADD a1,a1,#1
AND a1,a1,#&ffAND a1,a1,#&ff
MOV PC,LRMOV PC,LR
12
www.cnasic.comwww.cnasic.com
循环的处理
Fact1Fact1
……
0x000010:MUL R2,R1,R20x000010:MUL R2,R1,R2
0x000014:ADD R1,R1,#10x000014:ADD R1,R1,#1
0x000018:CMP R1,R00x000018:CMP R1,R0
0x00001c:BLE 0x100x00001c:BLE 0x10
……
Fact1Fact1
……
0x000010:MUL R0,R1,R00x000010:MUL R0,R1,R0
0x000014:SUBS R1,R1,#10x000014:SUBS R1,R1,#1
0x000018:BNE 0x100x000018:BNE 0x10
……
intint fact2(int limit)fact2(int limit)
{{
……
for(ifor(i= = limit;ilimit;i!!=0t;i=0t;i----))
{{ fact = fact = factfact*i;*i;
}}
……
}}
int fact1(int limit)
{
…
for(i=1;i<=limit;i++)
{ fact = fact*i;
}
…
}
www.cnasic.comwww.cnasic.com
循环的展开
IntInt checksum(intchecksum(int *data, unsigned *data, unsigned intint N )N )
{{
intint sum = 0;sum = 0;
dodo
{{
sum += *(data++);sum += *(data++);
sum += *(data++);sum += *(data++);
sum += *(data++);sum += *(data++);
sum += *(data++);sum += *(data++);
N N -- = 4; = 4;
} while ( N != 0 );} while ( N != 0 );
return sum;return sum;
} }
每次循环需要在循环体外增加每次循环需要在循环体外增加22
条指令:减法指令减少循环计条指令:减法指令减少循环计
数,分支指令。减法需要数,分支指令。减法需要11个周个周
期,分支需要期,分支需要33个周期,我们可个周期,我们可
以通过减少循环次数来降低这些以通过减少循环次数来降低这些
额外的开销。额外的开销。
ChecksumChecksum
MOV r2, MOV r2, ##0 ; sum = 00 ; sum = 0
Checksum_loopChecksum_loop
LDR r3,[r0],#4 ;r3 = *( data++ )LDR r3,[r0],#4 ;r3 = *( data++ )
SUBS r1,r1,#4 ; N SUBS r1,r1,#4 ; N --= 4 and set flag= 4 and set flag
ADD r2, r3, r2 ;sum += r3ADD r2, r3, r2 ;sum += r3
LDR r3 LDR r3 ,[r0],#4 ;r3 = *( data++ ),[r0],#4 ;r3 = *( data++ )
ADD r2, r3, r2 ;sum += r3ADD r2, r3, r2 ;sum += r3
LDR r3 ,[r0],#4 ;r3 = *( data++ )LDR r3 ,[r0],#4 ;r3 = *( data++ )
ADD r2, r3, r2 ;sum += r3ADD r2, r3, r2 ;sum += r3
LDR r3 ,[r0],#4 ;r3 = *( data++ )LDR r3 ,[r0],#4 ;r3 = *( data++ )
ADD r2, r3, r2 ;sum += r3ADD r2, r3, r2 ;sum += r3
BNE BNE checksum_loopchecksum_loop
MOV r0, r2 ; r0 = sumMOV r0, r2 ; r0 = sum
MOV pc, r14 ; return sum MOV pc, r14 ; return sum
13
www.cnasic.comwww.cnasic.com
关于循环展开的问题
只有当循环展开对提高程序的整体性能非
常重要时,才可以作循环展开;否则得益
不大反而会增加代码尺寸,甚至会因为替
换掉cache中更重要的代码而降低总体性能
www.cnasic.comwww.cnasic.com
寄存器的分配
编译器会试图对C函数中的每一局部变量分
配一个寄存器。如果几个局部变量不会交
叠使用,编译器会对他们分配相同的寄存
器。
当局部变量多于可用寄存器时,编译器会
将这些变量分配在堆栈中。
14
www.cnasic.comwww.cnasic.com
ATPCS(ARM Thumb过程调用标准)
r0~r3 用于传参,r0用于返回值
r4~r11 通用变量寄存器,
r12 临时过渡寄存器
r13 堆栈指针
r14 连接寄存器
r15 PC
所以函数内的局部变量最好不要超过所以函数内的局部变量最好不要超过1212个个
www.cnasic.comwww.cnasic.com
关于传参
参数0
参数1
参数2
参数3
返回值
参数4
参数5
参数6
参数7
参数…
r0r0
r1r1
r2r2
r3r3
高
低
spsp
Sp+4Sp+4
Sp+8Sp+8
Sp+12 Sp+12
如果一个函数的参数多于如果一个函数的参数多于44个,个,C++C++的参数的参数
多于多于33个,其他的参数会通过堆栈进行传递,个,其他的参数会通过堆栈进行传递,
降低效率,因此将多个相关的参数组织到一个降低效率,因此将多个相关的参数组织到一个
结构体中,传递该结构的指针,是一个好办法结构体中,传递该结构的指针,是一个好办法
15
www.cnasic.comwww.cnasic.com
通过寄存器传参实现C调汇编
AREA
StrCopy,CODE,READONLY
EXPORT strcopy
Strcopy
LDRB R2,[R1],#1
STRB R2,[R0],#1
CMP R2,#0
BNE strcopy
MOV PC,LR
END
Extern void strcopy(char *d,const char
*s)
Int main(void)
{
const char *src = “Source”;
char dest[10];
…
strcopy(dest,src);
}
从C中直接调用汇编函数
www.cnasic.comwww.cnasic.com
结构体的安排
StructStruct {{
char a;char a;
intint b;b;
char c;char c;
short d;short d;
}}
pad pad pad a
b[31,24] b[23,16] b[15,8] b[7,0]
d[15,8] d[7,0] pad c
+0+1+2+3
+0
+4
+8
StructStruct {{
char a;char a;
char c;char c;
short d;short d;
intint b;b;
}}
b[31,24] b[23,16] b[15,8] b[7,0]
d[15,8] d[7,0] c a
+0+1+2+3
+0
+4
把所有的8位元素安排在结构体的最前面
依次安排 16位,32位,64位
数组和较大的元素安排结构的最后
16
www.cnasic.comwww.cnasic.com
移植的问题
Char 类型(有符号还是无符号?)
Int类型(16位还是32位?)
不对齐的数据指针
把一个char *指针转换成 int *
www.cnasic.comwww.cnasic.com
总结
初级经济法重点总结下载党员个人总结TXt高中句型全总结.doc高中句型全总结.doc理论力学知识点总结pdf
对局部变量,函数参数和返回值要使用signed 或
unsigned int类型。这样可以避免类型转换,而且
可高效地使用ARM的32位数据操作指令
最高效的循环
设计
领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计
是count down to 0 的do-while
循环
展开重要的循环来减少循环开销
尽可能把函数的参数限制在4个以内
不要使用位域,可以用掩码和逻辑操作来代替
避免除法
避免数据边界的不对齐