Rust调用&dyn Fn的底层原理:汇编偏移40的由来
Rust中
dyn Fn()调用时汇编偏移量40的由来 问题背景
给定Rust函数:
pub fn abc(x: &dyn Fn()) { x() }
使用Rust 1.63.0编译为x86-64汇编后得到:
example::abc: push rax call qword ptr [rsi + 40] pop rax ret
疑问:汇编中的偏移量40是如何产生的?
核心解释
偏移量40来自Rust对trait对象(这里是dyn Fn())的底层实现机制:
胖指针的结构
&dyn Fn()是一个胖指针,在x86-64架构下占用16字节,拆分为两个8字节部分:- 第一部分(对应汇编中的
rdi寄存器):指向 trait 对象的数据实例 - 第二部分(对应汇编中的
rsi寄存器):指向该 trait 的虚表(vtable)
- 第一部分(对应汇编中的
vtable的布局
Rust的vtable是包含元数据和方法指针的结构体,针对Fn()trait的vtable在1.63.0版本中的布局(按字节偏移)如下:- 0-7: 类型的大小(
usize) - 8-15: 类型的对齐要求(
usize) - 16-23: 析构函数(
drop)的指针 - 24-31:
FnOnce::call_once方法的指针(因Fn继承自FnMut,FnMut继承自FnOnce,父trait方法排在前面) - 32-39:
FnMut::call_mut方法的指针 - 40-47:
Fn::call方法的指针(也就是调用x()时实际执行的函数)
- 0-7: 类型的大小(
汇编中的偏移计算
rsi指向vtable的起始地址,[rsi + 40]就是直接取vtable中偏移40字节处的Fn::call方法指针,再通过call指令执行该方法。
另外,汇编里的push rax和pop rax是为了满足x86-64 System V调用约定的栈对齐要求(调用函数前栈指针需为16字节对齐),和偏移量无直接关联。
内容的提问来源于stack exchange,提问作者radong
相关产品推荐
相关产品推荐

