This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

IAR: What is the problem of using of address of unaligned structure member?



Hi,

 

I have a lot of structures defined as packed (aligned to one byte). If I pass address of some element of such a structers to a function as a parameter, IAR compiler gives warning:

 

Little2Big4(&Admin->Serial); // Admin is a packed structure

Warning[Pa039]: use of address of unaligned structure member serial.c 2021

What is the purpose of this warning, is there any limitation?

10x

BP.

  • BasePointer said:
    What is the purpose of this warning, is there any limitation?

    Yes, there is.

    For maximum speed, the MSP accesses word values by a 16 bit read, using the 16 bit data bus. This read fails if the address is odd. The LSB of the address is silently ignored. Normally, this is no problem. Structs are automatically padded so that int members are on even offsets and the structs are placed on an even address. If you pack a struct, however, this does no longer apply. The resulting addres smight be odd. And when you pass cuh an odd address to a function requiring an int pointer, the function does not know that the pointer is not aligned and will likely access the wrong memory location (one byte less).

    If you KNOW that everything will be okay, you can ignore the warning (you should write a comment to the source file , explaining it), but the compiler cannot know, since the values are determined at runtime. So the compiler warns you.

  • Hi,

     

    Thanks for the reply, but I didn't understand the problem. The scenario you gave me as an example should be under compiler responsibility. If there is such a access problem, the compiler should generate the proper code as a byte pointer. Can you make it a little clear for me please with the small code below?

     

    Thank you,

    BP.

     

     

    #pragma pack(push)

    #pragma pack(1)   // pragma directive to specify the alignment of structs (1 byte)

     

    typedef struct

    {

        unsigned long   HASP_ID;    // 4 byte

        unsigned char   RC;         // 1 byte

        unsigned int    UserID;     // 2 byte

        unsigned long   Seed;       // 4 byte

        unsigned long   Serial;     // 4 byte  

        unsigned int    FCS;        // 2 byte

    } TAdmin;   // total: 17 byte

     

    #pragma pack(pop)

     

    void Little2Big4(void* data)

    {

        u8 tmp0, tmp1;

        u8* data_ptr = data;

       

        tmp0 = data_ptr[0];

        data_ptr[0] = data_ptr[3];

       

        tmp1 = data_ptr[1];

        data_ptr[1] = data_ptr[2];

       

        data_ptr[2] = tmp1;

        data_ptr[3] = tmp0;

    }

     

    void main(void)

    {

        TAdmin  admin1;

        TAdmin  admin2;

        TAdmin* ptr = &admin1; 

       

        Little2Big4(&ptr->Serial);      // is there any problem?

        Little2Big4(&admin2.HASP_ID);   // is there any problem?

    }

  • You didn't include the definition or at least the declaration of Little2Big in your first post.

    BasePointer said:
    void Little2Big4(void* data)

    There should be no problem. data is a void* and therefore a pointer with to a datasize of 1. The compiler will generate byte-access code.

    The problem only applies if your Little2Big4 expects a word* or long*. Then the compiler will generate code that only works if the passed pointer is word aligned. When the function is generated, the compiler cannot know that you intend to pass unaligned pointers (word pointers are assumed to be aligned on MSP due to the mentioned hardware restrictions, and the compiler knows and obeys that)
    If the compiler would know that the pointer may be misaligned, it could of course generate code that uses byte access for any operation, but this is by a magnitude slower and bigger than a single-word instruction. So it isn't done by default, only if it is clear that it is needed.

    BasePointer said:
    Little2Big4(&ptr->Serial);      // is there any problem?

    Not if it is (implicitely) typecast from int* to void*. If you get a warning here (it is jsut a warning) maybe the compiler is a bit pedantic, telling you that the int* is misaligned when the destination type is byte-aligned anyway. However, if you typecast the void* to int* later, it is obvious that the compiler will warn you, because a void* is only byte aligned by definition.

    BasePointer said:
    Little2Big4(&admin2.HASP_ID);   // is there any problem?

    No. Since admin2 itself is aligned (structs are generated on an aligned address, unless oyu do something weird), its first member is also aligned.

    It's a good thing anyway, if you define a struct so that either byte members are paired or appear at the end of the struct. If you are not forced to use a certain struct definition, this does not cost you anything and avoids problems.

    I use packed structs too, but this is because they are jammed into a byte stream with limited bandwidth, and also extracted from an incoming stream. For this to work (and do so even across endianess) I defined all data as byte arrays and use macros to extract them from teh struct. This works always and automatically solves problems with endianess.

  • Hi,

     

    I made some test with the compiler, but I couldn’t catch the problematic condition. All the examples below returned with proper value. Could you give me an example that may cause a problem with packed structures?

     

    Thanks,

    BP.

     

     

    unsigned char test[18] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11};

     

    unsigned long* val1 = (u32*)&test[0];      

    unsigned long result1 = *val1;  // -> after test, result1 is 0x04030201 like expected.

           

    unsigned long* val2 = (u32*)&test[1];      

    unsigned long result2 = *val2;      // -> 0x05040302 like expected

     

    TAdmin* Admin1 = (void*)&test[0]; // TAdmin packed

    unsigned long result3 = Admin1->Serial;     // -> result3 is 0x0F0E0D0C like expected

     

    TAdmin* Admin2 = (void*)&test[1]; // TAdmin packed

    unsigned long result4 = Admin2->Serial;     // -> 0x100F0E0D like expected

  • In all of youe examples, the compiler KNOWS that the pointer might be unaligned. In fact, the pointer values are known at compile-time. So the compiler will generate some complex code to patch the destination value together. Something like

    MOV.W &val2, R13
    MOV.B @R13, R15
    MOV.B 1(R13),R14
    SWPB R14
    AND.W R14,R15
    MOV.W R15, &result2
    MOV.B 2(R13), R15
    MOV.B 3(R13),R14
    SWPB R14
    AND.W R14,R15
    MOV.W R15, &(result2+2)

    instead of

    MOV.W &val2, R15
    MOV.W @R15, &result2
    MOV.W 2(R15),&(result2+2)

    Since the resulting pointers can be calculated at compile time (the indices are constant), the first line can be removed too and replaced by the precalculated pointer.

    The problem appears when you call a function with an unaligned pointer. When compiling the function, the compiler does not know that you'll passign an unaligned pointer and will generate the second code. Especially if the function is in a library or in a different .c file. And not inlined.

    So code like

    unsigned long result (u32* val) {
      return *val;
    }

    [...]
    i=1;
    [...]
    result2 =  result((u32*)&test[i]);

    will (if the call is not inlined, which with such a simple funciton is only sure if you have the function in a separate code file) most likely give a completely wrong result.

    If in question, you should take a look into the assembly list file with the generated code. One there the truth is told. The compiler can make everything from your c source.

     

  • [quote user="Jens-Michael Gross"]

    So code like

     

    unsigned long result (u32* val)

    {

      return *val;

    }

     

    [...]

    i=1;

    [...]

    result2 =  result((u32*)&test[i]);

    [/quote]

     

    Hi Michael,

     

    Thank you for this warning. Compiler gives waring in simulation mode only and nothink at compilation time. And the code is not working like expected. I have two questions.

     

    1. Is there any directive to tell IAR Compiler that assumes this pointer is unaligned? Somethink like that:

     

        unsigned long result (__packed unsigned long* val); // Keil supports this for ARMs

       

    2. Can I use safely unaligned pointers with library functions like memcpy(), memset(), strncpy(), etc..?

     

    Thanks.

  • So the emulator has detected that indeed a word instruction is executed with an odd address pointer.
    I too have fallen into this trap some time ago. The code looked okay but didn't work. It took me some hours to discover the tiny words that the LSB in word operations is silently ignored. While the linker had no problem generating such an instruction at link time. The compiler didn't warn too. While (in my case) he could have known.

    The latest versions of MSPGCC automatically generate byte-access code if the parameter is known to be possibly unaligned. I tested it some time ago, with a word-sized struct that had been packed.

    I'm not sure whether there is a way to tell the compiler to count a noemal int as unaligned. Maybe you'll have to generate your own type for this. And note that there are two possibly unaligned values: the address of the pointer itself and then the address it is pointing to.

    Try

    typedef uint32p struct { unsigned long } _packed;
    or
    typedef _packed unsigned long uint32p;

     or something like that. Then in your function use a uint32p* as parameter. The compiler should be able to autocast normal uint32 pointers to uint32p*, yet inside the function the compiler should generate byte-access code.

     

    library functions such as memcpy, memset etc should work on byte level Or at least detect an odd beginning/end address and access the additional byte on byte level. So there should be no problem. The pointer itself is normally passed in a register (you don't pass the address of the pointer but jsu tthe address it's pointing to). Loading the pointers value from a misaligned memory location into a register, however, could be a problem again. Either the compiler knows that the pointers address is possibly unaligned or it will load the wrong value (yet it is likely then that the value had been stored the wrong way too, so you won't notice)

    In my applications I pass packets which are not always even-sized and not always contain aligned data. So I have defined everything as either unsigned char or unsigned char[]. To access int or long values, I use read and write macros which assembly the value from the bytes in the arrays. The resulting code is almost as effective as the code the compiler would generate if knowing about the values being unaligned. But when I wrote these macros, the mspgcc didn't care for alignedness and accessed the data the wrong way, Now it does, and maybe now I don't need the macros anymore. But I keep them as they also solve the question of endianess between MSP, AVR and PC.

**Attention** This is a public forum