查看在构建期间如何打包 C 结构

Posted

技术标签:

【中文标题】查看在构建期间如何打包 C 结构【英文标题】:View how C structs are packed during build time 【发布时间】:2016-10-09 13:56:33 【问题描述】:

有什么方法可以查看 C 结构在构建期间是如何打包的?我已经阅读了几篇关于如何打包结构的类似帖子:

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

但我想知道是否有某种构建时生成的文件可以准确地显示结构是如何打包的?我尝试检查链接器生成的映射文件,但没有此信息。

PS:我正在尝试让一些微控制器通过 UART 相互通信,因为一个是 16 位,另一个是 32 位,所以每次更新这些结构时都会遇到一些错误。

【问题讨论】:

stddef.h 中有一个宏 offsetof()。见en.wikipedia.org/wiki/Offsetof 您当然可以为您的结构设置pack 宽度,并在两个项目上强制使用相同的值。 考虑写函数到serialize/反序列化通过UART传输之前/之后的数据。这个解决方案可能比试图控制编译器如何定义结构更健壮。 跨编译域使用结构是非常糟糕的做法。可以轻松避免的无休止的维护。避免使用结构进行这种通信。 您确实有 3 种不同的结构:16 位结构、32 位结构和用于串行通信的结构。与其尝试使所有 3 个在位级别完全相同,不如定义串行通信语法并将例程编写为 1)将 16 位转换为/从串行和 2)将 32 位转换为/从串行(类似于@kkrambo comment) 【参考方案1】:

预处理器无法计算出结构偏移量,因此仅靠宏魔法就不会为您转储偏移量。

如果您正在为嵌入式目标构建,您可能能够创建 .bin 文件(不是 elf、coff、mach-o)。如果您使用每个目标编译器在目标文件中创建偏移量数组,然后将其转储到 bin 文件中,则应该可以比较每个目标的 bin 文件。自动化构建时间检查的过程是一个好主意。

下面是我所说的一个例子:

#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)
;

这只是创建了一个偏移量表。为 mipsel 和 x86_64 构建它并进行比较。这是一个make文件:

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

现在,比较偏移量很容易:

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

mips 发出一个 32 字节的数据段,而不是 x86 的 20 字节。如果您设置offsets 的大小,您应该能够使用cmp 在您的构建中比较两者。

如果您的目标具有不同的字节序,您可能需要将 o 宏更改为使用 ntohl 或其他类似方法,以使两个编译器以相同的格式发出整数。

【讨论】:

以上是关于查看在构建期间如何打包 C 结构的主要内容,如果未能解决你的问题,请参考以下文章

C++ 数据成员对齐和数组打包

被遗忘的C结构体打包技术

带有位字段的 C/C++ 结构体打包在缓冲区中

可以在php中打包带有位字段的c结构吗?

破解Gradle 从Gradle Plugin 构建看APK打包流程解析

破解Gradle 从Gradle Plugin 构建看APK打包流程解析