亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.430618.com 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

我們在寫單片機裸機程序時,在主函數之前,會有一段啟動代碼,而啟動代碼是用匯編寫的,有些朋友可能看到匯編頭都大了,當時要想深入研究底層架構,這快硬骨頭就必須去啃。

匯編:匯編文件轉換為目標文件(里面是機器碼)。

反匯編:可執行文件(目標文件,里面是機器碼),轉換為匯編文件。

關于匯編的基礎知識,請看筆者以前的文章。

今天筆者以stm32F1的點燈程序為例,帶領大家進行反匯編,并閱讀反匯編后的代碼。

1 新建LED裸機程序

關于STM32裸機程序的創建,請看筆者博文:

https://bruceou.blog.csdn.NET/article/details/78244735

但是今天這個程序非常簡單,不應那么復雜。

1.新建文件夾

新建文件夾“STM32F1”,當然名字也可以另取,在 STM32F1文件夾下,我們新建五個文件夾,分別為CMSIS、Listing、Output、Project、User。


 

其中CMSIS文件夾放啟動文件:


 

筆者的開發板芯片是STM32F103ZE,這個文件是根據STM32的固件庫startup_stm32f10x_md.s文件修改而來。

2.新建工程

打開Keil,在工具欄 Project->New μVision Project…新建我們的工程文件。


 

輸入工程名,保存即可。


 

窗口是讓我們選擇公司跟芯片的型號,我們用的芯片是 ST 公司的STM32F103ZE,有64K SRAM,512K Flash,屬于高集成度的芯片。按如下選擇即可。


 

然后點擊項目管理。


 

最后修改后的內容如下:


 

并添加相應的文件。


 

其中main.c的內容如下所示:

* @brief 延時函數* @param d* @retval Nonevoid delay(int d)while(d--);* @brief main* @param None* @retval intint main(void)unsigned int *pReg;/* 使能GPIOB */pReg = (unsigned int *)(0x40021000 + 0x18);*pReg |= (1<<3);/* 設置GPIOB0為輸出引腳 */pReg = (unsigned int *)(0x40010C00 + 0x00);*pReg |= (1<<0);pReg = (unsigned int *)(0x40010C00 + 0x0C);while (1)/* 設置GPIOB0輸出1 */*pReg |= (1<<0);delay(1000000);/* 設置GPIOB0輸出0 */*pReg &= ~(1<<0);delay(1000000);

startup.s文件的內容如下:

;************************************ STM32F1 ************************************;* File Name : startup.s;* Author : BruceOu;* Version : V1.0;* Date : 2021-06-27;* Description : STM32F10x Medium Density Devices Vector table for MDK-ARM;* toolchain.;* This module performs:;* - Set the initial SP;* - Set the initial PC == Reset_Handler;* - Set the vector table entries with the exceptions ISR address;* - Configure the clock system;* - Branches to __main in the C library (which eventually;* calls main()).;* After Reset the CortexM3 processor is in Thread mode,;* priority is Privileged, and the Stack is set to Main.PRESERVE8THUMB; Vector Table MApped to Address 0 at ResetAREA RESET, DATA, READONLYEXPORT __Vectors__Vectors DCD 0DCD Reset_Handler ; Reset HandlerAREA |.text|, CODE, READONLY; Reset handlerReset_Handler PROCEXPORT Reset_Handler [WEAK]IMPORT mainLDR SP, =(0x20000000+0x10000)BL mainENDP

接下來還有配置工程。


 

選擇Output文件夾。


 

選擇Listing文件夾。


 

基本配置就這些,接下來編譯工程。


 

只要沒有錯誤就可以了,最后就是下載程序,筆者使用的是J-Link,最后的現象如下:


 

LED會不停閃爍。

2 Keil反匯編

接下來才是今天正題,反匯編。

在KEIL的User選項中,如下圖添加這兩項:

fromelf --bin --output=STM32F1.bin ../Output/STM32F1.axffromelf --text -a -c --output=STM32F1.dis ../Output/STM32F1.axf

然后重新編譯,即可得到二進制文件STM32F1.bin(以后會分析)、反匯編文件STM32F1.dis。

如下圖所示:


 

正常編譯過程是分為四個階段進行的,即預處理(也稱預編譯,Preprocessing)、編譯(Compilation)、匯編 (Assembly)和鏈接(Linking)。

但是反編譯是講為二進制文件反編譯成匯編文件,因此反匯編的流程如下:


 

3反匯編代碼解析

接下來就是查看反編譯代碼,打開反編譯文件Project/STM32F1.dis。這里只截取一段查看,因為格式都是一樣的,知識每條內容不同罷了。


 

