C语言生成的段和CMD文件_adams如何生成cmd文件
C语言生成的段和CMD文件由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“adams如何生成cmd文件”。
C语言生成的段和CMD文件
通用目标文件格式COFF(Common Object File Format),是一种很流行的二进制可执行文件格式。二进制可执行文件包括库文件(以后缀.lib结尾)、目标文件(以后缀.obj结尾)、最终的可执行文件(以后缀.out结尾)等。1. COFF格式
详细的COFF文件格式包括段头、可执行代码和初始化数据、可重定位信息、行号入口、符号表、字符串表等,这些属于编写操作系统和编译器人员关心的范畴。从应用的层面上讲,DSP的C语言程序员应掌握两点:通过伪指令定义段;并给段分配空间。至于二进制文件到底如何组织分配,则交由编译器完成。
把握COFF格式的概念,最关键的一点就是:二进制可执行文件是以段(section)的形式存储的。
使用段的好处是鼓励模块化编程,提供更强大而又灵活的方法来管理代码和目标系统的内存空间。这里模块化编程的意思是,程序员可以自由决定愿意把哪些代码归属到哪些段,然后加以不同的处理。
编译器处理段的过程为:每个源文件都编译成独立的目标文件(以后缀.obj结尾),每个目标文件含有自己的段,连接器把这些目标文件中相同段名的部分连接在一起,生成最终的可执行文件(以后缀.out结尾)。
段分为两大类:已初始化的段和未初始化的段。
已初始化的段含有真实的指令和数据,存放在程序存储空间。程序存储空间在DSP片内的FLASH。调试代码时,则常常把代码在线下载到RAM中运行。
未初始化的段只是保留变量的地址空间,未初始化的段存放在数据存储空间中,数据存储空间多为RAM存储单元。在DSP上电调用_c_int0初始化库前,未初始化的段并没有真实的内容。
汇编语言中,通过六条伪指令来定义段,因此时常把伪指令和段混为一谈,比如伪指令“.b”,也是段“.b”。
(1)未初始化的段
1).b:定义变量存放空间。
2).usect:用户可自行定义未初始化的段,提供给用户更多的灵活性。(2)已初始化的段
1).text:包含可执行的汇编指令代码。.text是系统定义的默认段,如果不明确声明,代码就归属.text段。
2).data:一般包括常数数据。比如,用来对变量初始化的数据或一个正弦表格等。
3).sect:用户可自行定已初始化的段,提供给用户更多的灵活性。
4).asect:作用类似于.sect,但是多了绝对地址定位功能。由于地址定位功能常用功能更强大又灵活的命令文件来完成,这条指令在汇编编程中已经废弃不用。
2.C语言生成的段
先解释一下堆栈的概念,二者是不同的概念。
栈(stack)是由系统自动管理的一片内存,用来存放局部变量和函数压栈出栈的状态量。进入C语言函数时需要保存一些寄存器的状态,即压栈操作;退出函数时要还原那些寄存器,即出栈操作。
堆(heap)是当用户想要自己能独立灵活地控制一些内存时,可以用malloc()等函数开辟一些动态内存区,这些动态内存区称为堆。C语言在运行时并不检查堆栈溢出与否。如果堆栈段定义在数据存储空间的最后区域,实际运行时即使堆栈发生溢出,也不会覆盖其他有用的数据,此时堆栈可用的最高限额是实际数据存储空间的最高地址。
C语言有7个定义好的段,没有了汇编语言中的.data段,以下分类叙述。
(1)已初始化的段
1).text:编译C语言中的语句时,生成的汇编指令代码存放于此。
2).cinit:存放用来对全局和静态变量初始化的常量
3).switch:存放switch语句产生的常数表格。
(2)未初始化的段
1).b:存放全局和静态变量。
2).stack:存放C语言的栈。
3).sysmen:存放C语言的堆。
4).const:稍微有些复杂的段。简单而言,是用来存放一些特殊的常数和字符等。
#pragma是标准C中保留的预处理命令。程序员可以通过#pragma来定义自己的段,这是预处理命令#pragma的主要用法。
#pragma的语法是:
#pragma CODE_SECTION(symbol,”section name”);#pragma DATA_SECTION(symbol,”section name”);说明:
1)symbol是符号,可以是函数名也可以是全局变量名,section name是用户自己定义的段名。
2)CODE_SECTION用来定义代码段,DATA_SECTION用来定义数据段。使用#pragma需要注意:
1)不能在函数体内声明#pragma.2)必须在符号被定义和使用前使用#pragma。
如果没有用到某些段,比如很多人都没有用到.sysmen段,就可以不用在CMD文件中为其分配空间。当然保险起见,也可以不论用到与否,都全分配空间,没有用到段的空间大小当然是零。
在CMD文件中,page0代表程序空间,page1代表数据空间,下表列出这些段应该分配的存储空间。
3.连接命令文件(CMD文件)
连接命令文件(Linker Command Files),以后缀.cmd结尾,简称为CMD文件。
CMD文件的两大主要功能是指示存储空间和分配段到存储空间,以下分别叙述。
〈1〉通过MEMORY伪指令来指示存储空间 MEMORY伪指令语法如下:
MEMORY {
PAGE 0:name 0[(attr)]:origin=constant,length=constant
PAGE 0:name n[(attr)]:origin=constant,length=constant }(1)PAGE 用来标示存储空间的关键字。page n的最大值为page 225。C24XX和C28XX系列中用的是page 0、page 1,其中page 0为程序存储空间,page 1为数据存储空间。
(2)name 代表某一属性和地址范围的存储空间名称。名称可以是1-8个字符,在同一个页内名称不能相同,不同页内名称能相同。
(3)attr 用来规定存储空间的属性。共有4个属性,分别用4个字母代表:只读R、只写W、该空间可包含可执行代码X、该空间可以被初始化I。实际使用时,简化起见,常忽略此选项,这样存储空间就能具有所有的属性。
(4)orgin 用来定义存储空间起始地址的关键字。(5)length 用来定义存储空间长度的关键字。〈2〉通过SECTIONS伪指令来分配段到存储空间
相对于简单的伪指令MEMORY,伪指令SECTION稍稍有些复杂。SECTION伪指令语法如下:
SECTIONS {
name:[property,property,property,…]
name:[property,property,property,…]
……
}(1)name 输出段的名称。
(2)property 输出段的属性。常用的有下面一些属性。
1)load:定义输出段将会被装载到哪里的关键字。其语法如下:
load=allocation或
allocation或
>allocation allocation可以是强制地址,比如“load=0x100”。但更多的时候,allocation是存储空间的名称,这也是最为通常的用法。
2)run:定义输出段将会在哪里运行的关键字。其语法如下:
run=allocation 或
run>allocation CMD文件规定当只出现一个关键字load或run时,表示load地址和run地址是重叠的。实际应用中,大部分段的load地址和run地址是重叠的,除了.const段。
3)输入段。其语法如下:
{input_sections} 花括号“{}”中是输入段。
这里是输入段与输出段做一个区分:每一个汇编或C语言文件经过编译会生成若干个段,多个汇编或C语言文件生成的段大都是同名的,常见的如前面已经介绍过的段.cinit,.b等等。这些段都是输入段。这些归属于不同文件的输入段,在CMD文件的指示下,会被连接器连接在一起生成输出段。
4)其余的特性。
CMD文件中还可以直接写各种编译命令。有些程序员也喜欢这么做,考虑到读者遇见时不致于困惑,兹举几例如下: 1)-l rts2xx.lib /*连接系统库文件rts2xx.lib*/ 2)-o roam.out /*最终生成的二进制文件命名为roam.out*/ 3)-m roam.map /*生成映射文件roam.map*/ 4)-stack 0x200 /*堆栈定为512字*/ 4.复杂的.const段
C语言有中有三种情况会产生.const段:
(1)关键字const 由关键字const限定的带有全局基本变量的初始化值,比如“const int a=90;”。但由于关键字const限定的局部基本变量的初始化值,不会产生.const段,局部变量都是运行时开辟在.b段中的。
(2)字符串常数 字符串常数出现在表达式中,比如,“strcpy(s,”abc”);”。字符串常数用来初始化指针变量,比如“char *p=”abc”;”。但当字符串常数用来初始化数组变量时,不论是全局还是局部数组变量,都不会产生.const段,此时字符串常数生成的是.cinit段。比如“char s[4]=”abc”;”。
(3)数组和结构体的初始值 数组和结构体是局部变量,其初始化值会产生.const段,比如“int a[8]={1,2,3};”。但当数组和结构体是全局变量时,其初始化值不会产生.const段,此时生成的是.cinit段。
设置.const段是基于灵活性考虑的,程序中常会有大量的常数占用数据空间,比如液晶显示用的点阵字库等。这些数据空间存放常数值,只被用来读,而从不会被写入。把这些常数单独编译成.const段,就为C编译器来做特定处理提供了条件。
那怎么存储这些常数呢?
一种解决的方法是把.const段中的常数存储在程序空间,上电时把这些常数由程序空间搬移到数据空间,但这样的初始化费时,且占用了大量的程序空间。理想的解决方法是把.const段中的常数固化或烧写到外在的一个ROM或FLASH中,并把ROM或FLASH的地址译码到DSP的数据空间,这样就能避免地一种解决方法的缺陷了。
但是,当没有这个ROM或FLASH来存储常数时,还是要用第一种解决方法。把.const段从page0搬移到page1,需要在两个地方作些设置和改动。
1)在CMD文件中的设置
在CMD文件需要把装载和运行分开,装载在page0,运行在page1。
2)修改连接的rts2xx.lib库
在DOS命令环境下,从trs.src源文件库中释放出boot.asm。
Dspar –x rts.src boot asm 打开boot.asm把里面的CONST_COPY常数改成1(原先是0)。
CONST_COPY.set 1 重新把boot.asm文件编译一遍,生成boot.obj。
dspa –v2xx boot.asm 把boot.obj归档到C语言的rts2xx.lib库中。
Dspa –r rts2xx.lib boot.obj 此时生成新的库文件rts2xx.lib 当C语言程序连接到新的库文件rts2xx.lib时,DSP上电初始化时,系统库将自动把.const段中的常数从page0区搬移到page1区。