ARM映像文件 he 編譯器自動生成的啟動函數

2020-12-25 電子產品世界
什麼是arm的映像文件

arm映像文件其實就是可執行文件,包括bin或hex兩種格式,可以直接燒到rom裡執行。在axd調試過程中,我們調試的是axf文件,其實這也是一種映像文件,它只是在bin文件中加了一個文件頭和一些調試信息。

可以參考下面的pdf:ARM映像文件及執行機理

part1 part3

映像文件的組成

ARM映像文件是一個層次性結構的文件,包括了域(region),輸出段(output section)和輸入段(input section)。

所謂域,指的就是整個bin映像文件所處在的區域,它又分為加載域和運行域。

加載域就是映像文件被靜態存放的工作區域,一般來說flash裡的 整個bin文件所在的地址空間就是加載域,當然在程序一般都不會放在 flash裡執行,一般都會搬到sdram裡運行工作,它們在被搬到sdram裡工作所處的地址空間就是運行域。

ARM映像文件一開始總是存儲在ROM/Flash裡面的,其RO部分既可以在ROM/Flash裡面執行,也可以轉移到速度更快的RAM中執行;而RW和ZI這兩部分是必須轉移到可寫的RAM裡去,其實RW包括ZI區域。

什麼是RO段、RW段和ZI段

一個ARM程序包含3部分:RO,RW和ZI

RO就是ReadOnly,程序中的指令和常量

RW就是Read/Write,程序中的已初始化變量

ZI就是Zero Init,程序中的未初始化的變量

Image文件包含了RO和RW數據。

之所以Image文件不包含ZI數據,是因為ZI數據都是0,沒必要包含,只要程序運行之前將ZI數據所在的區域一律清零即可。包含進去反而浪費存儲空間。

Q:為什麼Image中必須包含RO和RW?

A:因為RO中的指令和常量以及RW中初始化過的變量是不能像ZI那樣「無中生有」的。

ARM程序的執行過程

從以上兩點可以知道,燒錄到ROM中的image文件與實際運行時的ARM程序之間並不是完全一樣的。因此就有必要了解ARM程序是如何從ROM中的image到達實際運行狀態的。

實際上,RO中的指令至少應該有這樣的功能:

1. 將RW從ROM中搬到RAM中,因為RW是變量,變量不能存在ROM中。

2. 將ZI所在的RAM區域全部清零,因為ZI區域並不在Image中,所以需要程序根據編譯器給出的ZI地址及大小來將相應得RAM區域清零。ZI中也是變量,同理:變量不能存在ROM中

在程序運行的最初階段,RO中的指令完成了這兩項工作後C程序才能正常訪問變量。否則只能運行不含變量的代碼。

為了更直觀說明RO,RW,ZI在C中的意思,請看下面例子:

1)RO

看下面兩段程序,他們之間差了一條語句,這條語句就是聲明一個字符常量。因此按照我們之前說的,他們之間應該只會在RO數據中相差一個字節(字符常量為1位元組)。

Prog1:

#include

void main(void)

{

;

}

Prog2:

#include

const char a = 5;

void main(void)

{

;

}

Prog1編譯出來後的信息如下:

===========================================================

Code RO Data RW Data ZI Data Debug

948 60 0 96 0 Grand Totals

===========================================================

Total RO Size(Code + RO Data) 1008 ( 0.98kB)

Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)

Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)

===========================================================

Prog2編譯出來後的信息如下:

===========================================================

Code RO Data RW Data ZI Data Debug

948 61 0 96 0 Grand Totals

===========================================================

Total RO Size(Code + RO Data) 1009 ( 0.99kB)

Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)

Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)

===========================================================

以上兩個程序編譯出來後的信息可以看出:

Prog1和Prog2的RO包含了Code和RO Data兩類數據。他們的唯一區別就是Prog2的RO Data比Prog1多了1個字節。這正和之前的推測一致。

如果增加的是一條指令而不是一個常量,則結果應該是Code數據大小有差別。

2)RW

同樣再看兩個程序,他們之間只相差一個「已初始化的變量」,按照之前所講的,已初始化的變量應該是算在RW中的,所以兩個程序之間應該是RW大小有區別。

Prog3:

#include

void main(void)

{

;

}

Prog4:

#include

char a = 5;

void main(void)

{

;

}

Prog3編譯出來後的信息如下:

===========================================================

Code RO Data RW Data ZI Data Debug

948 60 0 96 0 Grand Totals

===========================================================

Total RO Size(Code + RO Data) 1008 ( 0.98kB)

Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)

Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)

===========================================================

Prog4編譯出來後的信息如下:

===========================================================

Code RO Data RW Data ZI Data Debug

948 60 1 96 0 Grand Totals

===========================================================

Total RO Size(Code + RO Data) 1008 ( 0.98kB)

Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)

Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)

===========================================================

可以看出Prog3和Prog4之間確實只有RW Data之間相差了1個字節,這個字節正是被初始化過的一個字符型變量「a」所引起的。

3) ZI

再看兩個程序,他們之間的差別是一個未初始化的變量「a」,從之前的了解中,應該可以推測,這兩個程序之間應該只有ZI大小有差別。

