cembeddedmicrochippic24xc16

How to remove unwanted data padding from custom sections on XC16


I am currently trying to dev a firmware for a PIC24F using the whole microchip stack (xc16, mcc, mplab X IDE, etc ...) (not necessarily relevant but i work on a debian 22)

Doesn't really matter why but i want to place some data on a specific section that i will place at specific location on my firmware.

Let's imagine i have a struct my_struct defined in file.h as follow :

#pragma pack(push, 1)   // using pragma pack to pack the struct and avoid padding
typedef struct {
    uint16_t magic;
    uint32_t length;
    uint32_t crc;
    uint16_t word_1;
    uint16_t word_2;
    uint8_t byte_1;
    uint8_t byte_2;
    uint8_t byte_3;
    uint8_t array[15];
}  my_struct;
#pragma pack(pop)

then in my file.c :

const my_struct  __attribute__((section("my_section"))) instance = {
    .magic            = 0xbaba,
    .crc              = 0xbabababa,
    .length           = 0xbabababa,
    .byte_1           = 0xba,
    .byte_2           = 0xba,
    .byte_3           = 0xba,
    .word_1           = 0xbaba,
    .word_2           = 0xbaba,
    .array            = {0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba }
};

When I create the object file file.o, I expect having a section my_section of size 32 and filled with 0xBA. So I try to inspect the produced file.o using objdump to ensure that the data are the one that I want

$ size -A file.o
section           size   addr
[...]
my_section              64      0
$ objdump -s -j my_section file.o

Contents of section my_section:
 0000 ba00ba00 ba00ba00 ba00ba00 ba00ba00  ................
 0010 ba00ba00 ba00ba00 ba00ba00 ba00ba00  ................
 0020 ba00ba00 ba00ba00 ba00ba00 ba00ba00  ................
 0030 ba00ba00 ba00ba00 ba00ba00 ba00ba00  ................

But it seems like XC16 added a 00 padding for each data byte and that it doubled the size of the section.

I know that padding is to keep data aligned for optimization purpose, but for this specific case, I really need those data to be packed and the size needs to be 32.

The issue is not only in file.o, even if I go until the end of the compilation process, I manage to have the section located where i want (thanks to an updated linker script) but it still contains the unwanted padding. So i'm pretty sure the issue is not about linker, but about the compilation process itself

I tried a loooot of things, really.

I tried to pack the structure using : #pragma pack(1) (with or without push/pop) I also tried to use __packed or __atribute__((packed(1)))

I tried to add the aligned(1) keyword, or the volatile specifier I even tried to use some uint32_t arr[8], uint16_t arr[16] and uint8_t arr[32] instead of a struct.
I tried a lot of mplab project configuration (i can't remember which ones, probably all of them) I tried all those options separately and together ...

But nothing changes the output. I checked documentation or anything i could, but i can't make it work. Is this even possible under xc16 or should i give up?

=== EDIT ===

Thanks to @pmacfarlane and @thebusybee, I dug a little further and here is what i found:

I declared two identical structs

when i compile and inspect file.o

I got the same output as before for the my_section output, coming from s1.
And some data has been added to the .const section, coming from s2:

$ objdump -s -j .const file.o

Contents of section .const:
 0000 ba00ba00 ba00ba00 ba00ba00 ba00ba00  ................
 0010 ba00ba00 ba00ba00 ba00ba00 ba00ba00  ................
 0020 ba00ba00 ba00ba00 ba00ba00 ba00ba00  ................
 0030 ba00ba00 ba00ba00 ba00ba00 ba00ba00  ................

So nothing new here, but if I inspect the final elf, there is a difference :

$ objdump -s -j .const firmware.elf

Contents of section .const:
 82cc baba0000 baba0000 baba0000 baba0000  ................
 82dc baba0000 baba0000 baba0000 baba0000  ................
 82ec baba0000 baba0000 baba0000 baba0000  ................
 82fc baba0000 baba0000 baba0000 baba0000  ................
$ objdump -s -j my_section firmware.elf

Contents of section my_section:
 0000 ba00ba00 ba00ba00 ba00ba00 ba00ba00  ................
 0010 ba00ba00 ba00ba00 ba00ba00 ba00ba00  ................
 0020 ba00ba00 ba00ba00 ba00ba00 ba00ba00  ................
 0030 ba00ba00 ba00ba00 ba00ba00 ba00ba00  ................

Please note that i am using the default linker script for this example.

I tried to add some code in it , even to modify it entirely but, as nothing worked as expected, I went back to the default one.
For minimal reproducible purpose, i even ended up removing everything about section management, and the result stays the same. So i think the behaviors is coming from the xc16 linker default behavior and not from the linker script.
I would gladly add the linker script to the question but it is 3k lines long.
So, it looks like the linking process is packing ba00ba00 into baba0000 for built-in section such as .const but not for my custom section.

Don't know if it's relevant, but I assumed that this would bring more information. Any hints ?


Solution

  • In order to mark this thread as resolved, I will post this answer, but the credit is not for me.

    As pointed out by @thebusybee, the issue was coming from the fact that the pic I was using, uses 24bits words.

    And as @pmacfarlane said, the solution was in this datasheet.

    For 16-bit devices, a 24-bit word is used in Flash memory. The architecture supports the mapping of areas of Flash into the data space, but this mapping is only 16 bits wide to fit in with data space dimensions, unless the __pack_upper_byte qualifier is used

    So by declaring my struct as follows :

    __pack_upper_byte const my_struct __attribute__((section("my_section"))) instance = {
        .magic            = 0xbaba,
        .crc              = 0xbabababa,
        .length           = 0xbabababa,
        .byte_1           = 0xba,
        .byte_2           = 0xba,
        .byte_3           = 0xba,
        .word_1           = 0xbaba,
        .word_2           = 0xbaba,
        .array            = {0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba }
    };
    
    

    I ended up with

    
    objdump -s -j my_section binary.elf
    
    binary.elf:     file format elf32-little
    
    Contents of section my_section :
     0000 bababa00 bababa00 bababa00 bababa00  ................
     0010 bababa00 bababa00 bababa00 bababa00  ................
     0020 bababa00 bababa00 baba0000 
    

    Which is way better than before, thank you very much for your help !