關於嵌入式的bin、hex、axf、map

別在註釋裏陷得太深——註釋很可能會誤導你,你要調試的只是代碼。

前言

個人博客

所有文章除特別聲明外,均採用 CC BY-NC-SA 4.0 許可協議。轉載請註明來自 nixgnauhcuy’s blog

如需轉載,請標明出處!

記錄工作中學習到的知識,在這裏做些筆記,方便自己後面溫習。


bin、hex、axf

bin、hex、axf 之間的關係

bin hex axf
數據 數據 數據
地址 地址
調試信息

所以同一工程中 bin、hex、axf 的文件大小爲 .bin < .hex < .axf

假設 bin 文件是一個三無產品,那麼hex就是一個帶有信息的產品,而 axf 文件則是帶有信息並且附了一張使用說明的產品。(我也不知道這樣舉例合不合理,意思到位就行)

因爲 bin 文件沒有地址信息,而 hex 文件帶了地址信息,所以實際上我們使用燒錄軟件 (J-Flash.exe) 是這樣的:

導入 .bin 時,因爲沒有地址信息,所以我們需要爲它指定燒寫的起始地址

導入 .hex 時,因爲包含了地址信息,所以我們不需要指定起始地址,燒錄工具會自動讀取要燒錄的地址


bin 文件

上面已經說了 bin 文件起始不包含地址信息,所以 bin 文件只是單純的二進制文件,是沒有格式的程序文件,只是包含了程序數據。我們看到燒錄到單片機的是 .hex 文件,但是實際上,燒錄軟件會幫我們將 hex 文件的地址解析提取,最後還是燒錄的 .bin 文件

下面這張圖可以看出,J-Flash 解析 test.hex 地址信息後,實際上要燒錄的還是 test.bin 的內容:


hex 文件

Hex 是由 Intel 制定的一種十六進制標準文件格式,是由編譯器轉換而成的一種用於下載到處理器裏面的文件。

Hex 文件格式是由一行一行的十六進制數據組成,每行包含:開始、長度、數據、類型、校驗和等重要信息。

一般 Hex 文件的記錄格式如下:

Record mark Length Load offset Record type Info or Data Chksum
冒號 byte(1) byte(2~3) byte(4) byte(5~n) byte(n+1)
: 數據長度 起始地址 數據類型 數據 校驗和
  • Record mark:標記頭(數據頭)
  • Length:表示本行的數據長度(Info or Data)
  • Load offset:表示本行數據的起始地址
  • Record type:數據類型,共分:

0x00 - Data Rrecord,數據記錄
0x01 - End of FileRecord,用來標識文件結束,放在文件的最後,標識HEX文件的結尾
0x02 - Extended Segment Address Record,用來標識擴展段地址的記錄,擴展段地址記錄(HEX86),它包含4~19位數據地址段。由於普通的 Intel 的 HEX 記錄文件只能記錄 64K 的地址範圍,所以大於 64K 的地址數據要靠擴展段地址記錄
0x03 - Start Segment Address Record,開始段地址記錄
0x04 - Extended Linear Address Record,用來標識擴展線性地址的記錄,擴展線性地址記錄也叫 32 位地址記錄或者 HEX386 記錄,這些記錄包含了數據在存儲器裏真實地址的高 16 位。 當一個擴展線性地址記錄被讀取後,將一直保持有效,直到它被另一個擴展地址記錄改變。因爲它記錄的是後面數據在存儲器裏存放的真實起始地址,所以它的起始地址偏移量 (Load offset) 總是 0000
0x05 - Start Linear Address Record,開始線性地址記錄




  • Info or Data:數據代表一個字節的數據,一個記錄可以有許多數據字節,數據字節數量應等於 Length
  • Chksum:一個字節,先將此字節前所有字節相加得到 sum,校驗和=(0x100-sum & 0xFF) & 0xFF

說了這麼多,還是來實際驗證一下,這裏我貼出了test.hex的前3行和後3行,中間的其他內容省略:

:020000040002F8
:1060000070F60320A1640200A96402008D640200FE
:10601000AD640200AF640200B16402000000000041
...(略n行)
:0CA2F000FFBF915A010401789495F0051D
:040000050002620192
:00000001FF

