Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1339461
  • 博文数量: 92
  • 博客积分: 10389
  • 博客等级: 上将
  • 技术积分: 1918
  • 用 户 组: 普通用户
  • 注册时间: 2006-08-10 16:13
文章存档

2014年(1)

2012年(15)

2009年(6)

2008年(37)

2007年(72)

2006年(54)

我的朋友

分类:

2007-11-15 16:55:39

要成为专家就先用几年时间把这个领域做个大概的了解把,之后十年在专注其中各个部分
 
 
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处。
 

 
阅读(4350) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~