Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing reference of packed struct member to template. gcc bug?

I encountered a problem, passing struct member to a template function. The function's goal is to take the address and size of the member. Here is simple example:

This is the struct. It has packed attribute.

struct TestStruct {
    unsigned char       elem1;
    unsigned char       elem2;
    uint64_t            elem3;
    char                buf[10000];
    int                 elem4;
    unsigned char       elem5;
}
__attribute__ ((packed));

this is the template function, which should get a member's address

template<typename T>
void addData(const T &val)
{
        printf ("address inside func: %p \n",&val);
}

int main(int argc, char *argv[])
{
        TestStruct testdata;
        testdata.elem4 = 0;
        printf ("struct address is:   %p \n",&testdata);
        printf ("elem4  address is:   %p \n",&testdata.elem4);
        addData(testdata.elem4);
        return 0;
}

The problem: When attribute ((packed)); is set (like in the example) the template function receives wrong address of the member:

Output:

struct address is:   0x7fff735bb4e0 
elem4  address is:   0x7fff735bdbfa 
address inside func: 0x7fff735bb4dc

If I remove the "packed" attribute, everything is OK. There is no error and no warning (even with -Wall -Wextra), but not the right address is passed to the function.

I read this:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36566

and found that there is an issue, getting references to packed-struct members. Interesting enough, replacing const T& with T& in my template function, produces the error message:

error: cannot bind packed field ‘testdata.TestStruct::elem4’ to ‘int&’

So, I have 2 questions:

  1. Why cannot packed-struct members be passed as const references, when their address can be passed as pointer

  2. What happens in the const T& case? There is no error, no warning, but the incorrect address is passed to the function. As we know, the address of reference is the address of the variable, the reference points to.

like image 503
Nuclear Avatar asked Mar 30 '15 07:03

Nuclear


1 Answers

Both of your questions are answered at the link you posted.

1. Why cannot packed-struct members be passed as const references, when their address can be passed as pointer

Gabriel M. Beddingfield wrote in his comment:

All assignments of obj.s to type short& and short* are incorrect, and ideally they would all result in compiler errors.

The C++ spec (C++03, Sects. 3.9, 3.9.1, 3.9.2) are very clear that T and "pointer to T" have implementation-specific alignment requirements. If you have a "pointer to T" then you may assume that it meets the alignment requirements. I'm sure the C spec has similar language.

I can only add to this the corresponding quote from C++14 standard ([basic.align]/1):

Object types have alignment requirements (3.9.1, 3.9.2) which place restrictions on the addresses at which an object of that type may be allocated. An alignment is an implementation-defined integer value representing the number of bytes between successive addresses at which a given object can be allocated. An object type imposes an alignment requirement on every object of that type; stricter alignment can be requested using the alignment specifier

The bottom line of it is that even taking an addres of a packed struct member should be an error.

2. What happens in the const T& case? There is no error, no warning, but the incorrect address is passed to the function. As we know, the address of reference is the address of the variable, the reference points to.

Jonathan Wakely wrote:

A const-reference causes a temporary to be created, you didn't bind to the packed field


The bottom line is that it's not a bug itself that you can't bind a non-const reference to a packed struct field, a bug is that at the same time you can take an address of it. The compiler should either allow or disallow both.

like image 191
Anton Savin Avatar answered Oct 17 '22 13:10

Anton Savin