最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2021年05月20日 09:26:12   作者:華為云開發(fā)者社區(qū)  
本文主要介紹了linux程序運(yùn)行的狀態(tài)以及如何推導(dǎo)調(diào)用棧。

1、背景知識(shí)

1、ARM64寄存器介紹

2、STP指令詳解(ARMV8手冊(cè))

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

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

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

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

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

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

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

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

Pseudocode如下:

Shared decode for all encodings
integer 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 encodings
bits(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;

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

2、一個(gè)例子

熟悉了上面的部分,接下來(lái)我們看一個(gè)實(shí)例:

C代碼如下:

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

main\f3\f4\strlen

我們通過(guò)gdb運(yùn)行后,可以看到strlen地方會(huì)觸發(fā)SEGFAULT,引發(fā)進(jìn)程掛掉

上述通過(guò)代碼編譯后,沒有strip,因此elf文件是帶著符號(hào)的

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

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

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

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

以下是推棧的過(guò)程(劃重點(diǎn))

再回頭來(lái)看之前的匯編:

main\f3\f4\strlen

從當(dāng)前的sp開始,frame 0是strlen,這塊沒有開棧,因此上一級(jí)的調(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的棧頂為當(dāng)前的sp + 48字節(jié):0xfffffffff2c0

(gdb) x/gx 0xfffffffff2c0+8
0xfffffffff2c8:    0x000000000040065c
(gdb) x/i 0x000000000040065c
   0x40065c <f4+36>:    mov w0, #0x0                       // #0
frame2的函數(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的棧頂為當(dāng)前的0xfffffffff2c0 + 48字節(jié):0xfffffffff2f0

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

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

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

總結(jié):

推棧的關(guān)鍵:

  • 當(dāng)前的現(xiàn)場(chǎng)
  • 熟悉cpu體系架構(gòu)的開棧的方式

3、實(shí)戰(zhàn)講解

現(xiàn)場(chǎng)有如下的core:可以看到,所有的符號(hào)找不到,加載了符號(hào)表依然不好使,解析不出來(lái)實(shí)際的調(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四個(gè)寄存器的值

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

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

下圖實(shí)際已先將結(jié)果標(biāo)出,我們下面來(lái)詳細(xì)描述如何推導(dǎo)

pc代表當(dāng)前執(zhí)行的函數(shù)指令,如果當(dāng)前指令未開棧,一般情況x30代表上一級(jí)的frame調(diào)用當(dāng)前函數(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

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

因此就找到了下一級(jí)的frame對(duì)應(yīng)的棧頂和上一級(jí)的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),可以看到這個(gè)函數(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]!

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

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

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

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

stp x29, x30, [sp,#immediate]! 有嘆號(hào)或者無(wú)嘆號(hào)

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

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

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

相關(guān)文章

  • linux mint 18虛擬機(jī)下設(shè)置1080P分辨率的方法

    linux mint 18虛擬機(jī)下設(shè)置1080P分辨率的方法

    下面小編就為大家?guī)?lái)一篇linux mint 18虛擬機(jī)下設(shè)置1080P分辨率的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-12-12
  • Linux之軟件包管理器yum詳解

    Linux之軟件包管理器yum詳解

    文章介紹了現(xiàn)代類Unix操作系統(tǒng)中軟件包管理和包存儲(chǔ)庫(kù)的工作原理,以及如何使用包管理器如yum來(lái)安裝、更新和卸載軟件,文章還介紹了如何配置yum源,更新系統(tǒng)軟件包和查看已安裝的軟件
    2025-01-01
  • linux停止和查看啟動(dòng)服務(wù)的命令使用方法

    linux停止和查看啟動(dòng)服務(wù)的命令使用方法

    這篇文章主要介紹了linux停止和查看啟動(dòng)服務(wù)的命令使用方法,需要的朋友可以參考下
    2014-02-02
  • CentOS 7.2 安裝MariaDB詳細(xì)過(guò)程

    CentOS 7.2 安裝MariaDB詳細(xì)過(guò)程

    這篇文章主要為大家介紹了CentOS 7.2 安裝MariaDBMariaDB數(shù)據(jù)庫(kù)管理系統(tǒng)詳細(xì)過(guò)程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • linux下多個(gè)硬盤劃分到同一掛載點(diǎn)問題

    linux下多個(gè)硬盤劃分到同一掛載點(diǎn)問題

    在Linux系統(tǒng)中,將多個(gè)硬盤劃分到同一掛載點(diǎn)需要通過(guò)邏輯卷管理(LVM)來(lái)實(shí)現(xiàn),首先,需要將物理存儲(chǔ)設(shè)備(如硬盤分區(qū))創(chuàng)建為物理卷,然后,將這些物理卷組成邏輯卷組,接著,從邏輯卷組中劃分出邏輯卷,并進(jìn)行格式化和掛載
    2025-02-02
  • 詳解Linux time 命令的使用

    詳解Linux time 命令的使用

    這篇文章主要介紹了詳解Linux time 命令的使用,幫助大家更好的理解和學(xué)習(xí)linux,感興趣的朋友可以了解下
    2020-08-08
  • centos iso鏡像文件安裝教程

    centos iso鏡像文件安裝教程

    這篇文章主要為大家詳細(xì)介紹了centos iso鏡像文件安裝教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • Centos7.2 系統(tǒng)上yum安裝lamp環(huán)境

    Centos7.2 系統(tǒng)上yum安裝lamp環(huán)境

    這篇文章主要介紹了centos7.2 系統(tǒng)上yum安裝lamp環(huán)境的相關(guān)資料,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-11-11
  • 關(guān)于Linux的mariadb數(shù)據(jù)庫(kù)

    關(guān)于Linux的mariadb數(shù)據(jù)庫(kù)

    這篇文章主要介紹了關(guān)于Linux的mariadb數(shù)據(jù)庫(kù)的相關(guān)資料,主要就是用戶可以對(duì)文件中的數(shù)據(jù)進(jìn)行新增、截取、更新、刪除等操作,需要的朋友可以參考下面文章內(nèi)容
    2021-09-09
  • linux安裝軟件時(shí)提示軟件包不存在的解決方法

    linux安裝軟件時(shí)提示軟件包不存在的解決方法

    今天小編就為大家分享一篇linux安裝軟件時(shí)提示軟件包不存在的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-07-07

最新評(píng)論

云龙县| 京山县| 天峻县| 西林县| 凉城县| 福贡县| 离岛区| 喀喇| 中西区| 清徐县| 武川县| 乌拉特后旗| 牙克石市| 南平市| 万载县| 南丹县| 临朐县| 白水县| 申扎县| 武宣县| 郎溪县| 驻马店市| 海阳市| 澜沧| 甘洛县| 聊城市| 卓资县| 扶风县| 陆丰市| 朝阳市| 祁连县| 紫金县| 敦化市| 娱乐| 台江县| 临高县| 正镶白旗| 孝义市| 万宁市| 河北省| 灵璧县|