Can't compile it

Why I can't compile it? Pointer to unsized T must be (usize*2), but I can't transmute equal array to pointer.

unsafe fn unsized_struct<T:?Sized,S>(addr: *const S,length: usize)->&'static T{
    let pointer: [usize;2];
    pointer[0]=addr as usize;
    pointer[1]=length;
    core::mem::transmute(pointer)
}

T: ?Sized doesn't mean T must be unsized, just that it can be.

How I can create pointer to unsized struct at runtime?

Can you be more specific about what you're trying to accomplish? Do you want to form a fat pointer to a trait object? To a slice? Something else entirely? A small example and context would be helpful to steer you in the right direction.

From ACPI documentation:

struct RSDT {
  struct ACPISDTHeader h;
  uint32_t PointerToOtherSDT[(h.Length - sizeof(h)) / 4];
};

I am trying to write code to handle this structure.
I need to make a reference to this structure at runtime

I never found a good way to represent the RSDT in Rust. I resorted to using unsafe code and just having a *const SDTHeader, which I then read pointers to the other tables from with ptr::read (after the end of the struct pointed to). It's not the cleanest solution ever, but it does work. It is part of a bigger codebase, but this might get you started.

If someone else has any better way of representing types like this, I'm all ears too :stuck_out_tongue:

Edit: before this, I represented the full structure by having an array with enough SDT pointer entries to cover any RSDT I thought could exist. This was an easier solution, but made the accessing code look safe, which it really wasn't (not just because it could lead to invalid data if you just naively iterated through the full array, but because only the RSDT was definitely mapped, so if it went over a page boundary, reading past the end of it would've caused a page-fault.) I don't recommend that approach.

1 Like

It sounds like this use case requires detection of structure size at runtime. If that's the case, you won't be able to define a single struct for it. You can define a struct for the header, but getting access to the pointers will all be unsafe iteration over raw pointers. (Correct me if I'm wrong?) Basically the same thing @IsaacWoods did.

1 Like

I do not want to use pointers and unsafe code where these structures will be handled. There are many similar dimensionless structures, for example, multiboot2. Can I make a macro for my purpose?

I think this type of struct requires better custom DST support. I recall servo doing something similar, which you can see here - it’s ugly and verbose, but consumers will see a safe API.

2 Likes

Yeah, I had hoped to use a DST, but the bitwise representation of the structure must be the same as the firmware's version, so I guess we'd need a completely new type of DST that didn't actually store its size?

1 Like

Not sure what you mean by “store its size”? The size today is stored in the fat pointer to the struct. I imagine if you had a repr(c) DST struct allocated on the heap, with a fixed size header and a dynamically size trailer for the array of ptrs, it should work. Or did I miss your point?

I understood. I can only get raw pointer and parse struct by hand. But how I may create temporary reference, which I can use in safe code? Writing such code for every unsized struct consume many time. I think, macro for transmutation [usize;2] to needed reference help me.
P.S. Excuse me for my bad English)

I have good idea. I can declare tail substructure for every unsized struct and implement Index trait for it. What you think?

You mean you’ll get a raw ptr to this struct from C/FFI code? If so, you can try something like this:

fn main() {
    // Pretend we have some allocation
    let v: Vec<u8> = vec![0; 128];
    // in reality, ptr would come from FFI
    let ptr = Box::into_raw(v.into_boxed_slice()) as *mut _ as *mut u8;
    let hdr_sz = std::mem::size_of::<Header>();
    // write some value into 2nd slot
    unsafe {
        std::ptr::write(ptr.offset(hdr_sz as isize + 8), 123);
    }
    let f = unsafe { unsized_struct::<Header>(ptr, 4) };
    println!("{}", std::mem::size_of_val(f));
    println!("{}", f.dynamic[1]);
}

#[repr(C)]
struct Header(i32, u32);

unsafe fn unsized_struct<'a, F>(addr: *const u8, length: usize) -> &'a Dynamic<F, usize> {
    std::mem::transmute([addr as usize, length])
}

#[repr(C)]
struct Dynamic<F, T> {
    fixed: F, // F must be repr C as well
    dynamic: [T],
}

playground to tinker with this

Note I wrote this up on mobile and so it’s just bare minimum to get an idea.

1 Like

Argh - the fn is supposed to be:

unsafe fn unsized_struct<'a, F, D>(addr: *const u8, length: usize) -> &'a Dynamic<F, D> {
    std::mem::transmute([addr as usize, length])
}

So the caller selects the type of the dynamic part.

1 Like

Yes, but typically in this case, the memory (and the pointer) doesn't point to the heap - they come from another structure called the RSDP, which just points to some physical memory address. So you could map the RSDT, grab the size from the SDT header, and then manually construct a fat pointer with the correct size, but it's not a particularly elegant solution. I might be missing something, but it's not a problem that translates nicely into Rust AFAICS.

1 Like

Oh I see - hardware sets this up? Yeah, I think you’d need to do a reinterpret_cast like thing with a Rust struct that has identical layout; the layout can be controlled and can match C. I do agree it’s ugly and fragile but not sure what can be done better short of codegen’ing these structs from some spec or something like that.

Actually, having a closer look at your example, it's a lot nicer than I'd envisaged; I might have a play around with my code and see if I can get it working with a DST. I'm not sure how stable the representations of these types are going to be, though. It'd be an annoying bug to track down if the transmute was suddenly unsound across a nightly update.

And yeah, these structures are set up by the BIOS/UEFI, to tell the OS about how that particular hardware is set up. It's a little over-engineered, to say the least.

1 Like

Indeed. I suspect a change in how fat ptrs are represented would be loudly announced, although that’s not too much of a solace.

Perhaps a custom DST solution, when/if made available, can make this a bit more robust.

1 Like

Thank you for reply. I am writing my own kernel, and try parse ACPI tables on initialization