淺析ARM架構(gòu)下的函數(shù)的調(diào)用過程

目錄1、背景知識1、ARM64寄存器介紹2、STP指令詳解(ARMV8手冊)

創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價比乾安網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式乾安網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋乾安地區(qū)。費用合理售后完善,10年實體公司更值得信賴。

我們先看一下指令格式(64bit),以及指令對于寄存機執(zhí)行結(jié)果的影響

類型1、STP <Xt1>, <Xt2>, [<Xn|SP>],#<imm>

將Xt1和Xt2存入Xn|SP對應(yīng)的地址內(nèi)存中,然后,將Xn|SP的地址變更為Xn|SP + imm偏移量的新地址

類型2、STP <Xt1>, <Xt2>, [<Xn|SP>, #<imm>]!

將Xt1和Xt2存入Xn|SP的地址自加imm對應(yīng)的地址內(nèi)存中,然后,將Xn|SP的地址變更為Xn|SP + imm的offset偏移量后的新地址

類型3、STP <Xt1>, <Xt2>, [<Xn|SP>{, #<imm>}]

將Xt1和Xt2存入Xn|SP的地址自加imm對應(yīng)的地址內(nèi)存中

手冊中有三種操作碼,我們只討論程序中涉及的后兩種

Pseudocode如下:

Shared decode for all encodingsinteger n = UInt(Rn);integer t = UInt(Rt);integer t2 = UInt(Rt2);if L:opc<0> == '01' || opc == '11' then UNDEFINED;integer scale = 2 + UInt(opc<1>);integer datasize = 8 << scale;bits(64) offset = LSL(SignExtend(imm7, 64), scale);boolean tag_checked = wback || n != 31;Operation for all encodingsbits(64) address;bits(datasize) data1;bits(datasize) data2;constant integer dbytes = datasize DIV 8;boolean rt_unknown = FALSE;if HaveMTEExt() then SetNotTagCheckedInstruction(!tag_checked);if wback && (t == n || t2 == n) && n != 31 then Constraint c = ConstrainUnpredictable(); assert c IN {Constraint_NONE, Constraint_UNKNOWN, Constraint_UNDEF, Constraint_NOP}; case c of when Constraint_NONE rt_unknown = FALSE; // value stored is pre-writeback when Constraint_UNKNOWN rt_unknown = TRUE; // value stored is UNKNOWN when Constraint_UNDEF UNDEFINED; when Constraint_NOP EndOfInstruction();if n == 31 then CheckSPAlignment(); address = SP[];else address = X[n];if !postindex then address = address + offset;if rt_unknown && t == n then data1 = bits(datasize) UNKNOWN;else data1 = X[t];if rt_unknown && t2 == n then data2 = bits(datasize) UNKNOWN;else data2 = X[t2];Mem[address, dbytes, AccType_NORMAL] = data1;Mem[address+dbytes, dbytes, AccType_NORMAL] = data2;if wback then if postindex then address = address + offset; if n == 31 then SP[] = address; else X[n] = address;

紅色部分對應(yīng)推棧的關(guān)鍵邏輯,其他匯編指令含義可自行參考armv8手冊或者度娘。

2、一個例子

熟悉了上面的部分,接下來我們看一個實例:

C代碼如下:

相關(guān)的幾個函數(shù)反匯編如下(和推棧相關(guān)的一般只有入口兩條指令):

main\f3\f4\strlen

我們通過gdb運行后,可以看到strlen地方會觸發(fā)SEGFAULT,引發(fā)進程掛掉

上述通過代碼編譯后,沒有strip,因此elf文件是帶著符號的

查看運行狀態(tài)(info register):關(guān)注$29、$30、SP、PC四個寄存器

一個核心的思想:CPU執(zhí)行的是指令而不是C代碼,函數(shù)調(diào)用和返回實際是在線程棧上面的壓棧和彈棧的過程

接下來我們來看上面的調(diào)用關(guān)系在當前這個任務(wù)棧是如何玩的:

函數(shù)調(diào)用在棧中的關(guān)系(call function壓棧,地址遞減;return彈棧,地址遞增):

以下是推棧的過程(劃重點)

再回頭來看之前的匯編:

main\f3\f4\strlen

從當前的sp開始,frame 0是strlen,這塊沒有開棧,因此上一級的調(diào)用函數(shù)仍然是x30,因此推導(dǎo):frame1調(diào)用為f3

函數(shù)f3的起始入口匯編:

(gdb) x/2i f3 0x400600 <f3>: stp x29, x30, [sp,#-48]! 0x400604 <f3+4>: mov x29, sp

可以看到,f3函數(shù)開辟的??臻g為48字節(jié),因此,倒推frame2的棧頂為當前的sp + 48字節(jié):0xfffffffff2c0

(gdb) x/gx 0xfffffffff2c0+80xfffffffff2c8: 0x000000000040065c(gdb) x/i 0x000000000040065c 0x40065c <f4+36>: mov w0, #0x0 // #0frame2的函數(shù)為sp+8:0x000000000040065c -> <f4+36>

繼續(xù)從sp = 0xfffffffff2c0倒推frame1的函數(shù)

函數(shù)f4的起始入口匯編為:

(gdb) x/2i f4 0x400638 <f4>: stp x29, x30, [sp,#-48]! 0x40063c <f4+4>: mov x29, sp

可以看到,f4函數(shù)開辟的??臻g也是為48字節(jié),因此,倒推frame3的棧頂為當前的0xfffffffff2c0 + 48字節(jié):0xfffffffff2f0

frame2的函數(shù)為0xfffffffff2c0 + 8:0x000000000040065c -> <f4+36>(gdb) x/gx 0xfffffffff2f0+80xfffffffff2f8: 0x0000000000400684(gdb) x/i 0x0000000000400684 0x400684 <main+28>: mov w0, #0x0 // #0

因此frame3的函數(shù)為main函數(shù),main函數(shù)對應(yīng)的棧頂為0xfffffffff320

至此推導(dǎo)結(jié)束(有興趣的同學(xué)可以繼續(xù)推導(dǎo),可以看到libc如何拉起main的過程)

總結(jié):

推棧的關(guān)鍵:

當前的現(xiàn)場 熟悉cpu體系架構(gòu)的開棧的方式3、實戰(zhàn)講解

現(xiàn)場有如下的core:可以看到,所有的符號找不到,加載了符號表依然不好使,解析不出來實際的調(diào)用棧

(gdb) bt#0 0x0000ffffaeb067bc in ?? () from /lib64/libc.so.6#1 0x0000aaaad15cf000 in ?? ()Backtrace stopped: previous frame inner to this frame (corrupt stack?)

先看info register,關(guān)注x29、x30、sp、pc四個寄存器的值

推導(dǎo)任務(wù)棧:

先將sp內(nèi)容導(dǎo)出:

下圖實際已先將結(jié)果標出,我們下面來詳細描述如何推導(dǎo)

pc代表當前執(zhí)行的函數(shù)指令,如果當前指令未開棧,一般情況x30代表上一級的frame調(diào)用當前函數(shù)的下一條指令,查看匯編,可以反解為如下函數(shù)

(gdb) x/i 0xaaaacd3de4fc 0xaaaacd3de4fc <PGXCNodeConnStr(char const*, int, char const*, char const*, char const*, char const*, int, char const*)+108>: mov x27, x0

找到棧頂函數(shù)后,查看該函數(shù)的棧操作:

(gdb) x/6i PGXCNodeConnStr 0xaaaacd3de490 <PGXCNodeConnStr(char const*, int, char const*, char const*, char const*, char const*, int, char const*)>: sub sp, sp, #0xd0 0xaaaacd3de494 <PGXCNodeConnStr(char const*, int, char const*, char const*, char const*, char const*, int, char const*)+4>: stp x29, x30, [sp,#80] 0xaaaacd3de498 <PGXCNodeConnStr(char const*, int, char const*, char const*, char const*, char const*, int, char const*)+8>: add x29, sp, #0x50

可以看到,上一級的frame存在了當前的sp + 0xd0 - 0x80也就是0xfffec4cebd40 + 0xd0 - 0x80 = 0xfffec4cebd90的地方,而棧底在0xfffec4cebd40+ 0xd0 = 0xfffec4cebe10的地方

因此就找到了下一級的frame對應(yīng)的棧頂和上一級的LR返回指令,反解,可以得到函數(shù)build_node_conn_str

(gdb) x/i 0x0000aaaacd414e08 0xaaaacd414e08 <build_node_conn_str(Oid, DatabasePool*)+224>: mov x21, x0

繼續(xù)重復(fù)上述推導(dǎo),可以看到這個函數(shù)build_node_conn_str開了176字節(jié)的棧,

(gdb) x/4i build_node_conn_str 0xaaaacd414d28 <build_node_conn_str(Oid, DatabasePool*)>: stp x29, x30, [sp,#-176]! 0xaaaacd414d2c <build_node_conn_str(Oid, DatabasePool*)+4>: mov x29, sp

因此繼續(xù)用0xfffec4cebe10 + 176 = 0xfffec4cebec0

查看調(diào)用者0xfffec4cebe10+8為reload_database_pools

繼續(xù)看reload_database_pools

(gdb) x/8i reload_database_pools 0xaaaacd4225e8 <reload_database_pools(PoolAgent*)>: sub sp, sp, #0x1c0 0xaaaacd4225ec <reload_database_pools(PoolAgent*)+4>: adrp x5, 0xaaaad15cf000 0xaaaacd4225f0 <reload_database_pools(PoolAgent*)+8>: adrp x3, 0xaaaacf0ed000 0xaaaacd4225f4 <reload_database_pools(PoolAgent*)+12>: adrp x4, 0xaaaaceeed000 <_ZN4llvm18ConvertUTF8toUTF16EPPKhS1_PPtS3_NS_15ConversionFlagsE> 0xaaaacd4225f8 <reload_database_pools(PoolAgent*)+16>: add x3, x3, #0x9e0 0xaaaacd4225fc <reload_database_pools(PoolAgent*)+20>: adrp x1, 0xaaaacf0ee000 <_ZZ25PoolManagerGetConnectionsP4ListS0_E8__func__+24> 0xaaaacd422600 <reload_database_pools(PoolAgent*)+24>: stp x29, x30, [sp,#-96]!

實際開棧0x220字節(jié),因此這一層frame的棧底為0xfffec4cebec0 + 0x220 = 0xfffec4cec0e0

因此得到基本的調(diào)用關(guān)系的結(jié)構(gòu)如下

以上基本可以夠用來分析問題了,因此不需要再繼續(xù)推導(dǎo)

TIPS:arm架構(gòu)下一般調(diào)用都會使用這種指令,

stp x29, x30, [sp,#immediate]! 有嘆號或者無嘆號

因此在每一層的frame都保存了上一層frame的棧頂?shù)刂泛蚅R指令,通過準確找到底層的frame 0棧頂后,就可以快速推導(dǎo)出所有的調(diào)用關(guān)系(紅色虛線圈出來的部分),函數(shù)的反解依賴符號表,只要原始的elf文件的symbol段沒有strip掉,是都可以找到對應(yīng)的函數(shù)符號(通過readelf -S查看即可)

找到Frame后,每一層frame里面的內(nèi)容,結(jié)合匯編基本就可以用來推導(dǎo)過程變量了。

以上就是淺析ARM架構(gòu)下的函數(shù)的調(diào)用過程的詳細內(nèi)容,更多關(guān)于ARM架構(gòu)下的函數(shù)的調(diào)用過程的資料請關(guān)注腳本之家其它相關(guān)文章!

當前標題:淺析ARM架構(gòu)下的函數(shù)的調(diào)用過程
網(wǎng)頁URL:http://bm7419.com/article2/coic.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供搜索引擎優(yōu)化、企業(yè)建站網(wǎng)站維護、外貿(mào)建站、云服務(wù)器、網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運營