获取寄存器的参数

我们的编译器主要分成了两个部分, 第一步部分就是语义分析阶段。在语义分析阶段我们主要做了下面这些事情:

  1. 类型推导, 我们会推导出对应node的类型, 比如我们会推导出pid这个函数的返回值类型
  2. 内存分配, 我们会将对应的数据结构分配到寄存上面活着栈上面
  3. 类型检查, 这一步部分还是有待完善的。

首先我们先简单的看一下我们获取跟踪函数参数的语法:

arg(1, "128s");
  • 函数的第一个参数是获取参数的位置, 比如1表示函数的第一个位置
  • 函数的第二个参数是获取参数的类型信息或参数的大小,比如128就是16字节, s表示对应的参数类型是字符串类型。

首先我们需要知道在我们的x86-64下面用于函数传递参数的寄存器, 下面这些 %rdi,%rsi%rdx%rcx%r8%r9 寄存器用来给函数参数传递值。

首先是对应的annote这个阶段, 在这个节点我们会计算对应的函数的类型和对应的大小:

static int probe_arg_annotate(node_t *call) {
	node_t *arg = call->call.vargs;
	intptr_t reg;

	reg = arch_reg_arg(arg->integer);
	
	arg->integer = reg;
	call->dyn->type = TYPE_INT;
	call->dyn->size = sizeof(int64_t);
	return 0;
}

这里我们需要注意的一点就是, 我们的arg函数返回值是int, 这是因为我们存储的是跟踪点函数参数的地址值。

然后我们会在栈上开辟一段空间, 在这段空间上面我们会对这个地址值进行存储:

static int probe_reg_loc_assign(node_t *call) {
	node_t *probe;
	
	call->dyn->addr = node_probe_stack_get(probe, call->dyn->size);
	call->call.vargs->dyn->loc = LOC_VIRTUAL;
	return 0;
}

然后就是编译, 编译的过程主要就是从我们的struct pt_regs *ctx这个变量中去读取对应的值, 然后存储在我们刚才开辟的栈空间上的。

emit_stack_zero(prog, call);

emit(prog, MOV(BPF_REG_1, BPF_REG_10));
emit(prog, ALU_IMM(BPF_ADD, BPF_REG_1, call->dyn->addr));
//寄存器的大小
emit(prog, MOV_IMM(BPF_REG_2, arch_reg_width()));
emit(prog, MOV(BPF_REG_3, BPF_REG_9));
//src的base位置
emit(prog, ALU_IMM(BPF_ADD, BPF_REG_3, sizeof(uintptr_t)*arg->integer));
emit(prog, CALL(BPF_FUNC_probe_read));

获取到我们参数的base地址, 然后就是根据我们的大小继续去读

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值