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?
Thanks to @pmacfarlane and @thebusybee, I dug a little further and here is what i found:
I declared two identical structs
s1
) with the __attribute__((used, section("my_section"))
,s2
) with no attributewhen 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 ?
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 !