Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fixed size buffer cannot be directly used from "this" object

Tags:

c#

this

fixed

I am used a structure to represent pure data. One of the fields is a fixed-size buffer, as shown below.

[StructLayout(LayoutKind.Sequential, Pack=2)]
unsafe struct ImageDosHeader
{
    ...
    private fixed ushort _e_res[4];
    ...

    [Description("Reserved")]
    [DisplayName("e_res[0]")]
    public ushort e_res_0 { get { ... } set { ... } }

    ...
}

Within the get/set functions I tried to do the following but I get "Compiler Error CS1666: You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement."

return this._e_res[0];

However, the following work:

fixed (ImageDosHeader* p = &this)
    return p->_e_res[0];

ImageDosHeader local = this;
return local._e_res[0];

I can easily use the workarounds, however, I am wondering why directly accessing the fixed-size buffer from this is illegal. Or is this a bug that I should report?

I am using .NET 2.0.

like image 982
coderforlife Avatar asked May 15 '11 23:05

coderforlife


1 Answers

It's because of the underlying IL instructions.

The program does this sequence of instructions to get the element you want:

  1. Load the address onto the stack.

  2. Load the offset onto the stack.

  3. Add them.

  4. Read the value at that memory address.

If the object is in the heap and then moves because of garbage collection before step 4, then the address loaded from step 1 will no longer be valid. To protect against this, you need to pin the object into memory first.

(The fact that you're accessing the structure through the this pointer means that you have no idea if the structure is on the heap or on the stack, so you have to pin it just in case it's on the heap.)

The second example works because it copies the structure to the stack, and so the copy can never move around, so the address will always be valid.

Why doesn't the same issue happen with other kinds of fields? Because their offset is known at compile-time, whereas the array index is known at run-time, so the JIT can generate code that will always access the fields correctly.

like image 125
user541686 Avatar answered Sep 19 '22 03:09

user541686