risc-v 栈分析
基础
栈(stack) 保存动态分配的自动变量时值,并且栈又操作系统自动分配内存。
测试程序:
cat sum.c
#include <stdio.h>#include <ctype.h>#include <stdlib.h>#define MAX (1UL << 20)typedef unsigned long u64;typedef unsigned int u32;u64 max_addend = MAX;u64 sum_till_MAX(u32 n){
u64 sum;n++;
sum = n;if(n < max_addend)sum += sum_till_MAX(n);return sum;}int main(int argc, char **argv){
u64 sum = 0;if((argc == 2) && isdigit(*(argv[1])))max_addend = strtoul(argv[1], NULL, 0);if(max_addend > MAX || max_addend == 0){fprintf(stderr, "Invalid number is specified\n");return 1;}sum = sum_till_MAX(0);printf("sum(0..%lu) = %lu\n", max_addend, sum);return 0;}
下面求出从0到10的总和
$ gcc -o sum -g sum.c
$ ./sum 10
sum(0..10) = 55
(gdb) disassemble main
…….0x0000000000000846 <+172>: li a0,0 //调用函数时,先将参数赋值给a00x0000000000000848 <+174>: jal ra,0x744 <sum_till_MAX> //jal将pc+4的值赋值给ra,然后将pc值设置为调用函数的地址
(gdb) disassemble sum_till_MAX
Dump of assembler code for function sum_till_MAX:0x0000000000000744 <+0>: addi sp,sp,-48 //分配一个栈空间0x0000000000000746 <+2>: sd ra,40(sp) //将返回地址压入栈中0x0000000000000748 <+4>: sd s0,32(sp) //将fp压入栈中,在riscv中,s0为fp寄存器0x000000000000074a <+6>: addi s0,sp,48 //fp指向当前的栈0x000000000000074c <+8>: mv a5,a0 //将参数a0赋值给a50x000000000000074e <+10>: sw a5,-36(s0) //将参数存入栈上0x0000000000000752 <+14>: lw a5,-36(s0) //从栈上读取参数0x0000000000000756 <+18>: addiw a5,a5,1 //n++0x0000000000000758 <+20>: sw a5,-36(s0) //下面两行取n的低32位0x000000000000075c <+24>: lwu a5,-36(s0)0x0000000000000760 <+28>: sd a5,-24(s0)0x0000000000000764 <+32>: lwu a4,-36(s0) //sum = n0x0000000000000768 <+36>: auipc a5,0x2 //将当前pc值加上2<<12赋值给a50x000000000000076c <+40>: addi a5,a5,-1888 # 0x2008 <max_addend>0x0000000000000770 <+44>: ld a5,0(a5) //得到max_addend的值0x0000000000000772 <+46>: bgeu a4,a5,0x78c <sum_till_MAX+72>0x0000000000000776 <+50>: lw a5,-36(s0)0x000000000000077a <+54>: mv a0,a50x000000000000077c <+56>: jal ra,0x744 <sum_till_MAX>0x0000000000000780 <+60>: mv a4,a00x0000000000000782 <+62>: ld a5,-24(s0)0x0000000000000786 <+66>: add a5,a5,a40x0000000000000788 <+68>: sd a5,-24(s0)0x000000000000078c <+72>: ld a5,-24(s0)0x0000000000000790 <+76>: mv a0,a50x0000000000000792 <+78>: ld ra,40(sp) //恢复返回地址0x0000000000000794 <+80>: ld s0,32(sp) //恢复调用者的fp0x0000000000000796 <+82>: addi sp,sp,48 //恢复sp地址0x0000000000000798 <+84>: ret //将ra赋值给pc
调试器的backtrace
GDB等调试器的backtrace功能是通过搜索栈中保存的 信息来实现的。
下面在第二次调用sum_till_MAX()时中断执行程序,
(gdb) bt
#0 sum_till_MAX (n=2) at sum.c:14
#1 0x0000002aaaaaa780 in sum_till_MAX (n=2) at sum.c:17
#2 0x0000002aaaaaa780 in sum_till_MAX (n=1) at sum.c:17
#3 0x0000002aaaaaa84c in main (argc=1, argv=0x3ffffffbe8) at sum.c:32
(gdb) i r pc sp
pc 0x2aaaaaa752 0x2aaaaaa752 <sum_till_MAX+14>
sp 0x3ffffff9a0 0x3ffffff9a0
(gdb) x/48x $sp
0x3ffffff9a0: 0xf7fd8570 0x0000003f 0xf7fd8368 0x00000002
0x3ffffff9b0: 0xffffffff 0x00000000 0x00000000 0x00000000
0x3ffffff9c0: 0xfffffa00 0x0000003f 0xaaaaa780 0x0000002a
0x3ffffff9d0: 0x00000001 0x00000000 0x00000000 0x00000002
0x3ffffff9e0: 0x00000001 0x00000000 0x00000002 0x00000000
0x3ffffff9f0: 0xfffffa30 0x0000003f 0xaaaaa780 0x0000002a
0x3ffffffa00: 0xffffffff 0x00000000 0xf7fe24f8 0x00000001
0x3ffffffa10: 0xf7ffea88 0x0000003f 0x00000001 0x00000000
0x3ffffffa20: 0xfffffa60 0x0000003f 0xaaaaa84c 0x0000002a
0x3ffffffa30: 0xfffffbe8 0x0000003f 0xffffffef 0x00000001
0x3ffffffa40: 0x00000000 0x00000000 0x00000000 0x00000000
0x3ffffffa50: 0xfffffbe8 0x0000003f 0xf7ecc66c 0x0000003f
从sum_till_MAX()反汇编可以知道每个栈帧都分配了48字节
手动解析栈帧:
(gdb) x/48x $sp
当sum_till_MAX 参数为2进入时,栈情况0x3ffffff9a0: 0xf7fd8570 0x0000003f 0xf7fd8368 0x00000002
0x3ffffff9b0: 0xffffffff 0x00000000 0x00000000 0x00000000
0x3ffffff9c0: 0xfffffa00 0x0000003f 0xaaaaa780 0x0000002a—————————————————————————— --------------------------0x3ffffffa00 上层fp 0x2aaaaaa780 返回地址
当sum_till_MAX 参数为1进入时,栈情况0x3ffffff9d0: 0x00000001 0x00000000 0x00000000 0x00000002——————n的值
0x3ffffff9e0: 0x00000001 0x00000000 0x00000002 0x00000000———————————————————————— -------------------------参数1 sum的值
0x3ffffff9f0: 0xfffffa30 0x0000003f 0xaaaaa780 0x0000002a------------------------- -------------------------0x3ffffffa30 上层fp 0x2aaaaaa780 返回地址当sum_till_MAX 参数为0进入时,栈情况
0x3ffffffa00: 0xffffffff 0x00000000 0xf7fe24f8 0x00000001
0x3ffffffa10: 0xf7ffea88 0x0000003f 0x00000001 0x00000000
0x3ffffffa20: 0xfffffa60 0x0000003f 0xaaaaa84c 0x0000002a0x3ffffffa30: 0xfffffbe8 0x0000003f 0xffffffef 0x00000001
0x3ffffffa40: 0x00000000 0x00000000 0x00000000 0x00000000
0x3ffffffa50: 0xfffffbe8 0x0000003f 0xf7ecc66c 0x0000003f
需要注意的是n为32bits,sum为64bits
总结:得出如下公式
PC_f 指的是父函数调用子函数时的pc值
LR_c 指的是父函数调用子函数时,返回子函数的pc值
FP_c 指的是指向父函数的栈帧。
PC_f = *LR_c - 4 = *(FP_c - 8) - 4
父函数FP = *(子函数FP - 16)
栈大小的限制
实际上,在本实例中,如果不带参数,会引起Segmentation fault(段错误),执行:
$ gdb ./sum
Reading symbols from ./sum...
(gdb) r
Starting program: /mnt/sum
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/riscv64-linux-gnu/libthread_db.so.1".Program received signal SIGSEGV, Segmentation fault.
0x0000002aaaaaa74e in sum_till_MAX (n=<error reading variable: Cannot access memory at address 0x3fff7ffffc>) at sum.c:12
12 {
(gdb) x/i $pc
=> 0x2aaaaaa74e <sum_till_MAX+10>: sw a5,-36(s0)
(gdb) p $sp
$1 = (void *) 0x3fff7ffff0
打印x/i $pc,此时的的汇编指令为:
sw a5,-36(s0)
是将参数存储到栈上,
在看sp的值:
(gdb) p $sp
$1 = (void *) 0x3fff7ffff0
下面查看该进程的内存映射(memory map),要查看GDB attach了的进程的内存映像,可以执行一下命令,执行该命令后,GDB就会显示与被调试的进程相对应的/proc/<PID>/maps的信息。
(gdb) i proc mappings
process 2132
Mapped address spaces:Start Addr End Addr Size Offset Perms objfile0x2aaaaaa000 0x2aaaaab000 0x1000 0x0 r-xp /mnt/sum0x2aaaaab000 0x2aaaaac000 0x1000 0x0 r--p /mnt/sum0x2aaaaac000 0x2aaaaad000 0x1000 0x1000 rw-p /mnt/sum0x3ff7ea6000 0x3ff7fc6000 0x120000 0x0 r-xp /lib/riscv64-linux-gnu/libc.so.60x3ff7fc6000 0x3ff7fc9000 0x3000 0x120000 r--p /lib/riscv64-linux-gnu/libc.so.60x3ff7fc9000 0x3ff7fcb000 0x2000 0x123000 rw-p /lib/riscv64-linux-gnu/libc.so.60x3ff7fcb000 0x3ff7fda000 0xf000 0x0 rw-p 0x3ff7fdf000 0x3ff7fe0000 0x1000 0x0 r--p [vdso_data]0x3ff7fe0000 0x3ff7fe2000 0x2000 0x0 r-xp [vdso]0x3ff7fe2000 0x3ff7ffd000 0x1b000 0x0 r-xp /lib/riscv64-linux-gnu/ld-linux-riscv64-lp64d.so.10x3ff7ffd000 0x3ff7ffe000 0x1000 0x1b000 r--p /lib/riscv64-linux-gnu/ld-linux-riscv64-lp64d.so.10x3ff7ffe000 0x3ff8000000 0x2000 0x1c000 rw-p /lib/riscv64-linux-gnu/ld-linux-riscv64-lp64d.so.10x3fff800000 0x4000000000 0x800000 0x0 rw-p [stack]
请注意最后一行的[stack]。它表示栈空间,栈空间的顶端是0x3fff800000。然而,刚看到的栈指针的值却是0x3fff7ffff0,超出了栈的范围。访问地址超出了栈的范围,也就是说,发生了栈溢出。
查看当前环境设置的进程栈大小:
# ulimit -s
8192
大小为8MB。
现在将栈扩大10倍。再次执行实例程序。就不会发生段错误而正常结束了。
# ulimit -Ss 81920
# ./sum
sum(0..1048576) = 549756338176
risc-v 栈分析相关推荐
- RISC V (RV32+RV64) 架构 整体介绍
文章目录 riscv 市场 芯片介绍 软件介绍 开发板介绍 PC介绍 riscv 架构 编程模型(指令集/寄存器/ABI/SBI) 运行状态 指令集 寄存器 riscv32和riscv64两者的区别 ...
- 计组学习笔记2(RISC v版)
指令集解释 (规定:R[r]表示通用寄存器r的内容,M[addr]表示存储单元addr的内容,SEXT[imm]表示对imm进行符号扩展,ZEXT[imm]表示对imm进行零扩展) 整数运算类 -U型 ...
- 【愚公系列】2023年06月 移动安全之安卓逆向(插桩及栈分析)
文章目录 前言 一.插桩及栈分析 1.概念简介 2.Android Killer插桩 二.栈跟踪及分析 1.DDMS工具介绍 1.1 设备信息窗口 1.2 过滤器窗口 1.3 功能窗口 1.4 信息输 ...
- 华为鸿蒙系统源码_鸿蒙系统 IO 栈分析 | 解读鸿蒙源码
华为的鸿蒙系统开源之后第一个想看的模块就是 FS 模块,想了解一下它的 IO 路径与 linux 的区别.现在鸿蒙开源的仓库中有两个内核系统,一个是 liteos_a 系统,一个是 liteos_m ...
- oracle sqlarea表结构,oracle v$sqlarea 分析SQL语句使用资源情况
V$SQLAREA 本视图持续跟踪所有shared pool中的共享cursor,在shared pool中的每一条SQL语句都对应一列.本视图在分析SQL语句资源使用方面非常重要. V$SQLARE ...
- Gstreamer的一些基本概念与A/V同步分析
http://blog.csdn.net/shenbin1430/article/details/4291963 ================================= 一.媒体流(str ...
- 服务外包技术培训——后端开发技术栈分析(Java)
技术栈 http://www.atguigu.com/download.shtml 学习资源 https://space.bilibili.com/302417610/channel/detail?c ...
- .windbg-k*实例分析(查看调用栈分析)
[cpp] view plaincopy #include "stdafx.h" int fun0(int i) { return i; }; int fun1(int i) { ...
- java中线程的状态以及线程栈分析
java中线程的状态 状态 说明 NEW 初始状态.线程刚刚被创建,并且start()方法还未被调用 RUNNABLE 运行状态.表示线程正在java虚拟机中执行,但是可能正在等待操作系统的其他资源, ...
- struts2值栈分析
前段日子对ognl表达式不是很理解,看了几本书上关于ognl表达式的描述后还是感觉很难,前几天学习了struts2中值栈的内容,现在感觉ognl表达式其实很容易. struts2中利用值栈来存储数据, ...
最新文章
- C# 36进制转10进制
- 创建一个触发器新增字段的时候设置某个字段的值
- 神策数据与 UCloud 达成战略合作,开启高效企业服务新模式
- Git 分支的创建与切换 —— Git 学习笔记 14
- TODO:Go语言goroutine和channel使用
- HDU - 4586 数学期望
- 大快人心!和P2P网贷彻底说再见
- 学fpga(在线verilog编程)
- 系统学习机器学习之总结(三)--多标签分类问题
- python 给字符串加颜色
- 使用 leastsq 对指定函数格式进行最小二乘拟合
- 单反相机tf卡用sd卡套稳定吗_存储卡可不是插上就能用 单反相机的使用细节
- delphi基本语法(摘自博主:沈金强)
- 不惑之年一次性通过软考高项的苦与乐
- Unity连接Photon
- python笔记6-python官方文档之format()格式化详解
- Python sklearn机器学习各种评价指标——Sklearn.metrics简介及应用示例
- 驾考计算机播报原理,驾考科二电脑语音提示
- Keil.STM32F1xx_DFP.2.4.0.pack
- Javascript日期和时间戳(毫秒/秒)相互转化,日期分隔符不同转化结果不同