我目前正在爲我的計算機組織課程中期學習,並在其上有一個部分,您需要閱讀並理解代碼才能找到某個寄存器的值。我只是想知道是否有什麼好的建議,我可以練習那個特定的部分。自己google搜索後,我似乎無法找到任何類似的東西,因此我想我會在這裏問。我可以練習閱讀手臂代碼的地方嗎?
另外我很抱歉,如果這是一個重複的問題,我環顧網站,並沒有發現這樣的事情,但如果有的話,它的鏈接將不勝感激。
謝謝您提前!
我目前正在爲我的計算機組織課程中期學習,並在其上有一個部分,您需要閱讀並理解代碼才能找到某個寄存器的值。我只是想知道是否有什麼好的建議,我可以練習那個特定的部分。自己google搜索後,我似乎無法找到任何類似的東西,因此我想我會在這裏問。我可以練習閱讀手臂代碼的地方嗎?
另外我很抱歉,如果這是一個重複的問題,我環顧網站,並沒有發現這樣的事情,但如果有的話,它的鏈接將不勝感激。
謝謝您提前!
也許你可以自己練習在ARM程序中做一些練習,以瞭解事情是如何工作的。例如,計算一個整數數組的總和,然後求平均值,找出最大的數等...
之後,您需要熟悉調用約定。因此,我建議你閱讀操作系統的一些代碼,比如樹莓派的DIY裸機操作系統。
最後,您可以練習ARM中的某些逆向工程/裂縫,而且您將做好準備!
本身的實踐非常棒,但我已經在ARM中製作了幾個程序,從簡單的字符串解析到統計元音到使用跳轉表來加密消息。我想我可以回頭看看他們,並真正熟悉他們,以確保我真的知道他們正在發生什麼。 感謝您的建議,但它真的很有幫助! – flip1012
我在獲取工具,只是嘗試陣營。
unsigned int fun (unsigned int a, unsigned int b)
{
return(a+b+1);
}
至於提到的問題是優化,如果你不優化你得到的東西是這樣的:
arm-none-eabi-gcc -c fun.c -o fun.o
arm-none-eabi-objdump -D fun.o
00000000 <fun>:
0: e52db004 push {r11} ; (str r11, [sp, #-4]!)
4: e28db000 add r11, sp, #0
8: e24dd00c sub sp, sp, #12
c: e50b0008 str r0, [r11, #-8]
10: e50b100c str r1, [r11, #-12]
14: e51b2008 ldr r2, [r11, #-8]
18: e51b300c ldr r3, [r11, #-12]
1c: e0823003 add r3, r2, r3
20: e2833001 add r3, r3, #1
24: e1a00003 mov r0, r3
28: e24bd000 sub sp, r11, #0
2c: e49db004 pop {r11} ; (ldr r11, [sp], #4)
30: e12fff1e bx lr
它實際上是相當可讀,但需要更多的工作比優化:
arm-none-eabi-gcc -O2 -c fun.c -o fun.o
arm-none-eabi-objdump -D fun.o
00000000 <fun>:
0: e2811001 add r1, r1, #1
4: e0810000 add r0, r1, r0
8: e12fff1e bx lr
你會開始感覺如何編寫簡單的代碼,不會被優化,這是另一個好的教訓海事組織。未優化的,至少在GCC的時候,會強烈地希望設置一個堆棧幀,然後它將把操作數傳遞並保存到堆棧中。它認爲它需要的任何本地或中間變量也會獲得堆棧空間。 C代碼的每一行都按照堆棧的順序分開處理,所以操作數將從堆棧中取出,並且結果會保存回來,即使它們直接返回到同一個變量中。因此,術語優化可以通過將東西保存在寄存器中來輕鬆刪除大量代碼。
您可以在沒有堆棧框架的情況下進行編譯,爲讀者提供一個練習,一個非常簡單的谷歌搜索。
如果你可以克服優化器(或者如果不優化,可以容忍所有堆棧的東西),你也可以開始看到調用約定的行動。
unsigned int more_fun (unsigned int, unsigned int);
unsigned int fun (unsigned int a, unsigned int b)
{
return(more_fun(a+1,b+2)+3);
}
0: e92d4010 push {r4, lr}
4: e2811002 add r1, r1, #2
8: e2800001 add r0, r0, #1
c: ebfffffe bl 0 <more_fun>
10: e8bd4010 pop {r4, lr}
14: e2800003 add r0, r0, #3
18: e12fff1e bx lr
即時1被添加到r0,因此必須是我們的第一個參數。 2與r1一起,第二個參數。三個被添加到r0從被調用的函數返回,所以r0必須在這樣的簡單函數返回的地方(64位返回值和結構和東西是讀者的練習,您還可以閱讀推薦的調用約定手臂,但1)編譯器可以做任何他們想做的事2)有時候編譯一個函數和反彙編更容易)。
這裏的另一個謎是爲什麼r4被推?或者,也許你的編譯器將r3或其他寄存器與lr一起推入,或者它可能不會。另一個SO問題多次問及沒有找到答案(因爲很難搜索)。 ARM建議保持堆棧64位對齊,在這種情況下,r4只是任意的寄存器,可能是其中任何一個只需要按兩個並彈出兩個。那麼爲什麼在之前的那個上他們沒有用r11推另一個寄存器呢?很明顯,gnu沒有看到在中斷期間需要擔心堆棧對齊,並且兩個堆棧調整在構建堆棧幀時進行補償以使其與64位對齊,您只需在幾條指令中進行中斷曝光。我不知道ARM對此提出的建議。
你可以將任何大小的任何項目的所有現有代碼都讀取出來,並嘗試讀取反彙編,這取決於你如何構建最終的堆棧內容,比如未優化的代碼。你可能會得到很多很好的優化,將立即載入到寄存器中,這些寄存器不能在一條指令中完成,但不想刻錄pc的相對負載。也許這正是你所追求的,真實世界的代碼是什麼樣子,你可以閱讀它。你會希望它的未優化結尾有更多的代碼,但是從C到機器代碼是有點線性的。優化代碼,重新排序操作,徹底清除代碼,與立即數和其他數學相關的技巧,以及尾部優化等等。然後,如果你冒險進入混合ARM /拇指更多的樂趣發生。您添加浮點更有趣。
沒有理由期待任何兩個不同的品牌編譯器(gnu,llvm等)從相同的輸入中產生相同的輸出,也沒有理由期望同一品牌的任何兩個版本(因爲缺乏更好的使用相同的命令行選項從相同的源生成相同的結果。所以再次增加樂趣。
底線工具一直存在,只是使用它們的問題。
下一次您看到C或C++函數並且認爲「我不知道如何編譯ARM」,請將其放在http://gcc.godbolt.org/上,並選擇ARM gcc作爲編譯器。使用'-O3'來獲得優化的代碼。如果你願意,可以使用-fverbose-asm來幫助你遵循代碼。作爲獎勵,Godbolt使它非常容易與x86,MIPS和PowerPC進行比較。 (並在x86上使用clang或ICC)。隨着最近的UI改進,您甚至可以一次打開兩個不同的編譯器輸出子窗口! –
@PeterCordes OP也可以在他的電腦上安裝ARM工具鏈。這具有在安裝後不需要互聯網訪問的優點。 – fuz
@FUZxxl:是的,但是你需要自己去噪聲gcc輸出:http://stackoverflow.com/questions/38552116/how-to-remove-noise-from-gcc-clang-assembly-output。不過,如果你想在某個地方的筆記本電腦上這樣做,這個建議很好。如果您想嘗試修改asm以查看它是否在您嘗試優化之後仍然有效,那也是很好的方法。 (例如,在非ARM Linux系統上使用用戶模式QEMU來單步執行一個使用gdb的ARM二進制文件,[像我這裏描述的那樣](http://stackoverflow.com/questions/39503997/how-to-run-一個單線路的組裝 - 則 - 見-R1-和輸入條件標誌))。 –