【问题标题】:Is there a way to determine the offsets of each of the trait methods in the VTable?有没有办法确定 VTable 中每个特征方法的偏移量?
【发布时间】:2016-09-26 23:59:02
【问题描述】:

我想我可以尝试或多或少地在不使用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 中每个 trait 方法的偏移量?

【问题讨论】:

    标签: rust low-level trait-objects


    【解决方案1】:

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

    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);
        }
    }
    

    【讨论】:

    • 这是我最初尝试使用的一种方法。您知道是否可以通过名称确定函数的偏移量?所以理论上我可以在运行时覆盖函数吗?即override(object, <SomeTrait>::fn_1, impl_1)?
    • 我认为这不可能。在 C++ 中,我认为你可以使用成员函数指针来做到这一点,但 Rust 没有这些。
    • 该死的。好吧,我想我至少可以看看我可以用 TraitObjects 和 Dynamic Dispatch 将界限推到多远。不过,这是一个不错的开始。
    猜你喜欢
    • 2014-01-26
    • 2021-09-23
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    • 2022-11-29
    • 2021-11-30
    • 2019-09-13
    • 2021-05-30
    相关资源
    最近更新 更多