第一列是鏈接地址,第二列是機器碼,第三列是匯編指令。

根本匯編指令,我們找到ARM®v7-M Architecture Reference Manual_DDI 0403E.d (ID070218)中的LDR指令。


 

我們將F8DFD004變成二進制。


 

這個使用的32位的Thumb2指令集。


 

其中b0~b11是立即數,這里是4,對應的匯編代碼的也是4,這里要注意的是,ARM指令采用流水線機制,當前執行地址A的指令,同時已經在對下一條指令進行譯碼同時已經在讀取下下一條指令:PC = A +4 (Thumb/Thumb2指令集)。


 

B12~b15是寄存器,這段大小是0XC,對應的寄存器就是sp;


 

后面16bit除了23位意外,全是固定的,其中‘U’表示無條件執行,這里置為1。

其他的匯編指令對應的機器碼也是類似的,值得注意的是,不同的架構對應的機器碼也是不同的,這也就回答了為了不同的處理器架構會對應不同的指令集。

有興趣的可以對比Cortex-M系列和Cortex-A系列的的指令集。請參考以下手冊:

ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition.pdf

ARM®v7-M Architecture Reference Manual.pdf

4反匯編代碼全解析

進入debug模式,在View下選擇disassembly window。


 

這樣就可將機器碼和對應的代碼對應起來。當程序運行起來了,也就從異常向量表中跳轉到Reset_Handler中,然后跳轉到main函數中,而main函數是在棧中,因此需要設置占空間的起始位置。根據STM32的參考手冊,SRAM的其起始地址和大小如下:


 

因此棧頂為起始位置加上棧的大小即可,只要不超過SRAM即可。


 

值得注意的是,棧是向低地址擴展的數據結構,是一塊連續的內存區域,棧頂的地址和棧的最大容量是在通過LDR設置,因此需要根據應用需求合理分配棧空間。

接下來往下走,如果在匯編中不打斷點,會默認進入main函數的一條指令,就從這里分析。為了分析方便,這里還有使用上一節方便出來的文件。


 


 

【C代碼33行】

從內存地址0x0800 017c拷貝數據0x40021018到r3中,也就是

r3 = * 0x0800 017c

也就是將pReg指針保存到r3中。


 

【C代碼34行】

這里對應3條指令


 

首先將r3拷貝到r0中,然后將r0或上1左移3位,也就是

ORR r0,r0,#8

最后將r0的值寫入r3所指地址中。

【C代碼37行】

同33行,從內存地址0x0800 0180拷貝數據40010c00到r3中


 

【C代碼38行】

同34行,這里也對應3條指令:


 

【C代碼40行】

和33行不同的是,這里分了兩條指令:


 

筆者認為前面是編譯器優化了。根據ARM指令采用流水線機制,當前執行地址A的指令,同時已經在對下一條指令進行譯碼同時已經在讀取下下一條指令:PC = A +4 (Thumb/Thumb2指令集)。因此前面類似的代碼被優化了。

接下來就進入循環中。


 

后面就移植在死循環中,不斷操作GPIO的亮滅。

【C代碼45行】

這里是將B0設置為1,和34行類似。


 

【C代碼47行】

這里將進入延時函數。


 

進入延時函數:


 

NOP是字節對齊,減少指令的內存訪問次數。首先將變量d保存到r0,然后將r0賦給r1,接著是r0自減1,緊接著是r1與0比較,如果r1等于0,則會返回,否則,又從頭開始,值得注意的是,這里先比較,然后r0才自減的。

為了進一步說明,可以看--d的匯編代碼。


 

這里就是相當于r1先減1,然后再比較的。

【C代碼50行】

這行代碼對應一下指令,很簡單。


 

5總結

在前面使用Keil進行了反匯編,也對相應的C代碼進行了分析。我們看到的反匯編代碼如下:


 

根據反匯編的代碼,可將其對應到Flash,在Flash上的內容如下表所示:

地址

Flash內容

0x08000000

0x08000004

0x08000008

f8dfd004

0x0800000c

f000f80c

最后總結下點燈的流程:

第一步:設置棧:CPU會從0x08000000讀取值,用來設置SP。

第二步:跳轉:CPU從0x08000004得到地址值,根據它的BIT0切換為ARM狀態或Thumb狀態,然后跳轉。

第三步:對于cortex M3/M4,它只支持Thumb狀態,所以0x08000004上的值bit0必定是1,0x08000004上的值 = Reset_Handler + 1。從Reset_Handler繼續執行。

第四步:然后進入到主函數中執行相應C代碼

分享到:
標簽:反編譯
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定