起因
群里做网络的小伙伴问了一个问题,他在wifi驱动的某个函数里加了dump stack,然后插入驱动,发现调用栈是这样的,为什么呢?
代码追溯
insmod这个app,是在busybox中的,所以找到busybox的代码:
入口就是这个insmod_main
调用了bb_init_module,它的尾部有这样的调用
init_module即为核心的初始化模块的函数。这里会涉及到系统调用:
系统调用号是__NR_finit_module或者__NR_init_module。
这里syscall的实现,没有找到确切的,估计是做了一层wrapper,可能是在glibc或者uclibc这种库里。
这里看着是触发了el0_sync
由于是在用户态触发的,所以是el0,但为什么是sync异常呢?
这里我继续追溯了一下,到uclibc-ng的库中,找到了:
这里把syscall_name存到了x8中,然后调用了svc 0
在ARM64(aarch64)体系结构中,svc 0是一条软中断指令,用于触发一个异常。svc是Supervisor Call的缩写,通常用于向操作系统发出请求或者系统调用。当执行svc 0指令时,处理器会进入异常处理流程,并跳转到异常向量表中相应的处理程序,即操作系统的内核代码。在处理器进入异常模式后,操作系统可以识别并处理此次异常,执行相应的操作,例如执行用户空间进程的系统调用,处理中断请求等。因此,svc 0通常用于触发用户空间程序与操作系统内核之间的通信,进行系统调用。
所以这得看kernel对el0的异常向量表的实现了。我们知道,aarch64架构,异常向量表比较复杂,每个EL等级都有一份,EL等级越高,要实现的异常向量表就越多。EL2要实现EL0,EL1,EL2的。EL1要实现EL0,EL1的。
这里是从EL0触发异常,所以只需要实现EL0的向量表。
我们找到linux内核如何实现EL0的异常向量表的:
(linux/arch/arm64/kernel/entry.S)
/*
* Exception vectors.
*/
.pushsection ".entry.text", "ax"
.align 11
SYM_CODE_START(vectors)
kernel_ventry 1, sync_invalid // Synchronous EL1t
kernel_ventry 1, irq_invalid // IRQ EL1t
kernel_ventry 1, fiq_invalid // FIQ EL1t
kernel_ventry 1, error_invalid // Error EL1t
kernel_ventry 1, sync // Synchronous EL1h
kernel_ventry 1, irq // IRQ EL1h
kernel_ventry 1, fiq // FIQ EL1h
kernel_ventry 1, error // Error EL1h
kernel_ventry 0, sync // Synchronous 64-bit EL0
kernel_ventry 0, irq // IRQ 64-bit EL0
kernel_ventry 0, fiq // FIQ 64-bit EL0
kernel_ventry 0, error // Error 64-bit EL0
#ifdef CONFIG_COMPAT
kernel_ventry 0, sync_compat, 32 // Synchronous 32-bit EL0
kernel_ventry 0, irq_compat, 32 // IRQ 32-bit EL0
kernel_ventry 0, fiq_compat, 32 // FIQ 32-bit EL0
kernel_ventry 0, error_compat, 32 // Error 32-bit EL0
#else
kernel_ventry 0, sync_invalid, 32 // Synchronous 32-bit EL0
kernel_ventry 0, irq_invalid, 32 // IRQ 32-bit EL0
kernel_ventry 0, fiq_invalid, 32 // FIQ 32-bit EL0
kernel_ventry 0, error_invalid, 32 // Error 32-bit EL0
#endif
SYM_CODE_END(vectors)
我们忽略invalid结尾的entry,第一个可用的就是sync异常。
找到el0_sync异常的handle:
这里invoke_syscall就是通过系统调用,查表sys_call_table来执行对应的处理接口。
sys_call_table会在arch spec的这个文件中定义。这里是arm64的。
这里包含了asm/unistd.h,这是内核公有代码。
127号系统调用就是init module。他在定义时是怎么定义的?
load_module就是内核在做模块加载的动作了。
最后做个总结:
结尾
调用栈init_module函数向下的内容,就是__INIT_MODULE这类的宏的实现了,一般和链接机制有关。TODO: