riscv 栈空间静态分析

分析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.orgicon-default.png?t=N7T8https://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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shenhuxi_yu

感谢投币,继续输出

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值