debugging (arm64)如何在调试内核时找到printk调用schedule()函数的位置?堆栈中的“lr”值显示奇怪的值

vfh0ocws  于 2022-11-14  发布在  其他
关注(0)|答案(1)|浏览(124)

我正在尝试使用busybox和vanilla linux内核(5.10.0)在一个实验板上 Boot linux。
在init_kernel的最后阶段,它执行init脚本,init脚本中的最后一个命令是exec /bin/sh。()函数被调用并返回。但我不知道程序在调度后去了哪里()函数。(我不能使用gdb的董事会)。所以如下所示,我尝试把一些打印在调度函数读取lr(链接寄存器,=x30),应该留在堆栈底部的调度函数。
(In ARM 64体系结构,在函数的入口处,x29(=fp)和x30(=lr)存储在堆栈的底部。lr寄存器是函数结束后返回的地址。参见understanding aarch64 assembly function call, how is stack operated?
变量passed_it用于将打印限制为调用init脚本后的时间。)
--内核/调度/核心. c--

extern int passed_it;
asmlinkage __visible void __sched schedule(void)
{
    struct task_struct *tsk = current;
    register void *sp asm ("sp");

    if (passed_it) printk("@entered schedule\n");
    sched_submit_work(tsk);
    do {
        preempt_disable();
            if (passed_it) printk("@entering __schedule\n");
        __schedule(false);
            if (passed_it) printk("@exited __schedule\n");
        sched_preempt_enable_no_resched();
    } while (need_resched());
    sched_update_worker(tsk);
    if (passed_it) printk("@exiting schedule. sp=%px, fp=%lx, lr=%lx\n",sp,*((long *)sp), *((long *)sp+8));
}

这是实验停止时的日志

This boot took 0.00 seconds

### calling /bin/sh ###
/bin/sh: can't a@entered schedule
@entering __schedule
@exited __schedule
@exiting schedule. sp=ffffffc0106a3f00, fp=ffffffc0106a3f40, lr=ffffffc0106a3f50

我通过aarch64-none-elf-objdump -S vmlinux > vmlinux.objdump生成了vmlinux.objdump,以查看lr值指向什么。但它不在任何text部分中,并且System.map显示此0xffffffc 0106 a3 f50正好位于__start_init_task之间(0xffffffc 0106 a0000,= __初始化堆栈)和__结束初始化任务(0xffffffc 0106 a4000)。初始化堆栈从__初始化堆栈向下增长。
--System.map网站--

ffffffc01063c1c0 d cfd_data
ffffffc01063c200 d csd_data
ffffffc01063c220 D __per_cpu_end
ffffffc0106a0000 D __init_end
ffffffc0106a0000 D __initdata_end
ffffffc0106a0000 D __start_init_task
ffffffc0106a0000 D _data
ffffffc0106a0000 D _sdata
ffffffc0106a0000 D init_stack
ffffffc0106a0000 D init_thread_union
ffffffc0106a4000 D __end_init_task
ffffffc0106a4000 D __nosave_begin
ffffffc0106a4000 D __nosave_end
ffffffc0106a4000 d vdso_data_store
ffffffc0106a5000 D boot_args

那么为什么它指向__init_task呢?有谁能告诉我我在这里遗漏了什么吗?再一想,我猜测实验中打印的虚拟地址可能是用户空间虚拟地址,而不是Linux内核地址,会是这样吗?

inkz8wg9

inkz8wg91#

前几天我发现为什么它不工作,忘记了在这里更新它。
原因很简单,我认为lr(=x30)值存储在当前堆栈帧的第二个值中,这是正确的,但问题是一个简单的错误(指针操作)。
printk状态

if (passed_it) printk("@exiting schedule. sp=%px, fp=%lx, lr=%lx\n",sp,*((long *)sp), *((long *)sp+8));

应该是

if (passed_it) printk("@exiting schedule. sp=%px, fp=%lx, lr=%lx\n",sp,*((long *)sp), *((long *)sp+1));

if (passed_it) printk("@exiting schedule. sp=%px, fp=%lx, lr=%lx\n",sp,*((long *)sp), *((long *)(sp+8)));

您知道在a(long *)上加1意味着在地址中增加8(非常基本)。
在修复这个问题之后,我可以从schedule()函数跟踪程序的去向。(它返回到schedule_preempt_disabled(),并返回到rest_init()。因此,这条路径是init任务,它将在最后空闲。)

相关问题