:02|0000|04|0002|F8(頭):

  • 02 - 本行數據長度爲 2
  • 0000 - 本行數據起始地址偏移爲 0x0000
  • 04 - 數據類型是標識擴展線性地址的記錄
  • 0002 - 本行 2 個數據爲0x00和0x02,(這裏 0x0002<<16=0x00020000,爲基地址)
  • F8 - 校驗和=0x100-(0x02+0x00+0x00+0x04+0x00+0x02)&0xFF=0xF8

:10|6000|00|70F60320A1640200A96402008D640200|FE

  • 10 - 本行數據長度爲16
  • 6000 - 本行數據起始地址偏移爲0x6000,所以這裏記錄的地址是0x000020000+0x6000=0x00026000(這個可以看上面J-Flash,test.hex的起始地址剛好是0x26000)
  • 00 - 數據類型是標識擴展線性地址的記錄
  • 70F60320A1640200A96402008D640200 - 本行16個數據爲0x70,0xF6…0x02,0x00
  • FE - 校驗和=0x100-(0x10+0x60+0x00…+0x02+0x00)&0xFF=0xFE

:00|0000|01|FF(尾):

  • 00 - 本行數據長度爲0
  • 0000 - 本行數據起始地址偏移爲0x0000
  • 01 - 數據類型是標識文件結束
  • FF - 校驗和=0x100-(0x00+0x00+0x00+0x01)&0xFF=0xFF

從上面這些可以驗證出,hex 文件確實保存了 bin 文件的地址信息

實際上 hex 文件會大於 2 倍的 bin 文件大小的,bin 文件一個 byte 在 hex 文件中用 Ascill 編碼則需要用兩個字符來表示一個字節,而且 hex 又包括了其他信息,所以一般 hex > 2bin


axf 文件

axf,全稱 ARM Executable File,它是由 ARM 編譯器產生,除了包含 bin 的內容之外,還附加其他調試信息,這些調試信息加在可執行的二進制數據之前。調試時這些調試信息不會下載到 RAM 中,真正下載到 RAM 中的信息僅僅是可執行代碼。

調試信息作用:

  1. 可將源代碼包括註釋夾在反彙編代碼中,這樣我們可隨時切換到源代碼中進行調試
  2. 我們還可以對程序中的函數調用情況進行跟蹤(Keil可以通過Watch & Call Stack Window查看)
  3. 對變量進行跟蹤(Keil可以通過Watch & Call Stack Window查看)

axf 文件轉 bin 文件

fromelf 格式

fromelf [options] input_file  (命令的格式)

Options:
       --help         display this help screen (顯示幫助信息)
       --vsn          display version information (顯示版本信息)
       --output file  the output file. (defaults to stdout for -text format) (輸出文件(默認的輸出爲文本格式))
       --nodebug      do not put debug areas in the output image (在生成的映象中不包含調試信息)
       --nolinkview   do not put sections in the output image (在生成的映象中不包含段的信息)

Binary Output Formats:
       --bin          Plain Binary (生成Plain Binary格式的文件)
       --m32          Motorola 32 bit Hex (生成Motorola 32位十六進制格式的文件)
       --i32          Intel 32 bit Hex (生成Intel 32位十六進制格式的文件)
       --vhx          Byte Oriented Hex format (面向字節的位十六進制格式的文件t)

       --base addr    Optionally set base address for m32,i32 (設置m32,i32格式文件的基地址)

Output Formats Requiring Debug Information (需要調試信息的格式)
       --fieldoffsets Assembly Language Description of Structures/Classes (結構/類的彙編語言描述)
       --expandarrays Arrays inside and outside structures are expanded (擴展數組內部和外部結構被擴展)

Other Output Formats:
       --elf         ELF
       --text        Text Information (顯示文本信息)

                Flags for Text Information
                -v          verbose (打印詳細信息)
                -a          print data addresses (For images built with debug) (打印數據地址(針對帶調試信息的映象))
                -c          disassemble code (打印反彙編代碼)
                -d          print contents of data section (打印數據段的內容)
                -e          print exception tables (打印表達式表)
                -g          print debug tables (打印調試表)
                -r          print relocation information (打印重定位信息)
                -s          print symbol table (打印字符表)
                -t          print string table (打印字符串表)
                -y          print dynamic segment contents (打印動態段的內容)
                -z          print code and data size information (打印代碼和數據大小的信息)

