ld 將這些資料塊正確移動到它們運行時的位址。此過程非常嚴格,資料的內部順序與長度均不能發生變化.這樣的資料單元叫做段,為段分配運行時位址叫再定位,此任務根據目標檔內的參考位址將段資料調整到運行時位址。
Avr-gcc 中彙編器生成的目標檔(object-file)至少包含四個段,分別為: .text 段、.data段、 .bss 段和.eeprom 段,它們包括了程式記憶體(FLASH)代碼,內部RAM 資料,和EEPROM 記憶體內的資料。這些段的大小決定了程式記憶體(FLASH)、資料記憶體(RAM)、EEPROM 記憶體的使用量,關係如下:
程式記憶體(FLASH)使用量 = .text + .data
資料記憶體(RAM)使用量 = .data + .bss [+ .noinit] + stack [+ heap]
EEPROM 記憶體使用量 = .eeprom
一..text 段
.text 段包含程式實際執行代碼。另外,此段還包含.initN 和.finiN 兩種段,下面詳細討論這兩種段。
段.initN 和段.finiN 是個程式塊,它不會象函數那樣返回,所以彙編或C 程式不能調用。.initN、.finN 和絕對段(absolute 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()。用戶可重載exit(),main 函數一旦退出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;
}
使用__arrribute__關鍵字為函數指定屬性要在函數的原型聲明上,而不是函數的實現例程裏。由於屬性section(“.init 1” )的指定,編譯後函數my_init_portb 生成的代碼自動插入到.init1 段中,在main 函數前就得到執行。naked 屬性確保編譯後該函數不生成返回指令,使下一個初始化段得以順序的執行。
二..data 段
.data 段包含程式中被初始化的RAM 區全局或靜態變數。而對於FLASH 記憶體此段包含在程式中定義變數的初始化資料。類似如下的代碼將生成.data 段數據。
char err_str[]=”Your program has died a horrible death!”;
struct point pt={1,1};
可以將.data 在SRAM 內的開始位址指定給連接器,這是通過給avr-gcc 命令行添加-Wl,-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 資料沒有丟失
}
… …
}
由於 rstflag 是.noinit 段變數,重定後avr-libc 應用框架不會初始化該變數,如果沒有掉電其內容應該保持,這便是我們判斷是否是掉電復位的依據。
四..eeprom 段
此段存儲 EEPROM 變數。
static unsigned char eep_buffer[3] __attribute__((section(“.eeprom”)))={1,2,3};
在連接選項中可指定段的開始位址,如下的選項將.noinit 段指定位到RAM 記憶體0X2000 地址處。
avr-gcc ... -Wl,--section-start=.noinit=0x802000
要注意的是,在編譯時 Avr-gcc 將FLASH、RAM 和EEPROM 內的段在一個統一的地址空間內處理,flash 記憶體被定位到0 位址開始處,RAM 記憶體被定位到0x800000 開始處,eeprom 記憶體被定位到0X810000 處。所以在指定段開始位址時若是RAM 內的段或eeprom 內的段時要在實際記憶體位址前分別加上0x800000 和0X810000。
除上述四個段外,自定義段因需要而可被定義。由於編譯器不知道這類段的開始位址,又稱它們為未定義段。必需在連接選項中指定自定義段的開始位址。如下例:
void MySection(void) __attribute__((section(".mysection")));
void MySection(void)
{
printf("hello avr!");
}
連接選項:
avr-gcc ... -Wl,--section-start=.mysection=0x 001c 00
這樣函數 MySection 被定位到了FLASH 記憶體0X 1C 00 處。
沒有留言:
張貼留言
注意:只有此網誌的成員可以留言。