音效素材网提供各类素材,打造精品素材网站!

站内导航 站长工具 投稿中心 手机访问

音效素材

浅析ARM架构下的函数的调用过程
日期:2021-05-20 09:26:12   来源:脚本之家

1、背景知识

1、ARM64寄存器介绍

2、STP指令详解(ARMV8手册)

我们先看一下指令格式(64bit),以及指令对于寄存机执行结果的影响

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

将Xt1和Xt2存入Xn|SP对应的地址内存中,然后,将Xn|SP的地址变更为Xn|SP + imm偏移量的新地址

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

将Xt1和Xt2存入Xn|SP的地址自加imm对应的地址内存中,然后,将Xn|SP的地址变更为Xn|SP + imm的offset偏移量后的新地址

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

将Xt1和Xt2存入Xn|SP的地址自加imm对应的地址内存中

手册中有三种操作码,我们只讨论程序中涉及的后两种

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;

红色部分对应推栈的关键逻辑,其他汇编指令含义可自行参考armv8手册或者度娘。

2、一个例子

熟悉了上面的部分,接下来我们看一个实例:

C代码如下:

相关的几个函数反汇编如下(和推栈相关的一般只有入口两条指令):

main\f3\f4\strlen

我们通过gdb运行后,可以看到strlen地方会触发SEGFAULT,引发进程挂掉

上述通过代码编译后,没有strip,因此elf文件是带着符号的

查看运行状态(info register):关注$29、$30、SP、PC四个寄存器

一个核心的思想:CPU执行的是指令而不是C代码,函数调用和返回实际是在线程栈上面的压栈和弹栈的过程

接下来我们来看上面的调用关系在当前这个任务栈是如何玩的:

函数调用在栈中的关系(call function压栈,地址递减;return弹栈,地址递增):

以下是推栈的过程(划重点)

再回头来看之前的汇编:

main\f3\f4\strlen

从当前的sp开始,frame 0是strlen,这块没有开栈,因此上一级的调用函数仍然是x30,因此推导:frame1调用为f3

函数f3的起始入口汇编:

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

可以看到,f3函数开辟的栈空间为48字节,因此,倒推frame2的栈顶为当前的sp + 48字节:0xfffffffff2c0

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

继续从sp = 0xfffffffff2c0倒推frame1的函数

函数f4的起始入口汇编为:

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

可以看到,f4函数开辟的栈空间也是为48字节,因此,倒推frame3的栈顶为当前的0xfffffffff2c0 + 48字节:0xfffffffff2f0

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

因此frame3的函数为main函数,main函数对应的栈顶为0xfffffffff320

至此推导结束(有兴趣的同学可以继续推导,可以看到libc如何拉起main的过程)

总结:

推栈的关键:

  • 当前的现场
  • 熟悉cpu体系架构的开栈的方式

3、实战讲解

现场有如下的core:可以看到,所有的符号找不到,加载了符号表依然不好使,解析不出来实际的调用栈