將 axf 文件轉 bin 文件

在 Keil 的安裝目錄下,我的裝在了 C 盤,在 C:\Keil_v5\ARM\ARMCC\bin 中,可以找到 fromelf.exe 這個可執行文件

使用命令行工具,例如 win10 自帶的或者 git 等,這裏考慮大家不一定有其他命令行工具,所以直接用 win10 自帶的命令行。

win+R 打開運行,輸入 cmd 打開運行,進入 fromelf.exe 路徑

這裏我將要轉化的文件放在 F 盤 test 文件夾中

命令格式爲:

[fromelf.exe文件路徑] --bin -o [BIN路徑] [AXF文件路徑]

命令行輸入:

fromelf.exe --bin -o F:\test\test_bin_out.bin F:\test\test.axf

可以看到在輸出目錄 F:\test 中,將 axf 文件轉化生成爲了 bin 文件,並且輸出的 bin 文件和 keil 生成 bin 文件大小一致,說明是相同的文件

hex 文件和bin文件相互轉換

srecord 軟件

在找軟件時,在Keil官網看到 BINARY to Motorola S-Record Converter Utility

翻譯一下: srec_cat.exe 應用程序是 HEX2BIN,BIN2HEX,BIN2MOT 和 MOT2BIN 的絕佳替代品,用途更廣泛。該工具是在 sourceforge.net 上託管的 SRecord 項目的一部分。您可以從 https://sourceforge.net/projects/srecord/files/srecord-win32 下載。

後面我就下載下來研究了一下,發現可以替代 hex2bin 和 bin2hex,所以直接拿來使用。

hex 轉 bin

命令:

srec_cat.exe *.hex -intel -offset -0x00000 -o *.bin -binary

*.hex指定要轉換的hex文件
*.bin指定要輸出的文件名
-offset -0x00000這個指定偏移要根據工程指定來

這裏我的偏移是0x26000,所以我輸入的命令是:

srec_cat.exe test.hex -intel -offset -0x26000 -o test_out.bin -binary

上面轉 axf 時,已經有了 test 文件,這裏我把 srec_cat.exe 放在了 F:/test1/srecord 中,並且把要轉換的 test.hex 文件放在了 srecord 文件夾中

可以看到生成的 test_out.bin 和 keil 生成的 test.bin 文件大小一致。

bin 轉 hex

參考Sorting Intel HEX Files,這裏我的命令是:

srec_cat.exe test_out.bin -Binary -o test_hex_out.hex -intel -Output-Block-Size=16 -Disable_Sequence_Warnings

這裏實際上和 keil 生的 hex 文件有稍稍區別,因爲我試了多種方式,都沒有辦法和 Keil 生成文件一樣,雖然 binw 文件存儲的數據一樣,但是 crc 及偏移還是稍微有區別,

圖左邊是生成的hex文件,圖右邊是原先的hex文件

map

map 文件

map文件是我們通過編譯器編譯生成的映射文件,map文件包含了五個部分:

A. Section Cross References(模塊、段的交叉引用關係)
B. Removing Unused input sections from the image(移除未使用的段)
C. Image Symbol Table(映射符號表,列出了各個段所存儲的對應地址)
D. Memory Map of the image(內存(映射)分佈)
E. Image component sizes(映像組成大小)



這裏也提一下關於map的基本概念

section(段):描述映像文件的代碼和數據塊
RO:Read-Only的縮寫,包括RO-data(只讀數據)和RO-code(代碼)
RW:Read-Write的縮寫,主要是RW-data,Rw-data由程序初始化初始值
ZI:Zero-initialized的縮寫,主要是ZI-data,由編程器初始化爲0
.text:與RO-code同義
.constdata:與RO-data同義
.bss:與ZI-data同義,通常是指存放未初始化的全局變量的區域
.data:與RW-data同義






