View how C structs are packed during build time
Asked Answered
A

1

6

Is there any way to see how C structs are packed during build time? I've read through several similar posts about how structs may be packed:

But I'm wondering is there some kind of build time generated file that shows me exactly how a struct is packed? I tried checking the mapping file generated by the linker but it doesn't have this information.

PS: I'm trying to get a few microcontrollers to communicate with each other via UART and because one is 16 bit and the others are 32 bit, I'm hitting a few bugs everytime I update these structs.

Adria answered 9/6, 2016 at 4:51 Comment(10)
There's a macro offsetof() in stddef.h. See en.wikipedia.org/wiki/OffsetofComnenus
The DWARF info in every object will have that (if debugging info enabled). But it does take some parsing to extract the data.Shelton
You can surely set the pack width for your structure and force the same value on both projects.Apology
Consider writing functions to serialize/deserialize the data before/after transmitting via the UART. This solution might be a more robust than trying to control how the compiler defines the struct.Sanbenito
Using structs across compile domains is very bad practice. Endless maintenance that can be easily avoided. Avoid using structs for this communication.Merrel
You really have 3 different structures: the 16-bit one, the 32- bit one and the one used in serial communication. Instead of trying to make all 3 the exact same at the bit level, define the serial communication syntax and write routines to 1) convert 16-bit to/from serial and 2) convert 32-bit to/from serial (similar to @Sanbenito comment)Kalevala
There is no point in a compiler providing packing information on an individual structure basis because it is entirely deterministic for all structures. The packing your compiler uses should be defined in the documentation, and in any case you can normally apply compiler directives to coerce specific packing and alignment - which is far more useful than letting it do what it wants. Enforcing a common structure layout across systems is only part of your problem however - there is endianness to consider.Gerund
This is an XY question. Ask a question about your problem (the post-script part), not your solution. Then we will not have to explain it to you in comments.Gerund
My initial assumption was that there was a compiler flag from which I could get this information. After reading @Clifford's comment about the struct packing being deterministic, I guess it makes sense that they don't. Definitely leaning towards the serialization/deserialization approach now. Thanks!Adria
I think you need to read up on serialization of data for transmission. It may be a hopeless task to get the structures the same on the two systems (though that fixed-size types from <stdint.h> will probably help) — but as soon as you hit a structure packing issue, you're hosed again, unless you're using serialization.Buyer
E
2

The preprocessor won't be able to figure out structure offsets, so no amount of macro magic alone will dump your offsets for you.

If you are building for embedded targets, you will probably be able to create .bin files (not elf, coff, mach-o). If you use each of your target compilers to create an array of offsets in an object file and then dump it to a bin file, it should be possible to compare the bin files for each of your targets. It would be a good idea to automate the process for build time checks.

Here is an example of what I'm talking about:

#include <stdint.h>
#include <stddef.h>
typedef struct s1{
    uint16_t f1;
    uint32_t f2;
    uint64_t f3;
    int8_t f4[5];
    uint32_t f5[2];
}s1;

#define o(f) ((int32_t)offsetof(s1,f))

int32_t offsets[]={
    o(f1),
    o(f2),
    o(f3),
    o(f4),
    o(f5)
};

This just creates a table of offsets. Build this for a mipsel and x86_64 and compare. Here is a make file:

T1:=x86_64-linux-gnu
CC1:=$(T1)-gcc
OBJCPY1:=$(T1)-objcopy
T2:=mipsel-linux
CC2:=$(T2)-gcc
OBJCPY2:=$(T2)-objcopy


.PHONY: all cmp clean hexdumps

all: hexdumps 

hexdumps: hexdump.$(T1).txt hexdump.$(T2).txt

hexdump.$(T1).txt: offsets.$(T1).bin
    hexdump -C $< > $@

hexdump.$(T2).txt: offsets.$(T2).bin
    hexdump -C $< > $@

offsets.$(T1).bin: offsets.$(T1).o
    $(OBJCPY1) -j.data  -O binary $< $@

offsets.$(T2).bin: offsets.$(T2).o
    $(OBJCPY2) -j .data -O binary $< $@

offsets.$(T1).o: offsets.c
    $(CC1) -Wall -c -o $@ $<

offsets.$(T2).o: offsets.c
    $(CC2) -Wall -c -o $@ $<

clean:
    -rm -f offsets.$(T1).o offsets.$(T2).o
    -rm -f offsets.$(T1).bin offsets.$(T2).bin
    -rm -f hexdump.$(T1).txt hexdump.$(T2).txt

Now, comparing the offsets is pretty easy:

evaitl@evbb ~/se $ cat hexdump.mipsel-linux.txt 
00000000  00 00 00 00 04 00 00 00  08 00 00 00 10 00 00 00  |................|
00000010  18 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000020
evaitl@evbb ~/se $ cat hexdump.x86_64-linux-gnu.txt 
00000000  00 00 00 00 04 00 00 00  08 00 00 00 10 00 00 00  |................|
00000010  18 00 00 00                                       |....|
00000014

The mips emits a 32 byte data section instead of the x86 20 byte. If you set the size of offsets, you should be able to use cmp to compare the two in your build.

If your targets have different endianess, you may need to change the o macro to use ntohl or some such to get both compilers to emit integers in the same format.

Engaged answered 9/7, 2016 at 0:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.