分析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