在Keil中,我們在 Project -> Options for Target -> Listing 中可以看到我們生成的map中鏈接的內容,

它包含了:

  • Memory Map:內存映射
  • Callgraph:圖像映射
  • Symbols:符號
  • Cross Reference:交叉引用
  • Size Info:大小信息
  • Totals Info:統計信息
  • Unused Section Info:未調用模塊(段)信息
  • Veneers Info:裝飾信息

Section Cross References

模塊、段的交叉引用關係:這裏主要是各個源文件之間生成的模塊、段之間相互引用的關係。

這裏我隨便找了個簡單的LED延時閃燈程序,編譯後打開生成的 map 文件

這裏指出了 test.c 中模塊調用其他模塊之間的關係:

test.o(i.main) refers to sys.o(i.Stm32_Clock_Init) for Stm32_Clock_Init
test.o(i.main) refers to delay.o(i.delay_init) for delay_init
test.o(i.main) refers to led.o(i.LED_Init) for LED_Init
test.o(i.main) refers to delay.o(i.delay_ms) for delay_ms


因爲我的 main 寫在 test.c 中,所以 test.c 中因爲 main 函數調用了 sys.c 中的 Stm32_Clock_Init 函數、調用了 delay.c 中的 delay_init 函數和 delay_ms 函數、調用了 led.c 的 LED_Init 函數,所以, 生成的 map 中 Section Cross References 指出了之前的相互引用關係。

Removing Unused input sections from the image

移除未使用的段:這個是我們工程中沒有被調用的模塊,會被編譯器移除並標識出來。

圖裏列出了被移除的模塊信息,例如

Removing delay.o(i.delay_us), (60 bytes).
Removing usart.o(i.uart_init), (220 bytes).
13 unused section(s) (total 786 bytes) removed from the image.

從上面 main 函數的執行中,可以看出我沒有調用 delay_us 和 uart_init 串口的初始化函數,所以編譯器把他們找出來並移除,共 13 個段沒有被調用,大小爲 786 字節。

Image Symbol Table

映射符號表,列出了各個段所存儲的對應地址

Image Symbol Table

    Local Symbols

    Symbol Name                              Value     Ov Type        Size  Object(Section)

    ../clib/angel/boardlib.s                 0x00000000   Number         0  boardinit3.o ABSOLUTE
    ....
    ..\Project\LED\led.c                     0x00000000   Number         0  led.o ABSOLUTE
    ..\SYSTEM\delay\delay.c                  0x00000000   Number         0  delay.o ABSOLUTE
    ..\SYSTEM\sys\sys.c                      0x00000000   Number         0  sys.o ABSOLUTE
    ..\SYSTEM\usart\usart.c                  0x00000000   Number         0  usart.o ABSOLUTE
    ..\\SYSTEM\\sys\\sys.c                   0x00000000   Number         0  sys.o ABSOLUTE
    ....
    RESET                                    0x08000000   Section      304  startup_stm32f10x_hd.o(RESET)
    !!!main                                  0x08000130   Section        8  __main.o(!!!main)
    ....
    fac_us                                   0x20000000   Data           1  delay.o(.data)
    fac_ms                                   0x20000002   Data           2  delay.o(.data)

    Global Symbols

    Symbol Name                              Value     Ov Type        Size  Object(Section)
    ....
    ....
    delay_init                               0x08000455   Thumb Code    56  delay.o(i.delay_init)
    delay_ms                                 0x08000495   Thumb Code    56  delay.o(i.delay_ms)
    main                                     0x080004d1   Thumb Code   114  test.o(i.main)
    LED_Init                                 0x08000269   Thumb Code    74  led.o(i.LED_Init)
    USART_RX_STA                             0x20000008   Data           2  usart.o(.data)
    USART_RX_BUF                             0x2000000c   Data         200  usart.o(.bss)

這裏我截取了一些 map 文件中 Image Symbol Table 的內容出來分析

Symbol 分爲兩類

Local Symbols(局部)
局部就是在函數內部用 static 聲明的變量,還有用 static 聲明的函數,基本上都是屬於局部,彙編文件裏面的變量如果作用域是本文件的就是局部。

