经常在vmcore里反汇编函数时会看到操作%gs相关的:
那么怎么获取每个cpu gs寄存器的值呢?
以这里为例:
#define in_interrupt() (irq_count())
#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK
| NMI_MASK))
#define preempt_count() (current_thread_info()->preempt_count)
#define this_cpu_read_stable(var) percpu_stable_op("mov", var)
因为per-cpu变量kernel_stack为unsigned long类型,因此命中case 8:
asm(op "q "__percpu_arg(P1)",%0"
: "=r" (pfo_ret__)
: "p" (&(var)));
展开为:
asm("movq %%gs:%P1, %0" : "=r"(pfo_ret__) : "p"(&(kernel_stack)));
这行代码的含义为将约束输入部分必须为有效的地址&(kernel_stack)(p约束), 将段寄存器gs加偏移量&(kernel_stack)通过寄存器(r约束)赋值给 pfo_ret__.
当前测试环境tasklist_read_lock函数对应反汇编代码:
当前测试环境&kernel_stack的值为0x10e38:
我们看下每cpu变量kernel_stack的值:
per-cpu基地址的值:
对应cpu0:
kernel_stack- per-cpu base address
crash> px 0xffff9d745fc10e38-0xffff9d745fc00000
$2 = 0x10e38
crash>
得出的结果0x10e38就是下面这条指令的0x10e38:
crash> dis tasklist_read_lock
...
...
0xffffffff9d88dcb6 <tasklist_read_lock 6>: mov %gs:0x10e38,%rax
...
...
因此可以知道实际上kmem -o的输出就是每个cpu上对应的gs寄存器的值了。
实际上这里0x10e38就是&kernel_stack的值:
crash> p &kernel_stack
$3 = (unsigned long *) 0x10e38
crash>
crash>
对应到上面的汇编语言
asm("movq %%gs:%P1, %0" : "=r"(pfo_ret__) : "p"(&(kernel_stack)));
转换为:
movq %%gs:&kernel_stack, pfo_ret__
pfo_ret__=ffff9d745fc00000 0x10e38=ffff9d745fc10e38
另外kmem -o实际上也是per-cpu 变量的基地址,跟__per_cpu_offset数组的值是对应的:
代码语言:javascript复制#define per_cpu_ptr(ptr, cpu) SHIFT_PERCPU_PTR((ptr), per_cpu_offset((cpu)))
#ifndef SHIFT_PERCPU_PTR
/* Weird cast keeps both GCC and sparse happy. */
#define SHIFT_PERCPU_PTR(__p, __offset) ({
__verify_pcpu_ptr((__p));
RELOC_HIDE((typeof(*(__p)) __kernel __force *)(__p), (__offset));
})
#endif
#ifndef __per_cpu_offset
extern unsigned long __per_cpu_offset[NR_CPUS];
#define per_cpu_offset(x) (__per_cpu_offset[x])
#endif