Prog5:

#include

void main(void)

{

;

}

Prog6:

#include

char a;

void main(void)

{

;

}

Prog5編譯出來後的信息如下:

===========================================================

Code RO Data RW Data ZI Data Debug

948 60 0 96 0 Grand Totals

===========================================================

Total RO Size(Code + RO Data) 1008 ( 0.98kB)

Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)

Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)

===========================================================

Prog6編譯出來後的信息如下:

===========================================================

Code RO Data RW Data ZI Data Debug

948 60 0 97 0 Grand Totals

===========================================================

Total RO Size(Code + RO Data) 1008 ( 0.98kB)

Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)

Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)

===========================================================

編譯的結果完全符合推測,只有ZI數據相差了1個字節。這個字節正是未初始化的一個字符型變量「a」所引起的。

注意:如果一個變量被初始化為0,則該變量的處理方法與未初始化華變量一樣放在ZI區域。



技術專區

相關焦點

  • ARM Linux根文件系統Root Filesystem的製作
    /etc是用來存放初始化腳本和其他配置文件的。關於初始化腳本的內容後面仔細說。/proc是用來掛載存放系統信息虛擬文件系統——「proc文件系統」,「proc文件系統」在內核裡面可以選。如果沒有「proc文件系統」,很多Shell自己的命令就沒有辦法運行,比如ifconfig。「proc文件系統」不像devfs可以自動掛載,它需要使用初始化腳本掛載。
  • 引用ARM開發,對entry point的含義
    答案是:由bootloader代碼來完成,進一步引出2個問題:1,誰來生成bootloader?2,怎麼把bootloader定位到硬體復位地址?2. 自己寫bootloader並通過scatter文件將之定位到硬體復位地址。或許某些開發工具會自動的為我們生成bootloader,這裡介紹的是自力更生的方法。
  • 高效的C編程之:C編譯器及其優化
    本章主要講解C編譯器在代碼優化時遇到的一些問題。要編寫高效的C語言原始碼,必須了解C編譯器對什麼形式的代碼有所改動,編譯器涉及的處理器結構的限制,以及一些特殊的C編譯器的限制。14.1.1為編譯器選擇處理器結構在編譯C源文件時,必須為編譯器指定正確的處理器類型。
  • 基於S3C2440的嵌入式Linux根文件系統構建
    3.3 編譯安裝busybox  在編譯之前,首先需要修改busybox根目錄下的Makefile,使用交叉編譯器。  將189行的ARCH?=$(SUBARCH)修改為ARCH?=arm,164行的CROSS_COMPILE?=修改為CROSS_COMPILE?=arm_linux-修改後執行make命令編譯busybox。
  • c編譯器so easy,gcc c編譯器生成、使用動靜態庫
    c編譯器作為常用軟體之一,並非具備無法逾越難度。對於c編譯器的學習,往往需要具備一定耐心。本文對c編譯器的講解基於gcc c編譯器,同時本文承接「c編譯器so easy,gcc c編譯器生成、使用動靜態庫(上篇)」一文而談,不了解的朋友可以先回顧一番哦。此外,本文主要內容為gcc生成靜態和動態連結庫的示例,一起來了解下吧。
  • 基於XIP技術的ARM Linux系統的研究與移植
    Kernel XIP 適用於作業系統啟動過程,而Application XIP 主要用於系統啟動後應用程式的執行。  Kernel XIP 原理如下,內核映像在Flash 設備上執行以後,只把映像中要讀寫的.data和.bss 拷貝到SDRAM 主存中,同時設置好系統的MMU,內核運行過程中,代碼段.text 指向Flash 空間,.data 和.bss 指向SDRAM 主存空間。
  • gcc、arm-linux-gcc和arm-elf-gcc的關係?
    \n"); return 0;}編譯命令為:gcc -o test test.c 編譯生成 test 可執行文件。gcc 編譯流程分為四個步驟:預處理、編譯 、彙編、連結。個人認為預處理和編譯主要由 gcc-core 來完成,彙編和連結主要由 Binutils 來完成。
  • CMake設置arm-linux-gcc交叉編譯器
    主機:Ubuntu10.04交叉編譯器:EABI-4.3.3本文引用地址:http://www.eepw.com.cn/article/201611/319327.
  • 小米大佬講解 Go 之編譯器原理
    編譯器原理詞法分析語法分析語義分析中間碼生成代碼優化機器碼生成總結認識 go build當我們敲下 go build 的時候,我們寫的源碼文件究竟經歷了哪些事情?首先先來認識一下go的代碼源文件分類命令源碼文件:簡單說就是含有 main 函數的那個文件,通常一個項目一個該文件,我也沒想過需要兩個命令源文件的項目測試源碼文件:就是我們寫的單元測試的代碼,都是以 _test.go 結尾庫源碼文件:沒有上面特徵的就是庫源碼文件,像我們使用的很多第三方包都屬於這部分go build 命令就是用來編譯這其中的
  • OpenCV ffmpeg移植到ARM平臺
    前一篇寫了如何在移植OpenCV庫到ARM平臺上,本來我只是用到OpenCV的打開USB攝像頭獲取圖像到IplImage結構中,這用到了V4L2的底層函數。然後進行視頻圖像的幀處理。那麼如何用OpenCV讀寫ARM板上的視頻文件,並進行視頻處理呢?這該又將如何移植呢?
  • ARM開發之source文件詳解
    第二類:Provided by IDE(Compiler)  第二類文件由IDE提供,C語言是編譯型語言,需要編譯器將C程序彙編成機器碼,所有便有了一些跟編譯器特性相關的函數庫。2.  編譯器函數庫是因IDE而異的,此處僅講一個例子以供參考,需要了解更多需查看各IDE手冊。
  • ARM/uClinux應用程式的開發
    返回來說說你的目標編譯器,雖然佔用了人家的地盤,編譯器,頭文件,庫文件,一個都不少,但你要編一個程序編譯器照樣發暈,因為沒有環境變量告訴它自己需要的頭文件和庫文件在哪裡。看來只有兩種辦法,一個是搶佔了主機的環境變量改成自己的(整個兒一個土匪),或者在編譯時加上必要參數(還是這樣紳士一些),告訴編譯器需要的文件的位置。(除此之外,還有其他一些參數也是如此)。
  • 基於busybox的嵌入式Linux根文件系統的的製作方法
    本文引用地址:http://www.eepw.com.cn/article/148616.htm  1 根文件  Linux要在一個分區上存放系統啟動所必需的文件,如內核映像文件、內核啟動後運行的第一個程序、給用戶提供操作界面的Shell程序、應用程式所依賴的庫等,這些必需、基本的文件合稱為根文件系統,它們存放在一個分區中。
  • 採用busybox的嵌入式Linux根文件系統的的製作方法
    本文引用地址:http://www.eepw.com.cn/article/151519.htm  1 根文件  Linux要在一個分區上存放系統啟動所必需的文件,如內核映像文件、內核啟動後運行的第一個程序、給用戶提供操作界面的Shell程序、應用程式所依賴的庫等,這些必需、基本的文件合稱為根文件系統,它們存放在一個分區中。
  • ARM-Linux開發與MCU開發的差別是什麼
    ARM-Linux:由於其沒有片內的flash, 並且需要運行作業系統,整個系統映像通常較大,故ARM-Linux開發的作業系統映像和應用通常存儲在外部的MMC、SD卡上,或者採用SATA設備等。 (5)啟動方式不同 單片機: 其結構簡單,內部集成flash, 通常是晶片廠商在程序上電時加入固定的跳轉指令,直接跳轉到程序入口(通常在flash上);開發的應用程式通過編譯器編譯,採用專用下載工具直接下載到相應的地址空間;所以系統上電後直接運行到相應的程序入口,實現系統的啟動。
  • S3C2410啟動代碼詳解(1)
    有些地方可能不正確,有待改正: 通常,啟動代碼是指CPU復位後到進入C語言的main函數之前需要執行的那段彙編代碼.這是由於C語言程序的運行需要具備一定的條件,比如:分配好外部數據空闓堆棧空間和中斷入口等篠另外彙編代碼可以更直接的對硬體進行操使效率更高.
  • xmake v2.3.8 發布, 新增 Intel C++/Fortran 編譯器支持
    $ xmakeWasm 平臺和 Qt/Wasm 支持上個版本,我們新增了 --toolchain=emcc 工具鏈來支持 wasm 程序的編譯,但是僅僅指定工具鏈,並不能很好的調整目標程序的擴展名,例如對 *.js 和 *.wasm 的文件生成
  • 實現一個簡單的編譯器
    編譯器完成編譯後,由 連結器(Linker) 將生成的目標文件連結成可執行文件,這一步並不是必須的,一些依賴於虛擬機運行的語言(如 Java,Erlang)就不需要連結。現在我們可以通過調用 Flex 生成 詞法分析器 的源碼:flex -o lexical.cpp lexical.l生成的 lexical.cpp 裡會有一個 yylex() 函數供 語法分析器 調用;你可能發現了,有些宏和變量並沒有被定義(如TEXTERN,yylval,yytext 等),其實有些是 Flex 會自動定義的內置變量(如 yytext
  • arm-linux-ld命令 ld連結腳本
    我們對每個c或者彙編文件進行單獨編譯,但是不去連接,生成很多.o 的文件,這些.o文件首先是分散的,我們首先要考慮的如何組合起來;其次,這些.o文件存在相互調用的關係;再者,我們最後生成的bin文件是要在硬體中運行的,每一部分放在什麼地址都要有仔細的說明。
  • DSP編程技巧之9-揭開編譯器神秘面紗之鉤子函數與庫函數
    鉤子函數可以被聲明為inline內聯類型的,此時編譯器把它們與其它的內聯函數按照相同的規則進行處理,例如函數優化等選項對它們起相同的作用。  4. 入口鉤子函數和出口鉤子函數是互相獨立的,我們可以只使用它們中的一個,或者同時使用它們。  5. 我們要避免對鉤子函數的遞歸調用,也就是說在鉤子函數中不要調用其它包含了對鉤子函數本身進行調用的函數(有點小拗口)。