Global Symbols(全局)
全局就是不是用 static 聲明的變量和函數,是 auto 聲明的全局變量和 C 文件函數就屬於全局。彙編文件裏面作用域是全工程的就是全局。

Symbol 符號名稱

Symbol Name:符號名

Value:存儲對應的地址

0x0800xxxx:指存儲在 flash 中的代碼和變量等
0x2000xxxx:指存儲在 RAM 中的變量 Data 等

Ov Type:符號類型

Number、Section、Thumb Code、Data等

Size:大小,指當前Symbol佔用的大小

Object(Section):段目標,指當前段所在的模塊及源文件

Memory Map of the image

內存(映射)分佈:映像文件可以分爲加載域(Load Region)和運行域(Execution Region):加載域反映了ARM可執行映像文件的各個段存放在存儲器中的位置關係

Memory Map of the image

  Image Entry point : 0x08000131

  Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00000578, Max: 0x00080000, ABSOLUTE)

    Execution Region ER_IROM1 (Base: 0x08000000, Size: 0x0000056c, Max: 0x00080000, ABSOLUTE)

    Base Addr    Size         Type   Attr      Idx    E Section Name        Object

    0x08000000   0x00000130   Data   RO            3    RESET               startup_stm32f10x_hd.o
    0x08000130   0x00000008   Code   RO          213  * !!!main             c_w.l(__main.o)
    ....
    ....
    
    Execution Region RW_IRAM1 (Base: 0x20000000, Size: 0x00000538, Max: 0x00010000, ABSOLUTE)

    Base Addr    Size         Type   Attr      Idx    E Section Name        Object

    0x20000000   0x00000004   Data   RW           49    .data               delay.o
    0x20000004   0x00000006   Data   RW          159    .data               usart.o
    ....
    ....

Image Entry point : 0x08000131:指映射入口地址。

Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00000578, Max: 0x00080000, ABSOLUTE):指加載域 LR_IROM1 起始地址爲 0x08000000,大小是 0x00000578,加載域最大大小爲 0x00080000

Execution Region ER_IROM1 (Base: 0x08000000, Size: 0x0000056c, Max: 0x00080000, ABSOLUTE)

Execution Region RW_IRAM1 (Base: 0x20000000, Size: 0x00000538, Max: 0x00010000, ABSOLUTE)

運行域 ER_IROM1 起始地址 0x08000000,大小是 0x0000056c,加載域最大大小爲 0x00080000

運行域 RW_IRAM1 起始地址 0x20000000,大小是 0x00000538,加載域最大大小爲 0x00010000

對應Keil中設定的IROM1和IRAM1:

Image component sizes

映像組成大小:各個映像模塊在各個文件中的代碼大小,主要就是對模塊進行彙總存儲大小信息

Image component sizes

      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name

       124         12          0          4          0       1196   delay.o
        84         10          0          0          0        435   led.o
    ....
    ....
==============================================================================


      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   

      1052        108        336         12       1324     215049   Grand Totals
      1052        108        336         12       1324     215049   ELF Image Totals
      1052        108        336         12          0          0   ROM Totals

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

    Total RO  Size (Code + RO Data)                 1388 (   1.36kB)
    Total RW  Size (RW Data + ZI Data)              1336 (   1.30kB)
    Total ROM Size (Code + RO Data + RW Data)       1400 (   1.37kB)

==============================================================================
Code RO Data RW Data ZI Data Debug Object Name
指代碼的大小 指除了內聯數據之外的常量數據 指可讀寫、已初始化的變量數據 指未初始化的變量數據 顯示調試數據佔用了多少字節 目標名

Total RW Size (RW Data + ZI Data) 1336 ( 1.30kB):是我們程序RAM所佔的字節總數,

Total ROM Size (Code + RO Data + RW Data) 1400 ( 1.37kB):是我們程序ROM所佔的字節總數,也就是我們程序所下載到ROM Flash中的大小。

寫的可能有點亂,因爲我晚上纔有空整理這些,上面這些是花了好幾個晚上才整理出來,所以有點亂也希望大家理解。有什麼錯誤也希望能在評論區指出,我會及時修改的!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章