要成为专家就先用几年时间把这个领域做个大概的了解把,之后十年在专注其中各个部分
avr-gcc段section与在定位relocation
粗略的讲,一个代表一无缝隙的数据快即地址范围,一个段里存储的数据都为同一性质,
如只读数据。as汇编器在编译局部程序时总假设从0地址开始,并生成目标文件。
最后LD连接器在连接多个目标文件时为每一个段分配运行时统一地址。这虽然是个简单的解
释,却足以说明我门为什么用段。
LD将这些数据快正确移动到他们运行时的地址。此过程非常严格,数据的内部顺序
与长度不能发生变化。这样的数据单元叫做段,为段分配运行时地址叫做在定位,此任务
根据目标文件内的参考地址将段数据调整到运行时地址。
AVR-GCC中汇编器生成的目标文件(object-file)至少包含4个段,分别为:text
段,data段,.bss段和.eeprom段,他们包括了程序存储器(FLash),数据存储器ram,
eeprom存储器的使用量,关系如下:
程序存储器(flash)使用 = .text+.data
数据存储器(RAM)使用量 =.data+.bss[+.noinit]+stack[+heap]
EEPROM存储器使用良 =.eeprom
.text段包含程序实际执行代码。另外,此段还包含.initN和.finiN两种
段,下面详细讨论这两种段。
段.initN和段.finiN是个程序快,它不会象函数那样返回,所以汇编或C程序不能
调用。.initN,.finN和绝对段(sbsolute section 提供中断向量)构成avr-libc应用程
序运行框架,拥护编写的应用程序在此框架中运行。
.initN段
此类段包含从复位到(main())函数开始执行之间的启动(startup)代码。
此类段共定义10个分别是.init0到.init9.执行顺序是从.init0到.init9.
.init0:
此段绑定到函数_init().拥护可重载_init(),复位后立即跳到该函数。
.init1:
未用,拥护可定义
.init2:
将栈指针初始化成器件对应ramend处,清零_zero_reg_积存器
.init3:
未用,拥护可定义
.init4:
初始化.data段(从FLASH复制全局或静态变量初始值到.data),清零.bss
段。象UNIX一样.data段直接从可执行文件中装入。AVR-GCC将.data段的
初始值存储到flash的rom里.text段的后面,init4代码则负责将这些数据
复制sram内.data段。
.init5:
未用,拥护可定义
.init6:
C代码未用,C++程序的构造代码
.init7:
未用,拥护可定义
.init8:
未用,拥护可定义
.init9:
跳到main()
avr-libc应用程序运行框架是以静态库的形式存储在
/avr/lib目录下,在拥护程序的连接时他们会自动连接近来。
.finiN段
此类段包含main()函数退出后执行的代码。
此类段可有0到9个,执行次序从fini9到fini1.
.fini9:
次段绑定到函数exit().
.fini8:
未用,拥护可定义
.fini7:
未用,拥护可定义
.fini6:
C代码未用,c++程序的析构代码
.fini5:
未用,拥护可定义
.fini4:
未用,拥护可定义
.fini3:
未用,拥护可定义
.fini2:
未用,拥护可定义
.fini1:
未用,拥护可定义
.fini0:
进入一个无限循环
拥护代码插入到.initN或.finiN
可以将拥护代码插入到.initN或.finiN段当中,这是通过给C函数指定段
属性来实现的,如下是一段插入到.init1段的示例:
void my_init_portb (void) _attribute_((naked))\
_attribute_ ((section (".init1")));
void my_init_portb(void)
{
PORTB=0XFF;
DDRB=0XFF;
}
使用_arribute_关键字为函数指定属性要在函数的原形声明上,而不是函数
的实现历程里。由于属性section(".init1")的指定,编译后函数my_init_portb生成的代码
自动插入到.init1段中,在main函数前就得到执行。naked属性确保编译后该函数不生成返回
指令,使下一个初始化段得以顺序的执行。
二. .data段
.data段包含程序中被初始化的ram区全局或静态变量。而对于FLASH存储器
此段包含在程序中定义变量的初始化数据。类似如下的代码将生成.data段数据。
char err_str[]="Your program has died ahorrible death!";
struct point pt ={1,1};
可以将.data 在sram内的开始地址指定给连接器,这是通过给avr-gcc命令行添加
-W1,-Tdata,addr选项来实现的,其中addr 必须是0x800000加sram实际地址。
例如要将.data段从0x1100开始,则addr要0x801100.
三. .bss 段
没有被初始化的ram区全局或静态变量被分配到此段,在应用程序被执行前
的startup过程中这些变量被清零。
另外,.bss段有一个子段.noinit,若变量被指定到.noinit段中则在startup
过程中不会被清零。将变量指定到.noinit段的方法如下:
int foo_attribute_((section(".noinit")));
由于指定到了.noinit段中,所以不能赋初值,如同以下代码在编译时产生错误:
int fol_attribute_((section(".noinit")))=0x00ff;
利用.noinit段变量可以在抚慰时判断是否是上电复位,以下是一段示例代码:
#include
unsigned char rstflag[5]_attribute_((section(".noinit")));
int main(void)
{
unsigned char j;
for(j=0;j<5;j++)
{
if(rstflag[j]!=j)
break;
}
if(j<5)
{
//上电复位
for(j=0;j<5;j++)
rstflag[j]=j;
}
else
{
//其他复位源,说明ram数据没有丢失
}
.............
}
四. .eeprom段
此段存储eeprom变量。
static unsigned char eep_buff[3]_attribute_((section(".eeprom")))={1,2,3};
在连接选项中可指定段的开始地址,如下的选项将.noinit段指定位到ram存储器0x2000
地址处。
avr-gcc...-W1,--SECTION-start=.noinit=0x802000
要注意的是,在编译时Avr-gcc将FLASH,RAM ,EEPROM内的段在一个统一的地址空间
处理,FLASH存储器被定位到0地址开始处,RAM存储器被定位到0X800000开始处,
EEPROM存储器被定位到0X810000处。所以在指定段开始地址时若是RAM内的段或
EEPROM内的段时要在实际存储器地址前分别加上0X800000和0X810000。
除上述4个段外,自定义段因需要而可被定义。由于编译器不知道这类段的开始地址,
又称他们为未定义段。必须在连接选项中指定自定义段的开始地址。如下例:
void Mysection(void)_attribute_((section(".mysection")));
void MySection(void)
{
printf("helloavr!");
}
连接选项:
avr-gcc...-W1,--section-start=.mysection=0x001c00
这样函数MySection被定位到了FLASH存储器0x1c00处。
阅读(4339) | 评论(0) | 转发(0) |