Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

View how C structs are packed during build time

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:

  • How structs are saved in memory C
  • Why isn't sizeof for a struct equal to the sum of sizeof of each member?

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.

like image 918
rith87 Avatar asked Jun 09 '16 04:06

rith87


1 Answers

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.

like image 143
evaitl Avatar answered Sep 20 '22 16:09

evaitl