(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,关注x29、x30、sp、pc四个寄存器的值

推导任务栈:

先将sp内容导出:

下图实际已先将结果标出,我们下面来详细描述如何推导

pc代表当前执行的函数指令,如果当前指令未开栈,一般情况x30代表上一级的frame调用当前函数的下一条指令,查看汇编,可以反解为如下函数

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

找到栈顶函数后,查看该函数的栈操作:

(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对应的栈顶和上一级的LR返回指令,反解,可以得到函数build_node_conn_str

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

继续重复上述推导,可以看到这个函数build_node_conn_str开了176字节的栈,

(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

因此继续用0xfffec4cebe10 + 176 = 0xfffec4cebec0

查看调用者0xfffec4cebe10+8为reload_database_pools

继续看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字节,因此这一层frame的栈底为0xfffec4cebec0 + 0x220 = 0xfffec4cec0e0

因此得到基本的调用关系的结构如下

以上基本可以够用来分析问题了,因此不需要再继续推导

TIPS:arm架构下一般调用都会使用这种指令,

stp x29, x30, [sp,#immediate]! 有叹号或者无叹号

因此在每一层的frame都保存了上一层frame的栈顶地址和LR指令,通过准确找到底层的frame 0栈顶后,就可以快速推导出所有的调用关系(红色虚线圈出来的部分),函数的反解依赖符号表,只要原始的elf文件的symbol段没有strip掉,是都可以找到对应的函数符号(通过readelf -S查看即可)

找到Frame后,每一层frame里面的内容,结合汇编基本就可以用来推导过程变量了。

以上就是浅析ARM架构下的函数的调用过程的详细内容,更多关于ARM架构下的函数的调用过程的资料请关注其它相关文章!

    您感兴趣的教程

    在docker中安装mysql详解

    本篇文章主要介绍了在docker中安装mysql详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编...

    详解 安装 docker mysql

    win10中文输入法仅在桌面显示怎么办?

    win10中文输入法仅在桌面显示怎么办?

    win10系统使用搜狗,QQ输入法只有在显示桌面的时候才出来,在使用其他程序输入框里面却只能输入字母数字,win10中...

    win10 中文输入法

    一分钟掌握linux系统目录结构

    这篇文章主要介绍了linux系统目录结构,通过结构图和多张表格了解linux系统目录结构,感兴趣的小伙伴们可以参考一...

    结构 目录 系统 linux

    PHP程序员玩转Linux系列 Linux和Windows安装

    这篇文章主要为大家详细介绍了PHP程序员玩转Linux系列文章,Linux和Windows安装nginx教程,具有一定的参考价值,感兴趣...

    玩转 程序员 安装 系列 PHP

    win10怎么安装杜比音效Doby V4.1 win10安装杜

    第四代杜比®家庭影院®技术包含了一整套协同工作的技术,让PC 发出清晰的环绕声同时第四代杜比家庭影院技术...

    win10杜比音效

    纯CSS实现iOS风格打开关闭选择框功能

    这篇文章主要介绍了纯CSS实现iOS风格打开关闭选择框,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作...

    css ios c

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的办法

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的

    Win7给电脑C盘扩容的办法大家知道吗?当系统分区C盘空间不足时,就需要给它扩容了,如果不管,C盘没有足够的空间...

    Win7 C盘 扩容

    百度推广竞品词的投放策略

    SEM是基于关键词搜索的营销活动。作为推广人员,我们所做的工作,就是打理成千上万的关键词,关注它们的质量度...

    百度推广 竞品词

    Visual Studio Code(vscode) git的使用教程

    这篇文章主要介绍了详解Visual Studio Code(vscode) git的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...

    教程 Studio Visual Code git

    七牛云储存创始人分享七牛的创立故事与

    这篇文章主要介绍了七牛云储存创始人分享七牛的创立故事与对Go语言的应用,七牛选用Go语言这门新兴的编程语言进行...

    七牛 Go语言

    Win10预览版Mobile 10547即将发布 9月19日上午

    微软副总裁Gabriel Aul的Twitter透露了 Win10 Mobile预览版10536即将发布,他表示该版本已进入内部慢速版阶段,发布时间目...

    Win10 预览版

    HTML标签meta总结,HTML5 head meta 属性整理

    移动前端开发中添加一些webkit专属的HTML5头部标签,帮助浏览器更好解析HTML代码,更好地将移动web前端页面表现出来...

    移动端html5模拟长按事件的实现方法

    这篇文章主要介绍了移动端html5模拟长按事件的实现方法的相关资料,小编觉得挺不错的,现在分享给大家,也给大家...

    移动端 html5 长按

    HTML常用meta大全(推荐)

    这篇文章主要介绍了HTML常用meta大全(推荐),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参...

    cdr怎么把图片转换成位图? cdr图片转换为位图的教程

    cdr怎么把图片转换成位图? cdr图片转换为

    cdr怎么把图片转换成位图?cdr中插入的图片想要转换成位图,该怎么转换呢?下面我们就来看看cdr图片转换为位图的...

    cdr 图片 位图

    win10系统怎么录屏?win10系统自带录屏详细教程

    win10系统怎么录屏?win10系统自带录屏详细

    当我们是使用win10系统的时候,想要录制电脑上的画面,这时候有人会想到下个第三方软件,其实可以用电脑上的自带...

    win10 系统自带录屏 详细教程

    + 更多教程 +
    WIN服务器linux服务器FTP服务器DNS服务器其他