有没有办法确定 VTable 中每个特征方法的偏移量?

Is there a way to determine the offsets of each of the trait methods in the VTable?

我想我可以尝试 或多或少 从头开始​​构建特征对象而不使用 impl 块。详细说明:

trait SomeTrait {
    fn fn_1(&self);
    fn fn_2(&self, a: i64);
    fn fn_3(&self, a: i64, b: i64);
}

struct TraitObject {
    data: *mut (),
    vtable: *mut (),
}

fn dtor(this: *mut ()) {
    // ...
}

fn imp_1(this: *mut ()) {
    // ...
}

fn imp_2(this: *mut (), a: i64) {
    // ...
}

fn imp_3(this: *mut (), a: i64, b: i64) {
    // ...
}

fn main() {
    let data = &... as *mut (); // something to be the object
    let vtable = [dtor as *mut (),
                  8 as *mut (),
                  8 as *mut (),
                  imp_1 as *mut (),
                  imp_2 as *mut (),
                  imp_3 as *mut ()]; // ignore any errors in typecasting,
        //this is not what I am worried about getting right

    let to = TraitObject {
        data: data,
        vtable: vtable.as_ptr() as *mut (),
    };
    // again, ignore any typecast errors,

    let obj: &SomeTrait = unsafe { mem::transmute(to) };

    // ...

    obj.fn_1();
    obj.fn_2(123);
    obj.fn_3(123, 456);
}

据我了解,成员函数在特征定义中出现的顺序并不总是与函数指针在 VTable 中出现的顺序相同。有没有办法确定 VTable 中每个特征方法的偏移量?

如果您不介意在运行时检测布局,那么您可以比较特定偏移处的函数地址,并将它们与已知的虚拟实现的地址进行比较以匹配它们。这假定您知道特征中有多少方法,因为您可能需要阅读所有方法。

use std::mem;

trait SomeTrait {
    fn fn_1(&self);
    fn fn_2(&self, a: i64);
    fn fn_3(&self, a: i64, b: i64);
}

struct Dummy;

impl SomeTrait for Dummy {
    fn fn_1(&self) { unimplemented!() }
    fn fn_2(&self, _a: i64) { unimplemented!() }
    fn fn_3(&self, _a: i64, _b: i64) { unimplemented!() }
}

struct TraitObject {
    data: *mut (),
    vtable: *mut (),
}

fn main() {
    unsafe {
        let fn_1 = Dummy::fn_1 as *const ();
        let fn_2 = Dummy::fn_2 as *const ();
        let fn_3 = Dummy::fn_3 as *const ();

        let dummy = &mut Dummy as &mut SomeTrait;
        let dummy: TraitObject = mem::transmute(dummy);
        let vtable = dummy.vtable as *const *const ();
        let vtable_0 = *vtable.offset(3);
        let vtable_1 = *vtable.offset(4);
        let vtable_2 = *vtable.offset(5);

        // Mapping vtable offsets to methods is left as an exercise to the reader. ;)
        println!("{:p} {:p} {:p}", fn_1, fn_2, fn_3);
        println!("{:p} {:p} {:p}", vtable_0, vtable_1, vtable_2);
    }
}