调试:我不';在反汇编调用程序中,我看不到回调是callq还是内联asm(Linux x86_64)
我在CentOS 7.9上使用崩溃实用程序调试内核oops(vmcore),我有一个调用回调的函数foo,但当我反汇编foo时,我看不到引用回调的callq指令,也看不到调用方中回调的程序集(表明它没有内联) 然而,内核堆栈显示RIP位于回调函数的偏移量33上。有什么好处 foo的最后一条指令显示:调试:我不';在反汇编调用程序中,我看不到回调是callq还是内联asm(Linux x86_64),linux,debugging,assembly,gcc,x86-64,Linux,Debugging,Assembly,Gcc,X86 64,我在CentOS 7.9上使用崩溃实用程序调试内核oops(vmcore),我有一个调用回调的函数foo,但当我反汇编foo时,我看不到引用回调的callq指令,也看不到调用方中回调的程序集(表明它没有内联) 然而,内核堆栈显示RIP位于回调函数的偏移量33上。有什么好处 foo的最后一条指令显示: callq 0xffffffff91c9af10 <__stack_chk_fail> 更新 我也看到了一个callq: \uuuuux86\u间接\uThunk\uRax 但我不知
callq 0xffffffff91c9af10 <__stack_chk_fail>
更新
我也看到了一个callq:
\uuuuux86\u间接\uThunk\uRax
但我不知道。。也许这就是电话?仔细看,它与一个回程蹦床有关,听起来很有趣!XD通常您应该看到
调用*%rcx
或其他内容(函数指针可能位于不同的寄存器或堆栈内存中,但可能是某种间接调用)。或者使用优化的tailcall,如jmp*%rcx
,如果您的代码可以优化为return func(args)
。同样,可能是在将函数指针移动到另一个寄存器之后,以及在清理堆栈之后
(第四个整数/指针函数arg到达RCX,但最终使用时它可能位于任何其他寄存器中)
call\u stack\u chk\u fail
只是-fstack protector=strong
机器的一部分
内核代码使用
gcc-mindirect branch=thunk
为间接调用启用gcc的幽灵缓解,因此是的,间接调用将通过\uuux86\u indirect\u thunk\u rax
。(或在任何其他寄存器中使用指针的另一个寄存器。)
当然,用户空间代码也可以使用这个选项进行编译,尽管我认为大多数发行版在默认情况下都不会启用它
typedef int (*my_callback_t)(int);
int foo(int a, int b, int c, my_callback_t func)
{
int ret = func(a);
return ret;
}
像这样编译,使用-O3-Wall-mindirect branch=thunk
(-mcmodel=kernel
)也可以进行良好的度量)
这是一个很好的例子。
如果没有
-mindirect branch=thunk
,您当然会得到预期的jmp*%rcx
我是否会看到%rdi在callq之前填充回调的第一个参数(由反汇编的调用方)?或者,如果函数参数在调用者中是本地的,那么寄存器是否不需要遵循参数传递的约定?我看到的唯一无法解释的另一个调用是对uuux86_indirect_thunk_rax的调用,我猜您引用的寄存器优化与此相关?@GreggLeventhal:哦,对了,我忘了内核代码将在启用幽灵缓解的情况下编译。更新。谢谢,这是一个很好的答案。如果你对在纽约地区从事Linux系统(编程或管理)的工作感兴趣,请留言给我!
typedef int (*my_callback_t)(int);
int foo(int a, int b, int c, my_callback_t func)
{
int ret = func(a);
return ret;
}
# your function
foo:
jmp __x86_indirect_thunk_rcx # like jmp *%rcx tailcall
# extra code that will be present once in the whole kernel, deduplicated by the linker
.section .text.__x86_indirect_thunk_rcx,"axG",@progbits,__x86_indirect_thunk_rcx,comdat
__x86_indirect_thunk_rcx:
call .LIND1
.LIND0:
pause
lfence # block speculation along this never-executed return path that return prediction will jump to.
jmp .LIND0 # this seems unnecessary after lfence in this unreachable code.
.LIND1:
mov %rcx, (%rsp) # overwrite the return address with your func ptr
ret # and pop it into RIP