精讀內核源碼就繞不過匯編語言,鴻蒙內核有6個匯編文件,讀不懂它們就真的很難理解以下問題.
1.系統調用是如何實現的?
2.CPU是如何切換任務和進程上下文的?
3.硬件中斷是如何處理的?
4.main函數到底是怎么來的?
5.開機最開始發生了什么?
6.關機最后的最后又發生了什么?
以下是一個很簡單的C文件編譯成匯編代碼后的注解.讀懂這些注解會發現匯編很可愛,甚至還會上癮,并沒有想象中的那么恐怖,讀懂它會顛覆你對匯編和棧的認知.
#include#include int square(int a,int b){ return a*b; } int fp(int b) { int a = 1; return square(a+b,a+b); } int main() { int sum = 1; for(int a = 0;a < 100; a++){ sum = sum + fp(a); } return sum; }
//編譯器: armv7-a clang (trunk) square(int, int): sub sp, sp, #8 @sp減去8,意思為給square分配??臻g,只用2個??臻g完成計算 str r0, [sp, #4] @第一個參數入棧 str r1, [sp] @第二個參數入棧 ldr r1, [sp, #4] @取出第一個參數給r1 ldr r2, [sp] @取出第二個參數給r2 mul r0, r1, r2 @執行a*b給R0,返回值的工作一直是交給R0的 add sp, sp, #8 @函數執行完了,要釋放申請的??臻g bx lr @子程序返回,等同于mov pc,lr,即跳到調用處 fp(int): push {r11, lr} @r11(fp)/lr入棧,保存調用者main的位置 mov r11, sp @r11用于保存sp值,函數棧開始位置 sub sp, sp, #8 @sp減去8,意思為給fp分配??臻g,只用2個??臻g完成計算 str r0, [sp, #4] @先保存參數值,放在SP+4,此時r0中存放的是參數 mov r0, #1 @r0=1 str r0, [sp] @再把1也保存在SP的位置 ldr r0, [sp] @把SP的值給R0 ldr r1, [sp, #4] @把SP+4的值給R1 add r1, r0, r1 @執行r1=a+b mov r0, r1 @r0=r1,用r0,r1傳參 bl square(int, int)@先mov lr, pc 再mov pc square(int, int) mov sp, r11 @函數執行完了,要釋放申請的??臻g pop {r11, lr} @彈出r11和lr,lr是專用標簽,彈出就自動復制給lr寄存器 bx lr @子程序返回,等同于mov pc,lr,即跳到調用處 main: push {r11, lr} @r11(fp)/lr入棧,保存調用者的位置 mov r11, sp @r11用于保存sp值,函數棧開始位置 sub sp, sp, #16 @sp減去8,意思為給main分配??臻g,只用2個??臻g完成計算 mov r0, #0 @初始化r0 str r0, [r11, #-4] @作用是保存SUM的初始值 str r0, [sp, #8] @sum將始終占用SP+8的位置 str r0, [sp, #4] @a將始終占用SP+4的位置 b .LBB1_1 @跳到循環開始位置 .LBB1_1: @循環開始位置入口 ldr r0, [sp, #4] @取出a的值給r0 cmp r0, #99 @跟99比較 bgt .LBB1_4 @大于99,跳出循環 mov pc .LBB1_4 b .LBB1_2 @繼續循環,直接 mov pc .LBB1_2 .LBB1_2: @符合循環條件入口 ldr r0, [sp, #8] @取出sum的值給r0,sp+8用于寫SUM的值 str r0, [sp] @先保存SUM的值,SP的位置用于讀SUM值 ldr r0, [sp, #4] @r0用于傳參,取出A的值給r0作為fp的參數 bl fp(int) @先mov lr, pc再mov pc fp(int) mov r1, r0 @fp的返回值為r0,保存到r1 ldr r0, [sp] @取出SUM的值 add r0, r0, r1 @計算新sum的值,由R0保存 str r0, [sp, #8] @將新sum保存到SP+8的位置 b .LBB1_3 @無條件跳轉,直接 mov pc .LBB1_3 .LBB1_3: @完成a++操作入口 ldr r0, [sp, #4] @SP+4中記錄是a的值,賦給r0 add r0, r0, #1 @r0增加1 str r0, [sp, #4] @把新的a值放回SP+4里去 b .LBB1_1 @跳轉到比較 a < 100 處 .LBB1_4: @循環結束入口 ldr r0, [sp, #8] @最后SUM的結果給R0,返回值的工作一直是交給R0的 mov sp, r11 @函數執行完了,要釋放申請的??臻g pop {r11, lr} @彈出r11和lr,lr是專用標簽,彈出就自動復制給lr寄存器 bx lr @子程序返回,跳轉到lr處等同于 MOV PC, LR
這個簡單的匯編并不是鴻蒙的匯編,只是先打個底,由淺入深, 但看懂了它基本理解鴻蒙匯編代碼沒有問題, 后續將詳細分析鴻蒙內核各個匯編文件的作用. 開始分析上面的匯編代碼.
第一:上面的代碼和鴻蒙內核用棧方式一樣,都采用了遞減滿棧的方式, 什么是遞減滿棧? 遞減指的是棧底地址高于棧頂地址,滿棧指的是SP指針永遠在棧頂.一定要理解遞減滿棧,否則讀不懂內核匯編代碼.舉例說明:
square(int, int): sub sp, sp, #8 @sp減去8,意思為給square分配??臻g,只用2個??臻g完成計算 str r0, [sp, #4] @第一個參數入棧 str r1, [sp] @第二個參數入棧 ldr r1, [sp, #4] @取出第一個參數給r1 ldr r2, [sp] @取出第二個參數給r2 mul r0, r1, r2 @執行a*b給R0,返回值的工作一直是交給R0的 add sp, sp, #8 @函數執行完了,要釋放申請的??臻g bx lr @子程序返回,等同于mov pc,lr,即跳到調用處
首句匯編的含義就是申請??臻g,sp = sp - 8,一個棧內單元(??臻g)占4個字節,申請2個??臻g搞定函數的計算,仔細看下代碼除了在函數的末尾sp = sp + 8又恢復在之前的位置的中間過程,SP的值是沒有任務變化,它的指向是不動的, 這跟很多人對棧的認知是不一樣的,它只是被用于計算,例如ldr r1, [sp, #4]的意思是取出SP+4這個虛擬地址的值給r1寄存器,SP的值并沒有改變的,為什么要+呢,因為SP是指向棧頂的,地址是最小的. 滿棧就是用棧過程中對地址的操作不能超過SP,所以你很少在計算過程中看到 把sp-4地址中的值給某個寄存器, 除非是特別的指令,否則不可能有這樣的指令.
第二:sub sp, sp, #8和add sp, sp, #8是成對出現的,這就跟申請內存,釋放內存的道理一樣,這是內核對任務的運行棧管理方式,一樣用多少申請多少,用完釋放.空間大小就是棧幀,這是棧幀的本質含義.
第三:push {r11, lr}和pop {r11, lr}也是成對出現的,主要是用于函數調用,例如 A -> B, B要保存A的棧幀范圍和指令位置, lr保存是是A函數執行到哪個指令的位置, r11干了fp的工作,其實就是指向 A的棧頂位置,如此B執行完后return回A的時候,先mov pc,lr 內核就知道改執行A的哪條指令了,同時又知道了A的棧頂位置.
第四:頻繁出現的R0寄存器的作用用于傳參和返回值, A調用B之前,假如有兩個參數,就把參數給r0 ,r1記錄,充當了A的變量, 到了B中后,先讓r0,r1入棧,目的是保存參數值, 因為 B中要用r0,r1,他們變成B的變量用了. 返回值都是默認統一給r0保存. B中將返回值給r0,回到A中取出R0值對A來說這就是B的返回值.
這是以上為匯編代碼的分析,追問兩個問題
第一:如果是可變參數怎么辦? 100個參數怎么整, 通過寄存器總共就12個,不夠傳參啊 第二:返回值可以有多個嗎?
編輯:hfy
-
寄存器
+關注
關注
30文章
5164瀏覽量
118135
發布評論請先 登錄
相關推薦
評論