调试:我没有在反汇编调用者中看到回调作为 callq 或内联 asm (Linux x86_64)
debugging: I don't see callback in disassembled caller as either a callq or inlined asm (Linux x86_64)
我正在使用 CentOS 7.9 上的崩溃实用程序调试内核 oops (vmcore),我有一个调用回调的函数 foo,但是当我反汇编 foo 时,我没有看到引用回调的 callq 指令,我也没有在调用方中看到回调的程序集(表明它没有内联)。
但是,内核堆栈显示 RIP 在回调函数的偏移量 33 上。给出了什么?
foo 的最后一条指令显示:
callq 0xffffffff91c9af10 <__stack_chk_fail>
这是否可能意味着回调破坏了堆栈并且 glibc 将其替换为这个 stack_chk thingamajig?
// signature for foo
foo(some_t *some, size_t off, size_t size,
my_callback_t *func, void *private)
// callback gets called in foo like:
ret = func(args)
更新
我也确实看到了一个呼叫:
__x86_indirect_thunk_rax
我不知道……也许这就是电话?查了一下,跟一个return蹦床有点关系,听起来很好玩! XD
通常你应该看到 call *%rcx
或其他东西(函数指针可能在不同的寄存器或堆栈内存中,但某种间接调用)。或者,如果您的代码可以优化为 return func(args)
,则使用像 jmp *%rcx
这样的优化尾调用。同样,可能在将函数指针移动到不同的寄存器之后,并在清理堆栈之后。
(第 4 个 integer/pointer 函数 arg 到达 RCX,但最终使用时它可能在任何其他寄存器中)。
call _stack_chk_fail
只是 -fstack-protector=strong
机器的一部分。
内核代码使用 gcc -mindirect-branch=thunk
为间接调用启用 GCC 的 Spectre 缓解措施,所以是的,间接调用将通过__x86_indirect_thunk_rax
。 (或者一个不同的指针在任何其他寄存器中。)
User-space 代码当然也可以用这个选项编译,虽然我不认为大多数发行版默认启用它。
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
(GCC10.2 on Godbolt,使用 -mcmodel=kernel
也是很好的衡量标准)
# 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
这是一个。
没有-mindirect-branch=thunk
,你当然得到预期的jmp *%rcx
.
我正在使用 CentOS 7.9 上的崩溃实用程序调试内核 oops (vmcore),我有一个调用回调的函数 foo,但是当我反汇编 foo 时,我没有看到引用回调的 callq 指令,我也没有在调用方中看到回调的程序集(表明它没有内联)。
但是,内核堆栈显示 RIP 在回调函数的偏移量 33 上。给出了什么?
foo 的最后一条指令显示:
callq 0xffffffff91c9af10 <__stack_chk_fail>
这是否可能意味着回调破坏了堆栈并且 glibc 将其替换为这个 stack_chk thingamajig?
// signature for foo
foo(some_t *some, size_t off, size_t size,
my_callback_t *func, void *private)
// callback gets called in foo like:
ret = func(args)
更新
我也确实看到了一个呼叫:
__x86_indirect_thunk_rax
我不知道……也许这就是电话?查了一下,跟一个return蹦床有点关系,听起来很好玩! XD
通常你应该看到 call *%rcx
或其他东西(函数指针可能在不同的寄存器或堆栈内存中,但某种间接调用)。或者,如果您的代码可以优化为 return func(args)
,则使用像 jmp *%rcx
这样的优化尾调用。同样,可能在将函数指针移动到不同的寄存器之后,并在清理堆栈之后。
(第 4 个 integer/pointer 函数 arg 到达 RCX,但最终使用时它可能在任何其他寄存器中)。
call _stack_chk_fail
只是 -fstack-protector=strong
机器的一部分。
内核代码使用 gcc -mindirect-branch=thunk
为间接调用启用 GCC 的 Spectre 缓解措施,所以是的,间接调用将通过__x86_indirect_thunk_rax
。 (或者一个不同的指针在任何其他寄存器中。)
User-space 代码当然也可以用这个选项编译,虽然我不认为大多数发行版默认启用它。
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
(GCC10.2 on Godbolt,使用 -mcmodel=kernel
也是很好的衡量标准)
# 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
这是一个
没有-mindirect-branch=thunk
,你当然得到预期的jmp *%rcx
.