1. 程式人生 > >GNU ARM彙編--(十二)arm彙編指令的B真的那麼簡單嗎?

GNU ARM彙編--(十二)arm彙編指令的B真的那麼簡單嗎?

             說句題外話,在輸入“指令”二字的時候,就想起了google搜尋時,提示“令”不能搜尋,要我換詞彙.如果不能說髒話,我真就無語了.

        在前面對具體晶片的各個基本模組做完了學習後,在上一篇小結中自以為已經具備了自己寫個bootloader的條件,但其實錯了,我還有很多基本的知識不瞭解.比如編譯連結\gnu的linker script等等.也有很多地方只懂表面,沒有做深入的理解.

        在《GNU ARM彙編--(二)彙編編譯連結與執行》中,仿照網上的例子做了makefile和linker script,在那篇blog的末尾我寫道“根據google,做了上面的總結,對GNU ARM彙編有了認識,並且對系統呼叫軟中斷,中斷處理,uboot異常向量表等等有了探究的慾望,也對elf格式和編譯連結有了興趣,根據自己的方向和精力,後續對這些內容做一個或深或淺的學習.

        當時看到了《linker and loader》,只是儲存了,當時並沒有細看,作為一個程式設計師,其實很多時候我們並不懂程式的細節.就像臺灣有個黑客關於“hello world”的分析,當初看了他的"hello world"系列ppt,就發現原來簡單的hello world裡面有這麼多不為人知的細節.

        這些天翻看了《linker and loader》的前面幾節,也翻了《程式設計師的自我修養--連結裝載和庫》,通讀了gnu.org的ld相關文件linker script.自己還對gnu的這個文件的大部分做了翻譯,在自己的筆記本上寫了好多頁,一根筆芯也用了大半,而且讓我有了久違寫字寫到手痠的感覺.覺得收穫不少.推薦去看看,值得的.

        廢話了這麼多,這篇blog我倒不想寫linker script或者ELF的一些細節.我想深究一下arm彙編指令中的B指令.

        很多網上的帖子都討論過arm彙編指令的B和LDR,這裡我按照我的思路來:

        首先翻一下《ARM Architecture Reference Manual》這份絕對權威的手冊:

        

        

        看完上面的英文,再結合下面的實際例子做個呼應:

程式碼如下:

_start: breset
......

reset:
        ......


反彙編如下:

10000000 <_start>:
10000000: ea00000e b 10000040 <reset>
......
10000040 <reset>:


對照彙編和反彙編的結果,一切正常.下面我們來看看ea00000e是如何表達b10000040 <reset>的?

31 28     27 26 25       24                  23 0
cond       1 0 1             L                    signed_immed_24

ea00000e轉換為二進位制後:

31 28     27 26 25       24                  23 0
1110      1    0   1         0                   0x00000e

cond是1110,即是條件執行的ALL,L為0表示指令是B,不是BL.signed_immed_24值為0x00000e.

signed表示有符號數.所以0x00000e就是+15,按照上圖的規則:

0x10000000+14<<2+8  =  0x10000040

這裡的0x10000000等地址都是VMA(虛擬記憶體地址),而B跳轉的地址是和PC相關的,所以這部分程式碼是與位置無關的.

注意上面等式中的+8與流水線有關.

B和BL的跳轉範圍是+-32MB,24bit的有符號數,也就是-2^23--2^23,即-8MB--8MB,因為有那個<<2,所以就是+-32MB了.

可以再看一個例子:

程式碼如下:

ledloop:


ldr r1,=0x1c0
str r1,[r2]
bl delay


ldr r1,=0x1a0
str r1,[r2]
bl delay


ldr r1,=0x160
str r1,[r2]
bl delay


ldr r1,=0x0e0
str r1,[r2]
bl delay

b ledloop


反彙編結果如下:

100002f0 <ledloop>:
100002f0: e3a01d07 mov r1, #448; 0x1c0
100002f4: e5821000 str r1, [r2]
100002f8: ebffffad bl 100001b4 <delay>
100002fc: e3a01e1a mov r1, #416; 0x1a0
10000300: e5821000 str r1, [r2]
10000304: ebffffaa bl 100001b4 <delay>
10000308: e3a01e16 mov r1, #352; 0x160
1000030c: e5821000 str r1, [r2]
10000310: ebffffa7 bl 100001b4 <delay>
10000314: e3a010e0 mov r1, #224; 0xe0
10000318: e5821000 str r1, [r2]
1000031c: ebffffa4 bl 100001b4 <delay>
10000320: eafffff2 b 100002f0 <ledloop>


同樣的:0x10000320+8+(-14)<<2 = 0x1000002f0

結合官方文件和兩個例項,才能真正明白B指令的一些細節.而不是一些中文書上所說的正確的但是表面的內容.

        這麼深入的分析指令B並不太麻煩,但是arm彙編有很多指令,不可能一一去分析.這裡分析指令B,只是因為跳轉指令在bootloader中很重要.而這裡給出了一個方法,如果對LDR這種指令不清楚的話,可以用同樣的方法來分析.