分析riscv架构的裸机代码中最大栈空间
riscv的基本过程调用标准
1.函数前8个参数用a0~a7传输
2.超过8个的参数使用栈传递
3.函数返回参数到a0,a1寄存器中,返回值保存在ra寄存器中
4.如果子函数有使用s0-s11寄存器,那么在使用前需要将这些寄存器的内容入栈,使用完后再出栈
5.栈向下增长,即向低地址增长
6.如果gcc打开了“-fno-omit-frame-pointer”选项,将使用s0寄存器作为栈帧指针FP
7.栈中保存临时变量,如局部变量等
静态程序栈分析工具
checkstack.pl来自linux源码中用来对栈空间做静态检查的工具,目前也是支持riscv架构的,我们的编译器也是riscv64-linux-gcc,也拿来用了下 ,可以正常输出,关于checkstack.pl的使用方法以及相关描述可以参考如下链接文章
Kernel Small Stacks - eLinux.orghttps://elinux.org/Kernel_Small_Stacks
栈空间静态分析原理
查看checkstack.pl的源代码能够了解对栈空间使用情况分析的原理,如果对perl语法理解有问题,可以借助文心一言,让他帮忙分析。
my (@stack, $re, $dre, $sub, $x, $xs, $funcre, $min_stack);
{my $arch = shift;if ($arch eq "") {$arch = `uname -m`;chomp($arch);}$min_stack = shift;if ($min_stack eq "" || $min_stack !~ /^\d+$/) {$min_stack = 100;}$x = "[0-9a-f]"; # hex character$xs = "[0-9a-f ]"; # hex character or space$funcre = qr/^$x* <(.*)>:$/;if ($arch =~ '^(aarch|arm)64$') {#ffffffc0006325cc: a9bb7bfd stp x29, x30, [sp, #-80]!#a110: d11643ff sub sp, sp, #0x590$re = qr/^.*stp.*sp, \#-([0-9]{1,8})\]\!/o;$dre = qr/^.*sub.*sp, sp, #(0x$x{1,8})/o;} elsif ($arch =~ /^riscv(64)?$/) {#ffffffff8036e868: c2010113 addi sp,sp,-992$re = qr/.*addi.*sp,sp,-(([0-9]{2}|[3-9])[0-9]{2})/o;} else {print("wrong or unknown architecture \"$arch\"\n");exit}
}
其中$funcre = qr/^$x* <(.*)>:$/;
$funcre = qr/..../是用于定义一个正则表达式的匹配的语法
$x* 匹配16进制字符
<(.*)> 匹配 <xx>尖括号
其中$re = qr/.*addi.*sp,sp,-(([0-9]{2}|[3-9])[0-9]{2})/o;
$re = qr/..../o是用于定义一个正则表达式的匹配的语法
sp,sp 匹配字符串"sp,sp",表示源和目标操作数都是栈指针(sp)
addi 汇编指令是RISC-V架构中用于整数加法的指令。
(([0-9]{2}|[3-9])[0-9]{2}) 这是一个捕获组,用于匹配两位或三位数的立即数,这个数表示栈指针减少的量。
由如上代码可以推测静态分析的原理,例如如下汇编
$funcre匹配汇编中的函数名,$re匹配sp指针调整的指令
函数一开始的时候会先跳到自己的栈帧addi sp,sp,-16 意思为将sp-16的值保存存到sp中
如上的perl函数记录每个函数占用的栈空间
my ($func, $file, $lastslash, $total_size, $addr, $intro);$total_size = 0;while (my $line = <STDIN>) {if ($line =~ m/$funcre/) {$func = $1;next if $line !~ m/^($xs*)/;if ($total_size > $min_stack) {push @stack, "$intro$total_size\n";}$addr = $1;$addr =~ s/ /0/g;$addr = "0x$addr";$intro = "$addr $func [$file]:";my $padlen = 56 - length($intro);while ($padlen > 0) {$intro .= ' ';$padlen -= 8;}$total_size = 0;}elsif ($line =~ m/$re/) {my $size = $1;$size = hex($size) if ($size =~ /^0x/);if ($size > 0xf0000000) {$size = - $size;$size += 0x80000000;$size += 0x80000000;}next if ($size > 0x10000000);$total_size += $size;}
}
if ($total_size > $min_stack) {push @stack, "$intro$total_size\n";
}# Sort output by size (last field)
print sort { ($b =~ /:\t*(\d+)$/)[0] <=> ($a =~ /:\t*(\d+)$/)[0] } @stack;
while 循环的处理每一行汇编代码如果是匹配上$funcre则记录为一个新函数,匹配上$re则累加到当前函数的栈大小$total_size
看起来这个脚本并不统计总使用的的栈的空间最大值,只是当前函数的栈空间
想要统计系统使用的栈的最大空间怎么看
avstack.pl