新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > ARM芯片詳解翻譯

        ARM芯片詳解翻譯

        作者: 時間:2016-11-20 來源:網絡 收藏
        譯者注:這篇文章主要介紹了Risc結構的PDA芯片組成和匯編程序,翻譯不周,肯定有錯誤,請多包涵,另外我忘記了出處,這里向作者表示歉意。

        RISC處理器被廣泛應用在小型設備上,例如PDA,移動電話,智能熱水器等。有很多關于RISC處理器的匯編程序,但最常見的還是ARM
        下面我要談的是ARM7,因為我研究的是這個。
        讓我們先了解一下ARM的架構。ARM處理器包含37個寄存器:31個通用的32位寄存器,以及6個狀態寄存器。寄存器的設置取決于處理器狀態。ARM狀態執行32位指令,Thumb狀態執行16位指令集。
        在ARM狀態,有18個寄存器可用:可供直接存儲的R0―R15,CPSR(當前程序狀態寄存器),SPSR(被存儲程序狀態)。其中3個可直接存儲器被稱為服務寄存器。

        (R13)SP??堆棧指針
        (R14)LR??連接寄存器,用來存儲調用過程的函數地址(譯注:可簡單理解為過程返回地址)。并且,LR并非存儲在堆棧中-它存在于寄存器中。
        (R15)PC??當前指令指針。用一般的mov指令就可以改變它的值,從而執行它所指向的命令。

        在Thumb狀態,有13個寄存器可用:R0-R8,R13-R15,CPSR,SPSR

        狀態的改變,不會影響寄存器內容的變化。
        如果想進入Thumb狀態,可以先將操作寄存器的狀態位設為1(bit1),然后執行BX指令。如果想進入ARM(譯注:原文誤為APM)狀態,可以先將操作寄存器的狀態位設為0(bit0),然后執行BX指令。
        2種狀態的指令集是不同的,但是很多指令都是類似的。Thumb指令集長度為2bytes,ARM-4bytes。關于2種狀態指令的具體資料可以參考:http://www.atmel.com/dyn/resources/p...ts/doc0673.pdf
        有趣的是很多指令可以同時操作多個寄存器。例如:
        ADDR3,SP,#4相當于: R3:=SP+4
        或者,用來存儲寄存器入棧的指令:
        PUSH{R2-R4,R7,LR} 這和x86匯編里面的pushad指令不同,在ARM匯編里面,這種將寄存器存入堆棧的方式是可行的。
        內存中,數據存儲方式可以是低位存儲(例如Intel寄存器)或者高位存儲(例如Motorola寄存器)。所以,寫代碼時候,有必要指明數據存放方式。
        下面是一些ARM編譯器的資料:
        http://heanet.dl.sourceforge.net/sou...de-arm-win.exe-GNUcompilerwithallconsequences-allthroughcommandline+debuggingthroughgdb.

        http://www.goldroad.co.uk/grARM.html-unpretentiousARMassembler.

        http://www.arm.com/support/downloads/index.html-officialtoolsforARM’sdevelpment.Hereyoucanonlybuythem.

        http://www.iar.com/-alternativetoIDAforARM.30-daystrialversionisoffered.
        下面講解一下由C++的ARM編譯器生成的ARM匯編程序。
        一般地,分析不同程序的時候,經常碰到的并不是純粹的匯編語言,而是由C++編譯器生成的代碼。當然,x86匯編程序員一般不會如此。
        函數調用:
        這里不存在函數參數調用約定(例如cdecl,stdcall等)!所有的函數調用約定類似于Borland的fastcall。參數由寄存器傳入,如果數目不夠,由堆棧傳入。
        例如:
        ROM:0001F4E2MOVR0,SP

        ROM:0001F4E4MOVR2,*6

        ROM:0001F4E6ADDR1,R4,*0

        ROM:0001F4E8BLmemcmp
        參數的傳遞順序對應于寄存器編號,R0為第一個,R1為第二個,R2為第三個(譯注:比較有意思)。相當于:
        intmemcmp(

        constvoid*buf1,

        constvoid*buf2,

        size_tcount

        );
        buf1=R0

        buf2=R1

        count=R2

        函數返回值被存放在R0中:
        ROM:0001F4E2MOVR0,SP

        ROM:0001F4E4MOVR2,*6

        ROM:0001F4E6ADDR1,R4,*0

        ROM:0001F4E8BLmemcmp

        ROM:0001F4ECCMPR0,*0

        ROM:0001F4EEBNEloc_1F4F4
        下面是一個利用堆棧傳遞參數的例子:
        ROM:000BCDECMOVR2,*0

        ROM:000BCDEESTRR2,[SP]

        ROM:000BCDF0MOVR2,*128

        ROM:000BCDF2MOVR3,*128

        ROM:000BCDF4MOVR1,*14

        ROM:000BCDF6MOVR0,*0

        ROM:000BCDF8BLFillBoxColor
        上面,R0-R3存儲坐標,第5個參數(色彩)被存放在堆棧中。

        只有通過分析才可以確定操作數的數目。我們可以分析函數和它的調用部分。有時候,參數信息可以通過對寄存器和堆棧的操作觀察出來。例如,在Thumb狀態下,程序對R0-R7和服務寄存器的操作。所以,如果看到類似于下面的代碼:
        ROM:00059ADAgetTextBounds

        ROM:00059ADAPUSH{R4-R7,LR},
        可以認為它的參數被存放在R0,R1,R2,R3和SP。如果見到:
        ROM:0005924EADDR0,SP,*0x14

        ROM:00059250ADDR1,SP,*0x6C

        ROM:00059252ADDR2,SP,*0x68

        ROM:00059254ADDR3,SP,*0x64

        ROM:00059256BLgetTextBounds
        我們看到只有R0-R3被使用,就是說只有4個參數被傳遞過來。

        轉移(Transitions)
        一般,轉移分為條件轉移和無條件轉移。轉移目標可以存放在寄存器或者其他處。寄存器轉移一般用于Thumb/ARM狀態轉換。無條件短轉移指令為B(branch)命令。長跳轉指令-BX(交換轉移)。函數調用采用BL(連接轉移),且調用時將返回地址存入LR寄存器。當然,改變PC寄存器內容也可以改變轉移地址:
        ADDPC,*0x64
        但是C編譯器通常不這樣處理,它們在轉移的時候,只是以寫入命令改變PC寄存器。

        分支(Branches)
        也稱為轉換,一般用法如下:
        ROM:0027806ECMPR2,*0x4D;M

        ROM:00278070BCSloc_27807A

        ROM:00278072ADRR3,word_27807C

        ROM:00278074ADDR3,R3,R2

        ROM:00278076LDRHR3,[R3,R2]

        ROM:00278078ADDPC,R3

        ROM:0027807A

        ROM:0027807Aloc_27807A

        ROM:0027807ABloc_278766

        ROM:0027807Cword_27807CDCW0xAA,0xBE,0xC6,0x180,0x186;0

        ROM:0027807CDCW0x190,0x1A0,0x1A8,0x1DE,0x1E4;5

        ROM:0027807CDCW0x1B0,0x212,0x276,0x1FE,0x294;10
        首先,檢查跳轉標記,該標記必須小于0x40,如果大于,則跳到默認處理位置,即:loc_27807A。
        然后執行位于word_27807C的轉移控制表。這個表里面存放的是偏移,并非地址。隨后,根據跳轉標記,取表中的偏移,擴展之,加操作放入PC寄存器。比如,如果跳轉標記為0,將會跳轉到地址:
        0x278078(currentvaluePC)+0xAA(offsetfromthetable)+0x4(!!!)=0x278126
        之所以加4,是因為ARM處理器的特征:操作PC寄存器時,其值應該比預先確定的數值大4(在文檔“toensureitiswordaligned”中有說明)。

        內存存取

        在Thumb狀態,處理器可以存取+/-256字節的空間。因此,無法直接存取內存,而需要利用寄存器來引導。也就是無法直接定位到0x974170,而需要采用寄存器。例如:
        ROM:00277FF6LDRR0,=unk_974170

        ROM:00277FF8LDRR0,[R0]
        我們獲得了0x974170處的數據,但是事情還沒有結束!該有效地址(0x974170)處于有效的正負256字節中:
        ROM:00278044off_278044DCDunk_974170
        這樣,就是說,LDR指令的機器碼中存儲了該命令當前的地址。(譯注:就是說0x974170雖然看起來比較大,實際上還是那+-256字節內,只不過通過LDR指令來定位)
        這里存在一個很藝術的優化方法:如果一個地址和該函數中另外一個被用到的地址有關聯,那么這個地址可以通過算術運算指令或者間接存取來獲取。舉例來說,如果一個函數需要用到0x100000處的變量,并且需要用到0x100150處的另外一個變量,那么,編譯器可以將這2個變量建立關聯,或者采用以下代碼:
        LDRR0,=0x100000

        ADDR0,*0xFF

        ADDR0,*0x51

        LDRR0,[R0]
        在x86里面,這種方法應用于結構中獲取子結構接口。但是此處,卻是一個常用的優化,這有什么好處呢?可以減小內存存儲,并且算術運算比數據加載快得多??梢哉J為整個ARM匯編程序充滿了不同的寄存器間算術運算。事實上,有多達16個寄存器用來進行此操作-減少內存和堆棧定位頻度。因此,只有在非常大的函數中才需要用堆棧存儲變量。對堆棧的操作和x86處理堆棧的方式一樣。

        IDA中的代碼分析

        既然ARM文件沒有統一格式,那么在加載ARM二進制映像的時候,有必要先加載該文件。在加載的時候,需要確定處理器類型。如果處理器規定代碼必須按照處理器模塊處理順序,那么你可以加載映像文件并且指定需要的處理方式,ARM處理方式(低位處理)或者ARMB(高位處理)。并且,有必要建立ROM或者RAM段。總之沒有固定的處理方式,具體的處理有賴于映像和每個ARM處理器的架構。例如,在ARM7中,內存一般有如下格式:
        0x0-0x8000ofRAMprocessor

        0x8000-0x1000000ROM

        0x1000000-0x.....-SRAM(這里看出自身數目)
        現在就可以分析代碼了,在很多設備中(一般都是移動電話),代碼的入口設定為0x8000。ARM模式下的代碼從0x8000開始執行,所以,開始執行的指令和該處的一樣。處理器的IDA模塊可以簡單地分析此類switching語句,然后Thumb代碼在ARM中執行。如果手工修改跳轉,可以按ALT-G,然后修改文件中的標記,如果為ARM文件,設為0,Thumb文件,設為1。


        關鍵詞: ARM芯片詳

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 阿瓦提县| 易门县| 宁武县| 犍为县| 铜山县| 成安县| 三都| 萝北县| 鹿泉市| 郓城县| 聂荣县| 景宁| 湖口县| 武陟县| 汾阳市| 常山县| 专栏| 社会| 西安市| 江孜县| 大荔县| 拉萨市| 巫溪县| 南漳县| 锦州市| 稷山县| 高雄县| 禹州市| 鹰潭市| 亚东县| 监利县| 连江县| 台北县| 江北区| 宜都市| 河池市| 宁河县| 栾城县| 徐水县| 土默特右旗| 福清市|