immunant / c2rust

Migrate C code to Rust
https://c2rust.com/
Other
3.79k stars 219 forks source link

Support dynamic instrumentation of `AddrOfStatic` #575

Open fw-immunant opened 1 year ago

fw-immunant commented 1 year ago

We don't handle this yet.

This basically amounts to finding the AllocId for a Const and then looking up that ID. This isn't completely trivial, because we need to look through the guts of the Const (which is an interning wrapper around ConstS, which annotates the type for a ConstKind, which wraps Valtree while accounting for lazy-evaluation and generics).

A ValTree holding a reference will give up its AllocId via try_to_scalar. We can then look up this id in the context's AllocMap via tcxt.global_alloc, which will point us to what the const actually is: a function, static, or const-eval allocation.

For reference/comparison,

rustc --emit=mir on this file:

static FOO: () = ();

fn x() -> usize {
    &FOO as *const _ as usize
}

gives the following MIR:

static FOO: () = {
    let mut _0: ();                      // return place in scope 0 at static.rs:1:13: 1:15

    bb0: {
        Deinit(_0);                      // scope 0 at static.rs:1:18: 1:20
        return;                          // scope 0 at static.rs:1:1: 1:15
    }
}

fn x() -> usize {
    let mut _0: usize;                   // return place in scope 0 at static.rs:3:11: 3:16
    let mut _1: *const ();               // in scope 0 at static.rs:4:2: 4:18
    let mut _2: *const ();               // in scope 0 at static.rs:4:2: 4:18
    let _3: &();                         // in scope 0 at static.rs:4:2: 4:6
    let _4: &();                         // in scope 0 at static.rs:4:3: 4:6

    bb0: {
        _4 = const {alloc1: &()};        // scope 0 at static.rs:4:3: 4:6
                                         // mir::Constant
                                         // + span: static.rs:4:3: 4:6
                                         // + literal: Const { ty: &(), val: Value(Scalar(alloc1)) }
        _3 = _4;                         // scope 0 at static.rs:4:2: 4:6
        _2 = &raw mconst (*_3);           // scope 0 at static.rs:4:2: 4:6
        _1 = _2;                         // scope 0 at static.rs:4:2: 4:18
        _0 = move _1 as usize (PointerExposeAddress); // scope 0 at static.rs:4:2: 4:27
        return;                          // scope 0 at static.rs:5:2: 5:2
    }
}

alloc1 (static: FOO, size: 0, align: 1) {}

where the important line is _4 = const {alloc1: &()};. This assign Statement has the Operand::Constant Rvalue that we care about.

This bug isn't necessarily a high priority, depending on whether we need to implement this to demonstrate real-world usefulness of the dynamic instrumentation and subsequent refactoring.

fw-immunant commented 1 year ago

I don't know whether in the example above the alloc1 (static: FOO, size: 0, align: 1) {} bit indicates that the AllocMap has a GlobalAlloc::Static entry (containing a DefId) for alloc1, or if there would actually be a GlobalAlloc::ConstAllocation entry for it. My mental model says that the linker "allocates" here and rustc has to reason in terms of symbols, but I don't know what this looks like from the MIR world. Maybe DefId is close enough to a symbol for middle-end purposes.